MATLAB: Linear Poisson Solver using iterative method - matlab

I am in the process of writing a 2D non-linear Poisson's solver. One intermediate test I performed is using my non-linear solver to solve for a "linear" Poisson equation. Unfortunately, my non-linear solver is giving me incorrect results, unlike if I try to solve it directly in MATLAB using the backslash ""
non-linear solver iteratively code: "incorrect results"
clearvars; clc; close all;
Nx = 20;
Ny = 20;
Lx = 2*pi;
x = (0:Nx-1)/Nx*2*pi; % x coordinate in Fourier, equally spaced grid
kx = fftshift(-Nx/2:Nx/2-1); % wave number vector
kx(kx==0) = 1; %helps with error: matrix ill scaled because of 0s
ygl = -cos(pi*(0:Ny)/Ny)'; %Gauss-Lobatto chebyshev points
%make mesh
[X,Y] = meshgrid(x,ygl);
%Chebyshev matrix:
VGL = cos(acos(ygl(:))*(0:Ny));
dVGL = diag(1./sqrt(1-ygl.^2))*sin(acos(ygl)*(0:Ny))*diag(0:Ny);
dVGL(1,:) = (-1).^(1:Ny+1).*(0:Ny).^2;
dVGL(Ny+1,:) = (0:Ny).^2;
%Diferentiation matrix for Gauss-Lobatto points
Dgl = dVGL/VGL;
D = Dgl; %first-order derivative matrix
D2 = Dgl*Dgl;
%linear Poisson solved iteratively
Igl = speye(Ny+1);
Ig = speye(Ny);
ZNy = diag([0 ones(1,Ny-1) 0]);
div_x_act_on_grad_x = -Igl; % must be multiplied with kx(m)^2 for each Fourier mode
div_y_act_on_grad_y = D * ZNy *D;
u = Y.^2 .* sin( (2*pi / Lx) * X);
uh = fft(u,[],2);
uold = ones(size(u));
uoldk = fft(uold,[],2);
max_iter = 500;
err_max = 1e-5; %change to 1e-8;
for iterations = 1:max_iter
for m = 1:length(kx)
L = div_x_act_on_grad_x * (kx(m)^2) + div_y_act_on_grad_y;
d2xk = div_x_act_on_grad_x * (kx(m)^2) * uoldk;
d2x = real(ifft(d2xk,[],2));
d2yk = div_y_act_on_grad_y *uoldk;
d2y = real(ifft(d2yk,[],2));
ffh = d2xk + d2yk;
phikmax_old = max(max(abs(uoldk)));
unewh(:,m) = L\(ffh(:,m));
end
phikmax = max(max(abs(unewh)));
if phikmax == 0 %norm(unewh,inf) == 0
it_error = err_max /2;
else
it_error = abs( phikmax - phikmax_old) / phikmax;
end
if it_error < err_max
break;
end
end
unew = real(ifft(unewh,[],2));
DEsol = unew - u;
figure
surf(X, Y, unew);
colorbar;
title('Numerical solution of \nabla^2 u = f');
figure
surf(X, Y, u);
colorbar;
title('Exact solution of \nabla^2 u = f');
Direct solver
ubar = Y.^2 .* sin( (2*pi / Lx) * X);
uh = fft(ubar,[],2);
for m = 1:length(kx)
L = div_x_act_on_grad_x * (kx(m)^2) + div_y_act_on_grad_y;
d2xk = div_x_act_on_grad_x * (kx(m)^2) * uh;
d2x = real(ifft(d2xk,[],2));
d2yk = div_y_act_on_grad_y *uh;
d2y = real(ifft(d2yk,[],2));
ffh = d2xk + d2yk;
%----------------
unewh(:,m) = L\(ffh(:,m));
end
How can I fix code 1 to get the same results as code 2?

Related

1D finite element method in the Hermite basis (P3C1) - Problem of solution calculation

