29bool equationHasElectron(
const std::string& equation,
const std::string& electron)
31 std::string s = equation;
33 if (ch ==
'+' || ch ==
'=' || ch ==
'<' || ch ==
'>') ch =
' ';
35 std::istringstream iss(s);
37 while (iss >> token) {
38 if (token == electron)
return true;
50 : m_sol(std::move(sol))
52 , m_ie(m_sol->thermo()->speciesIndex(
"e-"))
53 , m_reacting(cfg.reacting)
54 , m_collision(std::move(cfg.collision))
59 , m_sigma(
PropertyTable::fromTableOrScalar(cfg.sigma_T, cfg.sigma_v))
60 , m_nu_m_tab(
PropertyTable::fromTableOrScalar(cfg.nu_m_T, cfg.nu_m_v))
65 throw std::invalid_argument(
"ReactorRHS: mechanism has no 'e-' species.");
67 m_wt.assign(m_sol->thermo()->molecularWeights().begin(),
68 m_sol->thermo()->molecularWeights().end());
73 m_cp_mass.resize(m_nsp);
76 const std::size_t nR = m_sol->kinetics()->nReactions();
78 m_is_electron_rxn.assign(nR, 0);
84 const std::string e_name = m_sol->thermo()->speciesName(m_ie);
85 bool any_electron_rxn =
false;
86 for (std::size_t i = 0; i < nR; i++) {
87 if (equationHasElectron(m_sol->kinetics()->reaction(i)->equation(), e_name)) {
88 m_is_electron_rxn[i] = 1;
89 any_electron_rxn =
true;
99 m_eps_th.assign(nR, 0.0);
100 if (m_reacting && any_electron_rxn) {
102 throw std::invalid_argument(
103 "ReactorRHS: Config.species_h0k is required (mechanism has an "
104 "electron-impact reaction and reacting=true).");
107 throw std::invalid_argument(
108 "ReactorRHS: Config.species_h0k must have length nSpecies().");
110 m_sol->kinetics()->getReactionDelta(
118 return m_sol->thermo()->speciesName(k);
121void ReactorRHS::setState(
double Tg,
double Te,
const double* Y,
double rho,
double& P,
122 double& Xe,
double& Tmean,
double& n_e)
const
124 auto thermo = m_sol->thermo();
128 for (std::size_t k = 0; k < m_nsp; k++) m_Yc[k] = (Y[k] > 0.0) ? Y[k] : 0.0;
129 const double Tg_c = std::max(Tg, 200.0);
130 const double Te_c = std::max(Te, 200.0);
132 thermo->setElectronTemperature(Te_c);
133 thermo->setState_TD(Tg_c, rho);
134 P = thermo->pressure();
135 Xe = thermo->moleFraction(m_ie);
140 Tmean = (1.0 - Xe) * Tg_c + Xe * Te_c;
144void ReactorRHS::transport(
double Te,
double n_e,
double P,
double Tmean,
double& sigma,
145 double& nu_E,
double& nu_eH,
double& nu_eI,
146 double& nu_eH_mass_weighted)
const
148 const double Te_c = std::max(Te, 200.0);
153 if (!m_collision.
empty()) {
158 const double n_tot = P / (
Boltzmann * Tmean);
160 for (std::size_t k = 0; k < m_nsp; k++) m_nk[k] = m_X[k] * n_tot;
161 m_collision.
evaluate(Te_c, n_e, m_nk.data(), m_nsp, sigma, nu_E, nu_eH, nu_eI,
162 nu_eH_mass_weighted);
170 const double nu_m_eff = m_nu_m_tab.empty() ? m_nu_m : m_nu_m_tab.eval(Te_c);
171 sigma = (nu_m_eff > 0.0)
173 : m_sigma.eval(Te_c);
177 nu_eH_mass_weighted = 0.0;
182 double& dTg,
double& dTe,
double* dY,
double& lhs_Tg,
185 auto thermo = m_sol->thermo();
186 auto kin = m_sol->kinetics();
188 double P, Xe, Tmean, n_e;
189 setState(Tg, Te, Y, rho, P, Xe, Tmean, n_e);
190 const double Tg_c = std::max(Tg, 200.0);
191 const double Te_c = std::max(Te, 200.0);
193 thermo->getCp_R(m_cpR);
194 const double We = m_wt[m_ie];
203 for (std::size_t k = 0; k < m_nsp; k++) {
213 double cv_heavy = 0.0;
214 for (std::size_t k = 0; k < m_nsp; k++) {
215 if (k == m_ie)
continue;
216 cv_heavy += m_Yc[k] * (m_cpR[k] - 1.0) *
GasConstant / m_wt[k];
221 const double Ye_cap = std::max(m_Yc[m_ie], 1.0e-12);
222 lhs_Te = Ye_cap * cve * rho;
223 lhs_Tg = std::max(cv_heavy * rho, 1.0e-3);
225 double sigma, nu_E, nu_eH, nu_eI, nu_eH_mass_weighted;
226 transport(Te_c, n_e, P, Tmean, sigma, nu_E, nu_eH, nu_eI, nu_eH_mass_weighted);
235 const double P_el = nu_E * n_e * 1.5 *
Boltzmann * (Te_c - Tg_c);
239 const double P_joule = sigma * E * E;
241 double P_chem = 0.0, P_chem_e = 0.0, P_inel = 0.0;
242 for (std::size_t k = 0; k < m_nsp; k++) dY[k] = 0.0;
244 kin->getNetProductionRates(m_wdot);
245 kin->getNetRatesOfProgress(m_rop);
246 thermo->getPartialMolarIntEnergies(m_uk);
247 for (std::size_t k = 0; k < m_nsp; k++) {
252 dY[k] = m_wdot[k] * m_wt[k] / rho;
253 if (k != m_ie) P_chem += m_uk[k] * m_wdot[k];
255 P_chem_e = m_wdot[m_ie] * cve * We * Te_c;
259 for (std::size_t i = 0; i < m_rop.size(); i++) {
260 if (m_is_electron_rxn[i]) P_inel += m_eps_th[i] * m_rop[i];
264 dTe = (-P_chem_e - P_el - P_inel + P_joule) / lhs_Te;
265 dTg = (-P_chem + P_el + P_inel) / lhs_Tg;
267 if (!std::isfinite(dTe)) dTe = 0.0;
268 if (!std::isfinite(dTg)) dTg = 0.0;
269 for (std::size_t k = 0; k < m_nsp; k++) {
270 if (!std::isfinite(dY[k])) dY[k] = 0.0;
273 if (diag !=
nullptr) {
291 double P, Xe, Tmean, n_e;
292 setState(Tg, Te, Y, rho, P, Xe, Tmean, n_e);
293 double sigma, nu_E, nu_eH, nu_eI, nu_eH_mass_weighted;
294 transport(std::max(Te, 200.0), n_e, P, Tmean, sigma, nu_E, nu_eH, nu_eI,
295 nu_eH_mass_weighted);
302 double P, Xe, Tmean, n_e;
303 setState(Tg, Te, Y, rho, P, Xe, Tmean, n_e);
static double electronElectronCollisionFrequency(double n_e, double Te)
Electron-electron collision frequency [1/s] (Mitchner II-8.11e with Z=1), the closed-form ingredient...
void evaluate(double Te, double n_e, const double *n_k, std::size_t nsp, double &sigma, double &nu_E, double &nu_eH, double &nu_eI, double &nu_eH_mass_weighted) const
Composition-resolved electron transport.
double conductivity(double Tg, double Te, const double *Y, double rho) const
Electrical conductivity [S/m] at a node state.
std::string speciesName(std::size_t k) const
Name of species k in the mechanism.
ReactorRHS(std::shared_ptr< Cantera::Solution > sol, Config cfg)
Construct a ReactorRHS for a given plasma Solution and configuration.
double electronDensity(double Tg, double Te, const double *Y, double rho) const
Electron number density [1/m^3], .
void rates(double Tg, double Te, const double *Y, double rho, double E, double &dTg, double &dTe, double *dY, double &lhs_Tg, double &lhs_Te, Diagnostics *diag=nullptr) const
Single-cell RHS (no transport, no volume work).
std::size_t nSpecies() const
constexpr double k_b
Boltzmann constant [J/K]. Exact (CODATA).
constexpr double R_kmol
Ideal gas constant [J/(kmol K)] (matches Cantera's GasConstant).
constexpr double e
Elementary charge [C]. Exact (CODATA).
constexpr double m_e
Electron mass [kg] (CODATA 2022).
constexpr double Boltzmann
constexpr double GasConstant
std::vector< double > species_h0k
Per-species standard enthalpy of formation at 0 K [J/kmol], length nSpecies(), in mechanism species-i...
Optional power / Maxwellian-condition breakdown filled by rates() when a non-null pointer is passed.
double P_Joule
Joule heating power [W/m^3].
double nu_eH
Electron-heavy momentum-transfer collision frequency [1/s] (Mitchner II-13.3).
double P_chemical_e
Electron chemical power [W/m^3].
double P_chemical
Heavy-species chemical power [W/m^3].
double P_inelastic
Inelastic electron-impact-reaction power [W/m^3].
double nu_eI
Electron-ion momentum-transfer collision frequency [1/s].
double nu_ee
Electron-electron collision frequency [1/s] (Mitchner II-8.11e).
double P_elastic
Elastic electron-heavy exchange power [W/m^3].
double nu_eH_mass_weighted
sum_h nu_eh/m_h [1/kg/s] (Mitchner VIII-3.8, term 2)
Physical constants and unit conversions, in SI by default.