diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index e075375001b..3fe4c98bbe9 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -5764,6 +5764,14 @@ void CConfig::SetPostprocessing(SU2_COMPONENT val_software, unsigned short val_i Kind_Solver != MAIN_SOLVER::MULTIPHYSICS) SU2_MPI::Error("Species transport currently only available for compressible and incompressible flow.", CURRENT_FUNCTION); + /*--- The dual-time density history is recomputed via the fluid model, which needs the species + solution; the species solver only exists on the finest grid. ---*/ + if ((Kind_Regime == ENUM_REGIME::INCOMPRESSIBLE) && (Kind_DensityModel != INC_DENSITYMODEL::CONSTANT) && + (TimeMarching == TIME_MARCHING::DT_STEPPING_1ST || TimeMarching == TIME_MARCHING::DT_STEPPING_2ND) && + (nMGLevels > 0)) + SU2_MPI::Error("Dual-time stepping with species-dependent variable density does not support MGLEVEL > 0.", + CURRENT_FUNCTION); + /*--- Species specific OF currently can only handle one entry in Marker_Analyze. ---*/ for (unsigned short iObj = 0; iObj < nObj; iObj++) { if ((Kind_ObjFunc[iObj] == SURFACE_SPECIES_0 || diff --git a/SU2_CFD/include/solvers/CIncEulerSolver.hpp b/SU2_CFD/include/solvers/CIncEulerSolver.hpp index 9c18691cf56..9c94d4195e9 100644 --- a/SU2_CFD/include/solvers/CIncEulerSolver.hpp +++ b/SU2_CFD/include/solvers/CIncEulerSolver.hpp @@ -86,6 +86,18 @@ class CIncEulerSolver : public CFVMFlowSolverBase prim_idx; /*!< \brief Indices of the primitive flow variables. */ @@ -440,7 +441,7 @@ class CScalarSolver : public CSolver { * \param[in] geometry - Geometrical definition of the problem. * \param[in] config - Definition of the particular problem. */ - CScalarSolver(CGeometry* geometry, CConfig* config, bool conservative); + CScalarSolver(CGeometry* geometry, CConfig* config, bool conservative, bool bounded_scalar); /*! * \brief Compute the spatial integration using a upwind scheme. diff --git a/SU2_CFD/include/solvers/CScalarSolver.inl b/SU2_CFD/include/solvers/CScalarSolver.inl index d6e05191b22..2a20484449b 100644 --- a/SU2_CFD/include/solvers/CScalarSolver.inl +++ b/SU2_CFD/include/solvers/CScalarSolver.inl @@ -30,8 +30,8 @@ #include "../../include/variables/CFlowVariable.hpp" template -CScalarSolver::CScalarSolver(CGeometry* geometry, CConfig* config, bool conservative) - : CSolver(), Conservative(conservative), +CScalarSolver::CScalarSolver(CGeometry* geometry, CConfig* config, bool conservative, bool bounded_scalar) + : CSolver(), Conservative(conservative), BoundedScalar(bounded_scalar), prim_idx(config->GetKind_Regime() == ENUM_REGIME::INCOMPRESSIBLE, config->GetNEMOProblem(), geometry->GetnDim(), config->GetnSpecies()) { SU2_ZONE_SCOPED @@ -639,11 +639,11 @@ void CScalarSolver::SetResidual_DualTime(CGeometry* geometry, CSol const bool implicit = (config->GetKind_TimeIntScheme() == EULER_IMPLICIT); const bool first_order = (config->GetTime_Marching() == TIME_MARCHING::DT_STEPPING_1ST); const bool second_order = (config->GetTime_Marching() == TIME_MARCHING::DT_STEPPING_2ND); - const bool incompressible = (config->GetKind_Regime() == ENUM_REGIME::INCOMPRESSIBLE); - /*--- Flow solution, needed to get density. ---*/ + const bool bounded_scalar = BoundedScalar; - CVariable* flowNodes = solver_container[FLOW_SOL]->GetNodes(); + /*--- Flow solution, needed to get density. ---*/ + auto* flowNodes = su2staticcast_p(solver_container[FLOW_SOL]->GetNodes()); /*--- Store the physical time step ---*/ @@ -671,19 +671,9 @@ void CScalarSolver::SetResidual_DualTime(CGeometry* geometry, CSol SU2_OMP_FOR_STAT(omp_chunk_size) for (iPoint = 0; iPoint < nPointDomain; iPoint++) { if (Conservative) { - if (incompressible) { - /*--- This is temporary and only valid for constant-density problems: - density could also be temperature dependent, but as it is not a part - of the solution vector it's neither stored for previous time steps - nor updated with the solution at the end of each iteration. */ - Density_nM1 = flowNodes->GetDensity(iPoint); - Density_n = flowNodes->GetDensity(iPoint); - Density_nP1 = flowNodes->GetDensity(iPoint); - } else { - Density_nM1 = flowNodes->GetSolution_time_n1(iPoint)[0]; - Density_n = flowNodes->GetSolution_time_n(iPoint, 0); - Density_nP1 = flowNodes->GetSolution(iPoint, 0); - } + Density_nM1 = flowNodes->GetDensity_time_n1(iPoint); + Density_n = flowNodes->GetDensity_time_n(iPoint); + Density_nP1 = flowNodes->GetDensity(iPoint); } /*--- Retrieve the solution at time levels n-1, n, and n+1. Note that @@ -703,13 +693,20 @@ void CScalarSolver::SetResidual_DualTime(CGeometry* geometry, CSol time discretization scheme (1st- or 2nd-order).---*/ for (iVar = 0; iVar < nVar; iVar++) { + su2double unsteady_term = 0.0; if (first_order) - LinSysRes(iPoint, iVar) += - (Density_nP1 * U_time_nP1[iVar] - Density_n * U_time_n[iVar]) * Volume_nP1 / TimeStep; + unsteady_term = (Density_nP1 * U_time_nP1[iVar] - Density_n * U_time_n[iVar]) * Volume_nP1 / TimeStep; if (second_order) - LinSysRes(iPoint, iVar) += (3.0 * Density_nP1 * U_time_nP1[iVar] - 4.0 * Density_n * U_time_n[iVar] + + unsteady_term = (3.0 * Density_nP1 * U_time_nP1[iVar] - 4.0 * Density_n * U_time_n[iVar] + 1.0 * Density_nM1 * U_time_nM1[iVar]) * Volume_nP1 / (2.0 * TimeStep); + + if (bounded_scalar) { + if (first_order) unsteady_term -= U_time_nP1[iVar] * (Density_nP1 - Density_n) * Volume_nP1 / TimeStep; + if (second_order) unsteady_term -= U_time_nP1[iVar] * (3.0 * Density_nP1 - 4.0 * Density_n + 1.0 * Density_nM1) * Volume_nP1 / (2.0 * TimeStep); + } + + LinSysRes(iPoint, iVar) += unsteady_term; } /*--- Compute the Jacobian contribution due to the dual time source term. ---*/ @@ -737,10 +734,7 @@ void CScalarSolver::SetResidual_DualTime(CGeometry* geometry, CSol U_time_n = nodes->GetSolution_time_n(iPoint); if (Conservative) { - if (incompressible) - Density_n = flowNodes->GetDensity(iPoint); // Temporary fix - else - Density_n = flowNodes->GetSolution_time_n(iPoint, 0); + Density_n = flowNodes->GetDensity_time_n(iPoint); } for (iNeigh = 0; iNeigh < geometry->nodes->GetnPoint(iPoint); iNeigh++) { @@ -793,10 +787,7 @@ void CScalarSolver::SetResidual_DualTime(CGeometry* geometry, CSol /*--- Multiply by density at node i for the SST model ---*/ if (Conservative) { - if (incompressible) - Density_n = flowNodes->GetDensity(iPoint); // Temporary fix - else - Density_n = flowNodes->GetSolution_time_n(iPoint, 0); + Density_n = flowNodes->GetDensity_time_n(iPoint); } for (iVar = 0; iVar < nVar; iVar++) LinSysRes(iPoint, iVar) += Density_n * U_time_n[iVar] * Residual_GCL; @@ -832,37 +823,42 @@ void CScalarSolver::SetResidual_DualTime(CGeometry* geometry, CSol due to the time discretization has a new form.---*/ if (Conservative) { - /*--- If this is the SST model, we need to multiply by the density - in order to get the conservative variables ---*/ - if (incompressible) { - /*--- This is temporary and only valid for constant-density problems: - density could also be temperature dependent, but as it is not a part - of the solution vector it's neither stored for previous time steps - nor updated with the solution at the end of each iteration. */ - Density_nM1 = flowNodes->GetDensity(iPoint); - Density_n = flowNodes->GetDensity(iPoint); - Density_nP1 = flowNodes->GetDensity(iPoint); - } else { - Density_nM1 = flowNodes->GetSolution_time_n1(iPoint)[0]; - Density_n = flowNodes->GetSolution_time_n(iPoint, 0); - Density_nP1 = flowNodes->GetSolution(iPoint, 0); - } + /*--- Get density at different time levels via virtual methods ---*/ + Density_nM1 = flowNodes->GetDensity_time_n1(iPoint); + Density_n = flowNodes->GetDensity_time_n(iPoint); + Density_nP1 = flowNodes->GetDensity(iPoint); } for (iVar = 0; iVar < nVar; iVar++) { + su2double unsteady_term = 0.0; if (first_order) - LinSysRes(iPoint, iVar) += - (Density_nP1 * U_time_nP1[iVar] - Density_n * U_time_n[iVar]) * (Volume_nP1 / TimeStep); + unsteady_term = (Density_nP1 * U_time_nP1[iVar] - Density_n * U_time_n[iVar]) * (Volume_nP1 / TimeStep); if (second_order) - LinSysRes(iPoint, iVar) += - (Density_nP1 * U_time_nP1[iVar] - Density_n * U_time_n[iVar]) * (3.0 * Volume_nP1 / (2.0 * TimeStep)) + + unsteady_term = (Density_nP1 * U_time_nP1[iVar] - Density_n * U_time_n[iVar]) * (3.0 * Volume_nP1 / (2.0 * TimeStep)) + (Density_nM1 * U_time_nM1[iVar] - Density_n * U_time_n[iVar]) * (Volume_nM1 / (2.0 * TimeStep)); + + if (bounded_scalar) { + if (first_order) unsteady_term -= U_time_nP1[iVar] * (Density_nP1 - Density_n) * (Volume_nP1 / TimeStep); + if (second_order) unsteady_term -= U_time_nP1[iVar] * ((Density_nP1 - Density_n) * (3.0 * Volume_nP1 / (2.0 * TimeStep)) + + (Density_nM1 - Density_n) * (Volume_nM1 / (2.0 * TimeStep))); + } + + LinSysRes(iPoint, iVar) += unsteady_term; } /*--- Compute the Jacobian contribution due to the dual time source term. ---*/ if (implicit) { - if (first_order) Jacobian.AddVal2Diag(iPoint, Volume_nP1 / TimeStep); - if (second_order) Jacobian.AddVal2Diag(iPoint, (Volume_nP1 * 3.0) / (2.0 * TimeStep)); + su2double diag_factor = 1.0; + if (Conservative) { + if (bounded_scalar) { + if (first_order) diag_factor = Density_n; + if (second_order) diag_factor = (4.0 * Density_n - Density_nM1) / 3.0; + } else { + diag_factor = Density_nP1; + } + } + if (first_order) Jacobian.AddVal2Diag(iPoint, diag_factor * Volume_nP1 / TimeStep); + if (second_order) Jacobian.AddVal2Diag(iPoint, diag_factor * 3.0 * Volume_nP1 / (2.0 * TimeStep)); } } END_SU2_OMP_FOR diff --git a/SU2_CFD/include/solvers/CTurbSolver.hpp b/SU2_CFD/include/solvers/CTurbSolver.hpp index 47636253a9d..c5fce372e45 100644 --- a/SU2_CFD/include/solvers/CTurbSolver.hpp +++ b/SU2_CFD/include/solvers/CTurbSolver.hpp @@ -133,7 +133,7 @@ class CTurbSolver : public CScalarSolver { * \returns The number of extra variables. */ unsigned long RegisterSolutionExtra(bool input, const CConfig* config) final; - + /*! * \brief Compute a suitable under-relaxation parameter to limit the change in the solution variables over * a nonlinear iteration for stability. diff --git a/SU2_CFD/include/variables/CEulerVariable.hpp b/SU2_CFD/include/variables/CEulerVariable.hpp index 12308f29c6c..abd63a3afd2 100644 --- a/SU2_CFD/include/variables/CEulerVariable.hpp +++ b/SU2_CFD/include/variables/CEulerVariable.hpp @@ -97,6 +97,20 @@ class CEulerVariable : public CFlowVariable { CEulerVariable(su2double density, const su2double *velocity, su2double energy, unsigned long npoint, unsigned long ndim, unsigned long nvar, const CConfig *config); + /*! + * \brief Get the density at time level n for dual-time stepping. + * \param[in] iPoint - Point index. + * \return Density at time level n. + */ + inline su2double GetDensity_time_n(unsigned long iPoint) const final { return GetSolution_time_n(iPoint, 0); } + + /*! + * \brief Get the density at time level n-1 for dual-time stepping. + * \param[in] iPoint - Point index. + * \return Density at time level n-1. + */ + inline su2double GetDensity_time_n1(unsigned long iPoint) const final { return GetSolution_time_n1(iPoint, 0); } + /*! * \brief A virtual member. */ diff --git a/SU2_CFD/include/variables/CFlowVariable.hpp b/SU2_CFD/include/variables/CFlowVariable.hpp index 61e793cb42c..92115519b6b 100644 --- a/SU2_CFD/include/variables/CFlowVariable.hpp +++ b/SU2_CFD/include/variables/CFlowVariable.hpp @@ -270,4 +270,18 @@ class CFlowVariable : public CVariable { * \return Vector of magnitudes. */ inline su2activevector& GetStrainMag() { return StrainMag; } + + /*! + * \brief Get the density at time level n for dual-time stepping. + * \param[in] iPoint - Point index. + * \return Density at time level n. + */ + virtual su2double GetDensity_time_n(unsigned long iPoint) const = 0; + + /*! + * \brief Get the density at time level n-1 for dual-time stepping. + * \param[in] iPoint - Point index. + * \return Density at time level n-1. + */ + virtual su2double GetDensity_time_n1(unsigned long iPoint) const = 0; }; diff --git a/SU2_CFD/include/variables/CIncEulerVariable.hpp b/SU2_CFD/include/variables/CIncEulerVariable.hpp index 46be6d3dc2b..d76990b71eb 100644 --- a/SU2_CFD/include/variables/CIncEulerVariable.hpp +++ b/SU2_CFD/include/variables/CIncEulerVariable.hpp @@ -71,6 +71,8 @@ class CIncEulerVariable : public CFlowVariable { VectorType Streamwise_Periodic_RecoveredPressure, /*!< \brief Recovered/Physical pressure [Pa] for streamwise periodic flow. */ Streamwise_Periodic_RecoveredTemperature; /*!< \brief Recovered/Physical temperature [K] for streamwise periodic flow. */ + VectorType Density_time_n, /*!< \brief Density at time n for dual-time stepping. */ + Density_time_n1; /*!< \brief Density at time n-1 for dual-time stepping. */ su2double TemperatureLimits[2]; /*!< \brief Temperature limits [K]. */ public: /*! @@ -291,4 +293,36 @@ class CIncEulerVariable : public CFlowVariable { for (unsigned long iDim = 0; iDim < nDim; iDim++) Solution(iPoint, iDim+1) = val_vector[iDim]; } + /*! + * \brief Get the density at time level n for dual-time stepping. + * \param[in] iPoint - Point index. + * \return Density at time level n. + */ + inline su2double GetDensity_time_n(unsigned long iPoint) const final { + return Density_time_n.size() > 0 ? Density_time_n(iPoint) : GetDensity(iPoint); + } + + /*! + * \brief Get the density at time level n-1 for dual-time stepping. + * \param[in] iPoint - Point index. + * \return Density at time level n-1. + */ + inline su2double GetDensity_time_n1(unsigned long iPoint) const final { + return Density_time_n1.size() > 0 ? Density_time_n1(iPoint) : GetDensity(iPoint); + } + + /*! + * \brief Set the density at time level n for dual-time stepping. + * \param[in] iPoint - Point index. + * \param[in] val_density - Density value. + */ + inline void SetDensity_time_n(unsigned long iPoint, su2double val_density) { Density_time_n(iPoint) = val_density; } + + /*! + * \brief Set the density at time level n-1 for dual-time stepping. + * \param[in] iPoint - Point index. + * \param[in] val_density - Density value. + */ + inline void SetDensity_time_n1(unsigned long iPoint, su2double val_density) { Density_time_n1(iPoint) = val_density; } + }; diff --git a/SU2_CFD/include/variables/CNEMOEulerVariable.hpp b/SU2_CFD/include/variables/CNEMOEulerVariable.hpp index 408dcc7c144..e0d4199144c 100644 --- a/SU2_CFD/include/variables/CNEMOEulerVariable.hpp +++ b/SU2_CFD/include/variables/CNEMOEulerVariable.hpp @@ -122,6 +122,20 @@ class CNEMOEulerVariable : public CFlowVariable { unsigned long nvar, unsigned long nvalprim, unsigned long nvarprimgrad, const CConfig *config, CNEMOGas *fluidmodel); + /*! + * \brief Get the density at time level n for dual-time stepping. + * \param[in] iPoint - Point index. + * \return Density at time level n. + */ + inline su2double GetDensity_time_n(unsigned long iPoint) const final { return GetSolution_time_n(iPoint, 0); } + + /*! + * \brief Get the density at time level n-1 for dual-time stepping. + * \param[in] iPoint - Point index. + * \return Density at time level n-1. + */ + inline su2double GetDensity_time_n1(unsigned long iPoint) const final { return GetSolution_time_n1(iPoint, 0); } + /*---------------------------------------*/ /*--- U,V,S Routines ---*/ /*---------------------------------------*/ diff --git a/SU2_CFD/src/solvers/CDiscAdjSolver.cpp b/SU2_CFD/src/solvers/CDiscAdjSolver.cpp index 3194e28b6d3..0f396a2ebd0 100644 --- a/SU2_CFD/src/solvers/CDiscAdjSolver.cpp +++ b/SU2_CFD/src/solvers/CDiscAdjSolver.cpp @@ -168,11 +168,13 @@ void CDiscAdjSolver::RegisterSolution(CGeometry *geometry, CConfig *config) { /*--- Register quantities that are no solver variables but further inputs/outputs of the (outer) iteration. ---*/ direct_solver->RegisterSolutionExtra(true, config); - if (time_n_needed) + if (time_n_needed) { direct_solver->GetNodes()->RegisterSolution_time_n(); + } - if (time_n1_needed) + if (time_n1_needed) { direct_solver->GetNodes()->RegisterSolution_time_n1(); + } } void CDiscAdjSolver::RegisterVariables(CGeometry *geometry, CConfig *config, bool reset) { diff --git a/SU2_CFD/src/solvers/CHeatSolver.cpp b/SU2_CFD/src/solvers/CHeatSolver.cpp index 2a9e4790760..d944255a694 100644 --- a/SU2_CFD/src/solvers/CHeatSolver.cpp +++ b/SU2_CFD/src/solvers/CHeatSolver.cpp @@ -34,7 +34,7 @@ template class CScalarSolver; CHeatSolver::CHeatSolver(CGeometry *geometry, CConfig *config, unsigned short iMesh) - : CScalarSolver(geometry, config, false), + : CScalarSolver(geometry, config, false, false), flow(config->GetFluidProblem()) { SU2_ZONE_SCOPED diff --git a/SU2_CFD/src/solvers/CIncEulerSolver.cpp b/SU2_CFD/src/solvers/CIncEulerSolver.cpp index 4951551851f..41580c3062a 100644 --- a/SU2_CFD/src/solvers/CIncEulerSolver.cpp +++ b/SU2_CFD/src/solvers/CIncEulerSolver.cpp @@ -966,6 +966,8 @@ void CIncEulerSolver::CommonPreprocessing(CGeometry *geometry, CSolver **solver_ const bool center = (config->GetKind_ConvNumScheme_Flow() == SPACE_CENTERED); const bool center_jst = (config->GetKind_Centered_Flow() == CENTERED::JST || config->GetKind_Centered_Flow() == CENTERED::LD2) && (iMesh == MESH_0); const bool outlet = (config->GetnMarker_Outlet() != 0); + const bool dual_time = (config->GetTime_Marching() == TIME_MARCHING::DT_STEPPING_1ST) || + (config->GetTime_Marching() == TIME_MARCHING::DT_STEPPING_2ND); /*--- Set the primitive variables ---*/ @@ -974,6 +976,11 @@ void CIncEulerSolver::CommonPreprocessing(CGeometry *geometry, CSolver **solver_ SU2_OMP_ATOMIC ErrorCounter += SetPrimitive_Variables(solver_container, config); + /*--- InnerIter is not reset while recording the discrete adjoint tape. ---*/ + if (dual_time && (config->GetInnerIter() == 0 || AD::TapeActive())) { + RecomputeDensity_time_n(solver_container, config); + } + if ((iMesh == MESH_0) && (config->GetComm_Level() == COMM_FULL)) { BEGIN_SU2_OMP_SAFE_GLOBAL_ACCESS { @@ -1077,6 +1084,42 @@ unsigned long CIncEulerSolver::SetPrimitive_Variables(CSolver **solver_container return nonPhysicalPoints; } +void CIncEulerSolver::RecomputeDensity_time_n(CSolver **solver_container, const CConfig *config) { + SU2_ZONE_SCOPED + + /*--- Only variable-density (non-constant) cases allocate the density history. ---*/ + if (config->GetKind_DensityModel() == INC_DENSITYMODEL::CONSTANT) return; + + const bool second_order = (config->GetTime_Marching() == TIME_MARCHING::DT_STEPPING_2ND); + + CVariable* speciesNodes = (solver_container[SPECIES_SOL] != nullptr) + ? solver_container[SPECIES_SOL]->GetNodes() : nullptr; + + /*--- The species solver only exists on the fine grid; MG with scalar-dependent density is rejected in CConfig. ---*/ + const bool needs_scalars = (config->GetKind_Species_Model() != SPECIES_MODEL::NONE); + if (needs_scalars && speciesNodes == nullptr) return; + + SU2_OMP_FOR_STAT(omp_chunk_size) + for (unsigned long iPoint = 0; iPoint < nPoint; iPoint++) { + + /*--- Per-thread fluid model, mirroring the recipe in SetPrimitive_Variables. ---*/ + CFluidModel* fluidModel = GetFluidModel(); + + const su2double* scalar_n = speciesNodes ? speciesNodes->GetSolution_time_n(iPoint) : nullptr; + const su2double Enthalpy_n = nodes->GetSolution_time_n(iPoint, nDim + 1); + fluidModel->SetTDState_h(Enthalpy_n, scalar_n); + nodes->SetDensity_time_n(iPoint, fluidModel->GetDensity()); + + if (second_order) { + const su2double* scalar_n1 = speciesNodes ? speciesNodes->GetSolution_time_n1(iPoint) : nullptr; + const su2double Enthalpy_n1 = nodes->GetSolution_time_n1(iPoint, nDim + 1); + fluidModel->SetTDState_h(Enthalpy_n1, scalar_n1); + nodes->SetDensity_time_n1(iPoint, fluidModel->GetDensity()); + } + } + END_SU2_OMP_FOR +} + void CIncEulerSolver::SetTime_Step(CGeometry *geometry, CSolver **solver_container, CConfig *config, unsigned short iMesh, unsigned long Iteration) { SU2_ZONE_SCOPED @@ -2867,14 +2910,17 @@ void CIncEulerSolver::SetResidual_DualTime(CGeometry *geometry, CSolver **solver V_time_n = nodes->GetSolution_time_n(iPoint); V_time_nP1 = nodes->GetSolution(iPoint); - /*--- Access the density at this node (constant for now). ---*/ + /*--- Access the density at different time levels for non-constant density. ---*/ - Density = nodes->GetDensity(iPoint); + su2double Density_nM1 = nodes->GetDensity_time_n1(iPoint); + su2double Density_n = nodes->GetDensity_time_n(iPoint); + Density = nodes->GetDensity(iPoint); // Density at n+1 - /*--- Compute the conservative variable vector for all time levels. ---*/ + /*--- Compute the conservative variable vector for all time levels. + Use the density from the corresponding time level. ---*/ - V2U(Density, V_time_nM1, U_time_nM1); - V2U(Density, V_time_n, U_time_n); + V2U(Density_nM1, V_time_nM1, U_time_nM1); + V2U(Density_n, V_time_n, U_time_n); V2U(Density, V_time_nP1, U_time_nP1); /*--- CV volume at time n+1. As we are on a static mesh, the volume @@ -2922,8 +2968,8 @@ void CIncEulerSolver::SetResidual_DualTime(CGeometry *geometry, CSolver **solver /*--- Compute the conservative variables. ---*/ V_time_n = nodes->GetSolution_time_n(iPoint); - Density = nodes->GetDensity(iPoint); - V2U(Density, V_time_n, U_time_n); + su2double Density_n = nodes->GetDensity_time_n(iPoint); + V2U(Density_n, V_time_n, U_time_n); GridVel_i = geometry->nodes->GetGridVel(iPoint); @@ -2977,8 +3023,8 @@ void CIncEulerSolver::SetResidual_DualTime(CGeometry *geometry, CSolver **solver /*--- Compute the GCL component of the source term for node i ---*/ V_time_n = nodes->GetSolution_time_n(iPoint); - Density = nodes->GetDensity(iPoint); - V2U(Density, V_time_n, U_time_n); + su2double Density_n = nodes->GetDensity_time_n(iPoint); + V2U(Density_n, V_time_n, U_time_n); for (iVar = 0; iVar < nVar-!energy; iVar++) LinSysRes(iPoint,iVar) += U_time_n[iVar]*Residual_GCL; @@ -3004,14 +3050,17 @@ void CIncEulerSolver::SetResidual_DualTime(CGeometry *geometry, CSolver **solver V_time_n = nodes->GetSolution_time_n(iPoint); V_time_nP1 = nodes->GetSolution(iPoint); - /*--- Access the density at this node (constant for now). ---*/ + /*--- Access the density at different time levels for non-constant density. ---*/ - Density = nodes->GetDensity(iPoint); + su2double Density_nM1 = nodes->GetDensity_time_n1(iPoint); + su2double Density_n = nodes->GetDensity_time_n(iPoint); + Density = nodes->GetDensity(iPoint); // Density at n+1 - /*--- Compute the conservative variable vector for all time levels. ---*/ + /*--- Compute the conservative variable vector for all time levels. + Use the density from the corresponding time level. ---*/ - V2U(Density, V_time_nM1, U_time_nM1); - V2U(Density, V_time_n, U_time_n); + V2U(Density_nM1, V_time_nM1, U_time_nM1); + V2U(Density_n, V_time_n, U_time_n); V2U(Density, V_time_nP1, U_time_nP1); /*--- CV volume at time n-1 and n+1. In the case of dynamically deforming diff --git a/SU2_CFD/src/solvers/CSpeciesFlameletSolver.cpp b/SU2_CFD/src/solvers/CSpeciesFlameletSolver.cpp index 82bf04a0125..fee5a0702e2 100644 --- a/SU2_CFD/src/solvers/CSpeciesFlameletSolver.cpp +++ b/SU2_CFD/src/solvers/CSpeciesFlameletSolver.cpp @@ -83,7 +83,12 @@ void CSpeciesFlameletSolver::Preprocessing(CGeometry* geometry, CSolver** solver 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(); + unsigned long iter; + if (config->GetTime_Domain()) { + iter = config->GetTimeIter(); // Use time step counter for unsteady problems + } else { + iter = config->GetMultizone_Problem() ? config->GetOuterIter() : config->GetInnerIter(); + } ignition = ((iter >= spark_iter_start) && (iter <= (spark_iter_start + spark_duration))); } diff --git a/SU2_CFD/src/solvers/CSpeciesSolver.cpp b/SU2_CFD/src/solvers/CSpeciesSolver.cpp index 28d09dac050..38ad833f65e 100644 --- a/SU2_CFD/src/solvers/CSpeciesSolver.cpp +++ b/SU2_CFD/src/solvers/CSpeciesSolver.cpp @@ -36,7 +36,7 @@ template class CScalarSolver; CSpeciesSolver::CSpeciesSolver(CGeometry* geometry, CConfig* config, unsigned short iMesh) - : CScalarSolver(geometry, config, true) { + : CScalarSolver(geometry, config, true, config->GetBounded_Species()) { SU2_ZONE_SCOPED /*--- Dimension of the problem. ---*/ diff --git a/SU2_CFD/src/solvers/CTurbSolver.cpp b/SU2_CFD/src/solvers/CTurbSolver.cpp index 51562382311..b00ff47dd9f 100644 --- a/SU2_CFD/src/solvers/CTurbSolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSolver.cpp @@ -34,7 +34,7 @@ template class CScalarSolver; CTurbSolver::CTurbSolver(CGeometry* geometry, CConfig *config, bool conservative) - : CScalarSolver(geometry, config, conservative) { + : CScalarSolver(geometry, config, conservative, config->GetBounded_Turb()) { SU2_ZONE_SCOPED /*--- Store if an implicit scheme is used, for use during periodic boundary conditions. ---*/ SetImplicitPeriodic(config->GetKind_TimeIntScheme_Turb() == EULER_IMPLICIT); diff --git a/SU2_CFD/src/variables/CIncEulerVariable.cpp b/SU2_CFD/src/variables/CIncEulerVariable.cpp index 355a1587488..5274df76644 100644 --- a/SU2_CFD/src/variables/CIncEulerVariable.cpp +++ b/SU2_CFD/src/variables/CIncEulerVariable.cpp @@ -27,6 +27,7 @@ #include "../../include/variables/CIncEulerVariable.hpp" #include "../../include/fluid/CFluidModel.hpp" +#include "../../../Common/include/parallelization/omp_structure.hpp" CIncEulerVariable::CIncEulerVariable(su2double pressure, const su2double *velocity, su2double enthalpy, unsigned long npoint, unsigned long ndim, unsigned long nvar, const CConfig *config) @@ -58,6 +59,11 @@ CIncEulerVariable::CIncEulerVariable(su2double pressure, const su2double *veloci if (dual_time) { Solution_time_n = Solution; Solution_time_n1 = Solution; + + if (config->GetKind_DensityModel() != INC_DENSITYMODEL::CONSTANT) { + Density_time_n.resize(nPoint) = su2double(0.0); + Density_time_n1.resize(nPoint) = su2double(0.0); + } } if (config->GetKind_Streamwise_Periodic() != ENUM_STREAMWISE_PERIODIC::NONE) { @@ -127,3 +133,4 @@ bool CIncEulerVariable::SetPrimVar(unsigned long iPoint, CFluidModel *FluidModel return physical; } + diff --git a/TestCases/flamelet/09_laminar_premixed_ch4_flame_unsteady/lam_prem_ch4_unsteady.cfg b/TestCases/flamelet/09_laminar_premixed_ch4_flame_unsteady/lam_prem_ch4_unsteady.cfg new file mode 100644 index 00000000000..06258634178 --- /dev/null +++ b/TestCases/flamelet/09_laminar_premixed_ch4_flame_unsteady/lam_prem_ch4_unsteady.cfg @@ -0,0 +1,154 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SU2 configuration file % +% Case description: Laminar premixed transient flame propagating in a channel % +% Author: Nijso Beishuizen % +% Institution: TU Eindhoven % +% Date: 30/04/2026 % +% File Version 8.5.0 "Harrier" % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% ------------- DIRECT, ADJOINT, AND LINEARIZED PROBLEM DEFINITION ------------% +% +SOLVER= INC_NAVIER_STOKES +KIND_TURB_MODEL= NONE +MATH_PROBLEM= DIRECT +RESTART_SOL= YES +% +% ---------------- INCOMPRESSIBLE FLOW CONDITION DEFINITION -------------------% +% +%INC_DENSITY_MODEL= FLAMELET +INC_DENSITY_MODEL= VARIABLE +INC_DENSITY_INIT= 1.00 +INC_VELOCITY_INIT= (0.5, 0.0, 0.0 ) +INC_TEMPERATURE_INIT= 300.0 +INC_NONDIM= DIMENSIONAL +% +% -------------------- FLUID MODEL --------------------------------------- % +% +FLUID_MODEL= FLUID_FLAMELET +PREFERENTIAL_DIFFUSION= NO +FILENAMES_INTERPOLATOR= (fgm_ch4_phi075.drg) +CONTROLLING_VARIABLE_NAMES= (ProgressVariable, EnthalpyTot) +CONTROLLING_VARIABLE_SOURCE_NAMES= (ProdRateTot_PV, NULL) +% +% -------------------- SCALAR TRANSPORT ---------------------------------------% +% +KIND_SCALAR_MODEL= FLAMELET +DIFFUSIVITY_MODEL= FLAMELET +VISCOSITY_MODEL= FLAMELET +CONDUCTIVITY_MODEL= FLAMELET +%FLAME_INIT_METHOD= FLAME_FRONT +FLAME_INIT_METHOD= NONE + +FLAME_INIT= (0.009, 0.00, 0.00, 1.0, 0.0, 0.0, 5.0e-4, 0.1) +% # progvar, enthalpy +SPECIES_INIT= (0.0, -193150) +CONV_NUM_METHOD_SPECIES= BOUNDED_SCALAR +MUSCL_SPECIES= YES +SLOPE_LIMITER_SPECIES= NONE +TIME_DISCRE_SPECIES= EULER_IMPLICIT +% SCALAR CLIPPING +SPECIES_CLIPPING= YES +SPECIES_CLIPPING_MIN= 0.000 -1e6 +SPECIES_CLIPPING_MAX= 0.300 +1e5 +% +MARKER_INLET_SPECIES= (inlet, 0, -193154.0) +CFL_REDUCTION_SPECIES= 1.0 +MARKER_SPECIES_STRONG_BC= (inlet, outlet) +LOOKUP_NAMES= (MolarWeightMix, Conductivity, Cp) +% +% ---------------------- REFERENCE VALUE DEFINITION ---------------------------% +% +REF_ORIGIN_MOMENT_X= 0.25 +REF_ORIGIN_MOMENT_Y= 0.00 +REF_ORIGIN_MOMENT_Z= 0.00 +REF_LENGTH= 1.0 +REF_AREA= 1.0 +% +% -------------------- BOUNDARY CONDITION DEFINITION --------------------------% +% +MARKER_SYM= (symmetry_bottom) +MARKER_EULER= wall +INC_INLET_TYPE= VELOCITY_INLET + +% switch from 'steady' flame to 'propagating with 0.1 m/s' +MARKER_INLET= (inlet, 300.0, 0.105, 1.0, 0.0, 0.0) +%MARKER_INLET= (inlet, 300.0, 0.127, 1.0, 0.0, 0.0) + +INC_OUTLET_TYPE= PRESSURE_OUTLET +INC_INLET_DAMPING= 0.1 +INC_OUTLET_DAMPING= 0.1 +MARKER_OUTLET= (outlet, 0.0) +MARKER_PLOTTING= ( inlet ) +MARKER_MONITORING= ( inlet ) +MARKER_ANALYZE= ( inlet,outlet ) +MARKER_ANALYZE_AVERAGE= AREA +% +% ------------- COMMON PARAMETERS DEFINING THE NUMERICAL METHOD ---------------% +% +NUM_METHOD_GRAD= WEIGHTED_LEAST_SQUARES +%CFL_NUMBER= 10000 +CFL_NUMBER= 1000 +CFL_ADAPT= NO +OUTPUT_WRT_FREQ= 1,1 +% +% ------------------------- UNSTEADY SIMULATION -------------------------------% +% +TIME_DOMAIN= YES +% +TIME_MARCHING= DUAL_TIME_STEPPING-1ST_ORDER +%TIME_MARCHING= DUAL_TIME_STEPPING-2ND_ORDER +% +RESTART_ITER=1 +TIME_ITER= 25 +TIME_STEP= 5e-4 +% time= 0.1 s +MAX_TIME= 0.1 +% +UNST_CFL_NUMBER= 0.0 +INNER_ITER= 100 +LINEAR_SOLVER= FGMRES +LINEAR_SOLVER_PREC= ILU +% no impact on flame speed +LINEAR_SOLVER_ERROR= 0.1 +LINEAR_SOLVER_ITER= 5 +% +% -------------------------- MULTIGRID PARAMETERS -----------------------------% +% +MGLEVEL= 0 +% +% -------------------- FLOW NUMERICAL METHOD DEFINITION -----------------------% +% +CONV_NUM_METHOD_FLOW= FDS +MUSCL_FLOW= YES +SLOPE_LIMITER_FLOW= NONE +TIME_DISCRE_FLOW= EULER_IMPLICIT +% +% --------------------------- CONVERGENCE PARAMETERS --------------------------% +% +CONV_RESIDUAL_MINVAL= -10.0 +CONV_FIELD= RMS_ProgressVariable, RMS_VELOCITY-X, RMS_MixtureFraction +CONV_STARTITER= 10 +CONV_CAUCHY_ELEMS= 100 +CONV_CAUCHY_EPS= 1E-6 +SCREEN_OUTPUT= TIME_ITER INNER_ITER RMS_VELOCITY-X RMS_PRESSURE RMS_ProgressVariable RMS_EnthalpyTot RMS_MixtureFraction +HISTORY_OUTPUT= RMS_RES AERO_COEFF FLOW_COEFF FLOW_COEFF_SURF +VOLUME_OUTPUT= SOLUTION PRIMITIVE SOURCE RESIDUAL SENSITIVITY LOOKUP TIMESTEP +% +% ------------------------- INPUT/OUTPUT INFORMATION --------------------------% +% +MESH_FORMAT= SU2 +MESH_FILENAME= 1d_chamber.su2 +MESH_OUT_FILENAME= mesh_out +SOLUTION_FILENAME= solution +RESTART_FILENAME= restart +OUTPUT_FILES= (RESTART,PARAVIEW) +%OUTPUT_FILES= (RESTART) +TABULAR_FORMAT= CSV +CONV_FILENAME= history +VOLUME_FILENAME= ch4_flame_cfd +SURFACE_FILENAME= surface_flow +WRT_PERFORMANCE= YES +SCREEN_WRT_FREQ_INNER= 1 +SCREEN_WRT_FREQ_OUTER= 1 diff --git a/TestCases/parallel_regression.py b/TestCases/parallel_regression.py index e6242b093b1..fe7bfe81c60 100755 --- a/TestCases/parallel_regression.py +++ b/TestCases/parallel_regression.py @@ -86,6 +86,15 @@ def main(): flame_init_methods.new_output = True test_list.append(flame_init_methods) + # 2D laminar premixed ch4-air flame, transient flame propagation + cfd_flamelet_ch4_unsteady = TestCase('cfd_flamelet_ch4_unsteady') + cfd_flamelet_ch4_unsteady.cfg_dir = "flamelet/09_laminar_premixed_ch4_flame_unsteady" + cfd_flamelet_ch4_unsteady.cfg_file = "lam_prem_ch4_unsteady.cfg" + cfd_flamelet_ch4_unsteady.test_iter = 5 + cfd_flamelet_ch4_unsteady.test_vals = [-8.856420, -8.095249, -9.153744, -9.321679] + cfd_flamelet_ch4_unsteady.test_vals_aarch64 = [-8.855500, -8.095195, -9.153704, -9.321686] + test_list.append(cfd_flamelet_ch4_unsteady) + ######################### ## NEMO solver ### ######################### @@ -1561,7 +1570,7 @@ def main(): pywrapper_rigidMotion.cfg_dir = "py_wrapper/flatPlate_rigidMotion" pywrapper_rigidMotion.cfg_file = "flatPlate_rigidMotion_Conf.cfg" pywrapper_rigidMotion.test_iter = 5 - pywrapper_rigidMotion.test_vals = [-1.614166, 2.255135, 0.350196, 0.089496] + pywrapper_rigidMotion.test_vals = [-1.607009, 2.260791, 0.350196, 0.089496] pywrapper_rigidMotion.command = TestCase.Command("mpirun -np 2", "python", "launch_flatPlate_rigidMotion.py --parallel -f") pywrapper_rigidMotion.unsteady = True test_list.append(pywrapper_rigidMotion) diff --git a/TestCases/parallel_regression_AD.py b/TestCases/parallel_regression_AD.py index e68c3f27648..ee45cf41a79 100644 --- a/TestCases/parallel_regression_AD.py +++ b/TestCases/parallel_regression_AD.py @@ -308,7 +308,7 @@ def main(): da_sp_pinArray_cht_2d_dp_hf.cfg_dir = "incomp_navierstokes/streamwise_periodic/chtPinArray_2d" da_sp_pinArray_cht_2d_dp_hf.cfg_file = "DA_configMaster.cfg" da_sp_pinArray_cht_2d_dp_hf.test_iter = 100 - da_sp_pinArray_cht_2d_dp_hf.test_vals = [-11.620815, -6.488915, -13.316675] + da_sp_pinArray_cht_2d_dp_hf.test_vals = [-11.620815, -6.488915, -13.316362] da_sp_pinArray_cht_2d_dp_hf.multizone = True test_list.append(da_sp_pinArray_cht_2d_dp_hf) @@ -326,7 +326,7 @@ def main(): da_unsteadyCHT_cylinder.cfg_dir = "coupled_cht/disc_adj_unsteadyCHT_cylinder" da_unsteadyCHT_cylinder.cfg_file = "chtMaster.cfg" da_unsteadyCHT_cylinder.test_iter = 2 - da_unsteadyCHT_cylinder.test_vals = [-8.479629, -9.239920, -9.234868, -15.934511, -13.662005, 0.000000, 10.627000, 0.295190] + da_unsteadyCHT_cylinder.test_vals = [-8.479629, -9.239920, -9.234868, -15.934511, -13.662021, 0.000000, 10.627000, 0.295190] da_unsteadyCHT_cylinder.test_vals_aarch64 = [-8.479629, -9.239920, -9.234868, -15.934511, -13.662012, 0.000000, 89.932000, 0.295190] da_unsteadyCHT_cylinder.unsteady = True da_unsteadyCHT_cylinder.multizone = True diff --git a/TestCases/serial_regression.py b/TestCases/serial_regression.py index b5eeedb38ae..0d8e58e79d9 100755 --- a/TestCases/serial_regression.py +++ b/TestCases/serial_regression.py @@ -1633,7 +1633,7 @@ def main(): pywrapper_rigidMotion.cfg_dir = "py_wrapper/flatPlate_rigidMotion" pywrapper_rigidMotion.cfg_file = "flatPlate_rigidMotion_Conf.cfg" pywrapper_rigidMotion.test_iter = 5 - pywrapper_rigidMotion.test_vals = [-1.614166, 2.255135, 0.350208, 0.089496] + pywrapper_rigidMotion.test_vals = [-1.607008, 2.260791, 0.350208, 0.089496] pywrapper_rigidMotion.command = TestCase.Command(exec = "python", param = "launch_flatPlate_rigidMotion.py -f") pywrapper_rigidMotion.timeout = 1600 pywrapper_rigidMotion.tol = 0.00001