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