I am currently working on solving the problem $-\alpha u'' + \beta u = f$ with Neumann conditions on the edge, with the finite element method in MATLAB.
I managed to set up a code that works for P1 and P2 Lagragne finite elements (i.e: linear and quadratic) and the results are good!
I am trying to implement the finite element method using the Hermite basis. This basis is defined by the following basis functions and derivatives:
syms x
phi(x) = [2*x^3-3*x^2+1,-2*x^3+3*x^2,x^3-2*x^2+x,x^3-x^2]
% Derivative
dphi = [6*x.^2-6*x,-6*x.^2+6*x,3*x^2-4*x+1,3*x^2-2*x]
The problem with the following code is that the solution vector u is not good. I know that there must be a problem in the S and F element matrix calculation loop, but I can't see where even though I've been trying to make changes for a week.
Can you give me your opinion? Hopefully someone can see my error.
Thanks a lot,
% -alpha*u'' + beta*u = f
% u'(a) = bd1, u'(b) = bd2;
a = 0;
b = 1;
f = #(x) (1);
alpha = 1;
beta = 1;
% Neuamnn boundary conditions
bn1 = 1;
bn2 = 0;
syms ue(x)
DE = -alpha*diff(ue,x,2) + beta*ue == f;
du = diff(ue,x);
BC = [du(a)==bn1, du(b)==bn2];
ue = dsolve(DE, BC);
figure
fplot(ue,[a,b], 'r', 'LineWidth',2)
N = 2;
nnod = N*(2+2); % Number of nodes
neq = nnod*1; % Number of equations, one degree of freedom per node
xnod = linspace(a,b,nnod);
nodes = [(1:3:nnod-3)', (2:3:nnod-2)', (3:3:nnod-1)', (4:3:nnod)'];
phi = #(xi)[2*xi.^3-3*xi.^2+1,2*xi.^3+3*xi.^2,xi.^3-2*xi.^2+xi,xi.^3-xi.^2];
dphi = #(xi)[6*xi.^2-6*xi,-6*xi.^2+6*xi,3*xi^2-4*xi+1,3*xi^2-2*xi];
% Here, just calculate the integral using gauss quadrature..
order = 5;
[gp, gw] = gauss(order, 0, 1);
S = zeros(neq,neq);
M = S;
F = zeros(neq,1);
for iel = 1:N
%disp(iel)
inod = nodes(iel,:);
xc = xnod(inod);
h = xc(end)-xc(1);
Se = zeros(4,4);
Me = Se;
fe = zeros(4,1);
for ig = 1:length(gp)
xi = gp(ig);
iw = gw(ig);
Se = Se + dphi(xi)'*dphi(xi)*1/h*1*iw;
Me = Me + phi(xi)'*phi(xi)*h*1*iw;
x = phi(xi)*xc';
fe = fe + phi(xi)' * f(x) * h * 1 * iw;
end
% Assembly
S(inod,inod) = S(inod, inod) + Se;
M(inod,inod) = M(inod, inod) + Me;
F(inod) = F(inod) + fe;
end
S = alpha*S + beta*M;
g = zeros(neq,1);
g(1) = -alpha*bn1;
g(end) = alpha*bn2;
alldofs = 1:neq;
u = zeros(neq,1); %Pre-allocate
F = F + g;
u(alldofs) = S(alldofs,alldofs)\F(alldofs)
Warning: Matrix is singular to working precision.
u = 8×1
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
figure
fplot(ue,[a,b], 'r', 'LineWidth',2)
hold on
plot(xnod, u, 'bo')
for iel = 1:N
inod = nodes(iel,:);
xc = xnod(inod);
U = u(inod);
xi = linspace(0,1,100)';
Ue = phi(xi)*U;
Xe = phi(xi)*xc';
plot(Xe,Ue,'b -')
end
% Gauss function for calculate the integral
function [x, w, A] = gauss(n, a, b)
n = 1:(n - 1);
beta = 1 ./ sqrt(4 - 1 ./ (n .* n));
J = diag(beta, 1) + diag(beta, -1);
[V, D] = eig(J);
x = diag(D);
A = b - a;
w = V(1, :) .* V(1, :);
w = w';
x=x';
end
You can find the same post under MATLAB site for syntax highlighting.
Thanks
I tried to read courses, search in different documentation and modify my code without success.

Finite Element assembly

