Week 5: Chemical Kinetics and Numerical Integration

ODE Results

Overview and Objectives

Last week we used a system of integrated rate equations to model the time evolution of H\(_3^+\) and its deuterated isotopologues in an ion trap at 13.5 K, based on data from E. Hugo et al. (2009) J. Chem. Phys. 130, 164302. This approach suffered from a few serious limitations:

These limitations motivate an alternative approach to solving for concentrations/abundances as a function of time. In fact, models of most “real” chemical systems (e.g., atmospheric chemistry, marine chemistry, combustion, etc.) involve tens or hundreds of molecules linked by hundreds or thousands of elementary reactions, making the integrated rate approach impossible. General chemical kinetics models can be made by numerically integrating the coupled differential rate equations, and that is the subject of this week’s notebook.

Background

H\(_3^+\) Deuteration

Last week, we considered the following system of reactions under pseudo-first-order conditions (i.e., [HD] was constant):

Reaction Rate (cm\(^{-3}\) s\(^{-1}\))
H\(_3^+\) + HD \(\to\) H\(_2\)D\(^+\) + H\(_2\) \(k_1\)[H\(_3^+\)][HD]
H\(_2\)D\(^+\) + HD \(\to\) D\(_2\)H\(^+\) + H\(_2\) \(k_2\)[H\(_2\)D\(^+\)][HD]
D\(_2\)H\(^+\) + HD \(\to\) D\(_3^+\) + H\(_2\) \(k_3\)[D\(_2\)H\(^+\)][HD]

Because the temperature was low, we ignored the reverse reactions. However, it turns out that ignoring the reverse reactions is not a good assumption! There are 2 reasons for this:

Reaction Forward Rate (cm\(^{-3}\) s\(^{-1}\)) Reverse Rate (cm\(^{-3}\) s\(^{-1}\))
H\(_3^+\) + HD \(\rightleftharpoons\) H\(_2\)D\(^+\) + H\(_2\) \(k_1\)[H\(_3^+\)][HD] \(k_{-1}\)[H\(_2\)D\(^+\)][H\(_2\)]
H\(_2\)D\(^+\) + HD \(\rightleftharpoons\) D\(_2\)H\(^+\) + H\(_2\) \(k_2\)[H\(_2\)D\(^+\)][HD] \(k_{-2}\)[D\(_2\)H\(^+\)][H\(_2\)]
D\(_2\)H\(^+\) + HD \(\rightleftharpoons\) D\(_3^+\) + H\(_2\) \(k_3\)[D\(_2\)H\(^+\)][HD] \(k_{-3}\)[D\(_3^+\)][H\(_2\)]

\[ \frac{\text{d}[\text{H}_3^+]}{\text{d}t} = -k_1[\text{H}_3^+][\text{HD}] + k_{-1} [\text{H}_2\text{D}^+][\text{H}_2] \]

\[ \frac{\text{d}[\text{H}_2\text{D}^+]}{\text{d}t} = k_1[\text{H}_3^+][\text{HD}] + k_{-2}[\text{D}_2\text{H}^+][\text{H}_2] - k_2[\text{H}_2\text{D}^+][\text{HD}] - k_{-1} [\text{H}_2\text{D}^+][\text{H}_2]\]

\[ \frac{\text{d}[\text{D}_2\text{H}^+]}{\text{d}t} = k_2[\text{H}_2\text{D}^+][\text{HD}] + k_{-3}[\text{D}_3^+][\text{H}_2] - k_{-2}[\text{D}_2\text{H}^+][\text{H}_2] - k_3[\text{D}_2\text{H}^+][\text{HD}]\]

\[ \frac{\text{d}[\text{D}_3^+]}{\text{d}t} = k_3[\text{D}_2\text{H}^+] [\text{HD}]- k_{-3}[\text{D}_3^+][\text{H}_2]\]

\[ \frac{\text{d}[\text{H}_2]}{\text{d}t} = k_1[\text{H}_3^+][\text{HD}] + k_2[\text{H}_2\text{D}^+][\text{HD}] + k_3[\text{D}_2\text{H}^+][\text{HD}] \\ - k_{-1} [\text{H}_2\text{D}^+][\text{H}_2] - k_{-2}[\text{D}_2\text{H}^+][\text{H}_2] - k_{-3}[\text{D}_3^+][\text{H}_2] \]

\[ \frac{\text{d}[\text{HD}]}{\text{d}t} = k_{-1} [\text{H}_2\text{D}^+][\text{H}_2] + k_{-2}[\text{D}_2\text{H}^+][\text{H}_2] + k_{-3}[\text{D}_3^+][\text{H}_2] \\ - k_1[\text{H}_3^+][\text{HD}] - k_2[\text{H}_2\text{D}^+][\text{HD}] - k_3[\text{D}_2\text{H}^+][\text{HD}] \]

Since we know the abundances of all 6 species at the beginning of the experiment, we can numerically integrate this system of equations to compute their abundances as a function of time. This is an example of an initial value problem: we know the initial values of the abundances and their rates of change, and we want to know the abundances at a later time.

