42double logForwardRateConstant(
const ReactionRate& rate,
double kf,
double T,
43 const std::string& equation)
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();
50 throw std::domain_error(
51 "logForwardRateConstant: forward reaction '" + equation +
52 "' has a non-positive pre-exponential factor, which has no log.");
54 return std::log(A) + arrhenius.temperatureExponent() * std::log(T) -
57 if (type ==
"Druyvesteyn") {
60 if (kf > 0.0 && std::isfinite(kf)) {
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.");
72bool anyNonNormal(
const std::vector<double>& kf,
const std::vector<double>& K)
74 for (std::size_t i = 0; i < kf.size(); i++) {
75 if (!std::isnormal(kf[i]) || !std::isnormal(K[i])) {
88std::shared_ptr<Cantera::Solution>
loadMechanism(
const std::string& mech,
89 const std::string& phase)
99 f->reg(
"janev-dissociative-recombination-C2Hy",
111 f->reg(
"Druyvesteyn",
121 f->reg(
"reverse-two-temperature-plasma",
139 auto plasma =
dynamic_cast<const PlasmaPhase*
>(&phase);
162std::unique_ptr<MultiRateBase>
165 return std::make_unique<
200 return m_A / (std::sqrt(Te_eV) * (1.0 + 0.27 * std::pow(Te_eV, 0.55)));
209 if (std::isnan(
m_A)) {
231 return std::make_unique<MultiRate<DruyvesteynRate, ElectronTemperatureData>>();
245 m_b = node[
"b"].asDouble();
258 if (std::isnan(
m_A)) {
259 throw std::domain_error(
260 "DruyvesteynRate: missing pre-exponential 'A' for reaction '"
264 throw std::domain_error(
265 "DruyvesteynRate: negative pre-exponential 'A' for reaction '"
278 const double ratio =
m_Ea_K / Te;
282 return m_A * std::pow(Te,
m_b) * std::exp(-ratio * ratio);
287 const double ratio =
m_Ea_K / Te;
288 return std::log(
m_A) +
m_b * std::log(Te) - ratio * ratio;
297 if (std::isnan(
m_A)) {
302 node[
"Ea"].setQuantity(
m_Ea_K,
"K",
true);
315 auto plasma =
dynamic_cast<const PlasmaPhase*
>(&phase);
317 const double Te = plasma ? plasma->electronTemperature() : Tg;
319 if (Tg == m_Tg && Te == m_Te && P == m_P) {
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 " +
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++) {
349 sth->setMassFractions(Y);
350 sth->setState_TP(Tg, P);
351 skin->getFwdRateConstants(
kf_Tg);
352 skin->getEquilibriumConstants(
Keq_Tg);
356 computeLogEquilibriumConstants(
logKeq_Tg);
361 sth->setState_TP(Te, P);
362 skin->getFwdRateConstants(
kf_Te);
363 skin->getEquilibriumConstants(
Keq_Te);
365 computeLogEquilibriumConstants(
logKeq_Te);
376 auto thermo = std::make_shared<IdealGasPhase>();
378 thermo->addUndefinedElements();
379 for (
size_t k = 0; k < phase.
nSpecies(); k++) {
380 thermo->addSpecies(phase.
species(k));
382 thermo->initThermo();
391 skin->addThermo(thermo);
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") {
413 skin->addReaction(r,
false);
416 if (
fwdIndex.count(r->equation()) != 0) {
419 fwdIndex[r->equation()] = skin->nReactions() - 1;
425 shadow->setThermo(thermo);
426 shadow->setKinetics(skin);
427 const std::size_t nR = skin->nReactions();
431 m_deltaG.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);
440void ReverseTwoTempData::computeLogEquilibriumConstants(std::vector<double>& logKeq)
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;
464std::unique_ptr<MultiRateBase>
467 return std::make_unique<
496 if (it == shared_data.
fwdIndex.end()) {
497 throw CanteraError(
"ReverseTwoTemperaturePlasmaRate::evalFromStruct",
498 "Forward reaction '{}' not found in the forward (shadow) mechanism.",
502 throw std::invalid_argument(
503 "ReverseTwoTemperaturePlasmaRate: forward_equation '" +
505 "(duplicates), so the reverse rate has no unique forward rate.");
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];
519 if (std::isnormal(k) && std::isnormal(K)) {
527 const std::vector<double>& logKeq =
529 const double logkf = logForwardRateConstant(*shared_data.
fwdRate[
m_idx], k,
531 kr = std::exp(logkf - logKeq[
m_idx]);
533 if (!std::isfinite(kr)) {
534 throw TrialStateError(
"ReverseTwoTemperaturePlasmaRate::evalFromStruct",
536 std::to_string(T) +
" K.");
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
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].
DruyvesteynRate()=default
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.
JanevDissociativeRecombinationC2HyRate()=default
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.
ReverseTwoTemperaturePlasmaRate()=default
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={})
constexpr double K_to_eV
K -> eV.
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 ...
constexpr double TemperatureCeiling
Upper limit of the physically meaningful range [K].
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...
double electronTemp
electron temperature [K]
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.