使用Mathematica对化学反应平衡进行模拟

使用Mathematica对化学反应平衡进行模拟

反应平衡动力模拟

对于任何一个溶液中发生的基元可逆反应可以用以下方式表示

graph LR
    A[反应物] --k1--> B
    B[生成物] --k2--> A

\(k_1\)\(k_2\)是此反应的反应速率常数

反应方程式可表示为

\[ A\leftrightharpoons B\quad K=\frac{k_1}{k_2} \]

或者也可以写成下面这样(我瞎编的)

\[ A\stackrel{k_2}{\underset{k_1}{\leftrightharpoons}} B \]

由此可以写出物质\(A\)\(B\)的浓度关于时间\(t\)的微分方程

\[ \begin{cases} \frac{dA}{dt}=-k_1A+k_2B\\ \frac{dB}{dt}=+k_1A-k_2B \end{cases} \]

再翻译翻译就是

\[ \begin{cases} A'(t)=-k_1A(t)+k_2B(t) \\ B'(t)=+k_1A(t)-k_2B(t) \end{cases} \]

如果反应的反应物或生成物有多个物质或化学计量数不为\(1\),则需要带入物质浓度的幂

\[ a_1A_1+a_2A_2+\cdots+a_nA_n\stackrel{k_2}{\underset{k_1}{\leftrightharpoons}}b_1B_1+b_2B_2+\cdots+b_mB_m \]

\[ \begin{cases} \frac{dA_i}{dt}=-k_1\prod_{u=1}^nA_u^{a_u}+k_2\prod_{v=1}^mB_v^{b_v} \\ \frac{dB_j}{dt}=+k_1\prod_{u=1}^nA_u^{a_u}-k_2\prod_{v=1}^mB_v^{b_v} \end{cases}\\ \begin{cases} A_i'(t)=-k_1\prod_{u=1}^nA_u^{a_u}(t)+k_2\prod_{v=1}^mB_v^{b_v}(t) \\ B_j'(t)=+k_1\prod_{u=1}^nA_u^{a_u}(t)-k_2\prod_{v=1}^mB_v^{b_v}(t) \end{cases} \]

Mathematica!

看我博客的白嫖教程!

完整程序会放在最后

首先我们需要一个Mathematica

其次需要一些用来测试的化学反应

这里以碳酸的电离平衡为例

\[ \scriptsize \begin{matrix} \mathrm{CO_2}+&\mathrm{H_2O}&\stackrel{k_{12}}{\underset{k_{11}}{\leftrightharpoons}}&\mathrm{H_2CO_3}&&&&&&&\quad K=\frac{k_{11}}{k_{12}}=&\frac{1}{600}&& \\ &&&\mathrm{H_2CO_3}&\stackrel{k_{22}}{\underset{k_{21}}{\leftrightharpoons}}&\mathrm{HCO_3^-}&&+&\mathrm{H^+}&&\quad K=\frac{k_{21}}{k_{22}}=&4.3&\cdot&10^{-7} \\ &&&&&\mathrm{HCO_3^-}&\stackrel{k_{32}}{\underset{k_{31}}{\leftrightharpoons}}\mathrm{CO_3^{2-}}&+&\mathrm{H^+}&&\quad K=\frac{k_{31}}{k_{32}}=&5.6&\cdot&10^{-11} \\ &\mathrm{H_2O}&\stackrel{k_{42}}{\underset{k_{41}}{\leftrightharpoons}}&&&&&&\mathrm{H^+}&+\mathrm{OH^-}&\quad K=\frac{k_{41}}{k_{42}}=&&&10^{-14}&=K_w \end{matrix} \]

初始化

首先,为了避免奇奇怪怪的\(\mathfrak{BUG}\),先清除全局变量

1
Clear["Global`*"]

其次需要声明方程的反应速率常数

1
2
3
4
5
6
7
8
9
10
b=5;
k11=4.3 * 10^(-3.5 +b);
k12=1 * 10^(+3.5 +b);
k21=5.6 * 10^(-5.5 +b);
k22=1 * 10^(+5.5 +b);
k31=1 * 10^(-0 );
k32=1 * 10^(+14 );
k41=1 * 10^( +0);
k42=600 * 10^( +0);
T=10;

