rizer.cantera_ext
C++ Cantera 1-D plasma extension (custom Domain1D models and solvers)
Loading...
Searching...
No Matches
PlasmaRates.cpp
Go to the documentation of this file.
1// PlasmaRates.cpp — see PlasmaRates.h for the design rationale.
2
3#include "PlasmaRates.h"
4#include "units.h"
7
18
19#include <cmath>
20#include <stdexcept>
21#include <string>
22
23using namespace Cantera;
24
25namespace rizer {
26
27// ---------------------------------------------------------------------------------- //
28// File-local helpers //
29// ---------------------------------------------------------------------------------- //
30
31namespace {
32
42double logForwardRateConstant(const ReactionRate& rate, double kf, double T,
43 const std::string& equation)
44{
45 const std::string type = rate.type();
46 if (type == "Arrhenius" || type == "two-temperature-plasma") {
47 const auto& arrhenius = dynamic_cast<const ArrheniusBase&>(rate);
48 const double A = arrhenius.preExponentialFactor();
49 if (!(A > 0.0)) {
50 throw std::domain_error(
51 "logForwardRateConstant: forward reaction '" + equation +
52 "' has a non-positive pre-exponential factor, which has no log.");
53 }
54 return std::log(A) + arrhenius.temperatureExponent() * std::log(T) -
55 arrhenius.activationEnergy() / (GasConstant * T);
56 }
57 if (type == "Druyvesteyn") {
58 return dynamic_cast<const DruyvesteynRate&>(rate).logRate(T);
59 }
60 if (kf > 0.0 && std::isfinite(kf)) {
61 return std::log(kf);
62 }
63 throw std::domain_error(
64 "logForwardRateConstant: the forward rate constant of '" + equation +
65 "' (type " + type + ") is " + std::to_string(kf) + " at T = " +
66 std::to_string(T) + " K, and that rate type has no log form to recover it.");
67}
68
72bool anyNonNormal(const std::vector<double>& kf, const std::vector<double>& K)
73{
74 for (std::size_t i = 0; i < kf.size(); i++) {
75 if (!std::isnormal(kf[i]) || !std::isnormal(K[i])) {
76 return true;
77 }
78 }
79 return false;
80}
81
82} // namespace
83
84// ---------------------------------------------------------------------------------- //
85// Registration //
86// ---------------------------------------------------------------------------------- //
87
88std::shared_ptr<Cantera::Solution> loadMechanism(const std::string& mech,
89 const std::string& phase)
90{
91 registerPlasmaRates(); // Idempotent
92 return Cantera::newSolution(mech, phase);
93}
94
96{
98 // `reg` replaces any existing entry silently, so this is idempotent.
99 f->reg("janev-dissociative-recombination-C2Hy",
100 // Lambda parameters (a plain, non-Doxygen comment: this lambda has no
101 // separate declaration for \param to attach to, so a doc-comment
102 // here would be misattached to the enclosing registerPlasmaRates()):
103 // node YAML/AnyMap node for this reaction's rate block
104 // (holds "A").
105 // units Unit context used to convert "A" into Cantera's
106 // internal rate-coeff units.
107 [](const AnyMap& node, const UnitStack& units)
108 {
110 });
111 f->reg("Druyvesteyn",
112 // Lambda parameters (plain comment, see the note above):
113 // node YAML/AnyMap node for this reaction's rate block
114 // (holds "A", "b", "Ea").
115 // units Unit context used to convert "A" into Cantera's
116 // internal rate-coeff units.
117 [](const AnyMap& node, const UnitStack& units)
118 {
119 return new DruyvesteynRate(node, units);
120 });
121 f->reg("reverse-two-temperature-plasma",
122 // Lambda parameters (plain comment, see the note above):
123 // node YAML/AnyMap node for this reaction's rate block
124 // (holds "forward_equation", "T").
125 // units Unit context passed through to
126 // ReactionRate::setParameters.
127 [](const AnyMap& node, const UnitStack& units)
128 {
129 return new ReverseTwoTemperaturePlasmaRate(node, units);
130 });
131}
132
133// ---------------------------------------------------------------------------------- //
134// janev-dissociative-recombination-C2Hy - shared data //
135// ---------------------------------------------------------------------------------- //
136
137bool ElectronTemperatureData::update(const ThermoPhase& phase, const Kinetics& /*kin*/)
138{
139 auto plasma = dynamic_cast<const PlasmaPhase*>(&phase);
140 double Te = plasma ? plasma->electronTemperature() : phase.temperature();
141 if (Te != electronTemp) {
142 electronTemp = Te;
143 return true;
144 }
145 return false;
146}
147
148// ---------------------------------------------------------------------------------- //
149// janev-dissociative-recombination-C2Hy - constructor //
150// ---------------------------------------------------------------------------------- //
151
157
158// ---------------------------------------------------------------------------------- //
159// janev-dissociative-recombination-C2Hy - public methods //
160// ---------------------------------------------------------------------------------- //
161
162std::unique_ptr<MultiRateBase>
168
170 const AnyMap& node, const UnitStack& rate_units)
171{
172 ReactionRate::setParameters(node, rate_units); // sets units + m_input
173 if (!node.hasKey("A")) {
174 m_A = NAN;
175 return;
176 }
177 // A has the same units as the rate constant (the Te-dependent factor is
178 // dimensionless), so it converts like any rate coefficient (e.g.
179 // cm^3/mol/s or m^3/kmol/s, per the reaction's declared `units:` block,
180 // both into Cantera's internal kmol/m/s system).
181 m_A = node.units().convertRateCoeff(node["A"], conversionUnits());
182 m_valid = true;
183}
184
185// Compiled counterpart of the Python `JanevDissociativeRecombinationC2Hy`
186// ct.ExtensibleRate (rizer/kin/extensible_rate.py), which implements
187// Janev2004 Eq. 70 for dissociative recombination: a closed-form rate that
188// depends on the electron temperature Te only (no Tg, no reduced field).
190 const ElectronTemperatureData& shared_data) const
191{
192 // Convert Te from K to eV: @f$ T_{e,\text{eV}} = T_e k_B / e ==
193 // T_e \cdot \text{K\_to\_eV} @f$ (units.h mirrors units.py's
194 // `K_to_eV` exactly).
195 const double Te_eV = shared_data.electronTemp * rizer::units::K_to_eV;
196 // @f$ k(T_e) = A / ( \sqrt{T_{e,\text{eV}}} (1 + 0.27
197 // T_{e,\text{eV}}^{0.55}) ) @f$; the bracketed factor is dimensionless,
198 // so the result carries the same units as A (a Cantera rate
199 // coefficient in kmol/m/s units).
200 return m_A / (std::sqrt(Te_eV) * (1.0 + 0.27 * std::pow(Te_eV, 0.55)));
201}
202
203// ---------------------------------------------------------------------------------- //
204// janev-dissociative-recombination-C2Hy - protected methods //
205// ---------------------------------------------------------------------------------- //
206
208{
209 if (std::isnan(m_A)) {
210 return;
211 }
212 node["A"].setQuantity(m_A, conversionUnits());
213}
214
215// ---------------------------------------------------------------------------------- //
216// Druyvesteyn - constructor //
217// ---------------------------------------------------------------------------------- //
218
220 const Cantera::UnitStack& rate_units)
221{
222 setParameters(node, rate_units);
223}
224
225// ---------------------------------------------------------------------------------- //
226// Druyvesteyn - public methods //
227// ---------------------------------------------------------------------------------- //
228
229std::unique_ptr<MultiRateBase> DruyvesteynRate::newMultiRate() const
230{
231 return std::make_unique<MultiRate<DruyvesteynRate, ElectronTemperatureData>>();
232}
233
234void DruyvesteynRate::setParameters(const AnyMap& node, const UnitStack& rate_units)
235{
236 ReactionRate::setParameters(node, rate_units); // sets units + m_input
237 if (!node.hasKey("A")) {
238 m_A = m_b = m_Ea_K = NAN;
239 return;
240 }
241 // A carries the rate-constant units (Te^b's contribution is treated as
242 // dimensionless, exactly like the Python reference's convert_rate_coeff);
243 // Ea is expressed in Kelvin so (Ea/Te)^2 is dimensionless.
244 m_A = node.units().convertRateCoeff(node["A"], conversionUnits());
245 m_b = node["b"].asDouble();
246 m_Ea_K = node.units().convertActivationEnergy(node["Ea"], "K");
247 m_valid = true;
248}
249
250void DruyvesteynRate::validate(const std::string& equation,
251 const Cantera::Kinetics& /*kin*/)
252{
253 // Mirrors the Python rate's validate. Throw std::, not
254 // CanteraError("...{}...") — the fmt instantiation rule (CLAUDE.md).
255 // NaN (missing "A" in the YAML rate node, see setParameters) is caught
256 // separately: NaN < 0 is false, so the negative-A check below cannot
257 // catch it on its own.
258 if (std::isnan(m_A)) {
259 throw std::domain_error(
260 "DruyvesteynRate: missing pre-exponential 'A' for reaction '"
261 + equation + "'.");
262 }
263 if (m_A < 0) {
264 throw std::domain_error(
265 "DruyvesteynRate: negative pre-exponential 'A' for reaction '"
266 + equation + "'.");
267 }
268}
269
270// Compiled counterpart of the Python `DruyvesteynRate` ct.ExtensibleRate
271// (rizer/kin/extensible_rate.py): a modified Arrhenius in the electron
272// temperature whose exponential uses the squared temperature ratio of a
273// Druyvesteyn EEDF. Depends on Te only (no Tg, no reduced field).
275 const ElectronTemperatureData& shared_data) const
276{
277 const double Te = shared_data.electronTemp;
278 const double ratio = m_Ea_K / Te;
279 // @f$ k(T_e) = A \, T_e^{b} \, e^{-(E_a/T_e)^2} @f$; the exponential and
280 // the power factor are dimensionless, so the result carries A's units (a
281 // Cantera rate coefficient in kmol/m/s units).
282 return m_A * std::pow(Te, m_b) * std::exp(-ratio * ratio);
283}
284
285double DruyvesteynRate::logRate(double Te) const
286{
287 const double ratio = m_Ea_K / Te;
288 return std::log(m_A) + m_b * std::log(Te) - ratio * ratio;
289}
290
291// ---------------------------------------------------------------------------------- //
292// Druyvesteyn - protected methods //
293// ---------------------------------------------------------------------------------- //
294
296{
297 if (std::isnan(m_A)) {
298 return;
299 }
300 node["A"].setQuantity(m_A, conversionUnits());
301 node["b"] = m_b;
302 node["Ea"].setQuantity(m_Ea_K, "K", true);
303}
304
305// ---------------------------------------------------------------------------------- //
306// reverse-two-temperature-plasma - shared data //
307// ---------------------------------------------------------------------------------- //
308
309bool ReverseTwoTempData::update(const ThermoPhase& phase, const Kinetics& kin)
310{
311 if (!shadow) {
312 buildShadow(phase, kin);
313 }
314
315 auto plasma = dynamic_cast<const PlasmaPhase*>(&phase);
316 const double Tg = phase.temperature();
317 const double Te = plasma ? plasma->electronTemperature() : Tg;
318 const double P = phase.pressure();
319 if (Tg == m_Tg && Te == m_Te && P == m_P) {
320 return false;
321 }
322 // The shadow evaluates every heavy species' thermo fit at Tg, and at Te when
323 // a reverse reaction is Te-tagged, to form K_eq, so both are held to the
324 // ceiling: above it the fits extrapolate (the CH4 mechanism's C+ fits give
325 // cp/R < 1 from 1.52e5 K) and the detailed-balance rates would rest on Gibbs
326 // energies they cannot give.
327 if (!(Tg <= TemperatureCeiling) || (anyTeTagged && !(Te <= TemperatureCeiling))) {
328 throw TrialStateError("ReverseTwoTempData::update",
329 "Tg = " + std::to_string(Tg) + " K, Te = " + std::to_string(Te) +
330 " K; detailed-balance reverse rates evaluate the heavy species' "
331 "thermo fits at both, which hold only up to " +
332 std::to_string(TemperatureCeiling) + " K.");
333 }
334 m_Tg = Tg;
335 m_Te = Te;
336 m_P = P;
337
338 // Push the composition onto the ideal-gas shadow, then read the forward rate
339 // constants and (concentration) equilibrium constants at BOTH Tg and Te. A
340 // two-temperature-plasma forward rate in this single-T phase sees Tg=Te=T, so
341 // evaluating at Te gives kf(Te) and at Tg gives kf(Tg) -- exactly what the
342 // Python reference's ideal-gas shadow does (gas.TP = T, P).
343 auto sth = shadow->thermo();
344 auto skin = shadow->kinetics();
345 std::vector<double> Y(phase.nSpecies());
346 for (size_t k = 0; k < phase.nSpecies(); k++) {
347 Y[k] = phase.massFraction(k);
348 }
349 sth->setMassFractions(Y);
350 sth->setState_TP(Tg, P);
351 skin->getFwdRateConstants(kf_Tg);
352 skin->getEquilibriumConstants(Keq_Tg);
353 // ln K is read only where kf or K is not a normal number (the log-space
354 // path of evalFromStruct), so it is formed only when that can happen.
355 if (anyNonNormal(kf_Tg, Keq_Tg)) {
356 computeLogEquilibriumConstants(logKeq_Tg);
357 }
358 if (!anyTeTagged) {
359 return true;
360 }
361 sth->setState_TP(Te, P);
362 skin->getFwdRateConstants(kf_Te);
363 skin->getEquilibriumConstants(Keq_Te);
364 if (anyNonNormal(kf_Te, Keq_Te)) {
365 computeLogEquilibriumConstants(logKeq_Te);
366 }
367 return true;
368}
369
371{
372 // Thermo: a single-temperature IDEAL-GAS copy of the main phase (electron is
373 // a normal ideal-gas species), mirroring rizer's Python shadow
374 // (ct.Solution(thermo="ideal-gas", species=plasma.species())). The reverse
375 // rate uses detailed balance at one temperature (Tg or Te), NOT a 2-T K_eq.
376 auto thermo = std::make_shared<IdealGasPhase>();
377 // Auto-add elements (incl. "E") as species are added.
378 thermo->addUndefinedElements();
379 for (size_t k = 0; k < phase.nSpecies(); k++) {
380 thermo->addSpecies(phase.species(k));
381 }
382 thermo->initThermo();
383
384 // Kinetics: every "forward" reaction (i.e. everything EXCEPT the custom
385 // reverse-two-temperature-plasma rates). We keep the janev and
386 // two-temperature-plasma forwards because some reverse reactions name a
387 // janev forward in their `forward_equation`, so its k_fwd/K_eq must be
388 // computable here. Excluding only the reverse rates is what stops the
389 // shadow from recursing back into this code.
390 auto skin = newKinetics("bulk");
391 skin->addThermo(thermo);
392 // Size species arrays from the phase (factory does this before
393 // addReaction).
394 skin->init();
395 // Reaction::rate() is non-const; we only read reactions, so take a non-const
396 // view of the (const) source kinetics. The reaction objects are SHARED with
397 // the main kinetics (as rizer's Python shadow phase does): the native rates'
398 // setContext is stateless (TwoTempPlasmaRate only checks reversibility) and
399 // MultiRate evaluators key off their own internal indices, not the shared
400 // rate's index, so sharing does not corrupt the main solution. Sharing also
401 // avoids a parameters()->newReaction round-trip, which fails because A is
402 // serialized as a units-bearing Quantity that convert() cannot re-read.
403 auto& mkin = const_cast<Kinetics&>(kin);
404 for (size_t i = 0; i < mkin.nReactions(); i++) {
405 auto r = mkin.reaction(i);
406 std::string t = r->rate() ? r->rate()->type() : std::string();
407 if (t == "reverse-two-temperature-plasma") {
408 if (r->rate()->parameters().getString("T", "Tg") == "Te") {
409 anyTeTagged = true;
410 }
411 continue;
412 }
413 skin->addReaction(r, false);
414 // A second forward reaction with the same equation makes the lookup
415 // ambiguous; the index is withdrawn so a reverse rate naming it raises.
416 if (fwdIndex.count(r->equation()) != 0) {
417 ambiguousForward.insert(r->equation());
418 }
419 fwdIndex[r->equation()] = skin->nReactions() - 1;
420 fwdRate.push_back(r->rate());
421 }
422 skin->resizeReactions();
423
425 shadow->setThermo(thermo);
426 shadow->setKinetics(skin);
427 const std::size_t nR = skin->nReactions();
428 kf_Tg.assign(nR, 0.0); Keq_Tg.assign(nR, 0.0);
429 kf_Te.assign(nR, 0.0); Keq_Te.assign(nR, 0.0);
430 logKeq_Tg.assign(nR, 0.0); logKeq_Te.assign(nR, 0.0);
431 m_deltaG.assign(nR, 0.0);
432 deltaN.assign(nR, 0.0);
433 for (std::size_t i = 0; i < nR; i++) {
434 for (std::size_t k = 0; k < thermo->nSpecies(); k++) {
435 deltaN[i] += skin->productStoichCoeff(k, i) - skin->reactantStoichCoeff(k, i);
436 }
437 }
438}
439
440void ReverseTwoTempData::computeLogEquilibriumConstants(std::vector<double>& logKeq)
441{
442 auto sth = shadow->thermo();
443 shadow->kinetics()->getDeltaSSGibbs(m_deltaG);
444 const double logStandardConcentration = std::log(sth->standardConcentration());
445 for (std::size_t i = 0; i < logKeq.size(); i++) {
446 logKeq[i] = -m_deltaG[i] / sth->RT() + deltaN[i] * logStandardConcentration;
447 }
448}
449
450// ---------------------------------------------------------------------------------- //
451// reverse-two-temperature-plasma - constructor //
452// ---------------------------------------------------------------------------------- //
453
459
460// ---------------------------------------------------------------------------------- //
461// reverse-two-temperature-plasma - public methods //
462// ---------------------------------------------------------------------------------- //
463
464std::unique_ptr<MultiRateBase>
470
472 const AnyMap& node, const UnitStack& rate_units)
473{
474 ReactionRate::setParameters(node, rate_units);
475 m_forward_equation = node.getString("forward_equation", "");
476 m_T = node.getString("T", "Tg");
477 m_idx = npos;
478 m_valid = !m_forward_equation.empty();
479}
480
481// Compiled counterpart of the Python `ReverseTwoTemperaturePlasma`
482// ct.ExtensibleRate (rizer/kin/extensible_rate.py). Implements detailed
483// balance at a single (Tg- or Te-) temperature for the reverse of a forward
484// two-temperature-plasma-family reaction:
485// @f[
486// k_{rev}(T) = \frac{k_{fwd}(T)}{K_{eq}(T)}
487// @f]
488// with T = Te for electron-impact reactions and T = Tg for heavy-species
489// reactions (per the reaction's `T:` tag). k_fwd and K_eq are both looked up
490// from the shadow ideal-gas solution built/cached in ReverseTwoTempData.
492 const ReverseTwoTempData& shared_data) const
493{
494 if (m_idx == npos) {
495 auto it = shared_data.fwdIndex.find(m_forward_equation);
496 if (it == shared_data.fwdIndex.end()) {
497 throw CanteraError("ReverseTwoTemperaturePlasmaRate::evalFromStruct",
498 "Forward reaction '{}' not found in the forward (shadow) mechanism.",
500 }
501 if (shared_data.ambiguousForward.count(m_forward_equation) != 0) {
502 throw std::invalid_argument(
503 "ReverseTwoTemperaturePlasmaRate: forward_equation '" +
504 m_forward_equation + "' matches more than one forward reaction "
505 "(duplicates), so the reverse rate has no unique forward rate.");
506 }
507 m_idx = it->second;
508 }
509 // Detailed balance at the tagged temperature:
510 // @f$ k_{rev} = k_f(T) / K_c(T) @f$.
511 const bool useTe = (m_T == "Te");
512 const std::vector<double>& kf = useTe ? shared_data.kf_Te : shared_data.kf_Tg;
513 const std::vector<double>& Keq = useTe ? shared_data.Keq_Te : shared_data.Keq_Tg;
514 const double k = kf[m_idx];
515 const double K = Keq[m_idx];
516 const double T = useTe ? shared_data.electronTemperature()
517 : shared_data.gasTemperature();
518 double kr;
519 if (std::isnormal(k) && std::isnormal(K)) {
520 kr = k / K;
521 } else {
522 // K underflows to 0 for the electron-impact ionizations of the CH4
523 // mechanism below about 746 K (C++ -> C+++), and kf underflows with it
524 // (to 0, or to a subnormal within a few K of where K does), while their
525 // ratio, the three-body recombination rate, is finite. Take it in log
526 // space.
527 const std::vector<double>& logKeq =
528 useTe ? shared_data.logKeq_Te : shared_data.logKeq_Tg;
529 const double logkf = logForwardRateConstant(*shared_data.fwdRate[m_idx], k,
531 kr = std::exp(logkf - logKeq[m_idx]);
532 }
533 if (!std::isfinite(kr)) {
534 throw TrialStateError("ReverseTwoTemperaturePlasmaRate::evalFromStruct",
535 "the reverse rate of '" + m_forward_equation + "' overflows at T = " +
536 std::to_string(T) + " K.");
537 }
538 return kr;
539}
540
541// ---------------------------------------------------------------------------------- //
542// reverse-two-temperature-plasma - protected methods //
543// ---------------------------------------------------------------------------------- //
544
546{
547 node["forward_equation"] = m_forward_equation;
548 node["T"] = m_T;
549}
550
551} // namespace rizer
The two-temperature quantities every model in this extension reads back from a PlasmaPhase,...
The error a closure raises for a state it cannot evaluate, so that the integrator proposing that stat...
bool hasKey(const string &key) const
const UnitSystem & units() const
const string & getString(const string &key, const string &default_) const
virtual void resizeReactions()
double massFraction(size_t k) const
size_t nSpecies() const
double temperature() const
shared_ptr< Species > species(const string &name) const
virtual double pressure() const
double electronTemperature() const override
static ReactionRateFactory * factory()
virtual void setParameters(const AnyMap &node, const UnitStack &units)
const Units & conversionUnits() const
virtual const string type() const=0
static shared_ptr< Solution > create()
double convertRateCoeff(const AnyValue &val, const Units &dest) const
double convertActivationEnergy(double value, const string &src, const string &dest) const
void setParameters(const Cantera::AnyMap &node, const Cantera::UnitStack &rate_units) override
Set the rate parameters from a YAML/AnyMap rate node.
double logRate(double Te) const
Natural log of the rate, , finite where the rate itself underflows to 0.
std::unique_ptr< Cantera::MultiRateBase > newMultiRate() const override
double m_b
Te exponent [-].
double m_A
Pre-factor in Cantera's internal (kmol, m, s) rate-coefficient units [order-dependent].
void getParameters(Cantera::AnyMap &node) const override
void validate(const std::string &equation, const Cantera::Kinetics &kin) override
Reject a missing or negative pre-factor (mirrors the Python rate's validate; missing "A" leaves m_A a...
double evalFromStruct(const ElectronTemperatureData &shared_data) const
Evaluate the Druyvesteyn rate .
double m_Ea_K
Activation energy expressed in Kelvin [K].
std::unique_ptr< Cantera::MultiRateBase > newMultiRate() const override
void setParameters(const Cantera::AnyMap &node, const Cantera::UnitStack &rate_units) override
Set the rate parameters from a YAML/AnyMap rate node.
double evalFromStruct(const ElectronTemperatureData &shared_data) const
Evaluate the Janev dissociative-recombination rate .
double m_A
Rate pre-factor, in Cantera's internal (kmol, m, s) rate-coefficient units [kmol/m^3/s units,...
void getParameters(Cantera::AnyMap &node) const override
std::unique_ptr< Cantera::MultiRateBase > newMultiRate() const override
double evalFromStruct(const ReverseTwoTempData &shared_data) const
Evaluate the reverse rate at the tagged temperature.
void getParameters(Cantera::AnyMap &node) const override
std::string m_forward_equation
Equation string of the forward reaction this rate reverses [-].
size_t m_idx
Cached index of the forward reaction in the shadow kinetics' reaction list [-].
void setParameters(const Cantera::AnyMap &node, const Cantera::UnitStack &rate_units) override
Set the rate parameters from a YAML/AnyMap rate node.
std::string m_T
Which temperature tags this reaction ("Te" or "Tg"); informational, kept for YAML round-trip [-].
TrialStateError(const std::string &procedure, std::string message)
shared_ptr< Kinetics > newKinetics(const string &model)
shared_ptr< Solution > newSolution(const string &infile, const string &name="", const string &transport="default", const vector< shared_ptr< Solution > > &adjacent={})
const size_t npos
constexpr double K_to_eV
K -> eV.
Definition units.h:71
std::shared_ptr< Cantera::Solution > loadMechanism(const std::string &mech, const std::string &phase)
Load a mechanism with rizer's custom rate types registered first.
constexpr double GasConstant
Universal gas constant [J/kmol/K] and Boltzmann's constant [J/K], from units.h rather than Cantera's ...
Definition PlasmaState.h:48
constexpr double TemperatureCeiling
Upper limit of the physically meaningful range [K].
Definition PlasmaState.h:43
void registerPlasmaRates()
Register the custom plasma reaction rates with Cantera's ReactionRateFactory.
Shared data carrying the electron temperature (mirrors Cantera's own TwoTempPlasmaData but only needs...
Definition PlasmaRates.h:62
double electronTemp
electron temperature [K]
Definition PlasmaRates.h:74
bool update(const Cantera::ThermoPhase &phase, const Cantera::Kinetics &kin) override
Read the electron temperature from the phase.
Shared data for all reverse-two-temperature-plasma reactions.
bool update(const Cantera::ThermoPhase &phase, const Cantera::Kinetics &kin) override
Refresh the cached Tg, Te, P and the shadow-solution rate/equilibrium constants.
std::unordered_set< std::string > ambiguousForward
Forward equations shared by more than one forward reaction [-].
std::vector< std::shared_ptr< Cantera::ReactionRate > > fwdRate
Per-reaction forward rate of the shadow, read by the log-space path.
std::shared_ptr< Cantera::Solution > shadow
Forward-only ideal-gas shadow solution [-].
std::vector< double > Keq_Tg
bool anyTeTagged
Whether any reverse reaction is tagged T: Te.
std::vector< double > kf_Te
Per-reaction forward rate constant [kmol/m^3/s units, order-dependent] and dimensionless Kc,...
std::vector< double > logKeq_Tg
Per-reaction natural log of Kc at Tg and at Te [-], from the standard Gibbs energy of reaction,...
std::vector< double > logKeq_Te
std::vector< double > Keq_Te
double electronTemperature() const
Electron temperature from the last update() [K].
std::vector< double > deltaN
Per-reaction change in moles, products minus reactants [-].
std::unordered_map< std::string, size_t > fwdIndex
Forward reaction equation string -> index into the shadow kinetics' reaction list [-].
void buildShadow(const Cantera::ThermoPhase &phase, const Cantera::Kinetics &kin)
Lazily build the ideal-gas shadow Solution from the main phase + kinetics.
double gasTemperature() const
Gas temperature from the last update() [K].
std::vector< double > kf_Tg
Per-reaction forward rate constant [kmol/m^3/s units, order-dependent] and dimensionless Kc,...
Physical constants and unit conversions, in SI by default.