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 dc96c21ad3b7..bd228163e2d2 100644
--- a/Common/include/CConfig.hpp
+++ b/Common/include/CConfig.hpp
@@ -80,7 +80,14 @@ 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. */
+ string TransportModel; /*!< \brief name transport model used in Cantera. */
+ string* GasCompositionNames; /*!< \brief gas composition used in Cantera. */
+ string ChemicalMechanismFile; /*!< \brief Chemical Reaction mechanism used in Cantera. */
+ string PhaseName; /*!< \brief Name of the phase in the chemical mechanism file used in Cantera. */
+ unsigned short n_GasCompositionNames; /*!<\brief Number of gases in mixture composition for Cantera. */
+ bool Combustion; /*!< \brief Flag for Combustion Detailed chemistry problems using Cantera. */
+ su2double Spark_Temperature; /*!< \bried Spark temperature used for ignition in detailed chemistry using Cantera. */
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.*/
@@ -4016,6 +4023,47 @@ class CConfig {
*/
string GetFluid_Name(void) const { return FluidName; }
+ /*!
+ * \brief Returns the transport model used in Cantera.
+ */
+ string GetTransport_Model(void) const { return TransportModel; }
+
+ /*!
+ * \brief Returns the chemical reaction mechanism (mechanism.yaml) used in Cantera.
+ */
+ string GetChemical_MechanismFile(void) const { return ChemicalMechanismFile; }
+
+ /*!
+ * \brief Returns the name of the pase in the chemical reaction mechanism file used in Cantera.
+ */
+ string GetPhase_Name(void) const { return PhaseName; }
+
+ /*!
+ * \brief Returns the gas composition used in Cantera.
+ */
+ string GetChemical_GasComposition(unsigned short val_index = 0) const { return GasCompositionNames[val_index]; }
+
+ /*!
+ * \brief Set the gas composition used in Cantera.
+ */
+ void SetChemical_GasComposition(unsigned short val_index, string gas_composition) const {
+ GasCompositionNames[val_index] = gas_composition;
+ }
+
+ /*!
+ * \brief Get information about the Combustion-Detailed chemistry using Cantera.
+ * \return TRUE if combustion-detailed chemistry using Cantera is used; otherwise FALSE.
+ */
+ bool GetCombustion(void) const { return Combustion; }
+
+ /*!
+ * \brief Get High temperature applied during spark ignition.
+ * \return Spark Temperature.
+ */
+ const su2double GetSpark_Temperature(void) const {
+ return Spark_Temperature;
+ }
+
/*!
* \brief Option to define the density model for incompressible flows.
* \return Density model option
diff --git a/Common/include/option_structure.hpp b/Common/include/option_structure.hpp
index 690350fae050..e2c70bb6fdd8 100644
--- a/Common/include/option_structure.hpp
+++ b/Common/include/option_structure.hpp
@@ -551,6 +551,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)
@@ -566,6 +567,7 @@ static const MapType FluidModel_Map = {
MakePair("COOLPROP", COOLPROP)
MakePair("DATADRIVEN_FLUID", DATADRIVEN_FLUID)
MakePair("FLUID_FLAMELET", FLUID_FLAMELET)
+ MakePair("FLUID_CANTERA", FLUID_CANTERA)
};
/*!
@@ -672,6 +674,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)
@@ -679,6 +682,7 @@ static const MapType ViscosityModel_Map = {
MakePair("POLYNOMIAL_VISCOSITY", VISCOSITYMODEL::POLYNOMIAL)
MakePair("FLAMELET", VISCOSITYMODEL::FLAMELET)
MakePair("COOLPROP", VISCOSITYMODEL::COOLPROP)
+ MakePair("CANTERA", VISCOSITYMODEL::CANTERA)
};
/*!
@@ -702,6 +706,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)
@@ -709,6 +714,7 @@ static const MapType ConductivityModel_Map = {
MakePair("POLYNOMIAL_CONDUCTIVITY", CONDUCTIVITYMODEL::POLYNOMIAL)
MakePair("FLAMELET", CONDUCTIVITYMODEL::FLAMELET)
MakePair("COOLPROP", CONDUCTIVITYMODEL::COOLPROP)
+ MakePair("CANTERA", CONDUCTIVITYMODEL::CANTERA)
};
/*!
@@ -732,6 +738,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 = {
@@ -740,6 +747,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)
};
/*!
diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp
index 8a95237ac3fb..f1fcd6615f3b 100644
--- a/Common/src/CConfig.cpp
+++ b/Common/src/CConfig.cpp
@@ -1222,6 +1222,20 @@ 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 TRANSPORT_MODEL \n DESCRIPTION: Transport model \n OPTIONS: see Cantera homepage \n DEFAULT: mixture-averaged \ingroup Config*/
+ addStringOption("TRANSPORT_MODEL", TransportModel, string("mixture-averaged"));
+ /*!\brief CHEMICAL_MECHANISM_FILE \n DESCRIPTION: Chemical reaction mechanism \n OPTIONS: see Cantera homepage \n DEFAULT: h2o2.yaml \ingroup Config*/
+ addStringOption("CHEMICAL_MECHANISM_FILE", ChemicalMechanismFile, string("h2o2.yaml"));
+ /*!\brief PHASE_NAME \n DESCRIPTION: name of the phase in the chemical mechanism file \n OPTIONS: see Cantera homepage \n DEFAULT: gri30 \ingroup Config*/
+ addStringOption("PHASE_NAME", PhaseName, string("ohmech"));
+ /*!\brief GAS_COMPOSITION_NAMES \n DESCRIPTION: Gas composition names \n OPTIONS: see Cantera homepage \n DEFAULT: \ingroup Config*/
+ addStringListOption("GAS_COMPOSITION_NAMES", n_GasCompositionNames, GasCompositionNames);
+ /*\brief COMBUSTION \n DESCRIPTION: Combustion Detailed chemistry using Cantera \n DEFAULT: false \ingroup Config*/
+ addBoolOption("COMBUSTION", Combustion, false);
+ /*!\brief SPARK_TEMPERATURE \n DESCRIPTION: Spark temperature used for ignition in detailed chemistry using Cantera \n DEFAULT: 1000 K \ingroup Config*/
+ addDoubleOption("SPARK_TEMPERATURE", Spark_Temperature, 1000.0);
+
/*!\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);
@@ -3485,7 +3499,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));
@@ -4155,6 +4170,67 @@ void CConfig::SetPostprocessing(SU2_COMPONENT val_software, unsigned short val_i
}
}
+ if ((Kind_FluidModel != FLUID_CANTERA) && (Combustion == true)) {
+ SU2_MPI::Error(
+ "The use of COMBUSTION=YES requires the use of FLUID_MIXTURE=FLUID_CANTERA,\n"
+ "detailed chemistry cannot be performed with other fluid models",
+ CURRENT_FUNCTION);
+ }
+
+ if ((flamelet_ParsedOptions.ignition_method != FLAMELET_INIT_TYPE::SPARK) && (Combustion == true)) {
+ SU2_MPI::Error(
+ "The use of 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) {
+ /*--- Check whether the number of entries of the GAS_COMPOSITION_NAMES equals the number of transported scalar
+ equations solved + 1.--- */
+ if (n_GasCompositionNames != nSpecies_Init + 1) {
+ SU2_MPI::Error(
+ "The use of FLUID_CANTERA requires the number of entries for GAS_COMPOSITION_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 requiere 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) {
@@ -5221,7 +5297,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);
}
}
diff --git a/SU2_CFD/include/fluid/CFluidCantera.hpp b/SU2_CFD/include/fluid/CFluidCantera.hpp
new file mode 100644
index 000000000000..95a3492862e4
--- /dev/null
+++ b/SU2_CFD/include/fluid/CFluidCantera.hpp
@@ -0,0 +1,156 @@
+/*!
+ * \file CFluidCantera.hpp
+ * \brief Defines the multicomponent incompressible Ideal Gas model for reacting flows.
+ * \author T. Economon, Cristopher Morales Ubal
+ * \version 8.4.0 "Harrier"
+ *
+ * SU2 Project Website: https://su2code.github.io
+ *
+ * The SU2 Project is maintained by the SU2 Foundation
+ * (http://su2foundation.org)
+ *
+ * Copyright 2012-2024, 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 "CFluidModel.hpp"
+
+#if defined(HAVE_CANTERA)
+#define USE_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 USE_CANTERA
+ const int n_species_mixture; /*!< \brief Number of species in mixture. */
+ const su2double Pressure_Thermodynamic; /*!< \brief Constant pressure thermodynamic. */
+ const su2double GasConstant_Ref; /*!< \brief Gas constant reference needed for Nondimensional problems. */
+ 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.*/
+
+ static constexpr int ARRAYSIZE = 16;
+
+ su2double Heat_Release; /*!< \brief heat release due to combustion */
+ std::array speciesIndices; /*!< \brief Species indices within Cantera library. */
+ std::shared_ptr sol; /*!< \brief Object needed to describe a chemically-reacting solution*/
+ std::array chemicalSourceTerm; /*!< \brief chemical source term of all species*/
+ std::array molarMasses; /*!< \brief Molar masses of all species. */
+ std::array enthalpyFormation; /*!< \brief Enthalpy of Formation of all species. */
+ std::array massFractions; /*!< \brief Mass fractions of all species. */
+ std::array massDiffusivity; /*!< \brief mass diffusivity of all species. */
+ vector netProductionRates; /*!< \brief Net chemical production rates of all species*/
+ mutable vector enthalpiesSpecies; /*!< \brief Molar enthalpies of all species. */
+ mutable vector specificHeatSpecies; /*!< \brief Molar heat capacities of all species. */
+
+ /*!
+ * \brief Compute heat release due to combustion.
+ */
+ void ComputeHeatRelease();
+
+ /*!
+ * \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);
+#endif
+
+ public:
+ /*!
+ * \brief Constructor of the class.
+ */
+ CFluidCantera(su2double val_operating_pressure, const CConfig* config);
+
+ #ifdef USE_CANTERA
+
+ /*!
+ * \brief Get fluid laminar viscosity.
+ */
+ inline su2double GetLaminarViscosity() override { return Mu; }
+
+ /*!
+ * \brief Get fluid thermal conductivity.
+ */
+ inline su2double GetThermalConductivity() override { return Kt + Mu_Turb * Cp / Prandtl_Turb_Number; }
+
+ /*!
+ * \brief Get fluid mass diffusivity.
+ * \param[in] ivar - index of species.
+ */
+ inline su2double GetMassDiffusivity(int ivar) override { return massDiffusivity[speciesIndices[ivar]]; }
+
+ /*!
+ * \brief Compute chemical source term for species.
+ */
+ 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 Heat release due to combustion.
+ */
+ inline su2double GetHeatRelease() override { return Heat_Release; }
+
+ /*!
+ * \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 Virtual member.
+ * \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;
+ #endif
+};
\ No newline at end of file
diff --git a/SU2_CFD/include/fluid/CFluidModel.hpp b/SU2_CFD/include/fluid/CFluidModel.hpp
index 83fa9939a3bb..c7639c9f0ed8 100644
--- a/SU2_CFD/include/fluid/CFluidModel.hpp
+++ b/SU2_CFD/include/fluid/CFluidModel.hpp
@@ -192,6 +192,22 @@ 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 Heat release due to combustion.
+ */
+ inline virtual su2double GetHeatRelease() { return 0.0; }
+
/*!
* \brief Get the enthalpy diffusivity terms for all species being solved.
*
diff --git a/SU2_CFD/include/numerics/CNumerics.hpp b/SU2_CFD/include/numerics/CNumerics.hpp
index cc9194997478..319c1705ed8e 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 Species diffusion coefficients at point i. */
+ *Chemical_Source_Term_j; /*!< \brief Species diffusion coefficients at point j. */
su2double
Laminar_Viscosity_i, /*!< \brief Laminar viscosity at point i. */
Laminar_Viscosity_j; /*!< \brief Laminar viscosity at point j. */
@@ -757,6 +759,16 @@ class CNumerics {
Diffusion_Coeff_j = val_diffusioncoeff_j;
}
+ /*!
+ * \brief Set the Chemical source term
+ * \param[in] val_source_term_i - Value of the chemical source term at i.
+ * \param[in] val_source_term_j - Value of the chemical source term at j
+ */
+ inline void SetChemicalSourceTerm(const su2double* val_source_term_i, const su2double* val_source_term_j) {
+ Chemical_Source_Term_i = val_source_term_i;
+ Chemical_Source_Term_j = val_source_term_j;
+ }
+
/*!
* \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_convection.hpp b/SU2_CFD/include/numerics/scalar/scalar_convection.hpp
index b8d8a6403930..7f98e9efd41b 100644
--- a/SU2_CFD/include/numerics/scalar/scalar_convection.hpp
+++ b/SU2_CFD/include/numerics/scalar/scalar_convection.hpp
@@ -47,7 +47,7 @@
template
class CUpwScalar : public CNumerics {
protected:
- enum : unsigned short {MAXNVAR = 8};
+ enum : unsigned short {MAXNVAR = 20};
const FlowIndices idx; /*!< \brief Object to manage the access to the flow primitives. */
su2double a0 = 0.0; /*!< \brief The maximum of the face-normal velocity and 0. */
diff --git a/SU2_CFD/include/numerics/scalar/scalar_diffusion.hpp b/SU2_CFD/include/numerics/scalar/scalar_diffusion.hpp
index a2942fc72999..e1ee9dad00b9 100644
--- a/SU2_CFD/include/numerics/scalar/scalar_diffusion.hpp
+++ b/SU2_CFD/include/numerics/scalar/scalar_diffusion.hpp
@@ -59,7 +59,7 @@ struct CNoFlowIndices {
template
class CAvgGrad_Scalar : public CNumerics {
protected:
- enum : unsigned short {MAXNVAR = 8};
+ enum : unsigned short {MAXNVAR = 20};
const FlowIndices idx; /*!< \brief Object to manage the access to the flow primitives. */
su2double Proj_Mean_GradScalarVar[MAXNVAR]; /*!< \brief Mean_gradScalarVar DOT normal, corrected if required. */
diff --git a/SU2_CFD/include/numerics/species/species_sources.hpp b/SU2_CFD/include/numerics/species/species_sources.hpp
index b30166e3b27a..c32441e95e3f 100644
--- a/SU2_CFD/include/numerics/species/species_sources.hpp
+++ b/SU2_CFD/include/numerics/species/species_sources.hpp
@@ -91,3 +91,30 @@ class CSourceAxisymmetric_Species : public CSourceBase_Species {
ResidualType<> ComputeResidual(const CConfig* config) override;
};
+
+/*!
+ * \class CSourceCombustion_Species
+ * \brief Class for source term for combustion problems.
+ * \ingroup SourceDiscr
+ * \author C.Morales Ubal
+ */
+template
+class CSourceCombustion_Species : public CSourceBase_Species {
+
+ 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/solvers/CIncNSSolver.hpp b/SU2_CFD/include/solvers/CIncNSSolver.hpp
index 3a6be3df3c57..3ba11cafb751 100644
--- a/SU2_CFD/include/solvers/CIncNSSolver.hpp
+++ b/SU2_CFD/include/solvers/CIncNSSolver.hpp
@@ -36,6 +36,8 @@
* \author F. Palacios, T. Economon, T. Albring
*/
class CIncNSSolver final : public CIncEulerSolver {
+ FluidFlamelet_ParsedOptions flamelet_config_options;
+ su2double TemperatureLimits[2];
/*!
* \brief Generic implementation of the isothermal, heatflux and heat-transfer/convection walls.
diff --git a/SU2_CFD/include/solvers/CSpeciesFlameletSolver.hpp b/SU2_CFD/include/solvers/CSpeciesFlameletSolver.hpp
index df61813a3e27..c983bc54284d 100644
--- a/SU2_CFD/include/solvers/CSpeciesFlameletSolver.hpp
+++ b/SU2_CFD/include/solvers/CSpeciesFlameletSolver.hpp
@@ -37,7 +37,6 @@
*/
class CSpeciesFlameletSolver final : public CSpeciesSolver {
private:
- FluidFlamelet_ParsedOptions flamelet_config_options;
bool include_mixture_fraction = false; /*!< \brief include mixture fraction as a controlling variable. */
/*!
* \brief Compute the preconditioner for low-Mach flows.
diff --git a/SU2_CFD/include/solvers/CSpeciesSolver.hpp b/SU2_CFD/include/solvers/CSpeciesSolver.hpp
index 82898f83b83f..c8abda0f9dde 100644
--- a/SU2_CFD/include/solvers/CSpeciesSolver.hpp
+++ b/SU2_CFD/include/solvers/CSpeciesSolver.hpp
@@ -40,6 +40,7 @@ class CSpeciesSolver : public CScalarSolver {
protected:
unsigned short Inlet_Position; /*!< \brief Column index for scalar variables in inlet files. */
vector Inlet_SpeciesVars; /*!< \brief Species variables at inlet profiles. */
+ FluidFlamelet_ParsedOptions flamelet_config_options;
vector Wall_SpeciesVars; /*!< \brief Species variables at profiles. */
vector> CustomBoundaryScalar;
diff --git a/SU2_CFD/include/variables/CSpeciesVariable.hpp b/SU2_CFD/include/variables/CSpeciesVariable.hpp
index 848235012318..5a668e2ddeac 100644
--- a/SU2_CFD/include/variables/CSpeciesVariable.hpp
+++ b/SU2_CFD/include/variables/CSpeciesVariable.hpp
@@ -36,6 +36,8 @@
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*/
+ 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. */
@@ -74,4 +76,41 @@ class CSpeciesVariable : public CScalarVariable {
* \return Pointer to the mass diffusivities
*/
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 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; }
};
diff --git a/SU2_CFD/include/variables/CVariable.hpp b/SU2_CFD/include/variables/CVariable.hpp
index bc4bc55e57c7..3adefd15ef8c 100644
--- a/SU2_CFD/include/variables/CVariable.hpp
+++ b/SU2_CFD/include/variables/CVariable.hpp
@@ -1127,6 +1127,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 961ce04832ff..483b0a56e17b 100644
--- a/SU2_CFD/src/drivers/CDriver.cpp
+++ b/SU2_CFD/src/drivers/CDriver.cpp
@@ -1417,7 +1417,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() == YES) {
+ 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 cea19c47c155..4c2657b2a304 100644
--- a/SU2_CFD/src/fluid/CDataDrivenFluid.cpp
+++ b/SU2_CFD/src/fluid/CDataDrivenFluid.cpp
@@ -469,4 +469,4 @@ void CDataDrivenFluid::ComputeIdealGasQuantities() {
Cp_idealgas = Cp;
gamma_idealgas = (R_idealgas / Cv_idealgas) + 1;
-}
\ 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..43a4f6df039e
--- /dev/null
+++ b/SU2_CFD/src/fluid/CFluidCantera.cpp
@@ -0,0 +1,194 @@
+/*!
+ * \file CFluidCantera.cpp
+ * \brief Defines the multicomponent incompressible Ideal Gas model for reacting flows.
+ * \author T. Economon, Cristopher Morales Ubal
+ * \version 8.4.0 "Harrier"
+ *
+ * SU2 Project Website: https://su2code.github.io
+ *
+ * The SU2 Project is maintained by the SU2 Foundation
+ * (http://su2foundation.org)
+ *
+ * Copyright 2012-2024, 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 USE_CANTERA
+#include
+#include
+
+using namespace Cantera;
+
+CFluidCantera::CFluidCantera(su2double value_pressure_operating, const CConfig* config)
+ : CFluidModel(),
+ n_species_mixture(config->GetnSpecies() + 1),
+ Pressure_Thermodynamic(value_pressure_operating),
+ GasConstant_Ref(config->GetGas_Constant_Ref()),
+ 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()) {
+ if (n_species_mixture > ARRAYSIZE) {
+ SU2_MPI::Error("Too many species, increase ARRAYSIZE", CURRENT_FUNCTION);
+ }
+ sol = std::shared_ptr(newSolution(Chemical_MechanismFile, Phase_Name, Transport_Model));
+ sol->thermo()->getMolecularWeights(&molarMasses[0]);
+ for (int iVar = 0; iVar < n_species_mixture; iVar++) {
+ speciesIndices[iVar] = sol->thermo()->speciesIndex(config->GetChemical_GasComposition(iVar));
+ }
+ enthalpiesSpecies.resize(sol->thermo()->nSpecies());
+ specificHeatSpecies.resize(sol->thermo()->nSpecies());
+ netProductionRates.resize(sol->thermo()->nSpecies());
+ if (Combustion) SetEnthalpyFormation(config);
+}
+
+void CFluidCantera::SetEnthalpyFormation(const CConfig* config) {
+ SetMassFractions(config->GetSpecies_Init());
+ sol->thermo()->setMassFractions(massFractions.data());
+ sol->thermo()->setState_TP(STD_REF_TEMP, Pressure_Thermodynamic);
+ sol->thermo()->getEnthalpy_RT_ref(enthalpiesSpecies.data());
+ for (int iVar = 0; iVar < n_species_mixture; iVar++) {
+ enthalpyFormation[iVar] =
+ GasConstant * STD_REF_TEMP * enthalpiesSpecies[speciesIndices[iVar]] / molarMasses[speciesIndices[iVar]];
+ }
+}
+
+void CFluidCantera::ComputeChemicalSourceTerm() {
+ sol->kinetics()->getNetProductionRates(netProductionRates.data());
+ for (int iVar = 0; iVar < n_species_mixture - 1.0; iVar++) {
+ chemicalSourceTerm[iVar] = molarMasses[speciesIndices[iVar]] * netProductionRates[speciesIndices[iVar]];
+ }
+}
+
+void CFluidCantera::ComputeHeatRelease() {
+ sol->kinetics()->getNetProductionRates(netProductionRates.data());
+ Heat_Release = 0.0;
+ for (int iVar = 0; iVar < n_species_mixture; iVar++) {
+ Heat_Release +=
+ -1.0 * enthalpyFormation[iVar] * molarMasses[speciesIndices[iVar]] * netProductionRates[speciesIndices[iVar]];
+ }
+}
+
+void CFluidCantera::GetEnthalpyDiffusivity(su2double* enthalpy_diffusions) const {
+ sol->thermo()->getEnthalpy_RT_ref(enthalpiesSpecies.data());
+ for (int iVar = 0; iVar < n_species_mixture - 1; iVar++) {
+ enthalpy_diffusions[iVar] =
+ Density * GasConstant * Temperature *
+ ((enthalpiesSpecies[speciesIndices[iVar]] * massDiffusivity[speciesIndices[iVar]] / molarMasses[speciesIndices[iVar]]) -
+ (enthalpiesSpecies[speciesIndices[n_species_mixture - 1]] * massDiffusivity[speciesIndices[n_species_mixture - 1]] /
+ molarMasses[speciesIndices[n_species_mixture - 1]]));
+ enthalpy_diffusions[iVar] +=
+ Mu_Turb * GasConstant * Temperature *
+ ((enthalpiesSpecies[speciesIndices[iVar]] / molarMasses[speciesIndices[iVar]]) -
+ (enthalpiesSpecies[speciesIndices[n_species_mixture - 1]] /
+ molarMasses[speciesIndices[n_species_mixture - 1]])) / Schmidt_Turb_Number;
+ }
+}
+
+void CFluidCantera::GetGradEnthalpyDiffusivity(su2double* grad_enthalpy_diffusions) const {
+ sol->thermo()->getCp_R_ref(specificHeatSpecies.data());
+ for (int iVar = 0; iVar < n_species_mixture - 1; iVar++) {
+ grad_enthalpy_diffusions[iVar] =
+ Density * GasConstant *
+ ((specificHeatSpecies[speciesIndices[iVar]] * massDiffusivity[speciesIndices[iVar]] / molarMasses[speciesIndices[iVar]]) -
+ (specificHeatSpecies[speciesIndices[n_species_mixture - 1]] * massDiffusivity[speciesIndices[n_species_mixture - 1]] /
+ molarMasses[speciesIndices[n_species_mixture - 1]]));
+ grad_enthalpy_diffusions[iVar] += Mu_Turb * GasConstant *
+ ((specificHeatSpecies[speciesIndices[iVar]] / molarMasses[speciesIndices[iVar]]) -
+ (specificHeatSpecies[speciesIndices[n_species_mixture - 1]] /
+ molarMasses[speciesIndices[n_species_mixture - 1]])) /
+ Schmidt_Turb_Number;
+ }
+}
+
+void CFluidCantera::SetMassFractions(const su2double* val_scalars) {
+ su2double val_scalars_sum{0.0};
+ massFractions.fill(0.0);
+ for (int i_scalar = 0; i_scalar < n_species_mixture - 1; i_scalar++) {
+ massFractions[speciesIndices[i_scalar]] = val_scalars[i_scalar];
+ val_scalars_sum += val_scalars[i_scalar];
+ }
+ massFractions[speciesIndices[n_species_mixture - 1]] = 1.0 - val_scalars_sum;
+}
+
+void CFluidCantera::SetTDState_T(const su2double val_temperature, const su2double* val_scalars) {
+ Temperature = val_temperature;
+ SetMassFractions(val_scalars);
+
+ sol->thermo()->setMassFractions(massFractions.data());
+ sol->thermo()->setState_TP(Temperature, Pressure_Thermodynamic);
+ Density = sol->thermo()->density();
+ Enthalpy = sol->thermo()->enthalpy_mass();
+ Cp = sol->thermo()->cp_mass();
+ Cv = sol->thermo()->cv_mass();
+ Mu = sol->transport()->viscosity();
+ Kt = sol->transport()->thermalConductivity();
+
+ sol->transport()->getMixDiffCoeffsMass(massDiffusivity.data());
+ if (Combustion) ComputeHeatRelease();
+}
+
+void CFluidCantera::SetTDState_h(const su2double val_enthalpy, const su2double* val_scalars) {
+ Enthalpy = val_enthalpy;
+ /*--- convergence criterion for temperature in [K], high accuracy needed for restarts. ---*/
+ su2double toll = 1e-5;
+ su2double temp_iter = 300.0;
+ su2double delta_temp_iter = 1e10;
+ su2double delta_enthalpy_iter;
+ const int counter_limit = 20;
+
+ int counter = 0;
+
+ /*--- Set mass fractions. ---*/
+ SetMassFractions(val_scalars);
+ sol->thermo()->setMassFractions(massFractions.data());
+
+ /*--- Computing temperature given enthalpy and species mass fractions using Newton-Raphson. ---*/
+ while ((abs(delta_temp_iter) > toll) && (counter++ < counter_limit)) {
+ /*--- Set thermodynamic state based on the current value of temperature. ---*/
+ sol->thermo()->setState_TP(temp_iter, Pressure_Thermodynamic);
+
+ su2double Enthalpy_iter = sol->thermo()->enthalpy_mass();
+ su2double Cp_iter = sol->thermo()->cp_mass();
+
+ delta_enthalpy_iter = Enthalpy - Enthalpy_iter;
+
+ delta_temp_iter = delta_enthalpy_iter / Cp_iter;
+
+ temp_iter += delta_temp_iter;
+ if (temp_iter < 0.0) {
+ cout << "Warning: Negative temperature has been found during Newton-Raphson" << endl;
+ break;
+ }
+ }
+ Temperature = temp_iter;
+ if (counter == counter_limit) {
+ cout << "Warning Newton-Raphson exceed number of max iteration in temperature computation" << endl;
+ }
+ SetTDState_T(Temperature, val_scalars);
+}
+
+#else
+CFluidCantera::CFluidCantera(su2double value_pressure_operating, const CConfig* config) {
+ SU2_MPI::Error("SU2 was not compiled with Cantera(-Denable-cantera=true)", CURRENT_FUNCTION);
+}
+#endif
\ No newline at end of file
diff --git a/SU2_CFD/src/fluid/CFluidModel.cpp b/SU2_CFD/src/fluid/CFluidModel.cpp
index 992a9adb491a..b275e539ca6a 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/interfaces/cht/CConjugateHeatInterface.cpp b/SU2_CFD/src/interfaces/cht/CConjugateHeatInterface.cpp
index 1b9969b3965f..64964eb89510 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 3b40822a34f4..cc7d9e79ee30 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',
@@ -194,11 +195,16 @@ 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,
include_directories: su2_cfd_include)
diff --git a/SU2_CFD/src/numerics/CNumerics.cpp b/SU2_CFD/src/numerics/CNumerics.cpp
index c1efac25a19e..5480cca38534 100644
--- a/SU2_CFD/src/numerics/CNumerics.cpp
+++ b/SU2_CFD/src/numerics/CNumerics.cpp
@@ -53,7 +53,9 @@ 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->GetKind_FluidModel() == FLUID_MIXTURE || config->GetKind_FluidModel() == FLUID_CANTERA) &&
+ 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 d6bc783ab928..d7937a85773b 100644
--- a/SU2_CFD/src/numerics/flow/flow_diffusion.cpp
+++ b/SU2_CFD/src/numerics/flow/flow_diffusion.cpp
@@ -545,7 +545,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 4e664ab5b6e4..8671a66ccf72 100644
--- a/SU2_CFD/src/numerics/flow/flow_sources.cpp
+++ b/SU2_CFD/src/numerics/flow/flow_sources.cpp
@@ -245,7 +245,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->GetKind_FluidModel() == FLUID_MIXTURE) || (config->GetKind_FluidModel() == FLUID_CANTERA);
}
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 8d96386e9a3f..8934cc9b2997 100644
--- a/SU2_CFD/src/numerics/species/species_sources.cpp
+++ b/SU2_CFD/src/numerics/species/species_sources.cpp
@@ -135,7 +135,41 @@ CNumerics::ResidualType<> CSourceAxisymmetric_Species::ComputeResidual(const
return ResidualType<>(residual, jacobian, nullptr);
}
+template
+CSourceCombustion_Species::CSourceCombustion_Species(unsigned short val_nDim, unsigned short val_nVar,
+ const CConfig* config)
+ : CSourceBase_Species(val_nDim, val_nVar, config) {}
+
+template
+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 = 0; jVar < nVar; jVar++) {
+ jacobian[iVar][jVar] = 0.0;
+ }
+ }
+
+ for (auto iVar = 0u; iVar < nVar; iVar++) {
+ residual[iVar] += Volume * Chemical_Source_Term_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 >;
template class CSourceAxisymmetric_Species >;
+template class CSourceCombustion_Species >;
+template class CSourceCombustion_Species >;
+template class CSourceCombustion_Species >;
diff --git a/SU2_CFD/src/output/CAdjFlowOutput.cpp b/SU2_CFD/src/output/CAdjFlowOutput.cpp
index c8ebfe898b81..a8b96edb3381 100644
--- a/SU2_CFD/src/output/CAdjFlowOutput.cpp
+++ b/SU2_CFD/src/output/CAdjFlowOutput.cpp
@@ -57,7 +57,11 @@ 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);
+ if(config->GetKind_FluidModel()==FLUID_CANTERA){
+ AddHistoryOutput("RMS_ADJ_SPECIES_" + config->GetChemical_GasComposition(iVar), "rms[A_rho*Y_" + config->GetChemical_GasComposition(iVar) + "]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of the adjoint transported species.", HistoryFieldType::RESIDUAL);
+ }else{
+ 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);
+ }
}
}
@@ -94,7 +98,11 @@ 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);
+ if(config->GetKind_FluidModel()==FLUID_CANTERA){
+ AddHistoryOutput("MAX_ADJ_SPECIES_" + config->GetChemical_GasComposition(iVar), "max[A_rho*Y_" + config->GetChemical_GasComposition(iVar) + "]",ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of the adjoint transported species.", HistoryFieldType::RESIDUAL);
+ }else{
+ 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);
+ }
}
}
@@ -204,10 +212,18 @@ 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)));
- if (multiZone) {
- SetHistoryOutputValue("BGS_ADJ_SPECIES_" + std::to_string(iVar), log10(adjspecies_solver->GetRes_BGS(iVar)));
+ if (config->GetKind_FluidModel() == FLUID_CANTERA) {
+ SetHistoryOutputValue("RMS_ADJ_SPECIES_" + config->GetChemical_GasComposition(iVar), log10(adjspecies_solver->GetRes_RMS(iVar)));
+ SetHistoryOutputValue("MAX_ADJ_SPECIES_" + config->GetChemical_GasComposition(iVar), log10(adjspecies_solver->GetRes_Max(iVar)));
+ if (multiZone) {
+ SetHistoryOutputValue("BGS_ADJ_SPECIES_" + config->GetChemical_GasComposition(iVar), log10(adjspecies_solver->GetRes_BGS(iVar)));
+ }
+ } else {
+ 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)));
+ if (multiZone) {
+ SetHistoryOutputValue("BGS_ADJ_SPECIES_" + std::to_string(iVar), log10(adjspecies_solver->GetRes_BGS(iVar)));
+ }
}
}
diff --git a/SU2_CFD/src/output/CFlowOutput.cpp b/SU2_CFD/src/output/CFlowOutput.cpp
index d9216b7bdc2f..3dc5ebba76db 100644
--- a/SU2_CFD/src/output/CFlowOutput.cpp
+++ b/SU2_CFD/src/output/CFlowOutput.cpp
@@ -84,7 +84,11 @@ 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);
+ if(config->GetKind_FluidModel()==FLUID_CANTERA){
+ AddHistoryOutput("SURFACE_SPECIES_" + config->GetChemical_GasComposition(iVar), "Avg_Species_" + config->GetChemical_GasComposition(iVar), ScreenOutputFormat::FIXED, "SPECIES_COEFF", "Total average species " + config->GetChemical_GasComposition(iVar) + " on all markers set in MARKER_ANALYZE", HistoryFieldType::COEFFICIENT);
+ }else{
+ 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);
+ }
}
/// 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 +130,11 @@ 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);
+ if(config->GetKind_FluidModel()==FLUID_CANTERA){
+ AddHistoryOutputPerSurface("SURFACE_SPECIES_" + config->GetChemical_GasComposition(iVar), "Avg_Species_" + config->GetChemical_GasComposition(iVar), ScreenOutputFormat::FIXED, "SPECIES_COEFF_SURF", Marker_Analyze, HistoryFieldType::COEFFICIENT);
+ }else{
+ AddHistoryOutputPerSurface("SURFACE_SPECIES_" + std::to_string(iVar), "Avg_Species_" + std::to_string(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 +514,12 @@ 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);
+ if (config->GetKind_FluidModel() == FLUID_CANTERA) {
+ SetHistoryOutputPerSurfaceValue("SURFACE_SPECIES_" + config->GetChemical_GasComposition(iVar), Species,
+ iMarker_Analyze);
+ } else {
+ SetHistoryOutputPerSurfaceValue("SURFACE_SPECIES_" + std::to_string(iVar), Species, iMarker_Analyze);
+ }
Tot_Surface_Species[iVar] += Species;
if (iVar == 0)
config->SetSurface_Species_0(iMarker_Analyze, Species);
@@ -542,8 +555,13 @@ 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++) {
+ if (config->GetKind_FluidModel() == FLUID_CANTERA) {
+ SetHistoryOutputValue("SURFACE_SPECIES_" + config->GetChemical_GasComposition(iVar), Tot_Surface_Species[iVar]);
+ } else {
+ SetHistoryOutputValue("SURFACE_SPECIES_" + std::to_string(iVar), Tot_Surface_Species[iVar]);
+ }
+ }
SetAnalyzeSurfaceSpeciesVariance(solver, geometry, config, Surface_Species_Total, Surface_MassFlow_Abs_Total,
Surface_Area_Total);
@@ -1058,7 +1076,11 @@ 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);
+ if (config->GetKind_FluidModel()==FLUID_CANTERA){
+ AddHistoryOutput("RMS_SPECIES_" + config->GetChemical_GasComposition(iVar), "rms[rho*Y_" + config->GetChemical_GasComposition(iVar)+"]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of transported species.", HistoryFieldType::RESIDUAL);
+ }else{
+ 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);
+ }
}
break;
}
@@ -1116,7 +1138,11 @@ 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);
+ if (config->GetKind_FluidModel()==FLUID_CANTERA){
+ AddHistoryOutput("MAX_SPECIES_" + config->GetChemical_GasComposition(iVar), "max[rho*Y_" + config->GetChemical_GasComposition(iVar)+"]", ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of transported species.", HistoryFieldType::RESIDUAL);
+ }else{
+ 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);
+ }
}
break;
}
@@ -1172,7 +1198,11 @@ 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);
+ if (config->GetKind_FluidModel()==FLUID_CANTERA){
+ AddHistoryOutput("BGS_SPECIES_" + config->GetChemical_GasComposition(iVar), "bgs[rho*Y_" + config->GetChemical_GasComposition(iVar)+"]", ScreenOutputFormat::FIXED, "BGS_RES", "Maximum residual of transported species.", HistoryFieldType::RESIDUAL);
+ }else{
+ 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);
+ }
}
break;
}
@@ -1272,10 +1302,18 @@ 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)));
- if (multiZone) {
- SetHistoryOutputValue("BGS_SPECIES_" + std::to_string(iVar), log10(solver[SPECIES_SOL]->GetRes_BGS(iVar)));
+ if (config->GetKind_FluidModel()==FLUID_CANTERA){
+ SetHistoryOutputValue("RMS_SPECIES_" + config->GetChemical_GasComposition(iVar), log10(solver[SPECIES_SOL]->GetRes_RMS(iVar)));
+ SetHistoryOutputValue("MAX_SPECIES_" + config->GetChemical_GasComposition(iVar), log10(solver[SPECIES_SOL]->GetRes_Max(iVar)));
+ if (multiZone) {
+ SetHistoryOutputValue("BGS_SPECIES_" + config->GetChemical_GasComposition(iVar), log10(solver[SPECIES_SOL]->GetRes_BGS(iVar)));
+ }
+ }else{
+ 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)));
+ if (multiZone) {
+ SetHistoryOutputValue("BGS_SPECIES_" + std::to_string(iVar), log10(solver[SPECIES_SOL]->GetRes_BGS(iVar)));
+ }
}
}
SetHistoryOutputValue("LINSOL_ITER_SPECIES", solver[SPECIES_SOL]->GetIterLinSolver());
@@ -1342,8 +1380,14 @@ 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++) {
+ if (config->GetKind_FluidModel() == FLUID_CANTERA) {
+ AddVolumeOutput("SPECIES_" + config->GetChemical_GasComposition(iVar), "Species_" + config->GetChemical_GasComposition(iVar), "SOLUTION",
+ "Species_" + config->GetChemical_GasComposition(iVar) + " mass fraction");
+ } else {
+ AddVolumeOutput("SPECIES_" + std::to_string(iVar), "Species_" + std::to_string(iVar), "SOLUTION",
+ "Species_" + std::to_string(iVar) + " mass fraction");
+ }
}
break;
case SPECIES_MODEL::FLAMELET: {
@@ -1385,7 +1429,13 @@ 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));
+ if (config->GetKind_FluidModel() == FLUID_CANTERA) {
+ AddVolumeOutput("RES_SPECIES_" + config->GetChemical_GasComposition(iVar),
+ "Residual_Species_" + config->GetChemical_GasComposition(iVar), "RESIDUAL",
+ "Residual of the transported species " + config->GetChemical_GasComposition(iVar));
+ } else {
+ AddVolumeOutput("RES_SPECIES_" + std::to_string(iVar), "Residual_Species_" + std::to_string(iVar), "RESIDUAL", "Residual of the transported species " + std::to_string(iVar));
+ }
}
break;
case SPECIES_MODEL::FLAMELET: {
@@ -1470,8 +1520,20 @@ 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));
+ if (config->GetKind_FluidModel() == FLUID_CANTERA) {
+ AddVolumeOutput("DIFFUSIVITY_" + config->GetChemical_GasComposition(iVar), "Diffusivity_" + config->GetChemical_GasComposition(iVar), "PRIMITIVE",
+ "Diffusivity of the transported species " + config->GetChemical_GasComposition(iVar));
+ if (config->GetCombustion() == true) {
+ AddVolumeOutput("CHEMICAL_SOURCE_TERM_" + config->GetChemical_GasComposition(iVar),
+ "Chemical_Source_Term_" + config->GetChemical_GasComposition(iVar), "PRIMITIVE",
+ "Chemical source term of the transported species " + config->GetChemical_GasComposition(iVar));
+ }
+ } else {
+ AddVolumeOutput("DIFFUSIVITY_" + std::to_string(iVar), "Diffusivity_" + std::to_string(iVar), "PRIMITIVE", "Diffusivity of the transported species " + std::to_string(iVar));
+ }
}
+ if (config->GetCombustion() == true)
+ AddVolumeOutput("HEAT_RELEASE", "Heat_Release", "PRIMITIVE", "Heat release due to combustion");
break;
default:
break;
@@ -1656,16 +1718,32 @@ 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));
- 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));
+ for (unsigned short iVar = 0; iVar < config->GetnSpecies(); iVar++) {
+ if (config->GetKind_FluidModel() == FLUID_CANTERA) {
+ SetVolumeOutputValue("SPECIES_" + config->GetChemical_GasComposition(iVar), iPoint, Node_Species->GetSolution(iPoint, iVar));
+ SetVolumeOutputValue("RES_SPECIES_" + config->GetChemical_GasComposition(iVar), iPoint,
+ solver[SPECIES_SOL]->LinSysRes(iPoint, iVar));
+ SetVolumeOutputValue("DIFFUSIVITY_" + config->GetChemical_GasComposition(iVar), iPoint,
+ Node_Species->GetDiffusivity(iPoint, iVar));
+ if (config->GetCombustion() == true)
+ SetVolumeOutputValue("CHEMICAL_SOURCE_TERM_" + config->GetChemical_GasComposition(iVar), iPoint,
+ Node_Species->GetChemicalSourceTerm(iPoint, iVar));
+ if (config->GetKind_SlopeLimit_Species() != LIMITER::NONE)
+ SetVolumeOutputValue("LIMITER_SPECIES_" + config->GetChemical_GasComposition(iVar), iPoint,
+ Node_Species->GetLimiter(iPoint, iVar));
+ } else {
+ 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));
+ 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));
+ }
}
}
+ if (config->GetCombustion() == true)
+ SetVolumeOutputValue("HEAT_RELEASE", iPoint, Node_Species->GetHeatRelease(iPoint));
break;
}
@@ -3239,6 +3317,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";
@@ -3294,6 +3379,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 c99f87896c2d..d9e3960782ba 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"
@@ -336,6 +337,14 @@ void CIncEulerSolver::SetNondimensionalization(CConfig *config, unsigned short i
auxFluidModel->SetTDState_T(Temperature_FreeStream, config->GetSpecies_Init());
break;
+ case FLUID_CANTERA:
+
+ config->SetGas_Constant(UNIVERSAL_GAS_CONSTANT / (config->GetMolecular_Weight() / 1000.0));
+ Pressure_Thermodynamic = config->GetPressure_Thermodynamic();
+ 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);
@@ -498,6 +507,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) {
@@ -665,6 +679,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";
@@ -728,6 +751,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";
@@ -815,6 +845,23 @@ void CIncEulerSolver::SetNondimensionalization(CConfig *config, unsigned short i
NonDimTable.PrintFooter();
break;
+ case FLUID_CANTERA:
+ ModelTable << "CANTERA";
+ Unit << "N.m/kg.K";
+ NonDimTable << "Spec. Heat (Cp)" << config->GetSpecific_Heat_Cp() << config->GetSpecific_Heat_Cp() / config->GetSpecific_Heat_CpND() << Unit.str() << config->GetSpecific_Heat_CpND();
+ Unit.str("");
+ Unit << "g/mol";
+ NonDimTable << "Molecular weight" << config->GetMolecular_Weight() << 1.0 << Unit.str() << config->GetMolecular_Weight();
+ Unit.str("");
+ Unit << "N.m/kg.K";
+ NonDimTable << "Gas Constant" << config->GetGas_Constant() << config->GetGas_Constant_Ref() << Unit.str() << config->GetGas_ConstantND();
+ 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("");
@@ -1238,7 +1285,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->GetKind_FluidModel() == FLUID_MIXTURE) || (config->GetKind_FluidModel() == FLUID_CANTERA));
const su2double kappa = config->GetMUSCL_Kappa_Flow();
const su2double musclRamp = config->GetMUSCLRampValue() * config->GetNewtonKrylovRelaxation();
@@ -1416,7 +1463,8 @@ 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->GetKind_FluidModel() == FLUID_MIXTURE) || (config->GetKind_FluidModel() == FLUID_CANTERA));
AD::StartNoSharedReading();
@@ -2191,7 +2239,9 @@ 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->GetKind_FluidModel() == FLUID_MIXTURE || config->GetKind_FluidModel() == FLUID_CANTERA) &&
+ config->GetEnergy_Equation();
const bool species_model = config->GetKind_Species_Model() != SPECIES_MODEL::NONE;
su2double Normal[MAXNDIM] = {0.0};
@@ -2334,7 +2384,9 @@ 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->GetKind_FluidModel() == FLUID_MIXTURE || config->GetKind_FluidModel() == FLUID_CANTERA) &&
+ config->GetEnergy_Equation();
const bool species_model = config->GetKind_Species_Model() != SPECIES_MODEL::NONE;
string Marker_Tag = config->GetMarker_All_TagBound(val_marker);
@@ -2595,7 +2647,9 @@ 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->GetKind_FluidModel() == FLUID_MIXTURE || config->GetKind_FluidModel() == FLUID_CANTERA) &&
+ 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 6477fa6f3619..7aad568e375f 100644
--- a/SU2_CFD/src/solvers/CIncNSSolver.cpp
+++ b/SU2_CFD/src/solvers/CIncNSSolver.cpp
@@ -42,6 +42,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 ---*/
@@ -52,6 +54,7 @@ CIncNSSolver::CIncNSSolver(CGeometry *geometry, CConfig *config, unsigned short
default:
break;
}
+ if (config->GetCombustion()) flamelet_config_options = config->GetFlameletParsedOptions();
/*--- Set the initial Streamwise periodic pressure drop value. ---*/
@@ -69,6 +72,54 @@ 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();
+
+ /*--- Setting temperature, enthalpy and thermophysical properties for ignition in reacting flows. ---*/
+ if (energy && combustion) {
+ unsigned long spark_iter_start, spark_duration;
+ bool ignition = false;
+
+ /*--- Retrieve spark ignition parameters for spark-type ignition. ---*/
+ 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]);
+ unsigned long iter = config->GetMultizone_Problem() ? config->GetOuterIter() : config->GetInnerIter();
+ ignition = ((iter >= spark_iter_start) && (iter <= (spark_iter_start + spark_duration)));
+ }
+
+ SU2_OMP_SAFE_GLOBAL_ACCESS(config->SetGlobalParam(config->GetKind_Solver(), RunTime_EqSystem);)
+
+ if (ignition) {
+ SU2_OMP_FOR_STAT(omp_chunk_size)
+ for (auto i_point = 0u; i_point < nPoint; i_point++) {
+ /*--- Retrieve fluid model. ---*/
+ CFluidModel* fluid_model_local = solver_container[FLOW_SOL]->GetFluidModel();
+ /*--- Apply ignition temperature 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());
+ if (dist_from_center < pow(spark_radius, 2)) {
+ /*--- Retrieve scalars solution. ---*/
+ su2double* scalars = solver_container[SPECIES_SOL]->GetNodes()->GetSolution(i_point);
+ /*--- Set high temperature for ignition. ---*/
+ nodes->SetTemperature(i_point, config->GetSpark_Temperature(), TemperatureLimits);
+ /*--- Set thermodynamic state at high temperature. ---*/
+ fluid_model_local->SetTDState_T(config->GetSpark_Temperature(), scalars);
+ /*--- Set total enthalpy at high temperature. ---*/
+ nodes->SetSolution(i_point, nDim + 1, fluid_model_local->GetEnthalpy());
+ /*--- Set thermodynamics and transport properties at high temperature for consistency. ---*/
+ 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
+ }
+ }
/*--- Common preprocessing steps (implemented by CEulerSolver) ---*/
@@ -275,7 +326,9 @@ void CIncNSSolver::Compute_Streamwise_Periodic_Recovered_Values(CConfig *config,
void 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->GetKind_FluidModel() == FLUID_MIXTURE || config->GetKind_FluidModel() == FLUID_CANTERA) &&
+ config->GetEnergy_Equation();
/*--- Contribution to heat flux due to enthalpy diffusion for multicomponent and reacting flows ---*/
if (energy_multicomponent) {
diff --git a/SU2_CFD/src/solvers/CSpeciesSolver.cpp b/SU2_CFD/src/solvers/CSpeciesSolver.cpp
index e1b65c56f823..abe9c1a57c69 100644
--- a/SU2_CFD/src/solvers/CSpeciesSolver.cpp
+++ b/SU2_CFD/src/solvers/CSpeciesSolver.cpp
@@ -42,6 +42,8 @@ CSpeciesSolver::CSpeciesSolver(CGeometry* geometry, CConfig* config, unsigned sh
nVar = config->GetnSpecies();
+ if (config->GetCombustion()) flamelet_config_options = config->GetFlameletParsedOptions();
+
Initialize(geometry, config, iMesh, nVar);
/*--- Initialize the solution to the far-field state everywhere. ---*/
@@ -306,20 +308,57 @@ void CSpeciesSolver::LoadRestart(CGeometry** geometry, CSolver*** solver, CConfi
void CSpeciesSolver::Preprocessing(CGeometry* geometry, CSolver** solver_container, CConfig* config,
unsigned short iMesh, unsigned short iRKStep, unsigned short RunTime_EqSystem,
bool Output) {
+ unsigned long spark_iter_start, spark_duration;
+ bool ignition = false;
+ su2double temperature;
+
+ /*--- Retrieve spark ignition parameters for spark-type ignition. ---*/
+ 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]);
+ unsigned long iter = config->GetMultizone_Problem() ? config->GetOuterIter() : config->GetInnerIter();
+ ignition = ((iter >= spark_iter_start) && (iter <= (spark_iter_start + spark_duration)));
+ }
SU2_OMP_SAFE_GLOBAL_ACCESS(config->SetGlobalParam(config->GetKind_Solver(), RunTime_EqSystem);)
+ /*--- 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);
+ if (ignition) {
+ /*--- Apply ignition temperature 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(iPoint), flamelet_config_options.spark_init.data());
+ if (dist_from_center < pow(spark_radius, 2)) {
+ temperature = config->GetSpark_Temperature();
+ } else {
+ temperature = solver_container[FLOW_SOL]->GetNodes()->GetTemperature(iPoint);
+ }
+ } else {
+ temperature = solver_container[FLOW_SOL]->GetNodes()->GetTemperature(iPoint);
+ }
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);
+ if (config->GetCombustion() == true) {
+ /*--- Call function to compute chemical source term. ---*/
+ solver_container[FLOW_SOL]->GetFluidModel()->ComputeChemicalSourceTerm();
+ /*--- Get and set heat release due to combustion. ---*/
+ const su2double heat_release = solver_container[FLOW_SOL]->GetFluidModel()->GetHeatRelease();
+ nodes->SetHeatRelease(iPoint, heat_release);
+ }
/*--- 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);
+ if (config->GetCombustion() == true) {
+ /*--- Get and Set chemical source term. ---*/
+ const su2double chemical_source_term = solver_container[FLOW_SOL]->GetFluidModel()->GetChemicalSourceTerm(iVar);
+ nodes->SetChemicalSourceTerm(iPoint, chemical_source_term, iVar);
+ }
}
} // iPoint
END_SU2_OMP_FOR
@@ -603,6 +642,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];
@@ -652,7 +692,34 @@ 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), nullptr);
+
+ /*--- 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 f5201c92cb58..27e40057bf1f 100644
--- a/SU2_CFD/src/variables/CIncNSVariable.cpp
+++ b/SU2_CFD/src/variables/CIncNSVariable.cpp
@@ -69,7 +69,7 @@ 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. ---*/
diff --git a/SU2_CFD/src/variables/CSpeciesVariable.cpp b/SU2_CFD/src/variables/CSpeciesVariable.cpp
index a02f417a07b2..a0137422a78b 100644
--- a/SU2_CFD/src/variables/CSpeciesVariable.cpp
+++ b/SU2_CFD/src/variables/CSpeciesVariable.cpp
@@ -30,8 +30,12 @@
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);
+ 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 93a49a2ce0f5..612b8c9237b0 100644
--- a/SU2_PY/SU2/io/historyMap.py
+++ b/SU2_PY/SU2/io/historyMap.py
@@ -244,6 +244,12 @@
"HEADER": 'bgs[rho*Y_" + ' 'std::to_string(iVar)+"]',
"TYPE": "RESIDUAL",
},
+ 'BGS_SPECIES_" + config->GetChemical_GasComposition(iVar': {
+ "DESCRIPTION": "BGS residual of " "transported species.",
+ "GROUP": "BGS_RES",
+ "HEADER": 'bgs[rho*Y_" + ' 'config->GetChemical_GasComposition(iVar)+"]',
+ "TYPE": "RESIDUAL",
+ },
"BGS_TEMPERATURE": {
"DESCRIPTION": "Block-Gauss-Seidel residual of the " "temperature",
"GROUP": "BGS_RES",
@@ -810,6 +816,12 @@
"HEADER": 'max[A_rho*Y_" + ' "std::to_string(iVar) + " '"]',
"TYPE": "RESIDUAL",
},
+ 'MAX_ADJ_SPECIES_" + config->GetChemical_GasComposition(iVar': {
+ "DESCRIPTION": "Maximum residual " "of the adjoint " "transported " "species.",
+ "GROUP": "MAX_RES",
+ "HEADER": 'max[A_rho*Y_" + ' "config->GetChemical_GasComposition(iVar) + " '"]',
+ "TYPE": "RESIDUAL",
+ },
"MAX_ADJ_TEMPERATURE": {
"DESCRIPTION": "Maximum residual of the temperature.",
"GROUP": "MAX_RES",
@@ -900,6 +912,12 @@
"HEADER": "max[P]",
"TYPE": "RESIDUAL",
},
+ 'MAX_SPECIES_" + config->GetChemical_GasComposition(iVar': {
+ "DESCRIPTION": "Maximum residual of " "transported species.",
+ "GROUP": "MAX_RES",
+ "HEADER": 'max[rho*Y_" + ' 'config->GetChemical_GasComposition(iVar)+"]',
+ "TYPE": "RESIDUAL",
+ },
'MAX_SPECIES_" + std::to_string(iVar': {
"DESCRIPTION": "Maximum residual of " "transported species.",
"GROUP": "MAX_RES",
@@ -1061,6 +1079,16 @@
"HEADER": 'rms[A_rho*Y_" + ' "std::to_string(iVar) + " '"]',
"TYPE": "RESIDUAL",
},
+ 'RMS_ADJ_SPECIES_" + config->GetChemical_GasComposition(iVar': {
+ "DESCRIPTION": "Root-mean square "
+ "residual of the "
+ "adjoint "
+ "transported "
+ "species.",
+ "GROUP": "RMS_RES",
+ "HEADER": 'rms[A_rho*Y_" + ' "config->GetChemical_GasComposition(iVar) + " '"]',
+ "TYPE": "RESIDUAL",
+ },
"RMS_ADJ_TEMPERATURE": {
"DESCRIPTION": "Root-mean square residual of the " "adjoint temperature.",
"GROUP": "RMS_RES",
@@ -1178,6 +1206,12 @@
"HEADER": 'rms[rho*Y_" + ' 'std::to_string(iVar)+"]',
"TYPE": "RESIDUAL",
},
+ 'RMS_SPECIES_" + config->GetChemical_GasComposition(iVar': {
+ "DESCRIPTION": "Root-mean square " "residual of " "transported species.",
+ "GROUP": "RMS_RES",
+ "HEADER": 'rms[rho*Y_" + ' 'config->GetChemical_GasComposition(iVar)+"]',
+ "TYPE": "RESIDUAL",
+ },
"RMS_TEMPERATURE": {
"DESCRIPTION": "Root mean square residual of the " "temperature",
"GROUP": "RMS_RES",
@@ -1361,6 +1395,17 @@
"HEADER": 'Avg_Species_" + ' "std::to_string(iVar",
"TYPE": "COEFFICIENT",
},
+ 'SURFACE_SPECIES_" + config->GetChemical_GasComposition(iVar': {
+ "DESCRIPTION": "Total average "
+ 'species " + '
+ "std::to_string(iVar) "
+ '+ " on all '
+ "markers set in "
+ "MARKER_ANALYZE",
+ "GROUP": "SPECIES_COEFF",
+ "HEADER": 'Avg_Species_" + ' "config->GetChemical_GasComposition(iVar",
+ "TYPE": "COEFFICIENT",
+ },
"SURFACE_SPECIES_VARIANCE": {
"DESCRIPTION": "Total species variance",
"GROUP": "SPECIES_COEFF",
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..befcbaeca56b
--- /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.4.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
+COMBUSTION = NO
+%
+THERMODYNAMIC_PRESSURE= 101325.0
+%
+CONDUCTIVITY_MODEL= CANTERA
+%
+TURBULENT_CONDUCTIVITY_MODEL= NONE
+%
+GAS_COMPOSITION_NAMES= H2, O2
+CHEMICAL_MECHANISM_FILE= h2o2.yaml
+PHASE_NAME= ohmech
+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..e0af2a0d299a
--- /dev/null
+++ b/UnitTests/SU2_CFD/fluid/CFluidCantera_tests.cpp
@@ -0,0 +1,95 @@
+/*!
+ * \file CFluidCantera_tests.cpp
+ * \brief Unit tests for Cantera fluid model.
+ * \author C.Morales Ubal
+ * \version 8.4.0 "Harrier"
+ *
+ * SU2 Project Website: https://su2code.github.io
+ *
+ * The SU2 Project is maintained by the SU2 Foundation
+ * (http://su2foundation.org)
+ *
+ * Copyright 2012-2024, 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
+
+#if defined(HAVE_CANTERA)
+#define USE_CANTERA
+#include "../../../SU2_CFD/include/fluid/CFluidCantera.hpp"
+#include
+
+using namespace Cantera;
+#endif
+
+#ifdef USE_CANTERA
+TEST_CASE("Fluid_Cantera", "[Multicomponent_flow]") {
+ /*--- Cantera fluid model unit test cases. ---*/
+
+ SU2_COMPONENT val_software = SU2_COMPONENT::SU2_CFD;
+ CConfig* config = new CConfig("multicomponent_cantera.cfg", val_software, true);
+ CFluidCantera* auxFluidModel = nullptr;
+
+ /*--- Create Cantera fluid model. ---*/
+ su2double value_pressure_operating = config->GetPressure_Thermodynamic();
+ auxFluidModel = new CFluidCantera(value_pressure_operating, config);
+
+ /*--- Get scalar from config file and set temperature. ---*/
+ const su2double* scalar = nullptr;
+ scalar = config->GetSpecies_Init();
+ const su2double Temperature = 300.0;
+ /*--- Set state using temperature and scalar. ---*/
+ auxFluidModel->SetTDState_T(Temperature, scalar);
+
+ /*--- Check values for density and heat capacity. ---*/
+
+ su2double density = auxFluidModel->GetDensity();
+ su2double cp = auxFluidModel->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 = new CConfig("multicomponent_cantera.cfg", val_software, true);
+ CFluidCantera* auxFluidModel = nullptr;
+
+ /*--- Create Cantera fluid model. ---*/
+ su2double value_pressure_operating = config->GetPressure_Thermodynamic();
+ auxFluidModel = new CFluidCantera(value_pressure_operating, config);
+
+ /*--- Get scalar from config file and set temperature. ---*/
+
+ const su2double* scalar = nullptr;
+ scalar = config->GetSpecies_Init();
+ const su2double Temperature = 1900.0;
+ /*--- Set state using temperature and scalar ---*/
+ auxFluidModel->SetTDState_T(Temperature, scalar);
+
+ /*--- Compute chemical source terms. ---*/
+ auxFluidModel->ComputeChemicalSourceTerm();
+
+ /*--- Check values for source terms. ---*/
+
+ su2double sourceTerm_H2 = auxFluidModel->GetChemicalSourceTerm(0);
+ su2double sourceTerm_O2 = auxFluidModel->GetChemicalSourceTerm(1);
+ CHECK(sourceTerm_H2 == Approx(-0.13633797171426));
+ CHECK(sourceTerm_O2 == Approx(-2.16321066087493));
+}
+#endif
\ No newline at end of file
diff --git a/UnitTests/SU2_CFD/fluid/multicomponent_cantera.cfg b/UnitTests/SU2_CFD/fluid/multicomponent_cantera.cfg
new file mode 100644
index 000000000000..dc4a9dc4c3bb
--- /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.1.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
+COMBUSTION= YES
+FLAME_INIT_METHOD= SPARK
+%
+CONDUCTIVITY_MODEL= CANTERA
+VISCOSITY_MODEL= CANTERA
+%
+GAS_COMPOSITION_NAMES= H2, O2, N2
+CHEMICAL_MECHANISM_FILE= h2o2.yaml
+PHASE_NAME= ohmech
+TRANSPORT_MODEL= mixture-averaged
+%
+% -------------------- SCALAR TRANSPORT ---------------------------------------%
+%
+KIND_SCALAR_MODEL= SPECIES_TRANSPORT
+DIFFUSIVITY_MODEL= CANTERA
+%
+SPECIES_INIT= 0.020137986304114414,0.22830757006769173
diff --git a/UnitTests/meson.build b/UnitTests/meson.build
index 3e64cfb9aa3d..2839e0904b2a 100644
--- a/UnitTests/meson.build
+++ b/UnitTests/meson.build
@@ -12,6 +12,7 @@ su2_cfd_tests = files(['Common/geometry/primal_grid/CPrimalGrid_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',
@@ -31,14 +32,20 @@ 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]
+ if get_option('enable-cantera')
+ su2_cpp_args += '-DHAVE_CANTERA'
+ dependencies_default += dependency('cantera')
+ endif
test_driver = executable(
'test_driver',
unit_test_files,
install : true,
- 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/cantera_meson_build.py b/cantera_meson_build.py
new file mode 100644
index 000000000000..3a025dbda416
--- /dev/null
+++ b/cantera_meson_build.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python3
+
+## \file cantera_meson_build.py
+# \brief create necessary meson.build file for linking cantera to
+# SU2.
+# \author C.Morales Ubal
+# \version 8.4.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 sys, os
+
+absolute_path = sys.path[0]
+relative_path = "subprojects/cantera"
+meson_path = os.path.join(absolute_path, relative_path)
+
+# Path to the meson.build under SU2/subprojects/cantera/
+meson_build_path = os.path.join(meson_path, "meson.build")
+
+# Create meson.build in the cantera subproject if it does not already exist
+if not os.path.exists(meson_build_path):
+ print(f"Writing meson.build in {meson_path}")
+ meson_build_content = ["project('cantera', 'c', 'cpp', default_options: ['cpp_std=c++17'])\n",
+ "cc = meson.get_compiler('cpp')\n",
+ "cantera_inc = include_directories('install/include')\n",
+ "cantera_lib = cc.find_library('cantera', dirs: [meson.current_source_dir() / 'install' / 'lib'], required: false, static: true)\n",
+ "cantera_dep = declare_dependency(include_directories: cantera_inc, dependencies: cantera_lib)\n",
+ "meson.override_dependency('cantera', cantera_dep)"]
+ # Write the meson.build file
+ with open(meson_build_path, 'w') as f:
+ f.writelines(meson_build_content)
+ print(f"meson.build in {meson_path} Created ")
diff --git a/meson.build b/meson.build
index 8ceab579b765..1085f6c48175 100644
--- a/meson.build
+++ b/meson.build
@@ -8,6 +8,9 @@ project('SU2', 'c', 'cpp',
'b_ndebug=true',
'c_std=c99',
'cpp_std=c++17'])
+if get_option('enable-cantera')
+ add_global_arguments('-lcantera', language: 'cpp')
+endif
fsmod = import('fs')
if not fsmod.exists('su2preconfig.timestamp')
@@ -324,6 +327,17 @@ if get_option('enable-coolprop')
endif
endif
+if get_option('enable-cantera')
+ py = find_program('python3','python')
+ cantera_meson_path = join_paths(meson.current_source_dir(), 'cantera_meson_build.py')
+ p = run_command(py, cantera_meson_path, check: true)
+ if p.returncode() != 0
+ error(p.stdout())
+ endif
+ su2_cpp_args +='-DHAVE_CANTERA'
+ cantera_dep = dependency('cantera')
+endif
+
if get_option('enable-mlpcpp')
su2_cpp_args += '-DHAVE_MLPCPP'
endif
diff --git a/meson_options.txt b/meson_options.txt
index dcecd404204a..812b391cbb6c 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -21,6 +21,7 @@ option('extra-deps', type : 'string', value : '', description: 'comma-separated
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-cantera', type : 'boolean', value : false, description: 'enable Cantera support')
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/init.py b/meson_scripts/init.py
index 74e42b601776..52ed50afa7ec 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=True,
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 = "bafdea1f39ee873a6bb9833e3a21fe41f90b85e8"
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 516fd43f903c..343b3ded62d7 100755
--- a/preconfigure.py
+++ b/preconfigure.py
@@ -89,6 +89,7 @@ def run(
own_opdi=True,
own_mpp=True,
own_cool=True,
+ own_cantera=True,
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(
+ "--no-cantera",
+ help="do not download own copy of Cantera",
+ action="store_false",
+ )
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.no_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