From charlesreid1

(Fix LaTeX math: use \cdot separator in unit denominator, and wrap multi-letter subscripts (sp, rxns) in \text (via update-page on MediaWiki MCP Server))
 
(3 intermediate revisions by the same user not shown)
Line 23: Line 23:


<math>
<math>
\frac{d c_i}{dt} = R_i \qquad i = 1 \dots N_{sp}
\frac{d c_i}{dt} = R_i \qquad i = 1 \dots N_{\text{sp}}
</math>
</math>


where:
where:
* <math>c_i</math> is molar volume (units of <math>\frac{\text{mol}}{\text{m}^3}</math>)
* <math>c_i</math> is molar volume (units of <math>\frac{\text{mol}}{\text{m}^3}</math>)
* <math>R_i</math> is the volume-specific species rate of production (units of <math>\frac{\text{mol}}{\text{m}^3 \text{s}}</math>)
* <math>R_i</math> is the volume-specific species rate of production (units of <math>\frac{\text{mol}}{\text{m}^3 \cdot \text{s}}</math>)


==Reaction Production Rate==
==Reaction Production Rate==
Line 35: Line 35:


<math>
<math>
R_i = \sum_{j=1}^{N_{rxns}} \nu_{ij} \mathfrak{R}_j
R_i = \sum_{j=1}^{N_{\text{rxns}}} \nu_{ij} \mathfrak{R}_j
</math>
</math>


Line 60: Line 60:
=Code=
=Code=


For both surfaces and gases, calculating the rates of production is a two-step process.
First, the updateROP() method is called.
Second, the reaction stoichiometry manager object, ReactionStoichMgr, is called.


==Gas Reaction Rates==
==Gas Reaction Rates==
Line 65: Line 70:
The surface reaction rates of production are computed in the GasKinetics class, in GasKinetics.cpp:
The surface reaction rates of production are computed in the GasKinetics class, in GasKinetics.cpp:


<source lang="cpp">
<pre>
void GasKinetics::getNetProductionRates(doublereal* net)
void GasKinetics::getNetProductionRates(doublereal* net)
{
{
Line 71: Line 76:
     m_rxnstoich.getNetProductionRates(m_kk, &m_ropnet[0], net);
     m_rxnstoich.getNetProductionRates(m_kk, &m_ropnet[0], net);
}
}
</source>
</pre>
 
==Surface Reaction Rates==
 
The surface reaction rates of production are computed in the InterfaceKinetics class, in InterfaceKinetics.cpp:
 
<source lang="cpp">
void InterfaceKinetics::getNetProductionRates(doublereal* net)
{
    updateROP();
    m_rxnstoich.getNetProductionRates(m_kk,
                                      &m_kdata->m_ropnet[0],
                                      net);
}
</source>


==Code for Rate of Production==
For both surfaces and gases, calculating the rates of production is a two-step process.
First, the updateROP() method is called.
Second, the reaction stoichiometry manager object, ReactionStoichMgr, is called.


===Gas updateROP()===
===Gas updateROP()===


<source lang="cpp">
<pre>
void GasKinetics::updateROP()
void GasKinetics::updateROP()
{
{
Line 144: Line 128:


     m_ROP_ok = true;
     m_ROP_ok = true;
}
</pre>
==Surface Reaction Rates==
The surface reaction rates of production are computed in the InterfaceKinetics class, in InterfaceKinetics.cpp:
<source lang="cpp">
void InterfaceKinetics::getNetProductionRates(doublereal* net)
{
    updateROP();
    m_rxnstoich.getNetProductionRates(m_kk,
                                      &m_kdata->m_ropnet[0],
                                      net);
}
}
</source>
</source>
Line 265: Line 263:
</source>
</source>


=Flags=


 
{{CanteraFlag}}
 
[[Category:Cantera]]

Latest revision as of 23:18, 14 June 2026

Notes

  • Reaction rates are a functon of TPX, thermodynamic state of gas
  • phase object wraps an equation of state and wraps kinetic object
  • use phase to obtain reaction rates
  • Definition of reaction rate
    • for a component: sum over each reaction rate
  • Cantera phase object - calls
    • production rate for component i
    • production rate for reaction j
    • production rate for ij
    • species production rate for component i
    • species production rate for reaction j
    • species production rate for ij

