% 10.34 - Fall 2006
% Hw Set #6
% Rob Ashcraft - Oct. 12, 2006

% Chemostat problem with unscaled variables.  ODE solvers are used to
% determine the transient and steady-state behavior of a chemostat

function pset6p1_main

clear; clc;

% define the parameter values for the seed reactor
params = param_set;

V_flow_quiz1 = 2.3/60;  % liters/sec
Vrxtr_quiz1 = 150;   % liters
C_nut_in_quiz1 = 0.2;  % in Molar

%guess an initial condition from which to integrate to SS
Ncells_0 = 1e5; 
C_nut_0 = 0.05; 
C_P_0 = 0.05;

var_0 = [Ncells_0, C_nut_0, C_P_0];

options = odeset('RelTol',1e-10,'AbsTol',1e-12);
%options = [];

% Integrate from 0 --> 100 hours, which should reach SS, but will also be
% checked by visually inspecting a graph.
tspan = [0 100*3600];  %in seconds

% Solve the ODE for the seed reactor with the given parameters
[time, var] = ode15s(@chemostat,tspan,var_0,options,C_nut_in_quiz1,params,V_flow_quiz1,Vrxtr_quiz1);

% report the steady-state conditions by taking the conditions as the final
% time point in the integration
Ncells_SS = var(end,1);
C_cells_SS = Ncells_SS/Vrxtr_quiz1;
Nut_SS = var(end,2);
P_SS = var(end,3);

disp('  ');
disp('Steady-State Solution to the 150 L CSTR');
disp('  ');
disp(['Number of Cells in Reactor = ',num2str(var(end,1),'%8.6e')]);
disp(['Concentration of Cells in Reactor = ',num2str(C_cells_SS,'%8.6e')]);
disp(['Concentration of Nutrients (M) = ',num2str(var(end,2))]);
disp(['Concentration of Product (M) = ',num2str(var(end,3))]);
disp('  ');
disp(['Production Rate of Product (mole/hr) = ',num2str(var(end,3)*V_flow_quiz1*3600)]);
%resid_final = chemostat(var,C_nut_in,params)


% move on to the 230L reactor that will be seeded with output from the 150L
% reactor that we just solved.

% Define the times at which the flow will begin.  1e-6 was used as a
% surrogate for zero so the method below will work for all cases.
t_nut_vec = [1e-6, 2, 8];  % in hours

% define the parameters used in the 230L reactor
V_flow_p1 = 2.3/60;  % liters/sec
Vrxtr_p1 = 230;   % liters

C_nut_in_p1 = 0.2;  % in Molar

%initial conditions in the 230 L reactor.  Generally, concentrations are calculated
%as the number of moles of X in the one liter aliquot, then added to the
%number of moles in the 229L of nutrient solution, then divided by 230L.
Ncells_0 = Ncells_SS/150;  % Ncells in one liter of seed reactor mixture
C_nut_0 = (C_nut_in_p1*229 + Nut_SS*1)/230; 
C_P_0 = P_SS/230;

var_0 = [Ncells_0, C_nut_0, C_P_0];

disp('  ');
disp('  ');
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
disp('Using the Explicit Solver (ode23s)');
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
disp('  ');

