diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml index 5c2157eb2277..485936e1d2a0 100644 --- a/.github/workflows/regression.yml +++ b/.github/workflows/regression.yml @@ -46,7 +46,7 @@ jobs: config_set: [BaseMPI, ReverseMPI, ForwardMPI, BaseNoMPI, ReverseNoMPI, ForwardNoMPI, ReverseTagNoMPI, BaseOMP, ReverseOMP, ForwardOMP] include: - config_set: BaseMPI - flags: '${{ needs.setup.outputs.flags }} -Denable-pywrapper=true -Denable-coolprop=true -Denable-mpp=true -Dinstall-mpp=true -Denable-mlpcpp=true -Denable-tests=true --warnlevel=2' + flags: '${{ needs.setup.outputs.flags }} -Denable-pywrapper=true -Denable-coolprop=true -Denable-mpp=true -Dinstall-mpp=true -Denable-mlpcpp=true -Denable-cantera=true -Denable-tests=true --warnlevel=2' - config_set: ReverseMPI flags: '${{ needs.setup.outputs.flags }} -Denable-autodiff=true -Denable-normal=false -Denable-pywrapper=true -Denable-tests=true -Denable-mlpcpp=true --warnlevel=3 ${{ needs.setup.outputs.werror }}' - config_set: ForwardMPI @@ -233,7 +233,7 @@ jobs: uses: docker://ghcr.io/su2code/su2/test-su2:261011-1107 with: # -t -c - args: -b ${{github.ref}} -t develop -c develop -s ${{matrix.testscript}} + args: -b ${{github.ref}} -t develop -c feature_cantera -s ${{matrix.testscript}} - name: Cleanup uses: docker://ghcr.io/su2code/su2/test-su2:261011-1107 with: @@ -282,7 +282,7 @@ jobs: uses: docker://ghcr.io/su2code/su2/test-su2:261011-1107 with: # -t -c - args: -b ${{github.ref}} -t develop -c develop -s ${{matrix.testscript}} -a "--tapetests" + args: -b ${{github.ref}} -t develop -c feature_cantera -s ${{matrix.testscript}} -a "--tapetests" - name: Cleanup uses: docker://ghcr.io/su2code/su2/test-su2:261011-1107 with: @@ -330,7 +330,7 @@ jobs: PMIX_MCA_gds: hash with: # -t -c - args: -b ${{github.ref}} -t develop -c develop -s ${{matrix.testscript}} -a "--tsan" + args: -b ${{github.ref}} -t develop -c feature_cantera -s ${{matrix.testscript}} -a "--tsan" - name: Cleanup uses: docker://ghcr.io/su2code/su2/test-su2-tsan:261011-1107 with: @@ -375,7 +375,7 @@ jobs: uses: docker://ghcr.io/su2code/su2/test-su2-asan:261011-1107 with: # -t -c - args: -b ${{github.ref}} -t develop -c develop -s ${{matrix.testscript}} -a "--asan" + args: -b ${{github.ref}} -t develop -c feature_cantera -s ${{matrix.testscript}} -a "--asan" - name: Cleanup uses: docker://ghcr.io/su2code/su2/test-su2-asan:261011-1107 with: @@ -463,6 +463,7 @@ jobs: env: OMPI_MCA_osc: pt2pt LD_LIBRARY_PATH: /github/workspace/install/lib + CANTERA_DATA: /github/workspace/src/SU2/subprojects/cantera/data:/github/workspace/src/SU2/UnitTests/SU2_CFD/fluid with: entrypoint: install/bin/${{matrix.testdriver}} - name: Check NEMO convergence field migration diff --git a/.gitmodules b/.gitmodules index 47e94c29240d..061da74abc34 100644 --- a/.gitmodules +++ b/.gitmodules @@ -18,6 +18,9 @@ [submodule "subprojects/CoolProp"] path = subprojects/CoolProp url = https://github.com/CoolProp/CoolProp.git +[submodule "subprojects/cantera"] + path = subprojects/cantera + url = https://github.com/cantera/cantera.git [submodule "externals/opdi"] path = externals/opdi url = https://github.com/SciCompKL/OpDiLib diff --git a/Common/include/CConfig.hpp b/Common/include/CConfig.hpp index 9d4920c871b0..41d1a06607e4 100644 --- a/Common/include/CConfig.hpp +++ b/Common/include/CConfig.hpp @@ -81,7 +81,8 @@ class CConfig { su2double EA_ScaleFactor; /*!< \brief Equivalent Area scaling factor */ su2double AdjointLimit; /*!< \brief Adjoint variable limit */ string* ConvField; /*!< \brief Field used for convergence check.*/ - string FluidName; /*!< \brief name of the applied fluid. */ + string FluidName; /*!< \brief name of the applied fluid. */ + CanteraOptions cantera_ParsedOptions; /*!< \brief Options of the Cantera detailed chemistry model. */ string* WndConvField; /*!< \brief Function where to apply the windowed convergence criteria for the time average of the unsteady (single zone) flow problem. */ unsigned short nConvField; /*!< \brief Number of fields used to monitor convergence.*/ @@ -1185,7 +1186,7 @@ class CConfig { su2double *FreeStreamTurboNormal; /*!< \brief Direction to initialize the flow in turbomachinery computation */ su2double Restart_Bandwidth_Agg; /*!< \brief The aggregate of the bandwidth for writing binary restarts (to be averaged later). */ - su2double Max_Vel2; /*!< \brief The maximum velocity^2 in the domain for the incompressible preconditioner. */ + su2double Max_Vel2{0.0}; /*!< \brief The maximum velocity^2 in the domain for the incompressible preconditioner. */ su2double RangePressure[2]; /*!< \brief The pressure difference pmax-pmin in the domain for the target mass flow rate scaling. */ bool topology_optimization; /*!< \brief If the structural solver should consider a variable density field to penalize element stiffness. */ string top_optim_output_file; /*!< \brief File to where the derivatives w.r.t. element densities will be written to. */ @@ -3404,6 +3405,14 @@ class CConfig { */ unsigned long GetInnerIter(void) const { return InnerIter; } + /*! + * \brief Get the iteration counter that times a spark ignition. + * \return Outer iteration (multizone), time iteration (unsteady) or inner iteration. + */ + unsigned long GetIgnitionIter() const { + return Multizone_Problem ? OuterIter : (Time_Domain ? TimeIter : InnerIter); + } + /*! * \brief Set the current physical time. * \param[in] val_t - Current physical time. @@ -4087,6 +4096,77 @@ class CConfig { */ string GetFluid_Name(void) const { return FluidName; } + /*! + * \brief Returns the options of the Cantera detailed chemistry model. + */ + const CanteraOptions& GetCanteraParsedOptions() const { return cantera_ParsedOptions; } + + /*! + * \brief Returns the transport model used in Cantera. + */ + const string& GetTransport_Model() const { return cantera_ParsedOptions.transport_model; } + + /*! + * \brief Returns the chemical reaction mechanism file used in Cantera. + */ + const string& GetChemical_MechanismFile() const { return cantera_ParsedOptions.mechanism_file; } + + /*! + * \brief Returns the name of the phase in the chemical reaction mechanism file used in Cantera. + */ + const string& GetPhase_Name() const { return cantera_ParsedOptions.phase_name; } + + /*! + * \brief Returns the name of a species of the Cantera mixture, checked against the number of names. + */ + const string& GetChemical_GasComposition(unsigned short val_index = 0) const { + if (val_index >= cantera_ParsedOptions.n_species_names) { + SU2_MPI::Error("Index out of range for CANTERA_SPECIES_NAMES.", CURRENT_FUNCTION); + } + return cantera_ParsedOptions.species_names[val_index]; + } + + /*! + * \brief Returns the label of a transported species in output fields: its name for Cantera, otherwise its index. + */ + string GetSpecies_Label(unsigned short val_index) const { + return (Kind_FluidModel == FLUID_CANTERA) ? GetChemical_GasComposition(val_index) : to_string(val_index); + } + + /*! + * \brief Get information about the Combustion-Detailed chemistry using Cantera. + * \return TRUE if combustion-detailed chemistry using Cantera is used; otherwise FALSE. + */ + bool GetCombustion() const { return cantera_ParsedOptions.combustion; } + + /*! + * \brief Get whether the diagonal chemical sink Jacobian is added to the implicit species equations. + */ + bool GetChemical_Source_Jacobian() const { return cantera_ParsedOptions.source_jacobian; } + + /*! + * \brief Get the temperature below which the chemical source terms are set to zero. + */ + su2double GetCantera_DC_Min_Temp() const { return cantera_ParsedOptions.min_temperature; } + + /*! + * \brief Get whether the species diffusion fluxes are corrected to sum to zero. + */ + bool GetCantera_Correction_Velocity() const { return cantera_ParsedOptions.correction_velocity; } + + /*! + * \brief Get High temperature applied during spark ignition. + * \return Spark Temperature. + */ + su2double GetSpark_Temperature() const { return cantera_ParsedOptions.spark_temperature; } + + /*! + * \brief Whether the fluid is a mixture of transported species with their own enthalpy diffusion + * (FLUID_MIXTURE or FLUID_CANTERA). + */ + bool GetMulticomponentFluid() const { return (Kind_FluidModel == FLUID_MIXTURE) || (Kind_FluidModel == FLUID_CANTERA); } + + /*! * \brief Option to define the density model for incompressible flows. * \return Density model option diff --git a/Common/include/containers/C2DContainer.hpp b/Common/include/containers/C2DContainer.hpp index 380e43cfba7b..149258dc038e 100644 --- a/Common/include/containers/C2DContainer.hpp +++ b/Common/include/containers/C2DContainer.hpp @@ -349,6 +349,11 @@ class AccessorImpl= MAX_TRANSPORTED_SPECIES, + "CSysMatrix::MAXNVAR must hold the species equations: raise MAXNVAR together with MAX_TRANSPORTED_SPECIES."); enum { OMP_MAX_SIZE_L = 8192 }; /*!< \brief Max. chunk size used in light parallel for loops. */ enum { OMP_MAX_SIZE_H = 512 }; /*!< \brief Max. chunk size used in heavy parallel for loops. */ diff --git a/Common/include/option_structure.hpp b/Common/include/option_structure.hpp index 351e534671ae..1af43985c126 100644 --- a/Common/include/option_structure.hpp +++ b/Common/include/option_structure.hpp @@ -97,6 +97,14 @@ constexpr passivedouble BOLTZMANN_CONSTANT = 1.3806503E-23; /*!< \brief Boltzm constexpr passivedouble AVOGAD_CONSTANT = 6.0221415E26; /*!< \brief Avogadro's constant, number of particles in one kmole. */ constexpr passivedouble FUND_ELEC_CHARGE_CGS = 4.8032047E-10; /*!< \brief Fundamental electric charge in CGS units, cm^(3/2) g^(1/2) s^(-1). */ constexpr passivedouble STD_REF_TEMP = 298.15; /*!< \brief Standard reference temperature for enthalpy in Kelvin. */ + +/*! + * \brief Maximum number of transported species (scalar equations of the species model). + * \note Sizes the stack storage of the species edge-flux kernel and the static arrays of the species solver. It + * must not exceed the limits of the linear algebra (CSysMatrix::MAXNVAR), the multigrid and the limiters; + * static_asserts at those places fail to compile when it does, and name the limit to raise. + */ +constexpr unsigned short MAX_TRANSPORTED_SPECIES = 20; constexpr passivedouble EPS = 1.0E-16; /*!< \brief Error scale. */ constexpr passivedouble TURB_EPS = 1.0E-16; /*!< \brief Turbulent Error scale. */ @@ -651,6 +659,7 @@ enum ENUM_FLUIDMODEL { COOLPROP = 10, /*!< \brief Thermodynamics library. */ FLUID_FLAMELET = 11, /*!< \brief lookup table (LUT) method for premixed flamelets. */ DATADRIVEN_FLUID = 12, /*!< \brief multi-layer perceptron driven fluid model. */ + FLUID_CANTERA = 13, /*!< \brief Reacting flows model. */ }; static const MapType FluidModel_Map = { MakePair("STANDARD_AIR", STANDARD_AIR) @@ -666,6 +675,7 @@ static const MapType FluidModel_Map = { MakePair("COOLPROP", COOLPROP) MakePair("DATADRIVEN_FLUID", DATADRIVEN_FLUID) MakePair("FLUID_FLAMELET", FLUID_FLAMELET) + MakePair("FLUID_CANTERA", FLUID_CANTERA) }; /*! @@ -784,6 +794,7 @@ enum class VISCOSITYMODEL { POLYNOMIAL, /*!< \brief Polynomial viscosity. */ FLAMELET, /*!< \brief LUT method for flamelets */ COOLPROP, /*!< \brief CoolProp viscosity. */ + CANTERA, /*!< \brief Cantera viscosity. */ }; static const MapType ViscosityModel_Map = { MakePair("CONSTANT_VISCOSITY", VISCOSITYMODEL::CONSTANT) @@ -791,6 +802,7 @@ static const MapType ViscosityModel_Map = { MakePair("POLYNOMIAL_VISCOSITY", VISCOSITYMODEL::POLYNOMIAL) MakePair("FLAMELET", VISCOSITYMODEL::FLAMELET) MakePair("COOLPROP", VISCOSITYMODEL::COOLPROP) + MakePair("CANTERA", VISCOSITYMODEL::CANTERA) }; /*! @@ -814,6 +826,7 @@ enum class CONDUCTIVITYMODEL { POLYNOMIAL, /*!< \brief Polynomial thermal conductivity. */ FLAMELET, /*!< \brief LUT method for flamelets */ COOLPROP, /*!< \brief COOLPROP thermal conductivity. */ + CANTERA, /*!< \brief CANTERA thermal conductivity. */ }; static const MapType ConductivityModel_Map = { MakePair("CONSTANT_CONDUCTIVITY", CONDUCTIVITYMODEL::CONSTANT) @@ -821,6 +834,7 @@ static const MapType ConductivityModel_Map = { MakePair("POLYNOMIAL_CONDUCTIVITY", CONDUCTIVITYMODEL::POLYNOMIAL) MakePair("FLAMELET", CONDUCTIVITYMODEL::FLAMELET) MakePair("COOLPROP", CONDUCTIVITYMODEL::COOLPROP) + MakePair("CANTERA", CONDUCTIVITYMODEL::CANTERA) }; /*! @@ -844,6 +858,7 @@ enum class DIFFUSIVITYMODEL { UNITY_LEWIS, /*!< \brief Unity Lewis model for mass diffusion in scalar transport. */ CONSTANT_LEWIS, /*!< \brief Different Lewis number model for mass diffusion in scalar transport. */ FLAMELET, /*!< \brief flamelet model for tabulated chemistry, diffusivity from lookup table */ + CANTERA, /*!< \brief Mixture average diffusivity from CANTERA */ }; static const MapType Diffusivity_Model_Map = { @@ -852,6 +867,7 @@ static const MapType Diffusivity_Model_Map = { MakePair("UNITY_LEWIS", DIFFUSIVITYMODEL::UNITY_LEWIS) MakePair("CONSTANT_LEWIS", DIFFUSIVITYMODEL::CONSTANT_LEWIS) MakePair("FLAMELET", DIFFUSIVITYMODEL::FLAMELET) + MakePair("CANTERA", DIFFUSIVITYMODEL::CANTERA) }; /*! @@ -1608,6 +1624,43 @@ struct FluidFlamelet_ParsedOptions { bool thickenedflame_correction{true}; /*!< \brief Thickened flame correction. */ su2double Flame_T_ignition = 5000; /*!< \brief Ignition temperature for the flame, used for initialization. */ + /*! + * \brief Whether the artificial spark is active at the given iteration. + * \param[in] iter - Iteration counter that times the spark, see CConfig::GetIgnitionIter(). + */ + bool SparkActive(unsigned long iter) const { + if (ignition_method != FLAMELET_INIT_TYPE::SPARK) return false; + const unsigned long start = static_cast(std::ceil(SU2_TYPE::GetValue(spark_init[4]))); + const unsigned long duration = static_cast(std::ceil(SU2_TYPE::GetValue(spark_init[5]))); + return (iter >= start) && (iter <= start + duration); + } + + /*! + * \brief Whether a point is inside the spark region. + * \param[in] nDim - Number of dimensions. + * \param[in] coord - Coordinates of the point. + */ + bool InSpark(unsigned short nDim, const su2double* coord) const { + su2double dist2 = 0.0; + for (unsigned short iDim = 0; iDim < nDim; iDim++) dist2 += (coord[iDim] - spark_init[iDim]) * (coord[iDim] - spark_init[iDim]); + return dist2 < spark_init[3] * spark_init[3]; + } +}; + +/*! + * \brief Structure containing the parsed options of the Cantera detailed chemistry model (FLUID_CANTERA). + */ +struct CanteraOptions { + std::string mechanism_file; /*!< \brief Chemical reaction mechanism file, required. */ + std::string phase_name; /*!< \brief Name of the phase in the mechanism file, required. */ + std::string transport_model = "mixture-averaged"; /*!< \brief Cantera transport model. */ + unsigned short n_species_names = 0; /*!< \brief Number of species names. */ + std::string* species_names = nullptr; /*!< \brief Names of the species, the last one is the remainder. */ + bool combustion = false; /*!< \brief Add the chemical source terms to the species equations. */ + su2double spark_temperature = 1000.0; /*!< \brief Temperature inside the spark region while it is active. */ + bool source_jacobian = true; /*!< \brief Diagonal chemical sink Jacobian in the implicit species equations. */ + su2double min_temperature = 500.0; /*!< \brief Temperature below which the chemistry is skipped. */ + bool correction_velocity = true; /*!< \brief Correction velocity of the species diffusion fluxes. */ }; /*! diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index b0c273939be0..4b3d2e619a86 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -1287,6 +1287,26 @@ void CConfig::SetConfig_Options() { /*!\brief FLUID_NAME \n DESCRIPTION: Fluid name \n OPTIONS: see coolprop homepage \n DEFAULT: nitrogen \ingroup Config*/ addStringOption("FLUID_NAME", FluidName, string("nitrogen")); + /*!\par CONFIG_CATEGORY: Cantera fluid model \ingroup Config*/ + /*!\brief CANTERA_MECHANISM_FILE \n DESCRIPTION: Chemical reaction mechanism file (YAML) \n OPTIONS: see Cantera homepage \n DEFAULT: none, required \ingroup Config*/ + addStringOption("CANTERA_MECHANISM_FILE", cantera_ParsedOptions.mechanism_file, string("")); + /*!\brief CANTERA_PHASE_NAME \n DESCRIPTION: Name of the phase in the chemical mechanism file \n OPTIONS: see Cantera homepage \n DEFAULT: none, required \ingroup Config*/ + addStringOption("CANTERA_PHASE_NAME", cantera_ParsedOptions.phase_name, string("")); + /*!\brief CANTERA_TRANSPORT_MODEL \n DESCRIPTION: Transport model \n OPTIONS: see Cantera homepage \n DEFAULT: mixture-averaged \ingroup Config*/ + addStringOption("CANTERA_TRANSPORT_MODEL", cantera_ParsedOptions.transport_model, string("mixture-averaged")); + /*!\brief CANTERA_SPECIES_NAMES \n DESCRIPTION: Names of the species in the mechanism, the last one is the remainder \n DEFAULT: none, required \ingroup Config*/ + addStringListOption("CANTERA_SPECIES_NAMES", cantera_ParsedOptions.n_species_names, cantera_ParsedOptions.species_names); + /*!\brief CANTERA_COMBUSTION \n DESCRIPTION: Add the chemical source terms of the detailed chemistry to the species equations \n DEFAULT: false \ingroup Config*/ + addBoolOption("CANTERA_COMBUSTION", cantera_ParsedOptions.combustion, false); + /*!\brief CANTERA_SPARK_TEMPERATURE \n DESCRIPTION: Temperature [K] inside the spark region while the spark is active \n DEFAULT: 1000 K \ingroup Config*/ + addDoubleOption("CANTERA_SPARK_TEMPERATURE", cantera_ParsedOptions.spark_temperature, 1000.0); + /*!\brief CANTERA_SOURCE_JACOBIAN \n DESCRIPTION: Add the diagonal chemical sink Jacobian to the implicit species equations \n DEFAULT: true \ingroup Config*/ + addBoolOption("CANTERA_SOURCE_JACOBIAN", cantera_ParsedOptions.source_jacobian, true); + /*!\brief CANTERA_DC_MIN_TEMP \n DESCRIPTION: Temperature [K] below which the chemical source terms are set to zero \n DEFAULT: 500 K \ingroup Config*/ + addDoubleOption("CANTERA_DC_MIN_TEMP", cantera_ParsedOptions.min_temperature, 500.0); + /*!\brief CANTERA_CORRECTION_VELOCITY \n DESCRIPTION: Correct the species diffusion fluxes so that they sum to zero \n DEFAULT: true \ingroup Config*/ + addBoolOption("CANTERA_CORRECTION_VELOCITY", cantera_ParsedOptions.correction_velocity, true); + /*!\par CONFIG_CATEGORY: Data-driven fluid model parameters \ingroup Config*/ /*!\brief INTERPOLATION_METHOD \n DESCRIPTION: Interpolation method used to determine the thermodynamic state of the fluid. \n OPTIONS: See \link DataDrivenMethod_Map \endlink DEFAULT: MLP \ingroup Config*/ addEnumOption("INTERPOLATION_METHOD",datadriven_ParsedOptions.interp_algorithm_type, DataDrivenMethod_Map, ENUM_DATADRIVEN_METHOD::LUT); @@ -3676,7 +3696,8 @@ void CConfig::SetPostprocessing(SU2_COMPONENT val_software, unsigned short val_i (Kind_FluidModel == FLUID_MIXTURE) || (Kind_FluidModel == FLUID_FLAMELET) || (Kind_FluidModel == INC_IDEAL_GAS_POLY) || - (Kind_FluidModel == CONSTANT_DENSITY)); + (Kind_FluidModel == CONSTANT_DENSITY)|| + (Kind_FluidModel == FLUID_CANTERA)); bool noneq_gas = ((Kind_FluidModel == MUTATIONPP) || (Kind_FluidModel == SU2_NONEQ)); bool standard_air = ((Kind_FluidModel == STANDARD_AIR)); @@ -4330,6 +4351,15 @@ void CConfig::SetPostprocessing(SU2_COMPONENT val_software, unsigned short val_i "to be equal to the number of entries of SPECIES_INIT + 1", CURRENT_FUNCTION); } + for (unsigned short i = 0; i < cantera_ParsedOptions.n_species_names; i++) { + for (unsigned short j = 0; j < i; j++) { + if (cantera_ParsedOptions.species_names[i] == cantera_ParsedOptions.species_names[j]) { + SU2_MPI::Error("Species '" + cantera_ParsedOptions.species_names[i] + + "' appears more than once in CANTERA_SPECIES_NAMES.", + CURRENT_FUNCTION); + } + } + } /*--- Check whether the density model used is correct, in the case of FLUID_MIXTURE the density model must be VARIABLE. Otherwise, if the density model is CONSTANT, the scalars will not have influence the mixture density and it will remain constant through the complete domain. --- */ @@ -4412,6 +4442,79 @@ void CConfig::SetPostprocessing(SU2_COMPONENT val_software, unsigned short val_i } } + if ((Kind_FluidModel != FLUID_CANTERA) && cantera_ParsedOptions.combustion) { + SU2_MPI::Error( + "The use of CANTERA_COMBUSTION=YES requires the use of FLUID_MODEL=FLUID_CANTERA,\n" + "detailed chemistry cannot be performed with other fluid models", + CURRENT_FUNCTION); + } + + if ((flamelet_ParsedOptions.ignition_method != FLAMELET_INIT_TYPE::SPARK) && cantera_ParsedOptions.combustion) { + SU2_MPI::Error( + "The use of CANTERA_COMBUSTION=YES requires the use of FLAME_INIT_METHOD=SPARK,\n" + "Other ignition methods are not currently available", + CURRENT_FUNCTION); + } + + if (Kind_FluidModel == FLUID_CANTERA) { + if (Kind_Regime != ENUM_REGIME::INCOMPRESSIBLE || Kind_Solver == MAIN_SOLVER::INC_EULER) { + SU2_MPI::Error( + "FLUID_CANTERA is only available for the incompressible viscous solvers, " + "SOLVER= INC_NAVIER_STOKES or INC_RANS.", + CURRENT_FUNCTION); + } + if (cantera_ParsedOptions.mechanism_file.empty() || cantera_ParsedOptions.phase_name.empty()) { + SU2_MPI::Error( + "The use of FLUID_CANTERA requires CANTERA_MECHANISM_FILE and CANTERA_PHASE_NAME to be set,\n" + "there are no default values.", + CURRENT_FUNCTION); + } + /*--- Check whether the number of entries of the CANTERA_SPECIES_NAMES equals the number of transported scalar + equations solved + 1.--- */ + if (cantera_ParsedOptions.n_species_names != nSpecies_Init + 1) { + SU2_MPI::Error( + "The use of FLUID_CANTERA requires the number of entries for CANTERA_SPECIES_NAMES,\n" + "to be equal to the number of entries of SPECIES_INIT + 1", + CURRENT_FUNCTION); + } + /*--- Check whether the density model used is correct, in the case of FLUID_CANTERA the density model must be + VARIABLE. Otherwise, if the density model is CONSTANT, the scalars will not have influence the mixture density + and it will remain constant through the complete domain. --- */ + if (Kind_DensityModel != INC_DENSITYMODEL::VARIABLE) { + SU2_MPI::Error("The use of FLUID_CANTERA requires the INC_DENSITY_MODEL option to be VARIABLE", + CURRENT_FUNCTION); + } + /*--- Check whether the Kind scalar model used is correct, in the case of FLUID_CANTERA the kind scalar model must + be SPECIES_TRANSPORT. Otherwise, if the scalar model is NONE, the species transport equations will not be solved. + --- */ + if (Kind_Species_Model != SPECIES_MODEL::SPECIES_TRANSPORT) { + SU2_MPI::Error("The use of FLUID_CANTERA requires the KIND_SCALAR_MODEL option to be SPECIES_TRANSPORT", + CURRENT_FUNCTION); + } + + if (Ref_Inc_NonDim != DIMENSIONAL) { + SU2_MPI::Error( + "The use of FLUID_CANTERA requires the option INC_NONDIM= DIMENSIONAL, the nondimensionalization is " + "currently unavailable.", + CURRENT_FUNCTION); + } + + if (Kind_ConductivityModel != CONDUCTIVITYMODEL::CANTERA) { + SU2_MPI::Error("The use of FLUID_CANTERA requires the CONDUCTIVITY_MODEL option to be CANTERA", + CURRENT_FUNCTION); + } + + if (Kind_ViscosityModel != VISCOSITYMODEL::CANTERA) { + SU2_MPI::Error("The use of FLUID_CANTERA requires the VISCOSITY_MODEL option to be CANTERA", + CURRENT_FUNCTION); + } + + if (Kind_Diffusivity_Model != DIFFUSIVITYMODEL::CANTERA) { + SU2_MPI::Error("The use of FLUID_CANTERA requires the DIFFUSIVITY_MODEL option to be CANTERA", + CURRENT_FUNCTION); + } + } + if (Kind_Species_Model == SPECIES_MODEL::FLAMELET) { @@ -5401,7 +5504,7 @@ void CConfig::SetPostprocessing(SU2_COMPONENT val_software, unsigned short val_i } if (Kind_DensityModel == INC_DENSITYMODEL::VARIABLE) { - if (Kind_FluidModel != INC_IDEAL_GAS && Kind_FluidModel != INC_IDEAL_GAS_POLY && Kind_FluidModel != FLUID_MIXTURE && Kind_FluidModel != FLUID_FLAMELET) { + if (Kind_FluidModel != INC_IDEAL_GAS && Kind_FluidModel != INC_IDEAL_GAS_POLY && Kind_FluidModel != FLUID_MIXTURE && Kind_FluidModel != FLUID_FLAMELET && Kind_FluidModel != FLUID_CANTERA) { SU2_MPI::Error("Variable density incompressible solver limited to ideal gases.\n Check the fluid model options (use INC_IDEAL_GAS, INC_IDEAL_GAS_POLY).", CURRENT_FUNCTION); } } @@ -5965,6 +6068,17 @@ void CConfig::SetPostprocessing(SU2_COMPONENT val_software, unsigned short val_i /*--- Once consistency is checked set the var that is used throughout the code. ---*/ nSpecies = nSpecies_Init; + if (nSpecies > MAX_TRANSPORTED_SPECIES) { + SU2_MPI::Error( + "The species model supports at most " + to_string(MAX_TRANSPORTED_SPECIES) + + " transported species equations (the number of SPECIES_INIT entries, one less than the species of a\n" + "mixture), but " + to_string(nSpecies) + " were given.\n" + "To run more species, increase MAX_TRANSPORTED_SPECIES in Common/include/option_structure.hpp and\n" + "recompile. If the compiler then reports a failed static_assert, raise the limit it names as well\n" + "(for example MAXNVAR of CSysMatrix in Common/include/linear_algebra/CSysMatrix.hpp).", + CURRENT_FUNCTION); + } + /*--- Check whether some variables (or their sums) are in physical bounds. [0,1] for species related quantities. ---*/ /*--- Note, only for species transport, not for flamelet model ---*/ if (Kind_Species_Model == SPECIES_MODEL::SPECIES_TRANSPORT) { diff --git a/Common/src/geometry/CMultiGridGeometry.cpp b/Common/src/geometry/CMultiGridGeometry.cpp index d3bca729bf5b..b8caa00e9c94 100644 --- a/Common/src/geometry/CMultiGridGeometry.cpp +++ b/Common/src/geometry/CMultiGridGeometry.cpp @@ -1422,34 +1422,31 @@ void CMultiGridGeometry::FindNormal_Neighbor(const CConfig* config) { for (auto iVertex = 0ul; iVertex < nVertex[iMarker]; iVertex++) { auto iPoint = vertex[iMarker][iVertex]->GetNode(); - /*--- If the node belong to the domain ---*/ - if (nodes->GetDomain(iPoint)) { - /*--- Compute closest normal neighbor ---*/ - unsigned long Point_Normal = 0; - su2double* Normal = vertex[iMarker][iVertex]->GetNormal(); - su2double cos_max = -1.0; - for (auto jPoint : nodes->GetPoints(iPoint)) { - su2double scalar_prod = 0.0; - su2double norm_vect = 0.0; - su2double norm_Normal = 0.0; - for (auto iDim = 0u; iDim < nDim; iDim++) { - su2double diff_coord = nodes->GetCoord(jPoint, iDim) - nodes->GetCoord(iPoint, iDim); - scalar_prod += diff_coord * Normal[iDim]; - norm_vect += diff_coord * diff_coord; - norm_Normal += Normal[iDim] * Normal[iDim]; - } - norm_vect = sqrt(norm_vect); - norm_Normal = sqrt(norm_Normal); - su2double cos_alpha = scalar_prod / (norm_vect * norm_Normal); - - /*--- Get maximum cosine (not minimum because normals are oriented inwards) ---*/ - if (cos_alpha >= cos_max) { - Point_Normal = jPoint; - cos_max = cos_alpha; - } + /*--- Compute closest normal neighbor, also for halo points, which Friction_Forces visits. ---*/ + unsigned long Point_Normal = 0; + su2double* Normal = vertex[iMarker][iVertex]->GetNormal(); + su2double cos_max = -1.0; + for (auto jPoint : nodes->GetPoints(iPoint)) { + su2double scalar_prod = 0.0; + su2double norm_vect = 0.0; + su2double norm_Normal = 0.0; + for (auto iDim = 0u; iDim < nDim; iDim++) { + su2double diff_coord = nodes->GetCoord(jPoint, iDim) - nodes->GetCoord(iPoint, iDim); + scalar_prod += diff_coord * Normal[iDim]; + norm_vect += diff_coord * diff_coord; + norm_Normal += Normal[iDim] * Normal[iDim]; + } + norm_vect = sqrt(norm_vect); + norm_Normal = sqrt(norm_Normal); + su2double cos_alpha = scalar_prod / (norm_vect * norm_Normal); + + /*--- Get maximum cosine (not minimum because normals are oriented inwards) ---*/ + if (cos_alpha >= cos_max) { + Point_Normal = jPoint; + cos_max = cos_alpha; } - vertex[iMarker][iVertex]->SetNormal_Neighbor(Point_Normal); } + vertex[iMarker][iVertex]->SetNormal_Neighbor(Point_Normal); } } } diff --git a/SU2_CFD/include/fluid/CFluidCantera.hpp b/SU2_CFD/include/fluid/CFluidCantera.hpp new file mode 100644 index 000000000000..9de0e5f68a1e --- /dev/null +++ b/SU2_CFD/include/fluid/CFluidCantera.hpp @@ -0,0 +1,203 @@ +/*! + * \file CFluidCantera.hpp + * \brief Defines the multicomponent incompressible Ideal Gas model for reacting flows. + * \author T. Economon, Cristopher Morales Ubal + * \version 8.5.0 "Harrier" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2026, SU2 Contributors (cf. AUTHORS.md) + * + * SU2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * SU2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with SU2. If not, see . + */ + +#pragma once + +#include +#include +#include + +#include "CFluidModel.hpp" + +#ifdef HAVE_CANTERA +namespace Cantera { +class Solution; +} +#endif + + +/*! + * \class CFluidCantera + * \brief Child class for defining reacting incompressible ideal gas mixture model. + * \author: T. Economon, Cristopher Morales Ubal + */ +class CFluidCantera final : public CFluidModel { + private: +#ifdef HAVE_CANTERA + const size_t n_species_mixture; /*!< \brief Number of species in mixture. */ + const su2double Pressure_Thermodynamic; /*!< \brief Constant pressure thermodynamic. */ + const su2double Prandtl_Turb_Number; /*!< \brief Prandlt turbulent number.*/ + const su2double Schmidt_Turb_Number; /*!< \brief Schmidt turbulent number.*/ + const string Transport_Model; /*!< \brief Transport model used for computing mixture properties*/ + const string Chemical_MechanismFile; /*!< \brief Chemical reaction mechanism used for in cantera*/ + const string Phase_Name; /*!< \brief Name of the phase used for in cantera*/ + const bool Combustion; /*!< \brief Flag for problems involving combustion.*/ + const su2double Chemistry_Min_Temperature; /*!< \brief Temperature below which the chemistry is skipped. */ + const bool Correction_Velocity; /*!< \brief The diffusive species fluxes are corrected to sum to zero. */ + + su2double Heat_Release{0.0}; /*!< \brief heat release due to combustion */ + bool temperatureIterationFailed{false}; /*!< \brief The last temperature-from-enthalpy iteration did not converge. */ + double minTemperature{0.0}; /*!< \brief Lower temperature bound of the Newton iteration. */ + double maxTemperature{0.0}; /*!< \brief Upper temperature bound of the Newton iteration. */ + double temperatureGuess{0.0}; /*!< \brief Start of the next temperature iteration, 0 if unset. */ + const std::shared_ptr sol; /*!< \brief Object needed to describe a chemically-reacting solution*/ + std::vector speciesIndices; /*!< \brief Mechanism index of each transported species. */ + std::vector chemicalSourceTerm; /*!< \brief Chemical source term of the transported species. */ + std::vector chemicalSourceJacobian; /*!< \brief Diagonal sink Jacobian of the transported species. */ + std::vector enthalpyFormation; /*!< \brief Enthalpy of formation of the transported species. */ + const std::vector molarMasses; /*!< \brief Molar masses of all mechanism species. */ + std::vector massFractions; /*!< \brief Mass fractions of all mechanism species. */ + std::vector netProductionRates; /*!< \brief Net production rates of all mechanism species. */ + std::vector destructionRates; /*!< \brief Destruction rates of all mechanism species. */ + mutable std::vector massDiffusivity; /*!< \brief Mass diffusivity of all mechanism species. */ + mutable std::vector enthalpiesSpecies; /*!< \brief Molar enthalpies of all mechanism species. */ + mutable std::vector specificHeatSpecies; /*!< \brief Molar heat capacities of all mechanism species. */ + mutable su2double laminarViscosity{0.0}; /*!< \brief Laminar viscosity at the current state. */ + mutable su2double laminarConductivity{0.0}; /*!< \brief Laminar thermal conductivity at the current state. */ + mutable bool transportValid{false}; /*!< \brief Viscosity and conductivity match the current state. */ + mutable bool diffusivityValid{false}; /*!< \brief Mass diffusivities match the current state. */ + + /*! + * \brief Set enthalpies of formation. + */ + void SetEnthalpyFormation(const CConfig* config); + + /*! + * \brief Set species mass fraction array for Cantera. + * \param[in] val_scalars - Scalar mass fractions. + */ + void SetMassFractions(const su2double* val_scalars); + + /*! + * \brief Set the thermodynamic state at the given temperature and the current composition. + */ + void SetThermoState(double val_temperature); + + /*! + * \brief Evaluate viscosity and conductivity of the current state when they are out of date. + */ + void UpdateTransport() const; + + /*! + * \brief Evaluate the mass diffusivities of the current state when they are out of date. + */ + void UpdateDiffusivity() const; +#endif + + public: + /*! + * \brief Constructor of the class. + */ + CFluidCantera(su2double val_operating_pressure, const CConfig* config); + +#ifdef HAVE_CANTERA + /*! + * \brief Get fluid laminar viscosity. + */ + inline su2double GetLaminarViscosity() override { + UpdateTransport(); + return laminarViscosity; + } + + /*! + * \brief Get fluid thermal conductivity. + */ + inline su2double GetThermalConductivity() override { + UpdateTransport(); + return laminarConductivity + Mu_Turb * Cp / Prandtl_Turb_Number; + } + + /*! + * \brief Get fluid mass diffusivity. + * \param[in] ivar - index of species. + */ + inline su2double GetMassDiffusivity(int ivar) override { + UpdateDiffusivity(); + return massDiffusivity[speciesIndices[ivar]]; + } + + /*! + * \brief Compute chemical source term and heat release from the current state, zero below the minimum temperature. + */ + void ComputeChemicalSourceTerm() override; + + /*! + * \brief Get Chemical source term species. + * \param[in] ivar - index of species. + */ + inline su2double GetChemicalSourceTerm(int ivar) override { return chemicalSourceTerm[ivar]; } + + /*! + * \brief Get the derivative of the chemical source term with respect to the own density-weighted mass fraction + * (always <= 0). + * \param[in] ivar - index of species. + */ + inline su2double GetChemicalSourceJacobian(int ivar) override { return chemicalSourceJacobian[ivar]; } + + /*! + * \brief Get Heat release due to combustion. + */ + inline su2double GetHeatRelease() override { return Heat_Release; } + + /*! + * \brief Whether the last state evaluation failed to converge or needed a temperature far outside the data range. + */ + inline bool GetTemperatureIterationFailed() const override { return temperatureIterationFailed; } + + /*! + * \brief Get enthalpy diffusivity terms. + */ + void GetEnthalpyDiffusivity(su2double* enthalpy_diffusions) const override; + + /*! + * \brief Get gradient enthalpy diffusivity terms. + */ + void GetGradEnthalpyDiffusivity(su2double* grad_enthalpy_diffusions) const override; + + /*! + * \brief Set the Dimensionless State using Temperature. + * \param[in] val_temperature - Temperature value at the point. + * \param[in] val_scalars - Scalar mass fractions. + */ + void SetTDState_T(su2double val_temperature, const su2double* val_scalars) override; + + /*! + * \brief Set the state from enthalpy by Newton iteration on temperature. + * \param[in] val_enthalpy - Enthalpy value at the point. + * \param[in] val_scalars - Scalar mass fractions. + */ + void SetTDState_h(su2double val_enthalpy, const su2double* val_scalars) override; + + /*! + * \brief Set the start of the next temperature iteration of SetTDState_h, used once. + * \param[in] val_temperature - Temperature guess. + */ + inline void SetTemperatureGuess(su2double val_temperature) override { + temperatureGuess = SU2_TYPE::GetValue(val_temperature); + } +#endif +}; diff --git a/SU2_CFD/include/fluid/CFluidModel.hpp b/SU2_CFD/include/fluid/CFluidModel.hpp index 0e0c2e624bd0..6537e94649a2 100644 --- a/SU2_CFD/include/fluid/CFluidModel.hpp +++ b/SU2_CFD/include/fluid/CFluidModel.hpp @@ -192,6 +192,33 @@ class CFluidModel { return mass_diffusivity; } + /*! + * \brief Compute chemical source term for species. + */ + virtual void ComputeChemicalSourceTerm() {}; + + /*! + * \brief Get Chemical source term species. + * \param[in] iVar - index of species. + */ + inline virtual su2double GetChemicalSourceTerm(int iVar) { return 0.0; } + + /*! + * \brief Get the derivative of the chemical source term of a species with respect to its own mass fraction. + * \param[in] iVar - index of species. + */ + inline virtual su2double GetChemicalSourceJacobian(int iVar) { return 0.0; } + + /*! + * \brief Get Heat release due to combustion. + */ + inline virtual su2double GetHeatRelease() { return 0.0; } + + /*! + * \brief Whether the last temperature-from-enthalpy iteration failed, so the state is non-physical. + */ + inline virtual bool GetTemperatureIterationFailed() const { return false; } + /*! * \brief Get the enthalpy diffusivity terms for all species being solved. * @@ -376,6 +403,12 @@ class CFluidModel { */ virtual void SetTDState_h(su2double val_enthalpy, const su2double* val_scalars = nullptr) {} + /*! + * \brief Virtual member. Initial temperature for the next iterative SetTDState_h call. + * \param[in] val_temperature - Temperature guess, e.g. the previous value at the point. + */ + virtual void SetTemperatureGuess(su2double val_temperature) {} + /*! * \brief Set fluid eddy viscosity provided by a turbulence model needed for computing effective thermal conductivity. */ diff --git a/SU2_CFD/include/integration/CMultiGridIntegration.hpp b/SU2_CFD/include/integration/CMultiGridIntegration.hpp index d73b4962128f..fbabb5c35867 100644 --- a/SU2_CFD/include/integration/CMultiGridIntegration.hpp +++ b/SU2_CFD/include/integration/CMultiGridIntegration.hpp @@ -325,6 +325,7 @@ class CMultiGridIntegration final : public CIntegration { * independently of the CSysMatrix limit of the same name. ---*/ static constexpr unsigned short MAXNVAR = 25; static constexpr unsigned short MAXNDIM = 3; + static_assert(MAXNVAR >= MAX_TRANSPORTED_SPECIES, "MAXNVAR (size of the multigrid transfer buffers) must be at least MAX_TRANSPORTED_SPECIES."); /*--- Early-exit smoothing state (shared across OMP threads via master write + barrier). ---*/ bool mg_early_exit_flag = false; /*!< \brief Shared flag for early exit across OMP threads. */ diff --git a/SU2_CFD/include/limiters/computeLimiters_impl.hpp b/SU2_CFD/include/limiters/computeLimiters_impl.hpp index 3b19f241f3bc..b53b49018e00 100644 --- a/SU2_CFD/include/limiters/computeLimiters_impl.hpp +++ b/SU2_CFD/include/limiters/computeLimiters_impl.hpp @@ -79,6 +79,7 @@ void computeLimiters_impl(CSolver* solver, FieldType& limiter) { constexpr size_t MAXNVAR = 32; + static_assert(MAXNVAR >= MAX_TRANSPORTED_SPECIES, "MAXNVAR (size of the limiter work arrays) must be at least MAX_TRANSPORTED_SPECIES."); if (varEnd > MAXNVAR) SU2_MPI::Error("Number of variables is too large, increase MAXNVAR.", CURRENT_FUNCTION); diff --git a/SU2_CFD/include/numerics/CNumerics.hpp b/SU2_CFD/include/numerics/CNumerics.hpp index ec511a3fa1db..686065f817af 100644 --- a/SU2_CFD/include/numerics/CNumerics.hpp +++ b/SU2_CFD/include/numerics/CNumerics.hpp @@ -62,7 +62,9 @@ class CNumerics { const su2double delta [3][3] = {{1.0, 0.0, 0.0},{0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}}; /*!< \brief Identity matrix. */ const su2double *Diffusion_Coeff_i, /*!< \brief Species diffusion coefficients at point i. */ - *Diffusion_Coeff_j; /*!< \brief Species diffusion coefficients at point j. */ + *Diffusion_Coeff_j, /*!< \brief Species diffusion coefficients at point j. */ + *Chemical_Source_Term_i, /*!< \brief Chemical source terms of the species at the point. */ + *Chemical_Source_Jacobian_i; /*!< \brief Derivative of the chemical source terms w.r.t. the conserved species variables. */ su2double Laminar_Viscosity_i, /*!< \brief Laminar viscosity at point i. */ Laminar_Viscosity_j; /*!< \brief Laminar viscosity at point j. */ @@ -830,6 +832,18 @@ class CNumerics { Diffusion_Coeff_j = val_diffusioncoeff_j; } + /*! + * \brief Set the chemical source term of the species at the point. + * \param[in] val_source_term - Chemical source terms. + */ + inline void SetChemicalSourceTerm(const su2double* val_source_term) { Chemical_Source_Term_i = val_source_term; } + + /*! + * \brief Set the diagonal Jacobian of the chemical source term w.r.t. the density-weighted species variables. + * \param[in] val_jacobian - Derivative of each source term w.r.t. its own species variable. + */ + inline void SetChemicalSourceJacobian(const su2double* val_jacobian) { Chemical_Source_Jacobian_i = val_jacobian; } + /*! * \brief Set the heat flux due to enthalpy diffusion * \param[in] val_heatfluxdiffusion - Value of the heat flux due to enthalpy diffusion. diff --git a/SU2_CFD/include/numerics/scalar/scalar_edge_flux.hpp b/SU2_CFD/include/numerics/scalar/scalar_edge_flux.hpp index 4d2e79608bdf..b977b010b746 100644 --- a/SU2_CFD/include/numerics/scalar/scalar_edge_flux.hpp +++ b/SU2_CFD/include/numerics/scalar/scalar_edge_flux.hpp @@ -237,6 +237,8 @@ class CAvgGradScalarBase { self->coefficientJacobians(opt, D, projGrad, res); } + self->diffusionCorrection(opt, iPoint, side_i, jPoint, side_j, rho, projGrad, proj_on_w_i, proj_on_w_j, res); + self->extraDiffusionTerms(idx, opt, iPoint, side_i, jPoint, side_j, rho, normal, vector_ij, res); } @@ -246,6 +248,13 @@ class CAvgGradScalarBase { template FORCEINLINE void coefficientJacobians(Ts&&...) const {} + /*! + * \brief Flux that couples the equations through the projected gradients, with the Jacobians + * w.r.t. the conserved variables. + */ + template + FORCEINLINE void diffusionCorrection(Ts&&...) const {} + /*! * \brief Diffusion of a model that transports more than one gradient, of states it * synthesises from its own containers. @@ -368,7 +377,8 @@ class CUpwScalarBase : public CUpwScalarFlux Size) { - SU2_MPI::Error("Static arrays are too small for the requested equation count.", CURRENT_FUNCTION); + SU2_MPI::Error("The number of equations exceeds MAX_TRANSPORTED_SPECIES, the size of the static arrays. Increase it\n" + "in Common/include/option_structure.hpp and recompile.", CURRENT_FUNCTION); } } diff --git a/SU2_CFD/include/numerics/species/species_edge_flux.hpp b/SU2_CFD/include/numerics/species/species_edge_flux.hpp index e744d8c96553..a1afe5ca88f9 100644 --- a/SU2_CFD/include/numerics/species/species_edge_flux.hpp +++ b/SU2_CFD/include/numerics/species/species_edge_flux.hpp @@ -55,8 +55,61 @@ class CScalarFluxSpeciesBase : public CUpwScalarBase + FORCEINLINE void diffusionCorrection(const ScalarFluxOptions& opt, Int iPoint, const EdgeSide& side_i, + Int jPoint, const EdgeSide& side_j, const CPair& rho, + const Vector& projGrad, const Double& proj_on_w_i, + const Double& proj_on_w_j, EdgeResidual& res) const { + if (!correctionVelocity) return; + + const size_t nEqn = this->nEqn; + auto faceDiffusivity = [&](size_t iVar) { + const Double D_i = gatherVariables(iPoint, side_i.scalarNodes.GetDiffusivity(), iVar); + const Double D_j = gatherVariables(jPoint, side_j.scalarNodes.GetDiffusivity(), iVar); + return 0.5 * (rho.i * D_i + rho.j * D_j); + }; + + /*--- Difference of each species to the remainder, and the face mass flux of the correction. ---*/ + const Double D_last = faceDiffusivity(nEqn); + Vector c; + Double massFlux = 0.0; + for (size_t iVar = 0; iVar < nEqn; ++iVar) { + c(iVar) = faceDiffusivity(iVar) - D_last; + massFlux += c(iVar) * projGrad(iVar); + } + + for (size_t iVar = 0; iVar < nEqn; ++iVar) { + const Double Y_i = gatherVariables(iPoint, side_i.scalarNodes.GetSolution(), iVar); + const Double Y_j = gatherVariables(jPoint, side_j.scalarNodes.GetSolution(), iVar); + const Double Y_face = 0.5 * (Y_i + Y_j); + + const Double flux = Y_face * massFlux; + res.flux_i(iVar) += flux; + if (!opt.oneSided) res.flux_j(iVar) -= flux; + + if (opt.implicit) { + for (size_t jVar = 0; jVar < nEqn; ++jVar) { + const Double coeff = Y_face * c(jVar); + res.jac_ii(iVar, jVar) -= coeff * proj_on_w_i; + if (!opt.oneSided) { + res.jac_ij(iVar, jVar) += coeff * proj_on_w_j; + res.jac_ji(iVar, jVar) += coeff * proj_on_w_i; + res.jac_jj(iVar, jVar) -= coeff * proj_on_w_j; + } + } + } + } + } + /*! * \brief Diffusion coefficients, an i/j average of (rho * mass diffusivity) per species, plus a * turbulent (mu_t/Sc_t) contribution shared by every species, when a turbulence model is @@ -95,6 +148,7 @@ class CScalarFluxSpeciesBase : public CUpwScalarBase ComputeResidual(const CConfig* config) override; }; + +/*! + * \class CSourceCombustion_Species + * \brief Class for source term for combustion problems. + * \ingroup SourceDiscr + * \author C.Morales Ubal + */ +class CSourceCombustion_Species : public CSourceBase_Species { + const bool chemical_jacobian; /*!< \brief Add the diagonal chemical sink Jacobian to the implicit system. */ + + public: + /*! + * \brief Constructor of the class. + * \param[in] val_nDim - Number of dimensions of the problem. + * \param[in] val_nVar - Number of variables of the problem. + * \param[in] config - Definition of the particular problem. + */ + CSourceCombustion_Species(unsigned short val_nDim, unsigned short val_nVar, const CConfig* config); + + /*! + * \brief Residual of the combustion source term. + * \param[in] config - Definition of the particular problem. + * \return Lightweight const-view of residual and Jacobian. + */ + ResidualType<> ComputeResidual(const CConfig* config) override; + +}; diff --git a/SU2_CFD/include/numerics/util.hpp b/SU2_CFD/include/numerics/util.hpp index 92e5144b9c93..cb617e5dd22c 100644 --- a/SU2_CFD/include/numerics/util.hpp +++ b/SU2_CFD/include/numerics/util.hpp @@ -156,7 +156,7 @@ constexpr size_t Dynamic = size_t(-1); * \note The scalar numerics cap the equation count at this value and error above it, so a * configuration that fits them fits these kernels. */ -constexpr size_t MaxScalarVar = 8; +constexpr size_t MaxScalarVar = MAX_TRANSPORTED_SPECIES; /*! * \brief Residual of one edge, accumulated by the convective and the diffusive terms. @@ -179,10 +179,17 @@ struct EdgeResidual { /*! * \brief Zero the terms of the equations in use, so that both terms can accumulate into them. - * \note A static model zeroes its whole storage with constant trip counts; a dynamic one - * zeroes the leading nVar rows and columns and leaves the rest of the backing untouched. + * \note The storage is constructed uninitialized, so that a dynamic model, whose backing is + * sized for the maximum, only pays for zeroing the leading nVar rows and columns. */ - FORCEINLINE explicit EdgeResidual(size_t nEqn) : nVar(nEqn) { + FORCEINLINE explicit EdgeResidual(size_t nEqn) + : flux_i(C2DUninitialized{}), + flux_j(C2DUninitialized{}), + jac_ii(C2DUninitialized{}), + jac_ij(C2DUninitialized{}), + jac_ji(C2DUninitialized{}), + jac_jj(C2DUninitialized{}), + nVar(nEqn) { for (size_t iVar = 0; iVar < nVar; ++iVar) { flux_i(iVar) = 0.0; flux_j(iVar) = 0.0; diff --git a/SU2_CFD/include/solvers/CIncEulerSolver.hpp b/SU2_CFD/include/solvers/CIncEulerSolver.hpp index fd5fbf986092..1e3e5a2c7d31 100644 --- a/SU2_CFD/include/solvers/CIncEulerSolver.hpp +++ b/SU2_CFD/include/solvers/CIncEulerSolver.hpp @@ -246,8 +246,9 @@ class CIncEulerSolver : public CFVMFlowSolverBaseGetFlameletParsedOptions().SparkActive(config->GetIgnitionIter()); + } + + /*! + * \brief Whether a point lies inside the artificial ignition spark region. + * \param[in] config - Definition of the particular problem. + * \param[in] coord - Coordinates of the point. + */ + bool InSpark(const CConfig* config, const su2double* coord) const { + return config->GetFlameletParsedOptions().InSpark(nDim, coord); + } + /*! * \brief Allow outside access to the nodes of the solver, containing conservatives, primitives, etc. * \return Nodes of the solver. diff --git a/SU2_CFD/include/solvers/CSpeciesFlameletSolver.hpp b/SU2_CFD/include/solvers/CSpeciesFlameletSolver.hpp index 41bbf28c3115..ce4f323fdb65 100644 --- a/SU2_CFD/include/solvers/CSpeciesFlameletSolver.hpp +++ b/SU2_CFD/include/solvers/CSpeciesFlameletSolver.hpp @@ -37,10 +37,10 @@ */ class CSpeciesFlameletSolver final : public CSpeciesSolver { private: + FluidFlamelet_ParsedOptions flamelet_config_options; const su2double default_flame_thickness{1.0}; su2double global_flame_thickness; bool calc_flame_thickness{false}; - FluidFlamelet_ParsedOptions flamelet_config_options; bool include_mixture_fraction = false; /*!< \brief include mixture fraction as a controlling variable. */ /*!< \brief Number of points outside the manifold domain, shared across OpenMP threads so it can be * accumulated atomically and reduced by the master thread alone in Preprocessing. */ diff --git a/SU2_CFD/include/variables/CIncNSVariable.hpp b/SU2_CFD/include/variables/CIncNSVariable.hpp index bcc5e56be907..d24ed90a5bad 100644 --- a/SU2_CFD/include/variables/CIncNSVariable.hpp +++ b/SU2_CFD/include/variables/CIncNSVariable.hpp @@ -42,6 +42,7 @@ class CIncNSVariable final : public CIncEulerVariable { VectorType DES_LengthScale; /*!< \brief DES Length Scale. */ VectorType lesMode; /*!< \brief Sensor for local simulation mode (0=RANS, 1=LES).*/ const bool Energy; /*!< \brief Flag for Energy equation in incompressible flows. */ + MatrixType MassDiffusivity; /*!< \brief (nPoint, nSpecies+1) laminar mass diffusivities, evaluated by the fluid model (Cantera). */ public: /*! @@ -57,6 +58,23 @@ class CIncNSVariable final : public CIncEulerVariable { CIncNSVariable(su2double pressure, const su2double *velocity, su2double temperature, unsigned long npoint, unsigned long ndim, unsigned long nvar, const CConfig *config); + /*! + * \brief Store the laminar mass diffusivity of a species, which the species solver reads. + * \param[in] iPoint - Point index. + * \param[in] val_diffusivity - Mass diffusivity. + * \param[in] val_ivar - Species index, the remainder species included. + */ + inline void SetMassDiffusivity(unsigned long iPoint, su2double val_diffusivity, unsigned short val_ivar) { + MassDiffusivity(iPoint, val_ivar) = val_diffusivity; + } + + /*! + * \brief Get the laminar mass diffusivity of a species stored by the flow solver. + */ + inline su2double GetDiffusivity(unsigned long iPoint, unsigned short val_ivar) const override { + return MassDiffusivity(iPoint, val_ivar); + } + /*! * \brief Set the laminar viscosity. */ diff --git a/SU2_CFD/include/variables/CSpeciesVariable.hpp b/SU2_CFD/include/variables/CSpeciesVariable.hpp index 76db98d85a9b..8e45f5e8313b 100644 --- a/SU2_CFD/include/variables/CSpeciesVariable.hpp +++ b/SU2_CFD/include/variables/CSpeciesVariable.hpp @@ -36,9 +36,12 @@ class CSpeciesVariable : public CScalarVariable { protected: MatrixType Diffusivity; /*!< \brief Matrix (nPoint,nVar) of mass diffusivities for scalar transport. */ + MatrixType SpeciesSourceTerm; /*!< \brief Matrix (nPoint, nVar) of chemical source terms for species transport*/ + MatrixType SpeciesSourceJacobian; /*!< \brief Matrix (nPoint, nVar) of the diagonal chemical source Jacobian. */ + VectorType HeatRelease; /*!< \brief Vector of heat release due to combustion for species transport*/ public: - static constexpr size_t MAXNVAR = 20; /*!< \brief Max number of variables for static arrays. Increase, if necessary. */ + static constexpr size_t MAXNVAR = MAX_TRANSPORTED_SPECIES; /*!< \brief Max number of variables for static arrays. */ /*! * \brief Constructor of the class. @@ -75,6 +78,58 @@ class CSpeciesVariable : public CScalarVariable { */ inline const su2double* GetDiffusivity(unsigned long iPoint) const { return Diffusivity[iPoint]; } + /*! + * \brief Set the value of the chemical source term for species transport + * \param[in] val_sourceTerm - the chemical source term. + * \param[in] val_ivar - eqn. index to the chemical source term. + */ + inline void SetChemicalSourceTerm(unsigned long iPoint, su2double val_sourceTerm, unsigned short val_ivar) { + SpeciesSourceTerm(iPoint, val_ivar) = val_sourceTerm; + } + + /*! + * \brief Get the value of the chemical source term for species transport + * \param[in] val_ivar - eqn. index to the chemical source term. + * \return Value of the chemical source term. + */ + inline su2double GetChemicalSourceTerm(unsigned long iPoint, unsigned short val_ivar) const { + return SpeciesSourceTerm(iPoint, val_ivar); + } + + /*! + * \brief Get the value of the chemical source term for species transport + * \return Pointer to the chemical source term + */ + inline const su2double* GetChemicalSourceTerm(unsigned long iPoint) const { return SpeciesSourceTerm[iPoint]; } + + /*! + * \brief Set the derivative of the chemical source term of a species with respect to its own mass fraction. + * \param[in] val_jacobian - the derivative. + * \param[in] val_ivar - species index. + */ + inline void SetChemicalSourceJacobian(unsigned long iPoint, su2double val_jacobian, unsigned short val_ivar) { + SpeciesSourceJacobian(iPoint, val_ivar) = val_jacobian; + } + + /*! + * \brief Get the diagonal chemical source Jacobian of a point. + * \return Pointer to the derivatives of the species source terms with respect to their own mass fractions. + */ + inline const su2double* GetChemicalSourceJacobian(unsigned long iPoint) const { return SpeciesSourceJacobian[iPoint]; } + + /*! + * \brief Get heat release due to combustion + * \param[in] iPoint - Point index. + * \return Value of the heat release due to combustion. + */ + inline su2double GetHeatRelease(unsigned long iPoint) const { return HeatRelease(iPoint); } + + /*! + * \brief Set heat release due to combustion. + * \param[in] iPoint - Point index. + */ + inline void SetHeatRelease(unsigned long iPoint, su2double val_heatRelease) { HeatRelease(iPoint) = val_heatRelease; } + /*! * \brief Get the mass diffusivity container, for a per-species gather by point and equation. */ diff --git a/SU2_CFD/include/variables/CVariable.hpp b/SU2_CFD/include/variables/CVariable.hpp index 739a1af75fa5..5fdad211e4dc 100644 --- a/SU2_CFD/include/variables/CVariable.hpp +++ b/SU2_CFD/include/variables/CVariable.hpp @@ -1172,6 +1172,20 @@ class CVariable { */ inline virtual su2double GetDiffusivity(unsigned long iPoint, unsigned short val_ivar) const { return 0.0; } + /*! + * \brief A virtual member. + * \param[in] iPoint - Point index. + * \return Value of the chemical source term. + */ + inline virtual su2double GetChemicalSourceTerm(unsigned long iPoint, unsigned short val_ivar) const { return 0.0; } + + /*! + * \brief A virtual member. + * \param[in] iPoint - Point index. + * \return Value of the heat release due to combustion. + */ + inline virtual su2double GetHeatRelease(unsigned long iPoint) const { return 0.0; } + /*! * \brief A virtual member. * \param[in] iPoint - Point index. diff --git a/SU2_CFD/src/drivers/CDriver.cpp b/SU2_CFD/src/drivers/CDriver.cpp index 29fb325fa233..97565adee021 100644 --- a/SU2_CFD/src/drivers/CDriver.cpp +++ b/SU2_CFD/src/drivers/CDriver.cpp @@ -1389,7 +1389,11 @@ void CDriver::InstantiateSpeciesNumerics(unsigned short nVar_Species, int offset else { numerics[iMGlevel][SPECIES_SOL][source_first_term] = new CSourceNothing(nDim, nVar_Species, config); } - numerics[iMGlevel][SPECIES_SOL][source_second_term] = new CSourceNothing(nDim, nVar_Species, config); + if (config->GetCombustion()) { + numerics[iMGlevel][SPECIES_SOL][source_second_term] = new CSourceCombustion_Species(nDim, nVar_Species, config); + } else { + numerics[iMGlevel][SPECIES_SOL][source_second_term] = new CSourceNothing(nDim, nVar_Species, config); + } } } diff --git a/SU2_CFD/src/fluid/CDataDrivenFluid.cpp b/SU2_CFD/src/fluid/CDataDrivenFluid.cpp index 1de3c2ef1fc7..8bb25a552938 100644 --- a/SU2_CFD/src/fluid/CDataDrivenFluid.cpp +++ b/SU2_CFD/src/fluid/CDataDrivenFluid.cpp @@ -471,4 +471,4 @@ size_t MiniTable2D::FindNode(const size_t iX, const su2double val_x, const size_ } } return iMin; -} \ No newline at end of file +} diff --git a/SU2_CFD/src/fluid/CFluidCantera.cpp b/SU2_CFD/src/fluid/CFluidCantera.cpp new file mode 100644 index 000000000000..aea94ca6bbf4 --- /dev/null +++ b/SU2_CFD/src/fluid/CFluidCantera.cpp @@ -0,0 +1,278 @@ +/*! + * \file CFluidCantera.cpp + * \brief Defines the multicomponent incompressible Ideal Gas model for reacting flows. + * \author T. Economon, Cristopher Morales Ubal + * \version 8.5.0 "Harrier" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2026, SU2 Contributors (cf. AUTHORS.md) + * + * SU2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * SU2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with SU2. If not, see . + */ + +#include "../../include/fluid/CFluidCantera.hpp" + +#include +#include + +#ifdef HAVE_CANTERA +#include +#include + +using namespace Cantera; + +namespace { +std::shared_ptr LoadSolution(const CConfig* config) { + try { + return newSolution(config->GetChemical_MechanismFile(), config->GetPhase_Name(), config->GetTransport_Model()); + } catch (const CanteraError& error) { + SU2_MPI::Error(string("Cantera error during initialization: ") + error.what(), CURRENT_FUNCTION); + return nullptr; + } +} + +std::vector GetMolarMasses(Solution& solution) { + std::vector molarMasses(solution.thermo()->nSpecies()); + solution.thermo()->getMolecularWeights(molarMasses.data()); + return molarMasses; +} +} // namespace + +CFluidCantera::CFluidCantera(su2double value_pressure_operating, const CConfig* config) + : CFluidModel(), + n_species_mixture(config->GetnSpecies() + 1), + Pressure_Thermodynamic(value_pressure_operating), + Prandtl_Turb_Number(config->GetPrandtl_Turb()), + Schmidt_Turb_Number(config->GetSchmidt_Number_Turbulent()), + Transport_Model(config->GetTransport_Model()), + Chemical_MechanismFile(config->GetChemical_MechanismFile()), + Phase_Name(config->GetPhase_Name()), + Combustion(config->GetCombustion()), + Chemistry_Min_Temperature(config->GetCantera_DC_Min_Temp()), + Correction_Velocity(config->GetCantera_Correction_Velocity()), + sol(LoadSolution(config)), + molarMasses(GetMolarMasses(*sol)) { + try { + const auto& thermo = *sol->thermo(); + const size_t nSpeciesMechanism = thermo.nSpecies(); + + massFractions.assign(nSpeciesMechanism, 0.0); + massDiffusivity.assign(nSpeciesMechanism, 0.0); + netProductionRates.assign(nSpeciesMechanism, 0.0); + destructionRates.assign(nSpeciesMechanism, 0.0); + enthalpiesSpecies.assign(nSpeciesMechanism, 0.0); + specificHeatSpecies.assign(nSpeciesMechanism, 0.0); + /*--- Moderate extrapolation of the thermodynamic polynomials is allowed beyond the range of the data. ---*/ + minTemperature = 0.5 * thermo.minTemp(); + maxTemperature = 2.0 * thermo.maxTemp(); + + speciesIndices.resize(n_species_mixture); + for (size_t iVar = 0; iVar < n_species_mixture; iVar++) { + const string name = config->GetChemical_GasComposition(iVar); + const size_t index = thermo.speciesIndex(name); + if (index == npos) { + SU2_MPI::Error("Species '" + name + "' of CANTERA_SPECIES_NAMES is not part of phase '" + Phase_Name + + "' in " + Chemical_MechanismFile + ".", + CURRENT_FUNCTION); + } + if (std::find(speciesIndices.begin(), speciesIndices.begin() + iVar, index) != speciesIndices.begin() + iVar) { + SU2_MPI::Error("Species '" + name + "' appears more than once in CANTERA_SPECIES_NAMES.", CURRENT_FUNCTION); + } + speciesIndices[iVar] = index; + } + + chemicalSourceTerm.assign(n_species_mixture, 0.0); + chemicalSourceJacobian.assign(n_species_mixture, 0.0); + enthalpyFormation.assign(n_species_mixture, 0.0); + if (Combustion) SetEnthalpyFormation(config); + } catch (const CanteraError& error) { + SU2_MPI::Error(string("Cantera error during initialization: ") + error.what(), CURRENT_FUNCTION); + } +} + +void CFluidCantera::SetEnthalpyFormation(const CConfig* config) { + SetMassFractions(config->GetSpecies_Init()); + sol->thermo()->setMassFractions(massFractions.data()); + sol->thermo()->setState_TP(STD_REF_TEMP, SU2_TYPE::GetValue(Pressure_Thermodynamic)); + sol->thermo()->getEnthalpy_RT_ref(enthalpiesSpecies.data()); + for (size_t iVar = 0; iVar < n_species_mixture; iVar++) { + const size_t k = speciesIndices[iVar]; + enthalpyFormation[iVar] = GasConstant * STD_REF_TEMP * enthalpiesSpecies[k] / molarMasses[k]; + } +} + +void CFluidCantera::ComputeChemicalSourceTerm() { + if (Temperature < Chemistry_Min_Temperature) { + std::fill(chemicalSourceTerm.begin(), chemicalSourceTerm.end(), 0.0); + std::fill(chemicalSourceJacobian.begin(), chemicalSourceJacobian.end(), 0.0); + Heat_Release = 0.0; + return; + } + try { + sol->kinetics()->getNetProductionRates(netProductionRates.data()); + sol->kinetics()->getDestructionRates(destructionRates.data()); + } catch (const CanteraError& error) { + SU2_MPI::Error(string("Cantera error in the kinetics evaluation: ") + error.what(), CURRENT_FUNCTION); + } + Heat_Release = 0.0; + for (size_t iVar = 0; iVar < n_species_mixture; iVar++) { + const size_t k = speciesIndices[iVar]; + chemicalSourceTerm[iVar] = molarMasses[k] * netProductionRates[k]; + /*--- The destruction rate is taken proportional to the mass fraction; the derivative is w.r.t. rho*Y. ---*/ + chemicalSourceJacobian[iVar] = + -molarMasses[k] * destructionRates[k] / (Density * std::max(massFractions[k], 1e-10)); + Heat_Release -= enthalpyFormation[iVar] * chemicalSourceTerm[iVar]; + } +} + +void CFluidCantera::GetEnthalpyDiffusivity(su2double* enthalpy_diffusions) const { + UpdateDiffusivity(); + sol->thermo()->getEnthalpy_RT_ref(enthalpiesSpecies.data()); + const su2double RT = GasConstant * Temperature; + const size_t last = speciesIndices[n_species_mixture - 1]; + /*--- With the correction velocity every species enthalpy is taken relative to the mixture enthalpy. ---*/ + const su2double h_ref = Correction_Velocity ? Enthalpy : su2double(0.0); + const su2double h_last = RT * enthalpiesSpecies[last] / molarMasses[last] - h_ref; + for (size_t iVar = 0; iVar < n_species_mixture - 1; iVar++) { + const size_t k = speciesIndices[iVar]; + const su2double h_k = RT * enthalpiesSpecies[k] / molarMasses[k] - h_ref; + enthalpy_diffusions[iVar] = Density * (h_k * massDiffusivity[k] - h_last * massDiffusivity[last]) + + Mu_Turb * (h_k - h_last) / Schmidt_Turb_Number; + } +} + +void CFluidCantera::GetGradEnthalpyDiffusivity(su2double* grad_enthalpy_diffusions) const { + UpdateDiffusivity(); + sol->thermo()->getCp_R_ref(specificHeatSpecies.data()); + const size_t last = speciesIndices[n_species_mixture - 1]; + const su2double cp_ref = Correction_Velocity ? Cp : su2double(0.0); + const su2double cp_last = GasConstant * specificHeatSpecies[last] / molarMasses[last] - cp_ref; + for (size_t iVar = 0; iVar < n_species_mixture - 1; iVar++) { + const size_t k = speciesIndices[iVar]; + const su2double cp_k = GasConstant * specificHeatSpecies[k] / molarMasses[k] - cp_ref; + grad_enthalpy_diffusions[iVar] = Density * (cp_k * massDiffusivity[k] - cp_last * massDiffusivity[last]) + + Mu_Turb * (cp_k - cp_last) / Schmidt_Turb_Number; + } +} + +void CFluidCantera::SetMassFractions(const su2double* val_scalars) { + double scalarsSum = 0.0; + std::fill(massFractions.begin(), massFractions.end(), 0.0); + for (size_t iScalar = 0; iScalar < n_species_mixture - 1; iScalar++) { + const double value = SU2_TYPE::GetValue(val_scalars[iScalar]); + massFractions[speciesIndices[iScalar]] = value; + scalarsSum += value; + } + massFractions[speciesIndices[n_species_mixture - 1]] = 1.0 - scalarsSum; +} + +void CFluidCantera::SetThermoState(double val_temperature) { + auto& thermo = *sol->thermo(); + thermo.setState_TP(val_temperature, SU2_TYPE::GetValue(Pressure_Thermodynamic)); + Temperature = val_temperature; + Density = thermo.density(); + Enthalpy = thermo.enthalpy_mass(); + Cp = thermo.cp_mass(); + Cv = thermo.cv_mass(); + transportValid = false; + diffusivityValid = false; +} + +void CFluidCantera::UpdateTransport() const { + if (transportValid) return; + try { + laminarViscosity = sol->transport()->viscosity(); + laminarConductivity = sol->transport()->thermalConductivity(); + } catch (const CanteraError& error) { + SU2_MPI::Error(string("Cantera error in the transport evaluation: ") + error.what(), CURRENT_FUNCTION); + } + transportValid = true; +} + +void CFluidCantera::UpdateDiffusivity() const { + if (diffusivityValid) return; + try { + sol->transport()->getMixDiffCoeffsMass(massDiffusivity.data()); + } catch (const CanteraError& error) { + SU2_MPI::Error(string("Cantera error in the diffusivity evaluation: ") + error.what(), CURRENT_FUNCTION); + } + diffusivityValid = true; +} + +void CFluidCantera::SetTDState_T(const su2double val_temperature, const su2double* val_scalars) { + SetMassFractions(val_scalars); + try { + sol->thermo()->setMassFractions(massFractions.data()); + SetThermoState(SU2_TYPE::GetValue(val_temperature)); + } catch (const CanteraError& error) { + SU2_MPI::Error(string("Cantera error in SetTDState_T: ") + error.what(), CURRENT_FUNCTION); + } + temperatureIterationFailed = false; +} + +void CFluidCantera::SetTDState_h(const su2double val_enthalpy, const su2double* val_scalars) { + /*--- Temperature tolerance in [K], high accuracy needed for restarts. ---*/ + constexpr double tolerance = 1e-5; + constexpr int maxIterations = 20; + + SetMassFractions(val_scalars); + const double enthalpy = SU2_TYPE::GetValue(val_enthalpy); + double temperature = temperatureGuess; + temperatureGuess = 0.0; + bool converged = false; + + try { + sol->thermo()->setMassFractions(massFractions.data()); + + /*--- Without a guess, start from a single Newton step at the reference temperature. ---*/ + if (!(temperature > 0.0)) { + sol->thermo()->setState_TP(STD_REF_TEMP, SU2_TYPE::GetValue(Pressure_Thermodynamic)); + temperature = STD_REF_TEMP + (enthalpy - sol->thermo()->enthalpy_mass()) / sol->thermo()->cp_mass(); + } + temperature = std::clamp(temperature, minTemperature, maxTemperature); + + /*--- Newton-Raphson on temperature, kept inside the temperature bounds. ---*/ + for (int iter = 0; iter < maxIterations; iter++) { + sol->thermo()->setState_TP(temperature, SU2_TYPE::GetValue(Pressure_Thermodynamic)); + const double delta = (enthalpy - sol->thermo()->enthalpy_mass()) / sol->thermo()->cp_mass(); + if (!std::isfinite(delta)) break; + + const double next = std::clamp(temperature + delta, minTemperature, maxTemperature); + const bool inRange = (next == temperature + delta); + temperature = next; + if (!inRange) break; + if (std::abs(delta) <= tolerance) { + converged = true; + break; + } + } + SetThermoState(temperature); + } catch (const CanteraError& error) { + SU2_MPI::Error(string("Cantera error in SetTDState_h: ") + error.what(), CURRENT_FUNCTION); + } + temperatureIterationFailed = !converged; +} + +#else +CFluidCantera::CFluidCantera(su2double value_pressure_operating, const CConfig* config) { + SU2_MPI::Error( + "FLUID_CANTERA requires SU2 to be compiled with Cantera (-Denable-cantera=true). " + "Automatic differentiation builds do not support Cantera.", + CURRENT_FUNCTION); +} +#endif diff --git a/SU2_CFD/src/fluid/CFluidModel.cpp b/SU2_CFD/src/fluid/CFluidModel.cpp index a5139f3e557b..60296644d226 100644 --- a/SU2_CFD/src/fluid/CFluidModel.cpp +++ b/SU2_CFD/src/fluid/CFluidModel.cpp @@ -37,6 +37,7 @@ #include "../../include/fluid/CConstantSchmidt.hpp" #include "../../include/fluid/CConstantViscosity.hpp" #include "../../include/fluid/CFluidScalar.hpp" +#include "../../include/fluid/CFluidCantera.hpp" #include "../../include/fluid/CPolynomialConductivity.hpp" #include "../../include/fluid/CPolynomialConductivityRANS.hpp" #include "../../include/fluid/CPolynomialViscosity.hpp" @@ -62,6 +63,9 @@ unique_ptr CFluidModel::MakeLaminarViscosityModel(const CConfig case VISCOSITYMODEL::FLAMELET: /*--- Viscosity is obtained from the LUT ---*/ return nullptr; + case VISCOSITYMODEL::CANTERA: + /*--- Viscosity is obtained from the Cantera library---*/ + return nullptr; default: SU2_MPI::Error("Viscosity model not available.", CURRENT_FUNCTION); return nullptr; @@ -115,6 +119,9 @@ unique_ptr CFluidModel::MakeThermalConductivityModel(const C case CONDUCTIVITYMODEL::FLAMELET: /*--- Conductivity is obtained from the LUT ---*/ return nullptr; + case CONDUCTIVITYMODEL::CANTERA: + /*--- Conductivity is obtained from Cantera library---*/ + return nullptr; default: SU2_MPI::Error("Conductivity model not available.", CURRENT_FUNCTION); return nullptr; @@ -144,6 +151,10 @@ unique_ptr CFluidModel::MakeMassDiffusivityModel(const CConfi /*--- Diffusivity is obtained from the LUT ---*/ return nullptr; break; + case DIFFUSIVITYMODEL::CANTERA: + /*--- Diffusivity is obtained from CANTERA. Transport model option determines the diffusivity model---*/ + return nullptr; + break; default: SU2_MPI::Error("Diffusivity model not available.", CURRENT_FUNCTION); return nullptr; diff --git a/SU2_CFD/src/integration/CSingleGridIntegration.cpp b/SU2_CFD/src/integration/CSingleGridIntegration.cpp index 29c71b2ed360..7dca6641bce0 100644 --- a/SU2_CFD/src/integration/CSingleGridIntegration.cpp +++ b/SU2_CFD/src/integration/CSingleGridIntegration.cpp @@ -95,9 +95,9 @@ void CSingleGridIntegration::SingleGrid_Iteration(CGeometry ****geometry, CSolve SU2_OMP_SAFE_GLOBAL_ACCESS(solvers_fine[HEAT_SOL]->Heat_Fluxes(geometry_fine, solvers_fine, config[iZone]);) } - /*--- If turbulence model, copy the turbulence variables to the coarse levels ---*/ + /*--- Copy the turbulence or species variables to the coarse levels ---*/ - if (RunTime_EqSystem == RUNTIME_TURB_SYS) { + if (RunTime_EqSystem == RUNTIME_TURB_SYS || RunTime_EqSystem == RUNTIME_SPECIES_SYS) { for (unsigned short iMesh = FinestMesh; iMesh < config[iZone]->GetnMGLevels(); iMesh++) { @@ -108,12 +108,25 @@ void CSingleGridIntegration::SingleGrid_Iteration(CGeometry ****geometry, CSolve geometry[iZone][iInst][iMesh+1], config[iZone]); - SetRestricted_EddyVisc(RunTime_EqSystem, - solver_container[iZone][iInst][iMesh][Solver_Position], - solver_container[iZone][iInst][iMesh+1][Solver_Position], - geometry[iZone][iInst][iMesh], - geometry[iZone][iInst][iMesh+1], - config[iZone]); + if (RunTime_EqSystem == RUNTIME_SPECIES_SYS) { + /*--- The flow solver on the coarse level needs the species gradient for the enthalpy diffusion. ---*/ + CSolver* species_coarse = solver_container[iZone][iInst][iMesh+1][Solver_Position]; + CGeometry* geometry_coarse = geometry[iZone][iInst][iMesh+1]; + switch (config[iZone]->GetKind_Gradient_Method()) { + case GREEN_GAUSS: species_coarse->SetSolution_Gradient_GG(geometry_coarse, config[iZone], -1); break; + case WEIGHTED_LEAST_SQUARES: species_coarse->SetSolution_Gradient_LS(geometry_coarse, config[iZone], -1); break; + default: break; + } + } + + if (RunTime_EqSystem == RUNTIME_TURB_SYS) { + SetRestricted_EddyVisc(RunTime_EqSystem, + solver_container[iZone][iInst][iMesh][Solver_Position], + solver_container[iZone][iInst][iMesh+1][Solver_Position], + geometry[iZone][iInst][iMesh], + geometry[iZone][iInst][iMesh+1], + config[iZone]); + } } } diff --git a/SU2_CFD/src/interfaces/cht/CConjugateHeatInterface.cpp b/SU2_CFD/src/interfaces/cht/CConjugateHeatInterface.cpp index 3b5309131a01..be33c648eca2 100644 --- a/SU2_CFD/src/interfaces/cht/CConjugateHeatInterface.cpp +++ b/SU2_CFD/src/interfaces/cht/CConjugateHeatInterface.cpp @@ -99,6 +99,7 @@ void CConjugateHeatInterface::GetDonor_Variable(CSolver *donor_solution, CGeomet case CONDUCTIVITYMODEL::CONSTANT: case CONDUCTIVITYMODEL::FLAMELET: + case CONDUCTIVITYMODEL::CANTERA: case CONDUCTIVITYMODEL::COOLPROP: thermal_conductivity = thermal_conductivityND*donor_config->GetThermal_Conductivity_Ref(); break; diff --git a/SU2_CFD/src/meson.build b/SU2_CFD/src/meson.build index 8798e29f1869..f2458d8c87c2 100644 --- a/SU2_CFD/src/meson.build +++ b/SU2_CFD/src/meson.build @@ -13,7 +13,8 @@ su2_cfd_src += files(['fluid/CFluidModel.cpp', 'fluid/CNEMOGas.cpp', 'fluid/CMutationTCLib.cpp', 'fluid/CSU2TCLib.cpp', - 'fluid/CDataDrivenFluid.cpp']) + 'fluid/CDataDrivenFluid.cpp', + 'fluid/CFluidCantera.cpp']) su2_cfd_src += files(['output/COutputFactory.cpp', 'output/CAdjElasticityOutput.cpp', @@ -199,13 +200,19 @@ if get_option('enable-gprof') profiling_args = ['-pg','-no-pie'] endif + if get_option('enable-normal') + dependencies_default = [su2_deps, common_dep] + if get_option('enable-cantera') + dependencies_default += cantera_dep + endif su2_cfd_lib = static_library('SU2core', su2_cfd_src, install : false, - dependencies : [su2_deps, common_dep], + dependencies : dependencies_default, cpp_args: [default_warning_flags, su2_cpp_args]) su2_cfd_dep = declare_dependency(link_with: su2_cfd_lib, + dependencies: get_option('enable-cantera') ? [cantera_dep] : [], include_directories: su2_cfd_include) su2_cfd = executable('SU2_CFD', 'SU2_CFD.cpp', @@ -216,12 +223,15 @@ if get_option('enable-normal') link_args: profiling_args) endif +# Cantera works on plain doubles and is not differentiated, so the AD/DD builds compile without it. +cantera_off_args = ['-UHAVE_CANTERA'] + if get_option('enable-autodiff') su2_cfd_lib_ad = static_library('SU2core_AD', su2_cfd_src, install : false, dependencies : [su2_deps, codi_dep, commonAD_dep], - cpp_args: [default_warning_flags, su2_cpp_args, codi_rev_args]) + cpp_args: [default_warning_flags, su2_cpp_args, codi_rev_args, cantera_off_args]) su2_cfd_dep_ad = declare_dependency(link_with: su2_cfd_lib_ad, include_directories: su2_cfd_include) su2_cfd_ad = executable('SU2_CFD_AD', @@ -229,7 +239,7 @@ if get_option('enable-autodiff') install : true, install_rpath : '$ORIGIN/../lib', dependencies : [su2_cfd_dep_ad, su2_deps, codi_dep, commonAD_dep], - cpp_args: ['-fPIC'] + [default_warning_flags, su2_cpp_args, codi_rev_args] + profiling_args, + cpp_args: ['-fPIC'] + [default_warning_flags, su2_cpp_args, codi_rev_args, cantera_off_args] + profiling_args, link_args: profiling_args) endif @@ -238,7 +248,7 @@ if get_option('enable-directdiff') su2_cfd_src, install : false, dependencies : [su2_deps, codi_dep, commonDD_dep], - cpp_args: [default_warning_flags, su2_cpp_args, codi_for_args]) + cpp_args: [default_warning_flags, su2_cpp_args, codi_for_args, cantera_off_args]) su2_cfd_dep_dd = declare_dependency(link_with: su2_cfd_lib_dd, include_directories: su2_cfd_include) su2_cfd_dd = executable('SU2_CFD_DIRECTDIFF', @@ -246,7 +256,7 @@ if get_option('enable-directdiff') install : true, install_rpath : '$ORIGIN/../lib', dependencies : [su2_cfd_dep_dd, su2_deps, codi_dep, commonDD_dep], - cpp_args: ['-fPIC'] + [default_warning_flags, su2_cpp_args, codi_for_args] + profiling_args, + cpp_args: ['-fPIC'] + [default_warning_flags, su2_cpp_args, codi_for_args, cantera_off_args] + profiling_args, link_args: profiling_args) endif diff --git a/SU2_CFD/src/numerics/CNumerics.cpp b/SU2_CFD/src/numerics/CNumerics.cpp index bd8728e0e83f..6ec2831cd574 100644 --- a/SU2_CFD/src/numerics/CNumerics.cpp +++ b/SU2_CFD/src/numerics/CNumerics.cpp @@ -53,7 +53,8 @@ CNumerics::CNumerics(unsigned short val_nDim, unsigned short val_nVar, Gamma_Minus_One = Gamma - 1.0; Prandtl_Turb = config->GetPrandtl_Turb(); Gas_Constant = config->GetGas_ConstantND(); - energy_multicomponent = config->GetKind_FluidModel() == FLUID_MIXTURE && config->GetEnergy_Equation(); + energy_multicomponent = + config->GetMulticomponentFluid() && config->GetEnergy_Equation(); tau = new su2double* [nDim]; for (iDim = 0; iDim < nDim; iDim++) diff --git a/SU2_CFD/src/numerics/flow/flow_diffusion.cpp b/SU2_CFD/src/numerics/flow/flow_diffusion.cpp index 66945b2f700a..f2acb4fc358f 100644 --- a/SU2_CFD/src/numerics/flow/flow_diffusion.cpp +++ b/SU2_CFD/src/numerics/flow/flow_diffusion.cpp @@ -580,7 +580,7 @@ CNumerics::ResidualType<> CAvgGradInc_Flow::ComputeResidual(const CConfig* confi implicit = (config->GetKind_TimeIntScheme() == EULER_IMPLICIT); AD::StartPreacc(); - AD::SetPreaccIn(V_i, nDim+9); AD::SetPreaccIn(V_j, nDim+9); + AD::SetPreaccIn(V_i, nDim+10); AD::SetPreaccIn(V_j, nDim+10); AD::SetPreaccIn(Coord_i, nDim); AD::SetPreaccIn(Coord_j, nDim); AD::SetPreaccIn(PrimVar_Grad_i, nVar, nDim); AD::SetPreaccIn(PrimVar_Grad_j, nVar, nDim); diff --git a/SU2_CFD/src/numerics/flow/flow_sources.cpp b/SU2_CFD/src/numerics/flow/flow_sources.cpp index e3797c7c5a5f..45e4d2d2e10e 100644 --- a/SU2_CFD/src/numerics/flow/flow_sources.cpp +++ b/SU2_CFD/src/numerics/flow/flow_sources.cpp @@ -288,7 +288,7 @@ CSourceIncAxisymmetric_Flow::CSourceIncAxisymmetric_Flow(unsigned short val_nDim implicit = (config->GetKind_TimeIntScheme_Flow() == EULER_IMPLICIT); energy = config->GetEnergy_Equation(); viscous = config->GetViscous(); - multicomponent = (config->GetKind_FluidModel() == FLUID_MIXTURE); + multicomponent = config->GetMulticomponentFluid(); } CNumerics::ResidualType<> CSourceIncAxisymmetric_Flow::ComputeResidual(const CConfig* config) { diff --git a/SU2_CFD/src/numerics/species/species_sources.cpp b/SU2_CFD/src/numerics/species/species_sources.cpp index c0dbcc517217..1d5fa5ba847c 100644 --- a/SU2_CFD/src/numerics/species/species_sources.cpp +++ b/SU2_CFD/src/numerics/species/species_sources.cpp @@ -135,6 +135,45 @@ CNumerics::ResidualType<> CSourceAxisymmetric_Species::ComputeResidual(const return ResidualType<>(residual, jacobian, nullptr); } +CSourceCombustion_Species::CSourceCombustion_Species(unsigned short val_nDim, unsigned short val_nVar, + const CConfig* config) + : CSourceBase_Species(val_nDim, val_nVar, config), + chemical_jacobian(config->GetKind_TimeIntScheme_Species() == EULER_IMPLICIT && + config->GetChemical_Source_Jacobian()) { + Chemical_Source_Term_i = nullptr; + Chemical_Source_Jacobian_i = nullptr; +} + +CNumerics::ResidualType<> CSourceCombustion_Species::ComputeResidual(const CConfig* config) { + /*--- Preaccumulation ---*/ + AD::StartPreacc(); + AD::SetPreaccIn(Volume); + AD::SetPreaccIn(Chemical_Source_Term_i, nVar); + + /*--- Initialization. ---*/ + for (auto iVar = 0u; iVar < nVar; iVar++) { + residual[iVar] = 0.0; + for (auto jVar = 0u; jVar < nVar; jVar++) { + jacobian[iVar][jVar] = 0.0; + } + } + + for (auto iVar = 0u; iVar < nVar; iVar++) { + residual[iVar] += Volume * Chemical_Source_Term_i[iVar]; + } + + if (chemical_jacobian && Chemical_Source_Jacobian_i != nullptr) { + for (auto iVar = 0u; iVar < nVar; iVar++) { + jacobian[iVar][iVar] = Volume * Chemical_Source_Jacobian_i[iVar]; + } + } + + AD::SetPreaccOut(residual, nVar); + AD::EndPreacc(); + + return ResidualType<>(residual, jacobian, nullptr); +} + /*--- Explicit instantiations until we don't move this to the hpp. ---*/ template class CSourceAxisymmetric_Species >; template class CSourceAxisymmetric_Species >; diff --git a/SU2_CFD/src/output/CAdjFlowOutput.cpp b/SU2_CFD/src/output/CAdjFlowOutput.cpp index 12d22d467401..d80369a98a8c 100644 --- a/SU2_CFD/src/output/CAdjFlowOutput.cpp +++ b/SU2_CFD/src/output/CAdjFlowOutput.cpp @@ -57,7 +57,7 @@ void CAdjFlowOutput::AddHistoryOutputFields_AdjScalarRMS_RES(const CConfig* conf if (config->GetKind_Species_Model() == SPECIES_MODEL::SPECIES_TRANSPORT) { for (unsigned short iVar = 0; iVar < config->GetnSpecies(); iVar++) { - AddHistoryOutput("RMS_ADJ_SPECIES_" + std::to_string(iVar), "rms[A_rho*Y_" + std::to_string(iVar) + "]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of the adjoint transported species.", HistoryFieldType::RESIDUAL); + AddHistoryOutput("RMS_ADJ_SPECIES_" + config->GetSpecies_Label(iVar), "rms[A_rho*Y_" + config->GetSpecies_Label(iVar) + "]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of the adjoint transported species.", HistoryFieldType::RESIDUAL); } } @@ -94,7 +94,7 @@ void CAdjFlowOutput::AddHistoryOutputFields_AdjScalarMAX_RES(const CConfig* conf if (config->GetKind_Species_Model() == SPECIES_MODEL::SPECIES_TRANSPORT) { for (unsigned short iVar = 0; iVar < config->GetnSpecies(); iVar++) { - AddHistoryOutput("MAX_ADJ_SPECIES_" + std::to_string(iVar), "max[A_rho*Y_" + std::to_string(iVar) + "]",ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of the adjoint transported species.", HistoryFieldType::RESIDUAL); + AddHistoryOutput("MAX_ADJ_SPECIES_" + config->GetSpecies_Label(iVar), "max[A_rho*Y_" + config->GetSpecies_Label(iVar) + "]",ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of the adjoint transported species.", HistoryFieldType::RESIDUAL); } } @@ -204,10 +204,10 @@ void CAdjFlowOutput::LoadHistoryDataAdjScalar(const CConfig* config, const CSolv if (config->GetKind_Species_Model() == SPECIES_MODEL::SPECIES_TRANSPORT) { for (unsigned short iVar = 0; iVar < config->GetnSpecies(); iVar++) { - SetHistoryOutputValue("RMS_ADJ_SPECIES_" + std::to_string(iVar), log10(adjspecies_solver->GetRes_RMS(iVar))); - SetHistoryOutputValue("MAX_ADJ_SPECIES_" + std::to_string(iVar), log10(adjspecies_solver->GetRes_Max(iVar))); + SetHistoryOutputValue("RMS_ADJ_SPECIES_" + config->GetSpecies_Label(iVar), log10(adjspecies_solver->GetRes_RMS(iVar))); + SetHistoryOutputValue("MAX_ADJ_SPECIES_" + config->GetSpecies_Label(iVar), log10(adjspecies_solver->GetRes_Max(iVar))); if (multiZone) { - SetHistoryOutputValue("BGS_ADJ_SPECIES_" + std::to_string(iVar), log10(adjspecies_solver->GetRes_BGS(iVar))); + SetHistoryOutputValue("BGS_ADJ_SPECIES_" + config->GetSpecies_Label(iVar), log10(adjspecies_solver->GetRes_BGS(iVar))); } } diff --git a/SU2_CFD/src/output/CFlowOutput.cpp b/SU2_CFD/src/output/CFlowOutput.cpp index 2ac2693b9952..1faa6e16b272 100644 --- a/SU2_CFD/src/output/CFlowOutput.cpp +++ b/SU2_CFD/src/output/CFlowOutput.cpp @@ -39,7 +39,6 @@ #include "../../include/variables/CPrimitiveIndices.hpp" #include "../../include/fluid/CCoolProp.hpp" - CFlowOutput::CFlowOutput(const CConfig *config, unsigned short nDim, bool fem_output) : CFVMOutput(config, nDim, fem_output), lastInnerIter(curInnerIter) { @@ -84,7 +83,7 @@ void CFlowOutput::AddAnalyzeSurfaceOutput(const CConfig *config){ if (config->GetKind_Species_Model() == SPECIES_MODEL::SPECIES_TRANSPORT) { /// DESCRIPTION: Average Species for (unsigned short iVar = 0; iVar < config->GetnSpecies(); iVar++) { - AddHistoryOutput("SURFACE_SPECIES_" + std::to_string(iVar), "Avg_Species_" + std::to_string(iVar), ScreenOutputFormat::FIXED, "SPECIES_COEFF", "Total average species " + std::to_string(iVar) + " on all markers set in MARKER_ANALYZE", HistoryFieldType::COEFFICIENT); + AddHistoryOutput("SURFACE_SPECIES_" + config->GetSpecies_Label(iVar), "Avg_Species_" + config->GetSpecies_Label(iVar), ScreenOutputFormat::FIXED, "SPECIES_COEFF", "Total average species " + config->GetSpecies_Label(iVar) + " on all markers set in MARKER_ANALYZE", HistoryFieldType::COEFFICIENT); } /// DESCRIPTION: Species Variance AddHistoryOutput("SURFACE_SPECIES_VARIANCE", "Species_Variance", ScreenOutputFormat::SCIENTIFIC, "SPECIES_COEFF", "Total species variance, measure for mixing quality. On all markers set in MARKER_ANALYZE", HistoryFieldType::COEFFICIENT); @@ -126,7 +125,7 @@ void CFlowOutput::AddAnalyzeSurfaceOutput(const CConfig *config){ if (config->GetKind_Species_Model() == SPECIES_MODEL::SPECIES_TRANSPORT) { /// DESCRIPTION: Average Species for (unsigned short iVar = 0; iVar < config->GetnSpecies(); iVar++) { - AddHistoryOutputPerSurface("SURFACE_SPECIES_" + std::to_string(iVar), "Avg_Species_" + std::to_string(iVar), ScreenOutputFormat::FIXED, "SPECIES_COEFF_SURF", Marker_Analyze, HistoryFieldType::COEFFICIENT); + AddHistoryOutputPerSurface("SURFACE_SPECIES_" + config->GetSpecies_Label(iVar), "Avg_Species_" + config->GetSpecies_Label(iVar), ScreenOutputFormat::FIXED, "SPECIES_COEFF_SURF", Marker_Analyze, HistoryFieldType::COEFFICIENT); } /// DESCRIPTION: Species Variance AddHistoryOutputPerSurface("SURFACE_SPECIES_VARIANCE", "Species_Variance", ScreenOutputFormat::SCIENTIFIC, "SPECIES_COEFF_SURF", Marker_Analyze, HistoryFieldType::COEFFICIENT); @@ -506,7 +505,7 @@ void CFlowOutput::SetAnalyzeSurface(const CSolver* const*solver, const CGeometry if (species) { for (unsigned short iVar = 0; iVar < nSpecies; iVar++) { su2double Species = Surface_Species_Total(iMarker_Analyze, iVar); - SetHistoryOutputPerSurfaceValue("SURFACE_SPECIES_" + std::to_string(iVar), Species, iMarker_Analyze); + SetHistoryOutputPerSurfaceValue("SURFACE_SPECIES_" + config->GetSpecies_Label(iVar), Species, iMarker_Analyze); Tot_Surface_Species[iVar] += Species; if (iVar == 0) config->SetSurface_Species_0(iMarker_Analyze, Species); @@ -542,8 +541,9 @@ void CFlowOutput::SetAnalyzeSurface(const CSolver* const*solver, const CGeometry SetHistoryOutputValue("SURFACE_TOTAL_TEMPERATURE", Tot_Surface_TotalTemperature); SetHistoryOutputValue("SURFACE_TOTAL_PRESSURE", Tot_Surface_TotalPressure); if (species) { - for (unsigned short iVar = 0; iVar < nSpecies; iVar++) - SetHistoryOutputValue("SURFACE_SPECIES_" + std::to_string(iVar), Tot_Surface_Species[iVar]); + for (unsigned short iVar = 0; iVar < nSpecies; iVar++) { + SetHistoryOutputValue("SURFACE_SPECIES_" + config->GetSpecies_Label(iVar), Tot_Surface_Species[iVar]); + } SetAnalyzeSurfaceSpeciesVariance(solver, geometry, config, Surface_Species_Total, Surface_MassFlow_Abs_Total, Surface_Area_Total); @@ -1064,7 +1064,7 @@ void CFlowOutput::AddHistoryOutputFields_ScalarRMS_RES(const CConfig* config) { switch (config->GetKind_Species_Model()) { case SPECIES_MODEL::SPECIES_TRANSPORT: { for (unsigned short iVar = 0; iVar < config->GetnSpecies(); iVar++) { - AddHistoryOutput("RMS_SPECIES_" + std::to_string(iVar), "rms[rho*Y_" + std::to_string(iVar)+"]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of transported species.", HistoryFieldType::RESIDUAL); + AddHistoryOutput("RMS_SPECIES_" + config->GetSpecies_Label(iVar), "rms[rho*Y_" + config->GetSpecies_Label(iVar)+"]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of transported species.", HistoryFieldType::RESIDUAL); } break; } @@ -1132,7 +1132,7 @@ void CFlowOutput::AddHistoryOutputFields_ScalarMAX_RES(const CConfig* config) { switch (config->GetKind_Species_Model()) { case SPECIES_MODEL::SPECIES_TRANSPORT: { for (unsigned short iVar = 0; iVar < config->GetnSpecies(); iVar++) { - AddHistoryOutput("MAX_SPECIES_" + std::to_string(iVar), "max[rho*Y_" + std::to_string(iVar)+"]", ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of transported species.", HistoryFieldType::RESIDUAL); + AddHistoryOutput("MAX_SPECIES_" + config->GetSpecies_Label(iVar), "max[rho*Y_" + config->GetSpecies_Label(iVar)+"]", ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of transported species.", HistoryFieldType::RESIDUAL); } break; } @@ -1196,7 +1196,7 @@ void CFlowOutput::AddHistoryOutputFields_ScalarBGS_RES(const CConfig* config) { switch (config->GetKind_Species_Model()) { case SPECIES_MODEL::SPECIES_TRANSPORT: { for (unsigned short iVar = 0; iVar < config->GetnSpecies(); iVar++) { - AddHistoryOutput("BGS_SPECIES_" + std::to_string(iVar), "bgs[rho*Y_" + std::to_string(iVar)+"]", ScreenOutputFormat::FIXED, "BGS_RES", "Maximum residual of transported species.", HistoryFieldType::RESIDUAL); + AddHistoryOutput("BGS_SPECIES_" + config->GetSpecies_Label(iVar), "bgs[rho*Y_" + config->GetSpecies_Label(iVar)+"]", ScreenOutputFormat::FIXED, "BGS_RES", "Maximum residual of transported species.", HistoryFieldType::RESIDUAL); } break; } @@ -1309,10 +1309,10 @@ void CFlowOutput::LoadHistoryDataScalar(const CConfig* config, const CSolver* co switch(config->GetKind_Species_Model()) { case SPECIES_MODEL::SPECIES_TRANSPORT: { for (unsigned short iVar = 0; iVar < config->GetnSpecies(); iVar++) { - SetHistoryOutputValue("RMS_SPECIES_" + std::to_string(iVar), log10(solver[SPECIES_SOL]->GetRes_RMS(iVar))); - SetHistoryOutputValue("MAX_SPECIES_" + std::to_string(iVar), log10(solver[SPECIES_SOL]->GetRes_Max(iVar))); + SetHistoryOutputValue("RMS_SPECIES_" + config->GetSpecies_Label(iVar), log10(solver[SPECIES_SOL]->GetRes_RMS(iVar))); + SetHistoryOutputValue("MAX_SPECIES_" + config->GetSpecies_Label(iVar), log10(solver[SPECIES_SOL]->GetRes_Max(iVar))); if (multiZone) { - SetHistoryOutputValue("BGS_SPECIES_" + std::to_string(iVar), log10(solver[SPECIES_SOL]->GetRes_BGS(iVar))); + SetHistoryOutputValue("BGS_SPECIES_" + config->GetSpecies_Label(iVar), log10(solver[SPECIES_SOL]->GetRes_BGS(iVar))); } } SetHistoryOutputValue("LINSOL_ITER_SPECIES", solver[SPECIES_SOL]->GetIterLinSolver()); @@ -1387,8 +1387,9 @@ void CFlowOutput::SetVolumeOutputFieldsScalarSolution(const CConfig* config){ switch (config->GetKind_Species_Model()) { case SPECIES_MODEL::SPECIES_TRANSPORT: - for (unsigned short iVar = 0; iVar < config->GetnSpecies(); iVar++){ - AddVolumeOutput("SPECIES_" + std::to_string(iVar), "Species_" + std::to_string(iVar), "SOLUTION", "Species_" + std::to_string(iVar) + " mass fraction"); + for (unsigned short iVar = 0; iVar < config->GetnSpecies(); iVar++) { + AddVolumeOutput("SPECIES_" + config->GetSpecies_Label(iVar), "Species_" + config->GetSpecies_Label(iVar), "SOLUTION", + "Species_" + config->GetSpecies_Label(iVar) + " mass fraction"); } break; case SPECIES_MODEL::FLAMELET: { @@ -1439,7 +1440,7 @@ void CFlowOutput::SetVolumeOutputFieldsScalarResidual(const CConfig* config) { switch (config->GetKind_Species_Model()) { case SPECIES_MODEL::SPECIES_TRANSPORT: for (unsigned short iVar = 0; iVar < config->GetnSpecies(); iVar++){ - AddVolumeOutput("RES_SPECIES_" + std::to_string(iVar), "Residual_Species_" + std::to_string(iVar), "RESIDUAL", "Residual of the transported species " + std::to_string(iVar)); + AddVolumeOutput("RES_SPECIES_" + config->GetSpecies_Label(iVar), "Residual_Species_" + config->GetSpecies_Label(iVar), "RESIDUAL", "Residual of the transported species " + config->GetSpecies_Label(iVar)); } break; case SPECIES_MODEL::FLAMELET: { @@ -1496,7 +1497,7 @@ void CFlowOutput::SetVolumeOutputFieldsScalarLimiter(const CConfig* config) { switch (config->GetKind_Species_Model()) { case SPECIES_MODEL::SPECIES_TRANSPORT: for (unsigned short iVar = 0; iVar < config->GetnSpecies(); iVar++) - AddVolumeOutput("LIMITER_SPECIES_" + std::to_string(iVar), "Limiter_Species_" + std::to_string(iVar), "LIMITER", "Limiter value of the transported species " + std::to_string(iVar)); + AddVolumeOutput("LIMITER_SPECIES_" + config->GetSpecies_Label(iVar), "Limiter_Species_" + config->GetSpecies_Label(iVar), "LIMITER", "Limiter value of the transported species " + config->GetSpecies_Label(iVar)); break; case SPECIES_MODEL::FLAMELET: { const auto& flamelet_config_options = config->GetFlameletParsedOptions(); @@ -1524,8 +1525,16 @@ void CFlowOutput::SetVolumeOutputFieldsScalarPrimitive(const CConfig* config) { switch (config->GetKind_Species_Model()) { case SPECIES_MODEL::SPECIES_TRANSPORT: for (unsigned short iVar = 0; iVar < config->GetnSpecies(); iVar++){ - AddVolumeOutput("DIFFUSIVITY_" + std::to_string(iVar), "Diffusivity_" + std::to_string(iVar), "PRIMITIVE", "Diffusivity of the transported species " + std::to_string(iVar)); + AddVolumeOutput("DIFFUSIVITY_" + config->GetSpecies_Label(iVar), "Diffusivity_" + config->GetSpecies_Label(iVar), "PRIMITIVE", + "Diffusivity of the transported species " + config->GetSpecies_Label(iVar)); + if (config->GetCombustion()) { + AddVolumeOutput("CHEMICAL_SOURCE_TERM_" + config->GetSpecies_Label(iVar), + "Chemical_Source_Term_" + config->GetSpecies_Label(iVar), "PRIMITIVE", + "Chemical source term of the transported species " + config->GetSpecies_Label(iVar)); + } } + if (config->GetCombustion()) + AddVolumeOutput("HEAT_RELEASE", "Heat_Release", "PRIMITIVE", "Heat release due to combustion"); break; default: break; @@ -1555,7 +1564,7 @@ void CFlowOutput::SetVolumeOutputFieldsScalarSource(const CConfig* config) { case SPECIES_MODEL::SPECIES_TRANSPORT: if (config->GetPyCustomSource()) { for (unsigned short iVar = 0; iVar < config->GetnSpecies(); iVar++){ - AddVolumeOutput("SPECIES_UDS_" + std::to_string(iVar), "Species_UDS_" + std::to_string(iVar), "SOURCE", "Species User Defined Source " + std::to_string(iVar)); + AddVolumeOutput("SPECIES_UDS_" + config->GetSpecies_Label(iVar), "Species_UDS_" + config->GetSpecies_Label(iVar), "SOURCE", "Species User Defined Source " + config->GetSpecies_Label(iVar)); } } break; @@ -1732,16 +1741,20 @@ void CFlowOutput::LoadVolumeDataScalar(const CConfig* config, const CSolver* con case SPECIES_MODEL::SPECIES_TRANSPORT: { const auto Node_Species = solver[SPECIES_SOL]->GetNodes(); - for (unsigned long iVar = 0; iVar < config->GetnSpecies(); iVar++) { - SetVolumeOutputValue("SPECIES_" + std::to_string(iVar), iPoint, Node_Species->GetSolution(iPoint, iVar)); - SetVolumeOutputValue("RES_SPECIES_" + std::to_string(iVar), iPoint, solver[SPECIES_SOL]->LinSysRes(iPoint, iVar)); - SetVolumeOutputValue("DIFFUSIVITY_"+ std::to_string(iVar), iPoint, Node_Species->GetDiffusivity(iPoint,iVar)); + for (unsigned short iVar = 0; iVar < config->GetnSpecies(); iVar++) { + const auto label = config->GetSpecies_Label(iVar); + SetVolumeOutputValue("SPECIES_" + label, iPoint, Node_Species->GetSolution(iPoint, iVar)); + SetVolumeOutputValue("RES_SPECIES_" + label, iPoint, solver[SPECIES_SOL]->LinSysRes(iPoint, iVar)); + SetVolumeOutputValue("DIFFUSIVITY_" + label, iPoint, Node_Species->GetDiffusivity(iPoint, iVar)); + if (config->GetCombustion()) + SetVolumeOutputValue("CHEMICAL_SOURCE_TERM_" + label, iPoint, Node_Species->GetChemicalSourceTerm(iPoint, iVar)); if (config->GetKind_SlopeLimit_Species() != LIMITER::NONE) - SetVolumeOutputValue("LIMITER_SPECIES_" + std::to_string(iVar), iPoint, Node_Species->GetLimiter(iPoint, iVar)); - if (config->GetPyCustomSource()){ - SetVolumeOutputValue("SPECIES_UDS_" + std::to_string(iVar), iPoint, Node_Species->GetUserDefinedSource()(iPoint, iVar)); - } + SetVolumeOutputValue("LIMITER_SPECIES_" + label, iPoint, Node_Species->GetLimiter(iPoint, iVar)); + if (config->GetPyCustomSource()) + SetVolumeOutputValue("SPECIES_UDS_" + label, iPoint, Node_Species->GetUserDefinedSource()(iPoint, iVar)); } + if (config->GetCombustion()) + SetVolumeOutputValue("HEAT_RELEASE", iPoint, Node_Species->GetHeatRelease(iPoint)); break; } @@ -3325,6 +3338,13 @@ void CFlowOutput::WriteForcesBreakdown(const CConfig* config, const CSolver* flo case VISCOSITYMODEL::COOLPROP: file << "Viscosity Model: CoolProp \n"; break; + + case VISCOSITYMODEL::CANTERA: + file << "Viscosity Model: CANTERA \n"; + if (si_units) file << " N.s/m^2.\n"; + else file << " lbf.s/ft^2.\n"; + file << "Laminar Viscosity (non-dim): " << config->GetMu_ConstantND() << "\n"; + break; case VISCOSITYMODEL::SUTHERLAND: file << "Viscosity Model: SUTHERLAND \n"; @@ -3380,6 +3400,12 @@ void CFlowOutput::WriteForcesBreakdown(const CConfig* config, const CSolver* flo file << "Molecular Conductivity units: " << " W/m^2.K.\n"; file << "Molecular Conductivity (non-dim): " << config->GetThermal_Conductivity_ConstantND() << "\n"; break; + + case CONDUCTIVITYMODEL::CANTERA: + file << "Conductivity Model: CANTERA \n"; + file << "Molecular Conductivity units: " << " W/m^2.K.\n"; + file << "Molecular Conductivity (non-dim): " << config->GetThermal_Conductivity_ConstantND() << "\n"; + break; case CONDUCTIVITYMODEL::POLYNOMIAL: file << "Viscosity Model: POLYNOMIAL \n"; diff --git a/SU2_CFD/src/solvers/CIncEulerSolver.cpp b/SU2_CFD/src/solvers/CIncEulerSolver.cpp index 11b4e3f9f063..17fc6a068f4d 100644 --- a/SU2_CFD/src/solvers/CIncEulerSolver.cpp +++ b/SU2_CFD/src/solvers/CIncEulerSolver.cpp @@ -33,6 +33,7 @@ #include "../../include/variables/CIncNSVariable.hpp" #include "../../../Common/include/toolboxes/geometry_toolbox.hpp" #include "../../include/fluid/CFluidScalar.hpp" +#include "../../include/fluid/CFluidCantera.hpp" #include "../../include/fluid/CFluidFlamelet.hpp" #include "../../include/fluid/CFluidModel.hpp" @@ -356,6 +357,12 @@ void CIncEulerSolver::SetNondimensionalization(CConfig *config, unsigned short i auxFluidModel = new CFluidFlamelet(config, Pressure_Thermodynamic); break; + case FLUID_CANTERA: + + auxFluidModel = new CFluidCantera(Pressure_Thermodynamic, config); + auxFluidModel->SetTDState_T(Temperature_FreeStream, config->GetSpecies_Init()); + break; + default: SU2_MPI::Error("Fluid model not implemented for incompressible solver.", CURRENT_FUNCTION); @@ -537,6 +544,11 @@ void CIncEulerSolver::SetNondimensionalization(CConfig *config, unsigned short i fluidModel->SetTDState_T(Temperature_FreeStreamND, config->GetSpecies_Init()); break; + case FLUID_CANTERA: + fluidModel = new CFluidCantera(Pressure_ThermodynamicND, config); + fluidModel->SetTDState_T(Temperature_FreeStreamND, config->GetSpecies_Init()); + break; + case INC_IDEAL_GAS_POLY: fluidModel = new CIncIdealGasPolynomial(Gas_ConstantND, Pressure_ThermodynamicND, STD_REF_TEMP / config->GetTemperature_Ref()); if (viscous) { @@ -706,6 +718,15 @@ void CIncEulerSolver::SetNondimensionalization(CConfig *config, unsigned short i Unit.str(""); NonDimTable.PrintFooter(); break; + + case VISCOSITYMODEL::CANTERA: + ModelTable << "CANTERA"; + if (config->GetSystemMeasurements() == SI) Unit << "N.s/m^2"; + else if (config->GetSystemMeasurements() == US) Unit << "lbf.s/ft^2"; + NonDimTable << "Viscosity" << "--" << "--" << Unit.str() << config->GetMu_ConstantND(); + Unit.str(""); + NonDimTable.PrintFooter(); + break; case VISCOSITYMODEL::SUTHERLAND: ModelTable << "SUTHERLAND"; @@ -769,6 +790,13 @@ void CIncEulerSolver::SetNondimensionalization(CConfig *config, unsigned short i Unit.str(""); NonDimTable.PrintFooter(); break; + + case CONDUCTIVITYMODEL::CANTERA: + ModelTable << "CANTERA"; + Unit << "W/m^2.K"; + NonDimTable << "Molecular Cond." << "--" << "--" << Unit.str() << config->GetThermal_Conductivity_ConstantND(); + Unit.str(""); + break; case CONDUCTIVITYMODEL::POLYNOMIAL: ModelTable << "POLYNOMIAL"; @@ -856,6 +884,19 @@ void CIncEulerSolver::SetNondimensionalization(CConfig *config, unsigned short i NonDimTable.PrintFooter(); break; + case FLUID_CANTERA: + ModelTable << "CANTERA"; + if (viscous) { + Unit << "N.m/kg.K"; + NonDimTable << "Spec. Heat (Cp)" << config->GetSpecificHeatCp_FreeStream() << 1.0 << Unit.str() << config->GetSpecificHeatCp_FreeStream(); + Unit.str(""); + } + Unit << "Pa"; + NonDimTable << "Therm. Pressure" << config->GetPressure_Thermodynamic() << config->GetPressure_Ref() << Unit.str() << config->GetPressure_ThermodynamicND(); + Unit.str(""); + NonDimTable.PrintFooter(); + break; + case INC_IDEAL_GAS_POLY: ModelTable << "INC_IDEAL_GAS_POLY"; Unit.str(""); @@ -1380,7 +1421,7 @@ void CIncEulerSolver::Upwind_Residual(CGeometry *geometry, CSolver **solver_cont const bool limiter = (config->GetKind_SlopeLimit_Flow() != LIMITER::NONE); const bool van_albada = (config->GetKind_SlopeLimit_Flow() == LIMITER::VAN_ALBADA_EDGE); const bool bounded_scalar = config->GetBounded_Scalar(); - const bool multicomponent = (config->GetKind_FluidModel() == FLUID_MIXTURE); + const bool multicomponent = config->GetMulticomponentFluid(); const su2double kappa = config->GetMUSCL_Kappa_Flow(); const su2double musclRamp = config->GetMUSCLRampValue() * config->GetNewtonKrylovRelaxation(); @@ -1453,11 +1494,14 @@ void CIncEulerSolver::Upwind_Residual(CGeometry *geometry, CSolver **solver_cont Primitive_i[iVar] = V_i[iVar]; Primitive_j[iVar] = V_j[iVar]; } - if(multicomponent){ + /*--- A failed evaluation of the fluid state is a non-physical reconstruction, like a negative temperature. ---*/ + bool temperature_failed = false; + if (multicomponent) { const su2double* scalar_i = solver_container[SPECIES_SOL]->GetNodes()->GetSolution(iPoint); const su2double* scalar_j = solver_container[SPECIES_SOL]->GetNodes()->GetSolution(jPoint); - ComputeConsistentExtrapolation(GetFluidModel(), nDim, scalar_i, Primitive_i); - ComputeConsistentExtrapolation(GetFluidModel(), nDim, scalar_j, Primitive_j); + const bool failed_i = ComputeConsistentExtrapolation(GetFluidModel(), nDim, scalar_i, Primitive_i); + const bool failed_j = ComputeConsistentExtrapolation(GetFluidModel(), nDim, scalar_j, Primitive_j); + temperature_failed = failed_i || failed_j; } /*--- Check for non-physical solutions after reconstruction. If found, @@ -1467,14 +1511,17 @@ void CIncEulerSolver::Upwind_Residual(CGeometry *geometry, CSolver **solver_cont incompressible flow, only the temperature and density need to be checked. Pressure is the dynamic pressure (can be negative). ---*/ - if (config->GetEnergy_Equation()) { - const bool neg_temperature_i = (Primitive_i[prim_idx.Temperature()] < 0.0); - const bool neg_temperature_j = (Primitive_j[prim_idx.Temperature()] < 0.0); + if (config->GetEnergy_Equation() || temperature_failed) { + bool bad_recon = temperature_failed; + if (config->GetEnergy_Equation()) { + const bool neg_temperature_i = (Primitive_i[prim_idx.Temperature()] < 0.0); + const bool neg_temperature_j = (Primitive_j[prim_idx.Temperature()] < 0.0); - const bool neg_density_i = (Primitive_i[prim_idx.Density()] < 0.0); - const bool neg_density_j = (Primitive_j[prim_idx.Density()] < 0.0); + const bool neg_density_i = (Primitive_i[prim_idx.Density()] < 0.0); + const bool neg_density_j = (Primitive_j[prim_idx.Density()] < 0.0); - bool bad_recon = neg_temperature_i || neg_temperature_j || neg_density_i || neg_density_j; + bad_recon = bad_recon || neg_temperature_i || neg_temperature_j || neg_density_i || neg_density_j; + } bad_recon = nodes->UpdateNonPhysicalEdgeCounter(iEdge, bad_recon); counter_local += bad_recon; @@ -1530,15 +1577,18 @@ void CIncEulerSolver::Upwind_Residual(CGeometry *geometry, CSolver **solver_cont FinalizeResidualComputation(geometry, pausePreacc, counter_local, config); } -void CIncEulerSolver::ComputeConsistentExtrapolation(CFluidModel* fluidModel, unsigned short nDim, +bool CIncEulerSolver::ComputeConsistentExtrapolation(CFluidModel* fluidModel, unsigned short nDim, const su2double* scalar, su2double* primitive) { SU2_ZONE_SCOPED const CIncEulerVariable::CIndices prim_idx(nDim, 0); const su2double enthalpy = primitive[prim_idx.Enthalpy()]; + fluidModel->SetTemperatureGuess(primitive[prim_idx.Temperature()]); fluidModel->SetTDState_h(enthalpy, scalar); primitive[prim_idx.Temperature()] = fluidModel->GetTemperature(); primitive[prim_idx.Density()] = fluidModel->GetDensity(); + + return fluidModel->GetTemperatureIterationFailed(); } void CIncEulerSolver::Source_Residual(CGeometry *geometry, CSolver **solver_container, @@ -1560,7 +1610,7 @@ void CIncEulerSolver::Source_Residual(CGeometry *geometry, CSolver **solver_cont const bool energy = config->GetEnergy_Equation(); const bool streamwise_periodic = (config->GetKind_Streamwise_Periodic() != ENUM_STREAMWISE_PERIODIC::NONE); const bool streamwise_periodic_temperature = config->GetStreamwise_Periodic_Temperature(); - const bool multicomponent = (config->GetKind_FluidModel() == FLUID_MIXTURE); + const bool multicomponent = config->GetMulticomponentFluid(); AD::StartNoSharedReading(); @@ -1760,7 +1810,6 @@ void CIncEulerSolver::Source_Residual(CGeometry *geometry, CSolver **solver_cont if(multicomponent && energy){ /*--- retrieve number of species that are solved and set maximum static array ---*/ int n_species = config->GetnSpecies(); - static constexpr size_t MAXNVAR_SPECIES = 20UL; /*--- Obtain fluid model for computing the enthalpy diffusion terms. ---*/ CFluidModel* FluidModel = solver_container[FLOW_SOL]->GetFluidModel(); /*--- retrieve species gradient needed for multicomponent. ---*/ @@ -1768,8 +1817,8 @@ void CIncEulerSolver::Source_Residual(CGeometry *geometry, CSolver **solver_cont /*--- Set thermodynamic state. ---*/ FluidModel->SetTDState_T(nodes->GetTemperature(iPoint),solver_container[SPECIES_SOL]->GetNodes()->GetSolution(iPoint)); /*--- Get enthalpy diffusion terms and its gradients(for implicit). ---*/ - su2double EnthalpyDiffusion_i[MAXNVAR_SPECIES]{0.0}; - su2double GradEnthalpyDiffusion_i[MAXNVAR_SPECIES]{0.0}; + su2double EnthalpyDiffusion_i[MAX_TRANSPORTED_SPECIES]{0.0}; + su2double GradEnthalpyDiffusion_i[MAX_TRANSPORTED_SPECIES]{0.0}; FluidModel->GetEnthalpyDiffusivity(EnthalpyDiffusion_i); if (implicit) FluidModel->GetGradEnthalpyDiffusivity(GradEnthalpyDiffusion_i); /*--- Compute Enthalpy diffusion flux and its jacobian (for implicit iterations) ---*/ @@ -2183,9 +2232,9 @@ void CIncEulerSolver::SetBeta_Parameter(CGeometry *geometry, CSolver **solver_co static su2double MaxVel2; const su2double epsilon2_default = 4.1; - /*--- For now, only the finest mesh level stores the Beta for all levels. ---*/ + /*--- For now, only the finest active mesh level stores the Beta for all levels. ---*/ - if (iMesh == MESH_0) { + if (iMesh == config->GetFinestMesh()) { SU2_OMP_MASTER MaxVel2 = 0.0; END_SU2_OMP_MASTER @@ -2378,7 +2427,7 @@ void CIncEulerSolver::BC_Far_Field(CGeometry *geometry, CSolver **solver_contain const bool implicit = config->GetKind_TimeIntScheme() == EULER_IMPLICIT; const bool viscous = config->GetViscous(); - const bool energy_multicomponent = config->GetKind_FluidModel() == FLUID_MIXTURE && config->GetEnergy_Equation(); + const bool energy_multicomponent = config->GetMulticomponentFluid() && config->GetEnergy_Equation(); const bool species_model = config->GetKind_Species_Model() != SPECIES_MODEL::NONE; su2double Normal[MAXNDIM] = {0.0}; @@ -2581,7 +2630,7 @@ void CIncEulerSolver::BC_Inlet(CGeometry *geometry, CSolver **solver_container, const bool implicit = (config->GetKind_TimeIntScheme() == EULER_IMPLICIT); const bool viscous = config->GetViscous(); - const bool energy_multicomponent = config->GetKind_FluidModel() == FLUID_MIXTURE && config->GetEnergy_Equation(); + const bool energy_multicomponent = config->GetMulticomponentFluid() && config->GetEnergy_Equation(); const bool species_model = config->GetKind_Species_Model() != SPECIES_MODEL::NONE; string Marker_Tag = config->GetMarker_All_TagBound(val_marker); @@ -2887,7 +2936,7 @@ void CIncEulerSolver::BC_Outlet(CGeometry *geometry, CSolver **solver_container, const bool implicit = (config->GetKind_TimeIntScheme() == EULER_IMPLICIT); const bool viscous = config->GetViscous(); - const bool energy_multicomponent = config->GetKind_FluidModel() == FLUID_MIXTURE && config->GetEnergy_Equation(); + const bool energy_multicomponent = config->GetMulticomponentFluid() && config->GetEnergy_Equation(); string Marker_Tag = config->GetMarker_All_TagBound(val_marker); su2double Normal[MAXNDIM] = {0.0}; diff --git a/SU2_CFD/src/solvers/CIncNSSolver.cpp b/SU2_CFD/src/solvers/CIncNSSolver.cpp index ec1b78935e1f..4a2da580d9ff 100644 --- a/SU2_CFD/src/solvers/CIncNSSolver.cpp +++ b/SU2_CFD/src/solvers/CIncNSSolver.cpp @@ -43,6 +43,8 @@ CIncNSSolver::CIncNSSolver(CGeometry *geometry, CConfig *config, unsigned short Viscosity_Inf = config->GetViscosity_FreeStreamND(); Tke_Inf = config->GetTke_FreeStreamND(); + TemperatureLimits[0]= config->GetTemperatureLimits(0); + TemperatureLimits[1]= config->GetTemperatureLimits(1); /*--- Initialize the secondary values for direct derivative approximations ---*/ @@ -54,6 +56,11 @@ CIncNSSolver::CIncNSSolver(CGeometry *geometry, CConfig *config, unsigned short break; } + if (config->GetMulticomponentFluid() && config->GetEnergy_Equation()) { + EnthalpyDiffusion.resize(nPoint, config->GetnSpecies()) = su2double(0.0); + GradEnthalpyDiffusion.resize(nPoint, config->GetnSpecies()) = su2double(0.0); + } + /*--- Set the initial Streamwise periodic pressure drop value. ---*/ if (config->GetKind_Streamwise_Periodic() != ENUM_STREAMWISE_PERIODIC::NONE) @@ -61,6 +68,24 @@ CIncNSSolver::CIncNSSolver(CGeometry *geometry, CConfig *config, unsigned short SPvals.Streamwise_Periodic_PressureDrop = config->GetStreamwise_Periodic_PressureDrop(); } +void CIncNSSolver::ApplySpark(CGeometry* geometry, CSolver** solver_container, const CConfig* config) { + CFluidModel* fluid_model_local = solver_container[FLOW_SOL]->GetFluidModel(); + SU2_OMP_FOR_STAT(omp_chunk_size) + for (auto i_point = 0u; i_point < nPoint; i_point++) { + if (!InSpark(config, geometry->nodes->GetCoord(i_point))) continue; + const su2double* scalars = solver_container[SPECIES_SOL]->GetNodes()->GetSolution(i_point); + nodes->SetTemperature(i_point, config->GetSpark_Temperature(), TemperatureLimits); + fluid_model_local->SetTDState_T(config->GetSpark_Temperature(), scalars); + nodes->SetSolution(i_point, nDim + 1, fluid_model_local->GetEnthalpy()); + nodes->SetDensity(i_point, fluid_model_local->GetDensity()); + nodes->SetSpecificHeatCp(i_point, fluid_model_local->GetCp()); + nodes->SetSpecificHeatCv(i_point, fluid_model_local->GetCv()); + nodes->SetThermalConductivity(i_point, fluid_model_local->GetThermalConductivity()); + nodes->SetLaminarViscosity(i_point, fluid_model_local->GetLaminarViscosity()); + } + END_SU2_OMP_FOR +} + void CIncNSSolver::Preprocessing(CGeometry *geometry, CSolver **solver_container, CConfig *config, unsigned short iMesh, unsigned short iRKStep, unsigned short RunTime_EqSystem, bool Output) { SU2_ZONE_SCOPED @@ -71,11 +96,20 @@ void CIncNSSolver::Preprocessing(CGeometry *geometry, CSolver **solver_container const bool limiter = (config->GetKind_SlopeLimit_Flow() != LIMITER::NONE) && (InnerIter <= config->GetLimiterIter()); const bool van_albada = (config->GetKind_SlopeLimit_Flow() == LIMITER::VAN_ALBADA_EDGE); const bool wall_functions = config->GetWall_Functions(); + const bool energy = config->GetEnergy_Equation(); + const bool combustion = config->GetCombustion(); + + if (energy && combustion) { + SU2_OMP_SAFE_GLOBAL_ACCESS(config->SetGlobalParam(config->GetKind_Solver(), RunTime_EqSystem);) + if (SparkActive(config)) ApplySpark(geometry, solver_container, config); + } /*--- Common preprocessing steps (implemented by CEulerSolver) ---*/ CommonPreprocessing(geometry, solver_container, config, iMesh, iRKStep, RunTime_EqSystem, Output); + if (EnthalpyDiffusion.rows() > 0) ComputeEnthalpyDiffusionTerms(solver_container, config); + /*--- Compute gradient for MUSCL reconstruction ---*/ if (config->GetReconstructionGradientRequired() && muscl && !center) { @@ -279,7 +313,7 @@ void CIncNSSolver::Compute_Streamwise_Periodic_Recovered_Values(CConfig *config, CNumerics::ResidualType<> CIncNSSolver::Viscous_Residual(unsigned long iEdge, CGeometry *geometry, CSolver **solver_container, CNumerics *numerics, CConfig *config) { - const bool energy_multicomponent = config->GetKind_FluidModel() == FLUID_MIXTURE && config->GetEnergy_Equation(); + const bool energy_multicomponent = config->GetMulticomponentFluid() && config->GetEnergy_Equation(); /*--- Contribution to heat flux due to enthalpy diffusion for multicomponent and reacting flows ---*/ if (energy_multicomponent) { @@ -307,23 +341,8 @@ void CIncNSSolver::Compute_Enthalpy_Diffusion(unsigned long iEdge, CGeometry* ge const su2double* Coord_i = geometry->nodes->GetCoord(iPoint); const su2double* Coord_j = geometry->nodes->GetCoord(jPoint); - /*--- Obtain fluid model for computing the enthalpy diffusion terms. ---*/ - - CFluidModel* FluidModel = solver_container[FLOW_SOL]->GetFluidModel(); - - /*--- Helper function that retrieves enthalpy diffusion terms. ---*/ - auto GetEnthalpyDiffusionTerms = [&](unsigned long Point, const su2double* Species, su2double* EnthalpyDiffusion, - su2double* GradEnthalpyDiffusion) { - FluidModel->SetTDState_T(nodes->GetPrimitive(Point)[prim_idx.Temperature()], Species); - FluidModel->SetEddyViscosity(nodes->GetPrimitive(Point)[prim_idx.EddyViscosity()]); - FluidModel->GetEnthalpyDiffusivity(EnthalpyDiffusion); - if (implicit) FluidModel->GetGradEnthalpyDiffusivity(GradEnthalpyDiffusion); - }; - /*--- set maximum static array ---*/ - static constexpr size_t MAXNVAR_SPECIES = 20UL; - /*--- Species variables, and its gradients ---*/ const su2double* Species_i = speciesNodes->GetSolution(iPoint); const su2double* Species_j = speciesNodes->GetSolution(jPoint); @@ -331,19 +350,17 @@ void CIncNSSolver::Compute_Enthalpy_Diffusion(unsigned long iEdge, CGeometry* ge CMatrixView Species_Grad_j = speciesNodes->GetGradient(jPoint); /*--- Compute Projected gradient for species variables ---*/ - su2double ProjGradScalarVarNoCorr[MAXNVAR_SPECIES]{0.0}; - su2double Proj_Mean_GradScalarVar[MAXNVAR_SPECIES]{0.0}; + su2double ProjGradScalarVarNoCorr[MAX_TRANSPORTED_SPECIES]{0.0}; + su2double Proj_Mean_GradScalarVar[MAX_TRANSPORTED_SPECIES]{0.0}; numerics->ComputeProjectedGradient(nDim, n_species, Normal, Coord_i, Coord_j, Species_Grad_i, Species_Grad_j, true, Species_i, Species_j, ProjGradScalarVarNoCorr, Proj_Mean_GradScalarVar); - /*--- Get enthalpy diffusion terms and its gradient(for implicit) for each species at iPoint and jPoint. ---*/ + /*--- Enthalpy diffusion terms and their gradient (for implicit) were stored per point in Preprocessing. ---*/ - su2double EnthalpyDiffusion_i[MAXNVAR_SPECIES]{0.0}; - su2double GradEnthalpyDiffusion_i[MAXNVAR_SPECIES]{0.0}; - su2double EnthalpyDiffusion_j[MAXNVAR_SPECIES]{0.0}; - su2double GradEnthalpyDiffusion_j[MAXNVAR_SPECIES]{0.0}; - GetEnthalpyDiffusionTerms(iPoint, Species_i, EnthalpyDiffusion_i, GradEnthalpyDiffusion_i); - GetEnthalpyDiffusionTerms(jPoint, Species_j, EnthalpyDiffusion_j, GradEnthalpyDiffusion_j); + const su2double* EnthalpyDiffusion_i = EnthalpyDiffusion[iPoint]; + const su2double* EnthalpyDiffusion_j = EnthalpyDiffusion[jPoint]; + const su2double* GradEnthalpyDiffusion_i = GradEnthalpyDiffusion[iPoint]; + const su2double* GradEnthalpyDiffusion_j = GradEnthalpyDiffusion[jPoint]; /*--- Compute Enthalpy diffusion flux and its jacobian (for implicit iterations) ---*/ su2double flux_enthalpy_diffusion = 0.0; @@ -362,6 +379,33 @@ void CIncNSSolver::Compute_Enthalpy_Diffusion(unsigned long iEdge, CGeometry* ge if (implicit) numerics->SetJacHeatFluxDiffusion(jac_flux_enthalpy_diffusion); } +void CIncNSSolver::ComputeEnthalpyDiffusionTerms(CSolver** solver_container, const CConfig* config) { + SU2_ZONE_SCOPED + + CFluidModel* fluid_model = GetFluidModel(); + CVariable* speciesNodes = solver_container[SPECIES_SOL]->GetNodes(); + const bool implicit = (config->GetKind_TimeIntScheme() == EULER_IMPLICIT); + + /*--- Mass diffusivities are evaluated here and stored in the flow variables, from where the species solver copies + * them (CSpeciesSolver::Preprocessing), instead of being computed again. ---*/ + const bool store_diffusivity = (config->GetKind_FluidModel() == FLUID_CANTERA); + auto* flowNodes = static_cast(nodes); + const unsigned short nSpecies = config->GetnSpecies(); + + SU2_OMP_FOR_STAT(omp_chunk_size) + for (auto iPoint = 0ul; iPoint < nPoint; iPoint++) { + fluid_model->SetTDState_T(nodes->GetPrimitive(iPoint)[prim_idx.Temperature()], speciesNodes->GetSolution(iPoint)); + fluid_model->SetEddyViscosity(nodes->GetPrimitive(iPoint)[prim_idx.EddyViscosity()]); + fluid_model->GetEnthalpyDiffusivity(EnthalpyDiffusion[iPoint]); + if (implicit) fluid_model->GetGradEnthalpyDiffusivity(GradEnthalpyDiffusion[iPoint]); + if (store_diffusivity) { + for (unsigned short iVar = 0; iVar <= nSpecies; iVar++) + flowNodes->SetMassDiffusivity(iPoint, fluid_model->GetMassDiffusivity(iVar), iVar); + } + } + END_SU2_OMP_FOR +} + unsigned long CIncNSSolver::SetPrimitive_Variables(CSolver **solver_container, const CConfig *config) { SU2_ZONE_SCOPED diff --git a/SU2_CFD/src/solvers/CSpeciesFlameletSolver.cpp b/SU2_CFD/src/solvers/CSpeciesFlameletSolver.cpp index 1d39f9263381..92e45568c12f 100644 --- a/SU2_CFD/src/solvers/CSpeciesFlameletSolver.cpp +++ b/SU2_CFD/src/solvers/CSpeciesFlameletSolver.cpp @@ -84,27 +84,9 @@ void CSpeciesFlameletSolver::Preprocessing(CGeometry* geometry, CSolver** solver unsigned long n_not_in_domain_global = 0; vector scalars_vector(nVar); - unsigned long spark_iter_start, spark_duration; - bool ignition = false; + const bool ignition = SparkActive(config); auto* flowNodes = su2staticcast_p(solver_container[FLOW_SOL]->GetNodes()); - /*--- Retrieve spark ignition parameters for spark-type ignition. ---*/ - unsigned long iter; - if (config->GetMultizone_Problem()) { - iter = config->GetOuterIter(); - } else if (config->GetTime_Domain()) { - iter = config->GetTimeIter(); - } else { - iter = config->GetInnerIter(); - } - if ((flamelet_config_options.ignition_method == FLAMELET_INIT_TYPE::SPARK)) { - auto spark_init = flamelet_config_options.spark_init; - spark_iter_start = ceil(spark_init[4]); - spark_duration = ceil(spark_init[5]); - - ignition = ((iter >= spark_iter_start) && (iter <= (spark_iter_start + spark_duration))); - } - SU2_OMP_SAFE_GLOBAL_ACCESS(config->SetGlobalParam(config->GetKind_Solver(), RunTime_EqSystem); n_not_in_domain_local = 0;) @@ -148,11 +130,8 @@ void CSpeciesFlameletSolver::Preprocessing(CGeometry* geometry, CSolver** solver if (ignition) { /*--- Apply source terms within spark radius. ---*/ - su2double dist_from_center = 0, - spark_radius = flamelet_config_options.spark_init[3]; - dist_from_center = GeometryToolbox::SquaredDistance(nDim, geometry->nodes->GetCoord(i_point), flamelet_config_options.spark_init.data()); su2double T_local = flowNodes->GetTemperature(i_point); - if (dist_from_center < pow(spark_radius,2) && T_local < flamelet_config_options.Flame_T_ignition) { + if (InSpark(config, geometry->nodes->GetCoord(i_point)) && T_local < flamelet_config_options.Flame_T_ignition) { /*--- Add spark reaction rates to the sources that were just set by SetScalarSources ---*/ const su2double* current_sources = nodes->GetScalarSources(i_point); for (auto iVar = 0u; iVar < nVar; iVar++) { diff --git a/SU2_CFD/src/solvers/CSpeciesSolver.cpp b/SU2_CFD/src/solvers/CSpeciesSolver.cpp index 1d49ee5112d0..0aa5da72af0e 100644 --- a/SU2_CFD/src/solvers/CSpeciesSolver.cpp +++ b/SU2_CFD/src/solvers/CSpeciesSolver.cpp @@ -45,6 +45,7 @@ CSpeciesSolver::CSpeciesSolver(CGeometry* geometry, CConfig* config, const CSolv nVar = config->GetnSpecies(); + Initialize(geometry, config, iMesh, nVar); /*--- Initialize the solution to the far-field state everywhere. ---*/ @@ -108,7 +109,8 @@ void CSpeciesSolver::Initialize(CGeometry* geometry, CConfig* config, unsigned s nPrimVar = nVar; if (nVar > MAXNVAR) - SU2_MPI::Error("Increase static array size MAXNVAR for CSpeciesVariable and proceed.", CURRENT_FUNCTION); + SU2_MPI::Error("The number of species equations exceeds MAX_TRANSPORTED_SPECIES. Increase it in\n" + "Common/include/option_structure.hpp and recompile.", CURRENT_FUNCTION); nPoint = geometry->GetnPoint(); nPointDomain = geometry->GetnPointDomain(); @@ -317,20 +319,49 @@ void CSpeciesSolver::Preprocessing(CGeometry* geometry, CSolver** solver_contain unsigned short iMesh, unsigned short iRKStep, unsigned short RunTime_EqSystem, bool Output) { SU2_ZONE_SCOPED + const bool combustion = config->GetCombustion(); + const bool ignition = SparkActive(config); SU2_OMP_SAFE_GLOBAL_ACCESS(config->SetGlobalParam(config->GetKind_Solver(), RunTime_EqSystem);) + CFluidModel* fluid_model = solver_container[FLOW_SOL]->GetFluidModel(); + fluid_model->SetMassDiffusivityModel(config); + + /*--- The flow solver evaluates the Cantera mass diffusivities together with the enthalpy diffusion + * (CIncNSSolver::ComputeEnthalpyDiffusionTerms) and stores them in its variables. ---*/ + const bool diffusivity_from_flow = (config->GetKind_FluidModel() == FLUID_CANTERA) && config->GetEnergy_Equation(); + const CVariable* flowNodes = solver_container[FLOW_SOL]->GetNodes(); + + /*--- Set the laminar mass Diffusivity and chemical source term for the species solver. ---*/ SU2_OMP_FOR_STAT(omp_chunk_size) for (auto iPoint = 0u; iPoint < nPoint; iPoint++) { - const su2double temperature = solver_container[FLOW_SOL]->GetNodes()->GetTemperature(iPoint); + /*--- Chemical source terms are only used by the residual of the points owned by this rank. ---*/ + const bool chemistry = combustion && (iPoint < nPointDomain); + if (diffusivity_from_flow) { + for (auto iVar = 0u; iVar <= nVar; iVar++) nodes->SetDiffusivity(iPoint, flowNodes->GetDiffusivity(iPoint, iVar), iVar); + if (!chemistry) continue; + } + + su2double temperature = solver_container[FLOW_SOL]->GetNodes()->GetTemperature(iPoint); + if (ignition && InSpark(config, geometry->nodes->GetCoord(iPoint))) { + /*--- Apply ignition temperature within spark radius. ---*/ + temperature = config->GetSpark_Temperature(); + } const su2double* scalar = solver_container[SPECIES_SOL]->GetNodes()->GetSolution(iPoint); - solver_container[FLOW_SOL]->GetFluidModel()->SetMassDiffusivityModel(config); - solver_container[FLOW_SOL]->GetFluidModel()->SetTDState_T(temperature, scalar); - /*--- Recompute viscosity, important to get diffusivity correct across MPI ranks. ---*/ - nodes->SetLaminarViscosity(iPoint, solver_container[FLOW_SOL]->GetFluidModel()->GetLaminarViscosity()); - /*--- Set the laminar mass Diffusivity for the species solver. ---*/ - for (auto iVar = 0u; iVar <= nVar; iVar++) { - const su2double mass_diffusivity = solver_container[FLOW_SOL]->GetFluidModel()->GetMassDiffusivity(iVar); - nodes->SetDiffusivity(iPoint, mass_diffusivity, iVar); + fluid_model->SetTDState_T(temperature, scalar); + + if (chemistry) { + fluid_model->ComputeChemicalSourceTerm(); + nodes->SetHeatRelease(iPoint, fluid_model->GetHeatRelease()); + for (auto iVar = 0u; iVar <= nVar; iVar++) { + nodes->SetChemicalSourceTerm(iPoint, fluid_model->GetChemicalSourceTerm(iVar), iVar); + nodes->SetChemicalSourceJacobian(iPoint, fluid_model->GetChemicalSourceJacobian(iVar), iVar); + } + } + if (!diffusivity_from_flow) { + /*--- Recompute viscosity, important to get diffusivity correct across MPI ranks. ---*/ + nodes->SetLaminarViscosity(iPoint, fluid_model->GetLaminarViscosity()); + /*--- Set the laminar mass Diffusivity for the species solver. ---*/ + for (auto iVar = 0u; iVar <= nVar; iVar++) nodes->SetDiffusivity(iPoint, fluid_model->GetMassDiffusivity(iVar), iVar); } } // iPoint END_SU2_OMP_FOR @@ -620,6 +651,7 @@ void CSpeciesSolver::Source_Residual(CGeometry *geometry, CSolver **solver_conta const bool implicit = (config->GetKind_TimeIntScheme() == EULER_IMPLICIT); const bool axisymmetric = config->GetAxisymmetric(); + const bool combustion = config->GetCombustion(); if (axisymmetric) { CNumerics *numerics = numerics_container[SOURCE_FIRST_TERM + omp_get_thread_num()*MAX_TERMS]; @@ -669,7 +701,35 @@ void CSpeciesSolver::Source_Residual(CGeometry *geometry, CSolver **solver_conta if (config->GetPyCustomSource()) { CustomSourceResidual(geometry, solver_container, numerics_container, config, iMesh); } + + if (combustion) { + CNumerics* numerics = numerics_container[SOURCE_SECOND_TERM + omp_get_thread_num() * MAX_TERMS]; + + SU2_OMP_FOR_DYN(omp_chunk_size) + for (auto iPoint = 0u; iPoint < nPointDomain; iPoint++) { + /*--- Set Chemical Source Term ---*/ + numerics->SetChemicalSourceTerm(nodes->GetChemicalSourceTerm(iPoint)); + numerics->SetChemicalSourceJacobian(nodes->GetChemicalSourceJacobian(iPoint)); + + /*--- Set volume of the dual cell. ---*/ + + numerics->SetVolume(geometry->nodes->GetVolume(iPoint)); + + /*--- Update scalar sources in the fluidmodel ---*/ + + auto residual = numerics->ComputeResidual(config); + + /*--- Add Residual ---*/ + + LinSysRes.SubtractBlock(iPoint, residual); + + /*--- Implicit part ---*/ + + if (implicit) Jacobian.SubtractBlock2Diag(iPoint, residual.jacobian_i); + } + END_SU2_OMP_FOR + } } void CSpeciesSolver::SetInitialCondition(CGeometry **geometry, CSolver ***solver_container, CConfig *config, unsigned long TimeIter) { diff --git a/SU2_CFD/src/variables/CIncNSVariable.cpp b/SU2_CFD/src/variables/CIncNSVariable.cpp index 93d481956949..1482b12ad19d 100644 --- a/SU2_CFD/src/variables/CIncNSVariable.cpp +++ b/SU2_CFD/src/variables/CIncNSVariable.cpp @@ -39,6 +39,7 @@ CIncNSVariable::CIncNSVariable(su2double pressure, const su2double *velocity, su DES_LengthScale.resize(nPoint) = su2double(0.0); lesMode.resize(nPoint) = su2double(0.0); Max_Lambda_Visc.resize(nPoint); + if (config->GetKind_FluidModel() == FLUID_CANTERA) MassDiffusivity.resize(nPoint, config->GetnSpecies() + 1) = su2double(0.0); /*--- Allocate memory for the AuxVar and its gradient. See e.g. CIncEulerSolver::Source_Residual: * Axisymmetric: total-viscosity * y-vel / y-coord * Streamwise Periodic: eddy viscosity (mu_t) ---*/ @@ -59,8 +60,10 @@ bool CIncNSVariable::SetPrimVar(unsigned long iPoint, su2double eddy_visc, su2do SetPressure(iPoint); su2double Enthalpy = Solution(iPoint, nDim + 1); + FluidModel->SetTemperatureGuess(GetTemperature(iPoint)); FluidModel->SetTDState_h(Enthalpy, scalar); su2double Temperature = FluidModel->GetTemperature(); + const bool temperature_failed = FluidModel->GetTemperatureIterationFailed(); auto check_temp = SetTemperature(iPoint, Temperature, TemperatureLimits); @@ -70,11 +73,11 @@ bool CIncNSVariable::SetPrimVar(unsigned long iPoint, su2double eddy_visc, su2do /*--- Set the value of the density ---*/ - const auto check_dens = SetDensity(iPoint, FluidModel->GetDensity()); + const auto check_dens = check_temp ? true: SetDensity(iPoint, FluidModel->GetDensity()); /*--- Non-physical solution found. Revert to old values. ---*/ - if (check_dens || check_temp) { + if (check_dens || check_temp || temperature_failed) { /*--- Copy the old solution ---*/ diff --git a/SU2_CFD/src/variables/CSpeciesVariable.cpp b/SU2_CFD/src/variables/CSpeciesVariable.cpp index 34dfe46b69e2..7ec6d0e745d7 100644 --- a/SU2_CFD/src/variables/CSpeciesVariable.cpp +++ b/SU2_CFD/src/variables/CSpeciesVariable.cpp @@ -30,8 +30,13 @@ CSpeciesVariable::CSpeciesVariable(const su2double* species_inf, unsigned long npoint, unsigned long ndim, unsigned long nvar, const CConfig* config) : CScalarVariable(npoint, ndim, nvar, config) { - /*--- Allocate space for the mass diffusivity. ---*/ + /*--- Allocate space for the mass diffusivity and chemical source term. ---*/ Diffusivity.resize(nPoint, nVar + 1) = su2double(0.0); + if (config->GetCombustion()) { + SpeciesSourceTerm.resize(nPoint, nVar + 1) = su2double(0.0); + SpeciesSourceJacobian.resize(nPoint, nVar + 1) = su2double(0.0); + HeatRelease.resize(nPoint) = su2double(0.0); + } for (unsigned long iPoint = 0; iPoint < nPoint; iPoint++) for (unsigned long iVar = 0; iVar < nVar; iVar++) diff --git a/SU2_PY/SU2/io/historyMap.py b/SU2_PY/SU2/io/historyMap.py index 57392eb82073..fdab20449f1f 100644 --- a/SU2_PY/SU2/io/historyMap.py +++ b/SU2_PY/SU2/io/historyMap.py @@ -238,10 +238,10 @@ "HEADER": "bgs[P]", "TYPE": "RESIDUAL", }, - 'BGS_SPECIES_" + std::to_string(iVar': { + 'BGS_SPECIES_" + config->GetSpecies_Label(iVar': { "DESCRIPTION": "BGS residual of " "transported species.", "GROUP": "BGS_RES", - "HEADER": 'bgs[rho*Y_" + ' 'std::to_string(iVar)+"]', + "HEADER": 'bgs[rho*Y_" + ' 'config->GetSpecies_Label(iVar)+"]', "TYPE": "RESIDUAL", }, "BGS_TEMPERATURE": { @@ -581,10 +581,10 @@ "HEADER": "d[Secondary_Over_Uniformity]", "TYPE": "D_COEFFICIENT", }, - 'D_SURFACE_SPECIES_" + std::to_string(iVar': { + 'D_SURFACE_SPECIES_" + config->GetSpecies_Label(iVar': { "DESCRIPTION": "Derivative " "value", "GROUP": "D_SPECIES_COEFF", - "HEADER": 'd[Avg_Species_" + ' "std::to_string(iVar]", + "HEADER": 'd[Avg_Species_" + ' "config->GetSpecies_Label(iVar]", "TYPE": "D_COEFFICIENT", }, "D_SURFACE_STATIC_PRESSURE": { @@ -810,10 +810,10 @@ "HEADER": "max[A_Rho]", "TYPE": "RESIDUAL", }, - 'MAX_ADJ_SPECIES_" + std::to_string(iVar': { + 'MAX_ADJ_SPECIES_" + config->GetSpecies_Label(iVar': { "DESCRIPTION": "Maximum residual " "of the adjoint " "transported " "species.", "GROUP": "MAX_RES", - "HEADER": 'max[A_rho*Y_" + ' "std::to_string(iVar) + " '"]', + "HEADER": 'max[A_rho*Y_" + ' "config->GetSpecies_Label(iVar) + " '"]', "TYPE": "RESIDUAL", }, "MAX_ADJ_TEMPERATURE": { @@ -906,10 +906,10 @@ "HEADER": "max[P]", "TYPE": "RESIDUAL", }, - 'MAX_SPECIES_" + std::to_string(iVar': { + 'MAX_SPECIES_" + config->GetSpecies_Label(iVar': { "DESCRIPTION": "Maximum residual of " "transported species.", "GROUP": "MAX_RES", - "HEADER": 'max[rho*Y_" + ' 'std::to_string(iVar)+"]', + "HEADER": 'max[rho*Y_" + ' 'config->GetSpecies_Label(iVar)+"]', "TYPE": "RESIDUAL", }, "MAX_TEMPERATURE": { @@ -1057,14 +1057,14 @@ "HEADER": "rms[A_P1]", "TYPE": "RESIDUAL", }, - 'RMS_ADJ_SPECIES_" + std::to_string(iVar': { + 'RMS_ADJ_SPECIES_" + config->GetSpecies_Label(iVar': { "DESCRIPTION": "Root-mean square " "residual of the " "adjoint " "transported " "species.", "GROUP": "RMS_RES", - "HEADER": 'rms[A_rho*Y_" + ' "std::to_string(iVar) + " '"]', + "HEADER": 'rms[A_rho*Y_" + ' "config->GetSpecies_Label(iVar) + " '"]', "TYPE": "RESIDUAL", }, "RMS_ADJ_TEMPERATURE": { @@ -1178,10 +1178,10 @@ "HEADER": "rms[R]", "TYPE": "RESIDUAL", }, - 'RMS_SPECIES_" + std::to_string(iVar': { + 'RMS_SPECIES_" + config->GetSpecies_Label(iVar': { "DESCRIPTION": "Root-mean square " "residual of " "transported species.", "GROUP": "RMS_RES", - "HEADER": 'rms[rho*Y_" + ' 'std::to_string(iVar)+"]', + "HEADER": 'rms[rho*Y_" + ' 'config->GetSpecies_Label(iVar)+"]', "TYPE": "RESIDUAL", }, "RMS_TEMPERATURE": { @@ -1356,15 +1356,15 @@ "HEADER": "Secondary_Over_Uniformity", "TYPE": "COEFFICIENT", }, - 'SURFACE_SPECIES_" + std::to_string(iVar': { + 'SURFACE_SPECIES_" + config->GetSpecies_Label(iVar': { "DESCRIPTION": "Total average " 'species " + ' - "std::to_string(iVar) " + "config->GetSpecies_Label(iVar) " '+ " on all ' "markers set in " "MARKER_ANALYZE", "GROUP": "SPECIES_COEFF", - "HEADER": 'Avg_Species_" + ' "std::to_string(iVar", + "HEADER": 'Avg_Species_" + ' "config->GetSpecies_Label(iVar", "TYPE": "COEFFICIENT", }, "SURFACE_SPECIES_VARIANCE": { @@ -1690,10 +1690,10 @@ "HEADER": "dtavg[Secondary_Over_Uniformity]", "TYPE": "TAVG_D_COEFFICIENT", }, - 'TAVG_D_SURFACE_SPECIES_" + std::to_string(iVar': { + 'TAVG_D_SURFACE_SPECIES_" + config->GetSpecies_Label(iVar': { "DESCRIPTION": "weighted " "time " "average " "derivative " "value", "GROUP": "TAVG_D_SPECIES_COEFF", - "HEADER": 'dtavg[Avg_Species_" ' "+ " "std::to_string(iVar]", + "HEADER": 'dtavg[Avg_Species_" ' "+ " "config->GetSpecies_Label(iVar]", "TYPE": "TAVG_D_COEFFICIENT", }, "TAVG_D_SURFACE_STATIC_PRESSURE": { @@ -1936,10 +1936,10 @@ "HEADER": "tavg[Secondary_Over_Uniformity]", "TYPE": "TAVG_COEFFICIENT", }, - 'TAVG_SURFACE_SPECIES_" + std::to_string(iVar': { + 'TAVG_SURFACE_SPECIES_" + config->GetSpecies_Label(iVar': { "DESCRIPTION": "weighted " "time average " "value", "GROUP": "TAVG_SPECIES_COEFF", - "HEADER": 'tavg[Avg_Species_" ' "+ " "std::to_string(iVar]", + "HEADER": 'tavg[Avg_Species_" ' "+ " "config->GetSpecies_Label(iVar]", "TYPE": "TAVG_COEFFICIENT", }, "TAVG_SURFACE_STATIC_PRESSURE": { diff --git a/SU2_PY/pySU2/meson.build b/SU2_PY/pySU2/meson.build index 436eb4b0d56a..0e991da516b4 100644 --- a/SU2_PY/pySU2/meson.build +++ b/SU2_PY/pySU2/meson.build @@ -40,10 +40,14 @@ wrapper_deps = [ if get_option('enable-normal') cpp_source = swig_gen.process('pySU2.i') + swig_deps = [wrapper_deps, common_dep, su2_deps] + if get_option('enable-cantera') + swig_deps += cantera_dep + endif swig_lib = shared_library( '_pysu2', cpp_source, - dependencies: [wrapper_deps, common_dep, su2_deps], + dependencies: swig_deps, objects: [ su2_cfd_lib.extract_all_objects(), su2_def_lib.extract_objects('drivers/CDeformationDriver.cpp') diff --git a/TestCases/combustion/1step_methane_air/1S_CH4_MP1.yaml b/TestCases/combustion/1step_methane_air/1S_CH4_MP1.yaml new file mode 100644 index 000000000000..851733695e2a --- /dev/null +++ b/TestCases/combustion/1step_methane_air/1S_CH4_MP1.yaml @@ -0,0 +1,949 @@ +description: |- + CH4_MP1 mechanisme: CH4 + 2 O2 => CO2 + 2 H2O + + + Transport data from file ../transport/gri30_tran.dat. + +generator: cti2yaml +cantera-version: 3.0.1 +date: Sat, 25 Jan 2025 11:22:57 +0100 +input-files: [1S_CH4_MP1.cti] + +units: {length: cm, quantity: mol, activation-energy: cal/mol} + +phases: +- name: CH4_MP1 + thermo: ideal-gas + elements: [O, H, C, N] + species: [O2, H2O, CH4, CO2, N2] + kinetics: gas + reactions: all + state: + T: 300.0 + P: 1.01325e+05 +- name: CH4_MP1_mix + thermo: ideal-gas + elements: [O, H, C, N] + species: [O2, H2O, CH4, CO2, N2] + kinetics: gas + reactions: all + transport: mixture-averaged + state: + T: 300.0 + P: 1.01325e+05 +- name: CH4_MP1_multi + thermo: ideal-gas + elements: [O, H, C, N] + species: [O2, H2O, CH4, CO2, N2] + kinetics: gas + reactions: all + transport: multicomponent + state: + T: 300.0 + P: 1.01325e+05 + +species: +- name: H2 + composition: {H: 2} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [2.34433112, 7.98052075e-03, -1.9478151e-05, 2.01572094e-08, -7.37611761e-12, + -917.935173, 0.683010238] + - [3.3372792, -4.94024731e-05, 4.99456778e-07, -1.79566394e-10, 2.00255376e-14, + -950.158922, -3.20502331] + transport: + model: gas + geometry: linear + diameter: 2.92 + well-depth: 38.0 + polarizability: 0.79 + rotational-relaxation: 280.0 + note: TPIS78 +- name: H + composition: {H: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [2.5, 7.05332819e-13, -1.99591964e-15, 2.30081632e-18, -9.27732332e-22, + 2.54736599e+04, -0.446682853] + - [2.50000001, -2.30842973e-11, 1.61561948e-14, -4.73515235e-18, 4.98197357e-22, + 2.54736599e+04, -0.446682914] + transport: + model: gas + geometry: atom + diameter: 2.05 + well-depth: 145.0 + note: L 7/88 +- name: O + composition: {O: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [3.1682671, -3.27931884e-03, 6.64306396e-06, -6.12806624e-09, 2.11265971e-12, + 2.91222592e+04, 2.05193346] + - [2.56942078, -8.59741137e-05, 4.19484589e-08, -1.00177799e-11, 1.22833691e-15, + 2.92175791e+04, 4.78433864] + transport: + model: gas + geometry: atom + diameter: 2.75 + well-depth: 80.0 + note: L 1/90 +- name: O2 + composition: {O: 2} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [3.78245636, -2.99673416e-03, 9.84730201e-06, -9.68129509e-09, 3.24372837e-12, + -1063.94356, 3.65767573] + - [3.28253784, 1.48308754e-03, -7.57966669e-07, 2.09470555e-10, -2.16717794e-14, + -1088.45772, 5.45323129] + transport: + model: gas + geometry: linear + diameter: 3.46 + well-depth: 107.4 + polarizability: 1.6 + rotational-relaxation: 3.8 + note: TPIS89 +- name: OH + composition: {O: 1, H: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [3.99201543, -2.40131752e-03, 4.61793841e-06, -3.88113333e-09, 1.3641147e-12, + 3615.08056, -0.103925458] + - [3.09288767, 5.48429716e-04, 1.26505228e-07, -8.79461556e-11, 1.17412376e-14, + 3858.657, 4.4766961] + transport: + model: gas + geometry: linear + diameter: 2.75 + well-depth: 80.0 + note: RUS 78 +- name: H2O + composition: {H: 2, O: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [4.19864056, -2.0364341e-03, 6.52040211e-06, -5.48797062e-09, 1.77197817e-12, + -3.02937267e+04, -0.849032208] + - [3.03399249, 2.17691804e-03, -1.64072518e-07, -9.7041987e-11, 1.68200992e-14, + -3.00042971e+04, 4.9667701] + transport: + model: gas + geometry: nonlinear + diameter: 2.6 + well-depth: 572.4 + dipole: 1.84 + rotational-relaxation: 4.0 + note: L 8/89 +- name: HO2 + composition: {H: 1, O: 2} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [4.30179801, -4.74912051e-03, 2.11582891e-05, -2.42763894e-08, 9.29225124e-12, + 294.80804, 3.71666245] + - [4.0172109, 2.23982013e-03, -6.3365815e-07, 1.1424637e-10, -1.07908535e-14, + 111.856713, 3.78510215] + transport: + model: gas + geometry: nonlinear + diameter: 3.46 + well-depth: 107.4 + rotational-relaxation: 1.0 + note: L 5/89 +- name: H2O2 + composition: {H: 2, O: 2} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [4.27611269, -5.42822417e-04, 1.67335701e-05, -2.15770813e-08, 8.62454363e-12, + -1.77025821e+04, 3.43505074] + - [4.16500285, 4.90831694e-03, -1.90139225e-06, 3.71185986e-10, -2.87908305e-14, + -1.78617877e+04, 2.91615662] + transport: + model: gas + geometry: nonlinear + diameter: 3.46 + well-depth: 107.4 + rotational-relaxation: 3.8 + note: L 7/88 +- name: C + composition: {C: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [2.55423955, -3.21537724e-04, 7.33792245e-07, -7.32234889e-10, 2.66521446e-13, + 8.54438832e+04, 4.53130848] + - [2.49266888, 4.79889284e-05, -7.2433502e-08, 3.74291029e-11, -4.87277893e-15, + 8.54512953e+04, 4.80150373] + transport: + model: gas + geometry: atom + diameter: 3.3 + well-depth: 71.4 + note: L11/88 +- name: CH + composition: {C: 1, H: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [3.48981665, 3.23835541e-04, -1.68899065e-06, 3.16217327e-09, -1.40609067e-12, + 7.07972934e+04, 2.08401108] + - [2.87846473, 9.70913681e-04, 1.44445655e-07, -1.30687849e-10, 1.76079383e-14, + 7.10124364e+04, 5.48497999] + transport: + model: gas + geometry: linear + diameter: 2.75 + well-depth: 80.0 + note: TPIS79 +- name: CH2 + composition: {C: 1, H: 2} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [3.76267867, 9.68872143e-04, 2.79489841e-06, -3.85091153e-09, 1.68741719e-12, + 4.60040401e+04, 1.56253185] + - [2.87410113, 3.65639292e-03, -1.40894597e-06, 2.60179549e-10, -1.87727567e-14, + 4.6263604e+04, 6.17119324] + transport: + model: gas + geometry: linear + diameter: 3.8 + well-depth: 144.0 + note: L S/93 +- name: CH2(S) + composition: {C: 1, H: 2} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [4.19860411, -2.36661419e-03, 8.2329622e-06, -6.68815981e-09, 1.94314737e-12, + 5.04968163e+04, -0.769118967] + - [2.29203842, 4.65588637e-03, -2.01191947e-06, 4.17906e-10, -3.39716365e-14, + 5.09259997e+04, 8.62650169] + transport: + model: gas + geometry: linear + diameter: 3.8 + well-depth: 144.0 + note: L S/93 +- name: CH3 + composition: {C: 1, H: 3} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [3.6735904, 2.01095175e-03, 5.73021856e-06, -6.87117425e-09, 2.54385734e-12, + 1.64449988e+04, 1.60456433] + - [2.28571772, 7.23990037e-03, -2.98714348e-06, 5.95684644e-10, -4.67154394e-14, + 1.67755843e+04, 8.48007179] + transport: + model: gas + geometry: linear + diameter: 3.8 + well-depth: 144.0 + note: L11/89 +- name: CH4 + composition: {C: 1, H: 4} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [5.14987613, -0.0136709788, 4.91800599e-05, -4.84743026e-08, 1.66693956e-11, + -1.02466476e+04, -4.64130376] + - [0.074851495, 0.0133909467, -5.73285809e-06, 1.22292535e-09, -1.0181523e-13, + -9468.34459, 18.437318] + transport: + model: gas + geometry: nonlinear + diameter: 3.75 + well-depth: 141.4 + polarizability: 2.6 + rotational-relaxation: 13.0 + note: L 8/88 +- name: CO + composition: {C: 1, O: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [3.57953347, -6.1035368e-04, 1.01681433e-06, 9.07005884e-10, -9.04424499e-13, + -1.4344086e+04, 3.50840928] + - [2.71518561, 2.06252743e-03, -9.98825771e-07, 2.30053008e-10, -2.03647716e-14, + -1.41518724e+04, 7.81868772] + transport: + model: gas + geometry: linear + diameter: 3.65 + well-depth: 98.1 + polarizability: 1.95 + rotational-relaxation: 1.8 + note: TPIS79 +- name: CO2 + composition: {C: 1, O: 2} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [2.35677352, 8.98459677e-03, -7.12356269e-06, 2.45919022e-09, -1.43699548e-13, + -4.83719697e+04, 9.90105222] + - [3.85746029, 4.41437026e-03, -2.21481404e-06, 5.23490188e-10, -4.72084164e-14, + -4.8759166e+04, 2.27163806] + transport: + model: gas + geometry: linear + diameter: 3.76 + well-depth: 244.0 + polarizability: 2.65 + rotational-relaxation: 2.1 + note: L 7/88 +- name: HCO + composition: {H: 1, C: 1, O: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [4.22118584, -3.24392532e-03, 1.37799446e-05, -1.33144093e-08, 4.33768865e-12, + 3839.56496, 3.39437243] + - [2.77217438, 4.95695526e-03, -2.48445613e-06, 5.89161778e-10, -5.33508711e-14, + 4011.91815, 9.79834492] + transport: + model: gas + geometry: nonlinear + diameter: 3.59 + well-depth: 498.0 + note: L12/89 +- name: CH2O + composition: {H: 2, C: 1, O: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [4.79372315, -9.90833369e-03, 3.73220008e-05, -3.79285261e-08, 1.31772652e-11, + -1.43089567e+04, 0.6028129] + - [1.76069008, 9.20000082e-03, -4.42258813e-06, 1.00641212e-09, -8.8385564e-14, + -1.39958323e+04, 13.656323] + transport: + model: gas + geometry: nonlinear + diameter: 3.59 + well-depth: 498.0 + rotational-relaxation: 2.0 + note: L 8/88 +- name: CH2OH + composition: {C: 1, H: 3, O: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [3.86388918, 5.59672304e-03, 5.93271791e-06, -1.04532012e-08, 4.36967278e-12, + -3193.91367, 5.47302243] + - [3.69266569, 8.64576797e-03, -3.7510112e-06, 7.87234636e-10, -6.48554201e-14, + -3242.50627, 5.81043215] + transport: + model: gas + geometry: nonlinear + diameter: 3.69 + well-depth: 417.0 + dipole: 1.7 + rotational-relaxation: 2.0 + note: GUNL93 +- name: CH3O + composition: {C: 1, H: 3, O: 1} + thermo: + model: NASA7 + temperature-ranges: [300.0, 1000.0, 3000.0] + data: + - [2.106204, 7.216595e-03, 5.338472e-06, -7.377636e-09, 2.07561e-12, + 978.6011, 13.152177] + - [3.770799, 7.871497e-03, -2.656384e-06, 3.944431e-10, -2.112616e-14, + 127.83252, 2.929575] + transport: + model: gas + geometry: nonlinear + diameter: 3.69 + well-depth: 417.0 + dipole: 1.7 + rotational-relaxation: 2.0 + note: '121686' +- name: CH3OH + composition: {C: 1, H: 4, O: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [5.71539582, -0.0152309129, 6.52441155e-05, -7.10806889e-08, 2.61352698e-11, + -2.56427656e+04, -1.50409823] + - [1.78970791, 0.0140938292, -6.36500835e-06, 1.38171085e-09, -1.1706022e-13, + -2.53748747e+04, 14.5023623] + transport: + model: gas + geometry: nonlinear + diameter: 3.63 + well-depth: 481.8 + rotational-relaxation: 1.0 + note: L 8/88 +- name: C2H + composition: {C: 2, H: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [2.88965733, 0.0134099611, -2.84769501e-05, 2.94791045e-08, -1.09331511e-11, + 6.68393932e+04, 6.22296438] + - [3.16780652, 4.75221902e-03, -1.83787077e-06, 3.04190252e-10, -1.7723277e-14, + 6.7121065e+04, 6.63589475] + transport: + model: gas + geometry: linear + diameter: 4.1 + well-depth: 209.0 + rotational-relaxation: 2.5 + note: L 1/91 +- name: C2H2 + composition: {C: 2, H: 2} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [0.808681094, 0.0233615629, -3.55171815e-05, 2.80152437e-08, -8.50072974e-12, + 2.64289807e+04, 13.9397051] + - [4.14756964, 5.96166664e-03, -2.37294852e-06, 4.67412171e-10, -3.61235213e-14, + 2.59359992e+04, -1.23028121] + transport: + model: gas + geometry: linear + diameter: 4.1 + well-depth: 209.0 + rotational-relaxation: 2.5 + note: L 1/91 +- name: C2H3 + composition: {C: 2, H: 3} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [3.21246645, 1.51479162e-03, 2.59209412e-05, -3.57657847e-08, 1.47150873e-11, + 3.48598468e+04, 8.51054025] + - [3.016724, 0.0103302292, -4.68082349e-06, 1.01763288e-09, -8.62607041e-14, + 3.46128739e+04, 7.78732378] + transport: + model: gas + geometry: nonlinear + diameter: 4.1 + well-depth: 209.0 + rotational-relaxation: 1.0 + note: L 2/92 +- name: C2H4 + composition: {C: 2, H: 4} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [3.95920148, -7.57052247e-03, 5.70990292e-05, -6.91588753e-08, 2.69884373e-11, + 5089.77593, 4.09733096] + - [2.03611116, 0.0146454151, -6.71077915e-06, 1.47222923e-09, -1.25706061e-13, + 4939.88614, 10.3053693] + transport: + model: gas + geometry: nonlinear + diameter: 3.97 + well-depth: 280.8 + rotational-relaxation: 1.5 + note: L 1/91 +- name: C2H5 + composition: {C: 2, H: 5} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [4.30646568, -4.18658892e-03, 4.97142807e-05, -5.99126606e-08, 2.30509004e-11, + 1.28416265e+04, 4.70720924] + - [1.95465642, 0.0173972722, -7.98206668e-06, 1.75217689e-09, -1.49641576e-13, + 1.285752e+04, 13.4624343] + transport: + model: gas + geometry: nonlinear + diameter: 4.3 + well-depth: 252.3 + rotational-relaxation: 1.5 + note: L12/92 +- name: C2H6 + composition: {C: 2, H: 6} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [4.29142492, -5.5015427e-03, 5.99438288e-05, -7.08466285e-08, 2.68685771e-11, + -1.15222055e+04, 2.66682316] + - [1.0718815, 0.0216852677, -1.00256067e-05, 2.21412001e-09, -1.9000289e-13, + -1.14263932e+04, 15.1156107] + transport: + model: gas + geometry: nonlinear + diameter: 4.3 + well-depth: 252.3 + rotational-relaxation: 1.5 + note: L 8/88 +- name: HCCO + composition: {H: 1, C: 2, O: 1} + thermo: + model: NASA7 + temperature-ranges: [300.0, 1000.0, 4000.0] + data: + - [2.2517214, 0.017655021, -2.3729101e-05, 1.7275759e-08, -5.0664811e-12, + 2.0059449e+04, 12.490417] + - [5.6282058, 4.0853401e-03, -1.5934547e-06, 2.8626052e-10, -1.9407832e-14, + 1.9327215e+04, -3.9302595] + transport: + model: gas + geometry: nonlinear + diameter: 2.5 + well-depth: 150.0 + rotational-relaxation: 1.0 + note: SRIC91 +- name: CH2CO + composition: {C: 2, H: 2, O: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [2.1358363, 0.0181188721, -1.73947474e-05, 9.34397568e-09, -2.01457615e-12, + -7042.91804, 12.215648] + - [4.51129732, 9.00359745e-03, -4.16939635e-06, 9.23345882e-10, -7.94838201e-14, + -7551.05311, 0.632247205] + transport: + model: gas + geometry: nonlinear + diameter: 3.97 + well-depth: 436.0 + rotational-relaxation: 2.0 + note: L 5/90 +- name: HCCOH + composition: {C: 2, O: 1, H: 2} + thermo: + model: NASA7 + temperature-ranges: [300.0, 1000.0, 5000.0] + data: + - [1.2423733, 0.031072201, -5.0866864e-05, 4.3137131e-08, -1.4014594e-11, + 8031.6143, 13.874319] + - [5.9238291, 6.79236e-03, -2.5658564e-06, 4.4987841e-10, -2.9940101e-14, + 7264.626, -7.6017742] + transport: + model: gas + geometry: nonlinear + diameter: 3.97 + well-depth: 436.0 + rotational-relaxation: 2.0 + note: SRI91 +- name: N + composition: {N: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 6000.0] + data: + - [2.5, 0.0, 0.0, 0.0, 0.0, 5.6104637e+04, 4.1939087] + - [2.4159429, 1.7489065e-04, -1.1902369e-07, 3.0226245e-11, -2.0360982e-15, + 5.6133773e+04, 4.6496096] + transport: + model: gas + geometry: atom + diameter: 3.3 + well-depth: 71.4 + note: L 6/88 +- name: NH + composition: {N: 1, H: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 6000.0] + data: + - [3.4929085, 3.1179198e-04, -1.4890484e-06, 2.4816442e-09, -1.0356967e-12, + 4.1880629e+04, 1.8483278] + - [2.7836928, 1.329843e-03, -4.2478047e-07, 7.8348501e-11, -5.504447e-15, + 4.2120848e+04, 5.7407799] + transport: + model: gas + geometry: linear + diameter: 2.65 + well-depth: 80.0 + rotational-relaxation: 4.0 + note: And94 +- name: NH2 + composition: {N: 1, H: 2} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 6000.0] + data: + - [4.2040029, -2.1061385e-03, 7.1068348e-06, -5.6115197e-09, 1.6440717e-12, + 2.188591e+04, -0.14184248] + - [2.8347421, 3.2073082e-03, -9.3390804e-07, 1.3702953e-10, -7.9206144e-15, + 2.2171957e+04, 6.5204163] + transport: + model: gas + geometry: nonlinear + diameter: 2.65 + well-depth: 80.0 + polarizability: 2.26 + rotational-relaxation: 4.0 + note: And89 +- name: NH3 + composition: {N: 1, H: 3} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 6000.0] + data: + - [4.2860274, -4.660523e-03, 2.1718513e-05, -2.2808887e-08, 8.2638046e-12, + -6741.7285, -0.62537277] + - [2.6344521, 5.666256e-03, -1.7278676e-06, 2.3867161e-10, -1.2578786e-14, + -6544.6958, 6.5662928] + transport: + model: gas + geometry: nonlinear + diameter: 2.92 + well-depth: 481.0 + dipole: 1.47 + rotational-relaxation: 10.0 + note: J 6/77 +- name: NNH + composition: {N: 2, H: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 6000.0] + data: + - [4.3446927, -4.8497072e-03, 2.0059459e-05, -2.1726464e-08, 7.9469539e-12, + 2.8791973e+04, 2.977941] + - [3.7667544, 2.8915082e-03, -1.041662e-06, 1.6842594e-10, -1.0091896e-14, + 2.8650697e+04, 4.4705067] + transport: + model: gas + geometry: nonlinear + diameter: 3.8 + well-depth: 71.4 + rotational-relaxation: 1.0 + note: T07/93 +- name: NO + composition: {N: 1, O: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 6000.0] + data: + - [4.2184763, -4.638976e-03, 1.1041022e-05, -9.3361354e-09, 2.803577e-12, + 9844.623, 2.2808464] + - [3.2606056, 1.1911043e-03, -4.2917048e-07, 6.9457669e-11, -4.0336099e-15, + 9920.9746, 6.3693027] + transport: + model: gas + geometry: linear + diameter: 3.62 + well-depth: 97.53 + polarizability: 1.76 + rotational-relaxation: 4.0 + note: RUS 78 +- name: NO2 + composition: {N: 1, O: 2} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 6000.0] + data: + - [3.9440312, -1.585429e-03, 1.6657812e-05, -2.0475426e-08, 7.8350564e-12, + 2896.6179, 6.3119917] + - [4.8847542, 2.1723956e-03, -8.2806906e-07, 1.574751e-10, -1.0510895e-14, + 2316.4983, -0.11741695] + transport: + model: gas + geometry: nonlinear + diameter: 3.5 + well-depth: 200.0 + rotational-relaxation: 1.0 + note: L 7/88 +- name: N2O + composition: {N: 2, O: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 6000.0] + data: + - [2.2571502, 0.011304728, -1.3671319e-05, 9.6819806e-09, -2.9307182e-12, + 8741.7744, 10.757992] + - [4.8230729, 2.6270251e-03, -9.5850874e-07, 1.6000712e-10, -9.7752303e-15, + 8073.4048, -2.2017207] + transport: + model: gas + geometry: linear + diameter: 3.83 + well-depth: 232.4 + rotational-relaxation: 1.0 + note: L 7/88 +- name: HNO + composition: {H: 1, N: 1, O: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 6000.0] + data: + - [4.5334916, -5.6696171e-03, 1.8473207e-05, -1.7137094e-08, 5.5454573e-12, + 1.1548297e+04, 1.7498417] + - [2.9792509, 3.4944059e-03, -7.8549778e-07, 5.7479594e-11, -1.9335916e-16, + 1.1750582e+04, 8.6063728] + transport: + model: gas + geometry: nonlinear + diameter: 3.49 + well-depth: 116.7 + rotational-relaxation: 1.0 + note: And93 +- name: CN + composition: {C: 1, N: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 6000.0] + data: + - [3.6129351, -9.5551327e-04, 2.1442977e-06, -3.1516323e-10, -4.6430356e-13, + 5.170834e+04, 3.9804995] + - [3.7459805, 4.3450775e-05, 2.9705984e-07, -6.8651806e-11, 4.4134173e-15, + 5.1536188e+04, 2.7867601] + transport: + model: gas + geometry: linear + diameter: 3.86 + well-depth: 75.0 + rotational-relaxation: 1.0 + note: HBH92 +- name: HCN + composition: {H: 1, C: 1, N: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 6000.0] + data: + - [2.2589886, 0.01005117, -1.3351763e-05, 1.0092349e-08, -3.0089028e-12, + 1.4712633e+04, 8.9164419] + - [3.8022392, 3.1464228e-03, -1.0632185e-06, 1.6619757e-10, -9.799757e-15, + 1.4407292e+04, 1.5754601] + transport: + model: gas + geometry: linear + diameter: 3.63 + well-depth: 569.0 + rotational-relaxation: 1.0 + note: GRI/98 +- name: H2CN + composition: {H: 2, C: 1, N: 1} + thermo: + model: NASA7 + temperature-ranges: [300.0, 1000.0, 4000.0] + data: + - [2.851661, 5.6952331e-03, 1.07114e-06, -1.622612e-09, -2.3511081e-13, + 2.863782e+04, 8.9927511] + - [5.209703, 2.9692911e-03, -2.8555891e-07, -1.63555e-10, 3.0432589e-14, + 2.7677109e+04, -4.444478] + transport: + model: gas + geometry: linear + diameter: 3.63 + well-depth: 569.0 + rotational-relaxation: 1.0 + note: '41687' +- name: HCNN + composition: {C: 1, N: 2, H: 1} + thermo: + model: NASA7 + temperature-ranges: [300.0, 1000.0, 5000.0] + data: + - [2.5243194, 0.015960619, -1.8816354e-05, 1.212554e-08, -3.2357378e-12, + 5.4261984e+04, 11.67587] + - [5.8946362, 3.9895959e-03, -1.598238e-06, 2.9249395e-10, -2.0094686e-14, + 5.3452941e+04, -5.1030502] + transport: + model: gas + geometry: nonlinear + diameter: 2.5 + well-depth: 150.0 + rotational-relaxation: 1.0 + note: SRI/94 +- name: HCNO + composition: {H: 1, N: 1, C: 1, O: 1} + thermo: + model: NASA7 + temperature-ranges: [300.0, 1382.0, 5000.0] + data: + - [2.64727989, 0.0127505342, -1.04794236e-05, 4.41432836e-09, -7.57521466e-13, + 1.92990252e+04, 10.7332972] + - [6.59860456, 3.02778626e-03, -1.07704346e-06, 1.71666528e-10, -1.01439391e-14, + 1.79661339e+04, -10.3306599] + transport: + model: gas + geometry: nonlinear + diameter: 3.83 + well-depth: 232.4 + rotational-relaxation: 1.0 + note: BDEA94 +- name: HOCN + composition: {H: 1, N: 1, C: 1, O: 1} + thermo: + model: NASA7 + temperature-ranges: [300.0, 1368.0, 5000.0] + data: + - [3.78604952, 6.88667922e-03, -3.21487864e-06, 5.17195767e-10, 1.19360788e-14, + -2826.984, 5.63292162] + - [5.89784885, 3.16789393e-03, -1.11801064e-06, 1.77243144e-10, -1.04339177e-14, + -3706.53331, -6.18167825] + transport: + model: gas + geometry: nonlinear + diameter: 3.83 + well-depth: 232.4 + rotational-relaxation: 1.0 + note: BDEA94 +- name: HNCO + composition: {H: 1, N: 1, C: 1, O: 1} + thermo: + model: NASA7 + temperature-ranges: [300.0, 1478.0, 5000.0] + data: + - [3.63096317, 7.30282357e-03, -2.28050003e-06, -6.61271298e-10, 3.62235752e-13, + -1.55873636e+04, 6.19457727] + - [6.22395134, 3.17864004e-03, -1.09378755e-06, 1.70735163e-10, -9.95021955e-15, + -1.66599344e+04, -8.38224741] + transport: + model: gas + geometry: nonlinear + diameter: 3.83 + well-depth: 232.4 + rotational-relaxation: 1.0 + note: BDEA94 +- name: NCO + composition: {N: 1, C: 1, O: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 6000.0] + data: + - [2.8269308, 8.8051688e-03, -8.3866134e-06, 4.8016964e-09, -1.3313595e-12, + 1.4682477e+04, 9.5504646] + - [5.1521845, 2.3051761e-03, -8.8033153e-07, 1.4789098e-10, -9.0977996e-15, + 1.4004123e+04, -2.544266] + transport: + model: gas + geometry: linear + diameter: 3.83 + well-depth: 232.4 + rotational-relaxation: 1.0 + note: EA 93 +- name: N2 + composition: {N: 2} + thermo: + model: NASA7 + temperature-ranges: [300.0, 1000.0, 5000.0] + data: + - [3.298677, 1.4082404e-03, -3.963222e-06, 5.641515e-09, -2.444854e-12, + -1020.8999, 3.950372] + - [2.92664, 1.4879768e-03, -5.68476e-07, 1.0097038e-10, -6.753351e-15, + -922.7977, 5.980528] + transport: + model: gas + geometry: linear + diameter: 3.62 + well-depth: 97.53 + polarizability: 1.76 + rotational-relaxation: 4.0 + note: '121286' +- name: AR + composition: {Ar: 1} + thermo: + model: NASA7 + temperature-ranges: [300.0, 1000.0, 5000.0] + data: + - [2.5, 0.0, 0.0, 0.0, 0.0, -745.375, 4.366] + - [2.5, 0.0, 0.0, 0.0, 0.0, -745.375, 4.366] + transport: + model: gas + geometry: atom + diameter: 3.33 + well-depth: 136.5 + note: '120186' +- name: C3H7 + composition: {C: 3, H: 7} + thermo: + model: NASA7 + temperature-ranges: [300.0, 1000.0, 5000.0] + data: + - [1.0515518, 0.02599198, 2.380054e-06, -1.9609569e-08, 9.373247e-12, + 1.0631863e+04, 21.122559] + - [7.7026987, 0.016044203, -5.283322e-06, 7.629859e-10, -3.9392284e-14, + 8298.4336, -15.48018] + transport: + model: gas + geometry: nonlinear + diameter: 4.98 + well-depth: 266.8 + rotational-relaxation: 1.0 + note: L 9/84 +- name: C3H8 + composition: {C: 3, H: 8} + thermo: + model: NASA7 + temperature-ranges: [300.0, 1000.0, 5000.0] + data: + - [0.93355381, 0.026424579, 6.1059727e-06, -2.1977499e-08, 9.5149253e-12, + -1.395852e+04, 19.201691] + - [7.5341368, 0.018872239, -6.2718491e-06, 9.1475649e-10, -4.7838069e-14, + -1.6467516e+04, -17.892349] + transport: + model: gas + geometry: nonlinear + diameter: 4.98 + well-depth: 266.8 + rotational-relaxation: 1.0 + note: L 4/85 +- name: CH2CHO + composition: {O: 1, H: 3, C: 2} + thermo: + model: NASA7 + temperature-ranges: [300.0, 1000.0, 5000.0] + data: + - [3.409062, 0.010738574, 1.891492e-06, -7.158583e-09, 2.867385e-12, + 1521.4766, 9.55829] + - [5.97567, 8.130591e-03, -2.743624e-06, 4.070304e-10, -2.176017e-14, + 490.3218, -5.045251] + transport: + model: gas + geometry: nonlinear + diameter: 3.97 + well-depth: 436.0 + rotational-relaxation: 2.0 + note: SAND86 +- name: CH3CHO + composition: {C: 2, H: 4, O: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 6000.0] + data: + - [4.7294595, -3.1932858e-03, 4.7534921e-05, -5.7458611e-08, 2.1931112e-11, + -2.1572878e+04, 4.1030159] + - [5.4041108, 0.011723059, -4.2263137e-06, 6.8372451e-10, -4.0984863e-14, + -2.2593122e+04, -3.4807917] + transport: + model: gas + geometry: nonlinear + diameter: 3.97 + well-depth: 436.0 + rotational-relaxation: 2.0 + note: L 8/88 + +reactions: +- equation: ' CH4 + 2 O2 => CO2 + 2 H2O ' # Reaction 1 + rate-constant: {A: 1.1e+10, b: 0.0, Ea: 20000} + orders: {CH4: 1.0, O2: 0.5} diff --git a/TestCases/combustion/1step_methane_air/2d_slit_burner_fluid.cfg b/TestCases/combustion/1step_methane_air/2d_slit_burner_fluid.cfg new file mode 100644 index 000000000000..6ca69b298c8f --- /dev/null +++ b/TestCases/combustion/1step_methane_air/2d_slit_burner_fluid.cfg @@ -0,0 +1,173 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% % +% SU2 configuration file % +% Case description: Laminar premixed hydrogen 3D slit burner % +% Author: Cristopher Morales Ubal % +% Institution: Eindhoven University of Technology % +% Date: 2024/06/27 % +% File Version 8.5.0 "Harrier" % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% ------------- DIRECT, ADJOINT, AND LINEARIZED PROBLEM DEFINITION ------------% +% +SOLVER= INC_NAVIER_STOKES +KIND_TURB_MODEL= NONE +RESTART_SOL = YES +WRT_ZONE_HIST= YES + +% ---------------- INCOMPRESSIBLE FLOW CONDITION DEFINITION -------------------% +% +INC_DENSITY_MODEL= VARIABLE +INC_VELOCITY_INIT= (0.0, 0.2, 0.0) %0.565 +INC_ENERGY_EQUATION= YES +INC_TEMPERATURE_INIT= 300.0 +THERMODYNAMIC_PRESSURE= 101325.0 +INC_NONDIM= DIMENSIONAL + +% -------------------- FLUID MODEL --------------------------------------- % +% +FLUID_MODEL= FLUID_CANTERA +CANTERA_COMBUSTION = YES +VISCOSITY_MODEL= CANTERA +CONDUCTIVITY_MODEL= CANTERA +% +CANTERA_SPECIES_NAMES= O2, H2O, CH4, CO2, N2 +%H2, H, O, O2, OH, H2O, HO2, H2O2, AR, N2 + +CANTERA_MECHANISM_FILE= 1S_CH4_MP1.yaml %h2o2.yaml + +CANTERA_PHASE_NAME= CH4_MP1_mix +%ohmech +CANTERA_TRANSPORT_MODEL= unity-Lewis-number +%mixture-averaged % multicomponent % unity-Lewis-number +% +% -------------------- SCALAR TRANSPORT ---------------------------------------% +% +% Using an artificial spark to ignite the solution at some location and iteration +FLAME_INIT_METHOD= SPARK +% Spark parameters in order: +% x-location of spark center (m) +% y-location of spark center (m) +% z-location of spark center (m) +% Spark radius (m) +% Iteration at which the artificial spark starts +% Spark iteration duration +SPARK_INIT= (0.0, 0.002, 0.0, 1e-3, 100, 50) +% (0.0, 0.002, 0.0, 1e-3, 600, 300) +% Controlling variable source terms applied within the spark sphere for the spark +% duration. +CANTERA_SPARK_TEMPERATURE= 1200 +%1400 1200 %1500 +% equivalence ratio phi=0.8 +SPECIES_INIT= 0.222598, 0.0, 0.044642, 0.0 +SPECIES_CLIPPING=YES +SPECIES_CLIPPING_MIN= 0.0, 0.0, 0.0, 0.0 +SPECIES_CLIPPING_MAX= 1.0, 1.0, 1.0, 1.0 + +% -------------------- BOUNDARY CONDITION DEFINITION --------------------------% +% +% +MARKER_SYM= ( symmetry ) +MARKER_ISOTHERMAL = (solid_fluid, 300) +% +SPECIFIED_INLET_PROFILE= NO +INLET_FILENAME= inlet_venturi.dat +INC_INLET_TYPE= VELOCITY_INLET +MARKER_INLET= ( inlet, 300, 0.2, 0.0, 1.0, 0.0) +MARKER_INLET_SPECIES = (inlet, 0.222598, 0.0, 0.044642, 0.0) +% +% +INC_OUTLET_TYPE= PRESSURE_OUTLET +MARKER_OUTLET= ( outlet, 0.0 ) +% +% ------------- COMMON PARAMETERS DEFINING THE NUMERICAL METHOD ---------------% +% +NUM_METHOD_GRAD= WEIGHTED_LEAST_SQUARES +% +CFL_NUMBER= 1000 +CFL_ADAPT= YES +CFL_ADAPT_PARAM= ( 0.95, 1.01, 100, 10000, 0.1, 100) + +CFL_REDUCTION_SPECIES= 1.0 +% Run commented Iter for good results +ITER= 10000 +% +% +% ------------------------ LINEAR SOLVER DEFINITION ---------------------------% +% +LINEAR_SOLVER= FGMRES +LINEAR_SOLVER_PREC= ILU +LINEAR_SOLVER_ERROR= 1E-03 +LINEAR_SOLVER_ITER= 10 +% +% -------------------- FLOW AND SPECIES NUMERICAL METHOD DEFINITION -----------------------% +% +CONV_NUM_METHOD_FLOW= FDS +MUSCL_FLOW= NO +SLOPE_LIMITER_FLOW = NONE +TIME_DISCRE_FLOW= EULER_IMPLICIT + +% -------------------------- MULTIGRID PARAMETERS -----------------------------% +% +MGLEVEL=1 +MGCYCLE= V_CYCLE +MG_STARTUP_ITER= 40 +MG_STARTUP_CONVERGENCE= -3 +MG_STARTUP_STAGNATION= 0.99 +MG_STARTUP_STAGNATION_ITER= 5 +MG_MIN_MESHSIZE= 200 + +MG_PRE_SMOOTH= (2, 2, 2, 2, 2, 2 ) +MG_POST_SMOOTH= ( 0, 2, 2, 2, 2, 2) +MG_CORRECTION_SMOOTH= ( 1, 1, 1, 1, 1 ) + +MG_DAMP_RESTRICTION= 0.5 +MG_DAMP_PROLONGATION= 0.5 + +MG_SMOOTH_OUTPUT= NO +MG_SMOOTH_EARLY_EXIT= YES +MG_CFL_SCALING= ( 0.3, 0.3, 0.3, 0.3 ) + +MG_IMPLICIT_LINES= YES + +% +% -------------------- SCALAR TRANSPORT ---------------------------------------% +% +KIND_SCALAR_MODEL= SPECIES_TRANSPORT +DIFFUSIVITY_MODEL= CANTERA +DIFFUSIVITY_CONSTANT= 0.00005 +% +CONV_NUM_METHOD_SPECIES= BOUNDED_SCALAR +MUSCL_SPECIES= NO +SLOPE_LIMITER_SPECIES = NONE +% +TIME_DISCRE_SPECIES= EULER_IMPLICIT +% +% --------------------------- CONVERGENCE PARAMETERS --------------------------% +% +CONV_FIELD= RMS_PRESSURE, RMS_VELOCITY-X, RMS_VELOCITY-Y, RMS_SPECIES_CH4 +CONV_RESIDUAL_MINVAL= -12 +CONV_STARTITER= 10 +% +% ------------------------- INPUT/OUTPUT INFORMATION --------------------------% +% +SCREEN_OUTPUT= INNER_ITER WALL_TIME \ + RMS_RES \ + LINSOL_ITER LINSOL_RESIDUAL AVG_CFL +SCREEN_WRT_FREQ_INNER= 10 +% +HISTORY_OUTPUT= ITER RMS_RES LINSOL +CONV_FILENAME= history +MARKER_ANALYZE= inlet, outlet +MARKER_ANALYZE_AVERAGE= AREA +% +OUTPUT_FILES= RESTART, PARAVIEW_MULTIBLOCK +VOLUME_OUTPUT= RESIDUAL, PRIMITIVE, SOLUTION, TIMESTEP +MESH_FILENAME= 2D_slit_burner_mesh.su2 +OUTPUT_WRT_FREQ= 100 +% +READ_BINARY_RESTART= YES +RESTART_FILENAME= restart +SOLUTION_FILENAME= solution +WRT_PERFORMANCE= YES diff --git a/TestCases/combustion/h2o2_hydrogen_air/2d_slit_burner_h2.cfg b/TestCases/combustion/h2o2_hydrogen_air/2d_slit_burner_h2.cfg new file mode 100644 index 000000000000..43773d3fa455 --- /dev/null +++ b/TestCases/combustion/h2o2_hydrogen_air/2d_slit_burner_h2.cfg @@ -0,0 +1,175 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% % +% SU2 configuration file % +% Case description: Laminar premixed hydrogen-air (phi=0.6) 2D slit burner, % +% detailed chemistry (h2o2.yaml, 10 species) with Cantera % +% and mixture-averaged transport (preferential diffusion) % +% Based on: the one-step methane-air slit burner case (C. Morales Ubal, TU/e) % +% Laminar flame speed (Cantera, mixture-averaged, 300 K, 1 atm): 0.777 m/s % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% ------------- DIRECT, ADJOINT, AND LINEARIZED PROBLEM DEFINITION ------------% +% +SOLVER= INC_NAVIER_STOKES +KIND_TURB_MODEL= NONE +MATH_PROBLEM= DIRECT +RESTART_SOL = YES +WRT_ZONE_HIST= YES + +% ---------------- INCOMPRESSIBLE FLOW CONDITION DEFINITION -------------------% +% +INC_DENSITY_MODEL= VARIABLE +INC_VELOCITY_INIT= (0.0, 2.0, 0.0) % 2.6 times the flame speed, avoids flashback +INC_ENERGY_EQUATION= YES +INC_TEMPERATURE_INIT= 300.0 +THERMODYNAMIC_PRESSURE= 101325.0 +INC_NONDIM= DIMENSIONAL + +% -------------------- FLUID MODEL --------------------------------------- % +% +FLUID_MODEL= FLUID_CANTERA +CANTERA_COMBUSTION= YES +CANTERA_CORRECTION_VELOCITY= YES +CANTERA_SOURCE_JACOBIAN= YES +CANTERA_DC_MIN_TEMP= 500 +VISCOSITY_MODEL= CANTERA +CONDUCTIVITY_MODEL= CANTERA +% +CANTERA_SPECIES_NAMES= H2, H, O, O2, OH, H2O, HO2, H2O2, AR, N2 + +CANTERA_MECHANISM_FILE= h2o2.yaml + +CANTERA_PHASE_NAME= ohmech +CANTERA_TRANSPORT_MODEL= mixture-averaged + +% +% -------------------- SCALAR TRANSPORT ---------------------------------------% +% +% Using an artificial spark to ignite the solution at some location and iteration +FLAME_INIT_METHOD= SPARK +% Spark parameters in order: +% x-location of spark center (m) +% y-location of spark center (m) +% z-location of spark center (m) +% Spark radius (m) +% Iteration at which the artificial spark starts +% Spark iteration duration +SPARK_INIT= (0.0, 0.002, 0.0, 1e-3, 20, 30) +% (0.0, 0.002, 0.0, 1e-3, 600, 300) +% Controlling variable source terms applied within the spark sphere for the spark +% duration. +CANTERA_SPARK_TEMPERATURE= 1500 +%1400 1200 %1500 +% equivalence ratio phi=0.6, mass fractions H2, H, O, O2, OH, H2O, HO2, H2O2, AR +SPECIES_INIT= 0.017311, 0.0, 0.0, 0.228966, 0.0, 0.0, 0.0, 0.0, 0.0 +SPECIES_CLIPPING=YES +SPECIES_CLIPPING_MIN= 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 +SPECIES_CLIPPING_MAX= 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 + +% -------------------- BOUNDARY CONDITION DEFINITION --------------------------% +% +% +MARKER_SYM= ( symmetry ) +MARKER_ISOTHERMAL = (solid_fluid, 300) +% +SPECIFIED_INLET_PROFILE= NO +INLET_FILENAME= inlet_venturi.dat +INC_INLET_TYPE= VELOCITY_INLET +MARKER_INLET= ( inlet, 300, 2.0, 0.0, 1.0, 0.0) +MARKER_INLET_SPECIES = (inlet, 0.017311, 0.0, 0.0, 0.228966, 0.0, 0.0, 0.0, 0.0, 0.0) +% +% +INC_OUTLET_TYPE= PRESSURE_OUTLET +MARKER_OUTLET= ( outlet, 0.0 ) +% +% ------------- COMMON PARAMETERS DEFINING THE NUMERICAL METHOD ---------------% +% +NUM_METHOD_GRAD= WEIGHTED_LEAST_SQUARES +% +CFL_NUMBER= 100 +CFL_ADAPT= YES +CFL_ADAPT_PARAM= ( 0.95, 1.01, 100, 10000, 0.1, 100) + +CFL_REDUCTION_SPECIES= 1.0 +% Run commented Iter for good results +ITER= 10000 +% +% +% ------------------------ LINEAR SOLVER DEFINITION ---------------------------% +% +LINEAR_SOLVER= FGMRES +LINEAR_SOLVER_PREC= ILU +LINEAR_SOLVER_ERROR= 1E-02 +LINEAR_SOLVER_ITER= 10 +% +% -------------------- FLOW AND SPECIES NUMERICAL METHOD DEFINITION -----------------------% +% +CONV_NUM_METHOD_FLOW= FDS +MUSCL_FLOW= NO +SLOPE_LIMITER_FLOW = NONE +TIME_DISCRE_FLOW= EULER_IMPLICIT + +% -------------------------- MULTIGRID PARAMETERS -----------------------------% +% +MGLEVEL=1 +MGCYCLE= V_CYCLE +MG_STARTUP_ITER= 40 +MG_STARTUP_CONVERGENCE= -3 +MG_STARTUP_STAGNATION= 0.99 +MG_STARTUP_STAGNATION_ITER= 5 +MG_MIN_MESHSIZE= 200 + +MG_PRE_SMOOTH= (2, 2, 2, 2, 2, 2 ) +MG_POST_SMOOTH= ( 0, 2, 2, 2, 2, 2) +MG_CORRECTION_SMOOTH= ( 1, 1, 1, 1, 1 ) + +MG_DAMP_RESTRICTION= 0.5 +MG_DAMP_PROLONGATION= 0.5 + +MG_SMOOTH_OUTPUT= NO +MG_SMOOTH_EARLY_EXIT= YES +MG_CFL_SCALING= ( 0.3, 0.3, 0.3, 0.3 ) + +MG_IMPLICIT_LINES= YES + +% +% -------------------- SCALAR TRANSPORT ---------------------------------------% +% +KIND_SCALAR_MODEL= SPECIES_TRANSPORT +DIFFUSIVITY_MODEL= CANTERA +DIFFUSIVITY_CONSTANT= 0.00005 +% +CONV_NUM_METHOD_SPECIES= BOUNDED_SCALAR +MUSCL_SPECIES= NO +SLOPE_LIMITER_SPECIES = NONE +% +TIME_DISCRE_SPECIES= EULER_IMPLICIT +% +% --------------------------- CONVERGENCE PARAMETERS --------------------------% +% +CONV_FIELD= RMS_PRESSURE, RMS_VELOCITY-X, RMS_VELOCITY-Y, RMS_SPECIES_H2 +CONV_RESIDUAL_MINVAL= -12 +CONV_STARTITER= 10 +% +% ------------------------- INPUT/OUTPUT INFORMATION --------------------------% +% +SCREEN_OUTPUT= INNER_ITER WALL_TIME \ + RMS_RES \ + LINSOL_ITER LINSOL_RESIDUAL AVG_CFL +SCREEN_WRT_FREQ_INNER= 10 +% +HISTORY_OUTPUT= ITER RMS_RES LINSOL +CONV_FILENAME= history +MARKER_ANALYZE= inlet, outlet +MARKER_ANALYZE_AVERAGE= AREA +% +OUTPUT_FILES= RESTART, PARAVIEW_MULTIBLOCK +VOLUME_OUTPUT= RESIDUAL, PRIMITIVE, SOLUTION, TIMESTEP +MESH_FILENAME= 2D_slit_burner_mesh.su2 +OUTPUT_WRT_FREQ= 100 +% +READ_BINARY_RESTART= YES +RESTART_FILENAME= restart +SOLUTION_FILENAME= solution +WRT_PERFORMANCE= YES diff --git a/TestCases/combustion/h2o2_hydrogen_air/h2o2.yaml b/TestCases/combustion/h2o2_hydrogen_air/h2o2.yaml new file mode 100644 index 000000000000..65e6321a60ba --- /dev/null +++ b/TestCases/combustion/h2o2_hydrogen_air/h2o2.yaml @@ -0,0 +1,323 @@ +description: |- + Hydrogen-Oxygen submechanism extracted from GRI-Mech 3.0. + Modified from the original to include N2. + + Redlich-Kwong coefficients are based on tabulated critical properties or + estimated according to the method of Joback and Reid, "Estimation of pure- + component properties from group-contributions," Chem. Eng. Comm. 57 (1987) + 233-243 + +generator: ck2yaml +input-files: [h2o2.inp, gri30_tran.dat] +cantera-version: 2.5.0 +date: Wed, 11 Dec 2019 16:59:04 -0500 + +units: {length: cm, time: s, quantity: mol, activation-energy: cal/mol} + +phases: +- name: ohmech + thermo: ideal-gas + elements: [O, H, Ar, N] + species: [H2, H, O, O2, OH, H2O, HO2, H2O2, AR, N2] + kinetics: gas + transport: mixture-averaged + state: {T: 300.0, P: 1 atm} + +- name: ohmech-RK + thermo: Redlich-Kwong + elements: [O, H, Ar, N] + species: [H2, H, O, O2, OH, H2O, HO2, H2O2, AR, N2] + kinetics: gas + transport: mixture-averaged + state: {T: 300.0, P: 1 atm} + +species: +- name: H2 + composition: {H: 2} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [2.34433112, 7.98052075e-03, -1.9478151e-05, 2.01572094e-08, -7.37611761e-12, + -917.935173, 0.683010238] + - [3.3372792, -4.94024731e-05, 4.99456778e-07, -1.79566394e-10, 2.00255376e-14, + -950.158922, -3.20502331] + note: TPIS78 + transport: + model: gas + geometry: linear + well-depth: 38.0 + diameter: 2.92 + polarizability: 0.79 + rotational-relaxation: 280.0 + equation-of-state: + model: Redlich-Kwong + a: 1.43319e+11 + b: 18.42802577 +- name: H + composition: {H: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [2.5, 7.05332819e-13, -1.99591964e-15, 2.30081632e-18, -9.27732332e-22, + 2.54736599e+04, -0.446682853] + - [2.50000001, -2.30842973e-11, 1.61561948e-14, -4.73515235e-18, 4.98197357e-22, + 2.54736599e+04, -0.446682914] + note: L7/88 + transport: + model: gas + geometry: atom + well-depth: 145.0 + diameter: 2.05 + equation-of-state: + model: Redlich-Kwong + a: 1.32125e+11 + b: 17.63395812 +- name: O + composition: {O: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [3.1682671, -3.27931884e-03, 6.64306396e-06, -6.12806624e-09, 2.11265971e-12, + 2.91222592e+04, 2.05193346] + - [2.56942078, -8.59741137e-05, 4.19484589e-08, -1.00177799e-11, 1.22833691e-15, + 2.92175791e+04, 4.78433864] + note: L1/90 + transport: + model: gas + geometry: atom + well-depth: 80.0 + diameter: 2.75 + equation-of-state: + model: Redlich-Kwong + a: 4.74173e+11 + b: 10.69952492 +- name: O2 + composition: {O: 2} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [3.78245636, -2.99673416e-03, 9.84730201e-06, -9.68129509e-09, 3.24372837e-12, + -1063.94356, 3.65767573] + - [3.28253784, 1.48308754e-03, -7.57966669e-07, 2.09470555e-10, -2.16717794e-14, + -1088.45772, 5.45323129] + note: TPIS89 + transport: + model: gas + geometry: linear + well-depth: 107.4 + diameter: 3.458 + polarizability: 1.6 + rotational-relaxation: 3.8 + equation-of-state: + model: Redlich-Kwong + a: 1.74102e+12 + b: 22.08100907 +- name: OH + composition: {O: 1, H: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [3.99201543, -2.40131752e-03, 4.61793841e-06, -3.88113333e-09, 1.3641147e-12, + 3615.08056, -0.103925458] + - [3.09288767, 5.48429716e-04, 1.26505228e-07, -8.79461556e-11, 1.17412376e-14, + 3858.657, 4.4766961] + note: RUS78 + transport: + model: gas + geometry: linear + well-depth: 80.0 + diameter: 2.75 + equation-of-state: + model: Redlich-Kwong + a: 4.77552e+11 + b: 10.72986231 +- name: H2O + composition: {H: 2, O: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [4.19864056, -2.0364341e-03, 6.52040211e-06, -5.48797062e-09, 1.77197817e-12, + -3.02937267e+04, -0.849032208] + - [3.03399249, 2.17691804e-03, -1.64072518e-07, -9.7041987e-11, 1.68200992e-14, + -3.00042971e+04, 4.9667701] + note: L8/89 + transport: + model: gas + geometry: nonlinear + well-depth: 572.4 + diameter: 2.605 + dipole: 1.844 + rotational-relaxation: 4.0 + equation-of-state: + model: Redlich-Kwong + a: 1.42674e+13 + b: 21.12705912 +- name: HO2 + composition: {H: 1, O: 2} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [4.30179801, -4.74912051e-03, 2.11582891e-05, -2.42763894e-08, 9.29225124e-12, + 294.80804, 3.71666245] + - [4.0172109, 2.23982013e-03, -6.3365815e-07, 1.1424637e-10, -1.07908535e-14, + 111.856713, 3.78510215] + note: L5/89 + transport: + model: gas + geometry: nonlinear + well-depth: 107.4 + diameter: 3.458 + rotational-relaxation: 1.0 + note: '*' + equation-of-state: + model: Redlich-Kwong + a: 1.46652e+12 + b: 21.27344867 +- name: H2O2 + composition: {H: 2, O: 2} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [4.27611269, -5.42822417e-04, 1.67335701e-05, -2.15770813e-08, 8.62454363e-12, + -1.77025821e+04, 3.43505074] + - [4.16500285, 4.90831694e-03, -1.90139225e-06, 3.71185986e-10, -2.87908305e-14, + -1.78617877e+04, 2.91615662] + note: L7/88 + transport: + model: gas + geometry: nonlinear + well-depth: 107.4 + diameter: 3.458 + rotational-relaxation: 3.8 + equation-of-state: + model: Redlich-Kwong + a: 1.46652e+12 + b: 21.27344867 +- name: AR + composition: {Ar: 1} + thermo: + model: NASA7 + temperature-ranges: [300.0, 1000.0, 5000.0] + data: + - [2.5, 0.0, 0.0, 0.0, 0.0, -745.375, 4.366] + - [2.5, 0.0, 0.0, 0.0, 0.0, -745.375, 4.366] + note: '120186' + transport: + model: gas + geometry: atom + well-depth: 136.5 + diameter: 3.33 + equation-of-state: + model: Redlich-Kwong + a: 1.69466E+12 + b: 22.30627035 +- name: N2 + composition: {N: 2} + thermo: + model: NASA7 + temperature-ranges: [300.0, 1000.0, 5000.0] + data: + - [3.298677, 1.4082404e-03, -3.963222e-06, 5.641515e-09, -2.444854e-12, + -1020.8999, 3.950372] + - [2.92664, 1.4879768e-03, -5.68476e-07, 1.0097038e-10, -6.753351e-15, + -922.7977, 5.980528] + note: '121286' + transport: + model: gas + geometry: linear + well-depth: 97.53 + diameter: 3.621 + polarizability: 1.76 + rotational-relaxation: 4.0 + equation-of-state: + model: Redlich-Kwong + a: 1.55976e+12 + b: 26.81724983 + +reactions: +- equation: 2 O + M <=> O2 + M # Reaction 1 + type: three-body + rate-constant: {A: 1.2e+17, b: -1.0, Ea: 0.0} + efficiencies: {H2: 2.4, H2O: 15.4, AR: 0.83} +- equation: O + H + M <=> OH + M # Reaction 2 + type: three-body + rate-constant: {A: 5.0e+17, b: -1.0, Ea: 0.0} + efficiencies: {H2: 2.0, H2O: 6.0, AR: 0.7} +- equation: O + H2 <=> H + OH # Reaction 3 + rate-constant: {A: 3.87e+04, b: 2.7, Ea: 6260.0} +- equation: O + HO2 <=> OH + O2 # Reaction 4 + rate-constant: {A: 2.0e+13, b: 0.0, Ea: 0.0} +- equation: O + H2O2 <=> OH + HO2 # Reaction 5 + rate-constant: {A: 9.63e+06, b: 2.0, Ea: 4000.0} +- equation: H + O2 + M <=> HO2 + M # Reaction 6 + type: three-body + rate-constant: {A: 2.8e+18, b: -0.86, Ea: 0.0} + efficiencies: {O2: 0.0, H2O: 0.0, N2: 0.0, AR: 0.0} +- equation: H + 2 O2 <=> HO2 + O2 # Reaction 7 + rate-constant: {A: 2.08e+19, b: -1.24, Ea: 0.0} +- equation: H + O2 + H2O <=> HO2 + H2O # Reaction 8 + rate-constant: {A: 1.126e+19, b: -0.76, Ea: 0.0} +- equation: H + O2 + N2 <=> HO2 + N2 # Reaction 9 + rate-constant: {A: 2.6e+19, b: -1.24, Ea: 0.0} +- equation: H + O2 + AR <=> HO2 + AR # Reaction 10 + rate-constant: {A: 7.0e+17, b: -0.8, Ea: 0.0} +- equation: H + O2 <=> O + OH # Reaction 11 + rate-constant: {A: 2.65e+16, b: -0.6707, Ea: 1.7041e+04} +- equation: 2 H + M <=> H2 + M # Reaction 12 + type: three-body + rate-constant: {A: 1.0e+18, b: -1.0, Ea: 0.0} + efficiencies: {H2: 0.0, H2O: 0.0, AR: 0.63} +- equation: 2 H + H2 <=> 2 H2 # Reaction 13 + rate-constant: {A: 9.0e+16, b: -0.6, Ea: 0.0} +- equation: 2 H + H2O <=> H2 + H2O # Reaction 14 + rate-constant: {A: 6.0e+19, b: -1.25, Ea: 0.0} +- equation: H + OH + M <=> H2O + M # Reaction 15 + type: three-body + rate-constant: {A: 2.2e+22, b: -2.0, Ea: 0.0} + efficiencies: {H2: 0.73, H2O: 3.65, AR: 0.38} +- equation: H + HO2 <=> O + H2O # Reaction 16 + rate-constant: {A: 3.97e+12, b: 0.0, Ea: 671.0} +- equation: H + HO2 <=> O2 + H2 # Reaction 17 + rate-constant: {A: 4.48e+13, b: 0.0, Ea: 1068.0} +- equation: H + HO2 <=> 2 OH # Reaction 18 + rate-constant: {A: 8.4e+13, b: 0.0, Ea: 635.0} +- equation: H + H2O2 <=> HO2 + H2 # Reaction 19 + rate-constant: {A: 1.21e+07, b: 2.0, Ea: 5200.0} +- equation: H + H2O2 <=> OH + H2O # Reaction 20 + rate-constant: {A: 1.0e+13, b: 0.0, Ea: 3600.0} +- equation: OH + H2 <=> H + H2O # Reaction 21 + rate-constant: {A: 2.16e+08, b: 1.51, Ea: 3430.0} +- equation: 2 OH (+M) <=> H2O2 (+M) # Reaction 22 + type: falloff + low-P-rate-constant: {A: 2.3e+18, b: -0.9, Ea: -1700.0} + high-P-rate-constant: {A: 7.4e+13, b: -0.37, Ea: 0.0} + Troe: {A: 0.7346, T3: 94.0, T1: 1756.0, T2: 5182.0} + efficiencies: {H2: 2.0, H2O: 6.0, AR: 0.7} +- equation: 2 OH <=> O + H2O # Reaction 23 + rate-constant: {A: 3.57e+04, b: 2.4, Ea: -2110.0} +- equation: OH + HO2 <=> O2 + H2O # Reaction 24 + duplicate: true + rate-constant: {A: 1.45e+13, b: 0.0, Ea: -500.0} +- equation: OH + H2O2 <=> HO2 + H2O # Reaction 25 + duplicate: true + rate-constant: {A: 2.0e+12, b: 0.0, Ea: 427.0} +- equation: OH + H2O2 <=> HO2 + H2O # Reaction 26 + duplicate: true + rate-constant: {A: 1.7e+18, b: 0.0, Ea: 2.941e+04} +- equation: 2 HO2 <=> O2 + H2O2 # Reaction 27 + duplicate: true + rate-constant: {A: 1.3e+11, b: 0.0, Ea: -1630.0} +- equation: 2 HO2 <=> O2 + H2O2 # Reaction 28 + duplicate: true + rate-constant: {A: 4.2e+14, b: 0.0, Ea: 1.2e+04} +- equation: OH + HO2 <=> O2 + H2O # Reaction 29 + duplicate: true + rate-constant: {A: 5.0e+15, b: 0.0, Ea: 1.733e+04} diff --git a/TestCases/parallel_regression.py b/TestCases/parallel_regression.py index 54569d389d50..ec006e922eb7 100755 --- a/TestCases/parallel_regression.py +++ b/TestCases/parallel_regression.py @@ -25,7 +25,7 @@ # You should have received a copy of the GNU Lesser General Public # License along with SU2. If not, see . -# make print(*args) function available in PY2.6+, does'nt work on PY < 2.6 +# make print(*args) function available in PY2.6+, doesn't work on PY < 2.6 from __future__ import print_function import sys @@ -38,6 +38,28 @@ def main(): test_list = [] + ########################################## + ### Combustion with Detailed Chemistry ### + ########################################## + + # 2D planar laminar premixed methane flame on isothermal burner + cfd_cantera_dc_ch4 = TestCase('cfd_cantera_dc_ch4') + cfd_cantera_dc_ch4.cfg_dir = "combustion/1step_methane_air" + cfd_cantera_dc_ch4.cfg_file = "2d_slit_burner_fluid.cfg" + cfd_cantera_dc_ch4.test_iter = 10 + cfd_cantera_dc_ch4.test_vals = [-12.897453, -15.198159, -12.810575, 10.000000, -1.881353, 1000.000000] + cfd_cantera_dc_ch4.new_output = True + test_list.append(cfd_cantera_dc_ch4) + + # 2D planar laminar premixed hydrogen flame on isothermal burner (restart) + cfd_cantera_dc_h2 = TestCase('cfd_cantera_dc_h2') + cfd_cantera_dc_h2.cfg_dir = "combustion/h2o2_hydrogen_air" + cfd_cantera_dc_h2.cfg_file = "2d_slit_burner_h2.cfg" + cfd_cantera_dc_h2.test_iter = 10 + cfd_cantera_dc_h2.test_vals = [-15.993819, -17.134091, -32.000000, 5.000000, -2.029177, 100.000000] + cfd_cantera_dc_h2.new_output = True + test_list.append(cfd_cantera_dc_h2) + ####################### ### Flamelet solver ### ####################### @@ -56,7 +78,7 @@ def main(): cfd_flamelet_ch4_axi.cfg_dir = "flamelet/05_laminar_premixed_ch4_flame_cfd_axi" cfd_flamelet_ch4_axi.cfg_file = "lam_prem_ch4_cfd_axi.cfg" cfd_flamelet_ch4_axi.test_iter = 10 - cfd_flamelet_ch4_axi.test_vals = [-11.255837, -10.017962, -11.850828, -4.916261, 72.669000] + cfd_flamelet_ch4_axi.test_vals = [-9.774148, -11.255837, -10.017962, -11.850828, -4.916261, 72.669000] cfd_flamelet_ch4_axi.new_output = True test_list.append(cfd_flamelet_ch4_axi) diff --git a/TestCases/species_transport/venturi_primitive_3species/species2_primitiveVenturi_CANTERA.cfg b/TestCases/species_transport/venturi_primitive_3species/species2_primitiveVenturi_CANTERA.cfg new file mode 100644 index 000000000000..d3b41922d199 --- /dev/null +++ b/TestCases/species_transport/venturi_primitive_3species/species2_primitiveVenturi_CANTERA.cfg @@ -0,0 +1,135 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% % +% SU2 configuration file % +% Case description: Species mixing with 2 species, i.e. 1 transport equations % +% Including CANTERA fluid model. Fluid properties obtained % +% directly from CANTERA. % +% Author: Cristopher Morales Ubal % +% Institution: Eindhoven University of Technology % +% Date: 2026/01/19 % +% File Version 8.5.0 "Harrier" % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% ------------- DIRECT, ADJOINT, AND LINEARIZED PROBLEM DEFINITION ------------% +% +SOLVER= INC_RANS +KIND_TURB_MODEL= SST +% +% ---------------- INCOMPRESSIBLE FLOW CONDITION DEFINITION -------------------% +% +INC_DENSITY_MODEL= VARIABLE +INC_DENSITY_INIT= 1.1766 +% +INC_VELOCITY_INIT= ( 1.00, 0.0, 0.0 ) +% +INC_ENERGY_EQUATION= YES +INC_TEMPERATURE_INIT= 300.0 +% +INC_NONDIM= DIMENSIONAL +% +% -------------------- FLUID PROPERTIES ------------------------------------- % +% +FLUID_MODEL= FLUID_CANTERA +CANTERA_COMBUSTION = NO +% +THERMODYNAMIC_PRESSURE= 101325.0 +% +CONDUCTIVITY_MODEL= CANTERA +% +TURBULENT_CONDUCTIVITY_MODEL= NONE +% +CANTERA_SPECIES_NAMES= H2, O2 +CANTERA_MECHANISM_FILE= h2o2.yaml +CANTERA_PHASE_NAME= ohmech +CANTERA_TRANSPORT_MODEL= mixture-averaged +% +VISCOSITY_MODEL= CANTERA +% +% -------------------- BOUNDARY CONDITION DEFINITION --------------------------% +% +MARKER_HEATFLUX= ( wall, 0.0 ) +MARKER_SYM= ( axis ) +% +SPECIFIED_INLET_PROFILE= NO +INLET_FILENAME= inlet_venturi.dat +INC_INLET_TYPE= VELOCITY_INLET VELOCITY_INLET +MARKER_INLET= ( gas_inlet, 300, 1.0, 1.0, 0.0, 0.0,\ + air_axial_inlet, 350, 1.0, 0.0, -1.0, 0.0 ) +MARKER_INLET_SPECIES= (gas_inlet, 0.5,\ + air_axial_inlet, 0.6) +% +INC_OUTLET_TYPE= PRESSURE_OUTLET +MARKER_OUTLET= ( outlet, 0.0) +% +% ------------- COMMON PARAMETERS DEFINING THE NUMERICAL METHOD ---------------% +% +NUM_METHOD_GRAD= WEIGHTED_LEAST_SQUARES +% +CFL_NUMBER= 60 +CFL_REDUCTION_SPECIES= 1.0 +CFL_REDUCTION_TURB= 1.0 +% +ITER= 1000 +% +% ------------------------ LINEAR SOLVER DEFINITION ---------------------------% +% +LINEAR_SOLVER= FGMRES +LINEAR_SOLVER_PREC= ILU +LINEAR_SOLVER_ERROR= 1E-8 +LINEAR_SOLVER_ITER= 5 +% +% -------------------- FLOW NUMERICAL METHOD DEFINITION -----------------------% +% +CONV_NUM_METHOD_FLOW= FDS +MUSCL_FLOW= NO +SLOPE_LIMITER_FLOW = NONE +TIME_DISCRE_FLOW= EULER_IMPLICIT +% +% -------------------- SCALAR TRANSPORT ---------------------------------------% +% +KIND_SCALAR_MODEL= SPECIES_TRANSPORT +DIFFUSIVITY_MODEL= CANTERA +DIFFUSIVITY_CONSTANT= 0.001 +% +CONV_NUM_METHOD_SPECIES= BOUNDED_SCALAR +MUSCL_SPECIES= NO +SLOPE_LIMITER_SPECIES = NONE +% +TIME_DISCRE_SPECIES= EULER_IMPLICIT +% +SPECIES_INIT= 0.5 +SPECIES_CLIPPING= NO +SPECIES_CLIPPING_MIN= 0.0 +SPECIES_CLIPPING_MAX= 1.0 +% +% -------------------- TURBULENT TRANSPORT ---------------------------------------% +% +CONV_NUM_METHOD_TURB= BOUNDED_SCALAR +MUSCL_TURB= NO +% +% --------------------------- CONVERGENCE PARAMETERS --------------------------% +% +CONV_FIELD= RMS_PRESSURE, RMS_VELOCITY-X, RMS_VELOCITY-Y, RMS_TKE, RMS_SPECIES +CONV_RESIDUAL_MINVAL= -18 +CONV_STARTITER= 10 +% +% ------------------------- INPUT/OUTPUT INFORMATION --------------------------% +% +MESH_FILENAME= primitiveVenturi.su2 +SCREEN_OUTPUT= INNER_ITER WALL_TIME \ + RMS_PRESSURE RMS_VELOCITY-X RMS_VELOCITY-Y RMS_ENTHALPY RMS_TKE RMS_DISSIPATION RMS_SPECIES_H2 \ + LINSOL_ITER LINSOL_RESIDUAL \ + LINSOL_ITER_TURB LINSOL_RESIDUAL_TURB \ + LINSOL_ITER_SPECIES LINSOL_RESIDUAL_SPECIES SURFACE_SPECIES_VARIANCE +SCREEN_WRT_FREQ_INNER= 10 +% +HISTORY_OUTPUT= RMS_RES FLOW_COEFF LINSOL SPECIES_COEFF SPECIES_COEFF_SURF +MARKER_ANALYZE= outlet gas_inlet air_axial_inlet +% +OUTPUT_FILES= RESTART_ASCII, PARAVIEW_MULTIBLOCK +VOLUME_OUTPUT= RESIDUAL, PRIMITIVE +OUTPUT_WRT_FREQ= 100 +% +RESTART_SOL= NO +SOLUTION_FILENAME= solution diff --git a/UnitTests/SU2_CFD/fluid/CFluidCantera_tests.cpp b/UnitTests/SU2_CFD/fluid/CFluidCantera_tests.cpp new file mode 100644 index 000000000000..4dc0eb01f8f3 --- /dev/null +++ b/UnitTests/SU2_CFD/fluid/CFluidCantera_tests.cpp @@ -0,0 +1,170 @@ +/*! + * \file CFluidCantera_tests.cpp + * \brief Unit tests for Cantera fluid model. + * \author C.Morales Ubal + * \version 8.5.0 "Harrier" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2026, SU2 Contributors (cf. AUTHORS.md) + * + * SU2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * SU2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with SU2. If not, see . + */ + +#include "catch.hpp" + +#include + +#ifdef HAVE_CANTERA +#include "../../../SU2_CFD/include/fluid/CFluidCantera.hpp" +#include + +using namespace Cantera; + +TEST_CASE("Fluid_Cantera", "[Multicomponent_flow]") { + /*--- Cantera fluid model unit test cases. ---*/ + + SU2_COMPONENT val_software = SU2_COMPONENT::SU2_CFD; + CConfig config("src/SU2/UnitTests/SU2_CFD/fluid/multicomponent_cantera.cfg", val_software, true); + + /*--- Create Cantera fluid model. ---*/ + CFluidCantera fluid(config.GetPressure_Thermodynamic(), &config); + + /*--- Set state using the scalars of the config file and the temperature. ---*/ + const su2double* scalar = config.GetSpecies_Init(); + const su2double Temperature = 300.0; + fluid.SetTDState_T(Temperature, scalar); + + /*--- Check values for density and heat capacity. ---*/ + + su2double density = fluid.GetDensity(); + su2double cp = fluid.GetCp(); + CHECK(density == Approx(0.924236)); + CHECK(cp == Approx(1277.91)); +} +TEST_CASE("Fluid_Cantera_Combustion", "[Reacting_flow]") { + /*--- Cantera fluid model unit test cases. ---*/ + + SU2_COMPONENT val_software = SU2_COMPONENT::SU2_CFD; + CConfig config("src/SU2/UnitTests/SU2_CFD/fluid/multicomponent_cantera.cfg", val_software, true); + + /*--- Create Cantera fluid model. ---*/ + CFluidCantera fluid(config.GetPressure_Thermodynamic(), &config); + + /*--- Set state using the scalars of the config file and the temperature. ---*/ + const su2double* scalar = config.GetSpecies_Init(); + const su2double Temperature = 1900.0; + fluid.SetTDState_T(Temperature, scalar); + + /*--- Compute chemical source terms. ---*/ + fluid.ComputeChemicalSourceTerm(); + + /*--- Check values for source terms. ---*/ + + su2double sourceTerm_H2 = fluid.GetChemicalSourceTerm(0); + su2double sourceTerm_O2 = fluid.GetChemicalSourceTerm(1); + CHECK(sourceTerm_H2 == Approx(-0.13633797171426)); + CHECK(sourceTerm_O2 == Approx(-2.16321066087493)); +} +TEST_CASE("Fluid_Cantera_LargeMechanism", "[Multicomponent_flow]") { + /*--- Three transported species in a mechanism with more species than the transported set. ---*/ + + SU2_COMPONENT val_software = SU2_COMPONENT::SU2_CFD; + CConfig config("src/SU2/UnitTests/SU2_CFD/fluid/multicomponent_cantera_gri30.cfg", val_software, true); + CFluidCantera fluid(config.GetPressure_Thermodynamic(), &config); + + const su2double* scalar = config.GetSpecies_Init(); + fluid.SetTDState_T(300.0, scalar); + const su2double density = fluid.GetDensity(); + CHECK(density > 1.0); + CHECK(density < 1.4); + CHECK(fluid.GetMassDiffusivity(0) > 0.0); + CHECK(fluid.GetLaminarViscosity() > 0.0); + + /*--- Temperature recovered from the enthalpy of a given state. ---*/ + + fluid.SetTDState_T(1500.0, scalar); + const su2double enthalpy = fluid.GetEnthalpy(); + fluid.SetTDState_h(enthalpy, scalar); + CHECK(fluid.GetTemperatureIterationFailed() == false); + CHECK(fluid.GetTemperature() == Approx(1500.0).margin(1e-4)); + + /*--- The result does not depend on the temperature guess, which is used only once. ---*/ + + fluid.SetTemperatureGuess(1490.0); + fluid.SetTDState_h(enthalpy, scalar); + CHECK(fluid.GetTemperature() == Approx(1500.0).margin(1e-4)); + fluid.SetTemperatureGuess(5.0); + fluid.SetTDState_h(enthalpy, scalar); + CHECK(fluid.GetTemperatureIterationFailed() == false); + CHECK(fluid.GetTemperature() == Approx(1500.0).margin(1e-4)); + + /*--- An enthalpy outside the range of the thermodynamic data is flagged. ---*/ + + fluid.SetTDState_h(1e12, scalar); + CHECK(fluid.GetTemperatureIterationFailed() == true); +} +TEST_CASE("Fluid_Cantera_SourceJacobian", "[Reacting_flow]") { + /*--- Diagonal chemical source Jacobian of the one-step methane mechanism. ---*/ + + SU2_COMPONENT val_software = SU2_COMPONENT::SU2_CFD; + CConfig config("src/SU2/UnitTests/SU2_CFD/fluid/one_step_ch4.cfg", val_software, true); + CFluidCantera fluid(config.GetPressure_Thermodynamic(), &config); + + const su2double temperature = 1800.0; + su2double scalar[4] = {0.2226, 0.05, 0.0446, 0.05}; + fluid.SetTDState_T(temperature, scalar); + fluid.ComputeChemicalSourceTerm(); + + const su2double source_O2 = fluid.GetChemicalSourceTerm(0); + const su2double source_CH4 = fluid.GetChemicalSourceTerm(2); + CHECK(source_CH4 < 0.0); + + /*--- Reactants are consumed, so their Jacobian is negative; products have no sink. ---*/ + + CHECK(fluid.GetChemicalSourceJacobian(0) < 0.0); + CHECK(fluid.GetChemicalSourceJacobian(1) == 0.0); + CHECK(fluid.GetChemicalSourceJacobian(2) < 0.0); + CHECK(fluid.GetChemicalSourceJacobian(3) == 0.0); + + /*--- The rate is first order in CH4. The finite difference also contains the density change from moving N2 + * into CH4, which the Jacobian (at fixed density) does not, hence the tolerance. ---*/ + + const su2double delta = 1e-6; + scalar[2] += delta; + fluid.SetTDState_T(temperature, scalar); + fluid.ComputeChemicalSourceTerm(); + const su2double slope_CH4 = (fluid.GetChemicalSourceTerm(2) - source_CH4) / delta; + CHECK(fluid.GetChemicalSourceJacobian(2) * fluid.GetDensity() == Approx(slope_CH4).epsilon(0.1)); + CHECK(source_O2 < 0.0); + + /*--- Below CANTERA_DC_MIN_TEMP (500 K by default) the chemistry is skipped. ---*/ + + scalar[2] -= delta; + fluid.SetTDState_T(400.0, scalar); + fluid.ComputeChemicalSourceTerm(); + for (int iVar = 0; iVar < 4; iVar++) { + CHECK(fluid.GetChemicalSourceTerm(iVar) == 0.0); + CHECK(fluid.GetChemicalSourceJacobian(iVar) == 0.0); + } + CHECK(fluid.GetHeatRelease() == 0.0); + + fluid.SetTDState_T(600.0, scalar); + fluid.ComputeChemicalSourceTerm(); + CHECK(fluid.GetChemicalSourceTerm(2) != 0.0); +} +#endif diff --git a/UnitTests/SU2_CFD/fluid/multicomponent_cantera.cfg b/UnitTests/SU2_CFD/fluid/multicomponent_cantera.cfg new file mode 100644 index 000000000000..57ca5db7665c --- /dev/null +++ b/UnitTests/SU2_CFD/fluid/multicomponent_cantera.cfg @@ -0,0 +1,41 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% % +% SU2 configuration file % +% Case description: Unit test Multicomponent Cantera 3 species H2, O2, N2 % +% Author: Cristopher Morales Ubal % +% Institution: Eindhoven University of Technology % +% Date: 2024/10/01 % +% File Version 8.5.0 "Harrier" % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% ------------- DIRECT, ADJOINT, AND LINEARIZED PROBLEM DEFINITION ------------% +% +SOLVER= INC_NAVIER_STOKES +% +% ---------------- INCOMPRESSIBLE FLOW CONDITION DEFINITION -------------------% +% +INC_DENSITY_MODEL= VARIABLE +% +INC_NONDIM= DIMENSIONAL +% +% -------------------- FLUID PROPERTIES ------------------------------------- % +% +FLUID_MODEL= FLUID_CANTERA +CANTERA_COMBUSTION= YES +FLAME_INIT_METHOD= SPARK +% +CONDUCTIVITY_MODEL= CANTERA +VISCOSITY_MODEL= CANTERA +% +CANTERA_SPECIES_NAMES= H2, O2, N2 +CANTERA_MECHANISM_FILE= h2o2.yaml +CANTERA_PHASE_NAME= ohmech +CANTERA_TRANSPORT_MODEL= mixture-averaged +% +% -------------------- SCALAR TRANSPORT ---------------------------------------% +% +KIND_SCALAR_MODEL= SPECIES_TRANSPORT +DIFFUSIVITY_MODEL= CANTERA +% +SPECIES_INIT= 0.020137986304114414,0.22830757006769173 diff --git a/UnitTests/SU2_CFD/fluid/multicomponent_cantera_gri30.cfg b/UnitTests/SU2_CFD/fluid/multicomponent_cantera_gri30.cfg new file mode 100644 index 000000000000..a1b52cc05b7a --- /dev/null +++ b/UnitTests/SU2_CFD/fluid/multicomponent_cantera_gri30.cfg @@ -0,0 +1,39 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% % +% SU2 configuration file % +% Case description: Unit test Cantera, 3 transported species in gri30 (53 species)% +% Author: Cristopher Morales Ubal % +% Institution: Eindhoven University of Technology % +% Date: 2024/10/01 % +% File Version 8.5.0 "Harrier" % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% ------------- DIRECT, ADJOINT, AND LINEARIZED PROBLEM DEFINITION ------------% +% +SOLVER= INC_NAVIER_STOKES +% +% ---------------- INCOMPRESSIBLE FLOW CONDITION DEFINITION -------------------% +% +INC_DENSITY_MODEL= VARIABLE +% +INC_NONDIM= DIMENSIONAL +% +% -------------------- FLUID PROPERTIES ------------------------------------- % +% +FLUID_MODEL= FLUID_CANTERA +% +CONDUCTIVITY_MODEL= CANTERA +VISCOSITY_MODEL= CANTERA +% +CANTERA_SPECIES_NAMES= CH4, O2, N2 +CANTERA_MECHANISM_FILE= gri30.yaml +CANTERA_PHASE_NAME= gri30 +CANTERA_TRANSPORT_MODEL= mixture-averaged +% +% -------------------- SCALAR TRANSPORT ---------------------------------------% +% +KIND_SCALAR_MODEL= SPECIES_TRANSPORT +DIFFUSIVITY_MODEL= CANTERA +% +SPECIES_INIT= 0.05,0.22 diff --git a/UnitTests/SU2_CFD/fluid/one_step_ch4.cfg b/UnitTests/SU2_CFD/fluid/one_step_ch4.cfg new file mode 100644 index 000000000000..253f06291f51 --- /dev/null +++ b/UnitTests/SU2_CFD/fluid/one_step_ch4.cfg @@ -0,0 +1,41 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% % +% SU2 configuration file % +% Case description: Unit test Cantera, one-step methane, 4 transported species % +% Author: Cristopher Morales Ubal % +% Institution: Eindhoven University of Technology % +% Date: 2024/10/01 % +% File Version 8.5.0 "Harrier" % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% ------------- DIRECT, ADJOINT, AND LINEARIZED PROBLEM DEFINITION ------------% +% +SOLVER= INC_NAVIER_STOKES +% +% ---------------- INCOMPRESSIBLE FLOW CONDITION DEFINITION -------------------% +% +INC_DENSITY_MODEL= VARIABLE +% +INC_NONDIM= DIMENSIONAL +% +% -------------------- FLUID PROPERTIES ------------------------------------- % +% +FLUID_MODEL= FLUID_CANTERA +% +CONDUCTIVITY_MODEL= CANTERA +VISCOSITY_MODEL= CANTERA +% +CANTERA_COMBUSTION= YES +FLAME_INIT_METHOD= SPARK +CANTERA_SPECIES_NAMES= O2, H2O, CH4, CO2, N2 +CANTERA_MECHANISM_FILE= one_step_ch4.yaml +CANTERA_PHASE_NAME= ch4_one_step +CANTERA_TRANSPORT_MODEL= mixture-averaged +% +% -------------------- SCALAR TRANSPORT ---------------------------------------% +% +KIND_SCALAR_MODEL= SPECIES_TRANSPORT +DIFFUSIVITY_MODEL= CANTERA +% +SPECIES_INIT= 0.222598,0.0,0.044642,0.0 diff --git a/UnitTests/SU2_CFD/fluid/one_step_ch4.yaml b/UnitTests/SU2_CFD/fluid/one_step_ch4.yaml new file mode 100644 index 000000000000..d174c579b0fc --- /dev/null +++ b/UnitTests/SU2_CFD/fluid/one_step_ch4.yaml @@ -0,0 +1,82 @@ +description: One-step methane mechanism CH4 + 2 O2 => CO2 + 2 H2O used by the Cantera + unit tests. +units: {length: cm, quantity: mol, activation-energy: cal/mol} +phases: +- name: ch4_one_step + thermo: ideal-gas + elements: [O, H, C, N] + species: [O2, H2O, CH4, CO2, N2] + kinetics: gas + reactions: all + transport: mixture-averaged + state: {T: 300.0, P: 101325.0} +species: +- name: O2 + composition: {O: 2} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [3.78245636, -0.00299673416, 9.84730201e-06, -9.68129509e-09, 3.24372837e-12, + -1063.94356, 3.65767573] + - [3.28253784, 0.00148308754, -7.57966669e-07, 2.09470555e-10, -2.16717794e-14, + -1088.45772, 5.45323129] + transport: {model: gas, geometry: linear, diameter: 3.46, well-depth: 107.4, polarizability: 1.6, + rotational-relaxation: 3.8} + note: TPIS89 +- name: H2O + composition: {H: 2, O: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [4.19864056, -0.0020364341, 6.52040211e-06, -5.48797062e-09, 1.77197817e-12, + -30293.7267, -0.849032208] + - [3.03399249, 0.00217691804, -1.64072518e-07, -9.7041987e-11, 1.68200992e-14, + -30004.2971, 4.9667701] + transport: {model: gas, geometry: nonlinear, diameter: 2.6, well-depth: 572.4, dipole: 1.84, + rotational-relaxation: 4.0} + note: L 8/89 +- name: CH4 + composition: {C: 1, H: 4} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [5.14987613, -0.0136709788, 4.91800599e-05, -4.84743026e-08, 1.66693956e-11, + -10246.6476, -4.64130376] + - [0.074851495, 0.0133909467, -5.73285809e-06, 1.22292535e-09, -1.0181523e-13, + -9468.34459, 18.437318] + transport: {model: gas, geometry: nonlinear, diameter: 3.75, well-depth: 141.4, + polarizability: 2.6, rotational-relaxation: 13.0} + note: L 8/88 +- name: CO2 + composition: {C: 1, O: 2} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [2.35677352, 0.00898459677, -7.12356269e-06, 2.45919022e-09, -1.43699548e-13, + -48371.9697, 9.90105222] + - [3.85746029, 0.00441437026, -2.21481404e-06, 5.23490188e-10, -4.72084164e-14, + -48759.166, 2.27163806] + transport: {model: gas, geometry: linear, diameter: 3.76, well-depth: 244.0, polarizability: 2.65, + rotational-relaxation: 2.1} + note: L 7/88 +- name: N2 + composition: {N: 2} + thermo: + model: NASA7 + temperature-ranges: [300.0, 1000.0, 5000.0] + data: + - [3.298677, 0.0014082404, -3.963222e-06, 5.641515e-09, -2.444854e-12, -1020.8999, + 3.950372] + - [2.92664, 0.0014879768, -5.68476e-07, 1.0097038e-10, -6.753351e-15, -922.7977, + 5.980528] + transport: {model: gas, geometry: linear, diameter: 3.62, well-depth: 97.53, polarizability: 1.76, + rotational-relaxation: 4.0} + note: '121286' +reactions: +- equation: ' CH4 + 2 O2 => CO2 + 2 H2O ' + rate-constant: {A: 11000000000.0, b: 0.0, Ea: 20000} + orders: {CH4: 1.0, O2: 0.5} diff --git a/UnitTests/meson.build b/UnitTests/meson.build index 1d1a97f3b827..d9a425a2db45 100644 --- a/UnitTests/meson.build +++ b/UnitTests/meson.build @@ -13,6 +13,7 @@ su2_cfd_tests = files(['Common/CConfig_tests.cpp', 'Common/vectorization.cpp', 'Common/toolboxes/ndflattener_tests.cpp', 'Common/containers/CLookupTable_tests.cpp', + 'SU2_CFD/fluid/CFluidCantera_tests.cpp', 'Common/toolboxes/multilayer_perceptron/CLookUp_ANN_tests.cpp', 'SU2_CFD/numerics/CNumerics_tests.cpp', 'SU2_CFD/fluid/CFluidModel_tests.cpp', @@ -39,15 +40,17 @@ su2_cfd_tests_dd = files(['Common/simple_directdiff_test.cpp']) # End of unit test listings # ------------------------------------------------------------------------- + if get_option('enable-tests') if get_option('enable-normal') unit_test_files = su2_cfd_tests + files(['test_driver.cpp']) + dependencies_default = [su2_cfd_dep, common_dep, su2_deps, catch2_dep] test_driver = executable( 'test_driver', unit_test_files, install : true, install_rpath : '$ORIGIN/../lib', - dependencies : [su2_cfd_dep, common_dep, su2_deps, catch2_dep], + dependencies : dependencies_default, cpp_args: ['-fPIC', default_warning_flags, su2_cpp_args] ) test('Catch2 test driver', test_driver) diff --git a/config_template.cfg b/config_template.cfg index 5e611f22ed48..8d94f74f1d4d 100644 --- a/config_template.cfg +++ b/config_template.cfg @@ -465,7 +465,7 @@ SEMI_SPAN= 0.0 % ---- NONEQUILIBRIUM GAS, IDEAL GAS, POLYTROPIC, VAN DER WAALS AND PENG ROBINSON CONSTANTS, CoolProp library -------% % % Fluid model (STANDARD_AIR, IDEAL_GAS, VW_GAS, PR_GAS, -% CONSTANT_DENSITY, INC_IDEAL_GAS, INC_IDEAL_GAS_POLY, MUTATIONPP, SU2_NONEQ, FLUID_MIXTURE, COOLPROP, FLUID_FLAMELET, DATADRIVEN_FLUID) +% CONSTANT_DENSITY, INC_IDEAL_GAS, INC_IDEAL_GAS_POLY, MUTATIONPP, SU2_NONEQ, FLUID_MIXTURE, COOLPROP, FLUID_FLAMELET, DATADRIVEN_FLUID, FLUID_CANTERA) FLUID_MODEL= STANDARD_AIR % To find all available fluid name for CoolProp library, clikc the following link: % http://www.coolprop.org/fluid_properties/PurePseudoPure.html#list-of-fluids @@ -552,7 +552,7 @@ INLET_GAS_COMPOSITION = (0.77, 0.23, 0.0, 0.0, 0.0) % --------------------------- VISCOSITY MODEL ---------------------------------% % -% Viscosity model (SUTHERLAND, CONSTANT_VISCOSITY, POLYNOMIAL_VISCOSITY, FLAMELET). +% Viscosity model (SUTHERLAND, CONSTANT_VISCOSITY, POLYNOMIAL_VISCOSITY, FLAMELET, CANTERA). VISCOSITY_MODEL= SUTHERLAND % % Molecular Viscosity that would be constant (1.716E-5 by default) @@ -574,7 +574,7 @@ MU_POLYCOEFFS= (0.0, 0.0, 0.0, 0.0, 0.0) % --------------------------- THERMAL CONDUCTIVITY MODEL ----------------------% % % Laminar Conductivity model (CONSTANT_CONDUCTIVITY, CONSTANT_PRANDTL, -% POLYNOMIAL_CONDUCTIVITY, FLAMELET). +% POLYNOMIAL_CONDUCTIVITY, FLAMELET, CANTERA). CONDUCTIVITY_MODEL= CONSTANT_PRANDTL % % Molecular Thermal Conductivity that would be constant (0.0257 by default) @@ -930,7 +930,7 @@ KIND_SCALAR_MODEL= NONE % mixing model for species transport (DAVIDSON, WILKE) MIXING_VISCOSITY_MODEL= DAVIDSON % -% Mass diffusivity model (CONSTANT_DIFFUSIVITY, CONSTANT_SCHMIDT, UNITY_LEWIS, CONSTANT_LEWIS, FLAMELET) +% Mass diffusivity model (CONSTANT_DIFFUSIVITY, CONSTANT_SCHMIDT, UNITY_LEWIS, CONSTANT_LEWIS, FLAMELET, CANTERA) DIFFUSIVITY_MODEL= CONSTANT_DIFFUSIVITY % % Mass diffusivity if DIFFUSIVITY_MODEL= CONSTANT_DIFFUSIVITY is chosen. D_air ~= 0.001 @@ -980,6 +980,44 @@ SPECIES_CLIPPING_MAX= 1.0 % Minimum values for scalar clipping SPECIES_CLIPPING_MIN= 0.0 +% --------------------- DETAILED CHEMISTRY WITH CANTERA -----------------------% +% Detailed chemistry with the Cantera library, for INC_NAVIER_STOKES and INC_RANS only. +% Requires FLUID_MODEL= FLUID_CANTERA, VISCOSITY_MODEL= CANTERA, CONDUCTIVITY_MODEL= CANTERA, +% DIFFUSIVITY_MODEL= CANTERA, KIND_SCALAR_MODEL= SPECIES_TRANSPORT, INC_DENSITY_MODEL= VARIABLE +% and INC_NONDIM= DIMENSIONAL, and SU2 built with -Denable-cantera=true. +% +% Chemical mechanism file (YAML), searched in the working directory and in CANTERA_DATA. Required. +CANTERA_MECHANISM_FILE= h2o2.yaml +% +% Name of the phase in the mechanism file. Required. +CANTERA_PHASE_NAME= ohmech +% +% Cantera transport model (mixture-averaged, unity-Lewis-number). Mixture-averaged gives a +% mass diffusivity for each species, unity-Lewis-number gives the same one for all of them. +CANTERA_TRANSPORT_MODEL= mixture-averaged +% +% Names of the species in the mechanism, in the order of SPECIES_INIT. The last name is the +% remainder, Y_N = 1 - sum(Y_k), so there is one name more than the number of SPECIES_INIT +% entries. At most MAX_TRANSPORTED_SPECIES (20, see Common/include/option_structure.hpp) are transported. +CANTERA_SPECIES_NAMES= H2, O2, N2 +% +% Add the chemical source terms to the species equations (NO, YES). +% Requires FLAME_INIT_METHOD= SPARK, with SPARK_INIT defining the spark region and duration. +CANTERA_COMBUSTION= NO +% +% Temperature (K) imposed inside the spark region while the spark is active. +CANTERA_SPARK_TEMPERATURE= 1000.0 +% +% Add the diagonal chemical sink Jacobian to the implicit species equations (NO, YES). +CANTERA_SOURCE_JACOBIAN= YES +% +% Temperature (K) below which the chemical source terms are set to zero. 0 disables it. +CANTERA_DC_MIN_TEMP= 500.0 +% +% Correct the species diffusion fluxes so that they sum to zero (correction velocity) +% in the species and in the energy equation (NO, YES). +CANTERA_CORRECTION_VELOCITY= YES + % --------------------- FLAMELET MODEL -----------------------------% % The flamelet model uses the prompts INTERPOLATION_METHOD and FILENAMES_INTERPOLATOR % for definition of the flamelet manifold. Either LUT or MLP can be used as options. @@ -1025,6 +1063,7 @@ CONTROLLING_VARIABLE_SOURCE_NAMES= (ProdRateTot_PV, NULL) % of solver iterations. This artificial spark can be applied after a certain number of iterations has passed, allowing for % the flow to evolve before igniting the mixture. % NONE : no artificial solution ignition. +% SPARK is also the ignition of CANTERA_COMBUSTION= YES (Cantera detailed chemistry), see CANTERA_SPARK_TEMPERATURE. % By default, this option is set to NONE FLAME_INIT_METHOD= FLAME_FRONT diff --git a/meson.build b/meson.build index e7efa33d1a68..a7700912410a 100644 --- a/meson.build +++ b/meson.build @@ -8,7 +8,6 @@ project('SU2', 'c', 'cpp', 'b_ndebug=true', 'c_std=c99', 'cpp_std=c++17']) - fsmod = import('fs') if not fsmod.exists('su2preconfig.timestamp') error('SU2 must be (pre-)configured with the extended Meson script (./meson.py) or with the ./preconfigure.py script in the SU2 root directory.') @@ -375,6 +374,50 @@ if get_option('enable-coolprop') endif endif +if get_option('enable-cantera') + # Cantera is built from source via its own SCons build, driven silently as part of + # the normal SU2 build (custom_target below), rather than requiring a separate manual + # build step or a prebuilt system/conda package. Building from source (instead of + # linking a prebuilt shared library) keeps the door open for a future AD-enabled + # Cantera build variant compiled against SU2's own scalar type. + cantera_source_dir = join_paths(meson.current_source_dir(), 'subprojects', 'cantera') + cantera_prefix = join_paths(meson.current_build_dir(), 'cantera_install') + cantera_build_script = join_paths(meson.current_source_dir(), 'meson_scripts', 'build_cantera.py') + + cantera_build_command = [python, cantera_build_script, + '--source', cantera_source_dir, + '--prefix', cantera_prefix, + '--stamp', '@OUTPUT@', + '--eigen-inc-dir', join_paths(meson.current_source_dir(), 'externals', 'eigen')] + cantera_build_command += ['--jobs', get_option('cantera-build-jobs').to_string()] + if get_option('cantera-boost-inc-dir') != '' + cantera_build_command += ['--boost-inc-dir', get_option('cantera-boost-inc-dir')] + endif + if get_option('cpu-arch') != '' + cantera_build_command += ['--cpu-arch', get_option('cpu-arch')] + endif + + cantera_build = custom_target('build_cantera', + output: 'cantera_build.stamp', + command: cantera_build_command, + build_always_stale: true, + console: true, + ) + + # build_cantera.py makes Cantera's installed headers resolve Eigen through an + # "eigen3/Eigen/..." shim it creates next to the install prefix, pointing at SU2's + # own Eigen copy (see the ODR note in that script). Anything compiling those headers - + # including SU2's own CFluidCantera.cpp - needs that same shim directory on its + # include path so the "eigen3/..." form resolves for it too. + cantera_dep = declare_dependency( + compile_args: ['-I' + join_paths(cantera_prefix, 'include'), + '-I' + cantera_prefix + '_eigen_shim'], + link_args: ['-L' + join_paths(cantera_prefix, 'lib'), '-lcantera'], + sources: [cantera_build], + ) + su2_cpp_args +='-DHAVE_CANTERA' +endif + if get_option('enable-mlpcpp') su2_cpp_args += '-DHAVE_MLPCPP' endif @@ -431,6 +474,7 @@ message('''--------------------------------------------------------------------- CoolProp: @12@ MLPCpp: @13@ Tracy Profiler: @14@ + Cantera: @16@ Please be sure to add the $SU2_HOME and $SU2_RUN environment variables, and update your $PATH (and $PYTHONPATH if applicable) with $SU2_RUN @@ -446,7 +490,8 @@ message('''--------------------------------------------------------------------- '''.format(get_option('prefix')+'/bin', meson.project_source_root(), get_option('enable-tecio'), get_option('enable-cgns'), get_option('enable-autodiff'), get_option('enable-directdiff'), get_option('enable-pywrapper'), get_option('enable-mkl'), get_option('enable-openblas'), get_option('enable-pastix'), get_option('enable-mixedprec'), get_option('enable-librom'), get_option('enable-coolprop'), - get_option('enable-mlpcpp'), get_option('tracy_enable'), meson.project_build_root().startswith(meson.project_source_root()) ? meson.project_build_root().split('/')[-1] : meson.project_build_root())) + get_option('enable-mlpcpp'), get_option('tracy_enable'), meson.project_build_root().startswith(meson.project_source_root()) ? meson.project_build_root().split('/')[-1] : meson.project_build_root(), + get_option('enable-cantera'))) if get_option('enable-mpp') if get_option('install-mpp') @@ -466,6 +511,17 @@ if get_option('enable-mpp') endif endif +if get_option('enable-cantera') + message(''' To run SU2 with Cantera, add this line to your .bashrc file: + + export CANTERA_DATA=@0@/data + + Cantera's own compiled-in default data directory should normally be + found automatically, but setting CANTERA_DATA explicitly avoids + relying on that at runtime. + '''.format(cantera_prefix)) +endif + if get_option('enable-librom') message(''' To run SU2 with libROM library, add this line to your .bashrc file: diff --git a/meson.py b/meson.py index ca03e47b3f0c..76b1be9e7499 100755 --- a/meson.py +++ b/meson.py @@ -35,8 +35,9 @@ if sys.version_info[0] < 3: raise Exception("Script must be run using Python 3") - # Preconfigure - preconfigure.run(own_meson=True) + # Preconfigure, Cantera is large and only downloaded when it is enabled + use_cantera = any(arg.lower() == "-denable-cantera=true" for arg in sys.argv[1:]) + preconfigure.run(own_meson=True, own_cantera=use_cantera) # Add paths for meson and ninja to environment os.environ["NINJA"] = sys.path[0] + os.path.sep + "ninja" diff --git a/meson_options.txt b/meson_options.txt index db3bb47f5521..abc4abc2623b 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -21,7 +21,10 @@ option('enable-singleprec', type : 'boolean', value : false, description: 'use s option('extra-deps', type : 'string', value : '', description: 'comma-separated list of extra (custom) dependencies to add for compilation') option('enable-mpp', type : 'boolean', value : false, description: 'enable Mutation++ support') option('install-mpp', type : 'boolean', value : false, description: 'install Mutation++ in the directory defined with --prefix') -option('enable-coolprop', type : 'boolean', value : false, description: 'enable CoolProp support') +option('enable-coolprop', type : 'boolean', value : false, description: 'enable CoolProp support') +option('enable-cantera', type : 'boolean', value : false, description: 'enable Cantera support') +option('cantera-build-jobs', type : 'integer', min : 0, value : 4, description: 'number of parallel jobs used to build Cantera from source (0 uses all cores)') +option('cantera-boost-inc-dir', type : 'string', value : '', description: 'explicit Boost header directory for building Cantera from source (only needed if SCons cannot auto-detect Boost)') option('enable-mlpcpp', type : 'boolean', value : false, description: 'enable MLPCpp support') option('enable-gprof', type : 'boolean', value : false, description: 'enable profiling through gprof') option('opdi-backend', type : 'combo', choices : ['auto', 'macro', 'ompt'], value : 'auto', description: 'OpDiLib backend choice') diff --git a/meson_scripts/build_cantera.py b/meson_scripts/build_cantera.py new file mode 100644 index 000000000000..bf500b33a74f --- /dev/null +++ b/meson_scripts/build_cantera.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python3 + +## \file build_cantera.py +# \brief Builds and installs Cantera as a static C++ library via its own +# SCons build system, invoked automatically from meson.build so that +# '-Denable-cantera=true' does not require a separate manual build step. +# \author SU2 Contributors +# \version 8.5.0 "Harrier" +# +# SU2 Project Website: https://su2code.github.io +# +# The SU2 Project is maintained by the SU2 Foundation +# (http://su2foundation.org) +# +# Copyright 2012-2026, SU2 Contributors (cf. AUTHORS.md) +# +# SU2 is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# SU2 is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with SU2. If not, see . + +import argparse +import multiprocessing +import os +import shutil +import subprocess +import sys + +# Only the pieces of Cantera SU2 actually links against (thermo/kinetics/transport) +# are built: no Python/Fortran interfaces, docs, or test suite. Cantera's own SCons +# script fetches its required submodules (fmt, yaml-cpp, sundials) on its own. +# +# Eigen is the one exception: SU2 already vendors its own Eigen copy (externals/eigen) +# and links Eigen-templated code from both SU2 and Cantera into the same executable. +# Letting Cantera vendor a second, different Eigen version is an ODR violation - the +# linker silently merges identically-mangled template instantiations from the two +# versions, which segfaults deep inside Eigen at runtime. Building against SU2's own +# Eigen (system_eigen=y + extra_inc_dirs below) keeps a single, consistent copy. +# +# Same version isn't sufficient on its own, though: Eigen's reduction/QR templates are +# header-only, so each translation unit compiles its own body, and the linker COMDAT-folds +# identically-mangled instantiations from different translation units into one - even +# across different -march targets. SU2 compiles with -march= (native by +# default); if Cantera's own compile used a different (or no) -march, the linker could +# keep either compiled body for a shared symbol, mixing SIMD-width assumptions and +# segfaulting. --cpu-arch keeps the two in sync (same fix already applied to CoolProp's +# CMAKE_CXX_FLAGS in meson.build). +CANTERA_SCONS_ARGS = [ + "layout=compact", + "python_package=n", + "f90_interface=n", + "googletest=none", + "hdf_support=n", + "doxygen_docs=no", + "sphinx_docs=no", + "system_eigen=y", + "system_fmt=n", + "system_yamlcpp=n", + "system_sundials=n", + "system_highfive=n", + # Use Eigen instead of a detected system BLAS/LAPACK that SU2 does not link. + "system_blas_lapack=n", + "logging=warning", + # The generated CLib needs doxygen at build time; SU2 only uses the C++ API. + "clib_legacy=y", + "debug=n", +] + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--source", required=True, help="Path to the Cantera submodule checkout" + ) + parser.add_argument( + "--prefix", required=True, help="Install prefix for the built library" + ) + parser.add_argument( + "--stamp", + required=True, + help="File to create on a successful build, for meson tracking", + ) + parser.add_argument( + "--boost-inc-dir", default="", help="Explicit Boost header directory (optional)" + ) + parser.add_argument( + "--eigen-inc-dir", + required=True, + help="SU2's own Eigen include directory (externals/eigen)", + ) + parser.add_argument( + "--cpu-arch", + default="", + help="Value of SU2's -march= target (cpu-arch option), kept in sync so Eigen template code Cantera and SU2 both instantiate is not ODR-mixed across incompatible vectorization ISAs", + ) + parser.add_argument( + "--jobs", + type=int, + default=4, + help="Number of parallel SCons jobs (0 uses all available cores)", + ) + args = parser.parse_args() + + if not os.path.exists(os.path.join(args.source, "SConstruct")): + sys.exit( + "Cantera sources not found in '{}'.\n" + "Run './preconfigure.py --with-cantera' (or 'git submodule update --init subprojects/cantera') " + "before configuring with -Denable-cantera=true.".format(args.source) + ) + + scons = shutil.which("scons") + if scons is None: + sys.exit( + "scons was not found on the PATH. Install it (e.g. 'pip install scons' or " + "'apt install scons') to build Cantera from source." + ) + + # Cantera's SCons script probes for Eigen by searching for "eigen3/Eigen/Dense" first, + # then plain "Eigen/Dense", across the compiler's whole include search path - not just + # extra_inc_dirs. If the host has a system libeigen3-dev package, that "eigen3/..." + # form gets found ahead of SU2's own (unprefixed) copy, defeating the point of pointing + # this build at SU2's Eigen at all. Force the match onto SU2's copy by presenting it + # under an "eigen3/" name via a symlink, in a directory searched before system ones. + eigen_shim_dir = args.prefix.rstrip("/") + "_eigen_shim" + os.makedirs(eigen_shim_dir, exist_ok=True) + eigen_shim_link = os.path.join(eigen_shim_dir, "eigen3") + if os.path.islink(eigen_shim_link) or os.path.exists(eigen_shim_link): + os.remove(eigen_shim_link) + os.symlink(args.eigen_inc_dir, eigen_shim_link) + + scons_command = ( + [scons, "build", "install", "prefix=" + args.prefix] + + CANTERA_SCONS_ARGS + + ["extra_inc_dirs=" + eigen_shim_dir] + + ["-j{}".format(args.jobs if args.jobs > 0 else multiprocessing.cpu_count())] + ) + if args.boost_inc_dir: + scons_command.append("boost_inc_dir=" + args.boost_inc_dir) + if args.cpu_arch: + scons_command.append("cc_flags=-march=" + args.cpu_arch) + + # Cantera adds $CONDA_PREFIX paths to its flags, which would make the build depend on the shell. + scons_env = { + key: value for key, value in os.environ.items() if key != "CONDA_PREFIX" + } + + result = subprocess.run(scons_command, cwd=args.source, env=scons_env) + if result.returncode != 0: + sys.exit( + "Cantera build failed (see output above). If SCons could not find Boost headers, " + "set -Dcantera-boost-inc-dir= and reconfigure, " + "or install Boost development headers (e.g. the 'libboost-dev' package)." + ) + + # With system_fmt=n the installed headers include "cantera/ext/...", which SCons leaves + # in the source tree only; copy them next to the installed headers. + ext_src = os.path.join(args.source, "include", "cantera", "ext") + ext_dst = os.path.join(args.prefix, "include", "cantera", "ext") + if os.path.isdir(ext_src): + shutil.copytree(ext_src, ext_dst, dirs_exist_ok=True) + + # meson tracks this custom_target as always-stale, so re-running scons here is cheap + # once Cantera itself is up to date; the stamp file just gives meson an output to track. + open(args.stamp, "w").close() + + +if __name__ == "__main__": + main() diff --git a/meson_scripts/init.py b/meson_scripts/init.py index 7a9ce0f0e887..27ed37bd232a 100755 --- a/meson_scripts/init.py +++ b/meson_scripts/init.py @@ -47,6 +47,7 @@ def init_submodules( own_opdi=True, own_mpp=True, own_cool=True, + own_cantera=False, own_mel=True, own_fado=True, own_mlpcpp=True, @@ -70,6 +71,8 @@ def init_submodules( github_repo_mpp = "https://github.com/mutationpp/Mutationpp" sha_version_coolprop = "98b3523d5daa98454618d381d2ae53f7471d216b" github_repo_coolprop = "https://github.com/CoolProp/CoolProp" + sha_version_cantera = "f22643ecb91f7e8d1852197658efc5260d54d4a3" + github_repo_cantera = "https://github.com/cantera/cantera" sha_version_mel = "46205ab019e5224559091375a6d71aabae6bc5b9" github_repo_mel = "https://github.com/pcarruscag/MEL" sha_version_fado = "ce7ee018e4e699af5028d69baa1939fea290e18a" @@ -91,6 +94,7 @@ def init_submodules( ninja_name = "ninja" mpp_name = "Mutationpp" coolprop_name = "CoolProp" + cantera_name = "cantera" mel_name = "MEL" fado_name = "FADO" mlpcpp_name = "MLPCpp" @@ -107,6 +111,7 @@ def init_submodules( alt_name_eigen = base_path + "eigen" alt_name_mpp = cur_dir + os.path.sep + "subprojects" + os.path.sep + "Mutationpp" alt_name_coolprop = cur_dir + os.path.sep + "subprojects" + os.path.sep + "CoolProp" + alt_name_cantera = cur_dir + os.path.sep + "subprojects" + os.path.sep + "cantera" alt_name_mlpcpp = cur_dir + os.path.sep + "subprojects" + os.path.sep + "MLPCpp" if method == "auto": @@ -135,6 +140,8 @@ def init_submodules( submodule_status(alt_name_mpp, sha_version_mpp) if own_cool: submodule_status(alt_name_coolprop, sha_version_coolprop) + if own_cantera: + submodule_status(alt_name_cantera, sha_version_cantera) if own_mel: submodule_status(alt_name_mel, sha_version_mel) if own_fado: @@ -173,6 +180,13 @@ def init_submodules( github_repo_coolprop, sha_version_coolprop, ) + if own_cantera: + download_module( + cantera_name, + alt_name_cantera, + github_repo_cantera, + sha_version_cantera, + ) if own_mel: download_module(mel_name, alt_name_mel, github_repo_mel, sha_version_mel) if own_fado: @@ -261,6 +275,20 @@ def submodule_status(path, sha_commit): print(original_path) os.chdir(original_path) print("CoolProp updated") + if "cantera" in path: + # update cantera + original_path = os.getcwd() + print("update cantera") + absolute_path = sys.path[0] + relative_path = "subprojects/cantera" + full_path = os.path.join(absolute_path, relative_path) + os.chdir(full_path) + print(full_path) + subprocess.run(["git", "submodule", "init"]) + subprocess.run(["git", "submodule", "update"]) + print(original_path) + os.chdir(original_path) + print("cantera updated") # Check that the SHA tag stored in this file matches the one stored in the git index cur_sha_commit = status[1:].split(" ")[0] if cur_sha_commit != sha_commit: diff --git a/preconfigure.py b/preconfigure.py index 16744a48bf02..bfba6995c5bb 100755 --- a/preconfigure.py +++ b/preconfigure.py @@ -89,6 +89,7 @@ def run( own_opdi=True, own_mpp=True, own_cool=True, + own_cantera=False, own_mel=True, own_fado=True, own_mlpcpp=True, @@ -103,6 +104,7 @@ def run( own_opdi=own_opdi, own_mpp=own_mpp, own_cool=own_cool, + own_cantera=own_cantera, own_mel=own_mel, own_fado=own_fado, own_mlpcpp=own_mlpcpp, @@ -146,6 +148,11 @@ def run( help="do not download own copy of CoolProp", action="store_false", ) + parser.add_argument( + "--with-cantera", + help="download own copy of Cantera, needed for -Denable-cantera=true", + action="store_true", + ) parser.add_argument( "--no-mel", help="do not download own copy of MEL", action="store_false" ) @@ -169,6 +176,7 @@ def run( own_opdi=args.no_opdi, own_mpp=args.no_mpp, own_cool=args.no_coolprop, + own_cantera=args.with_cantera, own_mel=args.no_mel, own_fado=args.no_fado, own_mlpcpp=args.no_mlpcpp, diff --git a/subprojects/cantera b/subprojects/cantera new file mode 160000 index 000000000000..f22643ecb91f --- /dev/null +++ b/subprojects/cantera @@ -0,0 +1 @@ +Subproject commit f22643ecb91f7e8d1852197658efc5260d54d4a3