Definition of Rates

Species Production Rate

In a differential, perfectly uniform control volume in which a chemical reaction is the only phenomena occurring, the species rate of production (species i) can be defined as the change in moles of i with time:

$ \frac{d c_i}{dt} = R_i \qquad i = 1 \dots N_{\text{sp}} $

where:

  • $ c_i $ is molar volume (units of $ \frac{\text{mol}}{\text{m}^3} $)
  • $ R_i $ is the volume-specific species rate of production (units of $ \frac{\text{mol}}{\text{m}^3 \cdot \text{s}} $)

Reaction Production Rate

A species production rate can be further broken up into its contributions from each reaction. These rates of production are stoichiometric and known as the reaction rates of production:

$ R_i = \sum_{j=1}^{N_{\text{rxns}}} \nu_{ij} \mathfrak{R}_j $

where:

  • $ \nu_{ij} $ - stoichiometric coeff of species i in rxn j
  • $ \mathfrak{R}_j $ - volume-specific stoichiometric reaction rate for reaction j

Dealing with Phases

Gas Only: Volumetric Reaction Rates

Volumetric reaction rate is: (volume) x (rate of production per unit volume)

Rate of production per unit volume is: (stoichiometric coefficient) x (rate of production of reaction)

Gas and Surface: Surface Reaction Rates

Surface reaction rates are: (area) x (rate of production per unit area)

Rate of production per area is: (mass cat) x (surface area per mass cat) x (surface density of active sites) x (fractional coverage of surface species) x (rate of production of surface reaction)

See Cantera/Surface_Coverage

Code

For both surfaces and gases, calculating the rates of production is a two-step process.

First, the updateROP() method is called.

Second, the reaction stoichiometry manager object, ReactionStoichMgr, is called.

Gas Reaction Rates

The surface reaction rates of production are computed in the GasKinetics class, in GasKinetics.cpp:

void GasKinetics::getNetProductionRates(doublereal* net)
{
    updateROP();
    m_rxnstoich.getNetProductionRates(m_kk, &m_ropnet[0], net);
}


Gas updateROP()

void GasKinetics::updateROP()
{
    update_rates_C();
    update_rates_T();

    if (m_ROP_ok) {
        return;
    }

    // copy rate coefficients into ropf
    copy(m_rfn.begin(), m_rfn.end(), m_ropf.begin());

    // multiply ropf by enhanced 3b conc for all 3b rxns
    if (!concm_3b_values.empty()) {
        m_3b_concm.multiply(&m_ropf[0], &concm_3b_values[0]);
    }

    if (m_nfall) {
        processFalloffReactions();
    }

    // multiply by perturbation factor
    multiply_each(m_ropf.begin(), m_ropf.end(), m_perturb.begin());

    // copy the forward rates to the reverse rates
    copy(m_ropf.begin(), m_ropf.end(), m_ropr.begin());

    // for reverse rates computed from thermochemistry, multiply
    // the forward rates copied into m_ropr by the reciprocals of
    // the equilibrium constants
    multiply_each(m_ropr.begin(), m_ropr.end(), m_rkcn.begin());

    // multiply ropf by concentration products
    m_rxnstoich.multiplyReactants(&m_conc[0], &m_ropf[0]);
    //m_reactantStoich.multiply(m_conc.begin(), ropf.begin());

    // for reversible reactions, multiply ropr by concentration
    // products
    m_rxnstoich.multiplyRevProducts(&m_conc[0], &m_ropr[0]);
    //m_revProductStoich.multiply(m_conc.begin(), ropr.begin());

    for (size_t j = 0; j != m_ii; ++j) {
        m_ropnet[j] = m_ropf[j] - m_ropr[j];
    }

    m_ROP_ok = true;
}

Surface Reaction Rates

The surface reaction rates of production are computed in the InterfaceKinetics class, in InterfaceKinetics.cpp:

void InterfaceKinetics::getNetProductionRates(doublereal* net)
{
    updateROP();
    m_rxnstoich.getNetProductionRates(m_kk,
                                      &m_kdata->m_ropnet[0],
                                      net);
}

