23 m_thermo = m_phase.get();
29 if (m_plasma ==
nullptr)
31 throw std::invalid_argument(
32 "CanteraThermo: phase \"" + phase +
"\" in \"" + mech +
33 "\" is a " + m_thermo->type() +
34 ", not a PlasmaPhase, so it carries no electron temperature. "
35 "Declare it with \"thermo: plasma\" in the mechanism.");
38 m_ie = m_plasma->electronSpeciesIndex();
39 m_W_electron = m_thermo->molecularWeight(m_ie);
40 m_Yc.assign(m_nsp, 0.0);
41 m_work.assign(m_nsp, 0.0);
49 double Te,
const double *Y,
double &p_heavy,
50 double &p_electron)
const
52 setState(rho, Tg, Te, Y);
63 double Tg,
double Te,
const double *Y)
const
75 double p_electron = 0.0;
76 pressures(0.0, 0.0, rho, Tg, Te, Y, p_heavy, p_electron);
77 return soundSpeedAtSetState(p_heavy, p_electron, Tg);
87 setState(1.0, Tg, Tg, Y);
88 return heavyEnergyAtSetState();
97 double Te,
const double *Y)
const
102 setState(1.0, Tg, Te, Y);
103 m_thermo->setPressure(pressure);
104 const double density = m_thermo->density();
105 if (!(density > 0.0))
107 throw std::invalid_argument(
108 "CanteraThermo::densityFromPressure: a target pressure of " +
109 std::to_string(pressure) +
" Pa gives a density of " +
110 std::to_string(density) +
111 " kg/m^3; the pressure, the temperatures or the mass fractions "
112 "do not describe a gas.");
118 double heavy_internal_energy_target,
119 double Tg_seed)
const
126 constexpr int max_iterations = 100;
127 constexpr double tolerance = 1.0e-10;
129 if (!std::isfinite(Tg_seed))
131 throw std::runtime_error(
132 "CanteraThermo::heavyTemperature: the Tg seed is " +
133 std::to_string(Tg_seed) +
"; a non-finite seed means the state "
134 "handed in is already corrupted.");
136 double Tg = std::min(std::max(Tg_seed, T_low), T_high);
138 for (
int iteration = 0; iteration < max_iterations; iteration++)
140 double internal_energy, slope;
141 heavyEnergyAndCv(Tg, Y, internal_energy, slope);
142 const double residual = internal_energy - heavy_internal_energy_target;
145 throw std::runtime_error(
146 "CanteraThermo::heavyTemperature: non-positive heavy heat "
147 "capacity at Tg = " +
148 std::to_string(Tg) +
" K.");
150 const double step = residual / slope;
151 const double Tg_next = std::min(std::max(Tg - step, T_low), T_high);
152 if (std::abs(Tg_next - Tg) <= tolerance * std::max(1.0, Tg_next))
160 std::max(std::abs(heavy_internal_energy_target), 1.0);
161 const double final_residual =
162 heavyEnergy(Tg_next, Y) - heavy_internal_energy_target;
163 if (std::abs(final_residual) > 1.0e-6 * scale)
165 throw std::runtime_error(
166 "CanteraThermo::heavyTemperature: stalled at Tg = " +
167 std::to_string(Tg_next) +
" K with a relative energy residual of " +
168 std::to_string(std::abs(final_residual) / scale) +
169 ". The target internal energy of " +
170 std::to_string(heavy_internal_energy_target) +
171 " J/kg is outside the bracket [" + std::to_string(T_low) +
", " +
172 std::to_string(T_high) +
"] K for this composition.");
178 throw std::runtime_error(
179 "CanteraThermo::heavyTemperature: Newton did not converge in " +
180 std::to_string(max_iterations) +
" iterations from seed " +
181 std::to_string(Tg_seed) +
" K.");
185 double rho,
double Tg,
double Te,
186 const double *Y)
const
188 setState(rho, Tg, Te, Y);
203 void CanteraThermo::setState(
double rho,
double Tg,
double Te,
204 const double *Y)
const
215 throw std::runtime_error(
216 "CanteraThermo::setState: Tg = " + std::to_string(Tg) +
217 " K, Te = " + std::to_string(Te) +
" K; Tg must lie within [" +
221 " K, where the mechanism's thermo fits hold.");
228 for (std::size_t k = 0; k < m_nsp; k++)
230 if (std::isnan(Y[k]))
232 throw std::runtime_error(
233 "CanteraThermo::setState: the mass fraction of species " +
235 std::to_string(Tg) +
" K, Te = " + std::to_string(Te) +
" K.");
237 m_Yc[k] = (Y[k] > 0.0) ? Y[k] : 0.0;
239 m_thermo->setMassFractions_NoNorm(
240 Cantera::span<const double>(m_Yc.data(), m_nsp));
241 m_plasma->setElectronTemperature(Te);
242 m_thermo->setState_TD(Tg, rho);
245 void CanteraThermo::heavyEnergyAndCv(
double Tg,
const double *Y,
246 double &internal_energy,
253 double cp_heavy = 0.0;
254 heavyCpAndCv(cp_heavy, cv);
257 void CanteraThermo::heavyCpAndCv(
double &cp,
double &cv)
const
262 m_thermo->getPartialMolarCp(Cantera::span<double>(m_work.data(), m_nsp));
265 for (std::size_t k = 0; k < m_nsp; k++)
270 m_thermo->massFraction(k) / m_thermo->molecularWeight(k);
271 cp += moles * m_work[k];
276 double CanteraThermo::soundSpeedAtSetState(
double p_heavy,
double p_electron,
279 double cp_heavy = 0.0;
280 double cv_heavy = 0.0;
281 heavyCpAndCv(cp_heavy, cv_heavy);
282 if (!(cv_heavy > 0.0))
284 throw std::runtime_error(
285 "CanteraThermo::soundSpeed: non-positive heavy heat capacity at "
287 std::to_string(Tg) +
" K; the mixture has no heavy pool to carry "
288 "an acoustic wave.");
290 return std::sqrt(((cp_heavy / cv_heavy) * p_heavy +
292 m_thermo->density());
295 double CanteraThermo::heavyEnergyAtSetState()
const
301 m_thermo->getPartialMolarIntEnergies(
302 Cantera::span<double>(m_work.data(), m_nsp));
303 return m_thermo->intEnergy_mass() -
304 m_thermo->massFraction(m_ie) * m_work[m_ie] / m_W_electron;
The production ThermoAccessor for the hydro sub-step: Cantera's two-temperature PlasmaPhase behind Hy...
The two-temperature quantities every model in this extension reads back from a PlasmaPhase,...
string speciesName(size_t k) const
CanteraThermo(const std::string &mech, const std::string &phase)
Loads the phase alone, never its reactions: the accessor evaluates thermo only, and a mechanism's rea...
double electronCv() const override
exactly: no thermo lookup and no state, so it is the same number at every node and every step.
double heavyEnergy(double Tg, const double *Y) const override
Cantera's mixture internal energy less the electron's contribution, on the per kg of MIXTURE basis th...
double soundSpeed(double r, double t, double rho, double Tg, double Te, const double *Y) const override
summed over the heavy species alone, from the fixed ElectronGamma, over the same partial-pressure sp...
StateProperties stateProperties(double r, double t, double rho, double Tg, double Te, const double *Y) const override
The four state functions from one state-setting, rather than the three the default composition perfor...
double densityFromPressure(double pressure, double Tg, double Te, const double *Y) const override
PlasmaPhase::setPressure() read back through density().
double heavyTemperature(double rho, const double *Y, double heavy_internal_energy_target, double Tg_seed) const override
Damped Newton on = target, seeded with Tg_seed and confined to [200 K, 1e5 K].
void pressures(double r, double t, double rho, double Tg, double Te, const double *Y, double &p_heavy, double &p_electron) const override
PlasmaPhase::electronPressure(), and the total less that.
shared_ptr< ThermoPhase > newThermo(const AnyMap &phaseNode, const AnyMap &rootNode=AnyMap())
constexpr double ElectronGamma
Ratio of specific heats of the electron pool [-].
constexpr double TemperatureFloor
Lower limit of the mechanism's NASA polynomials [K].
double monatomicElectronCv(double electron_molecular_weight)
Electron specific heat at constant volume, per kg of ELECTRONS, .
constexpr double TemperatureCeiling
Upper limit of the physically meaningful range [K].
TwoTemperatureState readTwoTemperatureState(const Cantera::PlasmaPhase &phase)
Read the two-temperature EOS back from a phase whose state is set.
Every state function hydroStep() reads off one primitive state when it builds the conservative variab...
double heavy_energy
Heavy-pool energy [J/kg of mixture].
double p_heavy
Heavy-pool partial pressure [Pa].
double p_electron
Electron partial pressure [Pa].
double sound_speed
Frozen two-pool sound speed [m/s].
The two-temperature equation of state at the state currently set on a phase, PHYSICS....
double electron_pressure
[Pa]
double heavy_pressure
[Pa]