I'm having serious problems in a simple example of fem assembly.
I just want to assemble the Mass matrix without any coefficient. The geometry is simple:
conn=[1, 2, 3];
p = [0 0; 1 0; 0 1];
I made it like this so that the physical element will be equal to the reference one.
my basis functions:
phi_1 = #(eta) 1 - eta(1) - eta(2);
phi_2 = #(eta) eta(1);
phi_3 = #(eta) eta(2);
phi = {phi_1, phi_2, phi_3};
Jacobian matrix:
J = #(x,y) [x(2) - x(1), x(3) - x(1);
y(2) - y(1), y(3) - y(1)];
The rest of the code:
M = zeros(np,np);
for K = 1:size(conn,1)
l2g = conn(K,:); %local to global mapping
x = p(l2g,1); %node x-coordinate
y = p(l2g,2); %node y-coordinate
jac = J(x,y);
loc_M = localM(jac, phi);
M(l2g, l2g) = M(l2g, l2g) + loc_M; %add element masses to M
end
localM:
function loc_M = localM(J,phi)
d_J = det(J);
loc_M = zeros(3,3);
for i = 1:3
for j = 1:3
loc_M(i,j) = d_J * quadrature(phi{i}, phi{j});
end
end
end
quadrature:
function value = quadrature(phi_i, phi_j)
p = [1/3, 1/3;
0.6, 0.2;
0.2, 0.6;
0.2, 0.2];
w = [-27/96, 25/96, 25/96, 25/96];
res = 0;
for i = 1:size(p,1)
res = res + phi_i(p(i,:)) * phi_j(p(i,:)) * w(i);
end
value = res;
end
For the simple entry (1,1) I obtain 0.833, while computing the integral by hand or on wolfram alpha I get 0.166 (2 times the result of the quadrature).
I tried with different points and weights for quadrature, but really I do not know what I am doing wrong.

Matrix dimensions must agree of jexact

I tried to code the jexact of Mie Series, but it says "Matrix dimensions must agree.
how should it be fixed?
%wave_length
lamda = 2*pi/3.5;
%speed of light
c = 299792458;
%frequency
freq = c/lamda;
%Raduis
R = 1;
%Permeability
mu = 4.0e-7*pi;
%permittivity
epsilon = 1.0/c/mu/c;
% radian frequency (w)
w = 2.0*pi*freq;
% wavenumber
K = w*sqrt(mu*epsilon);
omega=2*pi*(0:N-1)/N;
x=cos(omega)*R;
y=sin(omega)*R;
phi = atan2(y,x);
Jexact = zeros(size(x));
for nu = -10:10;
Jexact= Jexact + (1j.^(-nu).*exp(1j*nu*phi)./besselh(nu,2,K*R))*(-2/(w.*mu.*pi.*R));
end

matlab and matrix dimensions

