rizer.cantera_ext
C++ Cantera 1-D plasma extension (custom Domain1D models and solvers)
Loading...
Searching...
No Matches
GammaLawThermo.cpp
Go to the documentation of this file.
2
4
5#include <cmath>
6#include <stdexcept>
7#include <string>
8#include <utility>
9#include <vector>
10
11namespace rizer
12{
13
14 // ------------------------------------------------------------------------------ //
15 // GammaLawThermo - constructor //
16 // ------------------------------------------------------------------------------ //
17
19 std::vector<double> molecular_weights,
20 int electron_index)
21 : m_gamma(gamma),
22 m_W(std::move(molecular_weights)),
23 m_electron_index(electron_index)
24 {
25 if (!(m_gamma > 1.0))
26 {
27 throw std::invalid_argument(
28 "GammaLawThermo: the heavy-pool gamma must be > 1, got " +
29 std::to_string(m_gamma));
30 }
31 if (m_W.empty())
32 {
33 throw std::invalid_argument("GammaLawThermo: molecular_weights is empty.");
34 }
35 for (std::size_t k = 0; k < m_W.size(); k++)
36 {
37 if (!(m_W[k] > 0.0))
38 {
39 throw std::invalid_argument(
40 "GammaLawThermo: molecular_weights[" +
41 std::to_string(k) +
42 "] must be > 0, got " +
43 std::to_string(m_W[k]));
44 }
45 }
46 if (m_electron_index < -1 || m_electron_index >= static_cast<int>(m_W.size()))
47 {
48 throw std::invalid_argument(
49 "GammaLawThermo: electron_index must be -1 (no electron species) or "
50 "a valid species index, got " +
51 std::to_string(m_electron_index));
52 }
53 }
54
55 // ------------------------------------------------------------------------------ //
56 // GammaLawThermo - public methods //
57 // ------------------------------------------------------------------------------ //
58
59 void GammaLawThermo::pressures(double /*r*/, double /*t*/, double rho, double Tg,
60 double Te, const double *Y, double &p_heavy,
61 double &p_electron) const
62 {
63 // PHYSICS.md (G6), one pool at a time: p_H = n_H k_B T_H and
64 // p_e = n_e k_B T_e, with n_pool/rho the pool's moles per kg of mixture.
65 p_heavy = rho * heavyMolesPerMass(Y) * GasConstant * Tg;
66 p_electron = rho * electronMolesPerMass(Y) * GasConstant * Te;
67 }
68
69 double GammaLawThermo::soundSpeed(double /*r*/, double /*t*/, double /*rho*/,
70 double Tg, double Te, const double *Y) const
71 {
72 // PHYSICS.md appendix A.4, frozen two-pool speed: each pool follows its
73 // own adiabat, isentropic bulk moduli add, so
74 // c = sqrt((gamma_H p_H + gamma_e p_e)/rho). Written on the moles-per-kg
75 // basis the partial pressures are built from, rho cancels, which keeps
76 // the speed defined on a state whose density has not been set.
77 const double moles_electron = electronMolesPerMass(Y);
78 const double moles_heavy = heavyMolesPerMass(Y);
79 if (!(moles_heavy + moles_electron > 0.0))
80 {
81 throw std::invalid_argument(
82 "GammaLawThermo::soundSpeed: mass fractions sum to zero moles.");
83 }
84 return std::sqrt(GasConstant * (m_gamma * moles_heavy * Tg +
85 ElectronGamma * moles_electron * Te));
86 }
87
88 double GammaLawThermo::heavyEnergy(double Tg, const double *Y) const
89 {
90 // Per kg of MIXTURE: sum_{s!=e} Y_s * R*Tg/(W_s*(gamma-1)), which is
91 // the heavy moles per kg of mixture times R*Tg/(gamma-1).
92 return Tg * GasConstant / (m_gamma - 1.0) * heavyMolesPerMass(Y);
93 }
94
96 {
97 if (m_electron_index < 0)
98 {
99 throw std::invalid_argument(
100 "GammaLawThermo::electronCv: no electron species "
101 "(electron_index = -1); the electron energy row is not solved.");
102 }
103 const std::size_t e = static_cast<std::size_t>(m_electron_index);
104 return monatomicElectronCv(m_W[e]);
105 }
106
107 double GammaLawThermo::densityFromPressure(double pressure, double Tg,
108 double Te, const double *Y) const
109 {
110 // Inverting PHYSICS.md (G6). pressures() writes each pool's number
111 // density as (moles per kg of mixture) times rho, so
112 //
113 // p = p_H + p_e = rho * [ sum_{s!=e} (Y_s/W_s) R T_H
114 // + (Y_e/W_e) R T_e ]
115 //
116 // and at fixed temperatures and composition the bracket is a constant:
117 // p is exactly linear in rho with no offset, so rho = p / k with k the
118 // bracket. Evaluating pressures() at rho = 1 IS that bracket, because
119 // every term there carries one factor of rho -- which is why the
120 // inverse reuses the forward formula rather than restating (G6) a
121 // second time and leaving two copies to keep in sync. Exact, not a
122 // linearisation: any probe density returns the same p(rho_0)/rho_0.
123 double p_heavy_unit = 0.0;
124 double p_electron_unit = 0.0;
125 pressures(/*r=*/0.0, /*t=*/0.0, 1.0, Tg, Te, Y, p_heavy_unit,
126 p_electron_unit);
127 const double p_unit = p_heavy_unit + p_electron_unit;
128 if (!(p_unit > 0.0))
129 {
130 throw std::invalid_argument(
131 "GammaLawThermo::densityFromPressure: the state has no pressure at "
132 "unit density; check the temperatures and mass fractions.");
133 }
134 return pressure / p_unit;
135 }
136
137 double GammaLawThermo::heavyTemperature(double /*rho*/, const double *Y,
138 double heavy_internal_energy_target,
139 double /*Tg_seed*/) const
140 {
141 // heavyEnergy() is linear in Tg for a calorically perfect gas, so the
142 // inversion is exact and the seed is unused. A Cantera-backed accessor
143 // needs the seed for its Newton iteration.
144 const double moles = heavyMolesPerMass(Y);
145 if (!(moles > 0.0))
146 {
147 throw std::invalid_argument(
148 "GammaLawThermo::heavyTemperature: no heavy species present.");
149 }
150 // The stub's energy is R*Tg/(gamma-1) per heavy mole, strictly positive
151 // for a positive temperature, so a non-positive target is an unphysical
152 // state and not a cold one. Raised rather than returned: the bare
153 // division would hand back a negative Tg, and the next soundSpeed() call
154 // takes the square root of a negative bulk modulus, so the NaN surfaces
155 // several sub-cycles later against the density row instead. A Cantera-backed
156 // accessor has its own bracket and does not need this, since formation
157 // enthalpies make a negative target legitimate there.
158 if (!(heavy_internal_energy_target > 0.0))
159 {
160 throw std::runtime_error(
161 "GammaLawThermo::heavyTemperature: heavy internal energy is " +
162 std::to_string(heavy_internal_energy_target) +
163 " J/kg, which no positive temperature can produce. The energy "
164 "row has lost more to the kinetic split than it holds; lower "
165 "cfl or refine the grid.");
166 }
167 return heavy_internal_energy_target * (m_gamma - 1.0) / (GasConstant * moles);
168 }
169
170 // ------------------------------------------------------------------------------ //
171 // GammaLawThermo - private methods //
172 // ------------------------------------------------------------------------------ //
173
174 double GammaLawThermo::heavyMolesPerMass(const double *Y) const
175 {
176 double moles = 0.0;
177 for (std::size_t k = 0; k < m_W.size(); k++)
178 {
179 if (static_cast<int>(k) == m_electron_index)
180 continue;
181 moles += Y[k] / m_W[k];
182 }
183 return moles;
184 }
185
186 double GammaLawThermo::electronMolesPerMass(const double *Y) const
187 {
188 if (m_electron_index < 0)
189 return 0.0;
190 const std::size_t e = static_cast<std::size_t>(m_electron_index);
191 return Y[e] / m_W[e];
192 }
193
194} // namespace rizer
Calorically perfect ThermoAccessor: one constant gamma for the heavy pool, ElectronGamma for the elec...
The two-temperature quantities every model in this extension reads back from a PlasmaPhase,...
double soundSpeed(double r, double t, double rho, double Tg, double Te, const double *Y) const override
Sound speed, PHYSICS.md appendix A.4: the frozen two-pool speed , each pool following its own adiaba...
double densityFromPressure(double pressure, double Tg, double Te, const double *Y) const override
Density reproducing a target total pressure at a given temperature pair and composition – the inverse...
double heavyTemperature(double rho, const double *Y, double heavy_internal_energy_target, double Tg_seed) const override
Invert for at fixed density and composition (NUMERICS.md section 8).
GammaLawThermo(double gamma, std::vector< double > molecular_weights, int electron_index)
double electronCv() const override
Electron specific heat at constant volume, per kg of ELECTRONS, .
void pressures(double r, double t, double rho, double Tg, double Te, const double *Y, double &p_heavy, double &p_electron) const override
Per-pool partial pressures, PHYSICS.md equation (G6).
double heavyEnergy(double Tg, const double *Y) const override
Heavy-pool internal energy per kg of MIXTURE, – the basis that makes the conserved variable of equa...
constexpr double e
Elementary charge [C]. Exact (CODATA).
Definition units.h:26
constexpr double ElectronGamma
Ratio of specific heats of the electron pool [-].
double monatomicElectronCv(double electron_molecular_weight)
Electron specific heat at constant volume, per kg of ELECTRONS, .
Definition PlasmaState.h:64
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