Surface updateROP()

The updateROP method for a surface is found in the InterfaceKinetics class:

void InterfaceKinetics::updateROP()
{

    _update_rates_T();
    _update_rates_C();

    if (m_kdata->m_ROP_ok) {
        return;
    }

    const vector_fp& rf = m_kdata->m_rfn;
    const vector_fp& m_rkc = m_kdata->m_rkcn;
    vector_fp& ropf = m_kdata->m_ropf;
    vector_fp& ropr = m_kdata->m_ropr;
    vector_fp& ropnet = m_kdata->m_ropnet;

    // copy rate coefficients into ropf
    copy(rf.begin(), rf.end(), ropf.begin());

    // multiply by perturbation factor
    multiply_each(ropf.begin(), ropf.end(), m_perturb.begin());

    // copy the forward rates to the reverse rates
    copy(ropf.begin(), ropf.end(), ropr.begin());

    // for reverse rates computed from thermochemistry, multiply
    // the forward rates copied into m_ropr by the reciprocals of
    // the equilibrium constants
    multiply_each(ropr.begin(), ropr.end(), m_rkc.begin());

    // multiply ropf by concentration products
    m_rxnstoich.multiplyReactants(DATA_PTR(m_conc), DATA_PTR(ropf));
    //m_reactantStoich.multiply(m_conc.begin(), ropf.begin());

    // for reversible reactions, multiply ropr by concentration
    // products
    m_rxnstoich.multiplyRevProducts(DATA_PTR(m_conc),
                                    DATA_PTR(ropr));
    //m_revProductStoich.multiply(m_conc.begin(), ropr.begin());

    // do global reactions
    //m_globalReactantStoich.power(m_conc.begin(), ropf.begin());

    for (size_t j = 0; j != m_ii; ++j) {
        ropnet[j] = ropf[j] - ropr[j];
    }


    /*
     *  For reactions involving multiple phases, we must check that the phase
     *  being consumed actually exists. This is particularly important for
     *  phases that are stoichiometric phases containing one species with a unity activity
     */
    if (m_phaseExistsCheck) {
        for (size_t j = 0; j != m_ii; ++j) {
            if ((ropr[j] >  ropf[j]) && (ropr[j] > 0.0)) {
                for (size_t p = 0; p < nPhases(); p++) {
                    if (m_rxnPhaseIsProduct[j][p]) {
                        if (! m_phaseExists[p]) {
                            ropnet[j] = 0.0;
                            ropr[j] = ropf[j];
                            if (ropf[j] > 0.0) {
                                for (size_t rp = 0; rp < nPhases(); rp++) {
                                    if (m_rxnPhaseIsReactant[j][rp]) {
                                        if (! m_phaseExists[rp]) {
                                            ropnet[j] = 0.0;
                                            ropr[j] = ropf[j] = 0.0;
                                        }
                                    }
                                }
                            }
                        }
                    }
                    if (m_rxnPhaseIsReactant[j][p]) {
                        if (! m_phaseIsStable[p]) {
                            ropnet[j] = 0.0;
                            ropr[j] = ropf[j];
                        }
                    }
                }
            } else if ((ropf[j] > ropr[j]) && (ropf[j] > 0.0)) {
                for (size_t p = 0; p < nPhases(); p++) {
                    if (m_rxnPhaseIsReactant[j][p]) {
                        if (! m_phaseExists[p]) {
                            ropnet[j] = 0.0;
                            ropf[j] = ropr[j];
                            if (ropf[j] > 0.0) {
                                for (size_t rp = 0; rp < nPhases(); rp++) {
                                    if (m_rxnPhaseIsProduct[j][rp]) {
                                        if (! m_phaseExists[rp]) {
                                            ropnet[j] = 0.0;
                                            ropf[j] = ropr[j] = 0.0;
                                        }
                                    }
                                }
                            }
                        }
                    }
                    if (m_rxnPhaseIsProduct[j][p]) {
                        if (! m_phaseIsStable[p]) {
                            ropnet[j] = 0.0;
                            ropf[j] = ropr[j];
                        }
                    }
                }
            }
        }
    }

    m_kdata->m_ROP_ok = true;
}

Flags