T是时间上限,即微分方程自变量的最大值

其中\(k_{ij}\)值与化学方程式中的\(k_{ij}\)有些许不同和错位,亿些细节放到最后再解释

接着就可以开始解微分方程了~

微分方程!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{H2CO3,H,OH,HCO3,CO3,CO2}=NDSolveValue[{

H2CO3'[t] == -k11*H2CO3[t]+k12*H[t]*HCO3[t] +k41*CO2[t]-k42*H2CO3[t] ,
HCO3'[t] == +k11*H2CO3[t]-k12*H[t]*HCO3[t] -k21*HCO3[t]+k22*H[t]*CO3[t] ,
CO3'[t] == +k21*HCO3[t]-k22*H[t]*CO3[t] ,
H'[t] == +k11*H2CO3[t]-k12*H[t]*HCO3[t] +k21*HCO3[t]-k22*H[t]*CO3[t] +k31-k32*H[t]*OH[t] ,
OH'[t] == +k31-k32*H[t]*OH[t] ,
CO2'[t] == -k41*CO2[t]+k42*H2CO3[t] ,

H2CO3[0] == 0 ,
H[0] == 10^(-7) ,
OH[0] == 10^(-7),
HCO3[0] == 0 ,
CO3[0] == 0.01 ,
CO2[0] == 0

},{H2CO3,H,OH,HCO3,CO3,CO2},{t,0,T}];

NDSolveValue[微分方程列表,所求因变量列表,{自变量,自变量最小值,自变量最大值}],返回数值解的列表,其元素为所求因变量与自变量的数值关系(大概)

可视化

1
2
3
4
5
6
7
8
Plot[{H2CO3[t]},{t,0,T},PlotLegends -> "Expressions"];
Plot[{HCO3[t]},{t,0,T},PlotLegends -> "Expressions"];
Plot[{CO3[t]},{t,0,T},PlotLegends -> "Expressions"];
Plot[{{H2CO3[t]},HCO3[t],CO3[t]},{t,0,T},PlotLegends -> "Expressions"]
Plot[{pH[t]=-Log10[H[t]],pOH[t] =-Log10[OH[t]]},{t,0,T}, PlotLegends -> "Expressions",PlotRange -> {0, 14}]
Plot[{H[t]*HCO3[t]/H2CO3[t] ,K1[t]=k11/k12},{t,0,T}, PlotLegends -> "Expressions"]
Plot[{H[t]*CO3[t]/HCO3[t] ,K2[t]=k21/k22},{t,0,T}, PlotLegends -> "Expressions"]
Plot[{H[t]*OH[t] ,K3[t]=k31/k32},{t,0,T}, PlotLegends -> "Expressions"]

Plot[因变量列表,{自变量,自变量最小值,自变量最大值},其他参数],返回绘制的折线图。其他参数中PlotLegends -> "Expressions"可显示图例(Plot[]中只绘制一个因变量时无作用)

以上代码绘制了\(\mathrm{H_2CO_3}\)\(\mathrm{HCO_3^-}\)\(\mathrm{CO_3^{2-}}\)\(\mathrm{H^+}\)\(\mathrm{OH^-}\)\(p\mathrm{H}\)\(p\mathrm{OH}\)以及三个可逆反应的\(Q-K\)关系

