% polycond_CSTR.m
% This program plots the number average chain
% length and polydispersity of a polymer
% produced by polycondensation (with balanced
% end group concentrations) in a CSTR.
% Input values:
% zeta = dimensionless ratio of monomer and condensate
%        molecular weights
% K_eq = equilibrium constant of reaction
% omega_in = dimensionless concentration of water in inlet
% mu_0_in = inlet value of zeroth moment of dimensionless
%        chain length distribution
% mu_2_in = inlet value of second moment of
%        dimensionless chain length distribution
% K.J. Beers. 2/20/2005.  ver. 2/21/2005.B.

function [iflag_main,Grid] = polycond_CSTR();
iflag_main = 0;

% ask user to input values of fixed system parameters
SysFixed = get_SysFixed();

% ask user to input simulation parameters
Sim = get_Sim();

% set values to scan of two variable parameters,
% gamma (the relative importance of mass transfer
% of water out of the reactor) and Da (the
% Damkoehler number).
Grid.gamma = logspace(Sim.gamma_log10_lo,...
    Sim.gamma_log10_hi,Sim.N);
Grid.Da = logspace(Sim.Da_log10_lo, ...
    Sim.Da_log10_hi,Sim.N);
% allocate memory to store results
Grid.xn = zeros(Sim.N,Sim.N);
Grid.Z = zeros(Sim.N,Sim.N);
Grid.omega = zeros(Sim.N,Sim.N);
Grid.phi = zeros(Sim.N,Sim.N);

% for each combination of the two parameters,
% compute the steady-state properties of the
% polymer produced by the CSTR.
Options = optimset('Largescale','off', ...
    'Display','off','TolFun', Sim.TolFun);

for ig = 1:Sim.N
    disp(' ');
    disp(['gamma iteration # ', int2str(ig)]);
    for iDa = 1:Sim.N
        disp(['   Da iteration # ', int2str(iDa)]);
        % complete structure of system
        % parameters with current values of
        % variable parameters
        Sys = SysFixed;
        Sys.gamma = Grid.gamma(ig);
        Sys.Da = Grid.Da(iDa);
        % get initial guess of parameters
        % for highest Da value, use values
        % of moments for inlets.  Otherwise,
        % use results of last computation.
        if(iDa == 1)
            x_0 = [Sys.mu_0_in; Sys.mu_2_in; ...
                Sys.omega_in];
            % if any of these are less than
            % trace, reset initial guess to
            % 2*trace
            k_too_lo = find(x_0 < Sim.trace);
            if(k_too_lo)
                x_0(k_too_lo) = 2*Sim.trace;
            end
        else
            x_0 = x;
        end
        % before starting Newton's method, try
        % running dynamic simulation to relax
        % the system.
        x_0 = relax_dyn(x_0, Sys, Sim);
        % calculate the zeroth and second moment
        % of the chain length distribution at
        % steady state
        [x,f,exitflag] = ...
            fsolve(@calc_f, x_0, Options, Sys, Sim);
        f_norm = norm(f,inf);
        if(exitflag ~= 1)
            phrase = ['FSOLVE(', int2str(ig), ...
                ',', int2str(iDa), ...
                '): exitflag = ', ...
                int2str(exitflag), ...
                ', ||f|| = ', num2str(f_norm)];
            if(Sim.isterminal)
                error(phrase);
            else
                disp(phrase);
            end
        else
            disp(['      ', ...
                '||f|| = ', num2str(f_norm)]);
        end

        % extract values from state vector
        mu_0 = x(1);  mu_2 = x(2);  omega = x(3);
        % calculate values of auxilliary variables
        [phi,mu_3] = calc_aux_var(mu_0,mu_2,omega,Sys);
        % compute number and weight average chain
        % lengths and the polydispersity
        xn = 1/mu_0;  xw = mu_2;  Z = xw/xn;
        % store results
        Grid.xn(iDa,ig) = xn;
        Grid.Z(iDa,ig) = Z;
        Grid.omega(iDa,ig) = omega;
        Grid.phi(iDa,ig) = phi;
    end
end

% plot the results
plot_results(Grid);

iflag_main = 1;
return;


% ====================
% This routine asks the user to input the values
% of the fixed system parameters.
function SysFixed = get_SysFixed();

disp('Input fixed system parameters ...');

disp(' ');
disp('zeta is the dimensionless ratio of condensate');
disp('to monomer molecular weights (0.2).');
SysFixed.zeta = input('Enter zeta : ');

disp(' ');
disp('K_eq is the equilibrium constant (100) : ');
SysFixed.K_eq = input('Enter K_eq : ');

disp(' ');
disp('omega_in is the dimensionless concentration of');
disp('condensate in the inlet stream (0).');
SysFixed.omega_in = input('Enter omega_in : ');

disp(' ');
disp('mu_0_in is the dimensionless zeroth moment of the');
disp('chain length distribution.  It is one for momomer feed.');
SysFixed.mu_0_in = input('Enter mu_0_in : ');

disp(' ');
disp('mu_2_in is the dimensionless second moment of the');
disp('chain length distribution.  It is one for monomer feed.');
SysFixed.mu_2_in = input('Enter mu_2_in : ');

