%function [u,utrue,errL2,errH1] = fem2d()
%
%  program to solve the equation
%
%     -Laplacian u = f   on  [0,1] X [0,1]
%
%  with boundary conditions
%
%           u(x,0) = u(0,y) = 0
%     u_y(x,1) = 0,  u_x(1,y) = -y(1-.5y)
%
%  using the finite element method with piecewise linear
%  approximation functions.   Uses function f
%  to evaluate the right-hand side.
%
%  User is asked for the number of subintervals n.
%  Routine plots the true and computed solutions and their
%  difference.  Returns the computed solution u, the true
%  solution utrue, the L2-norm of the error errL2, and the
%  H1-seminorm of the error errH1.
%
%  Stiffness matrix is stored as a dense matrix and solved
%  using MATLAB operator "\".
%

n = input(' Enter number of subintervals in each direction: ');
h = 1/n;
nsq = n*n;

%  Loop over squares.  Assemble global stiffness matrix A
%  and right-hand side vector b.

A = zeros(nsq,nsq);  b=zeros(nsq,1);

for j=1:n,                           % Outer loop over y
  yjm1 = (j-1)*h;
  yj = j*h;
  for i=1:n,                         % Inner loop over x
    xim1 = (i-1)*h;
    xi = i*h;

      % Handle two triangles within each square

    index_upperright = (j-1)*n + i;          % Indices of nodes in
    index_upperleft = index_upperright - 1;  % each triangle
    index_lowerright = index_upperright - n;
    index_lowerleft = index_lowerright - 1; 
    area = .5*h^2;                           % Area of each triangle

    if i > 1 & j > 1,           % Lower left node is a free node
      k = index_lowerleft;
      A(k,k) = A(k,k) + area*(1/h)^2;   % Contribution from right triangle
      A(k,k+1) = A(k,k+1) - area*(1/h)^2; 
      A(k,k) = A(k,k) + area*(1/h)^2;   % Contribution from left triangle
      A(k,k+n) = A(k,k+n) - area*(1/h)^2;
    end;
    if j > 1,                   % Lower right node is a free node
      k = index_lowerright;
      A(k,k) = A(k,k) + area*((1/h)^2 + (1/h)^2);
      if i > 1, A(k,k-1) = A(k,k-1) - area*(1/h)^2; end;
      A(k,k+n) = A(k,k+n) - area*(1/h)^2;
    end;
    if i > 1,                   % Upper left node is a free node
      k = index_upperleft;
      A(k,k) = A(k,k) + area*((1/h)^2 + (1/h)^2);
      if j > 1, A(k,k-n) = A(k,k-n) - area*(1/h)^2; end;
      A(k,k+1) = A(k,k+1) - area*(1/h)^2;
    end;
                                % Upper right node is always a free node
    k = index_upperright;
    A(k,k) = A(k,k) + area*(1/h)^2;      % Contribution from right triangle
    if j > 1, A(k,k-n) = A(k,k-n) - area*(1/h)^2; end;
    A(k,k) = A(k,k) + area*(1/h)^2;      % Contribution from left triangle
    if i > 1, A(k,k-1) = A(k,k-1) - area*(1/h)^2; end;

%         Right-hand side vector.  Use second order quadrature formula
%         involving function values at midpoints of each side.

      midptr1 = [.5*(xi+xim1),yjm1];    % Midpoints of sides of right triangle
      midptr2 = [xi,.5*(yj+yjm1)];
      midptr3 = [.5*(xi+xim1),.5*(yj+yjm1)];
      midptl1 = [xim1,.5*(yj+yjm1)];    % Midpoints of sides of left triangle
      midptl2 = [.5*(xi+xim1),yj];
      midptl3 = midptr3;
      fr1 = f(midptr1);                 % Evaluate f at midpoints.
      fr2 = f(midptr2);
      fr3 = f(midptr3);
      fl1 = f(midptl1);
      fl2 = f(midptl2);
      fl3 = f(midptl3);
            
%          Translate triangle to have lower left corner at (0,0).
      midptr1 = midptr1 - [xim1,yjm1];
      midptr2 = midptr2 - [xim1,yjm1];
      midptr3 = midptr3 - [xim1,yjm1];
      midptl1 = midptl1 - [xim1,yjm1];
      midptl2 = midptl2 - [xim1,yjm1];
      midptl3 = midptl3 - [xim1,yjm1];

    if i > 1 & j > 1,           % Lower left node is a free node
      k = index_lowerleft;
      phir1 = 1 - midptr1(1)/h;         % Contribution from right triangle
      phir2 = 1 - midptr2(1)/h;
      phir3 = 1 - midptr3(1)/h;
      b(k) = b(k) + (area/3)*(fr1*phir1 + fr2*phir2 + fr3*phir3);
      phil1 = 1 - midptl1(2)/h;         % Contribution from left triangle
      phil2 = 1 - midptl2(2)/h;
      phil3 = 1 - midptl3(2)/h;
      b(k) = b(k) + (area/3)*(fl1*phil1 + fl2*phil2 + fl3*phil3);
    end;
    if j > 1,                   % Lower right node is a free node
      k = index_lowerright;
      phir1 = (1/h)*midptr1(1) - (1/h)*midptr1(2);
      phir2 = (1/h)*midptr2(1) - (1/h)*midptr2(2);
      phir3 = (1/h)*midptr3(1) - (1/h)*midptr3(2);
      b(k) = b(k) + (area/3)*(fr1*phir1 + fr2*phir2 + fr3*phir3);
    end;
    if i > 1,                   % Upper left node is a free node
      k = index_upperleft;
      phil1 = -(1/h)*midptl1(1) + (1/h)*midptl1(2);
      phil2 = -(1/h)*midptl2(1) + (1/h)*midptl2(2);
      phil3 = -(1/h)*midptl3(1) + (1/h)*midptl3(2);
      b(k) = b(k) + (area/3)*(fl1*phil1 + fl2*phil2 + fl3*phil3);
    end;
                                % Upper right node is always a free node
    k = index_upperright;
    phir1 = (1/h)*midptr1(2);            % Contribution from right triangle
    phir2 = (1/h)*midptr2(2);
    phir3 = (1/h)*midptr3(2);
    b(k) = b(k) + (area/3)*(fr1*phir1 + fr2*phir2 + fr3*phir3);
    phil1 = (1/h)*midptl1(1);            % Contribution from left triangle
    phil2 = (1/h)*midptl2(1);
    phil3 = (1/h)*midptl3(1);
    b(k) = b(k) + (area/3)*(fl1*phil1 + fl2*phil2 + fl3*phil3);