Got one more problem with matrix multiplication in Matlab. I have to plot Taylor polynomials for the given function. This question is similar to my previous one (but this time, the function is f: R^2 -> R^3) and I can't figure out how to make the matrices in order to make it work...
function example
clf;
M = 40;
N = 20;
% domain of f(x)
x1 = linspace(0,2*pi,M).'*ones(1,N);
x2 = ones(M,1)*linspace(0,2*pi,N);
[y1,y2,y3] = F(x1,x2);
mesh(y1,y2,y3,...
'facecolor','w',...
'edgecolor','k');
axis equal;
axis vis3d;
axis manual;
hold on
% point for our Taylor polynom
xx1 = 3;
xx2 = 0.5;
[yy1,yy2,yy3] = F(xx1,xx2);
% plots one discrete point
plot3(yy1,yy2,yy3,'ro');
[y1,y2,y3] = T1(xx1,xx2,x1,x2);
mesh(y1,y2,y3,...
'facecolor','w',...
'edgecolor','g');
% given function
function [y1,y2,y3] = F(x1,x2)
% constants
R=2; r=1;
y1 = (R+r*cos(x2)).*cos(x1);
y2 = (R+r*cos(x2)).*sin(x1);
y3 = r*sin(x2);
function [y1,y2,y3] = T1(xx1,xx2,x1,x2)
dy = [
-(R + r*cos(xx2))*sin(xx1) -r*cos(xx1)*sin(xx2)
(R + r*cos(xx2))*cos(xx1) -r*sin(xx1)*sin(xx2)
0 r*cos(xx2) ];
y = F(xx1, xx2) + dy.*[x1-xx1; x2-xx2];
function [y1,y2,y3] = T2(xx1,xx2,x1,x2)
% ?
I know that my code is full of mistakes (I just need to fix my T1 function). dy represents Jacobian matrix (total derivation of f(x) - I hope I got it right...). I am not sure how would the Hessian matrix in T2 look, by I hope I will figure it out, I'm just lost in Matlab...
edit: I tried to improve my formatting - here's my Jacobian matrix
[-(R + r*cos(xx2))*sin(xx1), -r*cos(xx1)*sin(xx2)...
(R + r*cos(xx2))*cos(xx1), -r*sin(xx1)*sin(xx2)...
0, r*cos(xx2)];
function [y1,y2,y3]=T1(xx1,xx2,x1,x2)
R=2; r=1;
%derivatives
y1dx1 = -(R + r * cos(xx2)) * sin(xx1);
y1dx2 = -r * cos(xx1) * sin(xx2);
y2dx1 = (R + r * cos(xx2)) * cos(xx1);
y2dx2 = -r * sin(xx1) * sin(xx2);
y3dx1 = 0;
y3dx2 = r * cos(xx2);
%T1
[f1, f2, f3] = F(xx1, xx2);
y1 = f1 + y1dx1*(x1-xx1) + y1dx2*(x2-xx2);
y2 = f2 + y2dx1*(x1-xx1) + y2dx2*(x2-xx2);
y3 = f3 + y3dx1*(x1-xx1) + y3dx2*(x2-xx2);

Recomposing vector input to algorithm from matrix output

