function Uxx_U_Q_L3_GenBC (n_e, show) % Revised 3/02/2006
% File: Uxx_U_Q_L3_GenBC.m for E*Uxx + k*U + Q(x) = 0, E=1=k, plus
% two general EBC or 1 EBC and 1 NBC.  SEE COMMENTS AT THE BOTTOM.
% if U(0)=0=U(L) then U(x) = sin(x)/sin(L) - x
% if U(0)=0, dU/dx(L)=0 then U(x) = sin(x)/cos(L) - x
% if U(0)=0, dU/dx(L)=-0.357907 then U(x) = sin(x)/sin(L) - x
% Ref: J.E. Akin, "FEA w Error Estimators" Elsevier, 2005, page 50  
%.....................................................................
%      x_1=0   x_2    x_3     x_4   x_5= L
%  U_o  *------(1)-----*------(2)------*  U(L)
%       T_1    T_2    T_3     T_4     T_5
% Connectivity list:    e     i      j     k
%                       1     1      2     3  % L3
%                       2     3      4     5  % L3
%.....................................................................
if ( nargin == 1 ) ; 
  show = 0 ;             % flag to show exact solution (if = 1)
elseif ( nargin == 0 ) ; 
  show = 0 ; n_e = 2 ;   % n_e = number of elements
end % if from argument count

%            Set given constants (try changing n_e)
n_g = 1 ;                  % number of DOF per node
n_n = 3 ;                  % number of nodes per element
n_i = n_n*n_g           ;  % number of DOF per element
n_m = 1 + n_e*(n_n - 1) ;  % number of mesh nodes
n_d = n_g*n_m           ;  % system degrees of freedom (DOF)

% specific problem data
L = 1.0 ; L_e = L/n_e               ; % domain and element lengths
E = 1.0 ; k = 1.0                   ; % stiffness data
X = [0:1:(n_d-1)]*L/(n_d-1)         ; % sequential element lengths

%   Allocate (wasteful) storage for BC flags and values
EBC_flag  = zeros(n_m,n_g) ;          NBC_flag  = zeros(n_m,n_g) ; 
EBC_value = zeros(n_m,n_g) ;          NBC_value = zeros(n_m,n_g) ;

%   Manually set specific essential & Neumann bc's (SEE BOTTOM)
EBC_flag (1,   1) = 1 ;      EBC_value (1,   1) = 0.       ;
use_NBC = 1         % user control for BC choice
if ( use_NBC == 1 ) % then use non-zero NBC at x=1
  NBC_flag (n_m, 1) = 1 ;    NBC_value (n_m, 1) = -0.357907 ;
else % use EBC there
  EBC_flag (n_m, 1) = 1 ;    EBC_value (n_m, 1) = 0.       ;
end % if use NBC

%         Constant quadratic element square matrices
K_e = E   * [ 7, -8,  1  ; ...              % axial
             -8, 16, -8  ; ...
              1, -8,  7 ] / (3*L_e)  ;
M_e = L_e * [4, 2, -1 ; ...                 % mass
             2, 16, 2 ; ...
            -1,  2, 4 ] / 30         ;
K_k = k * M_e                             ; % foundation
S_e = K_e - K_k                           ; % net square matrix
Q_e = zeros (n_n,1) ; C_e = zeros (n_n,1) ; % pt & elem sources

%         Assemble n_d by n_d square matrix terms
S = zeros (n_d, n_d) ;  C = zeros (n_d, 1) ; % initalize sums
for j = 1:n_e                              ; % loop over elements
  rows (1:n_i) = [1:n_i] + (n_i-1)*(j-1)   ; % element DOF numbers
  S (rows, rows) = S (rows, rows) + S_e    ; % add to system sq
  Q_e = [X(rows)]'                         ; % nodal source Q(x)=x
  C_e = M_e * Q_e                          ; % element source
  C (rows) = C (rows) + C_e                ; % add to sys column
end % for

