├── AuxFunctions ├── dataset.xls ├── lagrange.m ├── sldplot.m ├── collocation.m └── figured.m ├── readme.md ├── license.txt ├── Heat_transfer_fins.m ├── Robertson_kinetics.m ├── Reaction_diffusion.m ├── Extractors.m ├── Steady_state_PFR.m ├── Tubular_reactor_2D.m ├── Furnace_Wall.m ├── MPC_tank_reactor.m └── Parameter_estimation.m /AuxFunctions/dataset.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asanet/chemeng-solved/HEAD/AuxFunctions/dataset.xls -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # CHEMical ENGineering Problems solved! 2 | 3 | This is a repository with classical chemical engineering problems solved in MatLab. 4 | (IN PROGRESS) 5 | 6 | ## Author 7 | 8 | * **Ataíde Neto** - ataide@peq.coppe.ufrj.br 9 | 10 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2018] [Ataíde Souza Andrade Neto] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /AuxFunctions/lagrange.m: -------------------------------------------------------------------------------- 1 | function P = lagrange(xg,yg,x) 2 | %LAGRANGE Gives the lagrange interpolator polynomial. 3 | % 4 | % P = LAGRANGE(XG,YG,X) Calculates the polynomial P in X with the nodal 5 | % points given in XG and coefficients in YG. XG and YG must be of the 6 | % same size. 7 | % 8 | % Examples:See the Furnace_wall.m file. 9 | % 10 | % ============================================================ 11 | % Author: ataide@peq.coppe.ufrj.br 12 | % homepage: github.com/asanet 13 | % Date: 2018-07-05 14 | % Matlab version: R2018a 15 | % Contact me for help/personal classes! 16 | 17 | % Error handling 18 | if length(xg) ~= length(yg) 19 | error('xg e yg must have the same lengths!') 20 | end 21 | 22 | % Put x in a column vector 23 | x = x(:); 24 | 25 | % Get the dimensions 26 | np = length(x); 27 | ng = length(xg); 28 | 29 | % Allocation 30 | P = zeros(np,1); 31 | 32 | % Calculate the lagrange polynomial in x -> P(x) 33 | for j = 1:ng 34 | l = ones(np,1); 35 | for i = 1:ng 36 | if i ~= j 37 | l = l.*( x - xg(i) )/( xg(j) - xg(i) ); 38 | end 39 | end 40 | P = P + l*yg(j); 41 | end 42 | 43 | -------------------------------------------------------------------------------- /Heat_transfer_fins.m: -------------------------------------------------------------------------------- 1 | function Heat_transfer_fins 2 | %% Steady state heat transfer in extended surfaces 3 | % 4 | % You'll learn: 5 | % +: How to solve a BVP with the shooting method 6 | % 7 | %% The problem 8 | % 9 | % Differential Equations: 10 | % -kA*d^2T/dx^2 = hP*(Tinf - T) 11 | % 12 | % Boundary Conditions: 13 | % x = 0 ... T = Tc 14 | % x = L ... -k*dT/dx = 0 15 | % 16 | % ============================================================ 17 | % Author: ataide@peq.coppe.ufrj.br 18 | % homepage: github.com/asanet 19 | % Date: 2018-07-05 20 | % Matlab version: R2018a 21 | % Contact me for help/personal classes! 22 | 23 | %% Problem setup 24 | addpath('AuxFunctions') 25 | 26 | % The parameters 27 | h = 100; 28 | d = 0.5; 29 | L = 2; 30 | P = pi*d; 31 | A = pi*d^2/4; 32 | k = 237; 33 | Tinf = 298.15; 34 | Tc = 800; 35 | 36 | % fsolve configuration 37 | op = optimoptions(@fsolve,'Algorithm','levenberg-marquardt','TolFun',1e-10,'TolX',1e-10, ... 38 | 'Display','iter-detailed','MaxIter',5000,'MaxFunEvals',10000); 39 | 40 | % find the initial condition for u 41 | u0_0 = 0; 42 | u0 = fsolve(@shooting,u0_0,op); 43 | 44 | % solve the BVP as a PVI 45 | [x,yc] = ode15s(@model,[0 L],[Tc u0]); 46 | 47 | % plot the profile 48 | figured; 49 | plot(x,yc(:,1),'LineWidth',1.5) 50 | xlabel('Length') 51 | ylabel('Temperature') 52 | 53 | function dy = model(~,y) 54 | 55 | T = y(1); 56 | u = y(2); 57 | 58 | dy(1,1) = u; 59 | dy(2,1) = -h*P/k/A*(Tinf - T); 60 | 61 | end 62 | 63 | function f = shooting(u0) 64 | 65 | y0 = [Tc u0]'; 66 | [~,y] = ode15s(@model,[0 L],y0); 67 | 68 | f = y(end,2) - 0; 69 | 70 | end 71 | 72 | end -------------------------------------------------------------------------------- /Robertson_kinetics.m: -------------------------------------------------------------------------------- 1 | function Robertson_kinetics 2 | %% Batch reactor with Robertson's reaction system 3 | % 4 | % You'll learn: 5 | % +: How to solve transient problems in ODE formulation 6 | % +: The differences between explicit and implicit solvers 7 | % +: How to pass the jacobian matrix to the integrator 8 | % 9 | %% The problem 10 | % 11 | % Differential equations: 12 | % dy1/dt = -k1y1 + k2y2y3 13 | % dy2/dt = k1y1 - k2y2y3 -k3y2^2 14 | % dy3/dt = k3y2^2; 15 | % 16 | % Initial conditions 17 | % t = 0 ... y1 = 1 18 | % y2 = 0 19 | % y3 = 0 20 | % 21 | % ============================================================ 22 | % Author: ataide@peq.coppe.ufrj.br 23 | % homepage: github.com/asanet 24 | % Date: 2018-07-05 25 | % Matlab version: R2018a 26 | % Contact me for help/personal classes! 27 | 28 | %% Problem setting 29 | addpath('AuxFunctions') 30 | 31 | % The kinetic constants 32 | k1 = 0.04; 33 | k2 = 1e4; 34 | k3 = 3e7; 35 | 36 | % Time interval and initial conditions 37 | tspan = [0 1e8]; 38 | y0 = [1 0 0]'; 39 | 40 | % Pass the analytic jacobian matrix to the odesolver 41 | opt = odeset('Jacobian',@jacobian); 42 | 43 | % Solve the model (what if you use a explicit integrator such as ode45?) 44 | tic 45 | [t,y] = ode15s(@model,tspan,y0,opt); 46 | toc 47 | 48 | % Plot the data 49 | figured; 50 | h = semilogx(t,y(:,1),t,y(:,2)*1e4,t,y(:,3)); 51 | set(h,'LineWidth',1.5); 52 | ylabel('Molar fraction') 53 | xlabel('Time') 54 | axis([t(2) t(end) 0 1]) 55 | legend({'y_1','y_2\times 10^4','y_3'}) 56 | 57 | % The model 58 | function dy = model(~,y) 59 | 60 | dy(1,1) = -k1*y(1) + k2*y(2)*y(3); 61 | dy(2,1) = k1*y(1) - k2*y(2)*y(3) - k3*y(2)^2; 62 | dy(3,1) = k3*y(2)^2; 63 | 64 | end 65 | 66 | function J = jacobian(~,y) 67 | 68 | J(1,1) = -k1; 69 | J(1,2) = k2*y(3); 70 | J(1,3) = k2*y(2); 71 | 72 | J(2,1) = k1; 73 | J(2,2) = -k2*y(3) - 2*k3*y(2); 74 | J(2,3) = -k2*y(2); 75 | 76 | J(3,1) = 0; 77 | J(3,2) = 2*k3*y(2); 78 | J(3,3) = 0; 79 | 80 | end 81 | 82 | end 83 | 84 | 85 | -------------------------------------------------------------------------------- /AuxFunctions/sldplot.m: -------------------------------------------------------------------------------- 1 | function sldplot(xcell,ycell,opts,labels,legendopt) 2 | %SLDPLOT Control plot with a slider. 3 | % 4 | % SLDPLOT(XCELL,YCELL) plots the arrays listed in XCELL and YCELL. XCELL 5 | % and YCELL must be of same length. Each element of XCELL must be a 6 | % N-by-1 vector. Each element of YCELL must be a M-by-N matrix. 7 | % 8 | % SLDPLOT(XCELL,YCELL,OPTS) passes the optional argument OPTS for 9 | % customize the plot. OPTS must be a length(XCELL)-by-1 cell array of 10 | % cell arrays. 11 | % 12 | % SLDPLOT(XCELL,YCELL,OPTS,LABELS) passes the labels of each slide step 13 | % to title the interactive figure. LABELS must be a M-by-1 cell array of 14 | % strings. 15 | % 16 | % SLDPLOT(XCELL,YCELL,OPTS,LABELS,LEGENDOPT) plot the legend. LEGENDOPT 17 | % is a cell array with the argument of the legend() command. 18 | % 19 | % Examples:See the Furnace_wall.m file. 20 | % 21 | % ============================================================ 22 | % Author: ataide@peq.coppe.ufrj.br 23 | % homepage: github.com/asanet 24 | % Date: 2018-07-05 25 | % Matlab version: R2018a 26 | % Contact me for help/personal classes! 27 | 28 | if nargin <= 2 29 | opts = {}; 30 | legendopt = {}; 31 | labels = {}; 32 | end 33 | 34 | nol = length(xcell); 35 | if nol ~= length(ycell) 36 | error('Incomplete data set. X and Y must be in pairs.') 37 | end 38 | 39 | f = gcf; 40 | ax = gca; 41 | 42 | sz = length(ycell{1}); 43 | 44 | ax.Units = 'normalized'; 45 | ax.Position([2 4]) = [ax.Position(2) + 0.075, ax.Position(4) - 0.075]; 46 | uicontrol('Parent',f,'style','slide','units','normalized','position',[0.1 0.025 0.8 0.05 ], ... 47 | 'min',1,'max',sz,'Value',1,'sliderstep',[1/sz 1/sz],'Tag','sld'); 48 | 49 | h = gobjects(nol,1); 50 | while ishandle(f) 51 | i = fix(get(f.Children(1),'Value')); 52 | h(1) = plot(xcell{1},ycell{1}(i,:)); 53 | set(h(1),opts{1}{:}) 54 | hold(ax,'on') 55 | for k = 2:nol 56 | h(k) = plot(xcell{k},ycell{k}(i,:)); 57 | set(h(k),opts{k}{:}) 58 | end 59 | hold(ax,'off') 60 | legend(legendopt{:}) 61 | if ~isempty(labels) 62 | title(labels{i}) 63 | end 64 | ax.NextPlot = 'replaceChildren'; 65 | waitfor(f.Children(1),'Value') 66 | end 67 | 68 | 69 | end 70 | 71 | -------------------------------------------------------------------------------- /AuxFunctions/collocation.m: -------------------------------------------------------------------------------- 1 | function [A,B,xgrid] = collocation(n,var,extrax) 2 | %COLLOCATION Calculates the first and second derivatives of the Lagrange 3 | % interpolator at the roots of the Legendre orthogonal 4 | % polynomial. 5 | % 6 | % [A,B,XGRID] = COLLOCATION(N,VAR,EXTRAX) N is the number of roots of the 7 | % orthogonal polynomial (usually the inner points of a discretization). 8 | % VAR is a string with the independent variable. Ex: with VAR = 'x', we 9 | % are calculating the roots xi in [-1, 1] such as P(x) = 0. 10 | % With VAR = '2*x-1', we are calculating the roots xi i [0, 1] such as 11 | % P(2x-1) = 0. 12 | % EXTRAX: '+val' to append val at the end 13 | % '-val' to preappend val at the start 14 | % 2x1 array to both append and preappend 15 | % 16 | % Examples:See the Furnace_wall.m file. 17 | % 18 | % ============================================================ 19 | % Author: ataide@peq.coppe.ufrj.br 20 | % homepage: github.com/asanet 21 | % Date: 2018-07-05 22 | % Matlab version: R2018a 23 | % Contact me for help/personal classes! 24 | 25 | if nargin < 3 26 | extrax = []; 27 | elseif nargin < 2 28 | var = 'x'; 29 | extrax = []; 30 | elseif nargin < 1 31 | error('Missing the number of roots.') 32 | end 33 | 34 | if n < 1 35 | n = 1; 36 | warning('Minimum value for n is 1') 37 | end 38 | 39 | if ~ischar(var) || ~any(var == 'x') 40 | error('Second argument must be a string and contain "x".') 41 | end 42 | 43 | % Symbolic variable 44 | syms('x') 45 | var = str2sym(var); 46 | 47 | % Polynomial in var 48 | LP = legendreP(n,var); 49 | 50 | % Roots of the polynomial 51 | xgrid = double(vpasolve(LP == 0)); 52 | 53 | if ~isempty(extrax) 54 | if strcmp(extrax(1),'+') 55 | xgrid = [xgrid; str2double(extrax(2:end))]; 56 | n = n + 1; 57 | elseif strcmp(extrax(1),'-') 58 | xgrid = [str2double(extrax(2:end)); xgrid]; 59 | n = n + 1; 60 | else 61 | xgrid = [extrax(1); xgrid; extrax(2)]; 62 | n = n+2; 63 | end 64 | end 65 | 66 | % Matrix A 67 | A = zeros(n,n); 68 | v = zeros(n,1); 69 | for i = 1:n 70 | p = 1; 71 | for j = 1:n 72 | fat = xgrid(i) - xgrid(j); 73 | v(i) = fat*v(i) + p; 74 | p = fat*p; 75 | end 76 | end 77 | 78 | for i = 1:n 79 | for j = 1:n 80 | if j ~= i 81 | A(i,j) = v(i)/v(j)/(xgrid(i)-xgrid(j)); 82 | A(i,i) = A(i,i) - A(i,j); 83 | end 84 | end 85 | end 86 | 87 | B = A^2; 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /Reaction_diffusion.m: -------------------------------------------------------------------------------- 1 | function Reaction_diffusion 2 | %% Steady steate reaction-diffusion in a spherical particle 3 | % 4 | % You'll learn: 5 | % +: How to discretize by finite differences 6 | % +: How to solve non linear algebraic problems 7 | % +: How to use the sparsity algebra package 8 | % 9 | %% The problem 10 | % 11 | % Differential Equation: 12 | % d^2y/dr^2 + 2/r*dy/dr - phi^2*y^m = 0 13 | % 14 | % Boundary Conditions: 15 | % r = 0 ... dy/dr = 0 16 | % r = 1 ... y = ys 17 | % 18 | % ============================================================ 19 | % Author: ataide@peq.coppe.ufrj.br 20 | % homepage: github.com/asanet 21 | % Date: 2018-07-05 22 | % Matlab version: R2018a 23 | % Contact me for help/personal classes! 24 | 25 | %% Problem setup 26 | addpath('AuxFunctions') 27 | 28 | % Grid Points 29 | n = 5000; 30 | 31 | % Reaction order 32 | m = 2; 33 | 34 | % Thiele's module 35 | phi = 20; 36 | 37 | % Particle Radius 38 | R = 1; 39 | 40 | % Delta r 41 | dr = R/(n-1); 42 | 43 | % Domain of discretization 44 | r = linspace(0,R,n)'; 45 | 46 | % Surface concentration 47 | ys = 1; 48 | 49 | % Sparsity pattern 50 | B = ones(n,3); 51 | Jp = spdiags(B,-1:1,n,n); 52 | 53 | % Initial guess 54 | y0 = ones(n,1); 55 | 56 | %% Solution -> fsolve 57 | 58 | % Solver Settings 59 | opt = optimoptions(@fsolve,'TolFun',1e-10,'TolX',1e-10,'Display','iter-detailed',... 60 | 'Algorithm','trust-region-reflective'); 61 | 62 | % Passing the Sparsity Pattern (Compare solution time with and without it) 63 | opt.JacobPattern = Jp; 64 | 65 | % Solver call 66 | tic 67 | y_sol = fsolve(@model,y0,opt); 68 | toc 69 | 70 | % Compute the final residue 71 | res = model(y_sol); 72 | 73 | % Plot the data 74 | close all; 75 | figured; 76 | h = plot(r,y_sol); 77 | xlabel('Particle Radius') 78 | ylabel('Concentration') 79 | set(h,'LineWidth',1.5) 80 | 81 | figured; 82 | h = plot(r,res); 83 | xlabel('Particle Radius') 84 | ylabel('Residue') 85 | set(h,'LineWidth',1.5) 86 | 87 | %% Model 88 | function f = model(y) 89 | 90 | % Memory allocations 91 | f = zeros(n,1); 92 | 93 | % BC in r = 0 94 | f(1) = -3*y(1) + 4*y(2) -y(3) ; 95 | 96 | % Diff. Eq. (inner points) 97 | for i = 2:n-1 98 | f(i) = (y(i+1) - 2*y(i) + y(i-1))/dr^2 + 2./r(i).*( y(i+1) - y(i-1) )/2/dr - phi^2*y(i).^m; 99 | end 100 | 101 | % BC in r = 1 102 | f(n) = y(n) - ys; 103 | 104 | end 105 | 106 | end -------------------------------------------------------------------------------- /AuxFunctions/figured.m: -------------------------------------------------------------------------------- 1 | function f = figured(varargin) 2 | %FIGURED Customized figure and axes for plotting and with alternative 3 | % save2pdf command. 4 | % 5 | % Pretty much the same usage of figure() command. 6 | % 7 | % ============================================================ 8 | % Author: ataide@peq.coppe.ufrj.br 9 | % homepage: github.com/asanet 10 | % Date: 2018-07-05 11 | % Matlab version: R2018a 12 | % Contact me for help/personal classes! 13 | 14 | f = figure(varargin{:}); 15 | 16 | if ~any(strcmpi(varargin,'position')) 17 | set(f,'Units','normalized','PaperOrientation','landscape','PaperPositionMode','auto',... 18 | 'Tag','main','Position',[0.3 0.25 0.4 0.5]); 19 | else 20 | set(f,'Units','normalized','PaperOrientation','landscape','PaperPositionMode','auto',... 21 | 'Tag','main'); 22 | end 23 | 24 | axes('Parent',f,'YGrid','on','Box','on','FontSize',16,'NextPlot','replacechildren', ... 25 | 'Units','normalized'); 26 | 27 | mh1 = uimenu('Parent',f,'Label','Additions'); 28 | uimenu(mh1,'Label','Save2PDF','Enable','on','Tag','saveinpdf','Accelerator','B'); 29 | 30 | h = guihandles(f); 31 | set(h.saveinpdf,'Callback',@save2pdfemb); 32 | 33 | guidata(f,h) 34 | end 35 | 36 | function save2pdfemb(ho,~) 37 | h = guidata(ho); 38 | 39 | [FileName,PathName] = uiputfile({'*.pdf','Portable Document File (.pdf)'}, 'Save Figure as...'); 40 | if FileName~=0 41 | save2pdflocal([PathName FileName],800,h.main) 42 | end 43 | 44 | end 45 | 46 | function save2pdflocal(pdfFileName,dpi,handle) 47 | % Adapted from (c) Gabe Hoffmann, gabe.hoffmann@gmail.com 48 | 49 | % Backup previous settings 50 | prePaperType = get(handle,'PaperType'); 51 | prePaperUnits = get(handle,'PaperUnits'); 52 | preUnits = get(handle,'Units'); 53 | prePaperPosition = get(handle,'PaperPosition'); 54 | prePaperSize = get(handle,'PaperSize'); 55 | 56 | % Make changing paper type possible 57 | set(handle,'PaperType',''); 58 | 59 | % Set units to all be the same 60 | set(handle,'PaperUnits','inches'); 61 | set(handle,'Units','inches'); 62 | 63 | % Set the page size and position to match the figure's dimensions 64 | % paperPosition = get(handle,'PaperPosition'); 65 | position = get(handle,'Position'); 66 | set(handle,'PaperPosition',[0,0,position(3:4)]); 67 | set(handle,'PaperSize',position(3:4)); 68 | 69 | % Save the pdf (this is the same method used by "saveas") 70 | print(handle,'-dpdf',pdfFileName,sprintf('-r%d',dpi)) 71 | 72 | % Restore the previous settings 73 | set(handle,'PaperType',prePaperType); 74 | set(handle,'PaperUnits',prePaperUnits); 75 | set(handle,'Units',preUnits); 76 | set(handle,'PaperPosition',prePaperPosition); 77 | set(handle,'PaperSize',prePaperSize); 78 | 79 | end 80 | -------------------------------------------------------------------------------- /Extractors.m: -------------------------------------------------------------------------------- 1 | function Extractors 2 | %% Steady state train of extractors 3 | % 4 | % You'll learn: 5 | % +: How to generalize staged processes 6 | % +: How to solve linear systems 7 | % 8 | %% The problem 9 | % 10 | % Physical model: 11 | % 12 | % Stg (i-1) Stg (i) Stg (i+1) 13 | % ----------------------------------------------- 14 | % X(i-2) -->| |-->X(i-1) |-->X(i) |-->X(i+1) (R) 15 | % | | | | 16 | % | | | | 17 | % | | | | 18 | % Y(i-1) <--| Y(i)<--| Y(i+1)<--| |<--Y(i+2) (E) 19 | % ----------------------------------------------- 20 | % 21 | % Input: 22 | % N: Number of Stages 23 | % R: Raffinate flow rate 24 | % E: Extract flow rate 25 | % Xf: Molar fraction feeding stage 1 at raffinate 26 | % Yf: Molar fraction feeding stage N at extract 27 | % Ki: Equilibrium constants 28 | % 29 | % Mass balance: 30 | % Stg 1: E*Y(2) + R*Xf = R*X(1) + E*Y(1) 31 | % Stg i: E*Y(i+1) + R*X(i-1) = R*X(i) + E*Y(i) 32 | % Stg N: E*Yf + R*X(N-1) = R*X(N) + E*Y(N) 33 | % 34 | % Equilibrium constraints 35 | % Y(i) = K(i)*X(i) 36 | % 37 | % Problem: Ax=b 38 | % 39 | % ============================================================ 40 | % Author: ataide@peq.coppe.ufrj.br 41 | % homepage: github.com/asanet 42 | % Date: 2018-07-05 43 | % Matlab version: R2018a 44 | % Contact me for help/personal classes! 45 | 46 | %% Problem setup 47 | addpath('AuxFunctions') 48 | 49 | % Number of stages 50 | N = 10; 51 | K = 0.25*ones(N,1); 52 | R = 1; 53 | E = 5; 54 | Xf = 0.8; 55 | Yf = 0; 56 | 57 | % Building the matrix A 58 | % Prototype 59 | % X1 X2 X3 Y1 Y2 Y3 60 | % 61 | % A = -R 0 0 | -E E 0 EQ1 62 | % R -R 0 | 0 -E E EQ2 63 | % 0 R -R | 0 0 -E EQ3 64 | % -------------+------------- 65 | % -K1 0 0 | 1 0 0 EQ4 66 | % 0 -K2 0 | 0 1 0 EQ5 67 | % 0 0 -K3 | 0 0 1 EQ6 68 | 69 | % Generalized 70 | MR = -R*diag(ones(N,1)) + R*diag(ones(N-1,1),-1); 71 | ME = -E*diag(ones(N,1)) + E*diag(ones(N-1,1),1); 72 | MK = -diag(K); 73 | MI = eye(N); 74 | 75 | A = [MR ME 76 | MK MI]; 77 | 78 | % Vector b 79 | b = zeros(2*N,1); 80 | b(1) = -R*Xf; 81 | b(N) = -E*Yf; 82 | 83 | % Solve the linear system 84 | sol = linsolve(A,b); 85 | 86 | % Plot the data 87 | close all 88 | 89 | Stages = 1:N; 90 | Xi = sol(1:N); 91 | Yi = sol(N+1:2*N); 92 | 93 | figured; 94 | h = plot(Stages,Xi,Stages,Yi); 95 | set(h(1),'Marker','o','LineStyle',':','LineWidth',1.5,'MarkerFaceColor',get(h(1),'Color')); 96 | set(h(2),'Marker','o','LineStyle',':','LineWidth',1.5,'MarkerFaceColor',get(h(2),'Color')); 97 | xlabel('Stage') 98 | ylabel('Molar fraction') 99 | axis([1 N 0 1]) 100 | legend({'Raffinate','Extract'}) 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /Steady_state_PFR.m: -------------------------------------------------------------------------------- 1 | function Steady_state_PFR 2 | %% Steady state PFR 3 | % 4 | % You'll learn: 5 | % +: How to solve steady state problems in DAE formulation 6 | % 7 | %% The problem 8 | % 9 | % Chemical reactions: A -> B (R1) 10 | % 2A -> C (R2) 11 | % 12 | % Differential Equations: 13 | % dF_i/dz = v_i1*r_1 + v_i2*r_2 14 | % dT/dz = 1/(cp*Ft)*[ K(Tc - T) - H1*r_1 - H2*r_2] 15 | % 16 | % Algebraic Constraints: 17 | % Ft = sum(F_i) 18 | % C_i = P/(R*T)*F_i/Ft 19 | % u*sum(C_i) = Ft 20 | % 21 | % Auxiliary equations 22 | % cp*Ft = sum(F_i*cp_i) 23 | % 24 | % ============================================================ 25 | % Author: ataide@peq.coppe.ufrj.br 26 | % homepage: github.com/asanet 27 | % Date: 2018-07-05 28 | % Matlab version: R2018a 29 | % Contact me for help/personal classes! 30 | 31 | %% Problem setup 32 | addpath('AuxFunctions') 33 | 34 | % The parameters 35 | H1 = -20000; H2 = -60000; 36 | cpa = 90; cpb = 90; cpc = 180; 37 | K = 4000; Tc = 373.15; Tr = 300; 38 | k1 = 1e-1; E1 = 4000; 39 | k2 = 1e-6; E2 = 9000; 40 | R = 8.3145; P = 101325; 41 | 42 | % The integration interval (reactor length) 43 | tspan = [0 1]; 44 | 45 | % The consistent initial conditions 46 | Fa0 = 100; Fb0 = 0; Fc0 = 0; T0 = 423; Ft0 = 100; 47 | Ca0 = P/R/T0; Cb0 = 0; Cc0 = 0; u0 = Ft0/(Ca0+Cb0+Cc0); 48 | 49 | y0 = [Fa0 Fb0 Fc0 T0 Ft0 u0 Ca0 Cb0 Cc0]'; 50 | yp0 = model(0,y0,zeros(9,1)); 51 | 52 | % The solution 53 | [x,y] = ode15i(@model,tspan,y0,yp0); 54 | 55 | % Plot the results 56 | close all 57 | 58 | Fas = y(:,1); Fbs = y(:,2); Fcs = y(:,3); 59 | Ts = y(:,4); Fts = y(:,5); us = y(:,6); 60 | Cas = y(:,7); Cbs = y(:,8); Ccs = y(:,9); 61 | 62 | 63 | figured; 64 | h = plot(x,[Fas Fbs Fcs]); 65 | xlabel('Length') 66 | ylabel('Molar flux') 67 | set(h,'LineWidth',1.5); 68 | legend({'F_a','F_b','F_c'}) 69 | 70 | figured; 71 | h = plot(x,[Cas Cbs Ccs]); 72 | xlabel('Length') 73 | ylabel('Concentration') 74 | set(h,'LineWidth',1.5); 75 | legend({'C_a','C_b','C_c'}) 76 | 77 | figure; 78 | subplot(311) 79 | plot(x,Fts,'LineWidth',1.5) 80 | xlabel('Length') 81 | ylabel('Total flux') 82 | subplot(312) 83 | plot(x,Ts,'LineWidth',1.5) 84 | xlabel('Length') 85 | ylabel('Temperature') 86 | subplot(313) 87 | plot(x,us,'LineWidth',1.5) 88 | xlabel('Length') 89 | ylabel('Velocity') 90 | 91 | function res = model(~,y,yp) 92 | 93 | % Variable allocation 94 | Fa = y(1); Fb = y(2); Fc = y(3); T = y(4); 95 | 96 | Ft = y(5); u = y(6); 97 | Ca = y(7); Cb = y(8); Cc = y(9); 98 | 99 | dFa = yp(1); dFb = yp(2); dFc = yp(3); dT = yp(4); 100 | 101 | % The reaction rates 102 | r1 = k1*exp(-E1*(1/T - 1/Tr))*Ca; 103 | r2 = k2*exp(-E2*(1/T - 1/Tr))*Ca^2; 104 | 105 | % The mean cp (not a constraint) 106 | cpFt = Fa*cpa + Fb*cpb + Fc*cpc; 107 | 108 | % The differential equations 109 | res(1,1) = -r1 -2*r2 - dFa; 110 | res(2,1) = r1 - dFb; 111 | res(3,1) = r2 - dFc; 112 | res(4,1) = 1/cpFt*( K*(Tc - T) - H1*r1 - H2*r2) - dT; 113 | 114 | % The algebraic constraints 115 | res(5,1) = Ft - Fa - Fb - Fc; 116 | res(6,1) = Ca - P/R/T*Fa/Ft; 117 | res(7,1) = Cb - P/R/T*Fb/Ft; 118 | res(8,1) = Cc - P/R/T*Fc/Ft; 119 | res(9,1) = u*(Ca+Cb+Cc) - Ft; 120 | 121 | 122 | end 123 | 124 | 125 | end 126 | 127 | -------------------------------------------------------------------------------- /Tubular_reactor_2D.m: -------------------------------------------------------------------------------- 1 | function Tubular_reactor_2D 2 | %% Transient tubular reactor in 2-D 3 | % 4 | % You'll learn: 5 | % +: how to discretize two dimensional domains using finite diff. 6 | % +: Auto generate the sparsity pattern for your problem. 7 | % +: How to solve transient problems in DAE formulation 8 | % 9 | %% The problem 10 | % 11 | % Differential Equation: 12 | % dC/dt = -d/dz( v_z*C - D_z*dC/dz ) + D_r/r*d/dr(r*dC/dr) - k*C 13 | % 14 | % With: 15 | % C = C(t,r,z) 16 | % v_z = v_z(r) 17 | % 18 | % Boundary Conditions: 19 | % z = 0 ... v_z*C_f(t,r) = v_z*C(t,r,0) - D_z*dC(t,r,0)/dz 20 | % z = L ... dC(t,r,L)/dz = 0 21 | % r = 0 ... dC(t,0,z)/dr = 0 22 | % r = R ... dC(t,R,z)/dz = 0 23 | % 24 | % Initial condition: 25 | % t = 0 ... C(0,r,z) = C0(r,z) = 0 26 | % 27 | % ============================================================ 28 | % Author: ataide@peq.coppe.ufrj.br 29 | % homepage: github.com/asanet 30 | % Date: 2018-07-05 31 | % Matlab version: R2018a 32 | % Contact me for help/personal classes! 33 | 34 | %% Problem setup 35 | addpath('AuxFunctions') 36 | 37 | % Reactor dimensions 38 | R = 0.1; L = 10; 39 | 40 | % Grid dimensions 41 | Nr = 150; 42 | Nz = 150; 43 | dr = R/(Nr-1); 44 | dz = L/(Nz-1); 45 | r = linspace(0,R,Nr)'; 46 | z = linspace(0,L,Nz)'; 47 | n = Nr*Nz; 48 | 49 | % Operational parameters (play around) 50 | vmax = 1; 51 | vz = vmax*(1 - (r/R).^2); % Velocity profile 52 | Dz = 0.00010; % Diffusion coefficients 53 | Dr = 0.00001; 54 | k = 0.05; % Kinetic constant 55 | Cf = ones(Nr,1); % Feed concentration 56 | 57 | % The initial condition 58 | y0 = zeros(n,1); 59 | zz = zeros(n,1); 60 | yp0 = model(0,y0,zz); 61 | 62 | % Workaround to get the sparsity pattern automatically 63 | tic 64 | yr = rand(n,1); 65 | per = zeros(n,1); 66 | idx = false(n,n); 67 | f0 = model(0,yr,zz); 68 | for ii = 1:n 69 | per(ii) = 1e-6; 70 | f1 = model(0,yr+per,zz); 71 | idx(:,ii) = f1 ~= f0; 72 | per(ii) = 0; 73 | end 74 | 75 | Jp = sparse(idx); 76 | Jyp = speye(n); 77 | etime = toc; 78 | 79 | fprintf('Elapsed time generating the sparsity pattern = %2.4f s\n',etime); 80 | 81 | % The solution 82 | tf = 20; 83 | frames = 2000; 84 | tspan = linspace(0,tf,frames); 85 | op = odeset('AbsTol',1e-6,'RelTol',1e-4,'JPattern',{Jp,Jyp}); 86 | 87 | tic 88 | [t,y] = ode15i(@model,tspan,y0,yp0,op); 89 | etime = toc; 90 | 91 | fprintf('Elapsed time integrating the system = %2.4f s\n',etime); 92 | 93 | 94 | % Parse the solution matrix 95 | tic 96 | Nt = length(t); 97 | Csol = zeros(Nr,Nz,Nt); 98 | for ii = 1:Nt 99 | Csol(:,:,ii) = reshape(y(ii,:)',Nr,Nz); 100 | end 101 | etime = toc; 102 | 103 | fprintf('Elapsed time parsing the solution = %2.4f s\n',etime); 104 | 105 | % Plot the data (to stop the animation, press CTRL+C on command window) 106 | close all 107 | 108 | figured; 109 | axis([0 L 0 2*R]) 110 | caxis([0 1]) 111 | xlabel('Length') 112 | ylabel('Radius') 113 | set(gca,'YTickLabel',[0.1, 0.05, 0, 0.05, 0.1]) 114 | hcb = colorbar; 115 | hcb.Label.String = 'Concentration'; 116 | colormap jet 117 | for ii = 1:Nt 118 | imagesc(z,[2*r;2*r],[flip(Csol(:,:,ii));Csol(:,:,ii)]) 119 | title(sprintf('Axial and radial profiles in t = %2.3f s',t(ii))) 120 | drawnow; 121 | pause(tf/frames/30) 122 | end 123 | 124 | %% The model -> DAE formulation (boundary conditions incorporated in the model) 125 | function res = model(~,y,yp) 126 | 127 | % Memory allocation 128 | res = zeros(Nr,Nz); 129 | C = reshape(y,Nr,Nz); 130 | dC = reshape(yp,Nr,Nz); 131 | 132 | % The Diff. Equations (inner points) 133 | for i = 2:Nr-1 134 | for j = 2:Nz-1 135 | res(i,j) = -vz(i)*( C(i,j) - C(i,j-1) )/dz + Dz*( C(i,j+1) - 2*C(i,j) + C(i,j-1) )/dz^2 + ... 136 | Dr*( C(i+1,j) - C(i-1,j) )/2/dr/r(i) + Dr*( C(i+1,j) - 2*C(i,j) + C(i-1,j) )/dr^2 - k*C(i,j) - dC(i,j); 137 | end 138 | end 139 | 140 | % In r = 0 and z = 1:Nz 141 | res(1,:) = C(2,:) - C(1,:); 142 | 143 | % In r = R and z = 1:Nz 144 | res(Nr,:) = C(Nr,:) - C(Nr-1,:); 145 | 146 | % In z = 0 and r = 2:Nr-1 147 | res(2:Nr-1,1) = Cf(2:Nr-1).*vz(2:Nr-1) - C(2:Nr-1,1).*vz(2:Nr-1) + Dz*( C(2:Nr-1,2) - C(2:Nr-1,1) )/dz; 148 | 149 | % In z = L and r = 2:Nr-1 150 | res(2:Nr-1,Nz) = C(2:Nr-1,Nz) - C(2:Nr-1,Nz-1); 151 | 152 | res = reshape(res,n,1); 153 | 154 | end 155 | 156 | end -------------------------------------------------------------------------------- /Furnace_Wall.m: -------------------------------------------------------------------------------- 1 | function Furnace_Wall 2 | %% Transient heat conduction on a composite furnace wall 3 | % 4 | % You'll learn: 5 | % +: How to discretize a domain by orthogonal collocation 6 | % +: How to handle problems with multiple domains 7 | % +: How to solve transient problems in DAE formulation 8 | % 9 | %% The problem 10 | % 11 | % Physical model 12 | % 13 | % \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 14 | % |----------------------------------| 15 | % h1, Tinf1 | Body A | Body B | h2, Tinf2 16 | % | TA0 | TB0 | 17 | % |_________________|________________| 18 | % \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 19 | % -LA|---------------- 0 -------------->|LB 20 | % 21 | % Differential Equations: 22 | % dTA/dt = (k/rho/cp)_A d^2TA/dx^2 23 | % dTB/dt = (k/rho/cp)_B d^2TB/dx^2 24 | % 25 | % Boundary Conditions: 26 | % x = -LA ... -kA*dTA/dx = h1*(Tinf1 - TA) 27 | % x = 0 ... TA = TB 28 | % kA*dTA/dx = kB*dTB/dx 29 | % x = LB ... -kB*dTB/dx = h2*(TB - Tinf2) 30 | % 31 | % Initial Condition: 32 | % t = 0 ... TA = TA0(x) 33 | % TB = TB0(x) 34 | % 35 | % ============================================================ 36 | % Author: ataide@peq.coppe.ufrj.br 37 | % homepage: github.com/asanet 38 | % Date: 2018-07-05 39 | % Matlab version: R2018a 40 | % Contact me for help/personal classes! 41 | 42 | %% Problem setup 43 | addpath('AuxFunctions') 44 | 45 | % Parameters 46 | kA = 50*3600; kB = 398*3600; 47 | cpA = 900; cpB = 386; 48 | rhoA = 2697; rhoB = 8920; 49 | LA = 1; LB = 1.5; 50 | h1 = 1e2*3600; h2 = 1e5*3600; 51 | Tinf1 = 298.15; Tinf2 = 1000; 52 | 53 | % Inner points 54 | N = 5; 55 | 56 | % Total points 57 | n = N + 2; 58 | 59 | % Grid and matrices 60 | [M1,M2,xgrid] = collocation(N,'2*x-1',[0 1]); 61 | xB = LB*xgrid; 62 | xA = sort(-LA*xgrid); 63 | 64 | % Initial condition 65 | y0 = 298.15*ones(2*n,1); 66 | yp0 = model(0,y0,zeros(2*n,1)); 67 | 68 | tf = 20; 69 | frames = 900 + 100; 70 | tspan = [ linspace(0, tf/10, 900) linspace( tf/10+0.1, tf, 100)]; 71 | [t,y] = ode15i(@model,tspan,y0,yp0); 72 | 73 | % Solution at the collocation points 74 | TAs = y(:,1:n); TBs = y(:,n+1:2*n); 75 | 76 | % Refined grid 77 | ref = 100; 78 | xAc = linspace(-LA,0,ref); 79 | xBc = linspace(0,LB,ref); 80 | 81 | % Lagrange polynomial 82 | TAc = zeros(frames,ref); 83 | TBc = zeros(frames,ref); 84 | 85 | labels = cell(frames,1); 86 | for i = 1:frames 87 | TAc(i,:) = lagrange(xA,TAs(i,:),xAc); 88 | TBc(i,:) = lagrange(xB,TBs(i,:),xBc); 89 | labels{i} = sprintf('Temperature profile in t = %2.4f h',t(i)); 90 | end 91 | 92 | % Plot data 93 | close all 94 | figured(1); 95 | h = plot(t,TAs(:,1:n-1),'b',t,TAs(:,n),'g',t,TBs(:,2:end),'r'); 96 | set(h,'LineWidth',1.5) 97 | set(h([2:n-1 n+2:2*n-1]),'HandleVisibility','off') 98 | xlabel('Time (h)') 99 | ylabel('Temperature (K)') 100 | legend({'Body A','Interface','Body B'},'Location','SouthEast') 101 | 102 | pause(1) 103 | figured(2); 104 | axis([-LA LB .95*Tinf1 1.05*Tinf2]) 105 | xlabel('Length (m)') 106 | ylabel('Temperature (K)') 107 | opts = { {'LineWidth',1.5,'Color','b'}; 108 | {'Marker','*','LineStyle','none','Color','b'}; 109 | {'LineWidth',1.5,'Color','r'}; 110 | {'Marker','*','LineStyle','none','Color','r'} }; 111 | 112 | legopt = {'A: Interp','A: Col point','B: Interp','B: Col point','location','NorthOutside','Orientation','Horizontal'}; 113 | sldplot({xAc xA xBc xB},{TAc TAs TBc TBs},opts,labels,legopt) 114 | 115 | % The model (discretization by orthogonal collocation) 116 | function res = model(~,y,yp) 117 | 118 | % Variable allocation 119 | TA = y(1:n); TB = y(n+1:2*n); 120 | dTA = yp (1:n); dTB = yp(n+1:2*n); 121 | res = zeros(2*n,1); 122 | 123 | % Boundary Conditions in x = -LA 124 | res(1) = -kA*M1(1,:)*TA/LA - h1*(Tinf1 - TA(1)); 125 | 126 | % Differential Equation for TA (inner points) 127 | res(2:n-1) = kA/rhoA/cpA*(M2(2:n-1,:)*TA)/LA^2 - dTA(2:n-1); 128 | 129 | % Boundary Conditions in x = 0 130 | res(n) = TA(n) - TB(1); 131 | res(n+1) = kA*M1(n,:)*TA/LA - kB*M1(1,:)*TB/LB; 132 | 133 | % Differential Equation for TB (inner points) 134 | res(n+2:2*n-1) = kB/rhoB/cpB*(M2(2:n-1,:)*TB)/LB^2 - dTB(2:n-1); 135 | 136 | % Boundary Conditions in x = LB 137 | res(2*n) = -kB*M1(n,:)*TB/LB - h2*(TB(n) - Tinf2); 138 | 139 | end 140 | end 141 | -------------------------------------------------------------------------------- /MPC_tank_reactor.m: -------------------------------------------------------------------------------- 1 | function MPC_tank_reactor 2 | %% Model predictive control of a tank reactor 3 | % 4 | % You'll learn: 5 | % +: how to solve optimization problems 6 | % +: How to apply a model predictive control with nonlinear models 7 | % 8 | %% The problem 9 | % 10 | % minimize the integral of (y_pred - y_sp)^2 11 | % subject to: F(t,y,y') = 0 12 | % lb < u < ub 13 | % 0 < |du| < du_max 14 | % 15 | % About the process: 16 | % CSTR with van de vusse reaction system 17 | % A -> B 18 | % B -> C 19 | % 2A -> D 20 | % 21 | % ============================================================ 22 | % Author: ataide@peq.coppe.ufrj.br 23 | % homepage: github.com/asanet 24 | % Date: 2018-07-05 25 | % Matlab version: R2018a 26 | % Contact me for help/personal classes! 27 | 28 | %% Problem setup 29 | addpath('AuxFunctions') 30 | 31 | % The model parameters 32 | k1 = 3.575e8; k2 = 3.575e8; k3 = 2.5119e3; 33 | E1 = 8.2101e4; E2 = 8.2101e4; E3 = 7.1172e4; 34 | H1 = 4.2e3; H2 = -11e3; H3 = -41.85e3; 35 | rho = 0.9342e3; cp = 3.01e3; V = 10e-3; 36 | tau = 80; Tf = 403.15; Caf = 1000; 37 | UA = 0.215*1120; R = 8.3145; 38 | 39 | % The objective function parameters 40 | Hp = 1000; % Prediction horizon 41 | nt = 50; % Time sampling points 42 | P_sp = 1; % Set point violation penalty 43 | P_u = 1e6; % Saturation penalty 44 | P_du = 1e6; % Delta u penalty 45 | lb = 273.15; % lower bound to u 46 | ub = 373.15; % upper bound to u 47 | du_max = 10; % max Delta u 48 | p = 20; % The regularization parameter 49 | 50 | % The times for control actions (the size defines the control horizon) 51 | td = [0 10 20 40]'; 52 | 53 | % The control horizon 54 | % Hc = length(td); 55 | 56 | % The Sampling time 57 | Ta = 100; 58 | 59 | % The set point 60 | spval = 380; 61 | T_sp = spval*ones(nt,1); 62 | 63 | % The current plant state (starting condition) 64 | ynow = [0 0 300]'; 65 | 66 | % Simulation span 67 | tspan = 1:3*Hp; 68 | 69 | % The initial u profile (must have Hc values) 70 | u = [298.15 0 0 0]'; 71 | umi = u(1); 72 | 73 | % The solver configuration 74 | simulationOpt = odeset('AbsTol',1e-4,'RelTol',1e-3); 75 | 76 | % use with fsolve 77 | % controlOpt = optimoptions(@fsolve,'Algorithm','levenberg-marquardt',... 78 | % 'TolFun',1e-8,'TolX',1e-8,'MaxIter',1e2,... 79 | % 'MaxFunEvals',1e5,'Display','none'); 80 | 81 | % use with fminsearch 82 | controlOpt = optimset('TolFun',1e-8,'TolX',1e-8,'MaxIter',100,'Display','none'); 83 | 84 | % The problem call 85 | nsim = length(tspan); 86 | nsamples = fix(nsim/Ta); 87 | 88 | tm = zeros(nsamples,1); 89 | ym = zeros(nsamples,3); 90 | um = zeros(nsamples,1); 91 | 92 | ym(1,:) = ynow; 93 | um(1) = u(1); 94 | 95 | close all 96 | for i = 1:nsamples-1 97 | 98 | fprintf('Sampling time %d of %d\n',i,nsamples-1) 99 | 100 | % call the controller to find the u predicted (either fsolve or fminsearch) 101 | %u = fsolve(@mpc_cost_function,u,controlOpt,ynow); 102 | u = fminsearch(@mpc_cost_function,u,controlOpt,ynow); 103 | 104 | % Apply only the first control action 105 | u(2:end) = 0; 106 | 107 | % call the plant with updated u and return t and y measured 108 | [tmi,ymi] = ode15s(@plant, [tm(i) tm(i)+Ta], ynow, simulationOpt, u); 109 | 110 | % Updated the measured variables 111 | ynow = ymi(end,:)'; 112 | 113 | tm(i+1) = tmi(end); 114 | ym(i+1,:) = ymi(end,:); 115 | um(i+1) = u(1); 116 | umi = u(1); 117 | 118 | % Disturbance in tau 119 | if i == fix(nsamples/2) 120 | Tf = 420; 121 | end 122 | end 123 | 124 | % Plot the data 125 | figure; 126 | subplot(211) 127 | h = plot(tm,ym(:,3),tm,spval*ones(nsamples,1),'LineWidth',1.5); 128 | set(h(2),'LineStyle',':') 129 | ylabel('Temperature') 130 | title('Controlled variable') 131 | set(gca,'ygrid','on','xgrid','on','fontsize',16) 132 | legend({'measured','setpoint'},'location','southeast') 133 | 134 | subplot(212) 135 | h = plot(tm,um,tm,lb*ones(nsamples,1),tm,ub*ones(nsamples,1),'LineWidth',1.5); 136 | set(h(2:3),'LineStyle',':','Color','k') 137 | ylabel('Jacket temperature') 138 | xlabel('Time') 139 | title('Manipulated variable') 140 | legend({'measured','bounds'},'location','southwest') 141 | set(gca,'ygrid','on','xgrid','on','fontsize',16) 142 | 143 | function f = mpc_cost_function(u,ynow) 144 | 145 | % Solve the model until the prediction horizon 146 | ts1 = linspace(0,Hp,nt); 147 | [t,y] = ode15s(@model,ts1,ynow,simulationOpt,u); 148 | 149 | % The set point tracking term 150 | f1 = trapz(t,( y(:,3) - T_sp ).^2 ); 151 | 152 | % The control variables bound 153 | min_u = min(cumsum(u)); 154 | max_u = max(cumsum(u)); 155 | f2 = -min(0, ub - max_u) -min(0, min_u - lb); 156 | 157 | % Delta u penalty 158 | f3 = -min(0, du_max - max(abs([umi- u(1); u(2:end)]))); 159 | 160 | % The objective function 161 | f = P_sp*f1 + P_u*f2 + P_du*f3; 162 | end 163 | 164 | function dy = model(t,y,u) 165 | % The model: in this case, we consider a perfect model, 166 | % that is, model = plant 167 | dy = plant(t,y,u); 168 | 169 | end 170 | 171 | function dy = plant(t,y,u) 172 | % The virtual plant: Van de vusse reaction in a CSTR 173 | 174 | % The states 175 | Ca = y(1); Cb = y(2); T = y(3); 176 | 177 | % The regularization function 178 | reg = 1/2 + tanh(p*(t - td))/2; 179 | 180 | % Control variable 181 | Tc = sum(u.*reg); 182 | 183 | % Reaction rates 184 | r1 = k1*exp(-E1/R/T)*Ca; 185 | r2 = k2*exp(-E2/R/T)*Cb; 186 | r3 = k3*exp(-E3/R/T)*Ca^2; 187 | 188 | % Mass balances 189 | dCa = (Caf - Ca)/tau -r1 -2*r3; 190 | dCb = -Cb/tau + r1 - r2; 191 | dT = (Tf - T)/tau -1/rho/cp*( UA/V*(T - Tc) + H1*r1 + H2*r2 +H3*r3 ); 192 | 193 | % vector of derivatives 194 | dy = [dCa dCb dT]'; 195 | 196 | end 197 | end 198 | 199 | -------------------------------------------------------------------------------- /Parameter_estimation.m: -------------------------------------------------------------------------------- 1 | function Parameter_estimation 2 | %% Parameter estimation of a dynamic model 3 | % 4 | % You'll learn: 5 | % +: How to solve optimization problems 6 | % +: How to estimate parameters of a non linear model with numerical 7 | % solution 8 | % 9 | %% The problem 10 | % 11 | % Given a data set (xe,ye), find the parameters of a dynamical 12 | % non linear model. By least squares the problem is: 13 | % 14 | % minimize the sum of (ye_i - ycalc_i)^2 15 | % 16 | % About the process: 17 | % CSTR with van de vusse reaction system 18 | % A -> B 19 | % B -> C 20 | % 2A -> D 21 | % 22 | % Estimate the Arrhenius pre-exponential factor and activation energies 23 | % 24 | % ============================================================ 25 | % Author: ataide@peq.coppe.ufrj.br 26 | % homepage: github.com/asanet 27 | % Date: 2018-07-05 28 | % Matlab version: R2018a 29 | % Contact me for help/personal classes! 30 | 31 | %% Problem setup 32 | addpath('AuxFunctions') 33 | 34 | % Read the data set from the dataset.xls file 35 | dataset = xlsread('dataset.xls'); 36 | 37 | % Separate the data set into vectors 38 | te = dataset(:,1); % The time (independent variable) 39 | Cae = dataset(:,2); % The temperature 40 | Cbe = dataset(:,3); % The temperature 41 | Te = dataset(:,4); % The temperature 42 | 43 | % The initial guess for the parameters 44 | k10 = 1e8; k20 = 1e8; k30 = 1e3; 45 | E10 = 1e5; E20 = 1e5; E30 = 1e5; 46 | 47 | par0 = [k10 k20 k30 E10 E20 E30]'; 48 | 49 | % The known model parameters 50 | H1 = 4.2e3; H2 = -11e3; H3 = -41.85e3; 51 | rho = 934.2; cp = 3.01e3; V = 1e-2; 52 | tau = 80; Tf = 403.15; Caf = 1000; 53 | UA = 0.215*1120; R = 8.3145; Tk = 402.1; 54 | 55 | % Configure the optimization solver 56 | op = optimset('Display','iter','MaxIter',700,'MaxFunEvals',1e5,'TolFun',1e-8,'TolX',1e-8); 57 | odeopt = odeset('Abstol',1e-6,'Reltol',1e-4); 58 | 59 | % The initial condition is part of the dataset!!! 60 | y0 = [Cae(1) Cbe(1) Te(1)]'; 61 | 62 | % Call the optimization solver 63 | parEst = fminsearch(@fobj,par0,op); 64 | 65 | % Calculate ycalc with the estimated parameters for comparison 66 | tspan = linspace(0,te(end),100)'; 67 | [tc,yc] = ode15s(@model,tspan,y0,odeopt,parEst); 68 | [~,yc2] = ode15s(@model,te,y0,odeopt,parEst); 69 | 70 | % Plot data 71 | close all 72 | 73 | Cac = yc(:,1); Cac2 = yc2(:,1); 74 | Cbc = yc(:,2); Cbc2 = yc2(:,2); 75 | Tc = yc(:,3); Tc2 = yc2(:,3); 76 | 77 | % measurement errors 78 | errCa = 20*ones(size(Cae)); 79 | errCb = 20*ones(size(Cbe)); 80 | errT = 5*ones(size(Te)); 81 | 82 | colors = get(0, 'DefaultAxesColorOrder'); 83 | 84 | figured; 85 | xlabel('Time (s)') 86 | ylabel('Concentration (mol \cdot m^{-3})') 87 | plot(tc,Cac,tc,Cbc,'LineWidth',1.5); 88 | hold on 89 | errorbar(te,Cae,errCa,'-s','MarkerSize',8,'LineStyle','none', ... 90 | 'MarkerEdgeColor',colors(1,:),'MarkerFaceColor',colors(1,:),'Color',colors(1,:)) 91 | 92 | errorbar(te,Cbe,errCb,'-d','MarkerSize',8,'LineStyle','none', ... 93 | 'MarkerEdgeColor',colors(2,:),'MarkerFaceColor',colors(2,:),'Color',colors(2,:)) 94 | 95 | legend({'Ca_{calc}','Cb_{calc}','Ca_{exp}','Cb_{exp}'},'location','southeast') 96 | hold off 97 | 98 | figured; 99 | xlabel('Time (s)') 100 | ylabel('Temperature (K)') 101 | plot(tc,Tc,'LineWidth',1.5,'Color',colors(3,:)); 102 | hold on 103 | errorbar(te,Te,errT,'-o','MarkerSize',6,'LineStyle','none', ... 104 | 'MarkerEdgeColor',colors(3,:),'MarkerFaceColor',colors(3,:),'Color',colors(3,:)) 105 | 106 | legend({'Calculated','Experimental'},'location','southeast') 107 | hold off 108 | 109 | figured; 110 | subplot(311) 111 | plot(Cac2,Cac2,'lineWidth',1.5) 112 | hold on 113 | errorbar(Cac2,Cae,errCa,'-s','MarkerSize',8,'LineStyle','none', 'handlevisibility','off', ... 114 | 'MarkerEdgeColor',colors(1,:),'MarkerFaceColor',colors(1,:),'Color',colors(1,:)) 115 | legend('Ca','location','southeast') 116 | set(gca,'Fontsize',16,'ygrid','on','xlim',[0 max(Cae)],'ylim',[0 max(Cae)]) 117 | hold off 118 | 119 | subplot(312) 120 | plot(Cbc2,Cbc2,'lineWidth',1.5,'Color',colors(2,:)) 121 | ylabel('Experimental values') 122 | hold on 123 | errorbar(Cbc2,Cbe,errCb,'-d','MarkerSize',8,'LineStyle','none', 'handlevisibility','off', ... 124 | 'MarkerEdgeColor',colors(2,:),'MarkerFaceColor',colors(2,:),'Color',colors(2,:)) 125 | legend('Cb','location','southeast') 126 | set(gca,'Fontsize',16,'ygrid','on','xlim',[0 max(Cbe)],'ylim',[0 max(Cbe)]) 127 | hold off 128 | 129 | subplot(313) 130 | plot(Tc2,Tc2,'lineWidth',1.5,'Color',colors(3,:)) 131 | xlabel('Predicted values') 132 | hold on 133 | errorbar(Tc2,Te,errT,'-o','MarkerSize',6,'LineStyle','none', 'handlevisibility','off', ... 134 | 'MarkerEdgeColor',colors(3,:),'MarkerFaceColor',colors(3,:),'Color',colors(3,:)) 135 | legend('Temperature','location','southeast') 136 | set(gca,'Fontsize',16,'ygrid','on','xlim',[min(Te) max(Te)],'ylim',[min(Te) max(Te)]) 137 | hold off 138 | 139 | 140 | function f = fobj(par) 141 | % ycalc must be evaluated at the same experimental time points ti 142 | [~,ycalc] = ode15s(@model,te,y0,odeopt,par); 143 | f = (Cae - ycalc(:,1)).^2 + (Cbe - ycalc(:,2)).^2 + (Te - ycalc(:,3)).^2; 144 | f = sum(f); 145 | 146 | end 147 | 148 | function dy = model(~,y,par) 149 | 150 | % Van de vusse reaction in a CSTR 151 | Ca = y(1); Cb = y(2); T = y(3); 152 | 153 | % The parameters to be estimated 154 | k1 = par(1); k2 = par(2); k3 = par(3); 155 | E1 = par(4); E2 = par(5); E3 = par(6); 156 | 157 | % reaction rates 158 | r1 = k1*exp(-E1/R/T)*Ca; 159 | r2 = k2*exp(-E2/R/T)*Cb; 160 | r3 = k3*exp(-E3/R/T)*Ca^2; 161 | 162 | % mass balances 163 | dCa = (Caf - Ca)/tau -r1 -2*r3; 164 | dCb = -Cb/tau + r1 - r2; 165 | dT = (Tf - T)/tau -1/rho/cp*( UA/V*(T - Tk) + H1*r1 + H2*r2 +H3*r3 ); 166 | 167 | % vector of derivatives 168 | dy = [dCa dCb dT]'; 169 | 170 | end 171 | 172 | 173 | end 174 | 175 | --------------------------------------------------------------------------------