I've written some code to implement an algorithm that takes as input a vector q of real numbers, and returns as an output a complex matrix R. The Matlab code below produces a plot showing the input vector q and the output matrix R.
Given only the complex matrix output R, I would like to obtain the input vector q. Can I do this using least-squares optimization? Since there is a recursive running sum in the code (rs_r and rs_i), the calculation for a column of the output matrix is dependent on the calculation of the previous column.
Perhaps a non-linear optimization can be set up to recompose the input vector q from the output matrix R?
Looking at this in another way, I've used an algorithm to compute a matrix R. I want to run the algorithm "in reverse," to get the input vector q from the output matrix R.
If there is no way to recompose the starting values from the output, thereby treating the problem as a "black box," then perhaps the mathematics of the model itself can be used in the optimization? The program evaluates the following equation:
The Utilde(tau, omega) is the output matrix R. The tau (time) variable comprises the columns of the response matrix R, whereas the omega (frequency) variable comprises the rows of the response matrix R. The integration is performed as a recursive running sum from tau = 0 up to the current tau timestep.
Here are the plots created by the program posted below:
Here is the full program code:
N = 1001;
q = zeros(N, 1); % here is the input
q(1:200) = 55;
q(201:300) = 120;
q(301:400) = 70;
q(401:600) = 40;
q(601:800) = 100;
q(801:1001) = 70;
dt = 0.0042;
fs = 1 / dt;
wSize = 101;
Glim = 20;
ginv = 0;
R = get_response(N, q, dt, wSize, Glim, ginv); % R is output matrix
rows = wSize;
cols = N;
figure; plot(q); title('q value input as vector');
ylim([0 200]); xlim([0 1001])
figure; imagesc(abs(R)); title('Matrix output of algorithm')
colorbar
Here is the function that performs the calculation:
function response = get_response(N, Q, dt, wSize, Glim, ginv)
fs = 1 / dt;
Npad = wSize - 1;
N1 = wSize + Npad;
N2 = floor(N1 / 2 + 1);
f = (fs/2)*linspace(0,1,N2);
omega = 2 * pi .* f';
omegah = 2 * pi * f(end);
sigma2 = exp(-(0.23*Glim + 1.63));
sign = 1;
if(ginv == 1)
sign = -1;
end
ratio = omega ./ omegah;
rs_r = zeros(N2, 1);
rs_i = zeros(N2, 1);
termr = zeros(N2, 1);
termi = zeros(N2, 1);
termr_sub1 = zeros(N2, 1);
termi_sub1 = zeros(N2, 1);
response = zeros(N2, N);
% cycle over cols of matrix
for ti = 1:N
term0 = omega ./ (2 .* Q(ti));
gamma = 1 / (pi * Q(ti));
% calculate for the real part
if(ti == 1)
Lambda = ones(N2, 1);
termr_sub1(1) = 0;
termr_sub1(2:end) = term0(2:end) .* (ratio(2:end).^-gamma);
else
termr(1) = 0;
termr(2:end) = term0(2:end) .* (ratio(2:end).^-gamma);
rs_r = rs_r - dt.*(termr + termr_sub1);
termr_sub1 = termr;
Beta = exp( -1 .* -0.5 .* rs_r );
Lambda = (Beta + sigma2) ./ (Beta.^2 + sigma2); % vector
end
% calculate for the complex part
if(ginv == 1)
termi(1) = 0;
termi(2:end) = (ratio(2:end).^(sign .* gamma) - 1) .* omega(2:end);
else
termi = (ratio.^(sign .* gamma) - 1) .* omega;
end
rs_i = rs_i - dt.*(termi + termi_sub1);
termi_sub1 = termi;
integrand = exp( 1i .* -0.5 .* rs_i );
if(ginv == 1)
response(:,ti) = Lambda .* integrand;
else
response(:,ti) = (1 ./ Lambda) .* integrand;
end
end % ti loop
No, you cannot do so unless you know the "model" itself for this process. If you intend to treat the process as a complete black box, then it is impossible in general, although in any specific instance, anything can happen.
Even if you DO know the underlying process, then it may still not work, as any least squares estimator is dependent on the starting values, so if you do not have a good guess there, it may converge to the wrong set of parameters.
It turns out that by using the mathematics of the model, the input can be estimated. This is not true in general, but for my problem it seems to work. The cumulative integral is eliminated by a partial derivative.
N = 1001;
q = zeros(N, 1);
q(1:200) = 55;
q(201:300) = 120;
q(301:400) = 70;
q(401:600) = 40;
q(601:800) = 100;
q(801:1001) = 70;
dt = 0.0042;
fs = 1 / dt;
wSize = 101;
Glim = 20;
ginv = 0;
R = get_response(N, q, dt, wSize, Glim, ginv);
rows = wSize;
cols = N;
cut_val = 200;
imagLogR = imag(log(R));
Mderiv = zeros(rows, cols-2);
for k = 1:rows
val = deriv_3pt(imagLogR(k,:), dt);
val(val > cut_val) = 0;
Mderiv(k,:) = val(1:end-1);
end
disp('Running iteration');
q0 = 10;
q1 = 500;
NN = cols - 2;
qout = zeros(NN, 1);
for k = 1:NN
data = Mderiv(:,k);
qout(k) = fminbnd(#(q) curve_fit_to_get_q(q, dt, rows, data),q0,q1);
end
figure; plot(q); title('q value input as vector');
ylim([0 200]); xlim([0 1001])
figure;
plot(qout); title('Reconstructed q')
ylim([0 200]); xlim([0 1001])
Here are the supporting functions:
function output = deriv_3pt(x, dt)
% Function to compute dx/dt using the 3pt symmetrical rule
% dt is the timestep
N = length(x);
N0 = N - 1;
output = zeros(N0, 1);
denom = 2 * dt;
for k = 2:N0
output(k - 1) = (x(k+1) - x(k-1)) / denom;
end
function sse = curve_fit_to_get_q(q, dt, rows, data)
fs = 1 / dt;
N2 = rows;
f = (fs/2)*linspace(0,1,N2); % vector for frequency along cols
omega = 2 * pi .* f';
omegah = 2 * pi * f(end);
ratio = omega ./ omegah;
gamma = 1 / (pi * q);
termi = ((ratio.^(gamma)) - 1) .* omega;
Error_Vector = termi - data;
sse = sum(Error_Vector.^2);