%iterate over the t_nut vector to do each case
for i=1:length(t_nut_vec)
        t_nut = t_nut_vec(i);  %pick the appropriate t_nut value

        % for the first leg, we will integrate for zero to t_nut, and set
        % the nutrient flow rate to zero over this time period.  
        tspan1 = [0 t_nut*3600];
        V_flow_p1_0 = 0;
    
        time_in = cputime;
        [time2, var2] = ode23s(@chemostat,tspan1,var_0,options,C_nut_in_quiz1,params,V_flow_p1_0,Vrxtr_p1);

        % next, we integrate from t_nut to t_final and set the flow rate to
        % the value specified in the problem.  The initial condition for
        % this ODE system is the final values return by the previous step
        tspan1 = [t_nut*3600 100*3600];
        var_0_1 = [var2(end,1), var2(end,2), var2(end,3)];
        V_flow_p1_1 = V_flow_p1;

        [time3, var3] = ode23s(@chemostat,tspan1,var_0_1,options,C_nut_in_quiz1,params,V_flow_p1_1,Vrxtr_p1);
        
        time_int = cputime - time_in;
        
        % combine the 1st and 2nd step into single vectors
        time_all = [time2; time3];
        var_all = [var2; var3];
        
        % The determine the number of steps taken in each 100-second
        % period, we need to divide the space up into equal bins of this
        % length.  This is done by using linspace to split T into T/100+1
        % points (between which are T/100 bins).
        t_histo = linspace(0,25*3600,25*3600/100+1);
        for k = 1:length(t_histo)
            if(k == 1)
                n_steps_0 = 0; % there are no steps before time = 0
            else
                % n_step_0 is the number steps taken before the current
                % time point.  
                n_steps_0 = n_steps_1(k-1);
            end
            % this finds the total number of steps taken before the current
            % time point
            n_steps_1(k) = length(find(time_all < t_histo(k)));
            
            % the number of steps in this time interval is found by
            % subtracting the number of steps before the previous time
            % point for the number of steps before the current time point
            n_steps(k) = n_steps_1(k) - n_steps_0;
        end
              
        % this plots the Ncell and n_steps on the same plot with two axes.
        % It is a bit complicated, so I will not explain it here
        figure;
        [AX,H1,H2] = plotyy(time_all/3600,var_all(:,1),t_histo/3600,n_steps,'loglog','semilogx');
        axis([1e-2 25 0 1]); axis 'auto y';
        set(H1,'LineStyle','-');
        set(H2,'Marker','o','LineStyle','none');
        set(AX(2),'xlim',[1e-2 25]);
        set(AX(1),'yTickMode','auto')
        set(AX(2),'yTickMode','auto')
        xlabel('Time  [hr]');
        set(get(AX(1),'YLabel'),'String','Number of Cells in CSTR');
        set(get(AX(2),'YLabel'),'String','Number of Time Steps in Bin');
        title(['Stiff ODE Solver (ode23s) with t_n_u_t = ',num2str(t_nut,'%2.0f'),' hrs']);


        % find the steady state values of the 230L reactor
        Ncells_SS_p1 = var_all(end,1);
        C_cells_SS_p1 = Ncells_SS_p1/Vrxtr_p1;
        Nut_SS_p1 = var_all(end,2);
        P_SS_p1 = var_all(end,3);
        
        % convert the Ncells vector to a percent of the steady state value.
        percent_SS = var_all(:,1)/Ncells_SS_p1;
        
        % find the vector indices where the Ncells is within 1% of the
        % long-time SS value
        [loc_99] = find(percent_SS < 0.99 | percent_SS > 1.01);
        
        % find the final time point that lies outside the 1% range, then take
        % the next time point as the time to reach 1% of SS
        [time_99] = time_all(max(loc_99)+1);


        disp(['For Time(flow start) = ',num2str(t_nut,'%2.2f'),' hours:']);
        disp('  ');
        disp(['Number of Cells in Reactor = ',num2str(var_all(end,1),'%8.6e')]);
        disp(['Concentration of Cells in Reactor = ',num2str(C_cells_SS_p1,'%8.6e')]);
        disp(['Concentration of Nutrients (M) = ',num2str(var_all(end,2))]);
        disp(['Concentration of Product (M) = ',num2str(var_all(end,3))]);
        disp('  ');
        disp(['Production Rate of Product (mole/hr) = ',num2str(var_all(end,3)*V_flow_quiz1*3600)]);
        disp('  ');
        disp(['Time to 99% of Steady-State (sec) = ',num2str(time_99)]);
        disp(['Time to Solve ODE System (sec) = ',num2str(time_int)]);
        disp(['Number of Integrator Steps Taken = ',num2str(length(time_all))]);
        disp('  ');
        disp('  ');
end

disp('  ');
disp('  ');
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
disp('Using the Explicit Solver (ode45)');
disp('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%');
disp('  ');