Basic Numerical Integration: Forward Euler Method

The Forward Euler Method is the simplest algorithm for numerically integrating differential equations. To keep things simple, we’ll use a 1D function as an example, but the same concepts apply to multidimensional ones as well. Recall that the derivative of a function \(f^\prime(t)\) is equal to the slope of the function’s tangent line: d\(f\)/d\(t\). If we know the value of the function at one point \(f(t)\), we can take a small time step \(\Delta t\). The derivative then approximately becomes:

\[ f^\prime(t) \approx \frac{\Delta f}{\Delta t} = \frac{f(t+\Delta t) - f(t)}{\Delta t} \]

and so we can rearrange to get

\[ f(t+\Delta t) \approx f(t) + f^\prime(t)\,\Delta t \]

This formula becomes exact as \(\Delta t \to 0\), but practically \(\Delta t\) is always a (small) nonzero number. We can then repeat this process for as many time steps as needed to reach our target final time. But, since the Euler method is approximate, there may be some error associated with each step (the local truncation error). For the simple Euler method, the local truncation error scales with \((\Delta t)^2\) as long as the step size is small. The global truncation error is just the sum of all of the local truncation errors at each step. Since the number of steps taken is equal to \((t_f-t_0)/\Delta t\), the global truncation error is proportional to \((\Delta t)^2/(\Delta t) = (\Delta t)^1\). So in total we expect the error to be proportional to the step size raised to the first power, and the forward Euler method is considered a first-order method. Other methods exist whose errors are proportional to higher powers of \(\Delta t\), which is more desirable since a small reduction in step size leads to a big reduction in the error. An example of the forward Euler method applied to the function \(f(t) = 10e^{-0.1t}\) is shown in the figure below, and the code is in the euler.ipynb file linked below.

Example Euler Method

For our chemical kinetics problem, we can simply represent the concentrations as an array, and the forward Euler method becomes

\[ \begin{bmatrix} n_1 \\ n_2 \\ \vdots \\ n_N \end{bmatrix} = \begin{bmatrix} \text{d}n_1/\text{d}t \\ \text{d}n_2/\text{d}t \\ \vdots \\ \text{d}n_N/\text{d}t \end{bmatrix}\Delta t \]

In our case, we have 6 molecules, and each molecule has a corresponding rate equation, so \(N=6\) and the derivatives are given by the equations above.

Higher-Order Methods

Scipy of course has more sophisticated numerical integration algorithms available for use; these can be found in the scipy.integrate module. Specifically, for our purpose the scipy.integrate.solve_ivp function allows for solving these initial value problems using your choice of integration method and a bunch of extra features, some of which we will explore in the ode.ipynb notebook. The most commonly-used higher order routine is the fourth-order Runge-Kutta method (RK4). Here we’ll briefly outline the algorithm as applied to our specific problem of chemical kinetics (see the further reading materials for a more complete discussion). To keep things simple, we will write the algorithm for one molecule, but it can be easily extended like before by using an array of molecules instead of a single value. Our initial value problem is:

\[ \left.\frac{\text{d}n_i}{\text{d}t}\right|_{t_i} = f(n_i), \quad n_0 \equiv n(t = t_0) \]

Here, \(n_i\) represents the abundance of the molecule at time \(t_i\), and we know its value \(n_0\) at \(t_0\). Its derivative is a function of its abundance \(f(n_i)\) but otherwise does not explicitly depend on time, so this is considered a time-invariant system. Given a step size \(\Delta t\), the RK4 method is:

\[ n_{i+1} = n_i + \frac{1}{6}(K_1 + 2K_2 + 2K_3 + K_4)\Delta t \\[1em] t_{i+1} = t_i + \Delta t \]

In this equation, the \(K\) values are different estimates of the derivative, and the formula uses a weighted average of these estimates to calculate the actual change in abundance. The \(K\) values are calculated as:

\[ K_1 = f(n_i) \\[1em] K_2 = f(n_i + \frac{K_1}{2}\Delta t) \\[1em] K_3 = f(n_i + \frac{K_2}{2}\Delta t) \\[1em] K_4 = f(n_i + K_3\Delta t) \]

In these equations, \(K_1\) is the estimate of the slope from the Euler method. \(K_2\) is an estimate of the slope at the midpoint of the step size. \(K_3\) is a different estimate of the slope at the midpoint, but is calculated using the slope from \(K_2\) instead of \(K_1\). Finally, \(K_4\) is an estimate of the slope at the end of the step. The figure below from Wikipedia attempts to illustrate the process, though the notation is slightly different.

RK4 slopes - HilberTraum

Fortunately, this and other algorithms are included in Scipy, so there is no need to code them by hand. This week we will use the RK4 method to numerically integrate the system of equations for the H\(_3^+\) deuteration experiment and see if we can improve agreement with the experimental data by including the reverse reactions.

Further Reading

Notebooks