%  Homework on steady heat flow with source
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  Steady diffusion:
%   m-file solves 
%
%       d/dx[ k(x) d_phi/dx ] + S(x) = 0         (1)
%
%  on domain [x_0, x_1] with Boundary Conditions
%    d_phi/dx(x_0) = d_phi_dx_0
%    phi(x_1)      = phi_1
%
%  When S(x) and 1/k(x) are polynomials, there is an
%  analytical solution.
%  This m-file first develops this analytical solution, then
%  generates numerical solutions using 2 finite difference
%  formulations and 1 finite volume formulation.
%
%  User is prompted for the number of nodes Nx to use in the
%  numerical solution.
%
%
%                               E. Waddington  2008-04-25
%  ------------------------------------------------------
%
%  Define the domain
%  -----------------
    x_0 = 0;
    x_1 = 1;
%
%  set number of nodes Nx, and find node spacing dx  
    Nx = input('How many nodes?   ')
    dx = (x_1 - x_0)/(Nx-1);
%
%  set up nodal vector for plotting stuff at nodes
%  -----------------------------------------------
    x_vec = x_0: dx: x_1;
%
%  Set up detailed array x_fine_vec for plotting 
%  exact solutions
%  -----------------
    Nx_fine = 501;
    dx_fine = (x_1 - x_0)/(Nx_fine - 1 );
    x_vec_fine = x_0: dx_fine: x_1;
%
%
%  set Boundary conditions
%  -----------------------
%   specified gradient at x = x_0
      d_phi_dx_0 = 2;
%
%   specified value at x = x_1
      phi_1 = 5;
%

%  ================================
%  First get an analytical solution
%  ================================
%
%  Set the polynomials defining source and conductivity
%  ----------------------------------------------------
%   1/k(x) conductivity
      k_inv =[ 2 -2 1];
%
%   S(x) source
      S = 100*[ 2 -3 1 0];   
