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(double* y, double* ydot)
36{
37 m_thermo->getMassFractions(y+m_offset_Y);
38 const vector<double>& 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.data()); // "omega dot"
59 }
60
61 // set the initial coverages
62 updateSurfaceProductionRates();
63
64 // reset ydot vector
65 std::fill(ydot, ydot + m_nv, 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.data());
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(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 + m_offset_Y);
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, double* y, double* ydot, double* residual)
172{
173 updateSurfaceProductionRates();
174 const vector<double>& mw = m_thermo->molecularWeights();
175 double sk_wk = 0;
176 for (size_t i = 0; i < m_nsp; ++i) {
177 sk_wk += m_sdot[i] * mw[i] / m_area;
178 }
179 m_thermo->getPartialMolarEnthalpies(m_hk.data());
180 // get net production
181 if (m_chem) {
182 m_kin->getNetProductionRates(m_wdot.data());
183 }
184
185 // set dphi/dz variables
186 double drhodz = ydot[0];
187 double dudz = ydot[1];
188 double dPdz = ydot[2];
189 double dTdz = ydot[3];
190
191 // use equation of state for density residual
192 residual[0] = m_rho - m_thermo->density();
193
194 //! use mass continuity for velocity residual
195 //! Kee.'s Chemically Reacting Flow, Eq. 16.48
196 residual[1] = m_u * drhodz + m_rho * dudz - sk_wk;
197
198 //! Use conservation of momentum for pressure residual
199 //! Kee.'s Chemically Reacting Flow, Eq. 16.62
200 //! [NOTE: friction is neglected]
201 residual[2] = m_rho * m_u * dudz + m_u * sk_wk + dPdz;
202
203 //! use conservation of energy for temperature residual
204 //! Kee.'s Chemically Reacting Flow, Eq. 16.58
205 //! [NOTE: adiabatic]
206 // Also, as above:
207 // h_mass = h_mole / Wk
208 // hence:
209 // h_mass * Wk = h_mol
210 if (m_energy) {
211 double cp_mass = m_thermo->cp_mass();
212 residual[3] = m_rho * m_u * cp_mass * dTdz;
213 for (size_t i = 0; i < m_nsp; ++i) {
214 residual[3] += m_hk[i] * (m_wdot[i] + m_sdot[i] / m_area);
215 }
216 } else {
217 residual[3] = dTdz;
218 }
219
220 //! species conservation equations
221 //! Kee.'s Chemically Reacting Flow, Eq. 16.51
222 double dSumYdz = 0;
223 for (size_t i = 0; i < m_nsp; ++i) {
224 residual[i + m_offset_Y] = m_rho * m_u * ydot[i + m_offset_Y] +
225 y[i + m_offset_Y] * sk_wk -
226 mw[i] * (m_wdot[i] + m_sdot[i] / m_area);
227 dSumYdz += ydot[i + m_offset_Y];
228 }
229 // Spread d/dz(sum(Y)) = 0 constraint across all species equations. `scale` is
230 // defined to make the size of the error in sum(Y) comparable to the overall rtol.
231 // TODO: find a better way to access a value of rtol.
232 double rtol = 1e-7;
233 double scale = 0.1 * m_rho * m_u / rtol;
234 for (size_t i = 0; i < m_nsp; ++i) {
235 residual[i + m_offset_Y] += scale * std::max(0.0, y[i + m_offset_Y]) * dSumYdz;
236 }
237}
238
239void FlowReactor::getConstraints(double* constraints) {
240 // mark all variables differential equations unless otherwise specified
241 std::fill(constraints, constraints + m_nv, 1.0);
242}
243
244size_t FlowReactor::componentIndex(const string& nm) const
245{
246 if (nm == "density") {
247 return 0;
248 } else if (nm == "speed") {
249 return 1;
250 } else if (nm == "pressure") {
251 return 2;
252 } else if (nm == "temperature") {
253 return 3;
254 }
255 try {
256 return m_thermo->speciesIndex(nm) + m_offset_Y;
257 } catch (const CanteraError&) {
258 throw CanteraError("FlowReactor::componentIndex",
259 "Component '{}' not found", nm);
260 }
261}
262
263string FlowReactor::componentName(size_t k)
264{
265 if (k == 0) {
266 return "density";
267 } else if (k == 1) {
268 return "speed";
269 } else if (k == 2) {
270 return "pressure";
271 } else if (k == 3) {
272 return "temperature";
273 } else if (k >= 4 && k < neq()) {
274 return m_thermo->speciesName(k - 4);
275 }
276 throw IndexError("FlowReactor::componentName", "component", k, m_nv);
277}
278
279}
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:104
const double GasConstant
Universal Gas Constant [J/kmol/K].
Definition ct_defs.h:122
Namespace for the Cantera kernel.
Definition AnyMap.cpp:595
void solve(DenseMatrix &A, double *b, size_t nrhs, size_t ldb)
Solve Ax = b. Array b is overwritten on exit with x.