\(\mathfrak{BUG}\)

  1. 必须要加Clear["Global`*"],否则会出现无法预料的\(\mathfrak{BUG}\)
  2. \(K=\frac{k_1}{k_2}\),但对\(k_1\)\(k_2\)本身的大小并没有要求,即每一组符合条件的\(k_1,k_2\)都可以用于表示同一个方程。对于多个反应,改变其中几个反应的\(k_1,k_2\)(保证\(k_1,k_2\)的比值\(K\)不变),反应达到平衡时各组分浓度不会发生改变,但平衡前各组分浓度变化的过程会发生改变。但\(|k_1|\)\(|k_2|\)越大,方程越快达到平衡。
  3. 程序中所有\(k_{ij}\)是经过调试找到的能较快达到平衡且较“好看”的组合。增加了一个参数b以更好控制\(k\)的数量级。这可能并不符合实际反应原理,但有利于调试
  4. 加入\(\mathrm{H_2O}\stackrel{k_{42}}{\underset{k_{41}}{\leftrightharpoons}}\mathrm{H^+}+\mathrm{OH^-}\)且画出\(p\mathrm{H},p\mathrm{OH}\)后出现了\(p\mathrm{H}+p\mathrm{OH}\neq 14\)的情况。需要将\(k_{41},k_{42}\)增大使得此反应更快平衡(或增大反应时间T也能看到效果)这里调整到\(k_{41}=1,k_{42}=10^{14}\),若再调高则超出了Mathematicaint范围(¿)
  5. 测试中各组分浓度初值尽量不超过\(1(mol/L)\)(我猜是这个单位?),否则可能出现不论如何调整\(k\)T也没有反应的情况,原因不明,我猜是因为水解平衡反应不过来了
  6. 测试中组分浓度越低,效果越好,这大概就是越稀越水解吧
  7. T较大时可视化的图表中的图线会出现抽风的情况,我猜是因为每次\(dt\)采样的间隔较大的原因
  8. 如果认为\(\mathrm{CO_2}\)生成后立即溢出,即视\(c(\mathrm{CO_2})\)在任何时间均为常数,则\(\mathrm{CO_3^{2-}}\)在酸性环境下生成\(\mathrm{CO_2}\uparrow\)的反应可视化效果较好

完整程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Clear["Global`*"]
b=5;
k11=4.3 * 10^(-3.5 +b);
k12=1 * 10^(+3.5 +b);
k21=5.6 * 10^(-5.5 +b);
k22=1 * 10^(+5.5 +b);
k31=1 * 10^(-0 );
k32=1 * 10^(+14 );
k41=1 * 10^( +0);
k42=600 * 10^( +0);
T=10;

{H2CO3,H,OH,HCO3,CO3,CO2}=NDSolveValue[{

H2CO3'[t] == -k11*H2CO3[t]+k12*H[t]*HCO3[t] +k41*CO2[t]-k42*H2CO3[t] ,
HCO3'[t] == +k11*H2CO3[t]-k12*H[t]*HCO3[t] -k21*HCO3[t]+k22*H[t]*CO3[t] ,
CO3'[t] == +k21*HCO3[t]-k22*H[t]*CO3[t] ,
H'[t] == +k11*H2CO3[t]-k12*H[t]*HCO3[t] +k21*HCO3[t]-k22*H[t]*CO3[t] +k31-k32*H[t]*OH[t] ,
OH'[t] == +k31-k32*H[t]*OH[t] ,
CO2'[t] == -k41*CO2[t]+k42*H2CO3[t] ,

H2CO3[0] == 0 ,
H[0] == 10^(-7) ,
OH[0] == 10^(-7),
HCO3[0] == 0 ,
CO3[0] == 0.01 ,
CO2[0] == 0

},{H2CO3,H,OH,HCO3,CO3,CO2},{t,0,T}];

Plot[{H2CO3[t]},{t,0,T},PlotLegends -> "Expressions"];
Plot[{HCO3[t]},{t,0,T},PlotLegends -> "Expressions"];
Plot[{CO3[t]},{t,0,T},PlotLegends -> "Expressions"];
Plot[{{H2CO3[t]},HCO3[t],CO3[t]},{t,0,T},PlotLegends -> "Expressions"]
Plot[{pH[t]=-Log10[H[t]],pOH[t] =-Log10[OH[t]]},{t,0,T}, PlotLegends -> "Expressions",PlotRange -> {0, 14}]
Plot[{H[t]*HCO3[t]/H2CO3[t] ,K1[t]=k11/k12},{t,0,T}, PlotLegends -> "Expressions"]
Plot[{H[t]*CO3[t]/HCO3[t] ,K2[t]=k21/k22},{t,0,T}, PlotLegends -> "Expressions"]
Plot[{H[t]*OH[t] ,K3[t]=k31/k32},{t,0,T}, PlotLegends -> "Expressions"]

测试输出

1675912940285
1675912960575
1675912981872
1675913031329