%
%
%  Integrate (1) from x_0 to x to find d_phi_dx,
%   then divide by k(x).
%  Boundary Condition d_phi_dx_0 appears naturally.
%
%   d_phi/dx =
%      [k(x_0) d_phi_dx_0 - int_(x_0)^x S(x') dx'] /k(x) (2)
%
%   The first term ( k(x_0) d_phi_dx_0 ) is flux q_0 at x_0  
%  --------------------------------------------------------
    q_0 = - d_phi_dx_0 / polyval(k_inv, x_0);
%
%  Integrate the source polynomial S(x)
%   Result S_int is also a polynomial 
%  ------------------------------------
    S_int = polyint(S);
%
%  Put q_0 and S_int (evaluated at the limits [x_0, x]) into (2)
%
%   d_phi/dx = [- q_0 - S_int(x) + S_int(x_0)] /k(x)   (3)
%
%   RHS of (3) is also a polynomial in x. 
%   We need to incorporate the constant terms into the polynomial
%   const_1 is a constant "polynomial"
%   (its only nonzero coefficient is coefficient of x^0)
%  --------------------------------------------------------
     const_1 =zeros( size(S_int) );
     const_1(end) = - polyval(S_int, x_0 ) + q_0;
%
%   Add const_1 to S_int(x) and divide by k(x) to get 
%   polynomial representation of d_phi/dx in (3)
%   ----------------------------------------------
     d_phi_dx = - conv( (S_int + const_1), k_inv );
%
%
%  Our other BC is at x_1, so now integrate d_phi_dx
%  from x to x_1 using phi_1 as BC. 
%   Result is phi(x) (also a polynomial) evaluated at 
%   the limits x_1 and x.
%
%   phi(x_1) - phi(x) = polyint(d_phi_dx)
%                       - polyval(polyint(d_phi_dx), x_1)
%
%  where the second BC phi(x_1) = phi_1 has appeared naturally
%  phi_const is constant polynomial used to add the constant 
%   term to phi polynomial
%     phi_const = desired value at x_1 - natural value of
%                 phi at x_1 
%  Natural polynomial phi(x) goes through origin, by construction
%  -------------------------------------------------------------
    phi_natural    = polyint(d_phi_dx);
    phi_const      = zeros( size(phi_natural) );
    phi_const(end) = phi_1 - polyval(phi_natural, x_1);
    phi = phi_natural + phi_const;
%

%  ===========
%  plot stuff
%  ===========
%
%  k(x)
%  ----
    k_vec      = 1./ polyval(k_inv, x_vec);
    k_vec_fine = 1./ polyval(k_inv, x_vec_fine);
%
%  Source S(x)
%  -----------
   S_vec      = polyval(S, x_vec);
   S_vec_fine = polyval(S, x_vec_fine);
%
%  solution phi(x)
%  ---------------
    phi_vec = polyval(phi, x_vec);
    phi_vec_fine = polyval(phi, x_vec_fine);
%
%
%
%  Conductivity k(x)
%  ------------------
    figure(1)
    hold on; grid on;
    plot(x_vec_fine, k_vec_fine, 'b-' )
    plot(x_vec, k_vec, 'rx' )
    xlabel('distance x')
    ylabel('conductivity   k(x)')
    set(gca,'ylim', [0,2])
%
%  Source  S(x)
%  ------------
    figure(2)
    hold on; grid on;
    plot(x_vec_fine, S_vec_fine, 'b-' )
    plot(x_vec, S_vec, 'rx' )
    xlabel('distance x')
    ylabel('source   S(x)')
%
%  Solution phi(x)
%  ---------------
    figure(3)
    hold on; grid on;
    plot(x_vec_fine, phi_vec_fine, 'k')
  %  plot(x_vec, phi_vec, 'ko')
    xlabel('distance  x')
    ylabel('solution   \phi(x)')
    title(' k- exact solution; r*-- fd solution #1; g+-- fd solution #2; bx-- fv solution')
%
%
%  ====================================================   
%    Now solve Equation (1) numerically with 3 schemes
%  ====================================================
%
%  Set up first 1-D finite-difference solution
%  ===========================================
%
%   Use product rule on derivatives term
%   -------------------------------------------
%  d/dx[k(x) d/dx(phi(x)) ] = dk/dx d_phi/dx + k(x) d^2phi/dx^2
%
%  Each derivative is then represented in standard fd way
%    [df/dx]_i = ( f_{i+1} - f_{i-1} )/ (2 dx_i)
%  
%    d^2_f/dx^2 = ( f_{i+1} - 2 f_i + f_{i-1} )/ dx_i^2
%  --------------------------------------------------
%
%  Initialize A matrix and RHS vector with all elements zero
%  ---------------------------------------------------------
    A_fd1 = zeros( length(x_vec) );
    RHS_fd1 = zeros(size(x_vec) )';
%
%  Put in the nonzero coefficients for interior points. 
%  This operation could be vectorized to avoid the loops,
%    but loops are easier to program quickly
%  ------------------------------------------
    for i = 2:length(x_vec)-1
        A_fd1(i,i-1) = ...
            ( k_vec(i) - ( k_vec(i+1) - k_vec(i-1) )/4 )/dx^2;
        A_fd1(i,i)   = -2*k_vec(i)/dx^2;
        A_fd1(i,i+1) = ...
            ( k_vec(i) + ( k_vec(i+1) - k_vec(i-1) )/4 )/dx^2;
  %      
        RHS_fd1(i) = - S_vec(i);
    end
%
%  Include boundary conditions
%  ---------------------------
%   At x_0 boundary point:  [ phi(2) - phi(1) ]/dx = d_phi_dx_0
       A_fd1(1,1) = -1/dx;
       A_fd1(1,2) =  1/dx;
       RHS_fd1(1) = d_phi_dx_0;
%
%   At x_1 boundary point:  phi(x_1) = phi_1
      A_fd1(end,end) = 1;
      RHS_fd1(end) = phi_1;                       ;
%
%
%   Now solve the finite-difference equations
%  ------------------------------------------
      phi_fd1_vec = A_fd1 \ RHS_fd1;

%
%   Add phi_fd1_vec to figure(3)
%  -----------------------------
     figure(3)
     plot(x_vec, phi_fd1_vec, 'r*--')
%
%
%
% Second fd solution
% ===================
%  Use first-order approximation to derivative term
%   [df/dx]_i = ( f_{i+1} - f_{i-1} ) /(2 dx)
%
%  So
%    d/dx[ k(x) d/dx(phi(x)) ] = 
%       k_{i+1} [d_phi/dx]_{i+1} - k_{i-1} [d_phi/dx]_{i-1} 
%
%  Then use first-order approximation on [d_phi_dx]_{i-1} etc
%    =  phi_{i-2} ( k_{i-1} /(4 dx^2) )
%     + phi_i     ( k_{i-1} - k_{i+1} ) /(4 dx^2)
%     + phi_{i+2} ( k_{i+1} /(4 dx^2) )  
%  --------------------------------------------------------------
%
%  Initialize matrices
%  -------------------
    A_fd2 = zeros( length(x_vec) );
    RHS_fd2 = zeros(size(x_vec) )';
%
%  Now, put in coefficients for interior points
%  --------------------------------------------
    for i = 3:length(x_vec)-2
        A_fd2(i,i-2) = k_vec(i-1)/(4*dx^2);
        A_fd2(i,i)   = - (k_vec(i+1) + k_vec(i-1)) / (4*dx^2);
        A_fd2(i,i+2) = k_vec(i+1)/(4*dx^2);
        
        RHS_fd2(i) = - S_vec(i);
    end
%
%  Put in coefficients for flux boundary at x_0.
%  With this fd approximation, which stretches over 5 grid points,
%  we need 2 equations for each boundary.
%   --------------------------------------
%   At x_0 boundary point:  [ phi(2) - phi(1) ]/dx = d_phi_dx_0
%   Same as in method #1
       A_fd2(1,1) = -1/dx;
       A_fd2(1,2) =  1/dx;
       
       RHS_fd2(1) = d_phi_dx_0;
%
%     At adjacent interior point:  
%     *** explain this ***
       A_fd2(2,1) = -k_vec(3)/(4*dx^2);
       A_fd2(2,3) =  k_vec(3)/(4*dx^2);
       
       RHS_fd2(2) = - S_vec(2) + d_phi_dx_0 * ( k_vec(1)/(2*dx) );
       
%
%   put in coefficients for fixed phi_1 at x_1 boundary
%   ---------------------------------------------------
 %  
%   x_{end-1} equation     
      A_fd2(end-1,end)   =  k_vec(end) /(1.5*dx^2);
      A_fd2(end-1,end-1) = - ( 2*k_vec(end) + k_vec(end-2) ) /(3*dx^2); 
      A_fd2(end-1,end-3) =  k_vec(end-2) /(3*dx^2); 
%         
      RHS_fd2(end-1) = - S_vec(end-1);        
%
%    x_end equation
      A_fd2(end,end) = 1;
      RHS_fd2(end) = phi_1;
%
%
%
%   Now solve the finite-difference equations
%  ------------------------------------------
      phi_fd2_vec = A_fd2 \ RHS_fd2;

%
%   Add phi_fd2_vec to figure(3)
%  -----------------------------
     figure(3)
     plot(x_vec, phi_fd2_vec, 'g+--')
%
%
%
%  Now set up finite-volume solution
%  ==================================
%
%  get interface conductivities k_e and k_w
%   using gemetric mean (Patankar, p. 44)
%  -----------------------------------------
      k_E = [ k_vec(2:end) k_vec(end) ];
      k_P = k_vec;
      k_W = [ k_vec(1) k_vec(1:(end-1) ) ];
      k_e = 2 * k_P .* k_E ./ (k_E + k_P); 
      k_w = 2 * k_P .* k_W ./ (k_W + k_P); 
%
%  get source vector
%  -----------------
      S_C = S_vec;
%
%  get coefficients for interior points
%  ------------------------------------
      a_E = k_e ./ dx;
      a_W = k_w ./ dx;
      a_P = a_E + a_W; 
      b   = S_C .* dx;
%
%   stuff coefficients into matrix A_fd2
%  -------------------------------------
      A_fvm   = zeros(length(x_vec) );
      RHS_fvm = zeros(size(x_vec))';
%
      for i=2:length(x_vec)-1
          A_fvm(i,i)   = -a_P(i);
          A_fvm(i,i-1) =  a_W(i);
          A_fvm(i,i+1) =  a_E(i);
          
          RHS_fvm(i)  = -b(i);
      end
%
%  now set up flux BC at x_0
   %  q_B - k_W(2) * ( phi(2) - phi(1) ) / dx + S_C(1) * dx = 0
% -------------------------------------------------------------
     A_fvm(1,1) = -k_w(2)/dx;
     A_fvm(1,2) =  k_w(2)/dx;
     RHS_fvm(1) = -(S_C(1)*dx - k_P(1) * d_phi_dx_0 );
%
%  now set up set phi at x_1
%  -------------------------
     A_fvm(end,end) = 1;
     RHS_fvm(end)   = phi_1;
%
%  Solve it!
%  ----------
     phi_fvm_vec = A_fvm \ RHS_fvm;
%

%   Add plot to figure(3)
%  ----------------------
    figure(3)
    plot( x_vec, phi_fvm_vec, 'bx--')
%
%
%  Now set up finite element solution
%  ----------------------------------
%   ha ha ha !!
%   Later, maybe...
%
%
%