%         Extra boundary term.  Use 2-point Gauss quadrature here.
    if i==n,
      pt1 = .5*(yj+yjm1) - h/sqrt(3);    % Gauss quadrature points
      pt2 = .5*(yj+yjm1) + h/sqrt(3);
      usubn1 = -pt1*(1-.5*pt1);          % u_n at quadrature points
      usubn2 = -pt2*(1-.5*pt2);
      pt1 = pt1 - yjm1;                  % Translate interval
      pt2 = pt2 - yjm1;

      k = index_upperright;              % Upper right basis function
      phi1 = (1/h)*pt1;
      phi2 = (1/h)*pt2;
      b(k) = b(k) + .5*h*(usubn1*phi1 + usubn2*phi2);
      if j > 1,              
        k = index_lowerright;            % Lower right basis function
        phi1 = 1 - (1/h)*pt1;
        phi2 = 1 - (1/h)*pt2;
        b(k) = b(k) + .5*h*(usubn1*phi1 + usubn2*phi2);
      end;
    end;
      
  end;
end;

%  Solve linear system.

u = A\b;

%  Plot true and approximate solutions and error.

u2d = reshape(u,n,n);
u_true = zeros(n,n);
for j=1:n,
  yj = j*h;
  for i=1:n,
    xi = i*h;
    u_true(i,j) = xi*(1-xi)*yj*(1-.5*yj);
  end;
end;

surf(u2d)
xlabel('x')
ylabel('y')
zlabel('ucomp')
pause(5)
figure(2)
surf(u_true)
xlabel('x')
ylabel('y')
zlabel('utrue')
pause(5)
figure(3)
surf(u_true-u2d)
xlabel('x')
ylabel('y')
zlabel('error')
pause(5)

%  Compute (approximate) L2-norm of error.
%  Replace integrals by second order quadrature rule involving
%  function values at the midpoints of sides of triangles.

errL2 = 0;  
for j=1:n,
  yj = j*h;
  yjm1 = (j-1)*h;
  for i=1:n,
    xi = i*h;
    xim1 = (i-1)*h;

    midptr1 = [.5*(xi+xim1),yjm1];    % Midpoints of sides of right triangle
    midptr2 = [xi,.5*(yj+yjm1)];
    midptr3 = [.5*(xi+xim1),.5*(yj+yjm1)];
    midptl1 = [xim1,.5*(yj+yjm1)];    % Midpoints of sides of left triangle
    midptl2 = [.5*(xi+xim1),yj];
    midptl3 = midptr3;
%
    utruer1 = midptr1(1)*(1-midptr1(1))*midptr1(2)*(1-.5*midptr1(2));
    utruer2 = midptr2(1)*(1-midptr2(1))*midptr2(2)*(1-.5*midptr2(2));
    utruer3 = midptr3(1)*(1-midptr3(1))*midptr3(2)*(1-.5*midptr3(2));
    utruel1 = midptl1(1)*(1-midptl1(1))*midptl1(2)*(1-.5*midptl1(2));
    utruel2 = midptl2(1)*(1-midptl2(1))*midptl2(2)*(1-.5*midptl2(2));
    utruel3 = midptl3(1)*(1-midptl3(1))*midptl3(2)*(1-.5*midptl3(2));
%
    if i > 1 & j > 1, 
      ur1 = .5*(u2d(i,j-1)+u2d(i-1,j-1)); 
    elseif i==1 & j > 1,
      ur1 = .5*u2d(i,j-1);
    else
      ur1 = 0;
    end;
    if j > 1, 
      ur2 = .5*(u2d(i,j)+u2d(i,j-1));
    else
      ur2 = .5*u2d(i,j);
    end;
    if i > 1 & j > 1,
      ur3 = .5*(u2d(i,j)+u2d(i-1,j-1));
    else
      ur3 = .5*u2d(i,j);
    end;
    if i > 1 & j > 1,
      ul1 = .5*(u2d(i-1,j)+u2d(i-1,j-1));
    elseif j==1 & i > 1,
      ul1 = .5*u2d(i-1,j);
    else
      ul1 = 0;
    end;
    if i > 1,
      ul2 = .5*(u2d(i,j)+u2d(i-1,j));
    else
      ul2 = .5*u2d(i,j);
    end;
    ul3 = ur3;

    errL2 = errL2 + (area/3)*((utruer1-ur1)^2 + (utruer2-ur2)^2 + ...
                              (utruer3-ur3)^2);
    errL2 = errL2 + (area/3)*((utruel1-ul1)^2 + (utruel2-ul2)^2 + ...
                              (utruel3-ul3)^2);
  end;
end;
errL2 = sqrt(errL2),
