Equilibrium

examples/equilibrium.py demonstrates how scaling can be used together with tolerances to achieve desired accuracy from the numerical integration.

We will consider the transient towards an equilibrium for a dimerization:

\[\begin{split}A + &B &\overset{k_f}{\underset{k_b}{\rightleftharpoons}} C\end{split}\]

The analytic solution is (its derivation is left as an exercise):

\[\begin{split}A(t) &= A_0 - x(t) \\ B(t) &= B_0 - x(t) \\ C(t) &= C_0 + x(t) \\ x(t) &= \frac{(U-b)(U+b)(e^{Ut}-1)}{2k_f(Ue^{Ut} + U - qe^{Ut} + q)} \\\end{split}\]

where

\[\begin{split}U &= \sqrt{A^2k_f^2 + 2ABk_f^2 - 2Ak_bk_f + B^2k_f^2 - 2Bk_bk_f + 4Ck_bk_f + k_b^2} \\ q &= Ak_f + Bk_f - k_b\end{split}\]
$ python equilibrium.py --help
usage: equilibrium.py [-h] [--tend TEND] [-A A0] [-B B0] [-C C0] [--nt NT]
                      [--t0 T0] [--kf KF] [--kb KB] [-a ATOL] [-r RTOL]
                      [--logy] [--logt] [--num-jac] [--plot]
                      [--savefig SAVEFIG] [--splitplots] [--plotlogy]
                      [--plotsymlogy] [--plotlogt] [--scale-err SCALE_ERR]
                      [--scaling SCALING] [-v]

    Runs the integration and (optionally) plots:

    - Individual concentrations as function of time
    - Reaction Quotient vs. time (with equilibrium constant as reference)
    - Numerical error commited (with tolerance span plotted)
    - Excess error committed (deviation outside tolerance span)

    Concentrations (A0, B0, C0) are taken to be in "M" (molar),
    kf in "M**-1 s**-1" and kb in "s**-1", t0 and tend in "s"


optional arguments:
  -h, --help            show this help message and exit
  --tend TEND           1.9
  -A A0, --A0 A0        4.2
  -B B0, --B0 B0        3.1
  -C C0, --C0 C0        1.4
  --nt NT               100
  --t0 T0               0.0
  --kf KF               0.9
  --kb KB               0.23
  -a ATOL, --atol ATOL  u'1e-7,1e-6,1e-5'
  -r RTOL, --rtol RTOL  u'1e-6'
  --logy                False
  --logt                False
  --num-jac             False
  --plot                False
  --savefig SAVEFIG     u'None'
  --splitplots          False
  --plotlogy            False
  --plotsymlogy         False
  --plotlogt            False
  --scale-err SCALE_ERR
                        1.0
  --scaling SCALING     1.0
  -v, --verbose         False

Here is an example generated by:

$ python equilibrium.py --plot --savefig equilibrium.png
../_images/equilibrium.png

If concentrations are far from 1 (and below abstol) the accuracy of the numerical solution will be very poor:

$ python equilibrium.py --A0 1.0 --B0 1e-10 --C0 1e-30 --kf 10 --kb 1 --t0 0 --tend 5 --plot --plotlogy --plotlogt --savefig equilibrium_unscaled.png
../_images/equilibrium_unscaled.png

But by scaling the concentrations so that the smallest is well above the absolute tolerance we can get accurate results:

$ python equilibrium.py --scaling 1e10 --A0 1.0 --B0 1e-10 --C0 1e-30 --kf 10 --kb 1 --t0 0 --tend 5 --plot --plotlogy --plotlogt --savefig equilibrium_scaled.png
../_images/equilibrium_scaled.png
equilibrium.analytic_x(t, A, B, C, kf, kb, _exp=<ufunc 'exp'>)[source]
Analytic solution to the dimeriztion reaction:
A + B <=> C; (K = kf/kb)
equilibrium.integrate_rd(tend=1.9, A0=4.2, B0=3.1, C0=1.4, nt=100, t0=0.0, kf=0.9, kb=0.23, atol=u'1e-7, 1e-6, 1e-5', rtol=u'1e-6', logy=False, logt=False, num_jac=False, plot=False, savefig=u'None', splitplots=False, plotlogy=False, plotsymlogy=False, plotlogt=False, scale_err=1.0, scaling=1.0, verbose=False)[source]

Runs the integration and (optionally) plots:

  • Individual concentrations as function of time
  • Reaction Quotient vs. time (with equilibrium constant as reference)
  • Numerical error commited (with tolerance span plotted)
  • Excess error committed (deviation outside tolerance span)

Concentrations (A0, B0, C0) are taken to be in “M” (molar), kf in “M**-1 s**-1” and kb in “s**-1”, t0 and tend in “s”