Cantera  3.1.0a2
Loading...
Searching...
No Matches
Flow1D.cpp
Go to the documentation of this file.
1//! @file Flow1D.cpp
2
3// This file is part of Cantera. See License.txt in the top-level directory or
4// at https://cantera.org/license.txt for license and copyright information.
5
8#include "cantera/oneD/refine.h"
12#include "cantera/base/global.h"
13
14using namespace std;
15
16namespace Cantera
17{
18
19Flow1D::Flow1D(ThermoPhase* ph, size_t nsp, size_t points) :
20 Domain1D(nsp+c_offset_Y, points),
21 m_nsp(nsp)
22{
23 m_points = points;
24
25 if (ph == 0) {
26 return; // used to create a dummy object
27 }
28 m_thermo = ph;
29
30 size_t nsp2 = m_thermo->nSpecies();
31 if (nsp2 != m_nsp) {
32 m_nsp = nsp2;
34 }
35
36 // make a local copy of the species molecular weight vector
37 m_wt = m_thermo->molecularWeights();
38
39 // set pressure based on associated thermo object
40 setPressure(m_thermo->pressure());
41
42 // the species mass fractions are the last components in the solution
43 // vector, so the total number of components is the number of species
44 // plus the offset of the first mass fraction.
45 m_nv = c_offset_Y + m_nsp;
46
47 // enable all species equations by default
48 m_do_species.resize(m_nsp, true);
49
50 // but turn off the energy equation at all points
51 m_do_energy.resize(m_points,false);
52
53 m_diff.resize(m_nsp*m_points);
54 m_multidiff.resize(m_nsp*m_nsp*m_points);
55 m_flux.resize(m_nsp,m_points);
58 m_dhk_dz.resize(m_nsp, m_points - 1, 0.0);
59 m_ybar.resize(m_nsp);
60 m_qdotRadiation.resize(m_points, 0.0);
61
62 //-------------- default solution bounds --------------------
63 setBounds(c_offset_U, -1e20, 1e20); // no bounds on u
64 setBounds(c_offset_V, -1e20, 1e20); // no bounds on V
65 setBounds(c_offset_T, 200.0, 2*m_thermo->maxTemp()); // temperature bounds
66 setBounds(c_offset_L, -1e20, 1e20); // lambda should be negative
67 setBounds(c_offset_E, -1e20, 1e20); // no bounds on electric field
68 setBounds(c_offset_Uo, -1e20, 1e20); // no bounds on Uo
69 // mass fraction bounds
70 for (size_t k = 0; k < m_nsp; k++) {
71 setBounds(c_offset_Y+k, -1.0e-7, 1.0e5);
72 }
73
74 //-------------------- grid refinement -------------------------
75 m_refiner->setActive(c_offset_U, false);
76 m_refiner->setActive(c_offset_V, false);
77 m_refiner->setActive(c_offset_T, false);
78 m_refiner->setActive(c_offset_L, false);
79 m_refiner->setActive(c_offset_Uo, false);
80
81 vector<double> gr;
82 for (size_t ng = 0; ng < m_points; ng++) {
83 gr.push_back(1.0*ng/m_points);
84 }
85 setupGrid(m_points, gr.data());
86
87 // Find indices for radiating species
88 m_kRadiating.resize(2, npos);
89 m_kRadiating[0] = m_thermo->speciesIndex("CO2");
90 m_kRadiating[1] = m_thermo->speciesIndex("H2O");
91}
92
93Flow1D::Flow1D(shared_ptr<ThermoPhase> th, size_t nsp, size_t points)
94 : Flow1D(th.get(), nsp, points)
95{
96 auto sol = Solution::create();
97 sol->setThermo(th);
98 setSolution(sol);
99}
100
101Flow1D::Flow1D(shared_ptr<Solution> sol, const string& id, size_t points)
102 : Flow1D(sol->thermo().get(), sol->thermo()->nSpecies(), points)
103{
104 setSolution(sol);
105 m_id = id;
106 m_kin = m_solution->kinetics().get();
107 m_trans = m_solution->transport().get();
108 if (m_trans->transportModel() == "none") {
109 throw CanteraError("Flow1D::Flow1D",
110 "An appropriate transport model\nshould be set when instantiating the "
111 "Solution ('gas') object.");
112 }
113 m_solution->registerChangedCallback(this, [this]() {
114 setKinetics(m_solution->kinetics());
115 setTransport(m_solution->transport());
116 });
117}
118
119Flow1D::~Flow1D()
120{
121 if (m_solution) {
122 m_solution->removeChangedCallback(this);
123 }
124}
125
126string Flow1D::domainType() const {
127 if (m_isFree) {
128 return "free-flow";
129 }
130 if (m_usesLambda) {
131 return "axisymmetric-flow";
132 }
133 return "unstrained-flow";
134}
135
136void Flow1D::setKinetics(shared_ptr<Kinetics> kin)
137{
138 m_kin = kin.get();
139 m_solution->setKinetics(kin);
140}
141
142void Flow1D::setTransport(shared_ptr<Transport> trans)
143{
144 if (!trans) {
145 throw CanteraError("Flow1D::setTransport", "Unable to set empty transport.");
146 }
147 m_trans = trans.get();
148 if (m_trans->transportModel() == "none") {
149 throw CanteraError("Flow1D::setTransport", "Invalid Transport model 'none'.");
150 }
151 m_do_multicomponent = (m_trans->transportModel() == "multicomponent" ||
152 m_trans->transportModel() == "multicomponent-CK");
153
154 m_diff.resize(m_nsp * m_points);
155 if (m_do_multicomponent) {
156 m_multidiff.resize(m_nsp * m_nsp*m_points);
157 m_dthermal.resize(m_nsp, m_points, 0.0);
158 }
159 m_solution->setTransport(trans);
160}
161
162void Flow1D::resize(size_t ncomponents, size_t points)
163{
164 Domain1D::resize(ncomponents, points);
165 m_rho.resize(m_points, 0.0);
166 m_wtm.resize(m_points, 0.0);
167 m_cp.resize(m_points, 0.0);
168 m_visc.resize(m_points, 0.0);
169 m_tcon.resize(m_points, 0.0);
170
171 m_diff.resize(m_nsp*m_points);
172 if (m_do_multicomponent) {
173 m_multidiff.resize(m_nsp*m_nsp*m_points);
174 m_dthermal.resize(m_nsp, m_points, 0.0);
175 }
176 m_flux.resize(m_nsp,m_points);
178 m_hk.resize(m_nsp, m_points, 0.0);
179 m_dhk_dz.resize(m_nsp, m_points - 1, 0.0);
180 m_do_energy.resize(m_points,false);
181 m_qdotRadiation.resize(m_points, 0.0);
182 m_fixedtemp.resize(m_points);
183
184 m_dz.resize(m_points-1);
185 m_z.resize(m_points);
186}
187
188void Flow1D::setupGrid(size_t n, const double* z)
189{
190 resize(m_nv, n);
191
192 m_z[0] = z[0];
193 for (size_t j = 1; j < m_points; j++) {
194 if (z[j] <= z[j-1]) {
195 throw CanteraError("Flow1D::setupGrid",
196 "grid points must be monotonically increasing");
197 }
198 m_z[j] = z[j];
199 m_dz[j-1] = m_z[j] - m_z[j-1];
200 }
201}
202
204{
205 double* x = xg + loc();
206 for (size_t j = 0; j < m_points; j++) {
207 double* Y = x + m_nv*j + c_offset_Y;
208 m_thermo->setMassFractions(Y);
209 m_thermo->getMassFractions(Y);
210 }
211}
212
213void Flow1D::setTransportModel(const string& trans)
214{
215 m_solution->setTransportModel(trans);
216}
217
219 return m_trans->transportModel();
220}
221
223 m_fluxGradientBasis = fluxGradientBasis;
224 if (transportModel() != "mixture-averaged-CK" &&
225 transportModel() != "mixture-averaged") {
226 warn_user("Flow1D::setFluxGradientBasis",
227 "Setting fluxGradientBasis only affects "
228 "the mixture-averaged diffusion model.");
229 }
230}
231
233{
234 for (size_t j = 0; j < m_points; j++) {
235 T(x,j) = m_thermo->temperature();
236 m_thermo->getMassFractions(&Y(x, 0, j));
237 m_rho[j] = m_thermo->density();
238 }
239}
240
241void Flow1D::setGas(const double* x, size_t j)
242{
243 m_thermo->setTemperature(T(x,j));
244 const double* yy = x + m_nv*j + c_offset_Y;
245 m_thermo->setMassFractions_NoNorm(yy);
246 m_thermo->setPressure(m_press);
247}
248
249void Flow1D::setGasAtMidpoint(const double* x, size_t j)
250{
251 m_thermo->setTemperature(0.5*(T(x,j)+T(x,j+1)));
252 const double* yyj = x + m_nv*j + c_offset_Y;
253 const double* yyjp = x + m_nv*(j+1) + c_offset_Y;
254 for (size_t k = 0; k < m_nsp; k++) {
255 m_ybar[k] = 0.5*(yyj[k] + yyjp[k]);
256 }
257 m_thermo->setMassFractions_NoNorm(m_ybar.data());
258 m_thermo->setPressure(m_press);
259}
260
261void Flow1D::_finalize(const double* x)
262{
263 if (!m_do_multicomponent && m_do_soret) {
264 throw CanteraError("Flow1D::_finalize",
265 "Thermal diffusion (the Soret effect) is enabled, and requires "
266 "using a multicomponent transport model.");
267 }
268
269 size_t nz = m_zfix.size();
270 bool e = m_do_energy[0];
271 for (size_t j = 0; j < m_points; j++) {
272 if (e || nz == 0) {
273 m_fixedtemp[j] = T(x, j);
274 } else {
275 double zz = (z(j) - z(0))/(z(m_points - 1) - z(0));
276 double tt = linearInterp(zz, m_zfix, m_tfix);
277 m_fixedtemp[j] = tt;
278 }
279 }
280 if (e) {
281 solveEnergyEqn();
282 }
283
284 if (m_isFree) {
285 // If the domain contains the temperature fixed point, make sure that it
286 // is correctly set. This may be necessary when the grid has been modified
287 // externally.
288 if (m_tfixed != Undef) {
289 for (size_t j = 0; j < m_points; j++) {
290 if (z(j) == m_zfixed) {
291 return; // fixed point is already set correctly
292 }
293 }
294
295 for (size_t j = 0; j < m_points - 1; j++) {
296 // Find where the temperature profile crosses the current
297 // fixed temperature.
298 if ((T(x, j) - m_tfixed) * (T(x, j+1) - m_tfixed) <= 0.0) {
299 m_tfixed = T(x, j+1);
300 m_zfixed = z(j+1);
301 return;
302 }
303 }
304 }
305 }
306}
307
308void Flow1D::eval(size_t jGlobal, double* xGlobal, double* rsdGlobal,
309 integer* diagGlobal, double rdt)
310{
311 // If evaluating a Jacobian, and the global point is outside the domain of
312 // influence for this domain, then skip evaluating the residual
313 if (jGlobal != npos && (jGlobal + 1 < firstPoint() || jGlobal > lastPoint() + 1)) {
314 return;
315 }
316
317 // start of local part of global arrays
318 double* x = xGlobal + loc();
319 double* rsd = rsdGlobal + loc();
320 integer* diag = diagGlobal + loc();
321
322 size_t jmin, jmax;
323 if (jGlobal == npos) { // evaluate all points
324 jmin = 0;
325 jmax = m_points - 1;
326 } else { // evaluate points for Jacobian
327 size_t jpt = (jGlobal == 0) ? 0 : jGlobal - firstPoint();
328 jmin = std::max<size_t>(jpt, 1) - 1;
329 jmax = std::min(jpt+1,m_points-1);
330 }
331
332 updateProperties(jGlobal, x, jmin, jmax);
333
334 if (m_do_radiation) { // Calculation of qdotRadiation
335 computeRadiation(x, jmin, jmax);
336 }
337
338 evalContinuity(x, rsd, diag, rdt, jmin, jmax);
339 evalMomentum(x, rsd, diag, rdt, jmin, jmax);
340 evalEnergy(x, rsd, diag, rdt, jmin, jmax);
341 evalLambda(x, rsd, diag, rdt, jmin, jmax);
342 evalElectricField(x, rsd, diag, rdt, jmin, jmax);
343 evalUo(x, rsd, diag, rdt, jmin, jmax);
344 evalSpecies(x, rsd, diag, rdt, jmin, jmax);
345}
346
347void Flow1D::updateProperties(size_t jg, double* x, size_t jmin, size_t jmax)
348{
349 // properties are computed for grid points from j0 to j1
350 size_t j0 = std::max<size_t>(jmin, 1) - 1;
351 size_t j1 = std::min(jmax+1,m_points-1);
352
353 updateThermo(x, j0, j1);
354 if (jg == npos || m_force_full_update) {
355 // update transport properties only if a Jacobian is not being
356 // evaluated, or if specifically requested
357 updateTransport(x, j0, j1);
358 }
359 if (jg == npos) {
360 double* Yleft = x + index(c_offset_Y, jmin);
361 m_kExcessLeft = distance(Yleft, max_element(Yleft, Yleft + m_nsp));
362 double* Yright = x + index(c_offset_Y, jmax);
363 m_kExcessRight = distance(Yright, max_element(Yright, Yright + m_nsp));
364 }
365
366 // update the species diffusive mass fluxes whether or not a
367 // Jacobian is being evaluated
368 updateDiffFluxes(x, j0, j1);
369}
370
371void Flow1D::updateTransport(double* x, size_t j0, size_t j1)
372{
373 if (m_do_multicomponent) {
374 for (size_t j = j0; j < j1; j++) {
375 setGasAtMidpoint(x,j);
376 double wtm = m_thermo->meanMolecularWeight();
377 double rho = m_thermo->density();
378 m_visc[j] = (m_dovisc ? m_trans->viscosity() : 0.0);
379 m_trans->getMultiDiffCoeffs(m_nsp, &m_multidiff[mindex(0,0,j)]);
380
381 // Use m_diff as storage for the factor outside the summation
382 for (size_t k = 0; k < m_nsp; k++) {
383 m_diff[k+j*m_nsp] = m_wt[k] * rho / (wtm*wtm);
384 }
385
386 m_tcon[j] = m_trans->thermalConductivity();
387 if (m_do_soret) {
388 m_trans->getThermalDiffCoeffs(m_dthermal.ptrColumn(0) + j*m_nsp);
389 }
390 }
391 } else { // mixture averaged transport
392 for (size_t j = j0; j < j1; j++) {
393 setGasAtMidpoint(x,j);
394 m_visc[j] = (m_dovisc ? m_trans->viscosity() : 0.0);
395
396 if (m_fluxGradientBasis == ThermoBasis::molar) {
397 m_trans->getMixDiffCoeffs(&m_diff[j*m_nsp]);
398 } else {
399 m_trans->getMixDiffCoeffsMass(&m_diff[j*m_nsp]);
400 }
401
402 double rho = m_thermo->density();
403
404 if (m_fluxGradientBasis == ThermoBasis::molar) {
405 double wtm = m_thermo->meanMolecularWeight();
406 for (size_t k=0; k < m_nsp; k++) {
407 m_diff[k+j*m_nsp] *= m_wt[k] * rho / wtm;
408 }
409 } else {
410 for (size_t k=0; k < m_nsp; k++) {
411 m_diff[k+j*m_nsp] *= rho;
412 }
413 }
414 m_tcon[j] = m_trans->thermalConductivity();
415 }
416 }
417}
418
419void Flow1D::updateDiffFluxes(const double* x, size_t j0, size_t j1)
420{
421 if (m_do_multicomponent) {
422 for (size_t j = j0; j < j1; j++) {
423 double dz = z(j+1) - z(j);
424 for (size_t k = 0; k < m_nsp; k++) {
425 double sum = 0.0;
426 for (size_t m = 0; m < m_nsp; m++) {
427 sum += m_wt[m] * m_multidiff[mindex(k,m,j)] * (X(x,m,j+1)-X(x,m,j));
428 }
429 m_flux(k,j) = sum * m_diff[k+j*m_nsp] / dz;
430 }
431 }
432 } else {
433 for (size_t j = j0; j < j1; j++) {
434 double sum = 0.0;
435 double dz = z(j+1) - z(j);
436 if (m_fluxGradientBasis == ThermoBasis::molar) {
437 for (size_t k = 0; k < m_nsp; k++) {
438 m_flux(k,j) = m_diff[k+m_nsp*j] * (X(x,k,j) - X(x,k,j+1))/dz;
439 sum -= m_flux(k,j);
440 }
441 } else {
442 for (size_t k = 0; k < m_nsp; k++) {
443 m_flux(k,j) = m_diff[k+m_nsp*j] * (Y(x,k,j) - Y(x,k,j+1))/dz;
444 sum -= m_flux(k,j);
445 }
446 }
447 // correction flux to insure that \sum_k Y_k V_k = 0.
448 for (size_t k = 0; k < m_nsp; k++) {
449 m_flux(k,j) += sum*Y(x,k,j);
450 }
451 }
452 }
453
454 if (m_do_soret) {
455 for (size_t m = j0; m < j1; m++) {
456 double gradlogT = 2.0 * (T(x,m+1) - T(x,m)) /
457 ((T(x,m+1) + T(x,m)) * (z(m+1) - z(m)));
458 for (size_t k = 0; k < m_nsp; k++) {
459 m_flux(k,m) -= m_dthermal(k,m)*gradlogT;
460 }
461 }
462 }
463}
464
465void Flow1D::computeRadiation(double* x, size_t jmin, size_t jmax)
466{
467 // Variable definitions for the Planck absorption coefficient and the
468 // radiation calculation:
469 double k_P_ref = 1.0*OneAtm;
470
471 // Polynomial coefficients:
472 const double c_H2O[6] = {-0.23093, -1.12390, 9.41530, -2.99880,
473 0.51382, -1.86840e-5};
474 const double c_CO2[6] = {18.741, -121.310, 273.500, -194.050,
475 56.310, -5.8169};
476
477 // Calculation of the two boundary values
478 double boundary_Rad_left = m_epsilon_left * StefanBoltz * pow(T(x, 0), 4);
479 double boundary_Rad_right = m_epsilon_right * StefanBoltz * pow(T(x, m_points - 1), 4);
480
481 for (size_t j = jmin; j < jmax; j++) {
482 // calculation of the mean Planck absorption coefficient
483 double k_P = 0;
484 // Absorption coefficient for H2O
485 if (m_kRadiating[1] != npos) {
486 double k_P_H2O = 0;
487 for (size_t n = 0; n <= 5; n++) {
488 k_P_H2O += c_H2O[n] * pow(1000 / T(x, j), (double) n);
489 }
490 k_P_H2O /= k_P_ref;
491 k_P += m_press * X(x, m_kRadiating[1], j) * k_P_H2O;
492 }
493 // Absorption coefficient for CO2
494 if (m_kRadiating[0] != npos) {
495 double k_P_CO2 = 0;
496 for (size_t n = 0; n <= 5; n++) {
497 k_P_CO2 += c_CO2[n] * pow(1000 / T(x, j), (double) n);
498 }
499 k_P_CO2 /= k_P_ref;
500 k_P += m_press * X(x, m_kRadiating[0], j) * k_P_CO2;
501 }
502
503 // Calculation of the radiative heat loss term
504 double radiative_heat_loss = 2 * k_P *(2 * StefanBoltz * pow(T(x, j), 4)
505 - boundary_Rad_left - boundary_Rad_right);
506
507 // set the radiative heat loss vector
508 m_qdotRadiation[j] = radiative_heat_loss;
509 }
510}
511
512void Flow1D::evalContinuity(double* x, double* rsd, int* diag,
513 double rdt, size_t jmin, size_t jmax)
514{
515 // The left boundary has the same form for all cases.
516 if (jmin == 0) { // left boundary
517 rsd[index(c_offset_U,jmin)] = -(rho_u(x,jmin + 1) - rho_u(x,jmin))/m_dz[jmin]
518 -(density(jmin + 1)*V(x,jmin + 1)
519 + density(jmin)*V(x,jmin));
520
521 diag[index(c_offset_U,jmin)] = 0; // Algebraic constraint
522 }
523
524 if (jmax == m_points - 1) { // right boundary
525 if (m_usesLambda) { // axisymmetric flow
526 rsd[index(c_offset_U, jmax)] = rho_u(x, jmax);
527 } else { // right boundary (same for unstrained/free-flow)
528 rsd[index(c_offset_U, jmax)] = rho_u(x, jmax) - rho_u(x, jmax - 1);
529 }
530 diag[index(c_offset_U, jmax)] = 0; // Algebraic constraint
531 }
532
533 // j0 and j1 are constrained to only interior points
534 size_t j0 = std::max<size_t>(jmin, 1);
535 size_t j1 = std::min(jmax, m_points - 2);
536 if (m_usesLambda) { // "axisymmetric-flow"
537 for (size_t j = j0; j <= j1; j++) { // interior points
538 // For "axisymmetric-flow", the continuity equation propagates the
539 // mass flow rate information to the left (j+1 -> j) from the value
540 // specified at the right boundary. The lambda information propagates
541 // in the opposite direction.
542 rsd[index(c_offset_U,j)] = -(rho_u(x,j+1) - rho_u(x,j))/m_dz[j]
543 -(density(j+1)*V(x,j+1) + density(j)*V(x,j));
544
545 diag[index(c_offset_U, j)] = 0; // Algebraic constraint
546 }
547 } else if (m_isFree) { // "free-flow"
548 for (size_t j = j0; j <= j1; j++) {
549 // terms involving V are zero as V=0 by definition
550 if (grid(j) > m_zfixed) {
551 rsd[index(c_offset_U,j)] = -(rho_u(x,j) - rho_u(x,j-1))/m_dz[j-1];
552 } else if (grid(j) == m_zfixed) {
553 if (m_do_energy[j]) {
554 rsd[index(c_offset_U,j)] = (T(x,j) - m_tfixed);
555 } else {
556 rsd[index(c_offset_U,j)] = (rho_u(x,j) - m_rho[0]*0.3); // why 0.3?
557 }
558 } else { // grid(j < m_zfixed
559 rsd[index(c_offset_U,j)] = -(rho_u(x,j+1) - rho_u(x,j))/m_dz[j];
560 }
561 diag[index(c_offset_U, j)] = 0; // Algebraic constraint
562 }
563 } else { // "unstrained-flow" (fixed mass flow rate)
564 for (size_t j = j0; j <= j1; j++) {
565 rsd[index(c_offset_U, j)] = rho_u(x, j) - rho_u(x, j - 1);
566 diag[index(c_offset_U, j)] = 0; // Algebraic constraint
567 }
568 }
569}
570
571void Flow1D::evalMomentum(double* x, double* rsd, int* diag,
572 double rdt, size_t jmin, size_t jmax)
573{
574 if (!m_usesLambda) { //disable this equation
575 for (size_t j = jmin; j <= jmax; j++) {
576 rsd[index(c_offset_V, j)] = V(x, j);
577 diag[index(c_offset_V, j)] = 0;
578 }
579 return;
580 }
581
582 if (jmin == 0) { // left boundary
583 rsd[index(c_offset_V,jmin)] = V(x,jmin);
584 }
585
586 if (jmax == m_points - 1) { // right boundary
587 rsd[index(c_offset_V,jmax)] = V(x,jmax);
588 diag[index(c_offset_V,jmax)] = 0;
589 }
590
591 // j0 and j1 are constrained to only interior points
592 size_t j0 = std::max<size_t>(jmin, 1);
593 size_t j1 = std::min(jmax, m_points - 2);
594 for (size_t j = j0; j <= j1; j++) { // interior points
595 rsd[index(c_offset_V,j)] = (shear(x, j) - lambda(x, j)
596 - rho_u(x, j) * dVdz(x, j)
597 - m_rho[j] * V(x, j) * V(x, j)) / m_rho[j]
598 - rdt * (V(x, j) - V_prev(j));
599 diag[index(c_offset_V, j)] = 1;
600 }
601}
602
603void Flow1D::evalLambda(double* x, double* rsd, int* diag,
604 double rdt, size_t jmin, size_t jmax)
605{
606 if (!m_usesLambda) { // disable this equation
607 for (size_t j = jmin; j <= jmax; j++) {
608 rsd[index(c_offset_L, j)] = lambda(x, j);
609 diag[index(c_offset_L, j)] = 0;
610 }
611 return;
612 }
613
614 if (jmin == 0) { // left boundary
615 if (m_twoPointControl) {
616 rsd[index(c_offset_L, jmin)] = lambda(x,jmin+1) - lambda(x,jmin);
617 } else {
618 rsd[index(c_offset_L, jmin)] = -rho_u(x, jmin);
619 }
620 }
621
622 if (jmax == m_points - 1) { // right boundary
623 rsd[index(c_offset_L, jmax)] = lambda(x, jmax) - lambda(x, jmax-1);
624 diag[index(c_offset_L, jmax)] = 0;
625 }
626
627 // j0 and j1 are constrained to only interior points
628 size_t j0 = std::max<size_t>(jmin, 1);
629 size_t j1 = std::min(jmax, m_points - 2);
630 for (size_t j = j0; j <= j1; j++) { // interior points
631 if (m_twoPointControl) {
632 if (grid(j) == m_zLeft) {
633 rsd[index(c_offset_L, j)] = T(x,j) - m_tLeft;
634 } else if (grid(j) > m_zLeft) {
635 rsd[index(c_offset_L, j)] = lambda(x,j) - lambda(x,j-1);
636 } else if (grid(j) < m_zLeft) {
637 rsd[index(c_offset_L, j)] = lambda(x,j+1) - lambda(x,j);
638 }
639 } else {
640 rsd[index(c_offset_L, j)] = lambda(x,j) - lambda(x,j-1);
641 }
642 diag[index(c_offset_L, j)] = 0;
643 }
644}
645
646void Flow1D::evalEnergy(double* x, double* rsd, int* diag,
647 double rdt, size_t jmin, size_t jmax)
648{
649 if (jmin == 0) { // left boundary
650 rsd[index(c_offset_T,jmin)] = T(x,jmin);
651 }
652
653 if (jmax == m_points - 1) { // right boundary
654 rsd[index(c_offset_T, jmax)] = T(x,jmax);
655 }
656
657 // j0 and j1 are constrained to only interior points
658 size_t j0 = std::max<size_t>(jmin, 1);
659 size_t j1 = std::min(jmax, m_points - 2);
660 for (size_t j = j0; j <= j1; j++) {
661 if (m_do_energy[j]) {
662 grad_hk(x, j);
663 double sum = 0.0;
664 for (size_t k = 0; k < m_nsp; k++) {
665 double flxk = 0.5*(m_flux(k,j-1) + m_flux(k,j));
666 sum += m_wdot(k,j)*m_hk(k,j);
667 sum += flxk * m_dhk_dz(k,j) / m_wt[k];
668 }
669
670 rsd[index(c_offset_T, j)] = - m_cp[j]*rho_u(x,j)*dTdz(x,j)
671 - divHeatFlux(x,j) - sum;
672 rsd[index(c_offset_T, j)] /= (m_rho[j]*m_cp[j]);
673 rsd[index(c_offset_T, j)] -= rdt*(T(x,j) - T_prev(j));
674 rsd[index(c_offset_T, j)] -= (m_qdotRadiation[j] / (m_rho[j] * m_cp[j]));
675 diag[index(c_offset_T, j)] = 1;
676 } else {
677 // residual equations if the energy equation is disabled
678 rsd[index(c_offset_T, j)] = T(x,j) - T_fixed(j);
679 diag[index(c_offset_T, j)] = 0;
680 }
681 }
682}
683
684void Flow1D::evalUo(double* x, double* rsd, int* diag,
685 double rdt, size_t jmin, size_t jmax)
686{
687 if (!m_twoPointControl) { // disable this equation
688 for (size_t j = jmin; j <= jmax; j++) {
689 rsd[index(c_offset_Uo, j)] = Uo(x, j);
690 diag[index(c_offset_Uo, j)] = 0;
691 }
692 return;
693 }
694
695 if (jmin == 0) { // left boundary
696 rsd[index(c_offset_Uo, jmin)] = Uo(x,jmin+1) - Uo(x,jmin);
697 diag[index(c_offset_Uo, jmin)] = 0;
698 }
699
700 if (jmax == m_points - 1) { // right boundary
702 rsd[index(c_offset_Uo, jmax)] = Uo(x,jmax) - Uo(x,jmax-1);
703 }
704 diag[index(c_offset_Uo, jmax)] = 0;
705 }
706
707 // j0 and j1 are constrained to only interior points
708 size_t j0 = std::max<size_t>(jmin, 1);
709 size_t j1 = std::min(jmax, m_points - 2);
710 for (size_t j = j0; j <= j1; j++) { // interior points
711 if (m_twoPointControl) {
712 if (grid(j) == m_zRight) {
713 rsd[index(c_offset_Uo, j)] = T(x,j) - m_tRight;
714 } else if (grid(j) > m_zRight) {
715 rsd[index(c_offset_Uo, j)] = Uo(x,j) - Uo(x,j-1);
716 } else if (grid(j) < m_zRight) {
717 rsd[index(c_offset_Uo, j)] = Uo(x,j+1) - Uo(x,j);
718 }
719 } else {
720 rsd[index(c_offset_Uo, j)] = Uo(x,j+1) - Uo(x,j);
721 }
722 diag[index(c_offset_Uo, j)] = 0;
723 }
724}
725
726void Flow1D::evalSpecies(double* x, double* rsd, int* diag,
727 double rdt, size_t jmin, size_t jmax)
728{
729 if (jmin == 0) { // left boundary
730 double sum = 0.0;
731 for (size_t k = 0; k < m_nsp; k++) {
732 sum += Y(x,k,jmin);
733 rsd[index(c_offset_Y + k, jmin)] = -(m_flux(k,jmin) +
734 rho_u(x,jmin) * Y(x,k,jmin));
735 }
736 rsd[index(c_offset_Y + leftExcessSpecies(), jmin)] = 1.0 - sum;
737 }
738
739 if (jmax == m_points - 1) { // right boundary
740 double sum = 0.0;
741 for (size_t k = 0; k < m_nsp; k++) {
742 sum += Y(x,k,jmax);
743 rsd[index(k+c_offset_Y,jmax)] = m_flux(k,jmax-1) +
744 rho_u(x,jmax)*Y(x,k,jmax);
745 }
746 rsd[index(c_offset_Y + rightExcessSpecies(), jmax)] = 1.0 - sum;
747 diag[index(c_offset_Y + rightExcessSpecies(), jmax)] = 0;
748 }
749
750 // j0 and j1 are constrained to only interior points
751 size_t j0 = std::max<size_t>(jmin, 1);
752 size_t j1 = std::min(jmax, m_points - 2);
753 for (size_t j = j0; j <= j1; j++) {
754 for (size_t k = 0; k < m_nsp; k++) {
755 double convec = rho_u(x,j)*dYdz(x,k,j);
756 double diffus = 2.0*(m_flux(k,j) - m_flux(k,j-1)) / (z(j+1) - z(j-1));
757 rsd[index(c_offset_Y + k, j)] = (m_wt[k]*m_wdot(k,j)
758 - convec - diffus)/m_rho[j]
759 - rdt*(Y(x,k,j) - Y_prev(k,j));
760 diag[index(c_offset_Y + k, j)] = 1;
761 }
762 }
763}
764
765void Flow1D::evalElectricField(double* x, double* rsd, int* diag,
766 double rdt, size_t jmin, size_t jmax)
767{
768 for (size_t j = jmin; j <= jmax; j++) {
769 // The same value is used for left/right/interior points
770 rsd[index(c_offset_E, j)] = x[index(c_offset_E, j)];
771 }
772}
773
774void Flow1D::evalContinuity(size_t j, double* x, double* rsd, int* diag, double rdt)
775{
776 throw CanteraError("Flow1D::evalContinuity",
777 "Overloaded by StFlow; to be removed after Cantera 3.1");
778}
779
780void Flow1D::show(const double* x)
781{
782 writelog(" Pressure: {:10.4g} Pa\n", m_press);
783
785
786 if (m_do_radiation) {
787 writeline('-', 79, false, true);
788 writelog("\n z radiative heat loss");
789 writeline('-', 79, false, true);
790 for (size_t j = 0; j < m_points; j++) {
791 writelog("\n {:10.4g} {:10.4g}", m_z[j], m_qdotRadiation[j]);
792 }
793 writelog("\n");
794 }
795}
796
797string Flow1D::componentName(size_t n) const
798{
799 switch (n) {
800 case c_offset_U:
801 return "velocity";
802 case c_offset_V:
803 return "spread_rate";
804 case c_offset_T:
805 return "T";
806 case c_offset_L:
807 return "lambda";
808 case c_offset_E:
809 return "eField";
810 case c_offset_Uo:
811 return "Uo";
812 default:
813 if (n >= c_offset_Y && n < (c_offset_Y + m_nsp)) {
814 return m_thermo->speciesName(n - c_offset_Y);
815 } else {
816 return "<unknown>";
817 }
818 }
819}
820
821size_t Flow1D::componentIndex(const string& name) const
822{
823 if (name=="velocity") {
824 return c_offset_U;
825 } else if (name=="spread_rate") {
826 return c_offset_V;
827 } else if (name=="T") {
828 return c_offset_T;
829 } else if (name=="lambda") {
830 return c_offset_L;
831 } else if (name == "eField") {
832 return c_offset_E;
833 } else if (name == "Uo") {
834 return c_offset_Uo;
835 } else {
836 for (size_t n=c_offset_Y; n<m_nsp+c_offset_Y; n++) {
837 if (componentName(n)==name) {
838 return n;
839 }
840 }
841 throw CanteraError("Flow1D1D::componentIndex",
842 "no component named " + name);
843 }
844}
845
846bool Flow1D::componentActive(size_t n) const
847{
848 switch (n) {
849 case c_offset_V: // spread_rate
850 return m_usesLambda;
851 case c_offset_L: // lambda
852 return m_usesLambda;
853 case c_offset_E: // eField
854 return false;
855 case c_offset_Uo: // oxidizer velocity for two-point control
856 return twoPointControlEnabled();
857 default:
858 return true;
859 }
860}
861
863{
864 AnyMap state = Domain1D::getMeta();
865 state["transport-model"] = m_trans->transportModel();
866
867 state["phase"]["name"] = m_thermo->name();
868 AnyValue source = m_thermo->input().getMetadata("filename");
869 state["phase"]["source"] = source.empty() ? "<unknown>" : source.asString();
870
871 state["radiation-enabled"] = m_do_radiation;
872 if (m_do_radiation) {
873 state["emissivity-left"] = m_epsilon_left;
874 state["emissivity-right"] = m_epsilon_right;
875 }
876
877 set<bool> energy_flags(m_do_energy.begin(), m_do_energy.end());
878 if (energy_flags.size() == 1) {
879 state["energy-enabled"] = m_do_energy[0];
880 } else {
881 state["energy-enabled"] = m_do_energy;
882 }
883
884 state["Soret-enabled"] = m_do_soret;
885
886 state["flux-gradient-basis"] = static_cast<long int>(m_fluxGradientBasis);
887
888 set<bool> species_flags(m_do_species.begin(), m_do_species.end());
889 if (species_flags.size() == 1) {
890 state["species-enabled"] = m_do_species[0];
891 } else {
892 for (size_t k = 0; k < m_nsp; k++) {
893 state["species-enabled"][m_thermo->speciesName(k)] = m_do_species[k];
894 }
895 }
896
897 state["refine-criteria"]["ratio"] = m_refiner->maxRatio();
898 state["refine-criteria"]["slope"] = m_refiner->maxDelta();
899 state["refine-criteria"]["curve"] = m_refiner->maxSlope();
900 state["refine-criteria"]["prune"] = m_refiner->prune();
901 state["refine-criteria"]["grid-min"] = m_refiner->gridMin();
902 state["refine-criteria"]["max-points"] =
903 static_cast<long int>(m_refiner->maxPoints());
904
905 if (m_zfixed != Undef) {
906 state["fixed-point"]["location"] = m_zfixed;
907 state["fixed-point"]["temperature"] = m_tfixed;
908 }
909
910 // Two-point control meta data
911 if (m_twoPointControl) {
912 state["continuation-method"]["type"] = "two-point";
913 state["continuation-method"]["left-location"] = m_zLeft;
914 state["continuation-method"]["right-location"] = m_zRight;
915 state["continuation-method"]["left-temperature"] = m_tLeft;
916 state["continuation-method"]["right-temperature"] = m_tRight;
917 }
918
919 return state;
920}
921
922shared_ptr<SolutionArray> Flow1D::asArray(const double* soln) const
923{
924 auto arr = SolutionArray::create(
925 m_solution, static_cast<int>(nPoints()), getMeta());
926 arr->addExtra("grid", false); // leading entry
927 AnyValue value;
928 value = m_z;
929 arr->setComponent("grid", value);
930 vector<double> data(nPoints());
931 for (size_t i = 0; i < nComponents(); i++) {
932 if (componentActive(i)) {
933 auto name = componentName(i);
934 for (size_t j = 0; j < nPoints(); j++) {
935 data[j] = soln[index(i, j)];
936 }
937 if (!arr->hasComponent(name)) {
938 arr->addExtra(name, componentIndex(name) > c_offset_Y);
939 }
940 value = data;
941 arr->setComponent(name, value);
942 }
943 }
944 value = m_rho;
945 arr->setComponent("D", value); // use density rather than pressure
946
947 if (m_do_radiation) {
948 arr->addExtra("radiative-heat-loss", true); // add at end
949 value = m_qdotRadiation;
950 arr->setComponent("radiative-heat-loss", value);
951 }
952
953 return arr;
954}
955
956void Flow1D::fromArray(SolutionArray& arr, double* soln)
957{
958 Domain1D::setMeta(arr.meta());
959 arr.setLoc(0);
960 auto phase = arr.thermo();
961 m_press = phase->pressure();
962
963 const auto grid = arr.getComponent("grid").as<vector<double>>();
964 setupGrid(nPoints(), &grid[0]);
965
966 for (size_t i = 0; i < nComponents(); i++) {
967 if (!componentActive(i)) {
968 continue;
969 }
970 string name = componentName(i);
971 if (arr.hasComponent(name)) {
972 const vector<double> data = arr.getComponent(name).as<vector<double>>();
973 for (size_t j = 0; j < nPoints(); j++) {
974 soln[index(i,j)] = data[j];
975 }
976 } else {
977 warn_user("Flow1D::fromArray", "Saved state does not contain values for "
978 "component '{}' in domain '{}'.", name, id());
979 }
980 }
981
982 updateProperties(npos, soln + loc(), 0, m_points - 1);
983 setMeta(arr.meta());
984}
985
986void Flow1D::setMeta(const AnyMap& state)
987{
988 if (state.hasKey("energy-enabled")) {
989 const AnyValue& ee = state["energy-enabled"];
990 if (ee.isScalar()) {
991 m_do_energy.assign(nPoints(), ee.asBool());
992 } else {
993 m_do_energy = ee.asVector<bool>(nPoints());
994 }
995 }
996
997 setTransportModel(state.getString("transport-model", "mixture-averaged"));
998
999 if (state.hasKey("Soret-enabled")) {
1000 m_do_soret = state["Soret-enabled"].asBool();
1001 }
1002
1003 if (state.hasKey("flux-gradient-basis")) {
1004 m_fluxGradientBasis = static_cast<ThermoBasis>(
1005 state["flux-gradient-basis"].asInt());
1006 }
1007
1008 if (state.hasKey("species-enabled")) {
1009 const AnyValue& se = state["species-enabled"];
1010 if (se.isScalar()) {
1011 m_do_species.assign(m_thermo->nSpecies(), se.asBool());
1012 } else {
1013 m_do_species = se.asVector<bool>(m_thermo->nSpecies());
1014 }
1015 }
1016
1017 if (state.hasKey("radiation-enabled")) {
1018 m_do_radiation = state["radiation-enabled"].asBool();
1019 if (m_do_radiation) {
1020 m_epsilon_left = state["emissivity-left"].asDouble();
1021 m_epsilon_right = state["emissivity-right"].asDouble();
1022 }
1023 }
1024
1025 if (state.hasKey("refine-criteria")) {
1026 const AnyMap& criteria = state["refine-criteria"].as<AnyMap>();
1027 double ratio = criteria.getDouble("ratio", m_refiner->maxRatio());
1028 double slope = criteria.getDouble("slope", m_refiner->maxDelta());
1029 double curve = criteria.getDouble("curve", m_refiner->maxSlope());
1030 double prune = criteria.getDouble("prune", m_refiner->prune());
1031 m_refiner->setCriteria(ratio, slope, curve, prune);
1032
1033 if (criteria.hasKey("grid-min")) {
1034 m_refiner->setGridMin(criteria["grid-min"].asDouble());
1035 }
1036 if (criteria.hasKey("max-points")) {
1037 m_refiner->setMaxPoints(criteria["max-points"].asInt());
1038 }
1039 }
1040
1041 if (state.hasKey("fixed-point")) {
1042 m_zfixed = state["fixed-point"]["location"].asDouble();
1043 m_tfixed = state["fixed-point"]["temperature"].asDouble();
1044 }
1045
1046 // Two-point control meta data
1047 if (state.hasKey("continuation-method")) {
1048 const AnyMap& cm = state["continuation-method"].as<AnyMap>();
1049 if (cm["type"] == "two-point") {
1050 m_twoPointControl = true;
1051 m_zLeft = cm["left-location"].asDouble();
1052 m_zRight = cm["right-location"].asDouble();
1053 m_tLeft = cm["left-temperature"].asDouble();
1054 m_tRight = cm["right-temperature"].asDouble();
1055 } else {
1056 warn_user("Flow1D::setMeta", "Unknown continuation method '{}'.",
1057 cm["type"].asString());
1058 }
1059 }
1060}
1061
1062void Flow1D::solveEnergyEqn(size_t j)
1063{
1064 bool changed = false;
1065 if (j == npos) {
1066 for (size_t i = 0; i < m_points; i++) {
1067 if (!m_do_energy[i]) {
1068 changed = true;
1069 }
1070 m_do_energy[i] = true;
1071 }
1072 } else {
1073 if (!m_do_energy[j]) {
1074 changed = true;
1075 }
1076 m_do_energy[j] = true;
1077 }
1078 m_refiner->setActive(c_offset_U, true);
1079 m_refiner->setActive(c_offset_V, true);
1080 m_refiner->setActive(c_offset_T, true);
1081 if (changed) {
1082 needJacUpdate();
1083 }
1084}
1085
1087{
1088 throw NotImplementedError("Flow1D::getSolvingStage",
1089 "Not used by '{}' objects.", type());
1090}
1091
1092void Flow1D::setSolvingStage(const size_t stage)
1093{
1094 throw NotImplementedError("Flow1D::setSolvingStage",
1095 "Not used by '{}' objects.", type());
1096}
1097
1099{
1100 throw NotImplementedError("Flow1D::solveElectricField",
1101 "Not used by '{}' objects.", type());
1102}
1103
1105{
1106 throw NotImplementedError("Flow1D::fixElectricField",
1107 "Not used by '{}' objects.", type());
1108}
1109
1110bool Flow1D::doElectricField(size_t j) const
1111{
1112 throw NotImplementedError("Flow1D::doElectricField",
1113 "Not used by '{}' objects.", type());
1114}
1115
1116void Flow1D::setBoundaryEmissivities(double e_left, double e_right)
1117{
1118 if (e_left < 0 || e_left > 1) {
1119 throw CanteraError("Flow1D::setBoundaryEmissivities",
1120 "The left boundary emissivity must be between 0.0 and 1.0!");
1121 } else if (e_right < 0 || e_right > 1) {
1122 throw CanteraError("Flow1D::setBoundaryEmissivities",
1123 "The right boundary emissivity must be between 0.0 and 1.0!");
1124 } else {
1125 m_epsilon_left = e_left;
1126 m_epsilon_right = e_right;
1127 }
1128}
1129
1130void Flow1D::fixTemperature(size_t j)
1131{
1132 bool changed = false;
1133 if (j == npos) {
1134 for (size_t i = 0; i < m_points; i++) {
1135 if (m_do_energy[i]) {
1136 changed = true;
1137 }
1138 m_do_energy[i] = false;
1139 }
1140 } else {
1141 if (m_do_energy[j]) {
1142 changed = true;
1143 }
1144 m_do_energy[j] = false;
1145 }
1146 m_refiner->setActive(c_offset_U, false);
1147 m_refiner->setActive(c_offset_V, false);
1148 m_refiner->setActive(c_offset_T, false);
1149 if (changed) {
1150 needJacUpdate();
1151 }
1152}
1153
1154void Flow1D::grad_hk(const double* x, size_t j)
1155{
1156 for(size_t k = 0; k < m_nsp; k++) {
1157 if (u(x, j) > 0.0) {
1158 m_dhk_dz(k,j) = (m_hk(k,j) - m_hk(k,j-1)) / m_dz[j - 1];
1159 }
1160 else {
1161 m_dhk_dz(k,j) = (m_hk(k,j+1) - m_hk(k,j)) / m_dz[j];
1162 }
1163 }
1164}
1165
1166// Two-point control functions
1168{
1169 if (m_twoPointControl) {
1170 if (m_zLeft != Undef) {
1171 return m_tLeft;
1172 } else {
1173 throw CanteraError("Flow1D::leftControlPointTemperature",
1174 "Invalid operation: left control point location is not set");
1175 }
1176 } else {
1177 throw CanteraError("Flow1D::leftControlPointTemperature",
1178 "Invalid operation: two-point control is not enabled.");
1179 }
1180}
1181
1183{
1184 if (m_twoPointControl) {
1185 if (m_zLeft != Undef) {
1186 return m_zLeft;
1187 } else {
1188 throw CanteraError("Flow1D::leftControlPointCoordinate",
1189 "Invalid operation: left control point location is not set");
1190 }
1191 } else {
1192 throw CanteraError("Flow1D::leftControlPointCoordinate",
1193 "Invalid operation: two-point control is not enabled.");
1194 }
1195}
1196
1198{
1199 if (m_twoPointControl) {
1200 if (m_zLeft != Undef) {
1201 m_tLeft = temperature;
1202 } else {
1203 throw CanteraError("Flow1D::setLeftControlPointTemperature",
1204 "Invalid operation: left control point location is not set");
1205 }
1206 } else {
1207 throw CanteraError("Flow1D::setLeftControlPointTemperature",
1208 "Invalid operation: two-point control is not enabled.");
1209 }
1210}
1211
1213{
1214 if (m_twoPointControl) {
1215 m_zLeft = z_left;
1216 } else {
1217 throw CanteraError("Flow1D::setLeftControlPointCoordinate",
1218 "Invalid operation: two-point control is not enabled.");
1219 }
1220}
1221
1223{
1224 if (m_twoPointControl) {
1225 if (m_zRight != Undef) {
1226 return m_tRight;
1227 } else {
1228 throw CanteraError("Flow1D::rightControlPointTemperature",
1229 "Invalid operation: right control point location is not set");
1230 }
1231 } else {
1232 throw CanteraError("Flow1D::rightControlPointTemperature",
1233 "Invalid operation: two-point control is not enabled.");
1234 }
1235}
1236
1238{
1239 if (m_twoPointControl) {
1240 if (m_zRight != Undef) {
1241 return m_zRight;
1242 } else {
1243 throw CanteraError("Flow1D::rightControlPointCoordinate",
1244 "Invalid operation: right control point location is not set");
1245 }
1246 } else {
1247 throw CanteraError("Flow1D::rightControlPointCoordinate",
1248 "Invalid operation: two-point control is not enabled.");
1249 }
1250}
1251
1253{
1254 if (m_twoPointControl) {
1255 if (m_zRight != Undef) {
1256 m_tRight = temperature;
1257 } else {
1258 throw CanteraError("Flow1D::setRightControlPointTemperature",
1259 "Invalid operation: right control point location is not set");
1260 }
1261 } else {
1262 throw CanteraError("Flow1D::setRightControlPointTemperature",
1263 "Invalid operation: two-point control is not enabled.");
1264 }
1265}
1266
1268{
1269 if (m_twoPointControl) {
1270 m_zRight = z_right;
1271 } else {
1272 throw CanteraError("Flow1D::setRightControlPointCoordinate",
1273 "Invalid operation: two-point control is not enabled.");
1274 }
1275}
1276
1277void Flow1D::enableTwoPointControl(bool twoPointControl)
1278{
1279 if (isStrained()) {
1280 m_twoPointControl = twoPointControl;
1281 } else {
1282 throw CanteraError("Flow1D::enableTwoPointControl",
1283 "Invalid operation: two-point control can only be used"
1284 "with axisymmetric flames.");
1285 }
1286}
1287
1288} // namespace
Header file defining class TransportFactory (see TransportFactory)
Headers for the Transport object, which is the virtual base class for all transport property evaluato...
const AnyValue & getMetadata(const string &key) const
Get a value from the metadata applicable to the AnyMap tree containing this node.
Definition AnyMap.cpp:580
A map of string keys to values whose type can vary at runtime.
Definition AnyMap.h:427
double getDouble(const string &key, double default_) const
If key exists, return it as a double, otherwise return default_.
Definition AnyMap.cpp:1520
bool hasKey(const string &key) const
Returns true if the map contains an item named key.
Definition AnyMap.cpp:1423
const string & getString(const string &key, const string &default_) const
If key exists, return it as a string, otherwise return default_.
Definition AnyMap.cpp:1530
A wrapper for a variable whose type is determined at runtime.
Definition AnyMap.h:86
const string & asString() const
Return the held value, if it is a string.
Definition AnyMap.cpp:739
bool & asBool()
Return the held value, if it is a bool.
Definition AnyMap.cpp:871
bool empty() const
Return boolean indicating whether AnyValue is empty.
Definition AnyMap.cpp:647
bool isScalar() const
Returns true if the held value is a scalar type (such as double, long int, string,...
Definition AnyMap.cpp:651
const vector< T > & asVector(size_t nMin=npos, size_t nMax=npos) const
Return the held value, if it is a vector of type T.
Definition AnyMap.inl.h:109
const T & as() const
Get the value of this key as the specified type.
Definition AnyMap.inl.h:16
double * ptrColumn(size_t j)
Return a pointer to the top of column j, columns are contiguous in memory.
Definition Array.h:203
virtual void resize(size_t n, size_t m, double v=0.0)
Resize the array, and fill the new entries with 'v'.
Definition Array.cpp:47
Base class for exceptions thrown by Cantera classes.
Base class for one-dimensional domains.
Definition Domain1D.h:28
size_t lastPoint() const
The index of the last (that is, right-most) grid point belonging to this domain.
Definition Domain1D.h:398
shared_ptr< Solution > m_solution
Composite thermo/kinetics/transport handler.
Definition Domain1D.h:565
size_t nComponents() const
Number of components at each grid point.
Definition Domain1D.h:143
virtual void setMeta(const AnyMap &meta)
Retrieve meta data.
Definition Domain1D.cpp:173
size_t nPoints() const
Number of grid points in this domain.
Definition Domain1D.h:165
virtual void resize(size_t nv, size_t np)
Resize the domain to have nv components and np grid points.
Definition Domain1D.cpp:44
void setSolution(shared_ptr< Solution > sol)
Set the solution manager.
Definition Domain1D.cpp:31
size_t m_points
Number of grid points.
Definition Domain1D.h:535
string m_id
Identity tag for the domain.
Definition Domain1D.h:558
string type() const
String indicating the domain implemented.
Definition Domain1D.h:49
size_t firstPoint() const
The index of the first (that is, left-most) grid point belonging to this domain.
Definition Domain1D.h:390
void needJacUpdate()
Set this if something has changed in the governing equations (for example, the value of a constant ha...
Definition Domain1D.cpp:113
virtual size_t loc(size_t j=0) const
Location of the start of the local solution vector in the global solution vector,.
Definition Domain1D.h:382
virtual AnyMap getMeta() const
Retrieve meta data.
Definition Domain1D.cpp:121
virtual void show(std::ostream &s, const double *x)
Print the solution.
Definition Domain1D.h:457
This class represents 1D flow domains that satisfy the one-dimensional similarity solution for chemic...
Definition Flow1D.h:46
void setLeftControlPointTemperature(double temperature)
Sets the temperature of the left control point.
Definition Flow1D.cpp:1197
void eval(size_t jGlobal, double *xGlobal, double *rsdGlobal, integer *diagGlobal, double rdt) override
Evaluate the residual functions for axisymmetric stagnation flow.
Definition Flow1D.cpp:308
void setLeftControlPointCoordinate(double z_left)
Sets the coordinate of the left control point.
Definition Flow1D.cpp:1212
size_t m_kExcessLeft
Index of species with a large mass fraction at each boundary, for which the mass fraction may be calc...
Definition Flow1D.h:781
void setMeta(const AnyMap &state) override
Retrieve meta data.
Definition Flow1D.cpp:986
double m_zLeft
Location of the left control point when two-point control is enabled.
Definition Flow1D.h:792
void setTransportModel(const string &trans)
Set the transport model.
Definition Flow1D.cpp:213
void setRightControlPointCoordinate(double z_right)
Sets the coordinate of the right control point.
Definition Flow1D.cpp:1267
void setTransport(shared_ptr< Transport > trans) override
Set transport model to existing instance.
Definition Flow1D.cpp:142
void setKinetics(shared_ptr< Kinetics > kin) override
Set the kinetics manager.
Definition Flow1D.cpp:136
void resetBadValues(double *xg) override
When called, this function should reset "bad" values in the state vector such as negative species con...
Definition Flow1D.cpp:203
bool twoPointControlEnabled() const
Returns the status of the two-point control.
Definition Flow1D.h:313
size_t rightExcessSpecies() const
Index of the species on the right boundary with the largest mass fraction.
Definition Flow1D.h:390
vector< double > m_qdotRadiation
radiative heat loss vector
Definition Flow1D.h:771
virtual void evalMomentum(double *x, double *rsd, int *diag, double rdt, size_t jmin, size_t jmax)
Evaluate the momentum equation residual.
Definition Flow1D.cpp:571
void updateThermo(const double *x, size_t j0, size_t j1)
Update the thermodynamic properties from point j0 to point j1 (inclusive), based on solution x.
Definition Flow1D.h:417
double m_tLeft
Temperature of the left control point when two-point control is enabled.
Definition Flow1D.h:795
void setRightControlPointTemperature(double temperature)
Sets the temperature of the right control point.
Definition Flow1D.cpp:1252
void resize(size_t components, size_t points) override
Change the grid size. Called after grid refinement.
Definition Flow1D.cpp:162
virtual void evalContinuity(double *x, double *rsd, int *diag, double rdt, size_t jmin, size_t jmax)
Evaluate the continuity equation residual.
Definition Flow1D.cpp:512
vector< double > m_cp
Vector of size m_nsp to cache specific heat capacities.
Definition Flow1D.h:725
void enableTwoPointControl(bool twoPointControl)
Sets the status of the two-point control.
Definition Flow1D.cpp:1277
double m_tRight
Temperature of the right control point when two-point control is enabled.
Definition Flow1D.h:801
void setBoundaryEmissivities(double e_left, double e_right)
Set the emissivities for the boundary values.
Definition Flow1D.cpp:1116
void setFluxGradientBasis(ThermoBasis fluxGradientBasis)
Compute species diffusive fluxes with respect to their mass fraction gradients (fluxGradientBasis = T...
Definition Flow1D.cpp:222
virtual void evalEnergy(double *x, double *rsd, int *diag, double rdt, size_t jmin, size_t jmax)
Evaluate the energy equation residual.
Definition Flow1D.cpp:646
vector< double > m_rho
Vector of size m_nsp to cache densities.
Definition Flow1D.h:720
Flow1D(ThermoPhase *ph=0, size_t nsp=1, size_t points=1)
Create a new flow domain.
Definition Flow1D.cpp:19
vector< double > m_diff
Array of size m_nsp by m_points for saving density times diffusion coefficient times species molar ma...
Definition Flow1D.h:732
shared_ptr< SolutionArray > asArray(const double *soln) const override
Save the state of this domain as a SolutionArray.
Definition Flow1D.cpp:922
size_t componentIndex(const string &name) const override
index of component with name name.
Definition Flow1D.cpp:821
void setGas(const double *x, size_t j)
Set the gas object state to be consistent with the solution at point j.
Definition Flow1D.cpp:241
ThermoBasis fluxGradientBasis() const
Compute species diffusive fluxes with respect to their mass fraction gradients (fluxGradientBasis = T...
Definition Flow1D.h:124
double Uo(const double *x, size_t j) const
Solution component for oxidizer velocity,.
Definition Flow1D.h:646
double m_tfixed
Temperature at the point used to fix the flame location.
Definition Flow1D.h:808
virtual bool componentActive(size_t n) const
Returns true if the specified component is an active part of the solver state.
Definition Flow1D.cpp:846
Array2D m_wdot
Array of size m_nsp by m_points for saving species production rates.
Definition Flow1D.h:744
Array2D m_hk
Array of size m_nsp by m_points for saving molar enthalpies.
Definition Flow1D.h:738
virtual bool doElectricField(size_t j) const
Retrieve flag indicating whether electric field is solved or not (used by IonFlow specialization)
Definition Flow1D.cpp:1110
virtual void evalSpecies(double *x, double *rsd, int *diag, double rdt, size_t jmin, size_t jmax)
Evaluate the species equations' residuals.
Definition Flow1D.cpp:726
void setupGrid(size_t n, const double *z) override
called to set up initial grid, and after grid refinement
Definition Flow1D.cpp:188
size_t leftExcessSpecies() const
Index of the species on the left boundary with the largest mass fraction.
Definition Flow1D.h:385
Array2D m_dhk_dz
Array of size m_nsp by m_points-1 for saving enthalpy fluxes.
Definition Flow1D.h:741
virtual void evalElectricField(double *x, double *rsd, int *diag, double rdt, size_t jmin, size_t jmax)
Evaluate the electric field equation residual to be zero everywhere.
Definition Flow1D.cpp:765
vector< double > m_wtm
Vector of size m_nsp to cache mean molecular weights.
Definition Flow1D.h:721
bool m_twoPointControl
Flag for activating two-point flame control.
Definition Flow1D.h:789
double m_zfixed
Location of the point where temperature is fixed.
Definition Flow1D.h:805
void _finalize(const double *x) override
In some cases, a domain may need to set parameters that depend on the initial solution estimate.
Definition Flow1D.cpp:261
virtual size_t getSolvingStage() const
Get the solving stage (used by IonFlow specialization)
Definition Flow1D.cpp:1086
size_t m_nsp
Number of species in the mechanism.
Definition Flow1D.h:746
virtual void evalLambda(double *x, double *rsd, int *diag, double rdt, size_t jmin, size_t jmax)
Evaluate the lambda equation residual.
Definition Flow1D.cpp:603
void fromArray(SolutionArray &arr, double *soln) override
Restore the solution for this domain from a SolutionArray.
Definition Flow1D.cpp:956
double leftControlPointCoordinate() const
Returns the z-coordinate of the left control point.
Definition Flow1D.cpp:1182
AnyMap getMeta() const override
Retrieve meta data.
Definition Flow1D.cpp:862
virtual void updateDiffFluxes(const double *x, size_t j0, size_t j1)
Update the diffusive mass fluxes.
Definition Flow1D.cpp:419
double leftControlPointTemperature() const
Returns the temperature at the left control point.
Definition Flow1D.cpp:1167
string componentName(size_t n) const override
Name of the nth component. May be overloaded.
Definition Flow1D.cpp:797
void setGasAtMidpoint(const double *x, size_t j)
Set the gas state to be consistent with the solution at the midpoint between j and j + 1.
Definition Flow1D.cpp:249
virtual void grad_hk(const double *x, size_t j)
Get the gradient of species specific molar enthalpies.
Definition Flow1D.cpp:1154
bool isStrained() const
Retrieve flag indicating whether flow uses radial momentum.
Definition Flow1D.h:353
string transportModel() const
Retrieve transport model.
Definition Flow1D.cpp:218
double rightControlPointCoordinate() const
Returns the z-coordinate of the right control point.
Definition Flow1D.cpp:1237
void computeRadiation(double *x, size_t jmin, size_t jmax)
Computes the radiative heat loss vector over points jmin to jmax and stores the data in the qdotRadia...
Definition Flow1D.cpp:465
virtual void updateProperties(size_t jg, double *x, size_t jmin, size_t jmax)
Update the properties (thermo, transport, and diffusion flux).
Definition Flow1D.cpp:347
virtual void evalUo(double *x, double *rsd, int *diag, double rdt, size_t jmin, size_t jmax)
Evaluate the oxidizer axial velocity equation residual.
Definition Flow1D.cpp:684
string domainType() const override
Domain type flag.
Definition Flow1D.cpp:126
void show(const double *x) override
Print the solution.
Definition Flow1D.cpp:780
virtual void setSolvingStage(const size_t stage)
Solving stage mode for handling ionized species (used by IonFlow specialization)
Definition Flow1D.cpp:1092
void setPressure(double p)
Set the pressure.
Definition Flow1D.h:130
virtual void fixElectricField(size_t j=npos)
Set to fix voltage in a point (used by IonFlow specialization)
Definition Flow1D.cpp:1104
virtual void updateTransport(double *x, size_t j0, size_t j1)
Update the transport properties at grid points in the range from j0 to j1, based on solution x.
Definition Flow1D.cpp:371
double m_zRight
Location of the right control point when two-point control is enabled.
Definition Flow1D.h:798
virtual void solveElectricField(size_t j=npos)
Set to solve electric field in a point (used by IonFlow specialization)
Definition Flow1D.cpp:1098
void _getInitialSoln(double *x) override
Write the initial solution estimate into array x.
Definition Flow1D.cpp:232
vector< size_t > m_kRadiating
Indices within the ThermoPhase of the radiating species.
Definition Flow1D.h:758
double rightControlPointTemperature() const
Returns the temperature at the right control point.
Definition Flow1D.cpp:1222
double T_fixed(size_t j) const
The fixed temperature value at point j.
Definition Flow1D.h:162
bool m_do_radiation
flag for the radiative heat loss
Definition Flow1D.h:768
An error indicating that an unimplemented function has been called.
size_t nSpecies() const
Returns the number of species in the phase.
Definition Phase.h:231
virtual void setMassFractions_NoNorm(const double *const y)
Set the mass fractions to the specified values without normalizing.
Definition Phase.cpp:355
double temperature() const
Temperature (K).
Definition Phase.h:562
virtual void setPressure(double p)
Set the internally stored pressure (Pa) at constant temperature and composition.
Definition Phase.h:616
double meanMolecularWeight() const
The mean molecular weight. Units: (kg/kmol)
Definition Phase.h:655
string speciesName(size_t k) const
Name of the species with index k.
Definition Phase.cpp:142
const vector< double > & molecularWeights() const
Return a const reference to the internal vector of molecular weights.
Definition Phase.cpp:395
size_t speciesIndex(const string &name) const
Returns the index of a species named 'name' within the Phase object.
Definition Phase.cpp:129
virtual double density() const
Density (kg/m^3).
Definition Phase.h:587
virtual void setTemperature(double temp)
Set the internally stored temperature of the phase (K).
Definition Phase.h:623
virtual void setMassFractions(const double *const y)
Set the mass fractions to the specified values and normalize them.
Definition Phase.cpp:341
void getMassFractions(double *const y) const
Get the species mass fractions.
Definition Phase.cpp:471
virtual double pressure() const
Return the thermodynamic pressure (Pa).
Definition Phase.h:580
string name() const
Return the name of the phase.
Definition Phase.cpp:20
A container class holding arrays of state information.
void setLoc(int loc, bool restore=true)
Update the buffered location used to access SolutionArray entries.
AnyValue getComponent(const string &name) const
Retrieve a component of the SolutionArray by name.
bool hasComponent(const string &name) const
Check whether SolutionArray contains a component.
AnyMap & meta()
SolutionArray meta data.
shared_ptr< ThermoPhase > thermo()
Retrieve associated ThermoPhase object.
static shared_ptr< SolutionArray > create(const shared_ptr< Solution > &sol, int size=0, const AnyMap &meta={})
Instantiate a new SolutionArray reference.
static shared_ptr< Solution > create()
Create an empty Solution object.
Definition Solution.h:54
Base class for a phase with thermodynamic properties.
virtual double maxTemp(size_t k=npos) const
Maximum temperature for which the thermodynamic data for the species are valid.
const AnyMap & input() const
Access input data associated with the phase description.
virtual void getThermalDiffCoeffs(double *const dt)
Return a vector of Thermal diffusion coefficients [kg/m/sec].
Definition Transport.h:271
virtual string transportModel() const
Identifies the model represented by this Transport object.
Definition Transport.h:93
virtual void getMixDiffCoeffs(double *const d)
Returns a vector of mixture averaged diffusion coefficients.
Definition Transport.h:315
virtual double thermalConductivity()
Returns the mixture thermal conductivity in W/m/K.
Definition Transport.h:155
virtual void getMixDiffCoeffsMass(double *const d)
Returns a vector of mixture averaged diffusion coefficients.
Definition Transport.h:327
virtual double viscosity()
The viscosity in Pa-s.
Definition Transport.h:122
virtual void getMultiDiffCoeffs(const size_t ld, double *const d)
Return the Multicomponent diffusion coefficients. Units: [m^2/s].
Definition Transport.h:300
Header for a file containing miscellaneous numerical functions.
This file contains definitions for utility functions and text for modules, inputfiles and logging,...
void writelog(const string &fmt, const Args &... args)
Write a formatted message to the screen.
Definition global.h:175
double linearInterp(double x, const vector< double > &xpts, const vector< double > &fpts)
Linearly interpolate a function defined on a discrete grid.
Definition funcs.cpp:13
const double OneAtm
One atmosphere [Pa].
Definition ct_defs.h:96
const double StefanBoltz
Stefan-Boltzmann constant [W/m2/K4].
Definition ct_defs.h:128
void warn_user(const string &method, const string &msg, const Args &... args)
Print a user warning raised from method as CanteraWarning.
Definition global.h:267
Namespace for the Cantera kernel.
Definition AnyMap.cpp:564
const size_t npos
index returned by functions to indicate "no position"
Definition ct_defs.h:180
const double Undef
Fairly random number to be used to initialize variables against to see if they are subsequently defin...
Definition ct_defs.h:164
@ c_offset_U
axial velocity [m/s]
Definition Flow1D.h:25
@ c_offset_L
(1/r)dP/dr
Definition Flow1D.h:28
@ c_offset_V
strain rate
Definition Flow1D.h:26
@ c_offset_E
electric field
Definition Flow1D.h:29
@ c_offset_Y
mass fractions
Definition Flow1D.h:31
@ c_offset_Uo
oxidizer axial velocity [m/s]
Definition Flow1D.h:30
@ c_offset_T
temperature [kelvin]
Definition Flow1D.h:27
ThermoBasis
Differentiate between mole fractions and mass fractions for input mixture composition.