function fem1dlinear % This function solves the 1D BVP % -au'' + bu' + cu = f in (x0, x1) % with either Dirichlet or Neumann boundary conditions at x0 and x1. % % The problem is solved using a finite element method % using the continuous piecewise linear approximation space. % % % Variables % ========================================= % coefficients: a structure containing three coefficients a,b,c in the equation. % They are stored in coefficients.a, coefficients.b, coefficients.c % % leftbdr, rightbdr: Two structures defining the boundary conditions on x0 and x1. % Each contains three components, we use leftbdr as an example: % leftbdr.x = x0 % leftbdr.type is either 'Dirichlet' or 'Neumann' % leftbdr.value: for Dirichlet boundary condition, it is % the value of u(x0); for Neumann boundary % condition, it is the value of u'(x0) % The definition of rightbdr is similar. % % f: the right-hand function. It is defined using "function handle". % See matlab help for more about function handle. % % n: number of sub-intervals. % % x: A row vector containing the x-coordinates of all grid nodes. % It needs to be monotonically increasing, and x(1) = x0, x(end) = x1. % % A, rhs, u: the linear system derived from the finite element approximation is % A u = rhs % % % Subroutines % ========================================= % assembleLocalMatrix % assembleLocalRhs % % ===================================================================== % PART I: initialization % ===================================================================== % In this part, we set up the problem. % You can change the settings here in order to solve a different problem. % setting coefficients coefficients.a = 1; coefficients.b = 5; coefficients.c = 5; % set left boundary condition leftbdr.x = 0; leftbdr.type = 'Dirichlet'; leftbdr.value = 0; % set right boundary condition rightbdr.x = 1; rightbdr.type = 'Neumann'; rightbdr.value = 0; % set right-hand side function f f = @(x) 12-5*x.*x; % set number of intervals and generate grid % For now, a uniform grid is generated. % One can rewrite this part to generate a non-uniform grid. n = 20; x = linspace(leftbdr.x, rightbdr.x, n+1); % ===================================================================== % PART II: finite element discretization % ===================================================================== % In this part, we assemble the linear system using the finite element method. % Then solve the linear system. % assemble matrix A A = sparse(n+1,n+1); for i=1:n % on each sub-interval [x(i), x(i+1)] localmat = assembleLocalMatrix(x(i),x(i+1),coefficients); A(i:i+1,i:i+1) = A(i:i+1,i:i+1) + localmat; end % assemble right-hand side vector rhs = (f,v) rhs = zeros(n+1,1); for i=1:n % on each sub-interval [x(i), x(i+1)] localrhs = assembleLocalRhs(x(i),x(i+1),f); rhs(i:i+1) = rhs(i:i+1) + localrhs; end % apply left boundary condition if strcmp(leftbdr.type, 'Dirichlet') % make the first row (1,0,0,...,0), and set rhs(1) A(1,:) = 0; A(1,1) = 1.0; rhs(1) = leftbdr.value; % change 2:end entries of rhs, % then erase 2:end entries in the first column of A rhs(2:end) = rhs(2:end) - A(2:end,1) * leftbdr.value; A(2:end,1) = 0; else % Neumann bdr condition rhs(1) = rhs(1) - coefficients.a * leftbdr.value; end % apply right boundary condition if strcmp(rightbdr.type, 'Dirichlet') % make the last row (0,...,0,0,1), and set rhs(end) A(end,:) = 0; A(end,end) = 1.0; rhs(end) = rightbdr.value; % change 1:end-1 entries of rhs, % then erase 1:end-1 entries in the last column of A rhs(1:end-1) = rhs(1:end-1) - A(1:end-1,end) * rightbdr.value; A(1:end-1,end) = 0; else % Neumann bdr condition rhs(end) = rhs(end) + coefficients.a * rightbdr.value; end % solve u = A \ rhs; % ===================================================================== % PART III: print out the result % ===================================================================== % for the current problem, we know that its exact solution is % u = x(1-x) plot(x, u, 'b', x, x.*(2-x), 'r+'); legend('FEM solution', 'Exact solution'); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % This is the end of the main function. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % ===================================================================== % PART IV: subroutines % ===================================================================== % This part contains some sub-functions. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % This is to compute (au',v') + (bu',v) + (cu,v) % on the subinterval [a,b] function localA = assembleLocalMatrix(a,b,coefficients) h = b-a; localA = coefficients.a / h * [1, -1; -1, 1] ... + coefficients.b * [-0.5, 0.5; -0.5, 0.5] ... + coefficients.c * h * [1/3, 1/6; 1/6, 1/3]; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % This is to compute (f,v) on the subinterval [a,b]. % A Gaussian quadrature will be used to evaluate the % integral. Here we use a 5th order scheme. You can % change "5" into what ever order you would like to use. function localrhs = assembleLocalRhs(a,b,f) % First, compute the Gaussian points and weights. % Both gaussianpoints and gaussianweights are column vectors. [gaussianpoints, gaussianweights] = lgwt(5,a,b); % evaluate the value f on Gaussian points % fvalue is a column vector fvalue = f(gaussianpoints); % evaluate the value of two basis functions % (b-x)/(b-a) and (x-a)/(b-a) % on gaussianpoints. % "shape" is a matrix with two columns, each column % contains the value of one basis function on gaussianpoints. h = b-a; shape = [ (b-gaussianpoints)/h, (gaussianpoints-a)/h ]; % apply the gaussian quadrature to compute localrhs localrhs = shape' * (fvalue.*gaussianweights);