% this is basically identical to the above procedure 
for i=1:length(t_nut_vec)
        t_nut = t_nut_vec(i);

        tspan1 = [0 t_nut*3600];
        V_flow_p1_0 = 0;
    
        time_in = cputime;
        [time2, var2] = ode45(@chemostat,tspan1,var_0,options,C_nut_in_quiz1,params,V_flow_p1_0,Vrxtr_p1);

        tspan1 = [t_nut*3600 100*3600];
        var_0_1 = [var2(end,1), var2(end,2), var2(end,3)];
        V_flow_p1_1 = V_flow_p1;

        [time3, var3] = ode45(@chemostat,tspan1,var_0_1,options,C_nut_in_quiz1,params,V_flow_p1_1,Vrxtr_p1);
        
        time_int = cputime - time_in;
        
        time_all = [time2; time3];
        var_all = [var2; var3];
        

        t_histo = linspace(0,25*3600,25*3600/100+1);
        for k = 1:length(t_histo)
            if(k == 1)
                n_steps_0 = 0;
            else
                n_steps_0 = n_steps_1(k-1);
            end
            n_steps_1(k) = length(find(time_all < t_histo(k)));
            
            n_steps(k) = n_steps_1(k) - n_steps_0;
        end
              
        figure;
        [AX,H1,H2] = plotyy(time_all/3600,var_all(:,1),t_histo/3600,n_steps,'loglog','semilogx');
        axis([1e-2 25 0 1]); axis 'auto y';
        set(H1,'LineStyle','-');
        set(H2,'Marker','o','LineStyle','none');
        set(AX(2),'xlim',[1e-2 25]);
        set(AX(1),'yTickMode','auto')
        set(AX(2),'yTickMode','auto')
        xlabel('Time  [hr]');
        set(get(AX(1),'YLabel'),'String','Number of Cells in CSTR');
        set(get(AX(2),'YLabel'),'String','Number of Time Steps in Bin');
        title(['Explicit ODE Solver (ode45) with t_n_u_t = ',num2str(t_nut,'%2.0f'),' hrs']);


        Ncells_SS_p1 = var_all(end,1);
        C_cells_SS_p1 = Ncells_SS_p1/Vrxtr_p1;
        Nut_SS_p1 = var_all(end,2);
        P_SS_p1 = var_all(end,3);
        
        percent_SS = var_all(:,1)/Ncells_SS_p1;
        
        [loc_99] = find(percent_SS < 0.99 | percent_SS > 1.01);;
        
        [time_99] = time_all(max(loc_99)+1);

        disp(['For Time(flow start) = ',num2str(t_nut,'%2.2f'),' hours:']);
        disp('  ');
        disp(['Number of Cells in Reactor = ',num2str(var_all(end,1),'%8.6e')]);
        disp(['Concentration of Cells in Reactor = ',num2str(C_cells_SS_p1,'%8.6e')]);
        disp(['Concentration of Nutrients (M) = ',num2str(var_all(end,2))]);
        disp(['Concentration of Product (M) = ',num2str(var_all(end,3))]);
        disp('  ');
        disp(['Production Rate of Product (mole/hr) = ',num2str(var_all(end,3)*V_flow_quiz1*3600)]);
        disp('  ');
        disp(['Time to 99% of Steady-State (sec) = ',num2str(time_99)]);
        disp(['Time to Solve ODE System (sec) = ',num2str(time_int)]);
        disp(['Number of Integrator Steps Taken = ',num2str(length(time_all))]);
        disp('  ');
        disp('  ');
end




%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% this function defines the ODE's to be solved: dX/dt = F(X)
function dXdt = chemostat(time,var,C_nut_in,params,V_flow,Vrxtr)

Ncells = var(1);  % number of cells
C_nut = var(2);  % concentration of nutrients
C_P = var(3);  % concentration of products

CMrate = Cell_Multiplication(Ncells,C_nut,C_P,params);
NCrate = Nutrient_Consumption(Ncells,C_nut,C_P,params);
Prate = P_production(Ncells,C_nut,C_P,params);

dNcell_dt = CMrate - V_flow*Ncells/Vrxtr;

dNut_dt = 1/Vrxtr*((C_nut_in - C_nut)*V_flow - NCrate);

dP_dt = 1/Vrxtr*(Prate - V_flow*C_P);

dXdt = [dNcell_dt; dNut_dt; dP_dt];

return;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function CMrate = Cell_Multiplication(Ncells,Nutrients,P,params)
% computes the Cell Multiplication rate (cells/s) in the CSTR
% inputs:
%   Ncells       number of cells in the CSTR
%   Nutrients    concentration of Nutrients in the CSTR (moles/liter)
%   P		     concentration of Product in CSTR, [=] moles/liter
%   params       values of [k1,k2,k3,c1,c2,d] as listed in problem statement.
k1 = params(1); c1=params(4); d=params(6);
CMrate = k1.*Ncells.*Nutrients./((1+c1.*Nutrients).*(1+d.*P));

return;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function NCrate = Nutrient_Consumption(Ncells,Nutrients,P,params)
% computes the nutrient consumption rate in the CSTR (moles/s)
% inputs:
%   Ncells    	number of cells in the CSTR
%   Nutrients     concentration of Nutrients in the CSTR (moles/liter)
%   P		      concentration of Product in CSTR, [=] moles/liter
%   params        values of [k1,k2,k3,c1,c2,d] as listed in problem statement.
k2 = params(2);    c2 = params(5);
NCrate = k2*Ncells*(Nutrients - 1e-6) + c2*Cell_Multiplication(Ncells,Nutrients,P,params);

return;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function Prate = P_production(Ncells,Nutrients,P,params)
% computes the product production rate in the CSTR (moles/s)
% inputs:
%   Ncells    	number of cells in the CSTR
%   Nutrients     concentration of Nutrients in the CSTR (moles/liter)
%   P		      concentration of Product in CSTR, [=] moles/liter
%   params        values of [k1,k2,k3,c1,c2,d] as listed in problem statement.
k3 = params(3); d=params(6); c1=params(4);
Prate = k3.*Ncells.*exp(-d.*P).*((Nutrients-0.01).^2)./(1+c1.*Nutrients);

return;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function params = param_set
% sets params as in the problem statement
% [k1  k2  k3  c1  c2  d]
params = [5e-3 1e-9 1e-8 0.1 1e-7 0.01];

return;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



