- Don't use anonymous functions unless absolutely necessary; they are not needed in this problem!
- Vectorize. You don't need to construct A & b in a for loop.
- Use spdiags. For a 5-point stencil there are 5 non-zero elements in each row (forming 5 diagonals), and you can use spdiags to generate your sparse matrix from an Nx5 matrix, rather than using spalloc and filling entries manually.
- Use exp(), not e^
Solve the generalized form of the Poisson equation
12 views (last 30 days)
Show older comments
Samuel Martinez
on 20 Jun 2021
Commented: Samuel Martinez
on 20 Jun 2021
Hello, I am trying to solve the following problem in a rectangle with Dirichlet conditions at the boundary. I have the following implementation for this problem:
n =25;
dx = 1/(n-1);
x= 0:dx:1;
y= x;
[X,Y] = ndgrid(x,y);
isDirichlet = (X==0) | (X==1) | (Y==0) | (Y==1);
u = @(x,y) 10*x.*y.*(1-x).*(1-y).*exp(x.^(4.5));
k = @(x,y) cos(x);
f = @(x,y) e^(x.^4.5).*(-45 *(-x.^5.5 + x.^4.5 - 0.444444*x + 0.222222).* (y - 1).* y.* sin(x)...
+ 247.5* (-1.36364*x.^4.5 + x.^3.5 - 0.818182*x.^9 + 0.818182*x.^8 - 0.0808081).*(y - 1).*y.*cos(x)...
- 20*(x - 1).*x.*cos(x));
g = @(x,y) 0;
A = spalloc(n^2,n^2,5);
b = zeros(n^2,1);
for i=1:n^2
l(i)=isDirichlet(i);
end
for i = 1:n^2
if isDirichlet(i)
A(i,i) = 1;
b(i) = g(X(i),Y(i));
continue
end
[row,col] = ind2sub([n,n],i);
L = sub2ind([n,n],row-1,col);
R = sub2ind([n,n],row+1,col);
U = sub2ind([n,n],row,col+1);
D = sub2ind([n,n],row,col-1);
kl = k((X(i)+X(L))/2,Y(i));
kr = k((X(i)+X(R))/2,Y(i));
ku = k(X(i),(Y(i)+Y(U))/2);
kd = k(X(i),(Y(i)+Y(D))/2);
A(i,i) = kl+kr+ku+kd;
A(i,L) = -kl;
A(i,R) = -kr;
A(i,U) = -ku;
A(i,D) = -kd;
b(i) = f(X(i),Y(i))*dx*dx;
end
uh=A\b;
Uh = reshape(uh,[n,n]);
surf(Uh); hold on
surf(u(X,Y)+1);
max(max(abs(Uh-u(X,Y))))
With the above it gives me the following error. With n = 50 I have the following error::
error: sub2ind: index (51,_): out of bound 50
error: called from
prueba at line 33 column 6
But with n = 51 it still works, I don't know what this error could be. Also, I have to solve this for 500, 1000, 5000 and 10000 points, but for that I would need a lot of memory, so I guess you can take advantage of the matrix structure in some way to achieve these results. I appreciate if you could help me to fix that error and see how I can implement for 500, 1000, 5000 and 10000. First of all, thank you.
0 Comments
Accepted Answer
Joel Lynch
on 20 Jun 2021
Edited: Joel Lynch
on 20 Jun 2021
I don't get an error when running your code with MATLAB version 2019b, but the bigger problem you have is that your method will not scale well. A few tips off the top:
I put together the following solution, which runs values 50, 100, and 500 in 0.027, 0.11, ands 1.56 seconds respectively on my computer. Your code runs the same values in 0.074, 0.31, and 405 seconds. That's >250 times faster for N=500!
close all
clear all
clc
% Grid Definition
nx = 50;
N = nx*nx;
x = linspace(0,1,nx);
y = x;
[X,Y] = ndgrid(x,y);
dx = x(2)-x(1);
% Compute RHS with no BC, assuming f(X,Y)
b = -1.36364*X.^4.5 + X.^3.5 - 0.818182*X.^9 + 0.818182*X.^8 - 0.0808081;
b = b * 247.5 .* (Y - 1).*Y.*cos(X);
b = b -45 *(-X.^5.5 + X.^4.5 - 0.444444*X + 0.222222).*(Y - 1).* Y.* sin(X);
b = b -20 *(X - 1).*X.*cos(X) ;
b = exp( X.^4.5 ) .* b;
b = b.*dx.*dx;
% Overwrite BC's on the RHS using vectorized assignment
isDirichlet = (X==0) | (X==1) | (Y==0) | (Y==1);
b(isDirichlet) = 0.0;
% Compute A Matrix diagonals Ad(1:N,1:5) assuming normal stencils
Ad = zeros(N,5);
% Compute off-center points, note that there is no y dependence in
% k(x,y)=cos(x)
Ad(:,1) = -cos(X(:)); % Down = -kd = -cos(X)
Ad(:,2) = -cos(X(:)-dx*0.5); % Left = -kl = -cos(X-dx/2)
Ad(:,4) = -cos(X(:)+dx*0.5); % Right= -kl = -cos(X+dx/2)
Ad(:,5) = Ad(:,1); % Up = -ku = -cos(X) = Down
% Use these to get center by summing the negatives
Ad(:,3) = -sum( Ad(:,[1,2,4,5]), 2 ); % Center
% Overwrite BC's on the A Matrix diagonals
idx = find(isDirichlet(:)); % array of inidices of BC points
Ad(idx,:) = 0.0; % Zero all elements of rows defined by idx
Ad(idx,3) = 1.0; % Make center node=1
% Create A Matrix from Ad, assigning columns to diagonals
A = spdiags(Ad,[-nx,-1,0,1,nx],N,N)'; % ADDED TRANSPOSE
% Solve & Reshape
uh=A\b(:);
uh = reshape(uh,[nx,nx]);
% Compute Exact solution and error
u_exact = 10*X.*Y.*(1-X).*(1-Y).*exp(X.^(4.5));
u_error = abs( (uh - u_exact)./u_exact );
% Reshape and plot solution on two subplots
subplot(1,3,1); view(0,90); hold on; colorbar;
surf(x,y,uh,'edgecolor','none');
xlabel('x'); ylabel('y')
title('Computed')
shading interp
axis square
% Add exact solution
subplot(1,3,2); view(0,90); hold on; colorbar;
surf(x,y,u_exact,'edgecolor','none');
xlabel('x');
title('Exact')
shading interp
axis square
% add Error
subplot(1,3,3); view(0,90); hold on; colorbar;
surf(x,y,u_error,'edgecolor','none');
xlabel('x');
title('Error')
shading interp
axis square
8 Comments
Joel Lynch
on 20 Jun 2021
Edited: Joel Lynch
on 20 Jun 2021
To clarify, the line A = spdiags(Ad,[-nx,-1,0,1,nx],N,N)'; has an added apostrophe on the end, which transposes A.
I can't say if the exact solution is correct and implemented properly, but that could be a source of confusion if it is not correct. If indeed the computed solution is not converging like a second-order scheme, I would check that your stencil is being applied correctly, as well as the "f" function, which is long and could contain an error.
Good luck!
More Answers (0)
See Also
Categories
Find more on Eigenvalue Problems in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!