Contents
%%%%%%%%%%%%%%%%%%%% %%%%%% FEM 2D %%%%%% %%%%%%%%%%%%%%%%%%%% % Solve: -laplace(u) = f on unit square % u = g on boundary % *NEGATIVE LAPLACIAN** plug in -f if you just want laplace(u) = f clear all; % PROCEDURE FOR MESHING SQUARE REGION AND ASSIGNING ELEMENTS %_______________________ %| \ | \ | %| \ 7 | \ 8 | %| 5 \ | 6 \ | %________\__|_______\_ | % gridline = 1 in this case %| \ | \ | one gridline for both x and y components %| \ 3 | \ 4 | %| 1 \ | 2 \ | %|_______\__|_______\__| %%%%%% ERRORS (u = sin(x+y))%%%%%%% % elements = 8 -- error = 0.5721 % % elements = 98 -- error = 0.2000 % % elements = 800 -- error =0.1004 % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%% CONTROL CENTER %%%%% ep2 = 10; % size of square g = @(x,y) sin(x)+cos(y); % boundary constant f = @(x,y) sin(x)+cos(y); % the function f gridlines = 10; % decide how you'd like to divide up the square resolution = 50; % resolution of plotted solution (e.g. 10 --> plot 10x10 grid) %%%%%%%%%%%%%%%%%%%%%%%%%% ep1 = 0; elements = 2; % dont change this for i = 1:gridlines elements = elements + 2*(2*i + 1); % make elements from grid # end elements local = 3; % number of linear basis functions Gnodes = (sqrt(elements/2)+1)^2; % total global nodes % Local linear basis elements N{1} = @(x,y) 1-x-y; N{2} = @(x,y) x; N{3} = @(x,y) y; disp('ID, IEN, LM...') % Assemble ID root = sqrt(Gnodes); ID = 1:Gnodes; ID = reshape(ID,root,root); ID = ID'; ID(1,:) = 0; ID(root,:) = 0; ID(:,1) = 0; ID(:,root) = 0; ID = reshape(ID,1,Gnodes); pp = size(find(ID>0),2); C = 1:pp; ID(find(ID>0)) = C; % Record interior and boundary global function sets (use in assembling f) I = find(ID>0); % Global interior function numbers B = find(ID == 0); % Global boundary function numbers % Assemble IEN % first need to make a template matrix which has the global nodes in order % just like how theyd look on paper. % The IEN is assembled by looking through the template and recording which % entries the local basis function is nonzero on. template = Gnodes:-1:1; template = reshape(template,root,root); template = template'; if mod(root,2) == 1 for i = 1:(root-1)/2 temp = template(:,i); template(:,i) = template(:,root+1-i); template(:,root+1-i) = temp; end else for i = 1:root/2 temp = template(:,i); template(:,i) = template(:,(root+1-i)); template(:,(root+1-i)) = temp; end end % First row numbers for IEN row1 = []; for i = root:-1:1 row1 = [row1 template(i,2:end)]; row1 = [row1 template(i,1:end-1)]; end row1(1:root-1) = []; row1(end:-1:end-root+2)=[]; % second row row2 = []; for i = root:-1:1 row2 = [row2 template(i,1:end-1)]; row2 = [row2 template(i,2:end)]; end row2(1:root-1) = []; row2(end:-1:end-root+2)=[]; % third row row3 = []; for i = 1:root-1 row3 = [row3 template(root-i,1:root-1)]; row3 = [row3 template(root-i+1,2:end)]; end % IEN IEN = [row1; row2; row3;]; % LM LM = ID(IEN); % Integral involved here: % integral (Ni_x*Nj_x + Ni_y*Nj_y)dydx (0<x<1, 0<y<1-x) % quad2d does not work for constant functions for some strange reason, so i % will just hardcode in all the integrals... k1 = zeros(local,local); k1(1,1)=1;k1(1,2)=-.5;k1(1,3)=-.5;k1(2,1) = -.5;k1(2,2)=.5; k1(2,3)=0;k1(3,1)=-.5;k1(3,2)=0;k1(3,3)=.5; % % Compile translation functions % set of lower triangles (element numbers) lower = []; for i = 1:2*(gridlines+1):elements-gridlines-2 lower = [lower i:i+gridlines]; end % % set of upper triangles (translation functions different for each % % triangle) upper = 1:elements; upper(lower) = []; width = (ep2-ep1)/sqrt(elements/2); %width of each triangle % x = horizontal global coordinate, y = vertical global coordinate % z = horizontal local coordinate, w = vertical local coordinate % x,y,z,w translations on lower triangle elements disp('Translation functions...') j = 1; p = 1; for e = lower xofz{e} = @(z) width*z + (j-1)*width; % x(z) local to global zofx{e} = @(z) z/width - (j-1); % z(x) global to local yofw{e} = @(z) width*z + (p-1)*width; % y(w) local to global wofy{e} = @(z) z/width - (p-1); % w(y) global to local % counters to keep track of constants j = j+1; if j == gridlines + 2 j = 1; p = p+1; end end % x,y,z,w translations on upper triangle elements j = 1; p = 1; for e = upper xofz{e} = @(z) width*(1-z) + (j-1)*width; zofx{e} = @(z) 1-z/width + (j-1); yofw{e} = @(z) width*(1-z) + (p-1)*width; wofy{e} = @(z) 1-z/width + (p-1); j = j+1; if j == gridlines + 2 j = 1; p = p+1; end end % Multiplier for global to local integral % Here, the jacobian for (f,N) integral is x'(z)*y'(w). Both x' and y' are % equal to width, but change signs on some elements. Since they both share % the same sign in each case, the jacobian is always just width^2 % (pretty convenient) jacobian = width*width; %%%%% SERENDIPITY %%%%%% % It turns out that, through 1D linear change of variables, chain rule, and % the jacobian, the global integral of delNi*delNj is EXACTLY the same as % the local integral of the local functions. No multipliers or anything % needed - local integration is all that's required (for K at least) %%%%%% First: Boundary function g needs to be converted into aN1+bN2+cN3 ( a linear % combination of local basis functions). Here we find the coefficients % corresponding to each element and store them in a matrix g(i,e) to be % used in the assembly of local f % Get all the necessary points in order: xvec = linspace(ep1,ep2, gridlines+2); yvec = linspace(ep1,ep2, gridlines+2); % x,y coordinates of A at each element % (A is the corner of each triangle) disp('Coefficients of boundary function...') newtemplate = 1:root; temp1 = []; temp2 = []; for i = 1:gridlines+1 temp1 = [temp1 newtemplate(1:end-1)]; temp1 = [temp1 newtemplate(2:end)]; temp2 = [temp2 newtemplate(i)*ones(1,gridlines+1) ... newtemplate(i+1)*ones(1,gridlines+1)]; end A1(:,1) = xvec(temp1)'; A1(:,2) = yvec(temp2)'; % Start again but assemble coordinates of B (the right vertex of each % triangular element) temp1 = []; temp2 = []; for i = 1:gridlines+1 temp1 = [temp1 newtemplate(2:end)]; temp1 = [temp1 newtemplate(1:end-1)]; temp2 = [temp2 newtemplate(i)*ones(1,gridlines+1) ... newtemplate(i+1)*ones(1,gridlines+1)]; end B1(:,1) = xvec(temp1)'; B1(:,2) = yvec(temp2)'; % Start again but assemble coordinates of C (the upper vertex of each % triangular element) temp1 = []; temp2 = []; for i = 1:gridlines+1 temp1 = [temp1 newtemplate(1:end-1)]; temp1 = [temp1 newtemplate(2:end)]; temp2 = [temp2 newtemplate(i+1)*ones(1,gridlines+1) ... newtemplate(i)*ones(1,gridlines+1)]; end C1(:,1) = xvec(temp1)'; C1(:,2) = yvec(temp2)'; Nmat = [1 0 0; -1 1 0 ; -1 0 1]; % Matrix of coefficients of local basis functions gtild = zeros(local,elements); % This will be the matrix containing the essential coefficients % These coefficients describe a local version of the boundary function on % each element. for e = 1:elements gatA = g(A1(e,1),A1(e,2)); % g evaluated at corner of element gatB = g(B1(e,1),B1(e,2)); % g evaluated at right vertex gatC = g(C1(e,1),C1(e,2)); % g evaluated at upper vertex vecb = [gatA;gatB-gatA;gatC-gatA]; % These are the coefficients of a function % that interpolates g at the vertices of each element % i.e. g(interpolated on element e) = gatA + (gatB-gatA)*x + (gatC-gatA)*y gtild(:,e) = Nmat\vecb; % this is solving the matrix equaton Nmat*gtild = vecb. This % provides the needed coefficients that describe the % linear combination of local basis functions that match the boundary % function g at the vertices of each element. % e.g. g(on element 1) = gtild(1,1)*N1 + gtild(2,1)*N2 + gtild(3,1)*N3 end disp('Local f...') % Assemble local f wmax = @(z) 1-z; % hypotenuse of local triangle - use for local integral fe = zeros(local,elements); % here im saying fe is a matrix % written convention f_i^e ---> program: fe(i,e) i = local basis #, e = element # for e = 1:elements % put 'e' here if youd like to track progress floc = @(z,w) f(xofz{e}(z),yofw{e}(w)); % local f function for integration for i = 1:local integrand = @(z,w) floc(z,w).*N{i}(z,w); % adding in (f,N) fe(i,e) = fe(i,e) + jacobian*quad2d(integrand,0,1,0,wmax); %%%% ADD BILINEAR BOUNDARY/INTERIOR PART TO LOCAL F %%%%% % Looking at the global weak form of this whole problem, it is % apparent that the only bilinear parts added to local f are ones % that consist of a(N_i,N_j) where i and j strictly belong to % DIFFERENT sets (e.g. i in Interior nodes, j in Boundary nodes - we % check this by referencing IEN and the sets I and B). % The following code emulates this process. j = [1 2 3]; j(i) = [];% other entries of IEN column e BESIDES row i to be checked a = IEN(i,e); % the entry in question b = IEN(j(1),e); % other entry of IEN column e c = IEN(j(2),e); % other entry of IEN column e ainI = ismember(a,I); % is IEN(i,e) a member of I? if ainI == 1 % IEN(i,e) is a member of I binB = ismember(b,B); cinB = ismember(c,B); %check if other entries are in B if binB == 1 fe(i,e) = fe(i,e) - gtild(i,e)*k1(i,j(1)); % add integral to f if IEN row % i is in I and row j(1) is in B end if cinB == 1 fe(i,e) = fe(i,e) - gtild(i,e)*k1(i,j(2)); % add integral to f if IEN row % i is in I and row j(2) is in B end else % this is assuming IEN(i,e) is in B binI = ismember(b,I); cinI = ismember(c,I); %check if other entries are in I if binI == 1 fe(i,e) = fe(i,e) - gtild(i,e)*k1(i,j(1)); % add integral to f if IEN row % i is in B and row j(1) is in I end if cinI == 1 fe(i,e) = fe(i,e) - gtild(i,e)*k1(i,j(2)); % add integral to f if IEN row % i is in B and row j(2) is in I end end end end disp('Global K and F...') % Assemble Global K and F o = length(find(ID>0)); % number of nonzero entries of ID matrix - # of rows % of K and F K = zeros(o,o); F = zeros(o,1); % Master loop for e = 1:elements for i = 1:local if LM(i,e) ~= 0 F(LM(i,e)) = F(LM(i,e)) + fe(i,e); for j = 1:local if LM(j,e) ~= 0 K(LM(i,e),LM(j,e)) = K(LM(i,e),LM(j,e)) + k1(i,j); end end end end end disp('Solving Kd = F...') % Solution to Kd = F d = K\F; % Assemble true coefficients of u d_new = zeros(Gnodes,1); points = combvec(xvec,yvec); for i = B d_new(i) = g(points(1,i),points(2,i)); % pretty simple - boundary coefficients of % the solution u should just be g evaluated at that specific point end d_new(I) = d; % add in calculated interior coefficients disp('Creating FEM solution points...')
Create matrix of solution points to plot
horizontal and vertical mesh to evaluate and plot solution
xmesh = linspace(ep1,ep2,resolution); ymesh = linspace(ep1,ep2,resolution); FEM = zeros(resolution); for y = 1:resolution for x = 1:resolution xpoint = xmesh(x); ypoint = ymesh(y); % the points to be evaluated at E = TheElement(gridlines,xpoint,ypoint,ep1,ep2); % corresponding element for i = 1:local globalnum = IEN(i,E); % index for d_new coefficient (global function #) FEM(x,y) = FEM(x,y) ... + d_new(globalnum)*N{i}(zofx{E}(xpoint),wofy{E}(ypoint)); end end end disp('Plotting...') [X, Y] = meshgrid(xmesh,ymesh); EXACT = sin(X)+cos(Y); EXACT = reshape(EXACT, resolution, resolution); figure(1) surf(X,Y, EXACT); rotate3d on; title('Exact Solution'); figure(2) FEM = FEM'; surf(X,Y, FEM); rotate3d on; title('FEM Solution'); FEM = reshape(FEM, resolution^2,1); EXACT = reshape(EXACT, resolution^2,1); error = norm(FEM-EXACT, 'inf')
elements = 242 ID, IEN, LM... Translation functions... Coefficients of boundary function... Local f... Global K and F... Solving Kd = F... Creating FEM solution points... Plotting... error = 0.6933

