Note
Status: Verified Validated against codebase (Feb 2026).
This document explains how the Python code in global_model.py translates a set of reaction formulas and rate coefficients into a system of Ordinary Differential Equations (ODEs) that can be solved numerically.
The model is a 0D Global Model, which means we assume the plasma is a spatially uniform "well-mixed reactor." We solve two fundamental conservation laws:
- Particle Balance: For each species
iin the plasma, the rate of change of its densityn_iis the sum of all creation rates minus the sum of all loss rates. - Power Balance: The rate of change of the total electron energy density is the absorbed power from the external source minus the sum of all power loss channels.
The particle balance equation for a single species i is written as:
Our code generalizes this using the concept of a stoichiometric coefficient. For a given reaction j, the net change of species i is described by its stoichiometric coefficient, S_{ij}.
The engine's first job is to parse the reaction formulas from the input file and build a net stoichiometric matrix, S.
Consider a generic reaction j:
aA + bB -> cC + dD
The _parse_formula and _create_stoich_matrices functions in global_model.py perform the following steps for each reaction j:
- Create a "Left" (Reactant) Vector (
L_j): For the example above, this vector would have a value ofaat the index for speciesA,bat the index forB, and 0 everywhere else. - Create a "Right" (Product) Vector (
R_j): This vector would havecat the index forC,dforD, and 0 elsewhere. - Calculate the Net Change Vector (
S_j): The net change for reactionjis simplyS_j = R_j - L_j.
By stacking these row vectors S_j for all reactions, we form the net stoichiometric matrix S. The element S_{ij} represents the net number of particles of species i created (if positive) or destroyed (if negative) by one instance of reaction j.
The rate R_j of a reaction j (in units of m⁻³s⁻¹) is calculated from the law of mass action. For our example aA + bB -> cC + dD, the rate is:
where k_j is the reaction rate coefficient (in units of m³(a+b-1)s⁻¹) and [X] is the density of species X.
The _ode_system function in global_model.py calculates a reaction rate vector R where each element R_j is computed as follows:
# Simplified logic from _ode_system
rate = k_j # The rate coefficient from the input file
reactants = self.stoich_matrix_left[j, :]
for species_idx in range(num_species):
if reactants[species_idx] > 0:
rate *= densities[species_idx] ** reactants[species_idx]
reaction_rates[j] = rateWith the stoichiometric matrix S and the reaction rate vector R, the complete system of ODEs for all N species densities can be expressed in a single, elegant matrix-vector product.
The rate of change of the density of species i is the sum of the rates of all reactions, each weighted by its stoichiometric coefficient for that species:
In vector form, where n is the column vector of all species densities, this is:
The code implements this with the following lines in _ode_system:
# dY_dt is the vector dn/dt
dY_dt = np.zeros(self.num_species)
for i in range(self.num_species):
# This is a dot product of the i-th column of S^T with R
dY_dt[i] = np.sum(reaction_rates * self.stoich_matrix_net[:, i])This dY_dt vector is the first part of the system of derivatives that is passed to the ODE solver (solve_ivp).
The second part of the system is the power balance equation for the electrons. The variable being solved for is the electron energy density, Uₑ, defined as:
The ODE is: $$ \frac{d U_e}{dt} = (P_{abs} / V) - P_{loss} $$
The absorbed power P_abs is given in the input file in Watts (Joules/sec). The code converts this to an energy density rate in eV·m⁻³s⁻¹:
This is implemented as:
power_W = self.mdef.get('constant_data', {}).get('power_input_W', 0.0)
Q_abs = power_W / self.const['q_e'] / params['volume']The total power loss P_loss (or Q_loss in the code) is the sum of the power lost in every reaction j. The power lost in a single reaction is the rate of that reaction multiplied by the energy lost per reaction, ε_j.
The ε_j values are provided in the input file. They represent different physical processes:
- Collisional Loss (
ε_coll): For inelastic collisions (ionization, excitation),ε_jis the threshold energy of the reaction in eV. - Wall Loss (
ε_wall): For particles lost to the wall,ε_jis the average energy carried to the wall by each lost ion-electron pair. For example, for an ioni, this is oftenε_i ≈ 2 T_{eV} + 0.5 T_{eV} + V_s, representing the kinetic energy of the electron, the ion, and the energy gained by the ion crossing the sheath potentialV_s.
The code implements this summation in _ode_system:
Q_loss = 0.0
for i, reaction in enumerate(self.mdef['reactions']):
energy_loss = reaction['energy_loss_func'](params)
q_loss_i = reaction_rates[i] * energy_loss
Q_loss += q_loss_iThe final derivative for the energy equation is dE_dt = Q_abs - Q_loss. This scalar value is appended to the dY_dt vector, forming the complete system of N+1 derivatives that is returned to the solve_ivp function at every time step.