How to properly model a kinetic reaction system from experimental data using lsqcurvefit?
44 views (last 30 days)
Show older comments
Hello,
I am currently trying to model chemical kinetics using MATLAB.
Currently, I am having a code that works but gives me a set on non-sense results. So there must be a mistake in the method, or in my way of thinking.
I will introduce the problem, the available data and my MATLAB integration. I hope you can help me find the mistakes and obtain a fit for my experimental data.
Thanks in advance.
Problem introduction:
This system consists of four reactions. Compound c1 reacts to compound c2. Compound c2 can react further to c3, although I am currently neglecting the reaction of c2 to c3. The challenge is that both c1 and c2 can undergo polymerization reactions to p1 and p2. This of course has an influence on their concentrations and therefore on kinetics. Fitting just reaction 1 (c1 to c2) and neglecting reactions 3 and 4 leads to extreme reaction orders. Also, during experiments the mass balance can never close properly if these reactions are to be neglected.
I have shared my code at the bottom of this question so you can see how I implemented the three reactions into MATLAB.
Available data:
Over a certain time period we have the concentration profiles of compounds c1 and c2. The amount of c3 is negligable, p1 and p2 can not be analyzed.
These profiles look like this:
This data is figurated however close to the experimental data. It should be well sufficient for this modeling challenge.
MATLAB Integration:
In MATLAB, I have used the lsqcurvefit method as described here: https://nl.mathworks.com/matlabcentral/answers/43439-monod-kinetics-and-curve-fitting. Thanks Star Strider for this starting point. I can now create a code that runs. However, the results from lsqcurvefit do not match the experimental data at all. Moreover, the results are different for every run.
My goal is to plot in figure 2 the experimental data and the model fit. As you will probably see, it is a mess although I am not sure why.
Your time and effort is very much apprieciated.
My code:
clear, clc
%Reaction system
%c1 --> c2 --> c3 (c2 --> c3 for now neglected)
%c1 --> p1
%c2 --> p2
%Reaction rates are described as r = k*c^n
%As c2 --> c3 is found negligable, data is available only for c1 and c2
%The last two reaction equations describe polymerization reactions, there
%is no data available for both p1 and p2.
%Data
time = [10 20 40 60 180];
c1 = [0.508 0.442 0.385 0.354 0.258];
c2 = [0.246 0.301 0.359 0.398 0.489];
c1_0 = 0.711;
c2_0 = 0;
%Plot
time_plot = [0,time];
c1_plot = [c1_0,c1];
c2_plot = [c2_0,c2];
figure(1)
plot(time_plot,c1_plot,'bo')
grid on
hold on
plot(time_plot,c2_plot,'ro')
hold off
xlabel('Time [min]')
ylabel('Concentration [mol/L]')
legend('c1','c2')
%Data processing
x_in = time';
y_in = [c1',c2'];
%Starting point for lsqcurvefit
p0 = [0.1,1,0.1,1,0.1,1];
lb=zeros(6,1);
ub=4*ones(6,1);
%Lsqcurvefit
[p,Rsdnrm,Rsd,ExFlg,OptmInfo,Lmda,Jmat] = lsqcurvefit(@diff1,p0,x_in,y_in,lb,ub);
FitData = diff1(p,time,1);
c1_out = FitData(:,1);
c2_out = FitData(:,2);
%Plot
c1_out_plot = [c1_0,c1_out'];
c2_out_plot = [c2_0,c2_out'];
figure(2)
plot(time_plot,c1_plot,'bo')
grid on
hold on
plot(time_plot,c2_plot,'ro')
plot(time_plot,c1_out_plot,'b')
plot(time_plot,c2_out_plot,'r')
hold off
xlabel('Time [min]')
ylabel('Concentration [mol/L]')
legend('c1','c2')
function C = diff1(p,time,~)
%dc1/dt = -(k1*c1^n1+k3*c1^k3)
%dc2/dt = k1*c1^n1-(k2*c2^k2+k4*c2^n4)
%dc3/dt = k2*c2^n2 = 0
%variables: c1 = x(1), c2 = x(2)
%parameters: k1 = p(1), n1 = p(2), k3 = p(3),
%n3 = p(4), k4 = p(5), n4 = p(6)
%for now we neglect reaction r2 of c2 to c3.
x0 = rand(2,1);
[t,Cout] = ode45(@DifEq, time, x0);
function dC = DifEq(t, x)
xdot = zeros(2,1);
xdot(1) = -(p(1).*x(1).^p(2)+p(3).*x(1)^p(4));
xdot(2) = p(1).*x(1).^p(2)-p(5).*x(2).^p(6);
dC = xdot;
end
C = Cout;
end
0 Comments
Accepted Answer
Star Strider
on 2 Jul 2019
Thank you for referring to my ’Monod Kinetics’ Answer! I’m glad you found it useful!
There are two problems with your code.
First, ‘xdot(1)’ should be:
xdot(1) = -(p(1).*x(1).^p(2)-p(3).*x(1)^p(4));
↑
with the sign of the second term being negative (corrected here).
Second, it is a good idea to always include the initial values as parameters, as a rule, the last parameters. So here:
p0 = [0.1,1,0.1,1,0.1,1,c1(1),c2(1)];
and then incorporating those changes in ‘diff1’:
function C = diff1(p,time,~)
%dc1/dt = -(k1*c1^n1+k3*c1^k3)
%dc2/dt = k1*c1^n1-(k2*c2^k2+k4*c2^n4)
%dc3/dt = k2*c2^n2 = 0
%variables: c1 = x(1), c2 = x(2)
%parameters: k1 = p(1), n1 = p(2), k3 = p(3),
%n3 = p(4), k4 = p(5), n4 = p(6)
%for now we neglect reaction r2 of c2 to c3.
x0 = p(7:8);
[t,Cout] = ode45(@DifEq, time, x0);
function dC = DifEq(t, x)
xdot = zeros(2,1);
xdot(1) = -(p(1).*x(1).^p(2)-p(3).*x(1)^p(4));
xdot(2) = p(1).*x(1).^p(2)-p(5).*x(2).^p(6);
dC = xdot;
end
C = Cout;
end
the estimated parameters are:
p =
0.105408507696630
3.999984376449200
0.000078501021266
0.007282181170239
0.002202264694465
3.902023967475535
0.502316713470111
0.244896706878439
and your model fits your data almost exactly!
4 Comments
Star Strider
on 5 Jul 2019
As always, my pleasure!
I had some problems yesterday getting ga to converge successfully. I intend to keep working on it, and if successful, will post the relevant code and results to a Comment here.
More Answers (0)
See Also
Categories
Find more on National Instruments Frame Grabbers in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!