1 //! @file StFlow.h
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 
6 #ifndef CT_STFLOW_H
7 #define CT_STFLOW_H
8 
9 #include "Domain1D.h"
10 #include "cantera/base/Array.h"
11 #include "cantera/thermo/IdealGasPhase.h"
12 #include "cantera/kinetics/Kinetics.h"
13 
14 namespace Cantera
15 {
16 
17 //------------------------------------------
18 //   constants
19 //------------------------------------------
20 
21 // Offsets of solution components in the solution array.
22 const size_t c_offset_U = 0; // axial velocity
23 const size_t c_offset_V = 1; // strain rate
24 const size_t c_offset_T = 2; // temperature
25 const size_t c_offset_L = 3; // (1/r)dP/dr
26 const size_t c_offset_E = 4; // electric poisson's equation
27 const size_t c_offset_Y = 5; // mass fractions
28 
29 class Transport;
30 
31 /**
32  *  This class represents 1D flow domains that satisfy the one-dimensional
33  *  similarity solution for chemically-reacting, axisymmetric flows.
34  *  @ingroup onedim
35  */
36 class StFlow : public Domain1D
37 {
38 public:
39     //--------------------------------
40     // construction and destruction
41     //--------------------------------
42 
43     //! Create a new flow domain.
44     //! @param ph Object representing the gas phase. This object will be used
45     //!     to evaluate all thermodynamic, kinetic, and transport properties.
46     //! @param nsp Number of species.
47     //! @param points Initial number of grid points
48     StFlow(ThermoPhase* ph = 0, size_t nsp = 1, size_t points = 1);
49 
50     //! Delegating constructor
51     StFlow(shared_ptr<ThermoPhase> th, size_t nsp = 1, size_t points = 1) :
52         StFlow(th.get(), nsp, points) {
53     }
54 
55     //! @name Problem Specification
56     //! @{
57 
58     virtual void setupGrid(size_t n, const doublereal* z);
59 
60     virtual void resetBadValues(double* xg);
61 
62 
phase()63     ThermoPhase& phase() {
64         return *m_thermo;
65     }
kinetics()66     Kinetics& kinetics() {
67         return *m_kin;
68     }
69 
70     /**
71      * Set the thermo manager. Note that the flow equations assume
72      * the ideal gas equation.
73      */
setThermo(IdealGasPhase & th)74     void setThermo(IdealGasPhase& th) {
75         m_thermo = &th;
76     }
77 
78     //! Set the kinetics manager. The kinetics manager must
setKinetics(Kinetics & kin)79     void setKinetics(Kinetics& kin) {
80         m_kin = &kin;
81     }
82 
83     //! set the transport manager
84     void setTransport(Transport& trans);
85 
86     //! Enable thermal diffusion, also known as Soret diffusion.
87     //! Requires that multicomponent transport properties be
88     //! enabled to carry out calculations.
enableSoret(bool withSoret)89     void enableSoret(bool withSoret) {
90         m_do_soret = withSoret;
91     }
withSoret()92     bool withSoret() const {
93         return m_do_soret;
94     }
95 
96     //! Set the pressure. Since the flow equations are for the limit of small
97     //! Mach number, the pressure is very nearly constant throughout the flow.
setPressure(doublereal p)98     void setPressure(doublereal p) {
99         m_press = p;
100     }
101 
102     //! The current pressure [Pa].
pressure()103     doublereal pressure() const {
104         return m_press;
105     }
106 
107     //! Write the initial solution estimate into array x.
108     virtual void _getInitialSoln(double* x);
109 
110     virtual void _finalize(const doublereal* x);
111 
112     //! Sometimes it is desired to carry out the simulation using a specified
113     //! temperature profile, rather than computing it by solving the energy
114     //! equation. This method specifies this profile.
setFixedTempProfile(vector_fp & zfixed,vector_fp & tfixed)115     void setFixedTempProfile(vector_fp& zfixed, vector_fp& tfixed) {
116         m_zfix = zfixed;
117         m_tfix = tfixed;
118     }
119 
120     /*!
121      * Set the temperature fixed point at grid point j, and disable the energy
122      * equation so that the solution will be held to this value.
123      */
setTemperature(size_t j,doublereal t)124     void setTemperature(size_t j, doublereal t) {
125         m_fixedtemp[j] = t;
126         m_do_energy[j] = false;
127     }
128 
129     //! The fixed temperature value at point j.
T_fixed(size_t j)130     doublereal T_fixed(size_t j) const {
131         return m_fixedtemp[j];
132     }
133 
134     // @}
135 
136     virtual std::string componentName(size_t n) const;
137 
138     virtual size_t componentIndex(const std::string& name) const;
139 
140     //! Print the solution.
141     virtual void showSolution(const doublereal* x);
142 
143     //! Save the current solution for this domain into an XML_Node
144     /*!
145      *  @param o    XML_Node to save the solution to.
146      *  @param sol  Current value of the solution vector. The object will pick
147      *              out which part of the solution vector pertains to this
148      *              object.
149      *
150      * @deprecated The XML output format is deprecated and will be removed in
151      *     Cantera 3.0.
152      */
153     virtual XML_Node& save(XML_Node& o, const doublereal* const sol);
154 
155     virtual void restore(const XML_Node& dom, doublereal* soln,
156                          int loglevel);
157 
158     //! Set flow configuration for freely-propagating flames, using an internal
159     //! point with a fixed temperature as the condition to determine the inlet
160     //! mass flux.
setFreeFlow()161     void setFreeFlow() {
162         m_type = cFreeFlow;
163         m_dovisc = false;
164     }
165 
166     //! Set flow configuration for axisymmetric counterflow or burner-stabilized
167     //! flames, using specified inlet mass fluxes.
setAxisymmetricFlow()168     void setAxisymmetricFlow() {
169         m_type = cAxisymmetricStagnationFlow;
170         m_dovisc = true;
171     }
172 
173     //! Return the type of flow domain being represented, either "Free Flame" or
174     //! "Axisymmetric Stagnation".
175     //! @see setFreeFlow setAxisymmetricFlow
flowType()176     virtual std::string flowType() {
177         if (m_type == cFreeFlow) {
178             return "Free Flame";
179         } else if (m_type == cAxisymmetricStagnationFlow) {
180             return "Axisymmetric Stagnation";
181         } else {
182             throw CanteraError("StFlow::flowType", "Unknown value for 'm_type'");
183         }
184     }
185 
186     void solveEnergyEqn(size_t j=npos);
187 
188     //! Turn radiation on / off.
189     /*!
190      *  The simple radiation model used was established by Y. Liu and B. Rogg
191      *  [Y. Liu and B. Rogg, Modelling of thermally radiating diffusion flames
192      *  with detailed chemistry and transport, EUROTHERM Seminars, 17:114-127,
193      *  1991]. This model considers the radiation of CO2 and H2O.
194      */
enableRadiation(bool doRadiation)195     void enableRadiation(bool doRadiation) {
196         m_do_radiation = doRadiation;
197     }
198 
199     //! Returns `true` if the radiation term in the energy equation is enabled
radiationEnabled()200     bool radiationEnabled() const {
201         return m_do_radiation;
202     }
203 
204     //! Return radiative heat loss at grid point j
radiativeHeatLoss(size_t j)205     double radiativeHeatLoss(size_t j) const {
206         return m_qdotRadiation[j];
207     }
208 
209     //! Set the emissivities for the boundary values
210     /*!
211      * Reads the emissivities for the left and right boundary values in the
212      * radiative term and writes them into the variables, which are used for the
213      * calculation.
214      */
215     void setBoundaryEmissivities(double e_left, double e_right);
216 
217     //! Return emissivitiy at left boundary
leftEmissivity()218     double leftEmissivity() const { return m_epsilon_left; }
219 
220     //! Return emissivitiy at right boundary
rightEmissivity()221     double rightEmissivity() const { return m_epsilon_right; }
222 
223     void fixTemperature(size_t j=npos);
224 
doEnergy(size_t j)225     bool doEnergy(size_t j) {
226         return m_do_energy[j];
227     }
228 
229     //! Change the grid size. Called after grid refinement.
230     virtual void resize(size_t components, size_t points);
231 
232     //! Set the gas object state to be consistent with the solution at point j.
233     void setGas(const doublereal* x, size_t j);
234 
235     //! Set the gas state to be consistent with the solution at the midpoint
236     //! between j and j + 1.
237     void setGasAtMidpoint(const doublereal* x, size_t j);
238 
density(size_t j)239     doublereal density(size_t j) const {
240         return m_rho[j];
241     }
242 
fixed_mdot()243     virtual bool fixed_mdot() {
244         return (domainType() != cFreeFlow);
245     }
setViscosityFlag(bool dovisc)246     void setViscosityFlag(bool dovisc) {
247         m_dovisc = dovisc;
248     }
249 
250     /*!
251      *  Evaluate the residual function for axisymmetric stagnation flow. If
252      *  j == npos, the residual function is evaluated at all grid points.
253      *  Otherwise, the residual function is only evaluated at grid points
254      *  j-1, j, and j+1. This option is used to efficiently evaluate the
255      *  Jacobian numerically.
256      */
257     virtual void eval(size_t j, doublereal* x, doublereal* r,
258                       integer* mask, doublereal rdt);
259 
260     //! Evaluate all residual components at the right boundary.
261     virtual void evalRightBoundary(double* x, double* res, int* diag,
262                                    double rdt);
263 
264     //! Evaluate the residual corresponding to the continuity equation at all
265     //! interior grid points.
266     virtual void evalContinuity(size_t j, double* x, double* r,
267                                 int* diag, double rdt);
268 
269     //! Index of the species on the left boundary with the largest mass fraction
leftExcessSpecies()270     size_t leftExcessSpecies() const {
271         return m_kExcessLeft;
272     }
273 
274     //! Index of the species on the right boundary with the largest mass fraction
rightExcessSpecies()275     size_t rightExcessSpecies() const {
276         return m_kExcessRight;
277     }
278 
279 protected:
wdot(size_t k,size_t j)280     doublereal wdot(size_t k, size_t j) const {
281         return m_wdot(k,j);
282     }
283 
284     //! Write the net production rates at point `j` into array `m_wdot`
getWdot(doublereal * x,size_t j)285     void getWdot(doublereal* x, size_t j) {
286         setGas(x,j);
287         m_kin->getNetProductionRates(&m_wdot(0,j));
288     }
289 
290     //! Update the properties (thermo, transport, and diffusion flux).
291     //! This function is called in eval after the points which need
292     //! to be updated are defined.
293     virtual void updateProperties(size_t jg, double* x, size_t jmin, size_t jmax);
294 
295     //! Evaluate the residual function. This function is called in eval
296     //! after updateProperties is called.
297     virtual void evalResidual(double* x, double* rsd, int* diag,
298                               double rdt, size_t jmin, size_t jmax);
299 
300     /**
301      * Update the thermodynamic properties from point j0 to point j1
302      * (inclusive), based on solution x.
303      */
updateThermo(const doublereal * x,size_t j0,size_t j1)304     void updateThermo(const doublereal* x, size_t j0, size_t j1) {
305         for (size_t j = j0; j <= j1; j++) {
306             setGas(x,j);
307             m_rho[j] = m_thermo->density();
308             m_wtm[j] = m_thermo->meanMolecularWeight();
309             m_cp[j] = m_thermo->cp_mass();
310         }
311     }
312 
313     //! @name Solution components
314     //! @{
315 
T(const doublereal * x,size_t j)316     doublereal T(const doublereal* x, size_t j) const {
317         return x[index(c_offset_T, j)];
318     }
T(doublereal * x,size_t j)319     doublereal& T(doublereal* x, size_t j) {
320         return x[index(c_offset_T, j)];
321     }
T_prev(size_t j)322     doublereal T_prev(size_t j) const {
323         return prevSoln(c_offset_T, j);
324     }
325 
rho_u(const doublereal * x,size_t j)326     doublereal rho_u(const doublereal* x, size_t j) const {
327         return m_rho[j]*x[index(c_offset_U, j)];
328     }
329 
u(const doublereal * x,size_t j)330     doublereal u(const doublereal* x, size_t j) const {
331         return x[index(c_offset_U, j)];
332     }
333 
V(const doublereal * x,size_t j)334     doublereal V(const doublereal* x, size_t j) const {
335         return x[index(c_offset_V, j)];
336     }
V_prev(size_t j)337     doublereal V_prev(size_t j) const {
338         return prevSoln(c_offset_V, j);
339     }
340 
lambda(const doublereal * x,size_t j)341     doublereal lambda(const doublereal* x, size_t j) const {
342         return x[index(c_offset_L, j)];
343     }
344 
Y(const doublereal * x,size_t k,size_t j)345     doublereal Y(const doublereal* x, size_t k, size_t j) const {
346         return x[index(c_offset_Y + k, j)];
347     }
348 
Y(doublereal * x,size_t k,size_t j)349     doublereal& Y(doublereal* x, size_t k, size_t j) {
350         return x[index(c_offset_Y + k, j)];
351     }
352 
Y_prev(size_t k,size_t j)353     doublereal Y_prev(size_t k, size_t j) const {
354         return prevSoln(c_offset_Y + k, j);
355     }
356 
X(const doublereal * x,size_t k,size_t j)357     doublereal X(const doublereal* x, size_t k, size_t j) const {
358         return m_wtm[j]*Y(x,k,j)/m_wt[k];
359     }
360 
flux(size_t k,size_t j)361     doublereal flux(size_t k, size_t j) const {
362         return m_flux(k, j);
363     }
364     //! @}
365 
366     //! @name convective spatial derivatives.
367     //! These use upwind differencing, assuming u(z) is negative
368     //! @{
dVdz(const doublereal * x,size_t j)369     doublereal dVdz(const doublereal* x, size_t j) const {
370         size_t jloc = (u(x,j) > 0.0 ? j : j + 1);
371         return (V(x,jloc) - V(x,jloc-1))/m_dz[jloc-1];
372     }
373 
dYdz(const doublereal * x,size_t k,size_t j)374     doublereal dYdz(const doublereal* x, size_t k, size_t j) const {
375         size_t jloc = (u(x,j) > 0.0 ? j : j + 1);
376         return (Y(x,k,jloc) - Y(x,k,jloc-1))/m_dz[jloc-1];
377     }
378 
dTdz(const doublereal * x,size_t j)379     doublereal dTdz(const doublereal* x, size_t j) const {
380         size_t jloc = (u(x,j) > 0.0 ? j : j + 1);
381         return (T(x,jloc) - T(x,jloc-1))/m_dz[jloc-1];
382     }
383     //! @}
384 
shear(const doublereal * x,size_t j)385     doublereal shear(const doublereal* x, size_t j) const {
386         doublereal c1 = m_visc[j-1]*(V(x,j) - V(x,j-1));
387         doublereal c2 = m_visc[j]*(V(x,j+1) - V(x,j));
388         return 2.0*(c2/(z(j+1) - z(j)) - c1/(z(j) - z(j-1)))/(z(j+1) - z(j-1));
389     }
390 
divHeatFlux(const doublereal * x,size_t j)391     doublereal divHeatFlux(const doublereal* x, size_t j) const {
392         doublereal c1 = m_tcon[j-1]*(T(x,j) - T(x,j-1));
393         doublereal c2 = m_tcon[j]*(T(x,j+1) - T(x,j));
394         return -2.0*(c2/(z(j+1) - z(j)) - c1/(z(j) - z(j-1)))/(z(j+1) - z(j-1));
395     }
396 
mindex(size_t k,size_t j,size_t m)397     size_t mindex(size_t k, size_t j, size_t m) {
398         return m*m_nsp*m_nsp + m_nsp*j + k;
399     }
400 
401     //! Update the diffusive mass fluxes.
402     virtual void updateDiffFluxes(const doublereal* x, size_t j0, size_t j1);
403 
404     //---------------------------------------------------------
405     //             member data
406     //---------------------------------------------------------
407 
408     doublereal m_press; // pressure
409 
410     // grid parameters
411     vector_fp m_dz;
412 
413     // mixture thermo properties
414     vector_fp m_rho;
415     vector_fp m_wtm;
416 
417     // species thermo properties
418     vector_fp m_wt;
419     vector_fp m_cp;
420 
421     // transport properties
422     vector_fp m_visc;
423     vector_fp m_tcon;
424     vector_fp m_diff;
425     vector_fp m_multidiff;
426     Array2D m_dthermal;
427     Array2D m_flux;
428 
429     // production rates
430     Array2D m_wdot;
431 
432     size_t m_nsp;
433 
434     IdealGasPhase* m_thermo;
435     Kinetics* m_kin;
436     Transport* m_trans;
437 
438     // boundary emissivities for the radiation calculations
439     doublereal m_epsilon_left;
440     doublereal m_epsilon_right;
441 
442     //! Indices within the ThermoPhase of the radiating species. First index is
443     //! for CO2, second is for H2O.
444     std::vector<size_t> m_kRadiating;
445 
446     // flags
447     std::vector<bool> m_do_energy;
448     bool m_do_soret;
449     std::vector<bool> m_do_species;
450     bool m_do_multicomponent;
451 
452     //! flag for the radiative heat loss
453     bool m_do_radiation;
454 
455     //! radiative heat loss vector
456     vector_fp m_qdotRadiation;
457 
458     // fixed T and Y values
459     vector_fp m_fixedtemp;
460     vector_fp m_zfix;
461     vector_fp m_tfix;
462 
463     //! Index of species with a large mass fraction at each boundary, for which
464     //! the mass fraction may be calculated as 1 minus the sum of the other mass
465     //! fractions
466     size_t m_kExcessLeft;
467     size_t m_kExcessRight;
468 
469     bool m_dovisc;
470 
471     //! Update the transport properties at grid points in the range from `j0`
472     //! to `j1`, based on solution `x`.
473     virtual void updateTransport(doublereal* x, size_t j0, size_t j1);
474 
475 public:
476     //! Location of the point where temperature is fixed
477     double m_zfixed;
478 
479     //! Temperature at the point used to fix the flame location
480     double m_tfixed;
481 
482 private:
483     vector_fp m_ybar;
484 };
485 
486 }
487 
488 #endif
489