Cantera  4.0.0a1
Loading...
Searching...
No Matches
FlowReactor.cpp
Go to the documentation of this file.
1//! @file FlowReactor.cpp A steady-state, ideal-gas, adiabatic,
2//! constant-area (cylindrical), frictionless plug flow reactor
3
4// This file is part of Cantera. See License.txt in the top-level directory or
5// at https://cantera.org/license.txt for license and copyright information.
6
16
17namespace Cantera
18{
19
20FlowReactor::FlowReactor(shared_ptr<Solution> sol, const string& name)
21 : FlowReactor(sol, true, name)
22{
23}
24
25FlowReactor::FlowReactor(shared_ptr<Solution> sol, bool clone, const string& name)
26 : IdealGasReactor(sol, clone, name)
27{
28 m_nv = 4 + m_nsp; // rho, u, P, T, and species mass fractions
29 m_rho = m_thermo->density();
30 // resize temporary arrays
31 m_wdot.resize(m_nsp);
32 m_hk.resize(m_nsp);
33}
34
35void FlowReactor::getStateDae(span<double> y, span<double> ydot)
36{
37 m_thermo->getMassFractions(y.subspan(m_offset_Y, m_nsp));
38 auto mw = m_thermo->molecularWeights();
39
40 // set the first component to the initial density
41 y[0] = m_thermo->density();
42
43 if (m_u < 0) {
44 throw CanteraError("FlowReactor::getStateDae",
45 "Set mass flow rate before initializing reactor");
46 }
47
48 // set the second component to the initial speed
49 y[1] = m_u;
50
51 // set the third component to the initial pressure
52 y[2] = m_thermo->pressure();
53
54 // set the fourth component to the initial temperature
55 y[3] = m_thermo->temperature();
56
57 if (m_chem) {
58 m_kin->getNetProductionRates(m_wdot); // "omega dot"
59 }
60
61 // set the initial coverages
62 updateSurfaceProductionRates();
63
64 // reset ydot vector
65 std::fill(ydot.begin(), ydot.end(), 0.0);
66
67 // next, we must solve for the initial derivative values
68 // a . ydot = b
69 // ydot -> [rho', u', P', T', Yk']
70 // a -> coefficients of [rho', u', P', T', Yk'] in the governing eq's, given
71 // the initial state
72 // b -> rhs constant of each conservation equation
73 //
74 // note that the species coverages are included in the algebraic constraints,
75 // hence are not included here
76 DenseMatrix a(m_offset_Y + m_nsp, m_offset_Y + m_nsp);
77
78 // first row is the ideal gas equation, differentiated with respect to z
79 a(0, 0) = - GasConstant * m_thermo->temperature() / m_thermo->meanMolecularWeight();
80 a(0, 2) = 1;
81 a(0, 3) = - m_rho * GasConstant / m_thermo->meanMolecularWeight();
82 for (size_t i = 0; i < m_nsp; ++i) {
83 a(0, m_offset_Y + i) = - m_rho * m_thermo->temperature() / mw[i];
84 }
85
86 // initialize the second row from mass conservation equation (Kee 16.48)
87 a(1, 0) = m_u; // rho' * u
88 a(1, 1) = m_rho; // u' * rho
89
90 // initialize the third row from momentum conservation equation (Kee 16.62),
91 // rho * u * u' + P' = -u * (P / A_c) * sum(sdot_k * W_k)
92 a(2, 1) = m_rho * m_u; // rho * u * u'
93 a(2, 2) = 1; // 1 * P'
94
95 // initialize the fourth row from conservation of energy (Kee 16.58), adiabatic
96 double cp_mass = m_thermo->cp_mass();
97 a(3, 3) = m_rho * m_u * cp_mass; // rho * u * cp * T'
98
99 // initialize the next rows from the mass-fraction equations (Kee 16.51)
100 for (size_t i = 0; i < m_nsp; ++i) {
101 a(m_offset_Y + i, m_offset_Y + i) = m_rho * m_u; // rho * u * Yk'
102 }
103
104 // now set the RHS vector
105
106 // get (perim / Ac) * sum(sk' * wk), used multiple places
107 double h_sk_wk = 0;
108 for (size_t i = 0; i < m_nsp; ++i) {
109 h_sk_wk += m_sdot[i] * mw[i] / m_area;
110 }
111
112 // RHS of ideal gas eq. is zero
113 ydot[0] = 0;
114
115 // mass continuity, Kee 16.48
116 // (perim / Ac) * sum(sk' * wk)
117 ydot[1] = h_sk_wk;
118
119 // momentum conservation, Kee 16.62, no friction
120 // - u * (perim / Ac) * sum(sk' * wk)
121 ydot[2] = -m_u * h_sk_wk;
122
123 if (m_energy) {
124 // energy conservation, Kee 16.58, adiabatic
125 // -sum(wk' * Wk * hk) - (perim / Ac) * sum(sk' * Wk * hk)
126 // Note: the partial molar enthalpies are in molar units, while Kee uses mass
127 // units, where:
128 // h_mass = h_mole / Wk
129 // hence:
130 // h_mass * Wk = h_mol
131 m_thermo->getPartialMolarEnthalpies(m_hk);
132 for (size_t i = 0; i < m_nsp; ++i) {
133 ydot[3] -= m_hk[i] * (m_wdot[i] + m_sdot[i] / m_area);
134 }
135 } else {
136 ydot[3] = 0;
137 }
138
139 // mass-fraction equations Kee 16.51
140 // - Yk * (perim / Ac) * sum(sk' * Wk) + wk' * Wk + (perim / Ac) * sk' * Wk
141 for (size_t i = 0; i < m_nsp; ++i) {
142 ydot[m_offset_Y + i] = -y[m_offset_Y + i] * h_sk_wk +
143 mw[i] * (m_wdot[i] + m_sdot[i] / m_area);
144 }
145
146 // and solve
147 solve(a, ydot, 1, 0);
148}
149
150void FlowReactor::updateState(span<const double> y)
151{
152 // Set the mass fractions and density of the mixture.
153 m_rho = y[0];
154 m_u = y[1];
155 m_thermo->setMassFractions_NoNorm(y.subspan(m_offset_Y, m_nsp));
156 m_thermo->setState_TP(y[3], y[2]);
157}
158
159void FlowReactor::setMassFlowRate(double mdot)
160{
161 m_rho = m_thermo->density();
162 m_u = mdot/(m_rho * m_area);
163}
164
165void FlowReactor::setArea(double area) {
166 double mdot = m_rho * m_u * m_area;
167 m_area = area;
168 setMassFlowRate(mdot);
169}
170
171void FlowReactor::evalDae(double time, span<const double> y,
172 span<const double> ydot, span<double> residual)
173{
174 updateSurfaceProductionRates();
175 auto mw = m_thermo->molecularWeights();
176 double sk_wk = 0;
177 for (size_t i = 0; i < m_nsp; ++i) {
178 sk_wk += m_sdot[i] * mw[i] / m_area;
179 }
180 m_thermo->getPartialMolarEnthalpies(m_hk);
181 // get net production
182 if (m_chem) {
183 m_kin->getNetProductionRates(m_wdot);
184 }
185
186 // set dphi/dz variables
187 double drhodz = ydot[0];
188 double dudz = ydot[1];
189 double dPdz = ydot[2];
190 double dTdz = ydot[3];
191
192 // use equation of state for density residual
193 residual[0] = m_rho - m_thermo->density();
194
195 //! use mass continuity for velocity residual
196 //! Kee.'s Chemically Reacting Flow, Eq. 16.48
197 residual[1] = m_u * drhodz + m_rho * dudz - sk_wk;
198
199 //! Use conservation of momentum for pressure residual
200 //! Kee.'s Chemically Reacting Flow, Eq. 16.62
201 //! [NOTE: friction is neglected]
202 residual[2] = m_rho * m_u * dudz + m_u * sk_wk + dPdz;
203
204 //! use conservation of energy for temperature residual
205 //! Kee.'s Chemically Reacting Flow, Eq. 16.58
206 //! [NOTE: adiabatic]
207 // Also, as above:
208 // h_mass = h_mole / Wk
209 // hence:
210 // h_mass * Wk = h_mol
211 if (m_energy) {
212 double cp_mass = m_thermo->cp_mass();
213 residual[3] = m_rho * m_u * cp_mass * dTdz;
214 for (size_t i = 0; i < m_nsp; ++i) {
215 residual[3] += m_hk[i] * (m_wdot[i] + m_sdot[i] / m_area);
216 }
217 } else {
218 residual[3] = dTdz;
219 }
220
221 //! species conservation equations
222 //! Kee.'s Chemically Reacting Flow, Eq. 16.51
223 double dSumYdz = 0;
224 for (size_t i = 0; i < m_nsp; ++i) {
225 residual[i + m_offset_Y] = m_rho * m_u * ydot[i + m_offset_Y] +
226 y[i + m_offset_Y] * sk_wk -
227 mw[i] * (m_wdot[i] + m_sdot[i] / m_area);
228 dSumYdz += ydot[i + m_offset_Y];
229 }
230 // Spread d/dz(sum(Y)) = 0 constraint across all species equations. `scale` is
231 // defined to make the size of the error in sum(Y) comparable to the overall rtol.
232 // TODO: find a better way to access a value of rtol.
233 double rtol = 1e-7;
234 double scale = 0.1 * m_rho * m_u / rtol;
235 for (size_t i = 0; i < m_nsp; ++i) {
236 residual[i + m_offset_Y] += scale * std::max(0.0, y[i + m_offset_Y]) * dSumYdz;
237 }
238}
239
240void FlowReactor::getConstraints(span<double> constraints) {
241 // mark all variables differential equations unless otherwise specified
242 std::fill(constraints.begin(), constraints.end(), 1.0);
243}
244
245size_t FlowReactor::componentIndex(const string& nm) const
246{
247 if (nm == "density") {
248 return 0;
249 } else if (nm == "speed") {
250 return 1;
251 } else if (nm == "pressure") {
252 return 2;
253 } else if (nm == "temperature") {
254 return 3;
255 }
256 try {
257 return m_thermo->speciesIndex(nm) + m_offset_Y;
258 } catch (const CanteraError&) {
259 throw CanteraError("FlowReactor::componentIndex",
260 "Component '{}' not found", nm);
261 }
262}
263
264string FlowReactor::componentName(size_t k)
265{
266 if (k == 0) {
267 return "density";
268 } else if (k == 1) {
269 return "speed";
270 } else if (k == 2) {
271 return "pressure";
272 } else if (k == 3) {
273 return "temperature";
274 } else if (k >= 4 && k < neq()) {
275 return m_thermo->speciesName(k - 4);
276 }
277 throw IndexError("FlowReactor::componentName", "component", k, m_nv);
278}
279
280}
Headers for the DenseMatrix object, which deals with dense rectangular matrices and description of th...
Base class for kinetics managers and also contains the kineticsmgr module documentation (see Kinetics...
Header file for class ReactorSurface.
Header for a simple thermodynamics model of a surface phase derived from ThermoPhase,...
Header file for class ThermoPhase, the base class for phases with thermodynamic properties,...
Base class for exceptions thrown by Cantera classes.
A class for full (non-sparse) matrices with Fortran-compatible data storage, which adds matrix operat...
Definition DenseMatrix.h:42
An array index is out of range.
This file contains definitions for utility functions and text for modules, inputfiles and logging,...
void scale(InputIter begin, InputIter end, OutputIter out, S scale_factor)
Multiply elements of an array by a scale factor.
Definition utilities.h:118
const double GasConstant
Universal Gas Constant [J/kmol/K].
Definition ct_defs.h:123
Namespace for the Cantera kernel.
Definition AnyMap.cpp:595
void solve(DenseMatrix &A, span< double > b, size_t nrhs, size_t ldb)
Solve Ax = b. Array b is overwritten on exit with x.