return;


% ====================
% This routine asks the user to input the
% simulation parameters.
function Sim = get_Sim();

disp(' ');
disp('Next, enter simulation parameters ...');
disp(' ');

Sim.TolFun = input(...
    'Enter TolFun (1e-12) : ');
Sim.trace = input(...
    'Enter trace conc. level (1e-15) : ');
Sim.isterminal = input(...
    'Stop calculations when exitflag~=1 (0=n, 1=y) : ');
Sim.f_norm_relax = input(...
    'Enter ||f|| at which to stop dynamic relaxation : ');
Sim.gamma_log10_lo = input(...
    'Enter low log10(gamma) (-4) : ');
Sim.gamma_log10_hi = input(...
    'Enter high log10(gamma) (2) : ');
Sim.Da_log10_lo = input(...
    'Enter low log10(Da) (-2) : ');
Sim.Da_log10_hi = input(...
    'Enter high log10(Da) (3) : ');
Sim.N = input(...
    'Enter # of grid points in 1-D (10) : ');

return;


% ====================
% This routine performs a dynamics simulation of the
% system until the function norm drops below a
% specified value.
function x = relax_dyn(x_0, Sys, Sim);

% simulate in dimensionless time periods of 1
% until the function norm drops below the
% specified max. allowable value.
for k=1:100
    [t_traj,x_traj] = ...
        ode23s(@calc_xdot, [0:0.5:1], x_0, [], ...
            Sys, Sim);
    x = x_traj(3,:)';
    f = calc_f(x, Sys, Sim);
    f_norm = norm(f,inf);
    if(f_norm <= Sim.f_norm_relax)
        disp('      relaxed dynamics');
        break;
    end
    x_0 = x;
    k_too_lo = find(x_0 < Sim.trace);
    if(k_too_lo)
        x_0(k_too_lo) = 2*Sim.trace;
    end
end
    
return;


% ====================
% This returns the time derivative vector
% for the dynamic relaxation simulation.
function xdot = calc_xdot(t, x, Sys, Sim);

xdot = calc_f(x, Sys, Sim);

return;


% ====================
% This routine results the function vector for
% the polycondensation CSTR.
function f = calc_f (x, Sys, Sim);

f = zeros(3,1);

% allocate moments
mu_0 = x(1);  mu_2 = x(2);  omega = x(3);
% ensure positivity
if(mu_0 < Sim.trace)
    f(1) = Sim.trace - mu_0;  % to increase value
    mu_0 = Sim.trace;
end
if(mu_2 < Sim.trace)
    f(2) = Sim.trace - mu_2;  % to increase value
    mu_2 = Sim.trace;
end
if(omega < Sim.trace)
    f(3) = Sim.trace - omega;  % to increase value
    omega = Sim.trace;
end

% calculate values of auxilliary variables
[phi,mu_3] = calc_aux_var(mu_0,mu_2,omega,Sys);

% compute function values, if not done above
% when values are below trace
% mu_0 balance
if(~f(1))
    f(1) = Sys.mu_0_in - phi*mu_0 - Sys.Da*(mu_0^2) ...
        + Sys.Da/Sys.K_eq*omega*(1 - mu_0);
end
% mu_2 balance
if(~f(2))
    f(2) = Sys.mu_2_in - phi*mu_2 + 2*Sys.Da ...
        + Sys.Da/Sys.K_eq*omega*(1/3 - mu_3);
end
% omega balance
if(~f(3))
    f(3) = Sys.omega_in - phi*omega ...
        - Sys.gamma*omega ...
        + Sys.Da*(mu_0^2) ...
        - Sys.Da/Sys.K_eq*omega*(1 - mu_0);
end

return;


% ==========
% This routine computes the values of auxilliary
% variables.
function [phi, mu_3] = ...
    calc_aux_var(mu_0, mu_2, omega, Sys);

% relative outlet flow rate to inlet flow rate
phi = 1 - Sys.gamma*Sys.zeta*omega;

% compute third moment from closure approximation
mu_3 = mu_2*(2*mu_2*mu_0 - 1)/mu_0;

return;


% ====================
% This routine plots the results of the
% calculation to a figure.
function iflag = plot_results(Grid);
iflag = 0;

x_vals = log10(Grid.gamma);
y_vals = log10(Grid.Da);

figure;
subplot(2,2,1);  surf(x_vals,y_vals,Grid.xn);
xlabel('log_{10} \gamma');  ylabel('log_{10} Da');
zlabel('x_n');  axis tight;

subplot(2,2,2);  surf(x_vals,y_vals,Grid.Z);
xlabel('log_{10} \gamma');  ylabel('log_{10} Da');
zlabel('Z');  axis tight;

subplot(2,2,3);  surf(x_vals,y_vals,Grid.omega);
xlabel('log_{10} \gamma');  ylabel('log_{10} Da');
zlabel('\omega');  axis tight;

subplot(2,2,4);  surf(x_vals,y_vals,Grid.phi);
xlabel('log_{10} \gamma');  ylabel('log_{10} Da');
zlabel('\phi');  axis tight;

iflag = 1;
return;