% Optional storage allocation for reaction recovery
EBC_count = sum(sum(EBC_flag))               % number of EBC's
NBC_count = sum(sum(NBC_flag))               % number of NBC's
EBC_row   = zeros(EBC_count, n_d) ; EBC_col = zeros(EBC_count, 1) ;
EBC_react = zeros(EBC_count, 1)   ;     % reaction arrays allocated

%  Apply essential BC at nodes, via trick to avoid partitions
EBC_flag  = reshape (EBC_flag,  n_d, 1)        ; % change to vector 
NBC_flag  = reshape (NBC_flag,  n_d, 1)        ; % change to vector 
EBC_value = reshape (EBC_value, n_d, 1)        ; % change to vector 
NBC_value = reshape (NBC_value, n_d, 1)        ; % change to vector 
kount = 0                                      ; % initialize counter
for j = 1:n_d       % check all DOF for essential and natural BC
  if ( NBC_flag(j) )                             % then NBC here
    C (j) = C (j) + NBC_value (j)              ; % add NBC value
  end % if NBC for this DOF

  if ( EBC_flag(j) )                         % then EBC here
    if ( EBC_flag(j) == NBC_flag(j) )        % warning both here
      fprintf ('Usually fatal error: EBC and NBC at same DOF \n')
    end % if fatal BC data

%   Save reaction data to be destroyed by EBC solver trick
    kount = kount + 1                      ; % copy counter
    EBC_row(kount, 1:n_d) = S (j, 1:n_d)   ; % copy reaction data
    EBC_col(kount)        = C (j)          ; % copy reaction data

%   Insert EBC identity to avoid matrix partition accounting
    EBC = EBC_value(j)                ; % recover EBC value
    C (:) = C (:) - EBC * S (:, j)    ; % carry known column to RHS
    S (:, j) = 0 ; S (j, :) = 0       ; % clear, restore symmetry
    S (j, j) = 1 ; C (j) = EBC        ; % insert identity into row
  end % if EBC for this DOF
end % for over all DOF

T = S \ C                           % Solve for unknown values
EBC_react = EBC_row * T - EBC_col   % Reaction from matrix system (+ or -)

% End finite element calculations. ---- Begin graphics ----
 
clf ; hold on ; grid on                         % clear & keep
title ('E*Uxx + k*U + Q(x), E=k=1, Q(x) = x')

%  Plot the exact results
z = [0:0.025:1] * L ;                           % points for graph
if (EBC_count == 2 )                            % for 2 EBC
  exact = sin(z)/sin(L) - z                   ; % analytic
else  % 1 EBC, 1 NBC                            % 1 EBC 1 NBC
  if (NBC_value (n_m) ~= 0 )
    exact = sin(z)/sin(L) - z                   ; % analytic
  else
    exact = sin(z)/cos(L) - z                   ; % analytic
  end % if
end % if true solution
plot (z, exact, 'r.-');  plot (X, T, 'ko')      % exact & nodal
legend ('Exact', 'FEA')                       ; % legend

%        Show BC locations on the plot
k_EBC = 0 ; k_NBC = 0 ;
for j = 1:n_d % check for essential and natural BC
  node  = 1 + (j - 1)/n_g                     ; % node at DOF j
  first = n_g*(node - 1) + 1                  ; % first DOF at node
  if ( EBC_flag(j) )                            % then EBC here
    k_EBC = k_EBC + 1                         ; % counter inc
    b_text = sprintf('--EBC (%i), React: %g', k_EBC, -EBC_react(k_EBC));
    plot (X(node), T(first), 'k*')              % star at EBC node
    text (X(node), T(first), b_text)            % notes at EBC node
  end % if                                      % EBC completed   
  if ( NBC_flag(j) )                            % then NBC here
    k_NBC = k_NBC + 1                         ; % counter inc
    b_text = sprintf('NBC (%i) = %g', k_NBC, NBC_value(j)) ; 
    plot (X(node), T(node), 'k*')              % star at NBC node
    text (X(node)-0.25, T(node), b_text)        % notes at NBC node
  end % if NBC at this node
end % for over all DOF

%  ------------------------------------------------------
%  graph of value in 3 node quadratic line elements
%  ------------------------------------------------------
% e_x, e_y   = coordinates & data values at 3 element nodes
% x_el, y_el = interpolated parametric values

 n_poly = ceil (75/n_e) ; % curve segments
 e_x(3)=0 ; e_y(3)=0 ; H(3)=0 ; % pre-allocation

%    Loop over all L3 elements
  V_X = -realmax ; V_N = realmax ; % for interpolated values
  for it = 1:n_e  ;
    
%      Extract nodal coordinates & values. Plot nodes.
    rows = [1, 2, 3] + (it - 1)*2     ;  % get connectivity 
    e_x  = X (rows) ; e_y  = T (rows) ;  % gather element data
    plot (e_x, e_y, 'ko')                % nodal symbols
    e_text = sprintf ('   (%g)', it)   ; % offset # from pt
    text (e_x(2), e_y(2), e_text)        % Plot element number

%     Loop over local points on the quadratic element
    for k = 1: (n_poly + 1) % points in parametric space
%     get parametric interpolation functions
      Z    = 2*(k - 1)/n_poly - 1            ; % on -1 to 1
      H(1) = 0.5*(Z*Z - Z) ; H(2) = 1. - Z*Z ;
      H(3) = 0.5*(Z*Z + Z) ;
      x_el (k) = dot(H,e_x) ; y_el (k) = dot(H,e_y) ; % true value
    end % for k parametric points in element
    plot (x_el, y_el, 'k-') % parametric curve through 3 nodes
    if ( max(y_el) > V_X )
      V_X = max(y_el) ;     % curve exceeds max node value
    end % if
    if ( min(y_el) < V_N )
      V_N = min(y_el) ;     % curve below min node value
    end % if
  end % for it over all elements
  xlabel (['Position (of ', int2str(n_e), ' elements)']) ; 
  ylabel (['U(x). FE Min, Max = ', ...
            num2str(V_N), ', ', num2str(V_X)])
% end of Uxx_U_Q_L3_GenBC

% ------------- Begin Samples --------------------------------
% if U(0)=0=U(L) then U(x) = sin(x)/sin(L) - x
%  EBC_flag (1,   1) = 1 ;      EBC_value (1,   1) = 0. ;
%  EBC_flag (n_m, 1) = 1 ;      EBC_value (n_m, 1) = 0. ;

% n_e = 1, EBC_react = -0.1898 -0.3565
% T = 0 0.0694 0

% n_e = 3, EBC_react = -0.1884 -0.3579
% T = 0 0.0305 0.0555 0.0697 0.0682 0.0463 0

% if U(0)=0, dU/dx(L)=0 then U(x) = sin(x)/cos(L) - x
%  EBC_flag (1,   1) = 1 ;      EBC_value (1,   1) = 0. ;
%  NBC_flag (n_m, 1) = 1 ;      NBC_value (n_m, 1) = 0. ;

% n_e = 1, EBC_react = -0.8489 
% T = 0 0.3849 0.5540
   
% n_e = 2, EBC_react = -0.1882
% T = 0 0.0439 0.0696 0.0598 -0.0002

% if U(0)=0, dU/dx(L)=-0.357907 then U(x) = sin(x)/sin(L) - x
%  EBC_flag (1,   1) = 1 ;      EBC_value (1,   1) = 0.       ;
%  NBC_flag (n_m, 1) = 1 ;      NBC_value (n_m, 1) = -0.35797 ;

%  n_e = 2, EBC_react = -0.1882
%  T = 0 0.0439 0.0696 0.0598 -0.0002

%  n_e = 4, EBC_react = -0.1883
%  T = 0 0.0231 0.0440 0.0602 0.0697 0.0703 0.0600 0.0370 -0.0001
% -------------- end samples -----------------------------------
