From 36b6f7a3ce7da466c2bcfad4d652c39be3cfa5f7 Mon Sep 17 00:00:00 2001 From: Angelo Passariello Date: Mon, 15 Sep 2025 18:26:39 +0200 Subject: [PATCH 01/25] Preliminary implementation: uncorrelated stochastic variables The Reynolds stress tensor definition is modified to include a random contribution, which is expressed as the curl of a normally-distributed stochastic vector potential. --- Common/include/CConfig.hpp | 7 +++ Common/src/CConfig.cpp | 8 +++ SU2_CFD/include/numerics/CNumerics.hpp | 58 +++++++++++++++++++ .../include/numerics/flow/flow_diffusion.hpp | 4 +- SU2_CFD/src/numerics/flow/flow_diffusion.cpp | 39 +++++++++++-- TestCases/ddes/flatplate/ddes_flatplate.cfg | 10 ++-- config_template.cfg | 3 + 7 files changed, 120 insertions(+), 9 deletions(-) diff --git a/Common/include/CConfig.hpp b/Common/include/CConfig.hpp index 4439410efff4..66c58eafdcf5 100644 --- a/Common/include/CConfig.hpp +++ b/Common/include/CConfig.hpp @@ -1077,6 +1077,7 @@ class CConfig { su2double Const_DES; /*!< \brief Detached Eddy Simulation Constant. */ WINDOW_FUNCTION Kind_WindowFct; /*!< \brief Type of window (weight) function for objective functional. */ unsigned short Kind_HybridRANSLES; /*!< \brief Kind of Hybrid RANS/LES. */ + bool StochasticBackscatter; /*!< \brief Option to include Stochastic Backscatter Model. */ unsigned short Kind_RoeLowDiss; /*!< \brief Kind of Roe scheme with low dissipation for unsteady flows. */ unsigned short nSpanWiseSections; /*!< \brief number of span-wise sections */ @@ -9466,6 +9467,12 @@ class CConfig { */ unsigned short GetKind_HybridRANSLES(void) const { return Kind_HybridRANSLES; } + /*! + * \brief Get if the Stochastic Backscatter Model must be activated. + * \return TRUE if the Stochastic Backscatter Model is activated. + */ + bool GetStochastic_Backscatter(void) const { return StochasticBackscatter; } + /*! * \brief Get the Kind of Roe Low Dissipation Scheme for Unsteady flows. * \return Value of Low dissipation approach. diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index 3cadb5be5ef1..a5aa8e2bd08e 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -2911,6 +2911,9 @@ void CConfig::SetConfig_Options() { /* DESCRIPTION: Specify Hybrid RANS/LES model */ addEnumOption("HYBRID_RANSLES", Kind_HybridRANSLES, HybridRANSLES_Map, NO_HYBRIDRANSLES); + /* DESCRIPTION: Specify if the Stochastic Backscatter Model must be activated */ + addBoolOption("STOCHASTIC_BACKSCATTER", StochasticBackscatter, false); + /* DESCRIPTION: Roe with low dissipation for unsteady flows */ addEnumOption("ROE_LOW_DISSIPATION", Kind_RoeLowDiss, RoeLowDiss_Map, NO_ROELOWDISS); @@ -6448,6 +6451,11 @@ void CConfig::SetOutput(SU2_COMPONENT val_software, unsigned short val_izone) { case SA_ZDES: cout << "Delayed Detached Eddy Simulation (DDES) with Vorticity-based SGS" << endl; break; case SA_EDDES: cout << "Delayed Detached Eddy Simulation (DDES) with Shear-layer Adapted SGS" << endl; break; } + cout << "Stochastic Backscatter: "; + if (StochasticBackscatter) + cout << "ON" << endl; + else + cout << "OFF" << endl; break; case MAIN_SOLVER::NEMO_EULER: if (Kind_Regime == ENUM_REGIME::COMPRESSIBLE) cout << "Compressible two-temperature thermochemical non-equilibrium Euler equations." << endl; diff --git a/SU2_CFD/include/numerics/CNumerics.hpp b/SU2_CFD/include/numerics/CNumerics.hpp index 13111efbb496..5130b17ef0e6 100644 --- a/SU2_CFD/include/numerics/CNumerics.hpp +++ b/SU2_CFD/include/numerics/CNumerics.hpp @@ -32,6 +32,7 @@ #include #include #include +#include #include "../../../Common/include/CConfig.hpp" #include "../../../Common/include/linear_algebra/blas_structure.hpp" @@ -180,6 +181,7 @@ class CNumerics { roughness_j = 0.0; /*!< \brief Roughness of the wall nearest to point j. */ su2double MeanPerturbedRSM[3][3]; /*!< \brief Perturbed Reynolds stress tensor */ + su2double stochReynStress[3][3]; /*!< \brief Stochastic contribution to Reynolds stress tensor for Backscatter Model. */ SST_ParsedOptions sstParsedOptions; /*!< \brief additional options for the SST turbulence model */ unsigned short Eig_Val_Comp; /*!< \brief Component towards which perturbation is perfromed */ su2double uq_delta_b; /*!< \brief Magnitude of perturbation */ @@ -635,6 +637,62 @@ class CNumerics { } } + /*! + * \brief Compute a random contribution to the Reynolds stress tensor (Stochastic Backscatter Model). + * \details See: Kok, Johan C. "A stochastic backscatter model for grey-area mitigation in detached + * eddy simulations." Flow, Turbulence and Combustion 99.1 (2017): 119-150. + * \param[in] nDim - Dimension of the flow problem, 2 or 3. + * \param[in] density - Density. + * \param[in] eddyVis - Eddy viscosity. + * \param[in] velGrad - Velocity gradient matrix. + * \param[out] stochReynStress - Stochastic tensor (to be added to the Reynolds stress tensor). + */ + template + NEVERINLINE static void ComputeStochReynStress(size_t nDim, Scalar density, Scalar eddyVis, + const Mat1& velGrad, Mat2& stochReynStress) { + + /* --- Initialize seed ---*/ + + static std::default_random_engine gen; + std::random_device rd; + gen.seed(rd()); + + /* --- Generate a vector of three independent normally-distributed samples ---*/ + + std::normal_distribution rnd(0.0,1.0); + Scalar rndVec [3] = {0.0}; + for (size_t iDim = 0; iDim < nDim; iDim++) + rndVec[iDim] = rnd(gen); + + /* --- Estimate turbulent kinetic energy --- */ + + Scalar turbKE = 0.0, strainMag = 0.0; + for (size_t iDim = 0; iDim < nDim; iDim++) { + for (size_t jDim = 0; jDim < nDim; jDim++) { + strainMag += pow(0.5 * (velGrad[iDim][jDim] + velGrad[jDim][iDim]), 2); + } + } + strainMag = sqrt(2.0 * strainMag); + turbKE = eddyVis * strainMag; + turbKE = max(turbKE, 1E-10); + + /* --- Calculate stochastic tensor --- */ + + stochReynStress[1][0] = - density * turbKE * rndVec[2]; + stochReynStress[2][0] = density * turbKE * rndVec[1]; + stochReynStress[2][1] = - density * turbKE * rndVec[0]; + for (size_t iDim = 0; iDim < nDim; iDim++) { + for (size_t jDim = 0; jDim <= iDim; jDim++) { + if (iDim==jDim) { + stochReynStress[iDim][jDim] = 0.0; + } else { + stochReynStress[jDim][iDim] = - stochReynStress[iDim][jDim]; + } + } + } + + } + /*! * \brief Project average gradient onto normal (with or w/o correction) for viscous fluxes of scalar quantities. * \param[in] nDim - Dimension of the flow problem, 2 or 3. diff --git a/SU2_CFD/include/numerics/flow/flow_diffusion.hpp b/SU2_CFD/include/numerics/flow/flow_diffusion.hpp index 6cbb450d4bf5..9c5919578e6c 100644 --- a/SU2_CFD/include/numerics/flow/flow_diffusion.hpp +++ b/SU2_CFD/include/numerics/flow/flow_diffusion.hpp @@ -198,12 +198,14 @@ class CAvgGrad_Base : public CNumerics { * \param[in] val_turb_ke - Turbulent kinetic energy * \param[in] val_laminar_viscosity - Laminar viscosity. * \param[in] val_eddy_viscosity - Eddy viscosity. + * \param[in] config - Definition of the particular problem. */ void SetStressTensor(const su2double *val_primvar, const su2double* const *val_gradprimvar, su2double val_turb_ke, su2double val_laminar_viscosity, - su2double val_eddy_viscosity); + su2double val_eddy_viscosity, + const CConfig* config); /*! * \brief Get a component of the viscous stress tensor. diff --git a/SU2_CFD/src/numerics/flow/flow_diffusion.cpp b/SU2_CFD/src/numerics/flow/flow_diffusion.cpp index 97b27e261d04..4732a134b78b 100644 --- a/SU2_CFD/src/numerics/flow/flow_diffusion.cpp +++ b/SU2_CFD/src/numerics/flow/flow_diffusion.cpp @@ -122,7 +122,8 @@ void CAvgGrad_Base::SetStressTensor(const su2double *val_primvar, const su2double* const *val_gradprimvar, const su2double val_turb_ke, const su2double val_laminar_viscosity, - const su2double val_eddy_viscosity) { + const su2double val_eddy_viscosity, + const CConfig* config) { const su2double Density = val_primvar[nDim+2]; @@ -142,6 +143,15 @@ void CAvgGrad_Base::SetStressTensor(const su2double *val_primvar, // turb_ke is not considered in the stress tensor, see #797 ComputeStressTensor(nDim, tau, val_gradprimvar+1, total_viscosity, Density, su2double(0.0)); } + + /* --- If the Stochastic Backscatter Model is active, add random contribution to stress tensor ---*/ + + if (config->GetStochastic_Backscatter()) { + for (unsigned short iDim = 0 ; iDim < nDim; iDim++) + for (unsigned short jDim = 0 ; jDim < nDim; jDim++) + tau[iDim][jDim] += stochReynStress[iDim][jDim]; + } + } void CAvgGrad_Base::AddTauWall(const su2double *UnitNormal, @@ -432,10 +442,17 @@ CNumerics::ResidualType<> CAvgGrad_Flow::ComputeResidual(const CConfig* config) Mean_turb_ke, MeanPerturbedRSM); } + /* --- If the Stochastic Backscatter Model is active, add random contribution to stress tensor ---*/ + + if (config->GetStochastic_Backscatter()) { + ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_Eddy_Viscosity, Mean_GradPrimVar+1, + stochReynStress); + } + /*--- Get projected flux tensor (viscous residual) ---*/ SetStressTensor(Mean_PrimVar, Mean_GradPrimVar, Mean_turb_ke, - Mean_Laminar_Viscosity, Mean_Eddy_Viscosity); + Mean_Laminar_Viscosity, Mean_Eddy_Viscosity,config); if (config->GetSAParsedOptions().qcr2000) AddQCR(nDim, &Mean_GradPrimVar[1], tau); if (Mean_TauWall > 0) AddTauWall(UnitNormal, Mean_TauWall); @@ -617,9 +634,16 @@ CNumerics::ResidualType<> CAvgGradInc_Flow::ComputeResidual(const CConfig* confi Mean_turb_ke, MeanPerturbedRSM); } + /* --- If the Stochastic Backscatter Model is active, add random contribution to stress tensor ---*/ + + if (config->GetStochastic_Backscatter()) { + ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_Eddy_Viscosity, Mean_GradPrimVar+1, + stochReynStress); + } + /*--- Get projected flux tensor (viscous residual) ---*/ SetStressTensor(Mean_PrimVar, Mean_GradPrimVar, Mean_turb_ke, - Mean_Laminar_Viscosity, Mean_Eddy_Viscosity); + Mean_Laminar_Viscosity, Mean_Eddy_Viscosity,config); if (config->GetSAParsedOptions().qcr2000) AddQCR(nDim, &Mean_GradPrimVar[1], tau); if (Mean_TauWall > 0) AddTauWall(UnitNormal, Mean_TauWall); @@ -947,10 +971,17 @@ CNumerics::ResidualType<> CGeneralAvgGrad_Flow::ComputeResidual(const CConfig* c Mean_turb_ke, MeanPerturbedRSM); } + /* --- If the Stochastic Backscatter Model is active, add random contribution to stress tensor ---*/ + + if (config->GetStochastic_Backscatter()) { + ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_Eddy_Viscosity, Mean_GradPrimVar+1, + stochReynStress); + } + /*--- Get projected flux tensor (viscous residual) ---*/ SetStressTensor(Mean_PrimVar, Mean_GradPrimVar, Mean_turb_ke, - Mean_Laminar_Viscosity, Mean_Eddy_Viscosity); + Mean_Laminar_Viscosity, Mean_Eddy_Viscosity,config); if (config->GetSAParsedOptions().qcr2000) AddQCR(nDim, &Mean_GradPrimVar[1], tau); if (Mean_TauWall > 0) AddTauWall(UnitNormal, Mean_TauWall); diff --git a/TestCases/ddes/flatplate/ddes_flatplate.cfg b/TestCases/ddes/flatplate/ddes_flatplate.cfg index 5c1d0804b3b4..03b780bcb067 100644 --- a/TestCases/ddes/flatplate/ddes_flatplate.cfg +++ b/TestCases/ddes/flatplate/ddes_flatplate.cfg @@ -13,9 +13,11 @@ % SOLVER= RANS KIND_TURB_MODEL= SA -HYBRID_RANSLES= SA_EDDES +HYBRID_RANSLES= SA_DES +STOCHASTIC_BACKSCATTER= YES MATH_PROBLEM= DIRECT RESTART_SOL= NO +RESTART_ITER= 100 % ----------- COMPRESSIBLE AND INCOMPRESSIBLE FREE-STREAM DEFINITION ----------% % @@ -41,7 +43,7 @@ TIME_MARCHING= DUAL_TIME_STEPPING-2ND_ORDER % % U_inf = 69.4448 - dt*=0.02 - dt=0.000288 TIME_STEP= 0.000288 -MAX_TIME= 20.0 +MAX_TIME= 5 UNST_CFL_NUMBER= 0.0 INNER_ITER= 20 @@ -61,7 +63,7 @@ CFL_NUMBER= 10.0 CFL_ADAPT= NO CFL_ADAPT_PARAM= ( 1.5, 0.5, 1.0, 100.0 ) RK_ALPHA_COEFF= ( 0.66667, 0.66667, 1.000000 ) -TIME_ITER= 10 +TIME_ITER= 500 % ----------------------- SLOPE LIMITER DEFINITION ----------------------------% % @@ -103,5 +105,5 @@ VOLUME_ADJ_FILENAME= adjoint GRAD_OBJFUNC_FILENAME= of_grad SURFACE_FILENAME= surface_flow SURFACE_ADJ_FILENAME= surface_adjoint -OUTPUT_WRT_FREQ= 1000 +OUTPUT_WRT_FREQ= 1000000 SCREEN_OUTPUT= (TIME_ITER, INNER_ITER, RMS_DENSITY, RMS_NU_TILDE, LIFT, DRAG, TOTAL_HEATFLUX) diff --git a/config_template.cfg b/config_template.cfg index bd7a8ff17003..fc154e575954 100644 --- a/config_template.cfg +++ b/config_template.cfg @@ -180,6 +180,9 @@ HYBRID_RANSLES= SA_DDES % % DES Constant (0.65) DES_CONST= 0.65 +% +% Stochastic Backscatter Model (NO, YES) +STOCHASTIC_BACKSCATTER= NO % -------------------- COMPRESSIBLE FREE-STREAM DEFINITION --------------------% % From cfa0cd80dfc2c09311c620aaceac216052bdab01 Mon Sep 17 00:00:00 2001 From: Angelo Passariello Date: Tue, 23 Sep 2025 10:12:29 +0200 Subject: [PATCH 02/25] Add solution of Langevin equations Add Langevin equations to Spalart-Allmaras solver (uncorrelated random source term) + add stochastic contribution to Reynolds stress tensor (using conservative variables from Langevin equations). --- Common/include/toolboxes/random_toolbox.hpp | 85 +++++++++++++++++++ Common/src/CConfig.cpp | 2 + SU2_CFD/include/numerics/CNumerics.hpp | 69 +++++++++++---- .../include/numerics/flow/flow_diffusion.hpp | 1 + .../numerics/turbulent/turb_convection.hpp | 17 +++- .../numerics/turbulent/turb_sources.hpp | 74 +++++++++++++++- .../include/solvers/CFVMFlowSolverBase.inl | 14 ++- SU2_CFD/include/variables/CIncNSVariable.hpp | 16 +++- SU2_CFD/include/variables/CNEMONSVariable.hpp | 1 + SU2_CFD/include/variables/CNSVariable.hpp | 14 +++ SU2_CFD/include/variables/CTurbSAVariable.hpp | 12 +++ SU2_CFD/include/variables/CTurbVariable.hpp | 2 +- SU2_CFD/include/variables/CVariable.hpp | 12 +++ SU2_CFD/src/numerics/flow/flow_diffusion.cpp | 12 ++- SU2_CFD/src/output/CFlowOutput.cpp | 41 +++++++++ SU2_CFD/src/solvers/CIncNSSolver.cpp | 7 +- SU2_CFD/src/solvers/CNSSolver.cpp | 2 + SU2_CFD/src/solvers/CTurbSASolver.cpp | 37 +++++++- SU2_CFD/src/variables/CIncNSVariable.cpp | 1 + SU2_CFD/src/variables/CNEMONSVariable.cpp | 1 + SU2_CFD/src/variables/CNSVariable.cpp | 1 + SU2_CFD/src/variables/CTurbSAVariable.cpp | 17 +++- 22 files changed, 409 insertions(+), 29 deletions(-) create mode 100644 Common/include/toolboxes/random_toolbox.hpp diff --git a/Common/include/toolboxes/random_toolbox.hpp b/Common/include/toolboxes/random_toolbox.hpp new file mode 100644 index 000000000000..d7300f2c950a --- /dev/null +++ b/Common/include/toolboxes/random_toolbox.hpp @@ -0,0 +1,85 @@ +/*! + * \file random_toolbox.hpp + * \brief Collection of utility functions for random number generation. + * \version 8.3.0 "Harrier" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2025, 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 + +namespace RandomToolbox { +/// \addtogroup RandomToolbox +/// @{ + +/*! + * \brief Combine two 64-bit integers into a single hash value. + * \param[in] v1 Current hash value. + * \param[in] v2 Value to mix in. + * \return Combined hash value. + */ +inline uint64_t HashCombine(uint64_t v1, uint64_t v2) { + const uint64_t prime = 1099511628211ULL; + v1 ^= v2; + v1 *= prime; + return v1; +} + +/*! + * \brief Convert a double to a 64-bit integer suitable for hashing. + * \param[in] x Double to integer. + * \return Hash value of the double. + */ +inline uint64_t ToUInt64(double x) { + return std::hash{}(x); +} + +/*! + * \brief Build a deterministic seed from physical time. + * \param[in] time Physical time. + * \return 64-bit seed value. + */ +inline uint64_t GetSeed(double x) { + uint64_t h = 1469598103934665603ULL; // Offset + h = HashCombine(h, ToUInt64(x)); + return h; +} + +/*! + * \brief Generate a standard normally-distributed random number. + * \param[in] seed Seed for the random number generator. + * \param[in] mean Mean of the normal distribution (default 0). + * \param[in] stddev Standard deviation of the normal distribution (default 1). + * \return Normally-distributed random number. + */ +inline double GetRandomNormal(uint64_t seed, + double mean = 0.0, + double stddev = 1.0) { + std::mt19937 gen(static_cast(seed)); + std::normal_distribution rnd(mean, stddev); + return rnd(gen); +} + +/// @} +} // namespace RandomToolbox diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index a5aa8e2bd08e..4e0ecf9cab1d 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -6456,6 +6456,8 @@ void CConfig::SetOutput(SU2_COMPONENT val_software, unsigned short val_izone) { cout << "ON" << endl; else cout << "OFF" << endl; + if (StochasticBackscatter && Kind_HybridRANSLES == NO_HYBRIDRANSLES) + SU2_MPI::Error("Stochastic Backscatter can only be activated with Hybrid RANS/LES.", CURRENT_FUNCTION); break; case MAIN_SOLVER::NEMO_EULER: if (Kind_Regime == ENUM_REGIME::COMPRESSIBLE) cout << "Compressible two-temperature thermochemical non-equilibrium Euler equations." << endl; diff --git a/SU2_CFD/include/numerics/CNumerics.hpp b/SU2_CFD/include/numerics/CNumerics.hpp index 5130b17ef0e6..a523d62d4ae8 100644 --- a/SU2_CFD/include/numerics/CNumerics.hpp +++ b/SU2_CFD/include/numerics/CNumerics.hpp @@ -182,6 +182,14 @@ class CNumerics { su2double MeanPerturbedRSM[3][3]; /*!< \brief Perturbed Reynolds stress tensor */ su2double stochReynStress[3][3]; /*!< \brief Stochastic contribution to Reynolds stress tensor for Backscatter Model. */ + su2double + lesSensor_i, /*!< \brief LES sensor at point i. */ + lesSensor_j; /*!< \brief LES sensor at point j. */ + su2double lastTime; /*!< \brief Physical time of unsteady simulation. */ + su2double stochSource[3]; /*!< \brief Source term for Langevin equations in Stochastic Backscatter Model. */ + const su2double + *stochVar_i, /*!< \brief Stochastic variables at point i for Stochastic Backscatter Model. */ + *stochVar_j; /*!< \brief Stochastic variables at point j for Stochastic Backscatter Model. */ SST_ParsedOptions sstParsedOptions; /*!< \brief additional options for the SST turbulence model */ unsigned short Eig_Val_Comp; /*!< \brief Component towards which perturbation is perfromed */ su2double uq_delta_b; /*!< \brief Magnitude of perturbation */ @@ -636,7 +644,7 @@ class CNumerics { } } } - + /*! * \brief Compute a random contribution to the Reynolds stress tensor (Stochastic Backscatter Model). * \details See: Kok, Johan C. "A stochastic backscatter model for grey-area mitigation in detached @@ -649,20 +657,8 @@ class CNumerics { */ template NEVERINLINE static void ComputeStochReynStress(size_t nDim, Scalar density, Scalar eddyVis, - const Mat1& velGrad, Mat2& stochReynStress) { - - /* --- Initialize seed ---*/ - - static std::default_random_engine gen; - std::random_device rd; - gen.seed(rd()); - - /* --- Generate a vector of three independent normally-distributed samples ---*/ - - std::normal_distribution rnd(0.0,1.0); - Scalar rndVec [3] = {0.0}; - for (size_t iDim = 0; iDim < nDim; iDim++) - rndVec[iDim] = rnd(gen); + const Mat1& velGrad, const su2double *rndVec, + Mat2& stochReynStress) { /* --- Estimate turbulent kinetic energy --- */ @@ -876,6 +872,16 @@ class CNumerics { turb_ke_j = val_turb_ke_j; } + /*! + * \brief Set the stochastic variables from Langevin equations (Stochastic Backscatter Model). + * \param[in] val_stochvar_i - Value of the stochastic variable at point i. + * \param[in] val_stochvar_j - Value of the stochastic variable at point j. + */ + inline void SetStochVar(su2double *val_stochvar_i, su2double *val_stochvar_j) { + stochVar_i = val_stochvar_i; + stochVar_j = val_stochvar_j; + } + /*! * \brief Set the value of the distance from the nearest wall. * \param[in] val_dist_i - Value of of the distance from point i to the nearest wall. @@ -886,6 +892,39 @@ class CNumerics { dist_j = val_dist_j; } + /*! + * \brief Set the value of the LES sensor. + * \param[in] val_les_i - Value of the LES sensor at point point i. + * \param[in] val_les_j - Value of the LES sensor at point point j. + */ + void SetLESSensor(su2double val_les_i, su2double val_les_j) { + lesSensor_i = val_les_i; + lesSensor_j = val_les_j; + } + + /*! + * \brief Set the value of physical time. + * \param[in] val_last_time - Value of physical time. + */ + void SetLastTime(su2double val_last_time) { + lastTime = val_last_time; + } + + /*! + * \brief Get the value of physical time. + * \param[out] lastTime - Value of physical time. + */ + inline su2double GetLastTime() const { return lastTime; } + + /*! + * \brief Set the stochastic source term for the Langevin equations (Backscatter Model). + * \param[in] val_stoch_source - Value of stochastic source term. + * \param[in] iDim - Index of Langevin equation. + */ + void SetStochSource(su2double val_stoch_source, unsigned short iDim) { + stochSource[iDim] = val_stoch_source; + } + /*! * \brief Set the value of the roughness from the nearest wall. * \param[in] val_dist_i - Value of of the roughness of the nearest wall from point i diff --git a/SU2_CFD/include/numerics/flow/flow_diffusion.hpp b/SU2_CFD/include/numerics/flow/flow_diffusion.hpp index 9c5919578e6c..a3afe1b3c45d 100644 --- a/SU2_CFD/include/numerics/flow/flow_diffusion.hpp +++ b/SU2_CFD/include/numerics/flow/flow_diffusion.hpp @@ -59,6 +59,7 @@ class CAvgGrad_Base : public CNumerics { Mean_Eddy_Viscosity, /*!< \brief Mean value of the eddy viscosity. */ Mean_turb_ke, /*!< \brief Mean value of the turbulent kinetic energy. */ Mean_TauWall, /*!< \brief Mean wall shear stress (wall functions). */ + *Mean_StochVar, /*!< \brief Mean stochastic variables (Stochastic Backscatter Model). */ TauWall_i, TauWall_j, /*!< \brief Wall shear stress at point i and j (wall functions). */ dist_ij_2, /*!< \brief Length of the edge and face, squared */ Edge_Vector[MAXNDIM] = {0.0}, /*!< \brief Vector from point i to point j. */ diff --git a/SU2_CFD/include/numerics/turbulent/turb_convection.hpp b/SU2_CFD/include/numerics/turbulent/turb_convection.hpp index 03c33d381384..2b5954ae22b7 100644 --- a/SU2_CFD/include/numerics/turbulent/turb_convection.hpp +++ b/SU2_CFD/include/numerics/turbulent/turb_convection.hpp @@ -59,9 +59,20 @@ class CUpwSca_TurbSA final : public CUpwScalar { * \param[in] config - Definition of the particular problem. */ void FinishResidualCalc(const CConfig* config) override { - Flux[0] = a0*ScalarVar_i[0] + a1*ScalarVar_j[0]; - Jacobian_i[0][0] = a0; - Jacobian_j[0][0] = a1; + bool backscatter = config->GetStochastic_Backscatter(); + if (!backscatter) { + Flux[0] = a0*ScalarVar_i[0] + a1*ScalarVar_j[0]; + Jacobian_i[0][0] = a0; + Jacobian_j[0][0] = a1; + } else { + for (unsigned short iVar = 0; iVar < 4; iVar++) { + Flux[iVar] = a0*ScalarVar_i[iVar] + a1*ScalarVar_j[iVar]; + for (unsigned short jVar = 0; jVar < 4; jVar++) { + Jacobian_i[iVar][jVar] = (iVar == jVar) ? a0 : 0.0; + Jacobian_j[iVar][jVar] = (iVar == jVar) ? a1 : 0.0; + } + } + } } public: diff --git a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp index 2195f94bd21f..27dfabb07fe0 100644 --- a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp +++ b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp @@ -74,6 +74,8 @@ class CSourceBase_TurbSA : public CNumerics { /*--- Residual and Jacobian ---*/ su2double Residual, *Jacobian_i; su2double Jacobian_Buffer; /*!< \brief Static storage for the Jacobian (which needs to be pointer for return type). */ + su2double ResidSB[4] = {0.0}, *JacobianSB_i[4]; + su2double JacobianSB_Buffer[16]; const FlowIndices idx; /*!< \brief Object to manage the access to the flow primitives. */ const SA_ParsedOptions options; /*!< \brief Struct with SA options. */ @@ -112,6 +114,55 @@ class CSourceBase_TurbSA : public CNumerics { Residual += yinv * dv_axi * Volume; } + /*! + * \brief Include source-term residuals for Langevin equations (Stochastic Backscatter Model) + */ + inline void ResidualStochEquations(su2double timeStep, const su2double ct, + su2double lengthScale, su2double les_sensor, + const CSAVariables& var) { + + const su2double& nue = ScalarVar_i[0]; + + const auto& density = V_i[idx.Density()]; + + const su2double nut_small = 1.0e-10; + + su2double Ji_2 = pow(var.Ji,2); + su2double Ji_3 = Ji_2 * var.Ji; + + const su2double nut = nue * var.fv1; + + su2double tTurb = ct * pow(lengthScale, 2) / max(nut, nut_small); + su2double tRat = timeStep / tTurb; + su2double corrFac = sqrt(0.5*(1.0+tRat)*(4.0+tRat)/(2.0+tRat)); + + su2double scaleFactor = 1.0/tTurb * sqrt(2.0/tRat) * density * corrFac; + + ResidSB[0] = Residual; + for (unsigned short iDim = 1; iDim < 4; iDim++ ) { + ResidSB[iDim] = les_sensor * scaleFactor * stochSource[iDim-1] - + 1.0/tTurb * density * ScalarVar_i[iDim]; + ResidSB[iDim] *= Volume; + } + + for (unsigned short iDim = 0; iDim < 4; iDim++ ) + for (unsigned short jDim = 0; jDim < 4; jDim++ ) + JacobianSB_i[iDim][jDim] = 0.0; + JacobianSB_i[0][0] = Jacobian_i[0]; + JacobianSB_i[1][1] = JacobianSB_i[2][2] = JacobianSB_i[3][3] = - 1.0/tTurb * density * Volume; + + su2double dnut_dnue = var.fv1 + 3.0 * var.cv1_3 * Ji_3 / pow(Ji_3 + var.cv1_3, 2); + su2double dtTurb_dnut = - ct * pow(lengthScale,2) / (max(nut, nut_small)*max(nut, nut_small)); + + for (unsigned short iDim = 1; iDim < 4; iDim++ ) { + JacobianSB_i[iDim][0] = - 1.0/tTurb * les_sensor * scaleFactor * stochSource[iDim-1] + + 1.0/(tTurb*tTurb) * ScalarVar_i[iDim] + + density * les_sensor * corrFac * stochSource[iDim-1] / + (tTurb * sqrt(2.0*tTurb*timeStep)); + JacobianSB_i[iDim][0] *= dtTurb_dnut * dnut_dnue * Volume; + } + } + public: /*! * \brief Constructor of the class. @@ -127,6 +178,9 @@ class CSourceBase_TurbSA : public CNumerics { /*--- Setup the Jacobian pointer, we need to return su2double** but we know * the Jacobian is 1x1 so we use this trick to avoid heap allocation. ---*/ Jacobian_i = &Jacobian_Buffer; + /*--- Setup the Jacobian pointer for Stochastic Backscatter Model. ---*/ + for (unsigned short iVar = 0; iVar < 4; iVar++) + JacobianSB_i[iVar] = JacobianSB_Buffer + 4*iVar; } @@ -145,6 +199,12 @@ class CSourceBase_TurbSA : public CNumerics { AD::SetPreaccIn(PrimVar_Grad_i + idx.Velocity(), nDim, nDim); AD::SetPreaccIn(ScalarVar_Grad_i[0], nDim); + bool backscatter = config->GetStochastic_Backscatter(); + if (backscatter) { + AD::SetPreaccIn(lesSensor_i); + AD::SetPreaccIn(stochSource, 3); + } + /*--- Common auxiliary variables and constants of the model. ---*/ CSAVariables var; @@ -252,12 +312,24 @@ class CSourceBase_TurbSA : public CNumerics { if (axisymmetric) ResidualAxisymmetricDiffusion(var.sigma); Jacobian_i[0] *= Volume; + + /*--- Compute residual for Langevin equations (Stochastic Backscatter Model). ---*/ + + if (backscatter) { + const su2double constDES = config->GetConst_DES(); + const su2double ctTurb = 0.05 / pow(constDES, 2); + ResidualStochEquations(config->GetDelta_UnstTime(), ctTurb, dist_i, lesSensor_i, var); + } } AD::SetPreaccOut(Residual); + if (backscatter) { AD::SetPreaccOut(ResidSB, 4); } AD::EndPreacc(); - return ResidualType<>(&Residual, &Jacobian_i, nullptr); + if (backscatter) + return ResidualType<>(ResidSB, JacobianSB_i, nullptr); + else + return ResidualType<>(&Residual, &Jacobian_i, nullptr); } }; diff --git a/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl b/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl index 1d88295d7ed9..459c915b1076 100644 --- a/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl +++ b/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl @@ -433,9 +433,10 @@ void CFVMFlowSolverBase::Viscous_Residual_impl(unsigned long iEdge, CGeome const bool implicit = (config->GetKind_TimeIntScheme() == EULER_IMPLICIT); const bool tkeNeeded = (config->GetKind_Turb_Model() == TURB_MODEL::SST); + const bool backscatter = config->GetStochastic_Backscatter(); CVariable* turbNodes = nullptr; - if (tkeNeeded) turbNodes = solver_container[TURB_SOL]->GetNodes(); + if (tkeNeeded || backscatter) turbNodes = solver_container[TURB_SOL]->GetNodes(); /*--- Points, coordinates and normal vector in edge ---*/ @@ -466,6 +467,17 @@ void CFVMFlowSolverBase::Viscous_Residual_impl(unsigned long iEdge, CGeome numerics->SetTurbKineticEnergy(turbNodes->GetSolution(iPoint,0), turbNodes->GetSolution(jPoint,0)); + /*--- Stochastic variables from Langevin equations (Stochastic Backscatter Model). ---*/ + + if (backscatter) { + su2double stochVars_i [3], stochVars_j [3]; + for (unsigned short iVar = 1; iVar < 4; iVar++) { + stochVars_i[iVar-1] = turbNodes->GetSolution(iPoint, iVar); + stochVars_j[iVar-1] = turbNodes->GetSolution(jPoint, iVar); + } + numerics->SetStochVar(stochVars_i, stochVars_j); + } + /*--- Wall shear stress values (wall functions) ---*/ numerics->SetTau_Wall(nodes->GetTau_Wall(iPoint), diff --git a/SU2_CFD/include/variables/CIncNSVariable.hpp b/SU2_CFD/include/variables/CIncNSVariable.hpp index 5e254506b84f..b9caeb7193f3 100644 --- a/SU2_CFD/include/variables/CIncNSVariable.hpp +++ b/SU2_CFD/include/variables/CIncNSVariable.hpp @@ -39,7 +39,8 @@ class CIncNSVariable final : public CIncEulerVariable { private: VectorType Tau_Wall; /*!< \brief Magnitude of the wall shear stress from a wall function. */ - VectorType DES_LengthScale; + VectorType DES_LengthScale; /*!< \brief DES Length Scale. */ + VectorType LES_Mode; /*!< \brief Sensor for local simulation mode (0=RANS, 1=LES).*/ public: /*! @@ -132,4 +133,17 @@ class CIncNSVariable final : public CIncEulerVariable { */ inline su2double GetDES_LengthScale(unsigned long iPoint) const override { return DES_LengthScale(iPoint); } + /*! + * \brief Set the LES sensor. + */ + inline void SetLES_Mode(unsigned long iPoint, su2double val_les_mode) override { + LES_Mode(iPoint) = val_les_mode; + } + + /*! + * \brief Get the LES sensor. + * \return Value of the LES sensor. + */ + inline su2double GetLES_Mode(unsigned long iPoint) const override { return LES_Mode(iPoint); } + }; diff --git a/SU2_CFD/include/variables/CNEMONSVariable.hpp b/SU2_CFD/include/variables/CNEMONSVariable.hpp index 1d399a7325ca..bdda130e14c0 100644 --- a/SU2_CFD/include/variables/CNEMONSVariable.hpp +++ b/SU2_CFD/include/variables/CNEMONSVariable.hpp @@ -52,6 +52,7 @@ class CNEMONSVariable final : public CNEMOEulerVariable { VectorType Tau_Wall; /*!< \brief Magnitude of the wall shear stress from a wall function. */ VectorType DES_LengthScale; /*!< \brief DES Length Scale. */ + VectorType LES_Mode; /*!< \brief Sensor for local simulation mode (0=RANS, 1=LES). */ VectorType Roe_Dissipation; /*!< \brief Roe low dissipation coefficient. */ VectorType Vortex_Tilting; /*!< \brief Value of the vortex tilting variable for DES length scale computation. */ diff --git a/SU2_CFD/include/variables/CNSVariable.hpp b/SU2_CFD/include/variables/CNSVariable.hpp index 69855f3d8a67..44cc1c5c60f6 100644 --- a/SU2_CFD/include/variables/CNSVariable.hpp +++ b/SU2_CFD/include/variables/CNSVariable.hpp @@ -41,6 +41,7 @@ class CNSVariable final : public CEulerVariable { VectorType Tau_Wall; /*!< \brief Magnitude of the wall shear stress from a wall function. */ VectorType DES_LengthScale; /*!< \brief DES Length Scale. */ + VectorType LES_Mode; /*!< \brief Sensor for local simulation mode (0=RANS, 1=LES).*/ VectorType Roe_Dissipation; /*!< \brief Roe low dissipation coefficient. */ VectorType Vortex_Tilting; /*!< \brief Value of the vortex tilting variable for DES length scale computation. */ @@ -185,6 +186,19 @@ class CNSVariable final : public CEulerVariable { DES_LengthScale(iPoint) = val_des_lengthscale; } +/*! + * \brief Set the LES sensor. + */ + inline void SetLES_Mode(unsigned long iPoint, su2double val_les_mode) override { + LES_Mode(iPoint) = val_les_mode; + } + + /*! + * \brief Get the LES sensor. + * \return Value of the LES sensor. + */ + inline su2double GetLES_Mode(unsigned long iPoint) const override { return LES_Mode(iPoint); } + /*! * \brief Set the new solution for Roe Dissipation. * \param[in] val_delta - A scalar measure of the grid size diff --git a/SU2_CFD/include/variables/CTurbSAVariable.hpp b/SU2_CFD/include/variables/CTurbSAVariable.hpp index adf3cb5cf17b..9cab3dd177f5 100644 --- a/SU2_CFD/include/variables/CTurbSAVariable.hpp +++ b/SU2_CFD/include/variables/CTurbSAVariable.hpp @@ -40,6 +40,7 @@ class CTurbSAVariable final : public CTurbVariable { private: VectorType DES_LengthScale; + VectorType LES_Mode; VectorType Vortex_Tilting; public: @@ -73,6 +74,17 @@ class CTurbSAVariable final : public CTurbVariable { */ inline void SetDES_LengthScale(unsigned long iPoint, su2double val_des_lengthscale) override { DES_LengthScale(iPoint) = val_des_lengthscale; } +/*! + * \brief Set the LES sensor. + */ + inline void SetLES_Mode(unsigned long iPoint, su2double val_les_mode) override { LES_Mode(iPoint) = val_les_mode; } + + /*! + * \brief Get the LES sensor. + * \return Value of the LES sensor. + */ + inline su2double GetLES_Mode(unsigned long iPoint) const override { return LES_Mode(iPoint); } + /*! * \brief Set the vortex tilting measure for computation of the EDDES length scale * \param[in] iPoint - Point index. diff --git a/SU2_CFD/include/variables/CTurbVariable.hpp b/SU2_CFD/include/variables/CTurbVariable.hpp index c0c33efd8c90..2fbec9abd244 100644 --- a/SU2_CFD/include/variables/CTurbVariable.hpp +++ b/SU2_CFD/include/variables/CTurbVariable.hpp @@ -40,7 +40,7 @@ class CTurbVariable : public CScalarVariable { VectorType muT; /*!< \brief Eddy viscosity. */ public: - static constexpr size_t MAXNVAR = 2; + static constexpr size_t MAXNVAR = 4; VectorType turb_index; VectorType intermittency; /*!< \brief Value of the intermittency for the trans. model. */ diff --git a/SU2_CFD/include/variables/CVariable.hpp b/SU2_CFD/include/variables/CVariable.hpp index dc905d79fcb4..307531798364 100644 --- a/SU2_CFD/include/variables/CVariable.hpp +++ b/SU2_CFD/include/variables/CVariable.hpp @@ -397,6 +397,18 @@ class CVariable { */ inline virtual void SetDES_LengthScale(unsigned long iPoint, su2double val_des_lengthscale) {} + /*! + * \brief A virtual member. + * \param[in] iPoint - Point index. + */ + inline virtual su2double GetLES_Mode(unsigned long iPoint) const { return 0.0; } + + /*! + * \brief A virtual member. + * \param[in] iPoint - Point index. + */ + inline virtual void SetLES_Mode(unsigned long iPoint, su2double val_les_mode) {} + /*! * \brief A virtual member. * \param[in] iPoint - Point index. diff --git a/SU2_CFD/src/numerics/flow/flow_diffusion.cpp b/SU2_CFD/src/numerics/flow/flow_diffusion.cpp index 4732a134b78b..9e13ffa7dc5c 100644 --- a/SU2_CFD/src/numerics/flow/flow_diffusion.cpp +++ b/SU2_CFD/src/numerics/flow/flow_diffusion.cpp @@ -445,8 +445,10 @@ CNumerics::ResidualType<> CAvgGrad_Flow::ComputeResidual(const CConfig* config) /* --- If the Stochastic Backscatter Model is active, add random contribution to stress tensor ---*/ if (config->GetStochastic_Backscatter()) { + for (iVar = 0; iVar < 3; iVar++) + Mean_StochVar[iVar] = 0.5*(stochVar_i[iVar] + stochVar_j[iVar]); ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_Eddy_Viscosity, Mean_GradPrimVar+1, - stochReynStress); + Mean_StochVar, stochReynStress); } /*--- Get projected flux tensor (viscous residual) ---*/ @@ -637,8 +639,10 @@ CNumerics::ResidualType<> CAvgGradInc_Flow::ComputeResidual(const CConfig* confi /* --- If the Stochastic Backscatter Model is active, add random contribution to stress tensor ---*/ if (config->GetStochastic_Backscatter()) { + for (iVar = 0; iVar < 3; iVar++) + Mean_StochVar[iVar] = 0.5*(stochVar_i[iVar] + stochVar_j[iVar]); ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_Eddy_Viscosity, Mean_GradPrimVar+1, - stochReynStress); + Mean_StochVar, stochReynStress); } /*--- Get projected flux tensor (viscous residual) ---*/ @@ -974,8 +978,10 @@ CNumerics::ResidualType<> CGeneralAvgGrad_Flow::ComputeResidual(const CConfig* c /* --- If the Stochastic Backscatter Model is active, add random contribution to stress tensor ---*/ if (config->GetStochastic_Backscatter()) { + for (iVar = 0; iVar < 3; iVar++) + Mean_StochVar[iVar] = 0.5*(stochVar_i[iVar] + stochVar_j[iVar]); ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_Eddy_Viscosity, Mean_GradPrimVar+1, - stochReynStress); + Mean_StochVar, stochReynStress); } /*--- Get projected flux tensor (viscous residual) ---*/ diff --git a/SU2_CFD/src/output/CFlowOutput.cpp b/SU2_CFD/src/output/CFlowOutput.cpp index b355c0698afb..d3f2bd02b9fd 100644 --- a/SU2_CFD/src/output/CFlowOutput.cpp +++ b/SU2_CFD/src/output/CFlowOutput.cpp @@ -964,6 +964,14 @@ void CFlowOutput::AddHistoryOutputFields_ScalarRMS_RES(const CConfig* config) { case TURB_FAMILY::SA: /// DESCRIPTION: Root-mean square residual of nu tilde (SA model). AddHistoryOutput("RMS_NU_TILDE", "rms[nu]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of nu tilde (SA model).", HistoryFieldType::RESIDUAL); + if (config->GetStochastic_Backscatter()) { + /// DESCRIPTION: Root-mean square residual of stochastic vector x-component (Stochastic Backscatter Model). + AddHistoryOutput("RMS_STOCH_VAR_X", "rms[stoch_var_x]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of stochastic vector x-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); + /// DESCRIPTION: Root-mean square residual of stochastic vector y-component (Stochastic Backscatter Model). + AddHistoryOutput("RMS_STOCH_VAR_Y", "rms[stoch_var_y]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of stochastic vector y-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); + /// DESCRIPTION: Root-mean square residual of stochastic vector x-component (Stochastic Backscatter Model). + AddHistoryOutput("RMS_STOCH_VAR_Z", "rms[stoch_var_z]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of stochastic vector z-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); + } break; case TURB_FAMILY::KW: @@ -1019,6 +1027,14 @@ void CFlowOutput::AddHistoryOutputFields_ScalarMAX_RES(const CConfig* config) { case TURB_FAMILY::SA: /// DESCRIPTION: Maximum residual of nu tilde (SA model). AddHistoryOutput("MAX_NU_TILDE", "max[nu]", ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of nu tilde (SA model).", HistoryFieldType::RESIDUAL); + if (config->GetStochastic_Backscatter()) { + /// DESCRIPTION: Maximum residual of stochastic vector x-component (Stochastic Backscatter Model). + AddHistoryOutput("MAX_STOCH_VAR_X", "max[stoch_var_x]", ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of stochastic vector x-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); + /// DESCRIPTION: Maximum residual of stochastic vector y-component (Stochastic Backscatter Model). + AddHistoryOutput("MAX_STOCH_VAR_Y", "max[stoch_var_y]", ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of stochastic vector y-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); + /// DESCRIPTION: Maximum residual of stochastic vector z-component (Stochastic Backscatter Model). + AddHistoryOutput("MAX_STOCH_VAR_Z", "max[stoch_var_z]", ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of stochastic vector z-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); + } break; case TURB_FAMILY::KW: @@ -1160,8 +1176,21 @@ void CFlowOutput::LoadHistoryDataScalar(const CConfig* config, const CSolver* co case TURB_FAMILY::SA: SetHistoryOutputValue("RMS_NU_TILDE", log10(solver[TURB_SOL]->GetRes_RMS(0))); SetHistoryOutputValue("MAX_NU_TILDE", log10(solver[TURB_SOL]->GetRes_Max(0))); + if (config->GetStochastic_Backscatter()) { + SetHistoryOutputValue("RMS_STOCH_VAR_X", log10(solver[TURB_SOL]->GetRes_RMS(1))); + SetHistoryOutputValue("RMS_STOCH_VAR_Y", log10(solver[TURB_SOL]->GetRes_RMS(2))); + SetHistoryOutputValue("RMS_STOCH_VAR_Z", log10(solver[TURB_SOL]->GetRes_RMS(3))); + SetHistoryOutputValue("MAX_STOCH_VAR_X", log10(solver[TURB_SOL]->GetRes_Max(1))); + SetHistoryOutputValue("MAX_STOCH_VAR_Y", log10(solver[TURB_SOL]->GetRes_Max(2))); + SetHistoryOutputValue("MAX_STOCH_VAR_Z", log10(solver[TURB_SOL]->GetRes_Max(3))); + } if (multiZone) { SetHistoryOutputValue("BGS_NU_TILDE", log10(solver[TURB_SOL]->GetRes_BGS(0))); + if (config->GetStochastic_Backscatter()) { + SetHistoryOutputValue("BGS_STOCH_VAR_X", log10(solver[TURB_SOL]->GetRes_BGS(1))); + SetHistoryOutputValue("BGS_STOCH_VAR_Y", log10(solver[TURB_SOL]->GetRes_BGS(2))); + SetHistoryOutputValue("BGS_STOCH_VAR_Z", log10(solver[TURB_SOL]->GetRes_BGS(3))); + } } break; @@ -1483,6 +1512,12 @@ void CFlowOutput::SetVolumeOutputFieldsScalarMisc(const CConfig* config) { if (config->GetKind_HybridRANSLES() != NO_HYBRIDRANSLES) { AddVolumeOutput("DES_LENGTHSCALE", "DES_LengthScale", "DDES", "DES length scale value"); AddVolumeOutput("WALL_DISTANCE", "Wall_Distance", "DDES", "Wall distance value"); + AddVolumeOutput("LES_SENSOR","LES_Sensor","DDES","LES sensor value"); + if (config->GetStochastic_Backscatter()) { + AddVolumeOutput("STOCHASTIC_VAR_X", "Stochastic_Var_X", "BACKSCATTER", "x-component of the stochastic vector potential"); + AddVolumeOutput("STOCHASTIC_VAR_Y", "Stochastic_Var_Y", "BACKSCATTER", "y-component of the stochastic vector potential"); + AddVolumeOutput("STOCHASTIC_VAR_Z", "Stochastic_Var_Z", "BACKSCATTER", "z-component of the stochastic vector potential"); + } } if (config->GetViscous()) { @@ -1582,6 +1617,12 @@ void CFlowOutput::LoadVolumeDataScalar(const CConfig* config, const CSolver* con if (config->GetKind_HybridRANSLES() != NO_HYBRIDRANSLES) { SetVolumeOutputValue("DES_LENGTHSCALE", iPoint, Node_Flow->GetDES_LengthScale(iPoint)); SetVolumeOutputValue("WALL_DISTANCE", iPoint, Node_Geo->GetWall_Distance(iPoint)); + SetVolumeOutputValue("LES_SENSOR", iPoint, Node_Flow->GetLES_Mode(iPoint)); + if (config->GetStochastic_Backscatter()) { + SetVolumeOutputValue("STOCHASTIC_VAR_X", iPoint, Node_Turb->GetSolution(iPoint, 1)); + SetVolumeOutputValue("STOCHASTIC_VAR_Y", iPoint, Node_Turb->GetSolution(iPoint, 2)); + SetVolumeOutputValue("STOCHASTIC_VAR_Z", iPoint, Node_Turb->GetSolution(iPoint, 3)); + } } switch (config->GetKind_Species_Model()) { diff --git a/SU2_CFD/src/solvers/CIncNSSolver.cpp b/SU2_CFD/src/solvers/CIncNSSolver.cpp index 30f0efd46d98..7c4955bf93e6 100644 --- a/SU2_CFD/src/solvers/CIncNSSolver.cpp +++ b/SU2_CFD/src/solvers/CIncNSSolver.cpp @@ -282,7 +282,7 @@ void CIncNSSolver::Viscous_Residual(unsigned long iEdge, CGeometry *geometry, CS unsigned long CIncNSSolver::SetPrimitive_Variables(CSolver **solver_container, const CConfig *config) { unsigned long iPoint, nonPhysicalPoints = 0; - su2double eddy_visc = 0.0, turb_ke = 0.0, DES_LengthScale = 0.0; + su2double eddy_visc = 0.0, turb_ke = 0.0, DES_LengthScale = 0.0, LES_Mode = 0.0; const su2double* scalar = nullptr; const TURB_MODEL turb_model = config->GetKind_Turb_Model(); const SPECIES_MODEL species_model = config->GetKind_Species_Model(); @@ -302,6 +302,7 @@ unsigned long CIncNSSolver::SetPrimitive_Variables(CSolver **solver_container, c if (config->GetKind_HybridRANSLES() != NO_HYBRIDRANSLES){ DES_LengthScale = solver_container[TURB_SOL]->GetNodes()->GetDES_LengthScale(iPoint); + LES_Mode = solver_container[TURB_SOL]->GetNodes()->GetLES_Mode(iPoint); } } @@ -322,6 +323,10 @@ unsigned long CIncNSSolver::SetPrimitive_Variables(CSolver **solver_container, c nodes->SetDES_LengthScale(iPoint,DES_LengthScale); + /*--- Set the LES sensor ---*/ + + nodes->SetLES_Mode(iPoint, LES_Mode); + } END_SU2_OMP_FOR diff --git a/SU2_CFD/src/solvers/CNSSolver.cpp b/SU2_CFD/src/solvers/CNSSolver.cpp index 2513cae7f34d..97be3482491e 100644 --- a/SU2_CFD/src/solvers/CNSSolver.cpp +++ b/SU2_CFD/src/solvers/CNSSolver.cpp @@ -151,6 +151,8 @@ unsigned long CNSSolver::SetPrimitive_Variables(CSolver **solver_container, cons if (config->GetKind_HybridRANSLES() != NO_HYBRIDRANSLES) { su2double DES_LengthScale = solver_container[TURB_SOL]->GetNodes()->GetDES_LengthScale(iPoint); nodes->SetDES_LengthScale(iPoint, DES_LengthScale); + su2double LES_Mode = solver_container[TURB_SOL]->GetNodes()->GetLES_Mode(iPoint); + nodes->SetLES_Mode(iPoint, LES_Mode); } } diff --git a/SU2_CFD/src/solvers/CTurbSASolver.cpp b/SU2_CFD/src/solvers/CTurbSASolver.cpp index 7649165d0be3..c76b819f7909 100644 --- a/SU2_CFD/src/solvers/CTurbSASolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp @@ -30,6 +30,7 @@ #include "../../include/variables/CFlowVariable.hpp" #include "../../../Common/include/parallelization/omp_structure.hpp" #include "../../../Common/include/toolboxes/geometry_toolbox.hpp" +#include "../../../Common/include/toolboxes/random_toolbox.hpp" CTurbSASolver::CTurbSASolver(CGeometry *geometry, CConfig *config, unsigned short iMesh, CFluidModel* FluidModel) @@ -46,6 +47,11 @@ CTurbSASolver::CTurbSASolver(CGeometry *geometry, CConfig *config, unsigned shor nPoint = geometry->GetnPoint(); nPointDomain = geometry->GetnPointDomain(); + /*--- Add Langevin equations if the Stochastic Backscatter Model is used ---*/ + + bool backscatter = config->GetStochastic_Backscatter(); + if (backscatter) { nVar += 3; nPrimVar += 3; } + /*--- Initialize nVarGrad for deallocation ---*/ nVarGrad = nVar; @@ -377,6 +383,24 @@ void CTurbSASolver::Source_Residual(CGeometry *geometry, CSolver **solver_contai /*--- Set DES length scale ---*/ numerics->SetDistance(nodes->GetDES_LengthScale(iPoint), 0.0); + numerics->SetLESSensor(nodes->GetLES_Mode(iPoint), 0.0); + + /*--- Compute source terms in Langevin equations (Stochastic Basckscatter Model) ---*/ + + bool backscatter = config->GetStochastic_Backscatter(); + if (backscatter) { + su2double currentTime = config->GetPhysicalTime(); + su2double lastTime = numerics->GetLastTime(); + if (currentTime != lastTime) { + numerics->SetLastTime(currentTime); + su2double randomSource; + for (unsigned short iDim = 0; iDim < 3; iDim++) { + unsigned long seed = RandomToolbox::GetSeed(currentTime); + randomSource = RandomToolbox::GetRandomNormal(seed, 0.0, 1.0); + numerics->SetStochSource(randomSource, iDim); + } + } + } } @@ -402,6 +426,10 @@ void CTurbSASolver::Source_Residual(CGeometry *geometry, CSolver **solver_contai nodes->SetIntermittency(iPoint,numerics->GetIntermittencyEff()); } + /*--- Store stochastic variables (Stochastic Backscatter Model) ---*/ + + + /*--- Subtract residual and the Jacobian ---*/ LinSysRes.SubtractBlock(iPoint, residual); @@ -558,7 +586,7 @@ void CTurbSASolver::BC_Inlet(CGeometry *geometry, CSolver **solver_container, CN conv_numerics->SetPrimitive(V_domain, V_inlet); /*--- Non-dimensionalize Inlet_TurbVars if Inlet-Files are used. ---*/ - su2double Inlet_Vars[MAXNVAR]; + su2double Inlet_Vars[MAXNVAR] = {0.0}; Inlet_Vars[0] = Inlet_TurbVars[val_marker][iVertex][0]; if (config->GetInlet_Profile_From_File()) { Inlet_Vars[0] *= config->GetDensity_Ref() / config->GetViscosity_Ref(); @@ -1396,7 +1424,7 @@ void CTurbSASolver::SetDES_LengthScale(CSolver **solver, CGeometry *geometry, CC su2double psi_2 = (1.0 - (cb1/(cw1*k2*fw_star))*(ft2 + (1.0 - ft2)*fv2))/(fv1 * max(1.0e-10,1.0-ft2)); psi_2 = min(100.0,psi_2); - su2double lengthScale = 0.0; + su2double lengthScale = 0.0, lesSensor = 0.0; switch(kindHybridRANSLES){ case SA_DES: { @@ -1408,6 +1436,7 @@ void CTurbSASolver::SetDES_LengthScale(CSolver **solver, CGeometry *geometry, CC const su2double maxDelta = geometry->nodes->GetMaxLength(iPoint); const su2double distDES = constDES * maxDelta; lengthScale = min(distDES,wallDistance); + lesSensor = (wallDistance<=distDES) ? 0.0 : 1.0; break; } @@ -1424,6 +1453,7 @@ void CTurbSASolver::SetDES_LengthScale(CSolver **solver, CGeometry *geometry, CC const su2double distDES = constDES * maxDelta; lengthScale = wallDistance-f_d*max(0.0,(wallDistance-distDES)); + lesSensor = (wallDistance<=distDES) ? 0.0 : f_d; break; } @@ -1464,6 +1494,7 @@ void CTurbSASolver::SetDES_LengthScale(CSolver **solver, CGeometry *geometry, CC const su2double distDES = constDES * maxDelta; lengthScale = wallDistance-f_d*max(0.0,(wallDistance-distDES)); + lesSensor = (wallDistance<=distDES) ? 0.0 : f_d; break; } @@ -1516,12 +1547,14 @@ void CTurbSASolver::SetDES_LengthScale(CSolver **solver, CGeometry *geometry, CC const su2double distDES = constDES * maxDelta; lengthScale = wallDistance-f_d*max(0.0,(wallDistance-distDES)); + lesSensor = (wallDistance<=distDES) ? 0.0 : f_d; break; } } nodes->SetDES_LengthScale(iPoint, lengthScale); + nodes->SetLES_Mode(iPoint, lesSensor); } END_SU2_OMP_FOR diff --git a/SU2_CFD/src/variables/CIncNSVariable.cpp b/SU2_CFD/src/variables/CIncNSVariable.cpp index 135851675045..ff2d8bea0d3d 100644 --- a/SU2_CFD/src/variables/CIncNSVariable.cpp +++ b/SU2_CFD/src/variables/CIncNSVariable.cpp @@ -36,6 +36,7 @@ CIncNSVariable::CIncNSVariable(su2double pressure, const su2double *velocity, su StrainMag.resize(nPoint); Tau_Wall.resize(nPoint) = su2double(-1.0); DES_LengthScale.resize(nPoint) = su2double(0.0); + LES_Mode.resize(nPoint) = su2double(0.0); Max_Lambda_Visc.resize(nPoint); /*--- Allocate memory for the AuxVar and its gradient. See e.g. CIncEulerSolver::Source_Residual: * Axisymmetric: total-viscosity * y-vel / y-coord diff --git a/SU2_CFD/src/variables/CNEMONSVariable.cpp b/SU2_CFD/src/variables/CNEMONSVariable.cpp index e673ad5603b7..b379ce2752df 100644 --- a/SU2_CFD/src/variables/CNEMONSVariable.cpp +++ b/SU2_CFD/src/variables/CNEMONSVariable.cpp @@ -70,6 +70,7 @@ CNEMONSVariable::CNEMONSVariable(su2double val_pressure, StrainMag.resize(nPoint) = su2double(0.0); Tau_Wall.resize(nPoint) = su2double(-1.0); DES_LengthScale.resize(nPoint) = su2double(0.0); + LES_Mode.resize(nPoint) = su2double(0.0); Roe_Dissipation.resize(nPoint) = su2double(0.0); Vortex_Tilting.resize(nPoint) = su2double(0.0); Max_Lambda_Visc.resize(nPoint) = su2double(0.0); diff --git a/SU2_CFD/src/variables/CNSVariable.cpp b/SU2_CFD/src/variables/CNSVariable.cpp index 6c360efc995e..753f7b4feebf 100644 --- a/SU2_CFD/src/variables/CNSVariable.cpp +++ b/SU2_CFD/src/variables/CNSVariable.cpp @@ -39,6 +39,7 @@ CNSVariable::CNSVariable(su2double density, const su2double *velocity, su2double StrainMag.resize(nPoint) = su2double(0.0); Tau_Wall.resize(nPoint) = su2double(-1.0); DES_LengthScale.resize(nPoint) = su2double(0.0); + LES_Mode.resize(nPoint) = su2double(0.0); Roe_Dissipation.resize(nPoint) = su2double(0.0); Vortex_Tilting.resize(nPoint) = su2double(0.0); Max_Lambda_Visc.resize(nPoint) = su2double(0.0); diff --git a/SU2_CFD/src/variables/CTurbSAVariable.cpp b/SU2_CFD/src/variables/CTurbSAVariable.cpp index 75119c89ed5f..6b42a4d9106a 100644 --- a/SU2_CFD/src/variables/CTurbSAVariable.cpp +++ b/SU2_CFD/src/variables/CTurbSAVariable.cpp @@ -27,13 +27,27 @@ #include "../../include/variables/CTurbSAVariable.hpp" +#include "random" CTurbSAVariable::CTurbSAVariable(su2double val_nu_tilde, su2double val_muT, unsigned long npoint, unsigned long ndim, unsigned long nvar, CConfig *config) : CTurbVariable(npoint, ndim, nvar, config) { - Solution_Old = Solution = val_nu_tilde; + /*--- Initialize solution (check if the Stochastic Backscatter Model is active) ---*/ + bool backscatter = config->GetStochastic_Backscatter(); + if (!backscatter) { + Solution_Old = Solution = val_nu_tilde; + } else { + std::default_random_engine gen(std::random_device{}()); + std::normal_distribution rnd(0.0,1.0); + for (unsigned long iPoint = 0; iPoint < npoint; iPoint++) { + Solution_Old(iPoint, 0) = Solution(iPoint, 0) = val_nu_tilde; + for (unsigned short iVar = 1; iVar < nvar; iVar++) { + Solution_Old(iPoint, iVar) = Solution(iPoint, iVar) = rnd(gen); + } + } + } muT.resize(nPoint) = val_muT; @@ -47,6 +61,7 @@ CTurbSAVariable::CTurbSAVariable(su2double val_nu_tilde, su2double val_muT, unsi } DES_LengthScale.resize(nPoint) = su2double(0.0); + LES_Mode.resize(nPoint) = su2double(0.0); Vortex_Tilting.resize(nPoint); } From 34f676f2b6c425a4bc25810e685942d10f480c3d Mon Sep 17 00:00:00 2001 From: Angelo Passariello Date: Wed, 24 Sep 2025 11:05:02 +0200 Subject: [PATCH 03/25] Fix sanitizer warnings and regression failures Fix sanitizer warnings and regression test failures - Address issues reported by clang sanitizers. - Adjust implementation to ensure regression tests pass. --- Common/include/toolboxes/random_toolbox.hpp | 21 +++++--------- SU2_CFD/include/numerics/CNumerics.hpp | 15 +++++----- .../numerics/turbulent/turb_convection.hpp | 11 +++++-- .../include/solvers/CFVMFlowSolverBase.inl | 8 ++--- SU2_CFD/src/output/CFlowOutput.cpp | 12 ++++---- SU2_CFD/src/solvers/CTurbSASolver.cpp | 29 +++++++++++++++---- SU2_CFD/src/variables/CTurbSAVariable.cpp | 11 +++---- 7 files changed, 62 insertions(+), 45 deletions(-) diff --git a/Common/include/toolboxes/random_toolbox.hpp b/Common/include/toolboxes/random_toolbox.hpp index d7300f2c950a..7d9a3a85608e 100644 --- a/Common/include/toolboxes/random_toolbox.hpp +++ b/Common/include/toolboxes/random_toolbox.hpp @@ -49,22 +49,17 @@ inline uint64_t HashCombine(uint64_t v1, uint64_t v2) { /*! * \brief Convert a double to a 64-bit integer suitable for hashing. * \param[in] x Double to integer. - * \return Hash value of the double. + * \return Hash value of the double (not portable). */ -inline uint64_t ToUInt64(double x) { - return std::hash{}(x); -} +inline uint64_t ToUInt64(double x) { return std::hash{}(x); } /*! * \brief Build a deterministic seed from physical time. - * \param[in] time Physical time. + * \param[in] x First integer value. + * \param[in] y Second integer value. * \return 64-bit seed value. */ -inline uint64_t GetSeed(double x) { - uint64_t h = 1469598103934665603ULL; // Offset - h = HashCombine(h, ToUInt64(x)); - return h; -} +inline uint64_t GetSeed(uint64_t x, uint64_t y) { return HashCombine(x, y); } /*! * \brief Generate a standard normally-distributed random number. @@ -73,10 +68,8 @@ inline uint64_t GetSeed(double x) { * \param[in] stddev Standard deviation of the normal distribution (default 1). * \return Normally-distributed random number. */ -inline double GetRandomNormal(uint64_t seed, - double mean = 0.0, - double stddev = 1.0) { - std::mt19937 gen(static_cast(seed)); +inline double GetRandomNormal(uint64_t seed, double mean = 0.0, double stddev = 1.0) { + std::mt19937_64 gen(seed); std::normal_distribution rnd(mean, stddev); return rnd(gen); } diff --git a/SU2_CFD/include/numerics/CNumerics.hpp b/SU2_CFD/include/numerics/CNumerics.hpp index fff3ed7b9053..b35c49596e60 100644 --- a/SU2_CFD/include/numerics/CNumerics.hpp +++ b/SU2_CFD/include/numerics/CNumerics.hpp @@ -184,11 +184,11 @@ class CNumerics { su2double lesSensor_i, /*!< \brief LES sensor at point i. */ lesSensor_j; /*!< \brief LES sensor at point j. */ - su2double lastTime; /*!< \brief Physical time of unsteady simulation. */ + unsigned long lastTime; /*!< \brief Physical time iteration of unsteady simulation. */ su2double stochSource[3]; /*!< \brief Source term for Langevin equations in Stochastic Backscatter Model. */ - const su2double - *stochVar_i, /*!< \brief Stochastic variables at point i for Stochastic Backscatter Model. */ - *stochVar_j; /*!< \brief Stochastic variables at point j for Stochastic Backscatter Model. */ + su2double + stochVar_i[3], /*!< \brief Stochastic variables at point i for Stochastic Backscatter Model. */ + stochVar_j[3]; /*!< \brief Stochastic variables at point j for Stochastic Backscatter Model. */ SST_ParsedOptions sstParsedOptions; /*!< \brief additional options for the SST turbulence model */ unsigned short Eig_Val_Comp; /*!< \brief Component towards which perturbation is perfromed */ su2double uq_delta_b; /*!< \brief Magnitude of perturbation */ @@ -875,10 +875,11 @@ class CNumerics { * \brief Set the stochastic variables from Langevin equations (Stochastic Backscatter Model). * \param[in] val_stochvar_i - Value of the stochastic variable at point i. * \param[in] val_stochvar_j - Value of the stochastic variable at point j. + * \param[in] iDim - Index of Langevin equation. */ - inline void SetStochVar(su2double *val_stochvar_i, su2double *val_stochvar_j) { - stochVar_i = val_stochvar_i; - stochVar_j = val_stochvar_j; + inline void SetStochVar(su2double val_stochvar_i, su2double val_stochvar_j, unsigned short iDim) { + stochVar_i[iDim] = val_stochvar_i; + stochVar_j[iDim] = val_stochvar_j; } /*! diff --git a/SU2_CFD/include/numerics/turbulent/turb_convection.hpp b/SU2_CFD/include/numerics/turbulent/turb_convection.hpp index 2b5954ae22b7..fe829cc054bc 100644 --- a/SU2_CFD/include/numerics/turbulent/turb_convection.hpp +++ b/SU2_CFD/include/numerics/turbulent/turb_convection.hpp @@ -48,6 +48,10 @@ class CUpwSca_TurbSA final : public CUpwScalar { using Base::ScalarVar_i; using Base::ScalarVar_j; using Base::bounded_scalar; + using Base::V_i; + using Base::V_j; + using Base::idx; + using Base::nVar; /*! * \brief Adds any extra variables to AD. @@ -65,9 +69,10 @@ class CUpwSca_TurbSA final : public CUpwScalar { Jacobian_i[0][0] = a0; Jacobian_j[0][0] = a1; } else { - for (unsigned short iVar = 0; iVar < 4; iVar++) { - Flux[iVar] = a0*ScalarVar_i[iVar] + a1*ScalarVar_j[iVar]; - for (unsigned short jVar = 0; jVar < 4; jVar++) { + Flux[0] = a0*ScalarVar_i[0] + a1*ScalarVar_j[0]; + for (unsigned short iVar = 1; iVar < nVar; iVar++) { + Flux[iVar] = a0*V_i[idx.Density()]*ScalarVar_i[iVar] + a1*V_j[idx.Density()]*ScalarVar_j[iVar]; + for (unsigned short jVar = 0; jVar < nVar; jVar++) { Jacobian_i[iVar][jVar] = (iVar == jVar) ? a0 : 0.0; Jacobian_j[iVar][jVar] = (iVar == jVar) ? a1 : 0.0; } diff --git a/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl b/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl index 13236680ac23..51db989405e7 100644 --- a/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl +++ b/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl @@ -470,12 +470,10 @@ void CFVMFlowSolverBase::Viscous_Residual_impl(unsigned long iEdge, CGeome /*--- Stochastic variables from Langevin equations (Stochastic Backscatter Model). ---*/ if (backscatter) { - su2double stochVars_i [3], stochVars_j [3]; - for (unsigned short iVar = 1; iVar < 4; iVar++) { - stochVars_i[iVar-1] = turbNodes->GetSolution(iPoint, iVar); - stochVars_j[iVar-1] = turbNodes->GetSolution(jPoint, iVar); + for (unsigned short iDim = 0; iDim < nDim; iDim++) { + numerics->SetStochVar(turbNodes->GetSolution(iPoint, 1 + iDim), + turbNodes->GetSolution(jPoint, 1 + iDim), iDim); } - numerics->SetStochVar(stochVars_i, stochVars_j); } /*--- Wall shear stress values (wall functions) ---*/ diff --git a/SU2_CFD/src/output/CFlowOutput.cpp b/SU2_CFD/src/output/CFlowOutput.cpp index d3f2bd02b9fd..d1ea32696349 100644 --- a/SU2_CFD/src/output/CFlowOutput.cpp +++ b/SU2_CFD/src/output/CFlowOutput.cpp @@ -966,11 +966,11 @@ void CFlowOutput::AddHistoryOutputFields_ScalarRMS_RES(const CConfig* config) { AddHistoryOutput("RMS_NU_TILDE", "rms[nu]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of nu tilde (SA model).", HistoryFieldType::RESIDUAL); if (config->GetStochastic_Backscatter()) { /// DESCRIPTION: Root-mean square residual of stochastic vector x-component (Stochastic Backscatter Model). - AddHistoryOutput("RMS_STOCH_VAR_X", "rms[stoch_var_x]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of stochastic vector x-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); + AddHistoryOutput("RMS_STOCH_VAR_X", "rms[stoch_x]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of stochastic vector x-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); /// DESCRIPTION: Root-mean square residual of stochastic vector y-component (Stochastic Backscatter Model). - AddHistoryOutput("RMS_STOCH_VAR_Y", "rms[stoch_var_y]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of stochastic vector y-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); + AddHistoryOutput("RMS_STOCH_VAR_Y", "rms[stoch_y]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of stochastic vector y-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); /// DESCRIPTION: Root-mean square residual of stochastic vector x-component (Stochastic Backscatter Model). - AddHistoryOutput("RMS_STOCH_VAR_Z", "rms[stoch_var_z]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of stochastic vector z-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); + AddHistoryOutput("RMS_STOCH_VAR_Z", "rms[stoch_z]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of stochastic vector z-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); } break; @@ -1029,11 +1029,11 @@ void CFlowOutput::AddHistoryOutputFields_ScalarMAX_RES(const CConfig* config) { AddHistoryOutput("MAX_NU_TILDE", "max[nu]", ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of nu tilde (SA model).", HistoryFieldType::RESIDUAL); if (config->GetStochastic_Backscatter()) { /// DESCRIPTION: Maximum residual of stochastic vector x-component (Stochastic Backscatter Model). - AddHistoryOutput("MAX_STOCH_VAR_X", "max[stoch_var_x]", ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of stochastic vector x-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); + AddHistoryOutput("MAX_STOCH_VAR_X", "max[stoch_x]", ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of stochastic vector x-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); /// DESCRIPTION: Maximum residual of stochastic vector y-component (Stochastic Backscatter Model). - AddHistoryOutput("MAX_STOCH_VAR_Y", "max[stoch_var_y]", ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of stochastic vector y-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); + AddHistoryOutput("MAX_STOCH_VAR_Y", "max[stoch_y]", ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of stochastic vector y-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); /// DESCRIPTION: Maximum residual of stochastic vector z-component (Stochastic Backscatter Model). - AddHistoryOutput("MAX_STOCH_VAR_Z", "max[stoch_var_z]", ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of stochastic vector z-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); + AddHistoryOutput("MAX_STOCH_VAR_Z", "max[stoch_z]", ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of stochastic vector z-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); } break; diff --git a/SU2_CFD/src/solvers/CTurbSASolver.cpp b/SU2_CFD/src/solvers/CTurbSASolver.cpp index c76b819f7909..1d6e6fc5e7de 100644 --- a/SU2_CFD/src/solvers/CTurbSASolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp @@ -60,6 +60,12 @@ CTurbSASolver::CTurbSASolver(CGeometry *geometry, CConfig *config, unsigned shor nDim = geometry->GetnDim(); + /*--- Check if Stochastic Backscatter Model can be used ---*/ + + if (backscatter && nDim != 3) { + SU2_MPI::Error("The Stochastic Backscatter Model can be used for three-dimensional problems only.", CURRENT_FUNCTION); + } + /*--- Single grid simulation ---*/ if (iMesh == MESH_0 || config->GetMGCycle() == FULLMG_CYCLE) { @@ -115,6 +121,11 @@ CTurbSASolver::CTurbSASolver(CGeometry *geometry, CConfig *config, unsigned shor } Solution_Inf[0] = nu_tilde_Inf; + if (backscatter) { + for (unsigned short iVar = 1; iVar < nVar; iVar++) { + Solution_Inf[iVar] = 0.0; // Backscatter variables initialized in CTurbSAVariable.hpp + } + } /*--- Factor_nu_Engine ---*/ Factor_nu_Engine = config->GetNuFactor_Engine(); @@ -161,8 +172,16 @@ CTurbSASolver::CTurbSASolver(CGeometry *geometry, CConfig *config, unsigned shor * due to arbitrary number of turbulence variables ---*/ Inlet_TurbVars.resize(nMarker); - for (unsigned long iMarker = 0; iMarker < nMarker; iMarker++) + for (unsigned long iMarker = 0; iMarker < nMarker; iMarker++) { Inlet_TurbVars[iMarker].resize(nVertex[iMarker],nVar) = nu_tilde_Inf; + if (backscatter) { + for (unsigned long iVertex = 0; iVertex < nVertex[iMarker]; iVertex++) { + for (unsigned short iVar = 1; iVar < nVar; iVar++) { + Inlet_TurbVars[iMarker](iVertex,iVar) = 0.0; + } + } + } + } /*--- Store the initial CFL number for all grid points. ---*/ @@ -389,14 +408,14 @@ void CTurbSASolver::Source_Residual(CGeometry *geometry, CSolver **solver_contai bool backscatter = config->GetStochastic_Backscatter(); if (backscatter) { - su2double currentTime = config->GetPhysicalTime(); - su2double lastTime = numerics->GetLastTime(); + uint64_t currentTime = static_cast(config->GetTimeIter()); + uint64_t lastTime = static_cast(numerics->GetLastTime()); if (currentTime != lastTime) { numerics->SetLastTime(currentTime); su2double randomSource; for (unsigned short iDim = 0; iDim < 3; iDim++) { - unsigned long seed = RandomToolbox::GetSeed(currentTime); - randomSource = RandomToolbox::GetRandomNormal(seed, 0.0, 1.0); + uint64_t seed = RandomToolbox::GetSeed(currentTime, iDim+1); + randomSource = RandomToolbox::GetRandomNormal(seed); numerics->SetStochSource(randomSource, iDim); } } diff --git a/SU2_CFD/src/variables/CTurbSAVariable.cpp b/SU2_CFD/src/variables/CTurbSAVariable.cpp index 6b42a4d9106a..5f4a58b84e01 100644 --- a/SU2_CFD/src/variables/CTurbSAVariable.cpp +++ b/SU2_CFD/src/variables/CTurbSAVariable.cpp @@ -27,7 +27,7 @@ #include "../../include/variables/CTurbSAVariable.hpp" -#include "random" +#include "../../../Common/include/toolboxes/random_toolbox.hpp" CTurbSAVariable::CTurbSAVariable(su2double val_nu_tilde, su2double val_muT, unsigned long npoint, @@ -39,12 +39,13 @@ CTurbSAVariable::CTurbSAVariable(su2double val_nu_tilde, su2double val_muT, unsi if (!backscatter) { Solution_Old = Solution = val_nu_tilde; } else { - std::default_random_engine gen(std::random_device{}()); - std::normal_distribution rnd(0.0,1.0); for (unsigned long iPoint = 0; iPoint < npoint; iPoint++) { Solution_Old(iPoint, 0) = Solution(iPoint, 0) = val_nu_tilde; - for (unsigned short iVar = 1; iVar < nvar; iVar++) { - Solution_Old(iPoint, iVar) = Solution(iPoint, iVar) = rnd(gen); + for (unsigned long iVar = 1; iVar < nvar; iVar++) { + uint64_t seed = RandomToolbox::GetSeed( + static_cast(config->GetTimeIter()+1), + static_cast(iPoint*nvar + iVar)); + Solution_Old(iPoint, iVar) = Solution(iPoint, iVar) = RandomToolbox::GetRandomNormal(seed); } } } From 66293fdc667d2027bda91ccbf6fcec40bd062f01 Mon Sep 17 00:00:00 2001 From: Angelo Passariello Date: Wed, 24 Sep 2025 12:51:28 +0200 Subject: [PATCH 04/25] Minor fixes - Enhance readability. - Ensure consistent declaration types. --- SU2_CFD/include/numerics/CNumerics.hpp | 2 +- .../include/numerics/turbulent/turb_convection.hpp | 12 ++++++------ SU2_CFD/src/solvers/CTurbSASolver.cpp | 4 ---- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/SU2_CFD/include/numerics/CNumerics.hpp b/SU2_CFD/include/numerics/CNumerics.hpp index b35c49596e60..6e2c636dffdf 100644 --- a/SU2_CFD/include/numerics/CNumerics.hpp +++ b/SU2_CFD/include/numerics/CNumerics.hpp @@ -906,7 +906,7 @@ class CNumerics { * \brief Set the value of physical time. * \param[in] val_last_time - Value of physical time. */ - void SetLastTime(su2double val_last_time) { + void SetLastTime(unsigned long val_last_time) { lastTime = val_last_time; } diff --git a/SU2_CFD/include/numerics/turbulent/turb_convection.hpp b/SU2_CFD/include/numerics/turbulent/turb_convection.hpp index fe829cc054bc..711934b518fe 100644 --- a/SU2_CFD/include/numerics/turbulent/turb_convection.hpp +++ b/SU2_CFD/include/numerics/turbulent/turb_convection.hpp @@ -63,15 +63,15 @@ class CUpwSca_TurbSA final : public CUpwScalar { * \param[in] config - Definition of the particular problem. */ void FinishResidualCalc(const CConfig* config) override { + Flux[0] = a0*ScalarVar_i[0] + a1*ScalarVar_j[0]; + Jacobian_i[0][0] = a0; + Jacobian_j[0][0] = a1; bool backscatter = config->GetStochastic_Backscatter(); - if (!backscatter) { - Flux[0] = a0*ScalarVar_i[0] + a1*ScalarVar_j[0]; - Jacobian_i[0][0] = a0; - Jacobian_j[0][0] = a1; - } else { - Flux[0] = a0*ScalarVar_i[0] + a1*ScalarVar_j[0]; + if (backscatter) { for (unsigned short iVar = 1; iVar < nVar; iVar++) { Flux[iVar] = a0*V_i[idx.Density()]*ScalarVar_i[iVar] + a1*V_j[idx.Density()]*ScalarVar_j[iVar]; + } + for (unsigned short iVar = 0; iVar < nVar; iVar++) { for (unsigned short jVar = 0; jVar < nVar; jVar++) { Jacobian_i[iVar][jVar] = (iVar == jVar) ? a0 : 0.0; Jacobian_j[iVar][jVar] = (iVar == jVar) ? a1 : 0.0; diff --git a/SU2_CFD/src/solvers/CTurbSASolver.cpp b/SU2_CFD/src/solvers/CTurbSASolver.cpp index 1d6e6fc5e7de..825e6faf7477 100644 --- a/SU2_CFD/src/solvers/CTurbSASolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp @@ -444,10 +444,6 @@ void CTurbSASolver::Source_Residual(CGeometry *geometry, CSolver **solver_contai if (transition_BC || config->GetKind_Trans_Model() != TURB_TRANS_MODEL::NONE) { nodes->SetIntermittency(iPoint,numerics->GetIntermittencyEff()); } - - /*--- Store stochastic variables (Stochastic Backscatter Model) ---*/ - - /*--- Subtract residual and the Jacobian ---*/ From 3481df3267fff8908bbcbd43ecaf20a9ef6222ae Mon Sep 17 00:00:00 2001 From: Angelo Passariello Date: Fri, 26 Sep 2025 09:33:06 +0200 Subject: [PATCH 05/25] Ensure consistency with DES - Extend implementation to 2D configurations. - Improve robustness. --- SU2_CFD/include/numerics/CNumerics.hpp | 2 +- .../include/numerics/flow/flow_diffusion.hpp | 2 +- .../numerics/turbulent/turb_sources.hpp | 67 ++++++++++++------- SU2_CFD/src/numerics/flow/flow_diffusion.cpp | 6 +- SU2_CFD/src/output/CFlowOutput.cpp | 16 ++--- SU2_CFD/src/solvers/CTurbSASolver.cpp | 12 +--- 6 files changed, 60 insertions(+), 45 deletions(-) diff --git a/SU2_CFD/include/numerics/CNumerics.hpp b/SU2_CFD/include/numerics/CNumerics.hpp index 6e2c636dffdf..6d8551135d48 100644 --- a/SU2_CFD/include/numerics/CNumerics.hpp +++ b/SU2_CFD/include/numerics/CNumerics.hpp @@ -914,7 +914,7 @@ class CNumerics { * \brief Get the value of physical time. * \param[out] lastTime - Value of physical time. */ - inline su2double GetLastTime() const { return lastTime; } + inline unsigned long GetLastTime() const { return lastTime; } /*! * \brief Set the stochastic source term for the Langevin equations (Backscatter Model). diff --git a/SU2_CFD/include/numerics/flow/flow_diffusion.hpp b/SU2_CFD/include/numerics/flow/flow_diffusion.hpp index 55cf22d5e8ff..b5bfc716afd0 100644 --- a/SU2_CFD/include/numerics/flow/flow_diffusion.hpp +++ b/SU2_CFD/include/numerics/flow/flow_diffusion.hpp @@ -61,7 +61,7 @@ class CAvgGrad_Base : public CNumerics { Mean_Cp, /*!< \brief Mean value of the specific heat capacity at constant pressure. */ Mean_turb_ke, /*!< \brief Mean value of the turbulent kinetic energy. */ Mean_TauWall, /*!< \brief Mean wall shear stress (wall functions). */ - *Mean_StochVar, /*!< \brief Mean stochastic variables (Stochastic Backscatter Model). */ + Mean_StochVar[3], /*!< \brief Mean stochastic variables (Stochastic Backscatter Model). */ TauWall_i, TauWall_j, /*!< \brief Wall shear stress at point i and j (wall functions). */ dist_ij_2, /*!< \brief Length of the edge and face, squared */ Edge_Vector[MAXNDIM] = {0.0}, /*!< \brief Vector from point i to point j. */ diff --git a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp index 27dfabb07fe0..970c7b6d555c 100644 --- a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp +++ b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp @@ -74,8 +74,8 @@ class CSourceBase_TurbSA : public CNumerics { /*--- Residual and Jacobian ---*/ su2double Residual, *Jacobian_i; su2double Jacobian_Buffer; /*!< \brief Static storage for the Jacobian (which needs to be pointer for return type). */ - su2double ResidSB[4] = {0.0}, *JacobianSB_i[4]; - su2double JacobianSB_Buffer[16]; + su2double* ResidSB = nullptr; + su2double** JacobianSB_i = nullptr; const FlowIndices idx; /*!< \brief Object to manage the access to the flow primitives. */ const SA_ParsedOptions options; /*!< \brief Struct with SA options. */ @@ -139,29 +139,31 @@ class CSourceBase_TurbSA : public CNumerics { su2double scaleFactor = 1.0/tTurb * sqrt(2.0/tRat) * density * corrFac; ResidSB[0] = Residual; - for (unsigned short iDim = 1; iDim < 4; iDim++ ) { - ResidSB[iDim] = les_sensor * scaleFactor * stochSource[iDim-1] - - 1.0/tTurb * density * ScalarVar_i[iDim]; - ResidSB[iDim] *= Volume; + for (unsigned short iVar = 1; iVar < nVar; iVar++ ) { + ResidSB[iVar] = std::nearbyint(les_sensor) * scaleFactor * stochSource[iVar-1] - + 1.0/tTurb * density * ScalarVar_i[iVar]; + ResidSB[iVar] *= Volume; } - for (unsigned short iDim = 0; iDim < 4; iDim++ ) - for (unsigned short jDim = 0; jDim < 4; jDim++ ) - JacobianSB_i[iDim][jDim] = 0.0; + for (unsigned short iVar = 0; iVar < nVar; iVar++ ) { + for (unsigned short jVar = 0; jVar < nVar; jVar++ ) { + JacobianSB_i[iVar][jVar] = 0.0; + if (iVar == jVar) JacobianSB_i[iVar][jVar] = -1.0/tTurb * density * Volume; + } + } JacobianSB_i[0][0] = Jacobian_i[0]; - JacobianSB_i[1][1] = JacobianSB_i[2][2] = JacobianSB_i[3][3] = - 1.0/tTurb * density * Volume; - su2double dnut_dnue = var.fv1 + 3.0 * var.cv1_3 * Ji_3 / pow(Ji_3 + var.cv1_3, 2); +/* su2double dnut_dnue = var.fv1 + 3.0 * var.cv1_3 * Ji_3 / pow(Ji_3 + var.cv1_3, 2); su2double dtTurb_dnut = - ct * pow(lengthScale,2) / (max(nut, nut_small)*max(nut, nut_small)); - for (unsigned short iDim = 1; iDim < 4; iDim++ ) { - JacobianSB_i[iDim][0] = - 1.0/tTurb * les_sensor * scaleFactor * stochSource[iDim-1] - + 1.0/(tTurb*tTurb) * ScalarVar_i[iDim] - + density * les_sensor * corrFac * stochSource[iDim-1] / + for (unsigned short iVar = 1; iVar < nVar; iVar++ ) { + JacobianSB_i[iVar][0] = - 1.0/tTurb * les_sensor * scaleFactor * stochSource[iVar-1] + + 1.0/(tTurb*tTurb) * ScalarVar_i[iVar] + + density * les_sensor * corrFac * stochSource[iVar-1] / (tTurb * sqrt(2.0*tTurb*timeStep)); - JacobianSB_i[iDim][0] *= dtTurb_dnut * dnut_dnue * Volume; - } - } + JacobianSB_i[iVar][0] *= dtTurb_dnut * dnut_dnue * Volume; + } */ + } public: /*! @@ -170,7 +172,7 @@ class CSourceBase_TurbSA : public CNumerics { * \param[in] config - Definition of the particular problem. */ CSourceBase_TurbSA(unsigned short nDim, const CConfig* config) - : CNumerics(nDim, 1, config), + : CNumerics(nDim, config->GetStochastic_Backscatter() ? 1+nDim : 1, config), idx(nDim, config->GetnSpecies()), options(config->GetSAParsedOptions()), axisymmetric(config->GetAxisymmetric()), @@ -178,11 +180,30 @@ class CSourceBase_TurbSA : public CNumerics { /*--- Setup the Jacobian pointer, we need to return su2double** but we know * the Jacobian is 1x1 so we use this trick to avoid heap allocation. ---*/ Jacobian_i = &Jacobian_Buffer; - /*--- Setup the Jacobian pointer for Stochastic Backscatter Model. ---*/ - for (unsigned short iVar = 0; iVar < 4; iVar++) - JacobianSB_i[iVar] = JacobianSB_Buffer + 4*iVar; + /*--- Setup the Jacobian for Stochastic Backscatter Model. ---*/ + if (config->GetStochastic_Backscatter()) { + ResidSB = new su2double [nVar] (); + JacobianSB_i = new su2double* [nVar]; + for (unsigned short iVar = 0; iVar < nVar; iVar++ ) { + JacobianSB_i[iVar] = new su2double [nVar] (); + } + } } + /*! + * \brief Destructor of the class. + */ + ~CSourceBase_TurbSA() { + if (JacobianSB_i) { + for (unsigned short iVar = 0; iVar < nVar; iVar++) { + delete [] JacobianSB_i[iVar]; + } + delete [] JacobianSB_i; + } + if (ResidSB) { + delete [] ResidSB; + } + } /*! * \brief Residual for source term integration. @@ -323,7 +344,7 @@ class CSourceBase_TurbSA : public CNumerics { } AD::SetPreaccOut(Residual); - if (backscatter) { AD::SetPreaccOut(ResidSB, 4); } + if (backscatter) { AD::SetPreaccOut(ResidSB, nVar); } AD::EndPreacc(); if (backscatter) diff --git a/SU2_CFD/src/numerics/flow/flow_diffusion.cpp b/SU2_CFD/src/numerics/flow/flow_diffusion.cpp index c31d29106c5d..dc5c51b41f33 100644 --- a/SU2_CFD/src/numerics/flow/flow_diffusion.cpp +++ b/SU2_CFD/src/numerics/flow/flow_diffusion.cpp @@ -460,7 +460,7 @@ CNumerics::ResidualType<> CAvgGrad_Flow::ComputeResidual(const CConfig* config) /* --- If the Stochastic Backscatter Model is active, add random contribution to stress tensor ---*/ if (config->GetStochastic_Backscatter()) { - for (iVar = 0; iVar < 3; iVar++) + for (iVar = 0; iVar < nDim; iVar++) Mean_StochVar[iVar] = 0.5*(stochVar_i[iVar] + stochVar_j[iVar]); ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_Eddy_Viscosity, Mean_GradPrimVar+1, Mean_StochVar, stochReynStress); @@ -640,7 +640,7 @@ CNumerics::ResidualType<> CAvgGradInc_Flow::ComputeResidual(const CConfig* confi /* --- If the Stochastic Backscatter Model is active, add random contribution to stress tensor ---*/ if (config->GetStochastic_Backscatter()) { - for (iVar = 0; iVar < 3; iVar++) + for (iVar = 0; iVar < nDim; iVar++) Mean_StochVar[iVar] = 0.5*(stochVar_i[iVar] + stochVar_j[iVar]); ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_Eddy_Viscosity, Mean_GradPrimVar+1, Mean_StochVar, stochReynStress); @@ -965,7 +965,7 @@ CNumerics::ResidualType<> CGeneralAvgGrad_Flow::ComputeResidual(const CConfig* c /* --- If the Stochastic Backscatter Model is active, add random contribution to stress tensor ---*/ if (config->GetStochastic_Backscatter()) { - for (iVar = 0; iVar < 3; iVar++) + for (iVar = 0; iVar < nDim; iVar++) Mean_StochVar[iVar] = 0.5*(stochVar_i[iVar] + stochVar_j[iVar]); ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_Eddy_Viscosity, Mean_GradPrimVar+1, Mean_StochVar, stochReynStress); diff --git a/SU2_CFD/src/output/CFlowOutput.cpp b/SU2_CFD/src/output/CFlowOutput.cpp index d1ea32696349..aff75b2e3eb5 100644 --- a/SU2_CFD/src/output/CFlowOutput.cpp +++ b/SU2_CFD/src/output/CFlowOutput.cpp @@ -969,8 +969,8 @@ void CFlowOutput::AddHistoryOutputFields_ScalarRMS_RES(const CConfig* config) { AddHistoryOutput("RMS_STOCH_VAR_X", "rms[stoch_x]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of stochastic vector x-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); /// DESCRIPTION: Root-mean square residual of stochastic vector y-component (Stochastic Backscatter Model). AddHistoryOutput("RMS_STOCH_VAR_Y", "rms[stoch_y]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of stochastic vector y-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); - /// DESCRIPTION: Root-mean square residual of stochastic vector x-component (Stochastic Backscatter Model). - AddHistoryOutput("RMS_STOCH_VAR_Z", "rms[stoch_z]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of stochastic vector z-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); + /// DESCRIPTION: Root-mean square residual of stochastic vector z-component (Stochastic Backscatter Model). + if (nDim==3) AddHistoryOutput("RMS_STOCH_VAR_Z", "rms[stoch_z]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of stochastic vector z-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); } break; @@ -1033,7 +1033,7 @@ void CFlowOutput::AddHistoryOutputFields_ScalarMAX_RES(const CConfig* config) { /// DESCRIPTION: Maximum residual of stochastic vector y-component (Stochastic Backscatter Model). AddHistoryOutput("MAX_STOCH_VAR_Y", "max[stoch_y]", ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of stochastic vector y-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); /// DESCRIPTION: Maximum residual of stochastic vector z-component (Stochastic Backscatter Model). - AddHistoryOutput("MAX_STOCH_VAR_Z", "max[stoch_z]", ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of stochastic vector z-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); + if (nDim==3) AddHistoryOutput("MAX_STOCH_VAR_Z", "max[stoch_z]", ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of stochastic vector z-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); } break; @@ -1179,17 +1179,17 @@ void CFlowOutput::LoadHistoryDataScalar(const CConfig* config, const CSolver* co if (config->GetStochastic_Backscatter()) { SetHistoryOutputValue("RMS_STOCH_VAR_X", log10(solver[TURB_SOL]->GetRes_RMS(1))); SetHistoryOutputValue("RMS_STOCH_VAR_Y", log10(solver[TURB_SOL]->GetRes_RMS(2))); - SetHistoryOutputValue("RMS_STOCH_VAR_Z", log10(solver[TURB_SOL]->GetRes_RMS(3))); + if (nDim==3) SetHistoryOutputValue("RMS_STOCH_VAR_Z", log10(solver[TURB_SOL]->GetRes_RMS(3))); SetHistoryOutputValue("MAX_STOCH_VAR_X", log10(solver[TURB_SOL]->GetRes_Max(1))); SetHistoryOutputValue("MAX_STOCH_VAR_Y", log10(solver[TURB_SOL]->GetRes_Max(2))); - SetHistoryOutputValue("MAX_STOCH_VAR_Z", log10(solver[TURB_SOL]->GetRes_Max(3))); + if (nDim==3) SetHistoryOutputValue("MAX_STOCH_VAR_Z", log10(solver[TURB_SOL]->GetRes_Max(3))); } if (multiZone) { SetHistoryOutputValue("BGS_NU_TILDE", log10(solver[TURB_SOL]->GetRes_BGS(0))); if (config->GetStochastic_Backscatter()) { SetHistoryOutputValue("BGS_STOCH_VAR_X", log10(solver[TURB_SOL]->GetRes_BGS(1))); SetHistoryOutputValue("BGS_STOCH_VAR_Y", log10(solver[TURB_SOL]->GetRes_BGS(2))); - SetHistoryOutputValue("BGS_STOCH_VAR_Z", log10(solver[TURB_SOL]->GetRes_BGS(3))); + if (nDim==3) SetHistoryOutputValue("BGS_STOCH_VAR_Z", log10(solver[TURB_SOL]->GetRes_BGS(3))); } } break; @@ -1516,7 +1516,7 @@ void CFlowOutput::SetVolumeOutputFieldsScalarMisc(const CConfig* config) { if (config->GetStochastic_Backscatter()) { AddVolumeOutput("STOCHASTIC_VAR_X", "Stochastic_Var_X", "BACKSCATTER", "x-component of the stochastic vector potential"); AddVolumeOutput("STOCHASTIC_VAR_Y", "Stochastic_Var_Y", "BACKSCATTER", "y-component of the stochastic vector potential"); - AddVolumeOutput("STOCHASTIC_VAR_Z", "Stochastic_Var_Z", "BACKSCATTER", "z-component of the stochastic vector potential"); + if (nDim==3) AddVolumeOutput("STOCHASTIC_VAR_Z", "Stochastic_Var_Z", "BACKSCATTER", "z-component of the stochastic vector potential"); } } @@ -1621,7 +1621,7 @@ void CFlowOutput::LoadVolumeDataScalar(const CConfig* config, const CSolver* con if (config->GetStochastic_Backscatter()) { SetVolumeOutputValue("STOCHASTIC_VAR_X", iPoint, Node_Turb->GetSolution(iPoint, 1)); SetVolumeOutputValue("STOCHASTIC_VAR_Y", iPoint, Node_Turb->GetSolution(iPoint, 2)); - SetVolumeOutputValue("STOCHASTIC_VAR_Z", iPoint, Node_Turb->GetSolution(iPoint, 3)); + if (nDim==3) SetVolumeOutputValue("STOCHASTIC_VAR_Z", iPoint, Node_Turb->GetSolution(iPoint, 3)); } } diff --git a/SU2_CFD/src/solvers/CTurbSASolver.cpp b/SU2_CFD/src/solvers/CTurbSASolver.cpp index 825e6faf7477..659d35693b8c 100644 --- a/SU2_CFD/src/solvers/CTurbSASolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp @@ -47,11 +47,6 @@ CTurbSASolver::CTurbSASolver(CGeometry *geometry, CConfig *config, unsigned shor nPoint = geometry->GetnPoint(); nPointDomain = geometry->GetnPointDomain(); - /*--- Add Langevin equations if the Stochastic Backscatter Model is used ---*/ - - bool backscatter = config->GetStochastic_Backscatter(); - if (backscatter) { nVar += 3; nPrimVar += 3; } - /*--- Initialize nVarGrad for deallocation ---*/ nVarGrad = nVar; @@ -60,11 +55,10 @@ CTurbSASolver::CTurbSASolver(CGeometry *geometry, CConfig *config, unsigned shor nDim = geometry->GetnDim(); - /*--- Check if Stochastic Backscatter Model can be used ---*/ + /*--- Add Langevin equations if the Stochastic Backscatter Model is used ---*/ - if (backscatter && nDim != 3) { - SU2_MPI::Error("The Stochastic Backscatter Model can be used for three-dimensional problems only.", CURRENT_FUNCTION); - } + bool backscatter = config->GetStochastic_Backscatter(); + if (backscatter) { nVar += nDim; nVarGrad = nPrimVar = nVar; } /*--- Single grid simulation ---*/ From 85b8aaca32a453ec6b6734891253382bae37f2f3 Mon Sep 17 00:00:00 2001 From: Angelo Passariello Date: Mon, 29 Sep 2025 18:09:40 +0200 Subject: [PATCH 06/25] Boundary conditions for Langevin equations Enforce boundary conditions in Langevin equations. --- SU2_CFD/include/numerics/CNumerics.hpp | 17 +--- .../numerics/scalar/scalar_diffusion.hpp | 2 +- .../numerics/turbulent/turb_sources.hpp | 50 ++++++---- SU2_CFD/include/solvers/CTurbSASolver.hpp | 29 +++++- SU2_CFD/include/solvers/CTurbSolver.hpp | 1 + SU2_CFD/include/variables/CTurbSAVariable.hpp | 17 ++++ SU2_CFD/include/variables/CVariable.hpp | 15 +++ SU2_CFD/src/solvers/CTurbSASolver.cpp | 99 ++++++++++++------- SU2_CFD/src/variables/CTurbSAVariable.cpp | 11 +-- 9 files changed, 163 insertions(+), 78 deletions(-) diff --git a/SU2_CFD/include/numerics/CNumerics.hpp b/SU2_CFD/include/numerics/CNumerics.hpp index 6d8551135d48..7327472d144d 100644 --- a/SU2_CFD/include/numerics/CNumerics.hpp +++ b/SU2_CFD/include/numerics/CNumerics.hpp @@ -184,8 +184,7 @@ class CNumerics { su2double lesSensor_i, /*!< \brief LES sensor at point i. */ lesSensor_j; /*!< \brief LES sensor at point j. */ - unsigned long lastTime; /*!< \brief Physical time iteration of unsteady simulation. */ - su2double stochSource[3]; /*!< \brief Source term for Langevin equations in Stochastic Backscatter Model. */ + su2double stochSource[3] = {0.0}; /*!< \brief Source term for Langevin equations in Stochastic Backscatter Model. */ su2double stochVar_i[3], /*!< \brief Stochastic variables at point i for Stochastic Backscatter Model. */ stochVar_j[3]; /*!< \brief Stochastic variables at point j for Stochastic Backscatter Model. */ @@ -902,20 +901,6 @@ class CNumerics { lesSensor_j = val_les_j; } - /*! - * \brief Set the value of physical time. - * \param[in] val_last_time - Value of physical time. - */ - void SetLastTime(unsigned long val_last_time) { - lastTime = val_last_time; - } - - /*! - * \brief Get the value of physical time. - * \param[out] lastTime - Value of physical time. - */ - inline unsigned long GetLastTime() const { return lastTime; } - /*! * \brief Set the stochastic source term for the Langevin equations (Backscatter Model). * \param[in] val_stoch_source - Value of stochastic source term. diff --git a/SU2_CFD/include/numerics/scalar/scalar_diffusion.hpp b/SU2_CFD/include/numerics/scalar/scalar_diffusion.hpp index 13ee01f7eda6..d92776f2f3b7 100644 --- a/SU2_CFD/include/numerics/scalar/scalar_diffusion.hpp +++ b/SU2_CFD/include/numerics/scalar/scalar_diffusion.hpp @@ -64,7 +64,7 @@ class CAvgGrad_Scalar : public CNumerics { 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. */ su2double proj_vector_ij = 0.0; /*!< \brief (Edge_Vector DOT normal)/|Edge_Vector|^2 */ - su2double Flux[MAXNVAR]; /*!< \brief Final result, diffusive flux/residual. */ + su2double Flux[MAXNVAR] = {0.0}; /*!< \brief Final result, diffusive flux/residual. */ su2double* Jacobian_i[MAXNVAR]; /*!< \brief Flux Jacobian w.r.t. node i. */ su2double* Jacobian_j[MAXNVAR]; /*!< \brief Flux Jacobian w.r.t. node j. */ su2double JacobianBuffer[2*MAXNVAR*MAXNVAR];/*!< \brief Static storage for the two Jacobians. */ diff --git a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp index 970c7b6d555c..29eb598652f4 100644 --- a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp +++ b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp @@ -119,7 +119,7 @@ class CSourceBase_TurbSA : public CNumerics { */ inline void ResidualStochEquations(su2double timeStep, const su2double ct, su2double lengthScale, su2double les_sensor, - const CSAVariables& var) { + const CSAVariables& var, TIME_MARCHING time_marching) { const su2double& nue = ScalarVar_i[0]; @@ -132,9 +132,17 @@ class CSourceBase_TurbSA : public CNumerics { const su2double nut = nue * var.fv1; - su2double tTurb = ct * pow(lengthScale, 2) / max(nut, nut_small); + su2double tTurb = 0.01 * ct * pow(lengthScale, 2) / max(nut, nut_small); su2double tRat = timeStep / tTurb; - su2double corrFac = sqrt(0.5*(1.0+tRat)*(4.0+tRat)/(2.0+tRat)); + + su2double corrFac = 1.0; + if (time_marching == TIME_MARCHING::DT_STEPPING_2ND) { + corrFac = sqrt(0.5*(1.0+tRat)*(4.0+tRat)/(2.0+tRat)); + } else if (time_marching == TIME_MARCHING::DT_STEPPING_1ST) { + corrFac = sqrt(1.0+0.5*tRat); + } else { + SU2_MPI::Error("Stochastic Backscatter Model only implemented for dual time stepping.", CURRENT_FUNCTION); + } su2double scaleFactor = 1.0/tTurb * sqrt(2.0/tRat) * density * corrFac; @@ -153,16 +161,16 @@ class CSourceBase_TurbSA : public CNumerics { } JacobianSB_i[0][0] = Jacobian_i[0]; -/* su2double dnut_dnue = var.fv1 + 3.0 * var.cv1_3 * Ji_3 / pow(Ji_3 + var.cv1_3, 2); + su2double dnut_dnue = var.fv1 + 3.0 * var.cv1_3 * Ji_3 / pow(Ji_3 + var.cv1_3, 2); su2double dtTurb_dnut = - ct * pow(lengthScale,2) / (max(nut, nut_small)*max(nut, nut_small)); for (unsigned short iVar = 1; iVar < nVar; iVar++ ) { - JacobianSB_i[iVar][0] = - 1.0/tTurb * les_sensor * scaleFactor * stochSource[iVar-1] + JacobianSB_i[iVar][0] = - 1.0/tTurb * std::nearbyint(les_sensor) * scaleFactor * stochSource[iVar-1] + 1.0/(tTurb*tTurb) * ScalarVar_i[iVar] - + density * les_sensor * corrFac * stochSource[iVar-1] / + + density * std::nearbyint(les_sensor) * corrFac * stochSource[iVar-1] / (tTurb * sqrt(2.0*tTurb*timeStep)); JacobianSB_i[iVar][0] *= dtTurb_dnut * dnut_dnue * Volume; - } */ + } } public: @@ -182,10 +190,10 @@ class CSourceBase_TurbSA : public CNumerics { Jacobian_i = &Jacobian_Buffer; /*--- Setup the Jacobian for Stochastic Backscatter Model. ---*/ if (config->GetStochastic_Backscatter()) { - ResidSB = new su2double [nVar] (); - JacobianSB_i = new su2double* [nVar]; - for (unsigned short iVar = 0; iVar < nVar; iVar++ ) { - JacobianSB_i[iVar] = new su2double [nVar] (); + ResidSB = new su2double [1+nDim] (); + JacobianSB_i = new su2double* [1+nDim]; + for (unsigned short iVar = 0; iVar < 1+nDim; iVar++ ) { + JacobianSB_i[iVar] = new su2double [1+nDim] (); } } } @@ -195,7 +203,7 @@ class CSourceBase_TurbSA : public CNumerics { */ ~CSourceBase_TurbSA() { if (JacobianSB_i) { - for (unsigned short iVar = 0; iVar < nVar; iVar++) { + for (unsigned short iVar = 0; iVar < 1+nDim; iVar++) { delete [] JacobianSB_i[iVar]; } delete [] JacobianSB_i; @@ -215,10 +223,11 @@ class CSourceBase_TurbSA : public CNumerics { const auto& laminar_viscosity = V_i[idx.LaminarViscosity()]; AD::StartPreacc(); - AD::SetPreaccIn(density, laminar_viscosity, StrainMag_i, ScalarVar_i[0], Volume, dist_i, roughness_i); + AD::SetPreaccIn(density, laminar_viscosity, StrainMag_i, Volume, dist_i, roughness_i); + AD::SetPreaccIn(ScalarVar_i, nVar); AD::SetPreaccIn(Vorticity_i, 3); AD::SetPreaccIn(PrimVar_Grad_i + idx.Velocity(), nDim, nDim); - AD::SetPreaccIn(ScalarVar_Grad_i[0], nDim); + AD::SetPreaccIn(ScalarVar_Grad_i, nVar, nDim); bool backscatter = config->GetStochastic_Backscatter(); if (backscatter) { @@ -337,14 +346,17 @@ class CSourceBase_TurbSA : public CNumerics { /*--- Compute residual for Langevin equations (Stochastic Backscatter Model). ---*/ if (backscatter) { - const su2double constDES = config->GetConst_DES(); - const su2double ctTurb = 0.05 / pow(constDES, 2); - ResidualStochEquations(config->GetDelta_UnstTime(), ctTurb, dist_i, lesSensor_i, var); + const su2double ctTurb = 1.0; + ResidualStochEquations(config->GetDelta_UnstTime(), ctTurb, dist_i, lesSensor_i, var, + config->GetTime_Marching()); } } - AD::SetPreaccOut(Residual); - if (backscatter) { AD::SetPreaccOut(ResidSB, nVar); } + if (backscatter) { + AD::SetPreaccOut(ResidSB, nVar); + } else { + AD::SetPreaccOut(Residual); + } AD::EndPreacc(); if (backscatter) diff --git a/SU2_CFD/include/solvers/CTurbSASolver.hpp b/SU2_CFD/include/solvers/CTurbSASolver.hpp index 7220ffc32907..e2672b3ba8b4 100644 --- a/SU2_CFD/include/solvers/CTurbSASolver.hpp +++ b/SU2_CFD/include/solvers/CTurbSASolver.hpp @@ -39,7 +39,13 @@ class CTurbSASolver final : public CTurbSolver { private: - su2double nu_tilde_Engine, nu_tilde_ActDisk; + su2double* nu_tilde_Engine = nullptr; + su2double* nu_tilde_ActDisk = nullptr; + su2double* ext_AverageNu = nullptr; + su2double* Res_Wall = nullptr; + su2double** Jacobian_i = nullptr; + su2double* nu_tilde_inturb = nullptr; + su2double* nu_tilde_WF = nullptr; /*! * \brief A virtual member. @@ -51,6 +57,11 @@ class CTurbSASolver final : public CTurbSolver { CGeometry *geometry, CConfig *config); + /*! + * \brief Update the source terms of the stochastic equations (Stochastic Backscatter Model). + */ + void SetLangevinSourceTerms(CConfig *config, CGeometry* geometry); + /*! * \brief Compute nu tilde from the wall functions. * \param[in] geometry - Geometrical definition of the problem. @@ -85,7 +96,21 @@ class CTurbSASolver final : public CTurbSolver { /*! * \brief Destructor of the class. */ - ~CTurbSASolver() = default; + ~CTurbSASolver() { + + for(unsigned short iVar = 0; iVar < nVar; ++iVar) { + delete [] Jacobian_i[iVar]; + } + delete [] Jacobian_i; + + delete [] Res_Wall; + delete [] nu_tilde_Engine; + delete [] nu_tilde_ActDisk; + delete [] ext_AverageNu; + delete [] nu_tilde_inturb; + delete [] nu_tilde_WF; + + } /*! * \brief Restart residual and compute gradients. diff --git a/SU2_CFD/include/solvers/CTurbSolver.hpp b/SU2_CFD/include/solvers/CTurbSolver.hpp index 8753093b346b..db6415a0fda4 100644 --- a/SU2_CFD/include/solvers/CTurbSolver.hpp +++ b/SU2_CFD/include/solvers/CTurbSolver.hpp @@ -152,6 +152,7 @@ class CTurbSolver : public CScalarSolver { * \brief Compute a suitable under-relaxation parameter to limit the change in the solution variables over * a nonlinear iteration for stability. * \param[in] allowableRatio - Maximum percentage update in variable per iteration. + * \param[in] backscatter - Flag for Stochastic Backscatter Model. */ void ComputeUnderRelaxationFactorHelper(su2double allowableRatio); }; diff --git a/SU2_CFD/include/variables/CTurbSAVariable.hpp b/SU2_CFD/include/variables/CTurbSAVariable.hpp index 9cab3dd177f5..2a15145825ea 100644 --- a/SU2_CFD/include/variables/CTurbSAVariable.hpp +++ b/SU2_CFD/include/variables/CTurbSAVariable.hpp @@ -41,6 +41,7 @@ class CTurbSAVariable final : public CTurbVariable { private: VectorType DES_LengthScale; VectorType LES_Mode; + MatrixType stochSource; VectorType Vortex_Tilting; public: @@ -74,6 +75,22 @@ class CTurbSAVariable final : public CTurbVariable { */ inline void SetDES_LengthScale(unsigned long iPoint, su2double val_des_lengthscale) override { DES_LengthScale(iPoint) = val_des_lengthscale; } +/*! + * \brief Get the source terms for the stochastic equations. + * \param[in] iPoint - Point index. + * \param[in] iDim - Dimension index. + * \return Value of the source term for the stochastic equations. + */ + inline su2double GetLangevinSourceTerms(unsigned long iPoint, unsigned short iDim) const override { return stochSource(iPoint, iDim); } + + /*! + * \brief Set the source terms for the stochastic equations. + * \param[in] iPoint - Point index. + * \param[in] iDim - Dimension index. + * \param[in] val_stochSource - Value of the source term for the stochastic equations. + */ + inline void SetLangevinSourceTerms(unsigned long iPoint, unsigned short iDim, su2double val_stochSource) override { stochSource(iPoint, iDim) = val_stochSource; } + /*! * \brief Set the LES sensor. */ diff --git a/SU2_CFD/include/variables/CVariable.hpp b/SU2_CFD/include/variables/CVariable.hpp index 307531798364..f07935f04bc8 100644 --- a/SU2_CFD/include/variables/CVariable.hpp +++ b/SU2_CFD/include/variables/CVariable.hpp @@ -409,6 +409,21 @@ class CVariable { */ inline virtual void SetLES_Mode(unsigned long iPoint, su2double val_les_mode) {} + /*! + * \brief A virtual member. + * \param[in] iPoint - Point index. + * \param[in] iDim - Dimension index. + */ + inline virtual su2double GetLangevinSourceTerms(unsigned long iPoint, unsigned short iDim) const { return 0.0; } + + /*! + * \brief A virtual member. + * \param[in] iPoint - Point index. + * \param[in] iDim - Dimension index. + * \param[in] val_stochSource - Source term for Langevin equations. + */ + inline virtual void SetLangevinSourceTerms(unsigned long iPoint, unsigned short iDim, su2double val_stochSource) {} + /*! * \brief A virtual member. * \param[in] iPoint - Point index. diff --git a/SU2_CFD/src/solvers/CTurbSASolver.cpp b/SU2_CFD/src/solvers/CTurbSASolver.cpp index 659d35693b8c..dbbaafe1cf8b 100644 --- a/SU2_CFD/src/solvers/CTurbSASolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp @@ -117,20 +117,22 @@ CTurbSASolver::CTurbSASolver(CGeometry *geometry, CConfig *config, unsigned shor Solution_Inf[0] = nu_tilde_Inf; if (backscatter) { for (unsigned short iVar = 1; iVar < nVar; iVar++) { - Solution_Inf[iVar] = 0.0; // Backscatter variables initialized in CTurbSAVariable.hpp + Solution_Inf[iVar] = 0.0; } } /*--- Factor_nu_Engine ---*/ - Factor_nu_Engine = config->GetNuFactor_Engine(); - nu_tilde_Engine = Factor_nu_Engine*Viscosity_Inf/Density_Inf; + Factor_nu_Engine = config->GetNuFactor_Engine(); + nu_tilde_Engine = new su2double [nVar] (); + nu_tilde_Engine[0] = Factor_nu_Engine*Viscosity_Inf/Density_Inf; if (config->GetSAParsedOptions().bc) { - nu_tilde_Engine = 0.005*Factor_nu_Engine*Viscosity_Inf/Density_Inf; + nu_tilde_Engine[0] = 0.005*Factor_nu_Engine*Viscosity_Inf/Density_Inf; } /*--- Factor_nu_ActDisk ---*/ - Factor_nu_ActDisk = config->GetNuFactor_Engine(); - nu_tilde_ActDisk = Factor_nu_ActDisk*Viscosity_Inf/Density_Inf; + Factor_nu_ActDisk = config->GetNuFactor_Engine(); + nu_tilde_ActDisk = new su2double [nVar] (); + nu_tilde_ActDisk[0] = Factor_nu_ActDisk*Viscosity_Inf/Density_Inf; /*--- Eddy viscosity at infinity ---*/ su2double Ji, Ji_3, fv1, cv1_3 = 7.1*7.1*7.1; @@ -140,6 +142,19 @@ CTurbSASolver::CTurbSASolver(CGeometry *geometry, CConfig *config, unsigned shor fv1 = Ji_3/(Ji_3+cv1_3); muT_Inf = Density_Inf*fv1*nu_tilde_Inf; + ext_AverageNu = new su2double [nVar] (); + + Res_Wall = new su2double [nVar] (); + + Jacobian_i = new su2double* [nVar]; + for (unsigned short iVar = 0; iVar < nVar; iVar++) { + Jacobian_i[iVar] = new su2double [nVar] (); + } + + nu_tilde_inturb = new su2double [nVar] (); + + nu_tilde_WF = new su2double [nVar] (); + /*--- Initialize the solution to the far-field state everywhere. ---*/ nodes = new CTurbSAVariable(nu_tilde_Inf, muT_Inf, nPoint, nDim, nVar, config); @@ -222,6 +237,10 @@ void CTurbSASolver::Preprocessing(CGeometry *geometry, CSolver **solver_containe SetDES_LengthScale(solver_container, geometry, config); + /*--- Compute source terms for Langevin equations ---*/ + + if (config->GetStochastic_Backscatter()) SetLangevinSourceTerms(config, geometry); + } } @@ -400,19 +419,9 @@ void CTurbSASolver::Source_Residual(CGeometry *geometry, CSolver **solver_contai /*--- Compute source terms in Langevin equations (Stochastic Basckscatter Model) ---*/ - bool backscatter = config->GetStochastic_Backscatter(); - if (backscatter) { - uint64_t currentTime = static_cast(config->GetTimeIter()); - uint64_t lastTime = static_cast(numerics->GetLastTime()); - if (currentTime != lastTime) { - numerics->SetLastTime(currentTime); - su2double randomSource; - for (unsigned short iDim = 0; iDim < 3; iDim++) { - uint64_t seed = RandomToolbox::GetSeed(currentTime, iDim+1); - randomSource = RandomToolbox::GetRandomNormal(seed); - numerics->SetStochSource(randomSource, iDim); - } - } + if (config->GetStochastic_Backscatter()) { + for (unsigned short iDim = 0; iDim < nDim; iDim++) + numerics->SetStochSource(nodes->GetLangevinSourceTerms(iPoint, iDim), iDim); } } @@ -540,19 +549,20 @@ void CTurbSASolver::BC_HeatFlux_Wall(CGeometry *geometry, CSolver **solver_conta su2double coeff = (nu_total/sigma); su2double RoughWallBC = nodes->GetSolution(iPoint,0)/(0.03*Roughness_Height); - su2double Res_Wall;// = new su2double [nVar]; - Res_Wall = coeff*RoughWallBC*Area; - LinSysRes.SubtractBlock(iPoint, &Res_Wall); + Res_Wall[0] = coeff*RoughWallBC*Area; + LinSysRes.SubtractBlock(iPoint, Res_Wall); - su2double Jacobian_i = (laminar_viscosity /density *Area)/(0.03*Roughness_Height*sigma); - Jacobian_i += 2.0*RoughWallBC*Area/sigma; - if (implicit) Jacobian.AddVal2Diag(iPoint, -Jacobian_i); + Jacobian_i[0][0] = (laminar_viscosity /density *Area)/(0.03*Roughness_Height*sigma); + Jacobian_i[0][0] += 2.0*RoughWallBC*Area/sigma; + if (implicit) Jacobian.AddVal2Diag(iPoint, 0, -Jacobian_i[0][0]); } } } END_SU2_OMP_FOR } + + void CTurbSASolver::BC_Isothermal_Wall(CGeometry *geometry, CSolver **solver_container, CNumerics *conv_numerics, CNumerics *visc_numerics, CConfig *config, unsigned short val_marker) { @@ -900,7 +910,7 @@ void CTurbSASolver::BC_Engine_Exhaust(CGeometry *geometry, CSolver **solver_cont /*--- Set the turbulent variable states (prescribed for an inflow) ---*/ - conv_numerics->SetScalarVar(nodes->GetSolution(iPoint), &nu_tilde_Engine); + conv_numerics->SetScalarVar(nodes->GetSolution(iPoint), nu_tilde_Engine); /*--- Set various other quantities in the conv_numerics class ---*/ @@ -1053,7 +1063,7 @@ void CTurbSASolver::BC_ActDisk(CGeometry *geometry, CSolver **solver_container, } else { /*--- Outflow analysis ---*/ - conv_numerics->SetScalarVar(nodes->GetSolution(iPoint), &nu_tilde_ActDisk); + conv_numerics->SetScalarVar(nodes->GetSolution(iPoint), nu_tilde_ActDisk); } /*--- Grid Movement ---*/ @@ -1114,6 +1124,8 @@ void CTurbSASolver::BC_Inlet_MixingPlane(CGeometry *geometry, CSolver **solver_c su2double extAverageNu = solver_container[FLOW_SOL]->GetExtAverageNu(val_marker, iSpan); + ext_AverageNu[0] = extAverageNu; + /*--- Loop over all the vertices on this boundary marker ---*/ SU2_OMP_FOR_STAT(OMP_MIN_SIZE) @@ -1148,7 +1160,7 @@ void CTurbSASolver::BC_Inlet_MixingPlane(CGeometry *geometry, CSolver **solver_c /*--- Set the turbulent variable states (prescribed for an inflow) ---*/ - conv_numerics->SetScalarVar(nodes->GetSolution(iPoint), &extAverageNu); + conv_numerics->SetScalarVar(nodes->GetSolution(iPoint), ext_AverageNu); /*--- Set various other quantities in the conv_numerics class ---*/ @@ -1178,7 +1190,7 @@ void CTurbSASolver::BC_Inlet_MixingPlane(CGeometry *geometry, CSolver **solver_c /*--- Turbulent variables w/o reconstruction, and its gradients ---*/ - visc_numerics->SetScalarVar(nodes->GetSolution(iPoint), &extAverageNu); + visc_numerics->SetScalarVar(nodes->GetSolution(iPoint), ext_AverageNu); visc_numerics->SetScalarVarGradient(nodes->GetGradient(iPoint), nodes->GetGradient(iPoint)); @@ -1253,7 +1265,9 @@ void CTurbSASolver::BC_Inlet_Turbo(CGeometry *geometry, CSolver **solver_contain /*--- Set the turbulent variable states (prescribed for an inflow) ---*/ - conv_numerics->SetScalarVar(nodes->GetSolution(iPoint), &nu_tilde); + nu_tilde_inturb[0] = nu_tilde; + + conv_numerics->SetScalarVar(nodes->GetSolution(iPoint), nu_tilde_inturb); if (dynamic_grid) conv_numerics->SetGridVel(geometry->nodes->GetGridVel(iPoint), @@ -1283,7 +1297,7 @@ void CTurbSASolver::BC_Inlet_Turbo(CGeometry *geometry, CSolver **solver_contain /*--- Turbulent variables w/o reconstruction, and its gradients ---*/ - visc_numerics->SetScalarVar(nodes->GetSolution(iPoint), &nu_tilde); + visc_numerics->SetScalarVar(nodes->GetSolution(iPoint), nu_tilde_inturb); visc_numerics->SetScalarVarGradient(nodes->GetGradient(iPoint), nodes->GetGradient(iPoint)); @@ -1374,7 +1388,9 @@ void CTurbSASolver::SetTurbVars_WF(CGeometry *geometry, CSolver **solver_contain if (counter > max_iter) break; } - nodes->SetSolution_Old(iPoint_Neighbor, &nu_til); + nu_tilde_WF[0] = nu_til; + + nodes->SetSolution_Old(iPoint_Neighbor, nu_tilde_WF); LinSysRes.SetBlock_Zero(iPoint_Neighbor); /*--- includes 1 in the diagonal ---*/ @@ -1569,6 +1585,22 @@ void CTurbSASolver::SetDES_LengthScale(CSolver **solver, CGeometry *geometry, CC END_SU2_OMP_FOR } +void CTurbSASolver::SetLangevinSourceTerms(CConfig *config, CGeometry* geometry) { + + uint64_t currentTime = static_cast(config->GetTimeIter()); + + SU2_OMP_FOR_DYN(omp_chunk_size) + for (auto iPoint = 0ul; iPoint < nPointDomain; iPoint++){ + const auto iGlobalPoint = geometry->nodes->GetGlobalIndex(iPoint); + for (auto iDim = 0u; iDim < nDim; iDim++){ + uint64_t seed = RandomToolbox::GetSeed(currentTime+1, iGlobalPoint+iDim+1); + nodes->SetLangevinSourceTerms(iPoint, iDim, RandomToolbox::GetRandomNormal(seed)); + } + } + END_SU2_OMP_FOR + +} + void CTurbSASolver::SetInletAtVertex(const su2double *val_inlet, unsigned short iMarker, unsigned long iVertex) { @@ -1603,7 +1635,8 @@ void CTurbSASolver::ComputeUnderRelaxationFactor(const CConfig *config) { SA_NEG model is more robust due to allowing for negative nu_tilde, so the under-relaxation is not applied to that variant. */ - if (config->GetSAParsedOptions().version == SA_OPTIONS::NEG) return; + if (config->GetSAParsedOptions().version == SA_OPTIONS::NEG || + config->GetStochastic_Backscatter()) return; /* Loop over the solution update given by relaxing the linear system for this nonlinear iteration. */ diff --git a/SU2_CFD/src/variables/CTurbSAVariable.cpp b/SU2_CFD/src/variables/CTurbSAVariable.cpp index 5f4a58b84e01..3450ebfbcd42 100644 --- a/SU2_CFD/src/variables/CTurbSAVariable.cpp +++ b/SU2_CFD/src/variables/CTurbSAVariable.cpp @@ -27,7 +27,6 @@ #include "../../include/variables/CTurbSAVariable.hpp" -#include "../../../Common/include/toolboxes/random_toolbox.hpp" CTurbSAVariable::CTurbSAVariable(su2double val_nu_tilde, su2double val_muT, unsigned long npoint, @@ -39,13 +38,10 @@ CTurbSAVariable::CTurbSAVariable(su2double val_nu_tilde, su2double val_muT, unsi if (!backscatter) { Solution_Old = Solution = val_nu_tilde; } else { - for (unsigned long iPoint = 0; iPoint < npoint; iPoint++) { + for (unsigned long iPoint = 0; iPoint < nPoint; iPoint++) { Solution_Old(iPoint, 0) = Solution(iPoint, 0) = val_nu_tilde; - for (unsigned long iVar = 1; iVar < nvar; iVar++) { - uint64_t seed = RandomToolbox::GetSeed( - static_cast(config->GetTimeIter()+1), - static_cast(iPoint*nvar + iVar)); - Solution_Old(iPoint, iVar) = Solution(iPoint, iVar) = RandomToolbox::GetRandomNormal(seed); + for (unsigned long iVar = 1; iVar < nVar; iVar++) { + Solution_Old(iPoint, iVar) = Solution(iPoint, iVar) = 0.0; } } } @@ -64,6 +60,7 @@ CTurbSAVariable::CTurbSAVariable(su2double val_nu_tilde, su2double val_muT, unsi DES_LengthScale.resize(nPoint) = su2double(0.0); LES_Mode.resize(nPoint) = su2double(0.0); Vortex_Tilting.resize(nPoint); + stochSource.resize(nPoint, nDim) = su2double(0.0); } void CTurbSAVariable::SetVortex_Tilting(unsigned long iPoint, CMatrixView PrimGrad_Flow, From 62930428a1a0e146753527b04cab6b0c0b985b4a Mon Sep 17 00:00:00 2001 From: Angelo Passariello Date: Tue, 30 Sep 2025 15:39:10 +0200 Subject: [PATCH 07/25] Use symmetry-preserving scheme for Langevin equations - Implement a skew-symmetric scheme for the discretization of the convective terms in the Langevin equations. - Fix the seed for the generation of the random source terms at the beginning of the simulation. --- Common/include/CConfig.hpp | 7 ++++ Common/include/toolboxes/random_toolbox.hpp | 12 +++---- Common/src/CConfig.cpp | 9 +++-- SU2_CFD/include/numerics/CNumerics.hpp | 13 ------- .../numerics/scalar/scalar_convection.hpp | 8 +++++ .../numerics/turbulent/turb_convection.hpp | 13 +++---- .../numerics/turbulent/turb_sources.hpp | 32 ++++++++--------- SU2_CFD/include/solvers/CTurbSASolver.hpp | 9 +++++ SU2_CFD/include/variables/CTurbSAVariable.hpp | 19 +++++++++- SU2_CFD/include/variables/CVariable.hpp | 19 +++++++++- SU2_CFD/src/output/CFlowOutput.cpp | 6 ++++ SU2_CFD/src/solvers/CTurbSASolver.cpp | 35 ++++++++++++++----- SU2_CFD/src/variables/CTurbSAVariable.cpp | 2 ++ config_template.cfg | 3 ++ 14 files changed, 133 insertions(+), 54 deletions(-) diff --git a/Common/include/CConfig.hpp b/Common/include/CConfig.hpp index bc753e18b935..85328ee4d438 100644 --- a/Common/include/CConfig.hpp +++ b/Common/include/CConfig.hpp @@ -1082,6 +1082,7 @@ class CConfig { WINDOW_FUNCTION Kind_WindowFct; /*!< \brief Type of window (weight) function for objective functional. */ unsigned short Kind_HybridRANSLES; /*!< \brief Kind of Hybrid RANS/LES. */ bool StochasticBackscatter; /*!< \brief Option to include Stochastic Backscatter Model. */ + su2double SBS_Ctau; /*!< \brief Stochastic Backscatter Model timescale coefficient. */ unsigned short Kind_RoeLowDiss; /*!< \brief Kind of Roe scheme with low dissipation for unsteady flows. */ unsigned short nSpanWiseSections; /*!< \brief number of span-wise sections */ @@ -9543,6 +9544,12 @@ class CConfig { */ su2double GetConst_DES(void) const { return Const_DES; } + /*! + * \brief Get the DES Constant. + * \return Value of DES constant. + */ + su2double GetSBS_Ctau(void) const { return SBS_Ctau; } + /*! * \brief Get the type of tape that will be checked in a tape debug run. */ diff --git a/Common/include/toolboxes/random_toolbox.hpp b/Common/include/toolboxes/random_toolbox.hpp index 7d9a3a85608e..811691ac772a 100644 --- a/Common/include/toolboxes/random_toolbox.hpp +++ b/Common/include/toolboxes/random_toolbox.hpp @@ -39,8 +39,8 @@ namespace RandomToolbox { * \param[in] v2 Value to mix in. * \return Combined hash value. */ -inline uint64_t HashCombine(uint64_t v1, uint64_t v2) { - const uint64_t prime = 1099511628211ULL; +inline unsigned long HashCombine(unsigned long v1, unsigned long v2) { + const unsigned long prime = 1099511628211ULL; v1 ^= v2; v1 *= prime; return v1; @@ -51,7 +51,7 @@ inline uint64_t HashCombine(uint64_t v1, uint64_t v2) { * \param[in] x Double to integer. * \return Hash value of the double (not portable). */ -inline uint64_t ToUInt64(double x) { return std::hash{}(x); } +inline unsigned long ToUInt64(double x) { return std::hash{}(x); } /*! * \brief Build a deterministic seed from physical time. @@ -59,7 +59,7 @@ inline uint64_t ToUInt64(double x) { return std::hash{}(x); } * \param[in] y Second integer value. * \return 64-bit seed value. */ -inline uint64_t GetSeed(uint64_t x, uint64_t y) { return HashCombine(x, y); } +inline unsigned long GetSeed(unsigned long x, unsigned long y) { return HashCombine(x, y); } /*! * \brief Generate a standard normally-distributed random number. @@ -68,8 +68,8 @@ inline uint64_t GetSeed(uint64_t x, uint64_t y) { return HashCombine(x, y); } * \param[in] stddev Standard deviation of the normal distribution (default 1). * \return Normally-distributed random number. */ -inline double GetRandomNormal(uint64_t seed, double mean = 0.0, double stddev = 1.0) { - std::mt19937_64 gen(seed); +inline double GetRandomNormal(unsigned long seed, double mean = 0.0, double stddev = 1.0) { + std::mt19937 gen(seed); std::normal_distribution rnd(mean, stddev); return rnd(gen); } diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index 898a0333c1e2..81695d75dbea 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -2911,6 +2911,9 @@ void CConfig::SetConfig_Options() { /* DESCRIPTION: DES Constant */ addDoubleOption("DES_CONST", Const_DES, 0.65); + /* DESCRIPTION: SBS timescale constant */ + addDoubleOption("SBS_CTAU", SBS_Ctau, 0.05); + /* DESCRIPTION: Specify Hybrid RANS/LES model */ addEnumOption("HYBRID_RANSLES", Kind_HybridRANSLES, HybridRANSLES_Map, NO_HYBRIDRANSLES); @@ -6451,10 +6454,12 @@ void CConfig::SetOutput(SU2_COMPONENT val_software, unsigned short val_izone) { case SA_EDDES: cout << "Delayed Detached Eddy Simulation (DDES) with Shear-layer Adapted SGS" << endl; break; } cout << "Stochastic Backscatter: "; - if (StochasticBackscatter) + if (StochasticBackscatter) { cout << "ON" << endl; - else + cout << "Backscatter timescale coefficient: " << SBS_Ctau << endl; + } else { cout << "OFF" << endl; + } if (StochasticBackscatter && Kind_HybridRANSLES == NO_HYBRIDRANSLES) SU2_MPI::Error("Stochastic Backscatter can only be activated with Hybrid RANS/LES.", CURRENT_FUNCTION); break; diff --git a/SU2_CFD/include/numerics/CNumerics.hpp b/SU2_CFD/include/numerics/CNumerics.hpp index 7327472d144d..8899491e17e5 100644 --- a/SU2_CFD/include/numerics/CNumerics.hpp +++ b/SU2_CFD/include/numerics/CNumerics.hpp @@ -181,9 +181,6 @@ class CNumerics { su2double MeanPerturbedRSM[3][3]; /*!< \brief Perturbed Reynolds stress tensor */ su2double stochReynStress[3][3]; /*!< \brief Stochastic contribution to Reynolds stress tensor for Backscatter Model. */ - su2double - lesSensor_i, /*!< \brief LES sensor at point i. */ - lesSensor_j; /*!< \brief LES sensor at point j. */ su2double stochSource[3] = {0.0}; /*!< \brief Source term for Langevin equations in Stochastic Backscatter Model. */ su2double stochVar_i[3], /*!< \brief Stochastic variables at point i for Stochastic Backscatter Model. */ @@ -891,16 +888,6 @@ class CNumerics { dist_j = val_dist_j; } - /*! - * \brief Set the value of the LES sensor. - * \param[in] val_les_i - Value of the LES sensor at point point i. - * \param[in] val_les_j - Value of the LES sensor at point point j. - */ - void SetLESSensor(su2double val_les_i, su2double val_les_j) { - lesSensor_i = val_les_i; - lesSensor_j = val_les_j; - } - /*! * \brief Set the stochastic source term for the Langevin equations (Backscatter Model). * \param[in] val_stoch_source - Value of stochastic source term. diff --git a/SU2_CFD/include/numerics/scalar/scalar_convection.hpp b/SU2_CFD/include/numerics/scalar/scalar_convection.hpp index 91b8429e0aaf..4395a4a2092b 100644 --- a/SU2_CFD/include/numerics/scalar/scalar_convection.hpp +++ b/SU2_CFD/include/numerics/scalar/scalar_convection.hpp @@ -52,6 +52,7 @@ class CUpwScalar : public CNumerics { 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. */ su2double a1 = 0.0; /*!< \brief The minimum of the face-normal velocity and 0. */ + su2double qij = 0.0; /*!< \brief The face-normal velocity (Langevin equations). */ su2double Flux[MAXNVAR]; /*!< \brief Final result, diffusive flux/residual. */ su2double* Jacobian_i[MAXNVAR]; /*!< \brief Flux Jacobian w.r.t. node i. */ su2double* Jacobian_j[MAXNVAR]; /*!< \brief Flux Jacobian w.r.t. node j. */ @@ -140,6 +141,13 @@ class CUpwScalar : public CNumerics { a1 = fmin(0.0, q_ij); } + if (config->GetStochastic_Backscatter()) { + qij = 0.0; + for (unsigned short iDim = 0; iDim < nDim; iDim++) { + qij += 0.5 * (V_i[idx.Density()]*V_i[iDim + idx.Velocity()] + V_j[idx.Density()]*V_j[iDim + idx.Velocity()]) * Normal[iDim]; + } + } + FinishResidualCalc(config); AD::SetPreaccOut(Flux, nVar); diff --git a/SU2_CFD/include/numerics/turbulent/turb_convection.hpp b/SU2_CFD/include/numerics/turbulent/turb_convection.hpp index 711934b518fe..15635dc9fcce 100644 --- a/SU2_CFD/include/numerics/turbulent/turb_convection.hpp +++ b/SU2_CFD/include/numerics/turbulent/turb_convection.hpp @@ -52,6 +52,7 @@ class CUpwSca_TurbSA final : public CUpwScalar { using Base::V_j; using Base::idx; using Base::nVar; + using Base::qij; /*! * \brief Adds any extra variables to AD. @@ -63,21 +64,21 @@ class CUpwSca_TurbSA final : public CUpwScalar { * \param[in] config - Definition of the particular problem. */ void FinishResidualCalc(const CConfig* config) override { - Flux[0] = a0*ScalarVar_i[0] + a1*ScalarVar_j[0]; - Jacobian_i[0][0] = a0; - Jacobian_j[0][0] = a1; bool backscatter = config->GetStochastic_Backscatter(); if (backscatter) { for (unsigned short iVar = 1; iVar < nVar; iVar++) { - Flux[iVar] = a0*V_i[idx.Density()]*ScalarVar_i[iVar] + a1*V_j[idx.Density()]*ScalarVar_j[iVar]; + Flux[iVar] = qij * 0.5 * (ScalarVar_i[iVar] + ScalarVar_j[iVar]); } for (unsigned short iVar = 0; iVar < nVar; iVar++) { for (unsigned short jVar = 0; jVar < nVar; jVar++) { - Jacobian_i[iVar][jVar] = (iVar == jVar) ? a0 : 0.0; - Jacobian_j[iVar][jVar] = (iVar == jVar) ? a1 : 0.0; + Jacobian_i[iVar][jVar] = (iVar == jVar) ? 0.5*qij : 0.0; + Jacobian_j[iVar][jVar] = (iVar == jVar) ? 0.5*qij : 0.0; } } } + Flux[0] = a0*ScalarVar_i[0] + a1*ScalarVar_j[0]; + Jacobian_i[0][0] = a0; + Jacobian_j[0][0] = a1; } public: diff --git a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp index 29eb598652f4..cb74c1523ac6 100644 --- a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp +++ b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp @@ -118,7 +118,7 @@ class CSourceBase_TurbSA : public CNumerics { * \brief Include source-term residuals for Langevin equations (Stochastic Backscatter Model) */ inline void ResidualStochEquations(su2double timeStep, const su2double ct, - su2double lengthScale, su2double les_sensor, + su2double lengthScale, const CSAVariables& var, TIME_MARCHING time_marching) { const su2double& nue = ScalarVar_i[0]; @@ -132,7 +132,7 @@ class CSourceBase_TurbSA : public CNumerics { const su2double nut = nue * var.fv1; - su2double tTurb = 0.01 * ct * pow(lengthScale, 2) / max(nut, nut_small); + su2double tTurb = ct * pow(lengthScale, 2) / max(nut, nut_small); su2double tRat = timeStep / tTurb; su2double corrFac = 1.0; @@ -148,8 +148,7 @@ class CSourceBase_TurbSA : public CNumerics { ResidSB[0] = Residual; for (unsigned short iVar = 1; iVar < nVar; iVar++ ) { - ResidSB[iVar] = std::nearbyint(les_sensor) * scaleFactor * stochSource[iVar-1] - - 1.0/tTurb * density * ScalarVar_i[iVar]; + ResidSB[iVar] = scaleFactor * stochSource[iVar-1] - 1.0/tTurb * density * ScalarVar_i[iVar]; ResidSB[iVar] *= Volume; } @@ -161,16 +160,16 @@ class CSourceBase_TurbSA : public CNumerics { } JacobianSB_i[0][0] = Jacobian_i[0]; - su2double dnut_dnue = var.fv1 + 3.0 * var.cv1_3 * Ji_3 / pow(Ji_3 + var.cv1_3, 2); - su2double dtTurb_dnut = - ct * pow(lengthScale,2) / (max(nut, nut_small)*max(nut, nut_small)); +// su2double dnut_dnue = var.fv1 + 3.0 * var.cv1_3 * Ji_3 / pow(Ji_3 + var.cv1_3, 2); +// su2double dtTurb_dnut = - ct * pow(lengthScale,2) / (max(nut, nut_small)*max(nut, nut_small)); - for (unsigned short iVar = 1; iVar < nVar; iVar++ ) { - JacobianSB_i[iVar][0] = - 1.0/tTurb * std::nearbyint(les_sensor) * scaleFactor * stochSource[iVar-1] - + 1.0/(tTurb*tTurb) * ScalarVar_i[iVar] - + density * std::nearbyint(les_sensor) * corrFac * stochSource[iVar-1] / - (tTurb * sqrt(2.0*tTurb*timeStep)); - JacobianSB_i[iVar][0] *= dtTurb_dnut * dnut_dnue * Volume; - } +// for (unsigned short iVar = 1; iVar < nVar; iVar++ ) { +// JacobianSB_i[iVar][0] = - 1.0/tTurb * scaleFactor * stochSource[iVar-1] +// + 1.0/(tTurb*tTurb) * ScalarVar_i[iVar] +// + density * corrFac * stochSource[iVar-1] / +// (tTurb * sqrt(2.0*tTurb*timeStep)); +// JacobianSB_i[iVar][0] *= dtTurb_dnut * dnut_dnue * Volume; +// } } public: @@ -231,7 +230,6 @@ class CSourceBase_TurbSA : public CNumerics { bool backscatter = config->GetStochastic_Backscatter(); if (backscatter) { - AD::SetPreaccIn(lesSensor_i); AD::SetPreaccIn(stochSource, 3); } @@ -346,8 +344,10 @@ class CSourceBase_TurbSA : public CNumerics { /*--- Compute residual for Langevin equations (Stochastic Backscatter Model). ---*/ if (backscatter) { - const su2double ctTurb = 1.0; - ResidualStochEquations(config->GetDelta_UnstTime(), ctTurb, dist_i, lesSensor_i, var, + const su2double DES_const = config->GetConst_DES(); + const su2double ctau = config->GetSBS_Ctau(); + const su2double ctTurb = ctau / pow(DES_const, 2); + ResidualStochEquations(config->GetDelta_UnstTime(), ctTurb, dist_i, var, config->GetTime_Marching()); } } diff --git a/SU2_CFD/include/solvers/CTurbSASolver.hpp b/SU2_CFD/include/solvers/CTurbSASolver.hpp index e2672b3ba8b4..2a6a489f3048 100644 --- a/SU2_CFD/include/solvers/CTurbSASolver.hpp +++ b/SU2_CFD/include/solvers/CTurbSASolver.hpp @@ -59,9 +59,18 @@ class CTurbSASolver final : public CTurbSolver { /*! * \brief Update the source terms of the stochastic equations (Stochastic Backscatter Model). + * \param[in] config - Definition of the particular problem. + * \param[in] geometry - Geometrical definition. */ void SetLangevinSourceTerms(CConfig *config, CGeometry* geometry); + /*! + * \brief Set seed for Langevin equations (Stochastic Backscatter Model). + * \param[in] config - Definition of the particular problem. + * \param[in] geometry - Geometrical definition. + */ + void SetLangevinSeed(CGeometry* geometry); + /*! * \brief Compute nu tilde from the wall functions. * \param[in] geometry - Geometrical definition of the problem. diff --git a/SU2_CFD/include/variables/CTurbSAVariable.hpp b/SU2_CFD/include/variables/CTurbSAVariable.hpp index 2a15145825ea..b47585dab6fd 100644 --- a/SU2_CFD/include/variables/CTurbSAVariable.hpp +++ b/SU2_CFD/include/variables/CTurbSAVariable.hpp @@ -43,6 +43,7 @@ class CTurbSAVariable final : public CTurbVariable { VectorType LES_Mode; MatrixType stochSource; VectorType Vortex_Tilting; + MatrixTypeInt stochSeed; public: /*! @@ -75,7 +76,7 @@ class CTurbSAVariable final : public CTurbVariable { */ inline void SetDES_LengthScale(unsigned long iPoint, su2double val_des_lengthscale) override { DES_LengthScale(iPoint) = val_des_lengthscale; } -/*! + /*! * \brief Get the source terms for the stochastic equations. * \param[in] iPoint - Point index. * \param[in] iDim - Dimension index. @@ -116,4 +117,20 @@ class CTurbSAVariable final : public CTurbVariable { */ inline su2double GetVortex_Tilting(unsigned long iPoint) const override { return Vortex_Tilting(iPoint); } + /*! + * \brief Get the seed for the stochastic equations. + * \param[in] iPoint - Point index. + * \param[in] iDim - Dimension index. + * \return Value of the seed for the stochastic equations. + */ + inline su2double GetLangevinSeed(unsigned long iPoint, unsigned short iDim) const override { return stochSeed(iPoint, iDim); } + + /*! + * \brief Set the seed for the stochastic equations. + * \param[in] iPoint - Point index. + * \param[in] iDim - Dimension index. + * \param[in] val_stochSeed - Value of the seed for the stochastic equations. + */ + inline void SetLangevinSeed(unsigned long iPoint, unsigned short iDim, unsigned long val_stochSeed) override { stochSeed(iPoint, iDim) = val_stochSeed; } + }; diff --git a/SU2_CFD/include/variables/CVariable.hpp b/SU2_CFD/include/variables/CVariable.hpp index f07935f04bc8..6b1159d38881 100644 --- a/SU2_CFD/include/variables/CVariable.hpp +++ b/SU2_CFD/include/variables/CVariable.hpp @@ -51,6 +51,7 @@ class CVariable { protected: using VectorType = C2DContainer; using MatrixType = C2DContainer; + using MatrixTypeInt = C2DContainer; MatrixType Solution; /*!< \brief Solution of the problem. */ MatrixType Solution_Old; /*!< \brief Old solution of the problem R-K. */ @@ -420,10 +421,26 @@ class CVariable { * \brief A virtual member. * \param[in] iPoint - Point index. * \param[in] iDim - Dimension index. - * \param[in] val_stochSource - Source term for Langevin equations. + * \param[in] val_stochSource - Seed for Langevin equations. */ inline virtual void SetLangevinSourceTerms(unsigned long iPoint, unsigned short iDim, su2double val_stochSource) {} + /*! + * \brief A virtual member. + * \param[in] iPoint - Point index. + * \param[in] iDim - Dimension index. + * \param[in] val_stochSeed - Seed for Langevin equations. + */ + inline virtual su2double GetLangevinSeed(unsigned long iPoint, unsigned short iDim) const {return 0.0;} + + /*! + * \brief A virtual member. + * \param[in] iPoint - Point index. + * \param[in] iDim - Dimension index. + * \param[in] val_stochSource - Source term for Langevin equations. + */ + inline virtual void SetLangevinSeed(unsigned long iPoint, unsigned short iDim, unsigned long val_stochSeed) {} + /*! * \brief A virtual member. * \param[in] iPoint - Point index. diff --git a/SU2_CFD/src/output/CFlowOutput.cpp b/SU2_CFD/src/output/CFlowOutput.cpp index aff75b2e3eb5..106aed24352c 100644 --- a/SU2_CFD/src/output/CFlowOutput.cpp +++ b/SU2_CFD/src/output/CFlowOutput.cpp @@ -1517,6 +1517,9 @@ void CFlowOutput::SetVolumeOutputFieldsScalarMisc(const CConfig* config) { AddVolumeOutput("STOCHASTIC_VAR_X", "Stochastic_Var_X", "BACKSCATTER", "x-component of the stochastic vector potential"); AddVolumeOutput("STOCHASTIC_VAR_Y", "Stochastic_Var_Y", "BACKSCATTER", "y-component of the stochastic vector potential"); if (nDim==3) AddVolumeOutput("STOCHASTIC_VAR_Z", "Stochastic_Var_Z", "BACKSCATTER", "z-component of the stochastic vector potential"); + AddVolumeOutput("STOCHASTIC_SOURCE_X", "Stochastic_Source_X", "BACKSCATTER", "x-component of the stochastic source vector"); + AddVolumeOutput("STOCHASTIC_SOURCE_Y", "Stochastic_Source_Y", "BACKSCATTER", "y-component of the stochastic source vector"); + if (nDim==3) AddVolumeOutput("STOCHASTIC_SOURCE_Z", "Stochastic_Source_Z", "BACKSCATTER", "z-component of the stochastic source vector"); } } @@ -1622,6 +1625,9 @@ void CFlowOutput::LoadVolumeDataScalar(const CConfig* config, const CSolver* con SetVolumeOutputValue("STOCHASTIC_VAR_X", iPoint, Node_Turb->GetSolution(iPoint, 1)); SetVolumeOutputValue("STOCHASTIC_VAR_Y", iPoint, Node_Turb->GetSolution(iPoint, 2)); if (nDim==3) SetVolumeOutputValue("STOCHASTIC_VAR_Z", iPoint, Node_Turb->GetSolution(iPoint, 3)); + SetVolumeOutputValue("STOCHASTIC_SOURCE_X", iPoint, Node_Turb->GetLangevinSourceTerms(iPoint, 0)); + SetVolumeOutputValue("STOCHASTIC_SOURCE_Y", iPoint, Node_Turb->GetLangevinSourceTerms(iPoint, 1)); + if (nDim==3) SetVolumeOutputValue("STOCHASTIC_SOURCE_Z", iPoint, Node_Turb->GetLangevinSourceTerms(iPoint, 2)); } } diff --git a/SU2_CFD/src/solvers/CTurbSASolver.cpp b/SU2_CFD/src/solvers/CTurbSASolver.cpp index dbbaafe1cf8b..6750be81dd1b 100644 --- a/SU2_CFD/src/solvers/CTurbSASolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp @@ -142,17 +142,14 @@ CTurbSASolver::CTurbSASolver(CGeometry *geometry, CConfig *config, unsigned shor fv1 = Ji_3/(Ji_3+cv1_3); muT_Inf = Density_Inf*fv1*nu_tilde_Inf; + /*--- Allocate memory for boundary conditions ---*/ ext_AverageNu = new su2double [nVar] (); - Res_Wall = new su2double [nVar] (); - Jacobian_i = new su2double* [nVar]; for (unsigned short iVar = 0; iVar < nVar; iVar++) { Jacobian_i[iVar] = new su2double [nVar] (); } - nu_tilde_inturb = new su2double [nVar] (); - nu_tilde_WF = new su2double [nVar] (); /*--- Initialize the solution to the far-field state everywhere. ---*/ @@ -160,6 +157,10 @@ CTurbSASolver::CTurbSASolver(CGeometry *geometry, CConfig *config, unsigned shor nodes = new CTurbSAVariable(nu_tilde_Inf, muT_Inf, nPoint, nDim, nVar, config); SetBaseClassPointerToNodes(); + /*--- Set seed for Langevin equations (Stochastic Backscatter Model) ---*/ + + if (backscatter) { SetLangevinSeed(geometry); } + /*--- MPI solution ---*/ InitiateComms(geometry, config, MPI_QUANTITIES::SOLUTION_EDDY); @@ -239,7 +240,10 @@ void CTurbSASolver::Preprocessing(CGeometry *geometry, CSolver **solver_containe /*--- Compute source terms for Langevin equations ---*/ - if (config->GetStochastic_Backscatter()) SetLangevinSourceTerms(config, geometry); + bool backscatter = config->GetStochastic_Backscatter(); + unsigned long innerIter = config->GetInnerIter(); + + if (backscatter && innerIter==0) SetLangevinSourceTerms(config, geometry); } @@ -415,7 +419,6 @@ void CTurbSASolver::Source_Residual(CGeometry *geometry, CSolver **solver_contai /*--- Set DES length scale ---*/ numerics->SetDistance(nodes->GetDES_LengthScale(iPoint), 0.0); - numerics->SetLESSensor(nodes->GetLES_Mode(iPoint), 0.0); /*--- Compute source terms in Langevin equations (Stochastic Basckscatter Model) ---*/ @@ -1587,14 +1590,28 @@ void CTurbSASolver::SetDES_LengthScale(CSolver **solver, CGeometry *geometry, CC void CTurbSASolver::SetLangevinSourceTerms(CConfig *config, CGeometry* geometry) { - uint64_t currentTime = static_cast(config->GetTimeIter()); + SU2_OMP_FOR_DYN(omp_chunk_size) + for (auto iPoint = 0ul; iPoint < nPointDomain; iPoint++){ + for (auto iDim = 0u; iDim < nDim; iDim++){ + unsigned long seed = nodes->GetLangevinSeed(iPoint, iDim); + su2double lesSensor = nodes->GetLES_Mode(iPoint); + su2double rnd = RandomToolbox::GetRandomNormal(seed); + rnd *= std::nearbyint(lesSensor); + nodes->SetLangevinSourceTerms(iPoint, iDim, rnd); + } + } + END_SU2_OMP_FOR + +} + +void CTurbSASolver::SetLangevinSeed(CGeometry* geometry) { SU2_OMP_FOR_DYN(omp_chunk_size) for (auto iPoint = 0ul; iPoint < nPointDomain; iPoint++){ const auto iGlobalPoint = geometry->nodes->GetGlobalIndex(iPoint); for (auto iDim = 0u; iDim < nDim; iDim++){ - uint64_t seed = RandomToolbox::GetSeed(currentTime+1, iGlobalPoint+iDim+1); - nodes->SetLangevinSourceTerms(iPoint, iDim, RandomToolbox::GetRandomNormal(seed)); + unsigned long seed = RandomToolbox::GetSeed(iGlobalPoint+1,iDim+1); + nodes->SetLangevinSeed(iPoint, iDim, seed); } } END_SU2_OMP_FOR diff --git a/SU2_CFD/src/variables/CTurbSAVariable.cpp b/SU2_CFD/src/variables/CTurbSAVariable.cpp index 3450ebfbcd42..12b1e415c186 100644 --- a/SU2_CFD/src/variables/CTurbSAVariable.cpp +++ b/SU2_CFD/src/variables/CTurbSAVariable.cpp @@ -61,6 +61,8 @@ CTurbSAVariable::CTurbSAVariable(su2double val_nu_tilde, su2double val_muT, unsi LES_Mode.resize(nPoint) = su2double(0.0); Vortex_Tilting.resize(nPoint); stochSource.resize(nPoint, nDim) = su2double(0.0); + stochSeed.resize(nPoint, nDim) = 0ul; + } void CTurbSAVariable::SetVortex_Tilting(unsigned long iPoint, CMatrixView PrimGrad_Flow, diff --git a/config_template.cfg b/config_template.cfg index 2422dcf60b20..f11335c5230c 100644 --- a/config_template.cfg +++ b/config_template.cfg @@ -183,6 +183,9 @@ DES_CONST= 0.65 % % Stochastic Backscatter Model (NO, YES) STOCHASTIC_BACKSCATTER= NO +% +% Backscatter timescale coefficient (0.001) +SBS_CTAU= 0.001 % -------------------- COMPRESSIBLE FREE-STREAM DEFINITION --------------------% % From 1e8e4e1e39b7bbef50f1e958c8505e4684b558e1 Mon Sep 17 00:00:00 2001 From: Angelo Passariello Date: Mon, 13 Oct 2025 10:23:40 +0200 Subject: [PATCH 08/25] Add option for simulating DIHT - Add the flag for the simulation of the Decaying Isotropic Homogeneous Turbulence. - Generate the initial velocity field matching the experimental data by Comte-Bellot & Corrsin. - Include a preliminary test case. --- Common/include/CConfig.hpp | 31 ++++ Common/include/toolboxes/random_toolbox.hpp | 17 ++- Common/src/CConfig.cpp | 21 +++ SU2_CFD/include/solvers/CTurbSASolver.hpp | 2 +- SU2_CFD/include/variables/CEulerVariable.hpp | 12 ++ SU2_CFD/include/variables/CTurbSAVariable.hpp | 9 +- SU2_CFD/include/variables/CVariable.hpp | 8 +- SU2_CFD/src/solvers/CEulerSolver.cpp | 4 + SU2_CFD/src/solvers/CTurbSASolver.cpp | 40 +++-- SU2_CFD/src/variables/CEulerVariable.cpp | 142 ++++++++++++++++++ SU2_CFD/src/variables/CTurbSAVariable.cpp | 2 +- .../backscatter/DIHT/backscatter_DIHT.cfg | 107 +++++++++++++ config_template.cfg | 12 ++ 13 files changed, 385 insertions(+), 22 deletions(-) create mode 100644 TestCases/backscatter/DIHT/backscatter_DIHT.cfg diff --git a/Common/include/CConfig.hpp b/Common/include/CConfig.hpp index 5a89fa1e979c..0e0905073df0 100644 --- a/Common/include/CConfig.hpp +++ b/Common/include/CConfig.hpp @@ -1084,6 +1084,10 @@ class CConfig { unsigned short Kind_HybridRANSLES; /*!< \brief Kind of Hybrid RANS/LES. */ bool StochasticBackscatter; /*!< \brief Option to include Stochastic Backscatter Model. */ su2double SBS_Ctau; /*!< \brief Stochastic Backscatter Model timescale coefficient. */ + bool DIHT; /*!< \brief Option to simulate Decaying Isotropic Homogeneous Turbulence (DIHT). */ + su2double DIHT_DomainLength[3]; /*!< \brief Domain length in each direction (DIHT). */ + su2double DIHT_nPoint[3]; /*!< \brief Number of points in each direction (DIHT). */ + unsigned long DIHT_nModes; /*!< \brief Number of Fourier modes (DIHT). */ unsigned short Kind_RoeLowDiss; /*!< \brief Kind of Roe scheme with low dissipation for unsteady flows. */ unsigned short nSpanWiseSections; /*!< \brief number of span-wise sections */ @@ -9543,6 +9547,33 @@ class CConfig { */ bool GetStochastic_Backscatter(void) const { return StochasticBackscatter; } + /*! + * \brief Get if the Decaying Isotropic Homogeneous Turbulence (DIHT) must be simulated. + * \return TRUE if DIHT is simulated. + */ + bool GetDIHT(void) const { return DIHT; } + + /*! + * \brief Get the computational domain length (DIHT). + * \param[in] iDim - spatial component + * \return Domain length. + */ + su2double GetDIHT_DomainLength(unsigned short iDim) const { return DIHT_DomainLength[iDim]*Length_Ref;} + + /*! + * \brief Get the number of grid points in each direction (DIHT). + * \param[in] iDim - spatial component + * \return Number of grid points. + */ + su2double GetDIHT_nPoint(unsigned short iDim) const { return DIHT_nPoint[iDim];} + + /*! + * \brief Get the number of Fourier modes (DIHT). + * \param[in] iDim - spatial component + * \return Number of Fourier modes. + */ + unsigned long GetDIHT_nModes() const { return DIHT_nModes;} + /*! * \brief Get the Kind of Roe Low Dissipation Scheme for Unsteady flows. * \return Value of Low dissipation approach. diff --git a/Common/include/toolboxes/random_toolbox.hpp b/Common/include/toolboxes/random_toolbox.hpp index 811691ac772a..fe8bc8ca9ebb 100644 --- a/Common/include/toolboxes/random_toolbox.hpp +++ b/Common/include/toolboxes/random_toolbox.hpp @@ -63,16 +63,27 @@ inline unsigned long GetSeed(unsigned long x, unsigned long y) { return HashComb /*! * \brief Generate a standard normally-distributed random number. - * \param[in] seed Seed for the random number generator. + * \param[in] gen Pseudo-random number generator. * \param[in] mean Mean of the normal distribution (default 0). * \param[in] stddev Standard deviation of the normal distribution (default 1). * \return Normally-distributed random number. */ -inline double GetRandomNormal(unsigned long seed, double mean = 0.0, double stddev = 1.0) { - std::mt19937 gen(seed); +inline double GetRandomNormal(std::mt19937 gen, double mean = 0.0, double stddev = 1.0) { std::normal_distribution rnd(mean, stddev); return rnd(gen); } +/*! + * \brief Generate a uniformly-distributed random number. + * \param[in] gen Pseudo-random number generator. + * \param[in] xmin Lower boundary of the interval (default 0). + * \param[in] xmax Upper bounary of the interval (default 1). + * \return Uniformly-distributed random number. + */ +inline double GetRandomUniform(std::mt19937 gen, double xmin = 0.0, double xmax = 1.0) { + std::uniform_real_distribution rnd(xmin, xmax); + return rnd(gen); +} + /// @} } // namespace RandomToolbox diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index 0e9320a7c179..8c963b3bea00 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -2920,6 +2920,18 @@ void CConfig::SetConfig_Options() { /* DESCRIPTION: Specify if the Stochastic Backscatter Model must be activated */ addBoolOption("STOCHASTIC_BACKSCATTER", StochasticBackscatter, false); + /* DESCRIPTION: Specify if the Decay of Isotropic Homogeneous Turbulence (DIHT) must be simulated */ + addBoolOption("DIHT", DIHT, false); + + /* DESCRIPTION: Domain length in x, y and z directions (DIHT) */ + addDoubleArrayOption("DIHT_DOMAIN_LENGTH", 3, DIHT_DomainLength); + + /* DESCRIPTION: Spacing of the grid points in x, y and z directions (DIHT) */ + addDoubleArrayOption("DIHT_NPOINT", 3, DIHT_nPoint); + + /* DESCRIPTION: Number of Fourier modes (DIHT) */ + addUnsignedLongOption("DIHT_NUM_MODES", DIHT_nModes, 1000); + /* DESCRIPTION: Roe with low dissipation for unsteady flows */ addEnumOption("ROE_LOW_DISSIPATION", Kind_RoeLowDiss, RoeLowDiss_Map, NO_ROELOWDISS); @@ -6462,6 +6474,15 @@ void CConfig::SetOutput(SU2_COMPONENT val_software, unsigned short val_izone) { } if (StochasticBackscatter && Kind_HybridRANSLES == NO_HYBRIDRANSLES) SU2_MPI::Error("Stochastic Backscatter can only be activated with Hybrid RANS/LES.", CURRENT_FUNCTION); + if (DIHT) { + cout << "Decaying Isotropic Homogeneous Turbulence (DIHT): spectrum by Comte-Bellot & Corrsin (1971)." << endl; + cout << "WARNING: DIHT algorithm is only compatible with structured grids." << endl; + cout << "Computational domain size: " << DIHT_DomainLength[0] << ", " << DIHT_DomainLength[1] << ", " << DIHT_DomainLength[2] << " (L_REF)" << endl; + cout << "Number of grid points in x, y and z directions: " << static_cast(DIHT_nPoint[0]) << ", " << static_cast(DIHT_nPoint[1]) << ", " << static_cast(DIHT_nPoint[2]) << endl; + cout << "Number of Fourier modes: " << DIHT_nModes << endl; + if (Kind_HybridRANSLES == NO_HYBRIDRANSLES) + SU2_MPI::Error("DIHT mode can only be activated with Hybrid RANS/LES.", CURRENT_FUNCTION); + } break; case MAIN_SOLVER::NEMO_EULER: if (Kind_Regime == ENUM_REGIME::COMPRESSIBLE) cout << "Compressible two-temperature thermochemical non-equilibrium Euler equations." << endl; diff --git a/SU2_CFD/include/solvers/CTurbSASolver.hpp b/SU2_CFD/include/solvers/CTurbSASolver.hpp index 2a6a489f3048..1f08fe9566b4 100644 --- a/SU2_CFD/include/solvers/CTurbSASolver.hpp +++ b/SU2_CFD/include/solvers/CTurbSASolver.hpp @@ -69,7 +69,7 @@ class CTurbSASolver final : public CTurbSolver { * \param[in] config - Definition of the particular problem. * \param[in] geometry - Geometrical definition. */ - void SetLangevinSeed(CGeometry* geometry); + void SetLangevinGen(CConfig* config, CGeometry* geometry); /*! * \brief Compute nu tilde from the wall functions. diff --git a/SU2_CFD/include/variables/CEulerVariable.hpp b/SU2_CFD/include/variables/CEulerVariable.hpp index 4d49f3825721..82102a7cf447 100644 --- a/SU2_CFD/include/variables/CEulerVariable.hpp +++ b/SU2_CFD/include/variables/CEulerVariable.hpp @@ -29,6 +29,7 @@ #include #include "CFlowVariable.hpp" +#include "../../../Common/include/geometry/CGeometry.hpp" /*! * \brief Returns the number of primitive variables for which to compute gradients. @@ -345,4 +346,15 @@ class CEulerVariable : public CFlowVariable { */ inline unsigned long GetNewtonSolverIterations(unsigned long iPoint) const final { return NIterNewtonsolver[iPoint]; } + /*! + * \brief Generate initial random velocity field (Decaying Isotropic Homogeneous Turbulence). + * \param[in] npoint - Number of points/nodes/vertices in the domain. + * \param[in] ndim - Number of dimensions of the problem. + * \param[in] nvar - Number of variables of the problem. + * \param[in] config - Definition of the particular problem. + */ + virtual void SetVelDIHT(unsigned long npoint, unsigned long ndim, unsigned long nvar, const CConfig *config, + const CGeometry *geometry); + + }; diff --git a/SU2_CFD/include/variables/CTurbSAVariable.hpp b/SU2_CFD/include/variables/CTurbSAVariable.hpp index b47585dab6fd..000f05d13d9d 100644 --- a/SU2_CFD/include/variables/CTurbSAVariable.hpp +++ b/SU2_CFD/include/variables/CTurbSAVariable.hpp @@ -28,6 +28,7 @@ #pragma once #include "CTurbVariable.hpp" +#include /*! * \class CTurbSAVariable @@ -43,7 +44,7 @@ class CTurbSAVariable final : public CTurbVariable { VectorType LES_Mode; MatrixType stochSource; VectorType Vortex_Tilting; - MatrixTypeInt stochSeed; + MatrixTypeGen stochGen; public: /*! @@ -123,14 +124,14 @@ class CTurbSAVariable final : public CTurbVariable { * \param[in] iDim - Dimension index. * \return Value of the seed for the stochastic equations. */ - inline su2double GetLangevinSeed(unsigned long iPoint, unsigned short iDim) const override { return stochSeed(iPoint, iDim); } + inline std::mt19937 GetLangevinGen(unsigned long iPoint, unsigned short iDim) const override { return stochGen(iPoint, iDim); } /*! * \brief Set the seed for the stochastic equations. * \param[in] iPoint - Point index. * \param[in] iDim - Dimension index. - * \param[in] val_stochSeed - Value of the seed for the stochastic equations. + * \param[in] val_stochGen - Value of the seed for the stochastic equations. */ - inline void SetLangevinSeed(unsigned long iPoint, unsigned short iDim, unsigned long val_stochSeed) override { stochSeed(iPoint, iDim) = val_stochSeed; } + inline void SetLangevinGen(unsigned long iPoint, unsigned short iDim, std::mt19937 val_stochGen) override { stochGen(iPoint, iDim) = val_stochGen; } }; diff --git a/SU2_CFD/include/variables/CVariable.hpp b/SU2_CFD/include/variables/CVariable.hpp index 6b1159d38881..b01b01bc9dba 100644 --- a/SU2_CFD/include/variables/CVariable.hpp +++ b/SU2_CFD/include/variables/CVariable.hpp @@ -34,6 +34,7 @@ #include #include #include +#include #include "../../../Common/include/CConfig.hpp" #include "../../../Common/include/containers/container_decorators.hpp" @@ -51,7 +52,7 @@ class CVariable { protected: using VectorType = C2DContainer; using MatrixType = C2DContainer; - using MatrixTypeInt = C2DContainer; + using MatrixTypeGen = C2DContainer; MatrixType Solution; /*!< \brief Solution of the problem. */ MatrixType Solution_Old; /*!< \brief Old solution of the problem R-K. */ @@ -429,9 +430,8 @@ class CVariable { * \brief A virtual member. * \param[in] iPoint - Point index. * \param[in] iDim - Dimension index. - * \param[in] val_stochSeed - Seed for Langevin equations. */ - inline virtual su2double GetLangevinSeed(unsigned long iPoint, unsigned short iDim) const {return 0.0;} + inline virtual std::mt19937 GetLangevinGen(unsigned long iPoint, unsigned short iDim) const {std::mt19937 gen(123); return gen; } /*! * \brief A virtual member. @@ -439,7 +439,7 @@ class CVariable { * \param[in] iDim - Dimension index. * \param[in] val_stochSource - Source term for Langevin equations. */ - inline virtual void SetLangevinSeed(unsigned long iPoint, unsigned short iDim, unsigned long val_stochSeed) {} + inline virtual void SetLangevinGen(unsigned long iPoint, unsigned short iDim, std::mt19937 val_stochGen) {} /*! * \brief A virtual member. diff --git a/SU2_CFD/src/solvers/CEulerSolver.cpp b/SU2_CFD/src/solvers/CEulerSolver.cpp index 5b85687071ff..ecb0e8ce0cc4 100644 --- a/SU2_CFD/src/solvers/CEulerSolver.cpp +++ b/SU2_CFD/src/solvers/CEulerSolver.cpp @@ -298,6 +298,10 @@ CEulerSolver::CEulerSolver(CGeometry *geometry, CConfig *config, } SetBaseClassPointerToNodes(); + /*--- Generate initial random velocity field (Decaying Isotropic Homogeneous Turbulence). ---*/ + + if (config->GetDIHT()) nodes->SetVelDIHT(nPoint, nDim, nVar, config, geometry); + if (iMesh == MESH_0) { nodes->NonPhysicalEdgeCounter.resize(geometry->GetnEdge()) = 0; } diff --git a/SU2_CFD/src/solvers/CTurbSASolver.cpp b/SU2_CFD/src/solvers/CTurbSASolver.cpp index 6750be81dd1b..7a47b77fa91f 100644 --- a/SU2_CFD/src/solvers/CTurbSASolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp @@ -157,10 +157,6 @@ CTurbSASolver::CTurbSASolver(CGeometry *geometry, CConfig *config, unsigned shor nodes = new CTurbSAVariable(nu_tilde_Inf, muT_Inf, nPoint, nDim, nVar, config); SetBaseClassPointerToNodes(); - /*--- Set seed for Langevin equations (Stochastic Backscatter Model) ---*/ - - if (backscatter) { SetLangevinSeed(geometry); } - /*--- MPI solution ---*/ InitiateComms(geometry, config, MPI_QUANTITIES::SOLUTION_EDDY); @@ -243,7 +239,10 @@ void CTurbSASolver::Preprocessing(CGeometry *geometry, CSolver **solver_containe bool backscatter = config->GetStochastic_Backscatter(); unsigned long innerIter = config->GetInnerIter(); - if (backscatter && innerIter==0) SetLangevinSourceTerms(config, geometry); + if (backscatter && innerIter==0) { + SetLangevinGen(config, geometry); + SetLangevinSourceTerms(config, geometry); + } } @@ -1466,6 +1465,11 @@ void CTurbSASolver::SetDES_LengthScale(CSolver **solver, CGeometry *geometry, CC lengthScale = min(distDES,wallDistance); lesSensor = (wallDistance<=distDES) ? 0.0 : 1.0; + if (config->GetDIHT()) { + lengthScale = distDES; + lesSensor = 1.0; + } + break; } case SA_DDES: { @@ -1483,6 +1487,11 @@ void CTurbSASolver::SetDES_LengthScale(CSolver **solver, CGeometry *geometry, CC lengthScale = wallDistance-f_d*max(0.0,(wallDistance-distDES)); lesSensor = (wallDistance<=distDES) ? 0.0 : f_d; + if (config->GetDIHT()) { + lengthScale = distDES; + lesSensor = 1.0; + } + break; } case SA_ZDES: { @@ -1524,6 +1533,11 @@ void CTurbSASolver::SetDES_LengthScale(CSolver **solver, CGeometry *geometry, CC lengthScale = wallDistance-f_d*max(0.0,(wallDistance-distDES)); lesSensor = (wallDistance<=distDES) ? 0.0 : f_d; + if (config->GetDIHT()) { + lengthScale = distDES; + lesSensor = 1.0; + } + break; } case SA_EDDES: { @@ -1577,6 +1591,11 @@ void CTurbSASolver::SetDES_LengthScale(CSolver **solver, CGeometry *geometry, CC lengthScale = wallDistance-f_d*max(0.0,(wallDistance-distDES)); lesSensor = (wallDistance<=distDES) ? 0.0 : f_d; + if (config->GetDIHT()) { + lengthScale = distDES; + lesSensor = 1.0; + } + break; } } @@ -1593,9 +1612,9 @@ void CTurbSASolver::SetLangevinSourceTerms(CConfig *config, CGeometry* geometry) SU2_OMP_FOR_DYN(omp_chunk_size) for (auto iPoint = 0ul; iPoint < nPointDomain; iPoint++){ for (auto iDim = 0u; iDim < nDim; iDim++){ - unsigned long seed = nodes->GetLangevinSeed(iPoint, iDim); + auto gen = nodes->GetLangevinGen(iPoint, iDim); su2double lesSensor = nodes->GetLES_Mode(iPoint); - su2double rnd = RandomToolbox::GetRandomNormal(seed); + su2double rnd = RandomToolbox::GetRandomNormal(gen); rnd *= std::nearbyint(lesSensor); nodes->SetLangevinSourceTerms(iPoint, iDim, rnd); } @@ -1604,14 +1623,17 @@ void CTurbSASolver::SetLangevinSourceTerms(CConfig *config, CGeometry* geometry) } -void CTurbSASolver::SetLangevinSeed(CGeometry* geometry) { +void CTurbSASolver::SetLangevinGen(CConfig* config, CGeometry* geometry) { + + unsigned long timeStep = config->GetTimeIter(); SU2_OMP_FOR_DYN(omp_chunk_size) for (auto iPoint = 0ul; iPoint < nPointDomain; iPoint++){ const auto iGlobalPoint = geometry->nodes->GetGlobalIndex(iPoint); for (auto iDim = 0u; iDim < nDim; iDim++){ unsigned long seed = RandomToolbox::GetSeed(iGlobalPoint+1,iDim+1); - nodes->SetLangevinSeed(iPoint, iDim, seed); + std::mt19937 gen(seed + timeStep); + nodes->SetLangevinGen(iPoint, iDim, gen); } } END_SU2_OMP_FOR diff --git a/SU2_CFD/src/variables/CEulerVariable.cpp b/SU2_CFD/src/variables/CEulerVariable.cpp index 263530705c87..cabdfcfeff68 100644 --- a/SU2_CFD/src/variables/CEulerVariable.cpp +++ b/SU2_CFD/src/variables/CEulerVariable.cpp @@ -27,6 +27,7 @@ #include "../../include/variables/CEulerVariable.hpp" #include "../../include/fluid/CFluidModel.hpp" +#include "../../../Common/include/toolboxes/random_toolbox.hpp" unsigned long EulerNPrimVarGrad(const CConfig *config, unsigned long ndim) { if (config->GetContinuous_Adjoint()) return ndim + 4; @@ -161,3 +162,144 @@ void CEulerVariable::SetSecondaryVar(unsigned long iPoint, CFluidModel *FluidMod SetdPde_rho(iPoint, FluidModel->GetdPde_rho()); } + +void CEulerVariable::SetVelDIHT(unsigned long npoint, unsigned long ndim, unsigned long nvar, const CConfig *config, + const CGeometry *geometry) { + + const unsigned short MAXnModes = 5000; + const unsigned short nModes = config->GetDIHT_nModes(); + if (nModes > MAXnModes) SU2_MPI::Error("The maximum number of Fourier modes for DIHT is 5000.", CURRENT_FUNCTION); + if (nModes <= 0) SU2_MPI::Error("Assign a valid value for the number of Fourier modes. (DIHT)", CURRENT_FUNCTION); + + const su2double Lx = config->GetDIHT_DomainLength(0); + const su2double Ly = config->GetDIHT_DomainLength(1); + const su2double Lz = config->GetDIHT_DomainLength(2); + if (Lx <= 0.0 || Ly <= 0.0 || Lz <= 0.0) SU2_MPI::Error("Assign a valid value for the computational domain size. (DIHT)", CURRENT_FUNCTION); + + const unsigned long nx = static_cast(config->GetDIHT_nPoint(0)); + const unsigned long ny = static_cast(config->GetDIHT_nPoint(1)); + const unsigned long nz = static_cast(config->GetDIHT_nPoint(2)); + if (nx <= 0 || ny <= 0 || nz <= 0) SU2_MPI::Error("Assign a valid value for the number of nodes. (DIHT)", CURRENT_FUNCTION); + + const su2double pi = 4.0 * atan(1.0); + const su2double vref = config->GetVelocity_Ref(); + su2double Density_Inf = config->GetDensity_FreeStreamND(); + su2double ModVel_Freestream = config->GetModVel_FreeStream(); + + su2double k_cbc[39] = {0.11, 0.15, 0.20, 0.25, 0.30, 0.40, 0.50, 0.70, 1.00, + 1.50, 2.00, 2.50, 3.00, 4.00, 6.00, 8.00, 10.0, 12.5, + 15.0, 17.5, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, + 90.0, 100., 110., 120., 130., 140., 150., 160., 170., + 180., 190., 200.}; + + su2double espec_cbc[39] = {30.0, 60.0, 129., 230., 322., 435., 457., 380., + 270., 168., 120., 89.0, 70.3, 47.0, 24.7, 12.6, + 7.42, 3.96, 2.33, 1.34, 0.80, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + + for (unsigned short ind = 0; ind < 39; ind++) { + k_cbc[ind] *= 100.0; + espec_cbc[ind] *= 1.0e-6; + } + + su2double dx = Lx/nx; + su2double dy = Ly/ny; + su2double dz = Lz/nz; + + if (ndim < 3) { + SU2_MPI::Error("DIHT: Decay of turbulence must be three-dimensional.", CURRENT_FUNCTION); + } + + su2double wmin = min(2.0*pi/Lx, min(2.0*pi/Ly, 2.0*pi/Lz)); + su2double wmax = max(pi/dx, max(pi/dy, pi/dz)); + su2double dk = (wmax-wmin)/nModes; + + auto espec_interpol = [&] (su2double k_) { + + if (k_k_cbc[38]) return espec_cbc[38]; + + unsigned short ind = 1; + while (ind < 39 && k_cbc[ind] < k_) ind++; + + su2double km = k_cbc[ind-1]; + su2double kp = k_cbc[ind]; + su2double em = espec_cbc[ind-1]; + su2double ep = espec_cbc[ind]; + + su2double de_dk = (ep-em)/(kp-km); + su2double e_interp = em + de_dk * (k_-km); + + return e_interp; + + }; + + unsigned long seed = RandomToolbox::GetSeed(npoint, ndim + nvar); + + su2double phi[MAXnModes], theta[MAXnModes], psi[MAXnModes], phi1[MAXnModes], theta1[MAXnModes]; + + for (unsigned long iMode = 0; iMode < nModes; iMode++) { + std::mt19937 gen(seed + iMode*nModes); + phi[iMode] = RandomToolbox::GetRandomUniform(gen, 0.0, 2.0*pi); + theta[iMode] = acos(RandomToolbox::GetRandomUniform(gen, -1.0, 1.0)); + psi[iMode] = RandomToolbox::GetRandomUniform(gen, -0.5*pi, 0.5*pi); + phi1[iMode] = RandomToolbox::GetRandomUniform(gen, 0.0, 2.0*pi); + theta1[iMode] = acos(RandomToolbox::GetRandomUniform(gen, -1.0, 1.0)); + } + + for (unsigned long iPoint = 0; iPoint < npoint; iPoint++) { + su2double u = 0.0, v = 0.0, w = 0.0; + su2double xp = geometry->nodes->GetCoord(iPoint,0); + su2double yp = geometry->nodes->GetCoord(iPoint,1); + su2double zp = geometry->nodes->GetCoord(iPoint,2); + for (unsigned long iMode = 0; iMode < nModes; iMode++) { + su2double wn = wmin + 0.5*dk + iMode*dk; + su2double kx = sin(theta[iMode]) * cos(phi[iMode]) * wn; + su2double ky = sin(theta[iMode]) * sin(phi[iMode]) * wn; + su2double kz = cos(theta[iMode]) * wn; + su2double ktx = sin(kx * dx * 0.5) / dx; + su2double kty = sin(ky * dy * 0.5) / dy; + su2double ktz = sin(kz * dz * 0.5) / dz; + su2double zetax = sin(theta1[iMode]) * cos(phi1[iMode]); + su2double zetay = sin(theta1[iMode]) * sin(phi1[iMode]); + su2double zetaz = cos(theta1[iMode]); + su2double sxm = zetay*ktz - zetaz*kty; + su2double sym = zetaz*ktx - zetax*ktz; + su2double szm = zetax*kty - zetay*ktx; + su2double smag = sqrt(sxm*sxm + sym*sym + szm*szm + 1.0e-16); + sxm /= smag; sym /= smag; szm /= smag; + su2double espec = espec_interpol(wn); + su2double qm = sqrt(espec*dk); + su2double arg = kx * xp + ky * yp + kz * zp - psi[iMode]; + su2double facx = 2.0 * qm * cos(arg - kx*dx*0.5); + su2double facy = 2.0 * qm * cos(arg - ky*dy*0.5); + su2double facz = 2.0 * qm * cos(arg - kz*dz*0.5); + u += facx * sxm; + v += facy * sym; + w += facz * szm; + } + Solution(iPoint, 1) = Density_Inf * u/vref; + Solution(iPoint, 2) = Density_Inf * v/vref; + Solution(iPoint, 3) = Density_Inf * w/vref; + su2double q2 = Solution(iPoint, 1)*Solution(iPoint, 1) + + Solution(iPoint, 2)*Solution(iPoint, 2) + + Solution(iPoint, 3)*Solution(iPoint, 3); + Solution(iPoint, 4) += 0.5 * (q2/Density_Inf - Density_Inf*ModVel_Freestream*ModVel_Freestream); + } + + const bool dual_time = (config->GetTime_Marching() == TIME_MARCHING::DT_STEPPING_1ST) || + (config->GetTime_Marching() == TIME_MARCHING::DT_STEPPING_2ND); + + const bool classical_rk4 = (config->GetKind_TimeIntScheme_Flow() == CLASSICAL_RK4_EXPLICIT); + + Solution_Old = Solution; + + if (classical_rk4) Solution_New = Solution; + + if (dual_time) { + Solution_time_n = Solution; + Solution_time_n1 = Solution; + } + +} \ No newline at end of file diff --git a/SU2_CFD/src/variables/CTurbSAVariable.cpp b/SU2_CFD/src/variables/CTurbSAVariable.cpp index 12b1e415c186..e463909ecf08 100644 --- a/SU2_CFD/src/variables/CTurbSAVariable.cpp +++ b/SU2_CFD/src/variables/CTurbSAVariable.cpp @@ -61,7 +61,7 @@ CTurbSAVariable::CTurbSAVariable(su2double val_nu_tilde, su2double val_muT, unsi LES_Mode.resize(nPoint) = su2double(0.0); Vortex_Tilting.resize(nPoint); stochSource.resize(nPoint, nDim) = su2double(0.0); - stochSeed.resize(nPoint, nDim) = 0ul; + stochGen.resize(nPoint, nDim); } diff --git a/TestCases/backscatter/DIHT/backscatter_DIHT.cfg b/TestCases/backscatter/DIHT/backscatter_DIHT.cfg new file mode 100644 index 000000000000..6fd2aa724855 --- /dev/null +++ b/TestCases/backscatter/DIHT/backscatter_DIHT.cfg @@ -0,0 +1,107 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% % +% SU2 configuration file % +% Case description: Decaying Isotropic Homogeneous Turbulence (DIHT) % +% Author: Angelo Passariello % +% Institution: University of Naples Federico II % +% Date: 2025.10.01 % +% File Version 8.3.0 "Harrier" % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% ------------- DIRECT, ADJOINT, AND LINEARIZED PROBLEM DEFINITION ------------% + +SOLVER= RANS +KIND_TURB_MODEL= SA +HYBRID_RANSLES= SA_DES +MATH_PROBLEM= DIRECT +RESTART_SOL= NO + +% ------------- STOCHASTIC BACKSCATTER MODEL PARAMETERS -----------------------% + +STOCHASTIC_BACKSCATTER= YES +SBS_CTAU= 0.001 + +% ------------- DECAYING ISOTROPIC HOMOGENEOUS TURBULENCE ---------------------% + +DIHT= YES +DIHT_DOMAIN_LENGTH= (0.5588, 0.5588, 0.5588) +DIHT_NPOINT= (64, 64, 64) +DIHT_NUM_MODES= 800 + +% ----------- COMPRESSIBLE AND INCOMPRESSIBLE FREE-STREAM DEFINITION ----------% + +MACH_NUMBER= 0.001 +FREESTREAM_TEMPERATURE= 300.0 +REYNOLDS_NUMBER= 10129 +REYNOLDS_LENGTH= 0.5588 +FREESTREAM_NU_FACTOR= 1.0 + +% ---------------------- REFERENCE VALUE DEFINITION ---------------------------% + +REF_LENGTH= 1.0 +REF_AREA= 1.0 + +% ------------------------- UNSTEADY SIMULATION -------------------------------% + +TIME_DOMAIN= NO +%TIME_MARCHING= DUAL_TIME_STEPPING-2ND_ORDER +%TIME_STEP= 0.001 +%MAX_TIME= 20 +%UNST_CFL_NUMBER= 0.0 +INNER_ITER= 0 + +% -------------------- BOUNDARY CONDITION DEFINITION --------------------------% + +MARKER_PERIODIC= (xmin, xmax, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5588, 0.0, 0.0, ymin, ymax, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5588, 0.0, zmin, zmax, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5588) + +% ------------- COMMON PARAMETERS DEFINING THE NUMERICAL METHOD ---------------% + +NUM_METHOD_GRAD= GREEN_GAUSS +CFL_NUMBER= 10.0 +CFL_ADAPT= NO +CFL_ADAPT_PARAM= ( 1.5, 0.5, 1.0, 100.0 ) +RK_ALPHA_COEFF= ( 0.66667, 0.66667, 1.000000 ) +TIME_ITER= 1 +LINEAR_SOLVER_PREC= ILU +LOW_MACH_PREC= YES +%LOW_MACH_CORR= YES + +% ----------------------- SLOPE LIMITER DEFINITION ----------------------------% + +MUSCL_FLOW= YES +SLOPE_LIMITER_FLOW= NONE +MUSCL_TURB= NO +VENKAT_LIMITER_COEFF= 0.05 + +% -------------------- FLOW NUMERICAL METHOD DEFINITION -----------------------% +% +CONV_NUM_METHOD_FLOW= SLAU2 +TIME_DISCRE_FLOW= EULER_IMPLICIT + +% -------------------- TURBULENT NUMERICAL METHOD DEFINITION ------------------% +% +CONV_NUM_METHOD_TURB= SCALAR_UPWIND +TIME_DISCRE_TURB= EULER_IMPLICIT + +% --------------------------- CONVERGENCE PARAMETERS --------------------------% +% +CONV_RESIDUAL_MINVAL= -15 +CONV_STARTITER= 0 +CONV_CAUCHY_ELEMS= 100 +CONV_CAUCHY_EPS= 1E-6 + +% ------------------------- INPUT/OUTPUT INFORMATION --------------------------% +% +MESH_FILENAME= cube_64.su2 +MESH_FORMAT= SU2 +TABULAR_FORMAT= CSV +CONV_FILENAME= history +RESTART_FILENAME= restart_flow +VOLUME_FILENAME= flow +VOLUME_OUTPUT= DDES, BACKSCATTER +SCREEN_OUTPUT= (TIME_ITER, INNER_ITER, RMS_DENSITY, RMS_MOMENTUM-X, RMS_MOMENTUM-Y, RMS_MOMENTUM-Z, RMS_NU_TILDE, RMS_STOCH_VAR_X, RMS_STOCH_VAR_Y, RMS_STOCH_VAR_Z) +HISTORY_OUTPUT= (TIME_ITER, RMS_DENSITY, RMS_MOMENTUM-X, RMS_MOMENTUM-Y, RMS_MOMENTUM-Z, RMS_NU_TILDE) +HISTORY_WRT_FREQ_INNER= 1000000 +OUTPUT_WRT_FREQ= 1000000 +OUTPUT_FILES= (RESTART, PARAVIEW) diff --git a/config_template.cfg b/config_template.cfg index 87c89f60cb65..5b9edee47f38 100644 --- a/config_template.cfg +++ b/config_template.cfg @@ -186,6 +186,18 @@ STOCHASTIC_BACKSCATTER= NO % % Backscatter timescale coefficient (0.001) SBS_CTAU= 0.001 +% +% Decaying Isotropic Homogeneous Turbulence (DIHT) simulation (NO, YES) +DIHT= NO +% +% Domain length in x, y and z directions for DIHT (non-dimensional, based on the reference length) +DIHT_DOMAIN_LENGTH= (1.0, 1.0, 1.0) +% +% Number of points in x, y and z directions for DIHT (non-dimensional) +DIHT_NPOINT= (1, 1, 1) +% +% Number of Fourier modes for DIHT +DIHT_NUM_MODES= 1000 % -------------------- COMPRESSIBLE FREE-STREAM DEFINITION --------------------% % From 67c133cccd6f8087c270531fdadffc8f44b4d828 Mon Sep 17 00:00:00 2001 From: Angelo Passariello Date: Thu, 16 Oct 2025 09:19:20 +0200 Subject: [PATCH 09/25] Fix options for DIHT - Implement flag to enforce LES in the whole domain. - Add option to set the LES filter width to a user-specified value. - Add velocity divergence to volume outputs. - Remove internal generation of initial velocity field for simulating the Decaying Isotropic Homogeneous Turbulence (DIHT). --- Common/include/CConfig.hpp | 33 +--- Common/src/CConfig.cpp | 40 +++-- .../numerics/turbulent/turb_sources.hpp | 2 - SU2_CFD/include/variables/CEulerVariable.hpp | 12 -- SU2_CFD/src/output/CFlowCompOutput.cpp | 9 ++ SU2_CFD/src/output/CFlowIncOutput.cpp | 9 ++ SU2_CFD/src/solvers/CEulerSolver.cpp | 4 - SU2_CFD/src/solvers/CTurbSASolver.cpp | 26 +++- SU2_CFD/src/variables/CEulerVariable.cpp | 142 ------------------ config_template.cfg | 16 +- 10 files changed, 68 insertions(+), 225 deletions(-) diff --git a/Common/include/CConfig.hpp b/Common/include/CConfig.hpp index 0e0905073df0..078357f14fc7 100644 --- a/Common/include/CConfig.hpp +++ b/Common/include/CConfig.hpp @@ -1084,10 +1084,8 @@ class CConfig { unsigned short Kind_HybridRANSLES; /*!< \brief Kind of Hybrid RANS/LES. */ bool StochasticBackscatter; /*!< \brief Option to include Stochastic Backscatter Model. */ su2double SBS_Ctau; /*!< \brief Stochastic Backscatter Model timescale coefficient. */ - bool DIHT; /*!< \brief Option to simulate Decaying Isotropic Homogeneous Turbulence (DIHT). */ - su2double DIHT_DomainLength[3]; /*!< \brief Domain length in each direction (DIHT). */ - su2double DIHT_nPoint[3]; /*!< \brief Number of points in each direction (DIHT). */ - unsigned long DIHT_nModes; /*!< \brief Number of Fourier modes (DIHT). */ + bool enforceLES; /*!< \brief Option to enforce LES mode in hybrid RANS-LES simulations. */ + su2double LES_FilterWidth; /*!< \brief LES filter width for hybrid RANS-LES simulations. */ unsigned short Kind_RoeLowDiss; /*!< \brief Kind of Roe scheme with low dissipation for unsteady flows. */ unsigned short nSpanWiseSections; /*!< \brief number of span-wise sections */ @@ -9548,31 +9546,16 @@ class CConfig { bool GetStochastic_Backscatter(void) const { return StochasticBackscatter; } /*! - * \brief Get if the Decaying Isotropic Homogeneous Turbulence (DIHT) must be simulated. - * \return TRUE if DIHT is simulated. + * \brief Get if the LES mode must be enforced. + * \return TRUE if LES is enforced. */ - bool GetDIHT(void) const { return DIHT; } + bool GetEnforceLES(void) const { return enforceLES; } /*! - * \brief Get the computational domain length (DIHT). - * \param[in] iDim - spatial component - * \return Domain length. - */ - su2double GetDIHT_DomainLength(unsigned short iDim) const { return DIHT_DomainLength[iDim]*Length_Ref;} - - /*! - * \brief Get the number of grid points in each direction (DIHT). - * \param[in] iDim - spatial component - * \return Number of grid points. - */ - su2double GetDIHT_nPoint(unsigned short iDim) const { return DIHT_nPoint[iDim];} - - /*! - * \brief Get the number of Fourier modes (DIHT). - * \param[in] iDim - spatial component - * \return Number of Fourier modes. + * \brief Get the LES Filter Width. + * \return Value of LES Filter Width. */ - unsigned long GetDIHT_nModes() const { return DIHT_nModes;} + su2double GetLES_FilterWidth(void) const { return LES_FilterWidth; } /*! * \brief Get the Kind of Roe Low Dissipation Scheme for Unsteady flows. diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index 8c963b3bea00..21a683fcc9cf 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -2920,17 +2920,11 @@ void CConfig::SetConfig_Options() { /* DESCRIPTION: Specify if the Stochastic Backscatter Model must be activated */ addBoolOption("STOCHASTIC_BACKSCATTER", StochasticBackscatter, false); - /* DESCRIPTION: Specify if the Decay of Isotropic Homogeneous Turbulence (DIHT) must be simulated */ - addBoolOption("DIHT", DIHT, false); + /* DESCRIPTION: Specify if the LES mode must be enforced */ + addBoolOption("ENFORCE_LES", enforceLES, false); - /* DESCRIPTION: Domain length in x, y and z directions (DIHT) */ - addDoubleArrayOption("DIHT_DOMAIN_LENGTH", 3, DIHT_DomainLength); - - /* DESCRIPTION: Spacing of the grid points in x, y and z directions (DIHT) */ - addDoubleArrayOption("DIHT_NPOINT", 3, DIHT_nPoint); - - /* DESCRIPTION: Number of Fourier modes (DIHT) */ - addUnsignedLongOption("DIHT_NUM_MODES", DIHT_nModes, 1000); + /* DESCRIPTION: Filter width for LES (if negative, it is computed based on the local cell size) */ + addDoubleOption("LES_FILTER_WIDTH", LES_FilterWidth, -1.0); /* DESCRIPTION: Roe with low dissipation for unsteady flows */ addEnumOption("ROE_LOW_DISSIPATION", Kind_RoeLowDiss, RoeLowDiss_Map, NO_ROELOWDISS); @@ -6465,23 +6459,23 @@ void CConfig::SetOutput(SU2_COMPONENT val_software, unsigned short val_izone) { case SA_ZDES: cout << "Delayed Detached Eddy Simulation (DDES) with Vorticity-based SGS" << endl; break; case SA_EDDES: cout << "Delayed Detached Eddy Simulation (DDES) with Shear-layer Adapted SGS" << endl; break; } - cout << "Stochastic Backscatter: "; - if (StochasticBackscatter) { - cout << "ON" << endl; - cout << "Backscatter timescale coefficient: " << SBS_Ctau << endl; - } else { - cout << "OFF" << endl; + if (Kind_HybridRANSLES != NO_HYBRIDRANSLES) { + if (LES_FilterWidth > 0.0) cout << "User-specified LES filter width: " << LES_FilterWidth << endl; + cout << "Stochastic Backscatter: "; + if (StochasticBackscatter) { + cout << "ON" << endl; + cout << "Backscatter timescale coefficient: " << SBS_Ctau << endl; + } else { + cout << "OFF" << endl; + } } if (StochasticBackscatter && Kind_HybridRANSLES == NO_HYBRIDRANSLES) SU2_MPI::Error("Stochastic Backscatter can only be activated with Hybrid RANS/LES.", CURRENT_FUNCTION); - if (DIHT) { - cout << "Decaying Isotropic Homogeneous Turbulence (DIHT): spectrum by Comte-Bellot & Corrsin (1971)." << endl; - cout << "WARNING: DIHT algorithm is only compatible with structured grids." << endl; - cout << "Computational domain size: " << DIHT_DomainLength[0] << ", " << DIHT_DomainLength[1] << ", " << DIHT_DomainLength[2] << " (L_REF)" << endl; - cout << "Number of grid points in x, y and z directions: " << static_cast(DIHT_nPoint[0]) << ", " << static_cast(DIHT_nPoint[1]) << ", " << static_cast(DIHT_nPoint[2]) << endl; - cout << "Number of Fourier modes: " << DIHT_nModes << endl; + if (enforceLES) { if (Kind_HybridRANSLES == NO_HYBRIDRANSLES) - SU2_MPI::Error("DIHT mode can only be activated with Hybrid RANS/LES.", CURRENT_FUNCTION); + SU2_MPI::Error("ENFORCE_LES can only be activated with Hybrid RANS/LES.", CURRENT_FUNCTION); + else + cout << "LES enforced in the whole computational domain." << endl; } break; case MAIN_SOLVER::NEMO_EULER: diff --git a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp index cb74c1523ac6..a6e8598c54ef 100644 --- a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp +++ b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp @@ -140,8 +140,6 @@ class CSourceBase_TurbSA : public CNumerics { corrFac = sqrt(0.5*(1.0+tRat)*(4.0+tRat)/(2.0+tRat)); } else if (time_marching == TIME_MARCHING::DT_STEPPING_1ST) { corrFac = sqrt(1.0+0.5*tRat); - } else { - SU2_MPI::Error("Stochastic Backscatter Model only implemented for dual time stepping.", CURRENT_FUNCTION); } su2double scaleFactor = 1.0/tTurb * sqrt(2.0/tRat) * density * corrFac; diff --git a/SU2_CFD/include/variables/CEulerVariable.hpp b/SU2_CFD/include/variables/CEulerVariable.hpp index 82102a7cf447..4d49f3825721 100644 --- a/SU2_CFD/include/variables/CEulerVariable.hpp +++ b/SU2_CFD/include/variables/CEulerVariable.hpp @@ -29,7 +29,6 @@ #include #include "CFlowVariable.hpp" -#include "../../../Common/include/geometry/CGeometry.hpp" /*! * \brief Returns the number of primitive variables for which to compute gradients. @@ -346,15 +345,4 @@ class CEulerVariable : public CFlowVariable { */ inline unsigned long GetNewtonSolverIterations(unsigned long iPoint) const final { return NIterNewtonsolver[iPoint]; } - /*! - * \brief Generate initial random velocity field (Decaying Isotropic Homogeneous Turbulence). - * \param[in] npoint - Number of points/nodes/vertices in the domain. - * \param[in] ndim - Number of dimensions of the problem. - * \param[in] nvar - Number of variables of the problem. - * \param[in] config - Definition of the particular problem. - */ - virtual void SetVelDIHT(unsigned long npoint, unsigned long ndim, unsigned long nvar, const CConfig *config, - const CGeometry *geometry); - - }; diff --git a/SU2_CFD/src/output/CFlowCompOutput.cpp b/SU2_CFD/src/output/CFlowCompOutput.cpp index 2476498e84d7..b96cdc6fbf32 100644 --- a/SU2_CFD/src/output/CFlowCompOutput.cpp +++ b/SU2_CFD/src/output/CFlowCompOutput.cpp @@ -234,6 +234,8 @@ void CFlowCompOutput::SetVolumeOutputFields(CConfig *config){ AddVolumeOutput("GRID_VELOCITY-Z", "Grid_Velocity_z", "GRID_VELOCITY", "z-component of the grid velocity vector"); } + AddVolumeOutput("VELOCITY_DIVERGENCE", "Velocity_Divergence", "DERIVED", "Divergence of the velocity field"); + // Primitive variables AddVolumeOutput("PRESSURE", "Pressure", "PRIMITIVE", "Pressure"); AddVolumeOutput("TEMPERATURE", "Temperature", "PRIMITIVE", "Temperature"); @@ -325,6 +327,13 @@ void CFlowCompOutput::LoadVolumeData(CConfig *config, CGeometry *geometry, CSolv SetVolumeOutputValue("ENERGY", iPoint, Node_Flow->GetSolution(iPoint, 3)); } + const auto VelocityGradient = Node_Flow->GetVelocityGradient(iPoint); + su2double divVel = 0.0; + for (unsigned short iDim = 0; iDim < nDim; iDim++) { + divVel += VelocityGradient[iDim][iDim]; + } + SetVolumeOutputValue("VELOCITY_DIVERGENCE", iPoint, divVel); + if (gridMovement){ SetVolumeOutputValue("GRID_VELOCITY-X", iPoint, Node_Geo->GetGridVel(iPoint)[0]); SetVolumeOutputValue("GRID_VELOCITY-Y", iPoint, Node_Geo->GetGridVel(iPoint)[1]); diff --git a/SU2_CFD/src/output/CFlowIncOutput.cpp b/SU2_CFD/src/output/CFlowIncOutput.cpp index 761f4ab9a13d..6c4c3ce2965a 100644 --- a/SU2_CFD/src/output/CFlowIncOutput.cpp +++ b/SU2_CFD/src/output/CFlowIncOutput.cpp @@ -312,6 +312,8 @@ void CFlowIncOutput::SetVolumeOutputFields(CConfig *config){ AddVolumeOutput("GRID_VELOCITY-Z", "Grid_Velocity_z", "GRID_VELOCITY", "z-component of the grid velocity vector"); } + AddVolumeOutput("VELOCITY_DIVERGENCE", "Velocity_Divergence", "DERIVED", "Divergence of the velocity field"); + // Primitive variables AddVolumeOutput("PRESSURE_COEFF", "Pressure_Coefficient", "PRIMITIVE", "Pressure coefficient"); AddVolumeOutput("DENSITY", "Density", "PRIMITIVE", "Density"); @@ -415,6 +417,13 @@ void CFlowIncOutput::LoadVolumeData(CConfig *config, CGeometry *geometry, CSolve SetVolumeOutputValue("GRID_VELOCITY-Z", iPoint, Node_Geo->GetGridVel(iPoint)[2]); } + const auto VelocityGradient = Node_Flow->GetVelocityGradient(iPoint); + su2double divVel = 0.0; + for (unsigned short iDim = 0; iDim < nDim; iDim++) { + divVel += VelocityGradient[iDim][iDim]; + } + SetVolumeOutputValue("VELOCITY_DIVERGENCE", iPoint, divVel); + const su2double factor = solver[FLOW_SOL]->GetReferenceDynamicPressure(); SetVolumeOutputValue("PRESSURE_COEFF", iPoint, (Node_Flow->GetPressure(iPoint) - solver[FLOW_SOL]->GetPressure_Inf())/factor); SetVolumeOutputValue("DENSITY", iPoint, Node_Flow->GetDensity(iPoint)); diff --git a/SU2_CFD/src/solvers/CEulerSolver.cpp b/SU2_CFD/src/solvers/CEulerSolver.cpp index ecb0e8ce0cc4..5b85687071ff 100644 --- a/SU2_CFD/src/solvers/CEulerSolver.cpp +++ b/SU2_CFD/src/solvers/CEulerSolver.cpp @@ -298,10 +298,6 @@ CEulerSolver::CEulerSolver(CGeometry *geometry, CConfig *config, } SetBaseClassPointerToNodes(); - /*--- Generate initial random velocity field (Decaying Isotropic Homogeneous Turbulence). ---*/ - - if (config->GetDIHT()) nodes->SetVelDIHT(nPoint, nDim, nVar, config, geometry); - if (iMesh == MESH_0) { nodes->NonPhysicalEdgeCounter.resize(geometry->GetnEdge()) = 0; } diff --git a/SU2_CFD/src/solvers/CTurbSASolver.cpp b/SU2_CFD/src/solvers/CTurbSASolver.cpp index 7a47b77fa91f..72c1096e67f3 100644 --- a/SU2_CFD/src/solvers/CTurbSASolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp @@ -1453,6 +1453,8 @@ void CTurbSASolver::SetDES_LengthScale(CSolver **solver, CGeometry *geometry, CC su2double lengthScale = 0.0, lesSensor = 0.0; + const su2double LES_FilterWidth = config->GetLES_FilterWidth(); + switch(kindHybridRANSLES){ case SA_DES: { /*--- Original Detached Eddy Simulation (DES97) @@ -1460,12 +1462,15 @@ void CTurbSASolver::SetDES_LengthScale(CSolver **solver, CGeometry *geometry, CC 1997 ---*/ - const su2double maxDelta = geometry->nodes->GetMaxLength(iPoint); + su2double maxDelta = geometry->nodes->GetMaxLength(iPoint); + if (LES_FilterWidth > 0.0){ + maxDelta = LES_FilterWidth; + } const su2double distDES = constDES * maxDelta; lengthScale = min(distDES,wallDistance); lesSensor = (wallDistance<=distDES) ? 0.0 : 1.0; - if (config->GetDIHT()) { + if (config->GetEnforceLES()) { lengthScale = distDES; lesSensor = 1.0; } @@ -1478,7 +1483,10 @@ void CTurbSASolver::SetDES_LengthScale(CSolver **solver, CGeometry *geometry, CC Theoretical and Computational Fluid Dynamics - 2006 ---*/ - const su2double maxDelta = geometry->nodes->GetMaxLength(iPoint); + su2double maxDelta = geometry->nodes->GetMaxLength(iPoint); + if (LES_FilterWidth > 0.0){ + maxDelta = LES_FilterWidth; + } const su2double r_d = (kinematicViscosityTurb+kinematicViscosity)/(uijuij*k2*pow(wallDistance, 2.0)); const su2double f_d = 1.0-tanh(pow(8.0*r_d,3.0)); @@ -1487,7 +1495,7 @@ void CTurbSASolver::SetDES_LengthScale(CSolver **solver, CGeometry *geometry, CC lengthScale = wallDistance-f_d*max(0.0,(wallDistance-distDES)); lesSensor = (wallDistance<=distDES) ? 0.0 : f_d; - if (config->GetDIHT()) { + if (config->GetEnforceLES()) { lengthScale = distDES; lesSensor = 1.0; } @@ -1529,11 +1537,14 @@ void CTurbSASolver::SetDES_LengthScale(CSolver **solver, CGeometry *geometry, CC maxDelta = deltaDDES; } + if (LES_FilterWidth > 0.0){ + maxDelta = LES_FilterWidth; + } const su2double distDES = constDES * maxDelta; lengthScale = wallDistance-f_d*max(0.0,(wallDistance-distDES)); lesSensor = (wallDistance<=distDES) ? 0.0 : f_d; - if (config->GetDIHT()) { + if (config->GetEnforceLES()) { lengthScale = distDES; lesSensor = 1.0; } @@ -1587,11 +1598,14 @@ void CTurbSASolver::SetDES_LengthScale(CSolver **solver, CGeometry *geometry, CC maxDelta = deltaDDES; } + if (LES_FilterWidth > 0.0){ + maxDelta = LES_FilterWidth; + } const su2double distDES = constDES * maxDelta; lengthScale = wallDistance-f_d*max(0.0,(wallDistance-distDES)); lesSensor = (wallDistance<=distDES) ? 0.0 : f_d; - if (config->GetDIHT()) { + if (config->GetEnforceLES()) { lengthScale = distDES; lesSensor = 1.0; } diff --git a/SU2_CFD/src/variables/CEulerVariable.cpp b/SU2_CFD/src/variables/CEulerVariable.cpp index cabdfcfeff68..263530705c87 100644 --- a/SU2_CFD/src/variables/CEulerVariable.cpp +++ b/SU2_CFD/src/variables/CEulerVariable.cpp @@ -27,7 +27,6 @@ #include "../../include/variables/CEulerVariable.hpp" #include "../../include/fluid/CFluidModel.hpp" -#include "../../../Common/include/toolboxes/random_toolbox.hpp" unsigned long EulerNPrimVarGrad(const CConfig *config, unsigned long ndim) { if (config->GetContinuous_Adjoint()) return ndim + 4; @@ -162,144 +161,3 @@ void CEulerVariable::SetSecondaryVar(unsigned long iPoint, CFluidModel *FluidMod SetdPde_rho(iPoint, FluidModel->GetdPde_rho()); } - -void CEulerVariable::SetVelDIHT(unsigned long npoint, unsigned long ndim, unsigned long nvar, const CConfig *config, - const CGeometry *geometry) { - - const unsigned short MAXnModes = 5000; - const unsigned short nModes = config->GetDIHT_nModes(); - if (nModes > MAXnModes) SU2_MPI::Error("The maximum number of Fourier modes for DIHT is 5000.", CURRENT_FUNCTION); - if (nModes <= 0) SU2_MPI::Error("Assign a valid value for the number of Fourier modes. (DIHT)", CURRENT_FUNCTION); - - const su2double Lx = config->GetDIHT_DomainLength(0); - const su2double Ly = config->GetDIHT_DomainLength(1); - const su2double Lz = config->GetDIHT_DomainLength(2); - if (Lx <= 0.0 || Ly <= 0.0 || Lz <= 0.0) SU2_MPI::Error("Assign a valid value for the computational domain size. (DIHT)", CURRENT_FUNCTION); - - const unsigned long nx = static_cast(config->GetDIHT_nPoint(0)); - const unsigned long ny = static_cast(config->GetDIHT_nPoint(1)); - const unsigned long nz = static_cast(config->GetDIHT_nPoint(2)); - if (nx <= 0 || ny <= 0 || nz <= 0) SU2_MPI::Error("Assign a valid value for the number of nodes. (DIHT)", CURRENT_FUNCTION); - - const su2double pi = 4.0 * atan(1.0); - const su2double vref = config->GetVelocity_Ref(); - su2double Density_Inf = config->GetDensity_FreeStreamND(); - su2double ModVel_Freestream = config->GetModVel_FreeStream(); - - su2double k_cbc[39] = {0.11, 0.15, 0.20, 0.25, 0.30, 0.40, 0.50, 0.70, 1.00, - 1.50, 2.00, 2.50, 3.00, 4.00, 6.00, 8.00, 10.0, 12.5, - 15.0, 17.5, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, - 90.0, 100., 110., 120., 130., 140., 150., 160., 170., - 180., 190., 200.}; - - su2double espec_cbc[39] = {30.0, 60.0, 129., 230., 322., 435., 457., 380., - 270., 168., 120., 89.0, 70.3, 47.0, 24.7, 12.6, - 7.42, 3.96, 2.33, 1.34, 0.80, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; - - for (unsigned short ind = 0; ind < 39; ind++) { - k_cbc[ind] *= 100.0; - espec_cbc[ind] *= 1.0e-6; - } - - su2double dx = Lx/nx; - su2double dy = Ly/ny; - su2double dz = Lz/nz; - - if (ndim < 3) { - SU2_MPI::Error("DIHT: Decay of turbulence must be three-dimensional.", CURRENT_FUNCTION); - } - - su2double wmin = min(2.0*pi/Lx, min(2.0*pi/Ly, 2.0*pi/Lz)); - su2double wmax = max(pi/dx, max(pi/dy, pi/dz)); - su2double dk = (wmax-wmin)/nModes; - - auto espec_interpol = [&] (su2double k_) { - - if (k_k_cbc[38]) return espec_cbc[38]; - - unsigned short ind = 1; - while (ind < 39 && k_cbc[ind] < k_) ind++; - - su2double km = k_cbc[ind-1]; - su2double kp = k_cbc[ind]; - su2double em = espec_cbc[ind-1]; - su2double ep = espec_cbc[ind]; - - su2double de_dk = (ep-em)/(kp-km); - su2double e_interp = em + de_dk * (k_-km); - - return e_interp; - - }; - - unsigned long seed = RandomToolbox::GetSeed(npoint, ndim + nvar); - - su2double phi[MAXnModes], theta[MAXnModes], psi[MAXnModes], phi1[MAXnModes], theta1[MAXnModes]; - - for (unsigned long iMode = 0; iMode < nModes; iMode++) { - std::mt19937 gen(seed + iMode*nModes); - phi[iMode] = RandomToolbox::GetRandomUniform(gen, 0.0, 2.0*pi); - theta[iMode] = acos(RandomToolbox::GetRandomUniform(gen, -1.0, 1.0)); - psi[iMode] = RandomToolbox::GetRandomUniform(gen, -0.5*pi, 0.5*pi); - phi1[iMode] = RandomToolbox::GetRandomUniform(gen, 0.0, 2.0*pi); - theta1[iMode] = acos(RandomToolbox::GetRandomUniform(gen, -1.0, 1.0)); - } - - for (unsigned long iPoint = 0; iPoint < npoint; iPoint++) { - su2double u = 0.0, v = 0.0, w = 0.0; - su2double xp = geometry->nodes->GetCoord(iPoint,0); - su2double yp = geometry->nodes->GetCoord(iPoint,1); - su2double zp = geometry->nodes->GetCoord(iPoint,2); - for (unsigned long iMode = 0; iMode < nModes; iMode++) { - su2double wn = wmin + 0.5*dk + iMode*dk; - su2double kx = sin(theta[iMode]) * cos(phi[iMode]) * wn; - su2double ky = sin(theta[iMode]) * sin(phi[iMode]) * wn; - su2double kz = cos(theta[iMode]) * wn; - su2double ktx = sin(kx * dx * 0.5) / dx; - su2double kty = sin(ky * dy * 0.5) / dy; - su2double ktz = sin(kz * dz * 0.5) / dz; - su2double zetax = sin(theta1[iMode]) * cos(phi1[iMode]); - su2double zetay = sin(theta1[iMode]) * sin(phi1[iMode]); - su2double zetaz = cos(theta1[iMode]); - su2double sxm = zetay*ktz - zetaz*kty; - su2double sym = zetaz*ktx - zetax*ktz; - su2double szm = zetax*kty - zetay*ktx; - su2double smag = sqrt(sxm*sxm + sym*sym + szm*szm + 1.0e-16); - sxm /= smag; sym /= smag; szm /= smag; - su2double espec = espec_interpol(wn); - su2double qm = sqrt(espec*dk); - su2double arg = kx * xp + ky * yp + kz * zp - psi[iMode]; - su2double facx = 2.0 * qm * cos(arg - kx*dx*0.5); - su2double facy = 2.0 * qm * cos(arg - ky*dy*0.5); - su2double facz = 2.0 * qm * cos(arg - kz*dz*0.5); - u += facx * sxm; - v += facy * sym; - w += facz * szm; - } - Solution(iPoint, 1) = Density_Inf * u/vref; - Solution(iPoint, 2) = Density_Inf * v/vref; - Solution(iPoint, 3) = Density_Inf * w/vref; - su2double q2 = Solution(iPoint, 1)*Solution(iPoint, 1) + - Solution(iPoint, 2)*Solution(iPoint, 2) + - Solution(iPoint, 3)*Solution(iPoint, 3); - Solution(iPoint, 4) += 0.5 * (q2/Density_Inf - Density_Inf*ModVel_Freestream*ModVel_Freestream); - } - - const bool dual_time = (config->GetTime_Marching() == TIME_MARCHING::DT_STEPPING_1ST) || - (config->GetTime_Marching() == TIME_MARCHING::DT_STEPPING_2ND); - - const bool classical_rk4 = (config->GetKind_TimeIntScheme_Flow() == CLASSICAL_RK4_EXPLICIT); - - Solution_Old = Solution; - - if (classical_rk4) Solution_New = Solution; - - if (dual_time) { - Solution_time_n = Solution; - Solution_time_n1 = Solution; - } - -} \ No newline at end of file diff --git a/config_template.cfg b/config_template.cfg index 5b9edee47f38..4ecf85b5a2b3 100644 --- a/config_template.cfg +++ b/config_template.cfg @@ -181,23 +181,17 @@ HYBRID_RANSLES= SA_DDES % DES Constant (0.65) DES_CONST= 0.65 % +% User-specified LES filter width (if negative, it is computed based on the local cell size, default -1.0) +LES_FILTER_WIDTH= -1.0 +% % Stochastic Backscatter Model (NO, YES) STOCHASTIC_BACKSCATTER= NO % % Backscatter timescale coefficient (0.001) SBS_CTAU= 0.001 % -% Decaying Isotropic Homogeneous Turbulence (DIHT) simulation (NO, YES) -DIHT= NO -% -% Domain length in x, y and z directions for DIHT (non-dimensional, based on the reference length) -DIHT_DOMAIN_LENGTH= (1.0, 1.0, 1.0) -% -% Number of points in x, y and z directions for DIHT (non-dimensional) -DIHT_NPOINT= (1, 1, 1) -% -% Number of Fourier modes for DIHT -DIHT_NUM_MODES= 1000 +% Enforce LES mode in Hybrid RANS-LES Simulations (NO, YES) +ENFORCE_LES= NO % -------------------- COMPRESSIBLE FREE-STREAM DEFINITION --------------------% % From 9657bfb7e31c7f027bff33f6fb73e11204335d19 Mon Sep 17 00:00:00 2001 From: Angelo Passariello Date: Fri, 24 Oct 2025 12:25:10 +0200 Subject: [PATCH 10/25] Consistent estimation of turb. kinetic energy (for SA) - Add consistent evaluation of the turbulent kinetic energy in the random source term appearing in the momentum equations. - Add backscatter intensity coefficient in the configuration file. - Add random initialization of the Langevin variables. --- Common/include/CConfig.hpp | 11 +++++++-- Common/src/CConfig.cpp | 12 ++++++++-- SU2_CFD/include/numerics/CNumerics.hpp | 24 +++++-------------- .../include/solvers/CFVMFlowSolverBase.inl | 13 ++++++++++ SU2_CFD/src/numerics/flow/flow_diffusion.cpp | 16 ++++++++----- SU2_CFD/src/variables/CTurbSAVariable.cpp | 7 ++++-- config_template.cfg | 7 ++++-- 7 files changed, 58 insertions(+), 32 deletions(-) diff --git a/Common/include/CConfig.hpp b/Common/include/CConfig.hpp index 580f107c3836..3a1448137cd2 100644 --- a/Common/include/CConfig.hpp +++ b/Common/include/CConfig.hpp @@ -1090,6 +1090,7 @@ class CConfig { unsigned short Kind_HybridRANSLES; /*!< \brief Kind of Hybrid RANS/LES. */ bool StochasticBackscatter; /*!< \brief Option to include Stochastic Backscatter Model. */ su2double SBS_Ctau; /*!< \brief Stochastic Backscatter Model timescale coefficient. */ + su2double SBS_Cmag; /*!< \brief Stochastic Backscatter Model intensity coefficient. */ bool enforceLES; /*!< \brief Option to enforce LES mode in hybrid RANS-LES simulations. */ su2double LES_FilterWidth; /*!< \brief LES filter width for hybrid RANS-LES simulations. */ unsigned short Kind_RoeLowDiss; /*!< \brief Kind of Roe scheme with low dissipation for unsteady flows. */ @@ -9553,11 +9554,17 @@ class CConfig { su2double GetConst_DES(void) const { return Const_DES; } /*! - * \brief Get the DES Constant. - * \return Value of DES constant. + * \brief Get the SBS timescale coefficient. + * \return Value of SBS timescale coefficient. */ su2double GetSBS_Ctau(void) const { return SBS_Ctau; } + /*! + * \brief Get the SBS intensity coefficient. + * \return Value of SBS intensity coefficient. + */ + su2double GetSBS_Cmag(void) const { return SBS_Cmag; } + /*! * \brief Get the type of tape that will be checked in a tape debug run. */ diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index 73124858c206..6318fecfb8c4 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -2921,8 +2921,11 @@ void CConfig::SetConfig_Options() { /* DESCRIPTION: DES Constant */ addDoubleOption("DES_CONST", Const_DES, 0.65); - /* DESCRIPTION: SBS timescale constant */ - addDoubleOption("SBS_CTAU", SBS_Ctau, 0.05); + /* DESCRIPTION: SBS timescale coefficient */ + addDoubleOption("SBS_TIMESCALE_COEFF", SBS_Ctau, 0.05); + + /* DESCRIPTION: SBS intensity coefficient */ + addDoubleOption("SBS_INTENSITY_COEFF", SBS_Cmag, 1.0); /* DESCRIPTION: Specify Hybrid RANS/LES model */ addEnumOption("HYBRID_RANSLES", Kind_HybridRANSLES, HybridRANSLES_Map, NO_HYBRIDRANSLES); @@ -6491,7 +6494,12 @@ void CConfig::SetOutput(SU2_COMPONENT val_software, unsigned short val_izone) { cout << "Stochastic Backscatter: "; if (StochasticBackscatter) { cout << "ON" << endl; + cout << "Backscatter intensity coefficient: " << SBS_Cmag << endl; + if (SBS_Cmag < 0.0) + SU2_MPI::Error("Backscatter intensity coefficient must be non-negative.", CURRENT_FUNCTION); cout << "Backscatter timescale coefficient: " << SBS_Ctau << endl; + if (SBS_Ctau < 0.0) + SU2_MPI::Error("Backscatter timescale coefficient must be non-negative.", CURRENT_FUNCTION); } else { cout << "OFF" << endl; } diff --git a/SU2_CFD/include/numerics/CNumerics.hpp b/SU2_CFD/include/numerics/CNumerics.hpp index 8899491e17e5..db326a23cc50 100644 --- a/SU2_CFD/include/numerics/CNumerics.hpp +++ b/SU2_CFD/include/numerics/CNumerics.hpp @@ -650,28 +650,16 @@ class CNumerics { * \param[in] velGrad - Velocity gradient matrix. * \param[out] stochReynStress - Stochastic tensor (to be added to the Reynolds stress tensor). */ - template + template NEVERINLINE static void ComputeStochReynStress(size_t nDim, Scalar density, Scalar eddyVis, - const Mat1& velGrad, const su2double *rndVec, - Mat2& stochReynStress) { - - /* --- Estimate turbulent kinetic energy --- */ - - Scalar turbKE = 0.0, strainMag = 0.0; - for (size_t iDim = 0; iDim < nDim; iDim++) { - for (size_t jDim = 0; jDim < nDim; jDim++) { - strainMag += pow(0.5 * (velGrad[iDim][jDim] + velGrad[jDim][iDim]), 2); - } - } - strainMag = sqrt(2.0 * strainMag); - turbKE = eddyVis * strainMag; - turbKE = max(turbKE, 1E-10); + Scalar turbKE, Vector rndVec, + Mat& stochReynStress, Scalar Cmag) { /* --- Calculate stochastic tensor --- */ - stochReynStress[1][0] = - density * turbKE * rndVec[2]; - stochReynStress[2][0] = density * turbKE * rndVec[1]; - stochReynStress[2][1] = - density * turbKE * rndVec[0]; + stochReynStress[1][0] = - Cmag * density * turbKE * rndVec[2]; + stochReynStress[2][0] = Cmag * density * turbKE * rndVec[1]; + stochReynStress[2][1] = - Cmag * density * turbKE * rndVec[0]; for (size_t iDim = 0; iDim < nDim; iDim++) { for (size_t jDim = 0; jDim <= iDim; jDim++) { if (iDim==jDim) { diff --git a/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl b/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl index 13ee5a116a04..747e006a0990 100644 --- a/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl +++ b/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl @@ -475,6 +475,19 @@ void CFVMFlowSolverBase::Viscous_Residual_impl(unsigned long iEdge, CGeome numerics->SetStochVar(turbNodes->GetSolution(iPoint, 1 + iDim), turbNodes->GetSolution(jPoint, 1 + iDim), iDim); } + su2double eddy_visc_i, eddy_visc_j, DES_length_i, DES_length_j, tke_i, tke_j; + eddy_visc_i = turbNodes->GetmuT(iPoint); + eddy_visc_j = turbNodes->GetmuT(jPoint); + DES_length_i = turbNodes->GetDES_LengthScale(iPoint); + DES_length_j = turbNodes->GetDES_LengthScale(jPoint); + const su2double tol = 1e-12; + if (DES_length_i < tol || DES_length_j < tol) { + tke_i = tke_j = 0.0; + } else { + tke_i = pow(eddy_visc_i/DES_length_i, 2); + tke_j = pow(eddy_visc_j/DES_length_j, 2); + } + numerics->SetTurbKineticEnergy(tke_i, tke_j); } /*--- Wall shear stress values (wall functions) ---*/ diff --git a/SU2_CFD/src/numerics/flow/flow_diffusion.cpp b/SU2_CFD/src/numerics/flow/flow_diffusion.cpp index 50a8fe915f4f..7f19ec3db64a 100644 --- a/SU2_CFD/src/numerics/flow/flow_diffusion.cpp +++ b/SU2_CFD/src/numerics/flow/flow_diffusion.cpp @@ -460,8 +460,9 @@ CNumerics::ResidualType<> CAvgGrad_Flow::ComputeResidual(const CConfig* config) if (config->GetStochastic_Backscatter()) { for (iVar = 0; iVar < nDim; iVar++) Mean_StochVar[iVar] = 0.5*(stochVar_i[iVar] + stochVar_j[iVar]); - ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_Eddy_Viscosity, Mean_GradPrimVar+1, - Mean_StochVar, stochReynStress); + su2double SBS_Cmag = config->GetSBS_Cmag(); + ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_Eddy_Viscosity, Mean_turb_ke, + Mean_StochVar, stochReynStress, SBS_Cmag); } /*--- Get projected flux tensor (viscous residual) ---*/ @@ -640,11 +641,13 @@ CNumerics::ResidualType<> CAvgGradInc_Flow::ComputeResidual(const CConfig* confi if (config->GetStochastic_Backscatter()) { for (iVar = 0; iVar < nDim; iVar++) Mean_StochVar[iVar] = 0.5*(stochVar_i[iVar] + stochVar_j[iVar]); - ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_Eddy_Viscosity, Mean_GradPrimVar+1, - Mean_StochVar, stochReynStress); + su2double SBS_Cmag = config->GetSBS_Cmag(); + ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_Eddy_Viscosity, Mean_turb_ke, + Mean_StochVar, stochReynStress, SBS_Cmag); } /*--- Get projected flux tensor (viscous residual) ---*/ + SetStressTensor(Mean_PrimVar, Mean_GradPrimVar, Mean_turb_ke, Mean_Laminar_Viscosity, Mean_Eddy_Viscosity,config); if (config->GetSAParsedOptions().qcr2000) AddQCR(nDim, &Mean_GradPrimVar[1], tau); @@ -965,8 +968,9 @@ CNumerics::ResidualType<> CGeneralAvgGrad_Flow::ComputeResidual(const CConfig* c if (config->GetStochastic_Backscatter()) { for (iVar = 0; iVar < nDim; iVar++) Mean_StochVar[iVar] = 0.5*(stochVar_i[iVar] + stochVar_j[iVar]); - ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_Eddy_Viscosity, Mean_GradPrimVar+1, - Mean_StochVar, stochReynStress); + su2double SBS_Cmag = config->GetSBS_Cmag(); + ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_Eddy_Viscosity, Mean_turb_ke, + Mean_StochVar, stochReynStress, SBS_Cmag); } /*--- Get projected flux tensor (viscous residual) ---*/ diff --git a/SU2_CFD/src/variables/CTurbSAVariable.cpp b/SU2_CFD/src/variables/CTurbSAVariable.cpp index e463909ecf08..82a636b06e67 100644 --- a/SU2_CFD/src/variables/CTurbSAVariable.cpp +++ b/SU2_CFD/src/variables/CTurbSAVariable.cpp @@ -27,7 +27,7 @@ #include "../../include/variables/CTurbSAVariable.hpp" - +#include "../../../Common/include/toolboxes/random_toolbox.hpp" CTurbSAVariable::CTurbSAVariable(su2double val_nu_tilde, su2double val_muT, unsigned long npoint, unsigned long ndim, unsigned long nvar, CConfig *config) : @@ -41,7 +41,10 @@ CTurbSAVariable::CTurbSAVariable(su2double val_nu_tilde, su2double val_muT, unsi for (unsigned long iPoint = 0; iPoint < nPoint; iPoint++) { Solution_Old(iPoint, 0) = Solution(iPoint, 0) = val_nu_tilde; for (unsigned long iVar = 1; iVar < nVar; iVar++) { - Solution_Old(iPoint, iVar) = Solution(iPoint, iVar) = 0.0; + unsigned long seed = RandomToolbox::GetSeed(val_nu_tilde, iVar); + std::mt19937 gen(seed); + su2double val_stoch_var = RandomToolbox::GetRandomNormal(gen); + Solution_Old(iPoint, iVar) = Solution(iPoint, iVar) = val_stoch_var; } } } diff --git a/config_template.cfg b/config_template.cfg index 4ed29b3b1f1d..fb93c0bfc8f2 100644 --- a/config_template.cfg +++ b/config_template.cfg @@ -187,8 +187,11 @@ LES_FILTER_WIDTH= -1.0 % Stochastic Backscatter Model (NO, YES) STOCHASTIC_BACKSCATTER= NO % -% Backscatter timescale coefficient (0.001) -SBS_CTAU= 0.001 +% Backscatter timescale coefficient (0.05) +SBS_TIMESCALE_COEFF= 0.05 +% +% Backscatter intensity coefficient (1.0) +SBS_INTENSITY_COEFF= 1.0 % % Enforce LES mode in Hybrid RANS-LES Simulations (NO, YES) ENFORCE_LES= NO From 968d8f96fa9bad1e0a66cf2138112cb04450ac04 Mon Sep 17 00:00:00 2001 From: Angelo Passariello Date: Thu, 30 Oct 2025 08:38:46 +0100 Subject: [PATCH 11/25] Add Laplacian smoothing (Langevin equations) - Add Laplacian smoothing of source terms in Langevin equations. Remark: pseudo-time integration is employed (residuals are printed on screen with fixed frequency, the maximum number of time iterations can be defined by the user). --- Common/include/CConfig.hpp | 14 ++ Common/include/option_structure.hpp | 1 + Common/src/CConfig.cpp | 9 + SU2_CFD/include/solvers/CTurbSASolver.hpp | 7 + SU2_CFD/include/variables/CTurbSAVariable.hpp | 17 ++ SU2_CFD/include/variables/CVariable.hpp | 20 ++- SU2_CFD/src/solvers/CSolver.cpp | 12 ++ SU2_CFD/src/solvers/CTurbSASolver.cpp | 154 ++++++++++++++++++ SU2_CFD/src/variables/CTurbSAVariable.cpp | 1 + config_template.cfg | 6 + 10 files changed, 239 insertions(+), 2 deletions(-) diff --git a/Common/include/CConfig.hpp b/Common/include/CConfig.hpp index 3a1448137cd2..c74f56998187 100644 --- a/Common/include/CConfig.hpp +++ b/Common/include/CConfig.hpp @@ -1089,6 +1089,8 @@ class CConfig { WINDOW_FUNCTION Kind_WindowFct; /*!< \brief Type of window (weight) function for objective functional. */ unsigned short Kind_HybridRANSLES; /*!< \brief Kind of Hybrid RANS/LES. */ bool StochasticBackscatter; /*!< \brief Option to include Stochastic Backscatter Model. */ + su2double SBS_Cdelta; /*!< \brief Stochastic Backscatter Model lengthscale coefficient. */ + unsigned short SBS_maxIterSmooth; /*!< \brief Maximum number of smoothing iterations for the SBS model. */ su2double SBS_Ctau; /*!< \brief Stochastic Backscatter Model timescale coefficient. */ su2double SBS_Cmag; /*!< \brief Stochastic Backscatter Model intensity coefficient. */ bool enforceLES; /*!< \brief Option to enforce LES mode in hybrid RANS-LES simulations. */ @@ -9553,6 +9555,18 @@ class CConfig { */ su2double GetConst_DES(void) const { return Const_DES; } + /*! + * \brief Get the SBS lengthscale coefficient. + * \return Value of SBS lengthscale coefficient. + */ + su2double GetSBS_Cdelta(void) const { return SBS_Cdelta; } + + /*! + * \brief Get the SBS timescale coefficient. + * \return Value of SBS timescale coefficient. + */ + su2double GetSBS_maxIterSmooth(void) const { return SBS_maxIterSmooth; } + /*! * \brief Get the SBS timescale coefficient. * \return Value of SBS timescale coefficient. diff --git a/Common/include/option_structure.hpp b/Common/include/option_structure.hpp index bf99257b1387..3a72f5e6ba5a 100644 --- a/Common/include/option_structure.hpp +++ b/Common/include/option_structure.hpp @@ -2645,6 +2645,7 @@ enum class MPI_QUANTITIES { MAX_LENGTH , /*!< \brief Maximum length communication. */ GRID_VELOCITY , /*!< \brief Grid velocity communication. */ SOLUTION_EDDY , /*!< \brief Turbulent solution plus eddy viscosity communication. */ + STOCH_SOURCE_LANG , /*!< \brief Stochastic source term for Langevin equations communication. */ SOLUTION_MATRIX , /*!< \brief Matrix solution communication. */ SOLUTION_MATRIXTRANS , /*!< \brief Matrix transposed solution communication. */ NEIGHBORS , /*!< \brief Neighbor point count communication (for JST). */ diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index 6318fecfb8c4..7fc6c833770b 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -2921,6 +2921,12 @@ void CConfig::SetConfig_Options() { /* DESCRIPTION: DES Constant */ addDoubleOption("DES_CONST", Const_DES, 0.65); + /* DESCRIPTION: SBS lengthscale coefficient */ + addDoubleOption("SBS_LENGTHSCALE_COEFF", SBS_Cdelta, 0.1); + + /* DESCRIPTION: Maximum number of smoothing iterations for SBS model. */ + addUnsignedShortOption("SBS_MAX_ITER_SMOOTH", SBS_maxIterSmooth, 100); + /* DESCRIPTION: SBS timescale coefficient */ addDoubleOption("SBS_TIMESCALE_COEFF", SBS_Ctau, 0.05); @@ -6497,6 +6503,9 @@ void CConfig::SetOutput(SU2_COMPONENT val_software, unsigned short val_izone) { cout << "Backscatter intensity coefficient: " << SBS_Cmag << endl; if (SBS_Cmag < 0.0) SU2_MPI::Error("Backscatter intensity coefficient must be non-negative.", CURRENT_FUNCTION); + cout << "Backscatter lengthscale coefficient: " << SBS_Cdelta << endl; + if (SBS_Cdelta < 0.0) + SU2_MPI::Error("Backscatter lengthscale coefficient must be non-negative.", CURRENT_FUNCTION); cout << "Backscatter timescale coefficient: " << SBS_Ctau << endl; if (SBS_Ctau < 0.0) SU2_MPI::Error("Backscatter timescale coefficient must be non-negative.", CURRENT_FUNCTION); diff --git a/SU2_CFD/include/solvers/CTurbSASolver.hpp b/SU2_CFD/include/solvers/CTurbSASolver.hpp index 1f08fe9566b4..0aaf50225462 100644 --- a/SU2_CFD/include/solvers/CTurbSASolver.hpp +++ b/SU2_CFD/include/solvers/CTurbSASolver.hpp @@ -71,6 +71,13 @@ class CTurbSASolver final : public CTurbSolver { */ void SetLangevinGen(CConfig* config, CGeometry* geometry); + /*! + * \brief Apply Laplacian smoothing to the source terms in Langevin equations (Stochastic Backscatter Model). + * \param[in] config - Definition of the particular problem. + * \param[in] geometry - Geometrical definition. + */ + void SmoothLangevinSourceTerms(CConfig* config, CGeometry* geometry); + /*! * \brief Compute nu tilde from the wall functions. * \param[in] geometry - Geometrical definition of the problem. diff --git a/SU2_CFD/include/variables/CTurbSAVariable.hpp b/SU2_CFD/include/variables/CTurbSAVariable.hpp index 000f05d13d9d..f07c2b8b8b67 100644 --- a/SU2_CFD/include/variables/CTurbSAVariable.hpp +++ b/SU2_CFD/include/variables/CTurbSAVariable.hpp @@ -43,6 +43,7 @@ class CTurbSAVariable final : public CTurbVariable { VectorType DES_LengthScale; VectorType LES_Mode; MatrixType stochSource; + MatrixType stochSource_old; VectorType Vortex_Tilting; MatrixTypeGen stochGen; @@ -93,6 +94,22 @@ class CTurbSAVariable final : public CTurbVariable { */ inline void SetLangevinSourceTerms(unsigned long iPoint, unsigned short iDim, su2double val_stochSource) override { stochSource(iPoint, iDim) = val_stochSource; } + /*! + * \brief Get the old value of the source terms for the stochastic equations. + * \param[in] iPoint - Point index. + * \param[in] iDim - Dimension index. + * \return Old value of the source terms for the stochastic equations. + */ + inline virtual su2double GetLangevinSourceTermsOld(unsigned long iPoint, unsigned short iDim) const override { return stochSource_old(iPoint, iDim); } + + /*! + * \brief Set the old value of source terms for the stochastic equations. + * \param[in] iPoint - Point index. + * \param[in] iDim - Dimension index. + * \param[in] val_stochSource_old - Old value of the source term for the stochastic equations. + */ + inline void SetLangevinSourceTermsOld(unsigned long iPoint, unsigned short iDim, su2double val_stochSource_old) override { stochSource_old(iPoint, iDim) = val_stochSource_old; } + /*! * \brief Set the LES sensor. */ diff --git a/SU2_CFD/include/variables/CVariable.hpp b/SU2_CFD/include/variables/CVariable.hpp index b01b01bc9dba..4eb19e9dd0b9 100644 --- a/SU2_CFD/include/variables/CVariable.hpp +++ b/SU2_CFD/include/variables/CVariable.hpp @@ -408,6 +408,7 @@ class CVariable { /*! * \brief A virtual member. * \param[in] iPoint - Point index. + * \param[in] val_les_mode - Value of the LES sensor. */ inline virtual void SetLES_Mode(unsigned long iPoint, su2double val_les_mode) {} @@ -422,10 +423,25 @@ class CVariable { * \brief A virtual member. * \param[in] iPoint - Point index. * \param[in] iDim - Dimension index. - * \param[in] val_stochSource - Seed for Langevin equations. + * \param[in] val_stochSource - Source term in Langevin equations. */ inline virtual void SetLangevinSourceTerms(unsigned long iPoint, unsigned short iDim, su2double val_stochSource) {} + /*! + * \brief A virtual member. + * \param[in] iPoint - Point index. + * \param[in] iDim - Dimension index. + */ + inline virtual su2double GetLangevinSourceTermsOld(unsigned long iPoint, unsigned short iDim) const { return 0.0; } + + /*! + * \brief A virtual member. + * \param[in] iPoint - Point index. + * \param[in] iDim - Dimension index. + * \param[in] val_stochSource_old - Old value of source term in Langevin equations. + */ + inline virtual void SetLangevinSourceTermsOld(unsigned long iPoint, unsigned short iDim, su2double val_stochSource_old) {} + /*! * \brief A virtual member. * \param[in] iPoint - Point index. @@ -437,7 +453,7 @@ class CVariable { * \brief A virtual member. * \param[in] iPoint - Point index. * \param[in] iDim - Dimension index. - * \param[in] val_stochSource - Source term for Langevin equations. + * \param[in] val_stochGen - Pseudo-random number generator for Langevin equations. */ inline virtual void SetLangevinGen(unsigned long iPoint, unsigned short iDim, std::mt19937 val_stochGen) {} diff --git a/SU2_CFD/src/solvers/CSolver.cpp b/SU2_CFD/src/solvers/CSolver.cpp index a9c3875406bf..502a459ec55e 100644 --- a/SU2_CFD/src/solvers/CSolver.cpp +++ b/SU2_CFD/src/solvers/CSolver.cpp @@ -1356,6 +1356,10 @@ void CSolver::GetCommCountAndType(const CConfig* config, COUNT_PER_POINT = nVar+1; MPI_TYPE = COMM_TYPE_DOUBLE; break; + case MPI_QUANTITIES::STOCH_SOURCE_LANG: + COUNT_PER_POINT = nDim; + MPI_TYPE = COMM_TYPE_DOUBLE; + break; case MPI_QUANTITIES::SOLUTION_FEA: if (config->GetTime_Domain()) COUNT_PER_POINT = nVar*3; @@ -1483,6 +1487,10 @@ void CSolver::InitiateComms(CGeometry *geometry, bufDSend[buf_offset+iVar] = base_nodes->GetSolution(iPoint, iVar); bufDSend[buf_offset+nVar] = base_nodes->GetmuT(iPoint); break; + case MPI_QUANTITIES::STOCH_SOURCE_LANG: + for (iDim = 0; iDim < nDim; iDim++) + bufDSend[buf_offset+iDim] = base_nodes->GetLangevinSourceTerms(iPoint, iDim); + break; case MPI_QUANTITIES::UNDIVIDED_LAPLACIAN: for (iVar = 0; iVar < nVar; iVar++) bufDSend[buf_offset+iVar] = base_nodes->GetUndivided_Laplacian(iPoint, iVar); @@ -1631,6 +1639,10 @@ void CSolver::CompleteComms(CGeometry *geometry, base_nodes->SetSolution(iPoint, iVar, bufDRecv[buf_offset+iVar]); base_nodes->SetmuT(iPoint,bufDRecv[buf_offset+nVar]); break; + case MPI_QUANTITIES::STOCH_SOURCE_LANG: + for (iDim = 0; iDim < nDim; iDim++) + base_nodes->SetLangevinSourceTerms(iPoint, iDim, bufDRecv[buf_offset+iDim]); + break; case MPI_QUANTITIES::UNDIVIDED_LAPLACIAN: for (iVar = 0; iVar < nVar; iVar++) base_nodes->SetUnd_Lapl(iPoint, iVar, bufDRecv[buf_offset+iVar]); diff --git a/SU2_CFD/src/solvers/CTurbSASolver.cpp b/SU2_CFD/src/solvers/CTurbSASolver.cpp index 72c1096e67f3..6e99fd4559c0 100644 --- a/SU2_CFD/src/solvers/CTurbSASolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp @@ -242,6 +242,7 @@ void CTurbSASolver::Preprocessing(CGeometry *geometry, CSolver **solver_containe if (backscatter && innerIter==0) { SetLangevinGen(config, geometry); SetLangevinSourceTerms(config, geometry); + SmoothLangevinSourceTerms(config, geometry); } } @@ -1630,6 +1631,7 @@ void CTurbSASolver::SetLangevinSourceTerms(CConfig *config, CGeometry* geometry) su2double lesSensor = nodes->GetLES_Mode(iPoint); su2double rnd = RandomToolbox::GetRandomNormal(gen); rnd *= std::nearbyint(lesSensor); + nodes->SetLangevinSourceTermsOld(iPoint, iDim, rnd); nodes->SetLangevinSourceTerms(iPoint, iDim, rnd); } } @@ -1654,6 +1656,158 @@ void CTurbSASolver::SetLangevinGen(CConfig* config, CGeometry* geometry) { } +void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geometry) { + + const su2double LES_FilterWidth = config->GetLES_FilterWidth(); + const su2double pi = 4.0*atan(1.0); + const su2double cDelta = config->GetSBS_Cdelta(); + const unsigned short maxIter = config->GetSBS_maxIterSmooth(); + const unsigned long global_nPointDomain = geometry->GetGlobal_nPointDomain(); + const su2double tol = 1.0e-6; + + /*--- Compute the time step ensuring stability of the pseudo-time integration. ---*/ + + su2double localMinDx = -1.0; + for (unsigned long iPoint = 0; iPoint < nPointDomain; ++iPoint) { + auto coord_i = geometry->nodes->GetCoord(iPoint); + for (unsigned short iNode = 0; iNode < geometry->nodes->GetnPoint(iPoint); ++iNode) { + unsigned long jPoint = geometry->nodes->GetPoint(iPoint, iNode); + auto coord_j = geometry->nodes->GetCoord(jPoint); + su2double dx_ij = GeometryToolbox::Distance(nDim, coord_i, coord_j); + su2double maxDelta = geometry->nodes->GetMaxLength(iPoint); + if (LES_FilterWidth > 0.0) maxDelta = LES_FilterWidth; + su2double b = sqrt(cDelta) * maxDelta; + dx_ij /= b; + if (dx_ij < localMinDx || localMinDx < 0.0) localMinDx = dx_ij; + } + } + su2double globalMinDx = 0.0; + SU2_MPI::Allreduce(&localMinDx, &globalMinDx, 1, MPI_DOUBLE, MPI_MIN, SU2_MPI::GetComm()); + su2double CFL = 0.8; + su2double dt = CFL * globalMinDx * globalMinDx; + + /*--- Start the pseudo-time integration for the Laplacian smoothing. ---*/ + + for (unsigned short iDim = 0; iDim < nDim; iDim++) { + + for (unsigned short iter = 0; iter < maxIter; iter++) { + + /*--- Set the stochastic source terms to zero at solid walls. ---*/ + + for (unsigned short iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { + if (config->GetSolid_Wall(iMarker)) { + for (unsigned long iVertex = 0; iVertex < geometry->nVertex[iMarker]; iVertex++ ) { + unsigned long iPoint = geometry->vertex[iMarker][iVertex]->GetNode(); + nodes->SetLangevinSourceTerms(iPoint, iDim, 0.0); + nodes->SetLangevinSourceTermsOld(iPoint, iDim, 0.0); + } + } + } + + /*--- MPI communication. ---*/ + + InitiateComms(geometry, config, MPI_QUANTITIES::STOCH_SOURCE_LANG); + CompleteComms(geometry, config, MPI_QUANTITIES::STOCH_SOURCE_LANG); + + /*--- Update the solution in pseudo-time. ---*/ + + su2double localResNorm = 0.0; + + SU2_OMP_FOR_DYN(omp_chunk_size) + for (unsigned long iPoint = 0; iPoint < nPointDomain; iPoint++) { + + unsigned short lesSensor = std::nearbyint(nodes->GetLES_Mode(iPoint)); + if (lesSensor == 0) continue; + + su2double maxDelta = geometry->nodes->GetMaxLength(iPoint); + if (LES_FilterWidth > 0.0) maxDelta = LES_FilterWidth; + su2double b = sqrt(cDelta) * maxDelta; + su2double b2 = b * b; + su2double volume_iPoint = geometry->nodes->GetVolume(iPoint); + su2double source_i = nodes->GetLangevinSourceTerms(iPoint, iDim); + auto coord_i = geometry->nodes->GetCoord(iPoint); + + /*--- Discretize the Laplace operator. ---*/ + + su2double lap = 0.0; + for (unsigned short iNode = 0; iNode < geometry->nodes->GetnPoint(iPoint); iNode++) { + auto jPoint = geometry->nodes->GetPoint(iPoint, iNode); + auto coord_j = geometry->nodes->GetCoord(jPoint); + auto iEdge = geometry->nodes->GetEdge(iPoint, iNode); + auto* normal = geometry->edges->GetNormal(iEdge); + su2double area = GeometryToolbox::Norm(nDim, normal); + su2double dx_ij = GeometryToolbox::Distance(nDim, coord_i, coord_j); + su2double source_j = nodes->GetLangevinSourceTerms(jPoint, iDim); + lap += area/volume_iPoint * (source_j - source_i)/dx_ij; + } + lap *= b2; + + /*--- Update the solution and sum the residual. ---*/ + + su2double source_i_old = nodes->GetLangevinSourceTermsOld(iPoint, iDim); + su2double rhs = (source_i_old - source_i + lap) * dt; + localResNorm += rhs * rhs; + source_i += rhs; + nodes->SetLangevinSourceTerms(iPoint, iDim, source_i); + + } + END_SU2_OMP_FOR + + /*--- Stop integration if residual drops below tolerance. ---*/ + + su2double globalResNorm = 0.0; + SU2_MPI::Allreduce(&localResNorm, &globalResNorm, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); + globalResNorm = sqrt(globalResNorm / global_nPointDomain); + + if (rank == MASTER_NODE) { + if (iter == 0) { + cout << endl + << "Residual of Laplacian smoothing along dimension " << iDim+1 << "." << endl + << "---------------------------------" << endl + << " Iter RMS Residual" << endl + << "---------------------------------" << endl; + } + if (iter%10 == 0) { + cout << " " + << std::setw(5) << iter + << " " + << std::setw(12) << std::fixed << std::scientific << std::setprecision(6) << globalResNorm + << endl; + } + } + + if (globalResNorm < tol || iter == maxIter-1) { + + if (rank == MASTER_NODE) { + cout << " " + << std::setw(5) << iter + << " " + << std::setw(12) << std::fixed << std::scientific << ::setprecision(6) << globalResNorm + << endl; + cout << "---------------------------------" << endl; + } + + /*--- Scale source terms for variance preservation. ---*/ + + for (unsigned long iPoint = 0; iPoint < nPointDomain; iPoint++) { + su2double source = nodes->GetLangevinSourceTerms(iPoint, iDim); + su2double maxDelta = geometry->nodes->GetMaxLength(iPoint); + if (LES_FilterWidth > 0.0) maxDelta = LES_FilterWidth; + su2double b = sqrt(cDelta) * maxDelta; + su2double b3 = b * b * b; + su2double volume = geometry->nodes->GetVolume(iPoint); + source *= sqrt(8.0*pi*b3/volume); + nodes->SetLangevinSourceTerms(iPoint, iDim, source); + } + + break; + + } + } + } + +} + void CTurbSASolver::SetInletAtVertex(const su2double *val_inlet, unsigned short iMarker, unsigned long iVertex) { diff --git a/SU2_CFD/src/variables/CTurbSAVariable.cpp b/SU2_CFD/src/variables/CTurbSAVariable.cpp index 82a636b06e67..c0ad7d6e6ab2 100644 --- a/SU2_CFD/src/variables/CTurbSAVariable.cpp +++ b/SU2_CFD/src/variables/CTurbSAVariable.cpp @@ -64,6 +64,7 @@ CTurbSAVariable::CTurbSAVariable(su2double val_nu_tilde, su2double val_muT, unsi LES_Mode.resize(nPoint) = su2double(0.0); Vortex_Tilting.resize(nPoint); stochSource.resize(nPoint, nDim) = su2double(0.0); + stochSource_old.resize(nPoint, nDim) = su2double(0.0); stochGen.resize(nPoint, nDim); } diff --git a/config_template.cfg b/config_template.cfg index fb93c0bfc8f2..ca8aba44d7a0 100644 --- a/config_template.cfg +++ b/config_template.cfg @@ -187,6 +187,12 @@ LES_FILTER_WIDTH= -1.0 % Stochastic Backscatter Model (NO, YES) STOCHASTIC_BACKSCATTER= NO % +% Backscatter lengthscale coefficient (0.1) +SBS_LENGTHSCALE_COEFF= 0.1 +% +% Maximum number of smoothing iterations for SBS model (100) +SBS_MAX_ITER_SMOOTH= 100 +% % Backscatter timescale coefficient (0.05) SBS_TIMESCALE_COEFF= 0.05 % From 2f8245e84d49610370aa4297b5b0bd80b7ba3d1d Mon Sep 17 00:00:00 2001 From: Angelo Passariello Date: Thu, 30 Oct 2025 08:42:24 +0100 Subject: [PATCH 12/25] Fix redundancy in CTurbSAVariable.hpp - Fix redundancy in virtual member definition. --- SU2_CFD/include/variables/CTurbSAVariable.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SU2_CFD/include/variables/CTurbSAVariable.hpp b/SU2_CFD/include/variables/CTurbSAVariable.hpp index f07c2b8b8b67..0944b5803407 100644 --- a/SU2_CFD/include/variables/CTurbSAVariable.hpp +++ b/SU2_CFD/include/variables/CTurbSAVariable.hpp @@ -100,7 +100,7 @@ class CTurbSAVariable final : public CTurbVariable { * \param[in] iDim - Dimension index. * \return Old value of the source terms for the stochastic equations. */ - inline virtual su2double GetLangevinSourceTermsOld(unsigned long iPoint, unsigned short iDim) const override { return stochSource_old(iPoint, iDim); } + inline su2double GetLangevinSourceTermsOld(unsigned long iPoint, unsigned short iDim) const override { return stochSource_old(iPoint, iDim); } /*! * \brief Set the old value of source terms for the stochastic equations. From bdbfa059c5fc68e823000cc9d1f4c605ab091113 Mon Sep 17 00:00:00 2001 From: Angelo Passariello Date: Thu, 30 Oct 2025 15:20:41 +0100 Subject: [PATCH 13/25] Add stochastic source to turbulence model equation - Add stochastic source term to Spalart-Allmaras turbulence model equation (to ensure exchange of modeled and resolved kinetic energy). --- SU2_CFD/include/numerics/CNumerics.hpp | 13 +++++ .../numerics/turbulent/turb_sources.hpp | 57 +++++++++++++++---- SU2_CFD/src/solvers/CTurbSASolver.cpp | 7 ++- 3 files changed, 63 insertions(+), 14 deletions(-) diff --git a/SU2_CFD/include/numerics/CNumerics.hpp b/SU2_CFD/include/numerics/CNumerics.hpp index db326a23cc50..6df63a83fa1d 100644 --- a/SU2_CFD/include/numerics/CNumerics.hpp +++ b/SU2_CFD/include/numerics/CNumerics.hpp @@ -185,6 +185,9 @@ class CNumerics { su2double stochVar_i[3], /*!< \brief Stochastic variables at point i for Stochastic Backscatter Model. */ stochVar_j[3]; /*!< \brief Stochastic variables at point j for Stochastic Backscatter Model. */ + su2double + lesMode_i, /*!< \brief LES sensor at point i for hybrid RANS-LES methods. */ + lesMode_j; /*!< \brief LES sensor at point j for hybrid RANS-LES methods. */ SST_ParsedOptions sstParsedOptions; /*!< \brief additional options for the SST turbulence model */ unsigned short Eig_Val_Comp; /*!< \brief Component towards which perturbation is perfromed */ su2double uq_delta_b; /*!< \brief Magnitude of perturbation */ @@ -866,6 +869,16 @@ class CNumerics { stochVar_j[iDim] = val_stochvar_j; } + /*! + * \brief Set the LES sensor for hybrid RANS-LES methods. + * \param[in] val_lesMode_i - Value of the LES sensor at point i. + * \param[in] val_lesMode_j - Value of the LES sensor at point j. + */ + inline void SetLES_Mode(su2double val_lesMode_i, su2double val_lesMode_j) { + lesMode_i = val_lesMode_i; + lesMode_j = val_lesMode_j; + } + /*! * \brief Set the value of the distance from the nearest wall. * \param[in] val_dist_i - Value of of the distance from point i to the nearest wall. diff --git a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp index a6e8598c54ef..61d041bb7fc4 100644 --- a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp +++ b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp @@ -158,17 +158,50 @@ class CSourceBase_TurbSA : public CNumerics { } JacobianSB_i[0][0] = Jacobian_i[0]; -// su2double dnut_dnue = var.fv1 + 3.0 * var.cv1_3 * Ji_3 / pow(Ji_3 + var.cv1_3, 2); -// su2double dtTurb_dnut = - ct * pow(lengthScale,2) / (max(nut, nut_small)*max(nut, nut_small)); - -// for (unsigned short iVar = 1; iVar < nVar; iVar++ ) { -// JacobianSB_i[iVar][0] = - 1.0/tTurb * scaleFactor * stochSource[iVar-1] -// + 1.0/(tTurb*tTurb) * ScalarVar_i[iVar] -// + density * corrFac * stochSource[iVar-1] / -// (tTurb * sqrt(2.0*tTurb*timeStep)); -// JacobianSB_i[iVar][0] *= dtTurb_dnut * dnut_dnue * Volume; -// } - } + su2double dnut_dnue = var.fv1 + 3.0 * var.cv1_3 * Ji_3 / pow(Ji_3 + var.cv1_3, 2); + su2double dtTurb_dnut = - ct * pow(lengthScale,2) / (max(nut, nut_small)*max(nut, nut_small)); + + for (unsigned short iVar = 1; iVar < nVar; iVar++ ) { + JacobianSB_i[iVar][0] = - 1.0/tTurb * scaleFactor * stochSource[iVar-1] + + 1.0/(tTurb*tTurb) * ScalarVar_i[iVar] + + density * corrFac * stochSource[iVar-1] / + (tTurb * sqrt(2.0*tTurb*timeStep)); + JacobianSB_i[iVar][0] *= dtTurb_dnut * dnut_dnue * Volume; + } + + } + + /*! + * \brief Include stochastic source term in the Spalart-Allmaras turbulence model equation (Stochastic Backscatter Model). + */ + template + inline void AddStochSource(const CSAVariables& var, const MatrixType& velGrad, const su2double Cmag) { + + su2double dist2 = dist_i * dist_i; + const su2double eps = 1.0e-10; + su2double xi3 = pow(var.Ji, 3); + + su2double factor = dist2 / (2.0 * var.fv1 * ScalarVar_i[0] + eps); + factor /= (3.0 * xi3 * var.cv1_3 / pow(xi3 + var.cv1_3, 2) + var.fv1 + eps); + + const auto& density = V_i[idx.Density()]; + + su2double tke = pow(var.fv1*ScalarVar_i[0]/dist_i, 2); + + su2double R12 = Cmag * density * tke * ScalarVar_i[2]; + su2double R13 = - Cmag * density * tke * ScalarVar_i[1]; + su2double R23 = Cmag * density * tke * ScalarVar_i[0]; + + su2double RGradU = R12 * (velGrad[0][1] - velGrad[1][0]) + + R13 * (velGrad[0][2] - velGrad[2][0]) + + R23 * (velGrad[1][2] - velGrad[2][1]); + + su2double source_k = - RGradU; + su2double source_nu = factor * source_k; + + Residual += source_nu * Volume; + + } public: /*! @@ -342,6 +375,8 @@ class CSourceBase_TurbSA : public CNumerics { /*--- Compute residual for Langevin equations (Stochastic Backscatter Model). ---*/ if (backscatter) { + if (lesMode_i > 0.999) + AddStochSource(var, PrimVar_Grad_i + idx.Velocity(), config->GetSBS_Cmag()); const su2double DES_const = config->GetConst_DES(); const su2double ctau = config->GetSBS_Ctau(); const su2double ctTurb = ctau / pow(DES_const, 2); diff --git a/SU2_CFD/src/solvers/CTurbSASolver.cpp b/SU2_CFD/src/solvers/CTurbSASolver.cpp index 6e99fd4559c0..1c426e5dcb7b 100644 --- a/SU2_CFD/src/solvers/CTurbSASolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp @@ -425,6 +425,7 @@ void CTurbSASolver::Source_Residual(CGeometry *geometry, CSolver **solver_contai if (config->GetStochastic_Backscatter()) { for (unsigned short iDim = 0; iDim < nDim; iDim++) numerics->SetStochSource(nodes->GetLangevinSourceTerms(iPoint, iDim), iDim); + numerics->SetLES_Mode(nodes->GetLES_Mode(iPoint), 0.0); } } @@ -1764,14 +1765,14 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet cout << endl << "Residual of Laplacian smoothing along dimension " << iDim+1 << "." << endl << "---------------------------------" << endl - << " Iter RMS Residual" << endl + << " Iter RMS Residual" << endl << "---------------------------------" << endl; } if (iter%10 == 0) { cout << " " << std::setw(5) << iter << " " - << std::setw(12) << std::fixed << std::scientific << std::setprecision(6) << globalResNorm + << std::setw(12) << std::fixed << std::setprecision(6) << log10(globalResNorm) << endl; } } @@ -1782,7 +1783,7 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet cout << " " << std::setw(5) << iter << " " - << std::setw(12) << std::fixed << std::scientific << ::setprecision(6) << globalResNorm + << std::setw(12) << std::fixed << ::setprecision(6) << log10(globalResNorm) << endl; cout << "---------------------------------" << endl; } From 3272a1b00c4f45247bc83c7a9fc4e6ddbdf9edf0 Mon Sep 17 00:00:00 2001 From: Angelo Passariello Date: Tue, 11 Nov 2025 09:44:58 +0100 Subject: [PATCH 14/25] SOR algorithm for Laplacian smoothing - Replace dual-time integration with Successive Over-Relaxation for Laplacian smoothing. - Initialize stochastic vector potential as equal to the stochastic source terms in Langevin equations. - Add random source term to main balance equations in LES zones exclusively. - Blend RANS and LES turbulence timescales using the LES sensor. --- Common/src/CConfig.cpp | 4 + SU2_CFD/include/numerics/CNumerics.hpp | 26 +++-- .../numerics/turbulent/turb_sources.hpp | 15 ++- .../include/solvers/CFVMFlowSolverBase.inl | 6 +- SU2_CFD/src/numerics/flow/flow_diffusion.cpp | 9 +- SU2_CFD/src/solvers/CTurbSASolver.cpp | 104 +++++++++--------- SU2_CFD/src/variables/CTurbSAVariable.cpp | 8 +- 7 files changed, 95 insertions(+), 77 deletions(-) diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index 7fc6c833770b..6bcc39d1039b 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -6509,6 +6509,10 @@ void CConfig::SetOutput(SU2_COMPONENT val_software, unsigned short val_izone) { cout << "Backscatter timescale coefficient: " << SBS_Ctau << endl; if (SBS_Ctau < 0.0) SU2_MPI::Error("Backscatter timescale coefficient must be non-negative.", CURRENT_FUNCTION); + if (SBS_maxIterSmooth > 0) + cout << "Maximum number of iterations for implicit smoothing: " << SBS_maxIterSmooth << endl; + else + cout << "No smoothing applied to source terms in Langevin equations." << endl; } else { cout << "OFF" << endl; } diff --git a/SU2_CFD/include/numerics/CNumerics.hpp b/SU2_CFD/include/numerics/CNumerics.hpp index 6df63a83fa1d..5cd17e770fe6 100644 --- a/SU2_CFD/include/numerics/CNumerics.hpp +++ b/SU2_CFD/include/numerics/CNumerics.hpp @@ -655,20 +655,28 @@ class CNumerics { */ template NEVERINLINE static void ComputeStochReynStress(size_t nDim, Scalar density, Scalar eddyVis, - Scalar turbKE, Vector rndVec, + Scalar turbKE, Vector rndVec, Scalar lesSensor, Mat& stochReynStress, Scalar Cmag) { /* --- Calculate stochastic tensor --- */ - stochReynStress[1][0] = - Cmag * density * turbKE * rndVec[2]; - stochReynStress[2][0] = Cmag * density * turbKE * rndVec[1]; - stochReynStress[2][1] = - Cmag * density * turbKE * rndVec[0]; - for (size_t iDim = 0; iDim < nDim; iDim++) { - for (size_t jDim = 0; jDim <= iDim; jDim++) { - if (iDim==jDim) { + if (lesSensor > 0.999) { + stochReynStress[1][0] = - Cmag * density * turbKE * rndVec[2]; + stochReynStress[2][0] = Cmag * density * turbKE * rndVec[1]; + stochReynStress[2][1] = - Cmag * density * turbKE * rndVec[0]; + for (size_t iDim = 0; iDim < nDim; iDim++) { + for (size_t jDim = 0; jDim <= iDim; jDim++) { + if (iDim==jDim) { + stochReynStress[iDim][jDim] = 0.0; + } else { + stochReynStress[jDim][iDim] = - stochReynStress[iDim][jDim]; + } + } + } + } else { + for (size_t iDim = 0; iDim < nDim; iDim++) { + for (size_t jDim = 0; jDim < nDim; jDim++) { stochReynStress[iDim][jDim] = 0.0; - } else { - stochReynStress[jDim][iDim] = - stochReynStress[iDim][jDim]; } } } diff --git a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp index 61d041bb7fc4..224f0441a37a 100644 --- a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp +++ b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp @@ -118,7 +118,7 @@ class CSourceBase_TurbSA : public CNumerics { * \brief Include source-term residuals for Langevin equations (Stochastic Backscatter Model) */ inline void ResidualStochEquations(su2double timeStep, const su2double ct, - su2double lengthScale, + su2double lengthScale, su2double DES_const, const CSAVariables& var, TIME_MARCHING time_marching) { const su2double& nue = ScalarVar_i[0]; @@ -132,7 +132,10 @@ class CSourceBase_TurbSA : public CNumerics { const su2double nut = nue * var.fv1; - su2double tTurb = ct * pow(lengthScale, 2) / max(nut, nut_small); + su2double tLES = ct * pow(lengthScale/DES_const, 2) / max(nut, nut_small); + su2double tRANS = pow(lengthScale, 2) / max(nut, nut_small); + su2double tLR = tLES / tRANS; + su2double tTurb = tLES / (lesMode_i + (1.0-lesMode_i)*tLR); su2double tRat = timeStep / tTurb; su2double corrFac = 1.0; @@ -142,7 +145,8 @@ class CSourceBase_TurbSA : public CNumerics { corrFac = sqrt(1.0+0.5*tRat); } - su2double scaleFactor = 1.0/tTurb * sqrt(2.0/tRat) * density * corrFac; + su2double scaleFactor = 0.0; + if (lesMode_i > 0.999) scaleFactor = 1.0/tTurb * sqrt(2.0/tRat) * density * corrFac; ResidSB[0] = Residual; for (unsigned short iVar = 1; iVar < nVar; iVar++ ) { @@ -379,9 +383,8 @@ class CSourceBase_TurbSA : public CNumerics { AddStochSource(var, PrimVar_Grad_i + idx.Velocity(), config->GetSBS_Cmag()); const su2double DES_const = config->GetConst_DES(); const su2double ctau = config->GetSBS_Ctau(); - const su2double ctTurb = ctau / pow(DES_const, 2); - ResidualStochEquations(config->GetDelta_UnstTime(), ctTurb, dist_i, var, - config->GetTime_Marching()); + ResidualStochEquations(config->GetDelta_UnstTime(), ctau, dist_i, DES_const, + var, config->GetTime_Marching()); } } diff --git a/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl b/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl index 747e006a0990..b2a42e4c1666 100644 --- a/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl +++ b/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl @@ -475,11 +475,14 @@ void CFVMFlowSolverBase::Viscous_Residual_impl(unsigned long iEdge, CGeome numerics->SetStochVar(turbNodes->GetSolution(iPoint, 1 + iDim), turbNodes->GetSolution(jPoint, 1 + iDim), iDim); } - su2double eddy_visc_i, eddy_visc_j, DES_length_i, DES_length_j, tke_i, tke_j; + su2double eddy_visc_i, eddy_visc_j, DES_length_i, + DES_length_j, tke_i, tke_j, lesMode_i, lesMode_j; eddy_visc_i = turbNodes->GetmuT(iPoint); eddy_visc_j = turbNodes->GetmuT(jPoint); DES_length_i = turbNodes->GetDES_LengthScale(iPoint); DES_length_j = turbNodes->GetDES_LengthScale(jPoint); + lesMode_i = turbNodes->GetLES_Mode(iPoint); + lesMode_j = turbNodes->GetLES_Mode(jPoint); const su2double tol = 1e-12; if (DES_length_i < tol || DES_length_j < tol) { tke_i = tke_j = 0.0; @@ -488,6 +491,7 @@ void CFVMFlowSolverBase::Viscous_Residual_impl(unsigned long iEdge, CGeome tke_j = pow(eddy_visc_j/DES_length_j, 2); } numerics->SetTurbKineticEnergy(tke_i, tke_j); + numerics->SetLES_Mode(lesMode_i, lesMode_j); } /*--- Wall shear stress values (wall functions) ---*/ diff --git a/SU2_CFD/src/numerics/flow/flow_diffusion.cpp b/SU2_CFD/src/numerics/flow/flow_diffusion.cpp index 7f19ec3db64a..e357dbdeab59 100644 --- a/SU2_CFD/src/numerics/flow/flow_diffusion.cpp +++ b/SU2_CFD/src/numerics/flow/flow_diffusion.cpp @@ -461,8 +461,9 @@ CNumerics::ResidualType<> CAvgGrad_Flow::ComputeResidual(const CConfig* config) for (iVar = 0; iVar < nDim; iVar++) Mean_StochVar[iVar] = 0.5*(stochVar_i[iVar] + stochVar_j[iVar]); su2double SBS_Cmag = config->GetSBS_Cmag(); + su2double lesSensor = max(lesMode_i, lesMode_j); ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_Eddy_Viscosity, Mean_turb_ke, - Mean_StochVar, stochReynStress, SBS_Cmag); + Mean_StochVar, lesSensor, stochReynStress, SBS_Cmag); } /*--- Get projected flux tensor (viscous residual) ---*/ @@ -642,8 +643,9 @@ CNumerics::ResidualType<> CAvgGradInc_Flow::ComputeResidual(const CConfig* confi for (iVar = 0; iVar < nDim; iVar++) Mean_StochVar[iVar] = 0.5*(stochVar_i[iVar] + stochVar_j[iVar]); su2double SBS_Cmag = config->GetSBS_Cmag(); + su2double lesSensor = max(lesMode_i, lesMode_j); ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_Eddy_Viscosity, Mean_turb_ke, - Mean_StochVar, stochReynStress, SBS_Cmag); + Mean_StochVar, lesSensor, stochReynStress, SBS_Cmag); } /*--- Get projected flux tensor (viscous residual) ---*/ @@ -969,8 +971,9 @@ CNumerics::ResidualType<> CGeneralAvgGrad_Flow::ComputeResidual(const CConfig* c for (iVar = 0; iVar < nDim; iVar++) Mean_StochVar[iVar] = 0.5*(stochVar_i[iVar] + stochVar_j[iVar]); su2double SBS_Cmag = config->GetSBS_Cmag(); + su2double lesSensor = max(lesMode_i, lesMode_j); ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_Eddy_Viscosity, Mean_turb_ke, - Mean_StochVar, stochReynStress, SBS_Cmag); + Mean_StochVar, lesSensor, stochReynStress, SBS_Cmag); } /*--- Get projected flux tensor (viscous residual) ---*/ diff --git a/SU2_CFD/src/solvers/CTurbSASolver.cpp b/SU2_CFD/src/solvers/CTurbSASolver.cpp index 1c426e5dcb7b..da36de5760b8 100644 --- a/SU2_CFD/src/solvers/CTurbSASolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp @@ -238,11 +238,29 @@ void CTurbSASolver::Preprocessing(CGeometry *geometry, CSolver **solver_containe bool backscatter = config->GetStochastic_Backscatter(); unsigned long innerIter = config->GetInnerIter(); + unsigned long timeIter = config->GetTimeIter(); + unsigned long restartIter = config->GetRestart_Iter(); if (backscatter && innerIter==0) { SetLangevinGen(config, geometry); SetLangevinSourceTerms(config, geometry); - SmoothLangevinSourceTerms(config, geometry); + const unsigned short maxIter = config->GetSBS_maxIterSmooth(); + bool dual_time = ((config->GetTime_Marching() == TIME_MARCHING::DT_STEPPING_1ST) || + (config->GetTime_Marching() == TIME_MARCHING::DT_STEPPING_2ND)); + if (maxIter>0) SmoothLangevinSourceTerms(config, geometry); + if (timeIter == restartIter) { + for (unsigned long iPoint = 0; iPoint < nPointDomain; iPoint++) { + for (unsigned short iVar = 1; iVar < nVar; iVar++) { + const su2double randomSource = nodes->GetLangevinSourceTerms(iPoint, iVar-1); + nodes->SetSolution(iPoint, iVar, randomSource); + nodes->SetSolution_Old(iPoint, iVar, randomSource); + if (dual_time) { + nodes->Set_Solution_time_n(iPoint, iVar, randomSource); + nodes->Set_Solution_time_n1(iPoint, iVar, randomSource); + } + } + } + } } } @@ -1630,8 +1648,8 @@ void CTurbSASolver::SetLangevinSourceTerms(CConfig *config, CGeometry* geometry) for (auto iDim = 0u; iDim < nDim; iDim++){ auto gen = nodes->GetLangevinGen(iPoint, iDim); su2double lesSensor = nodes->GetLES_Mode(iPoint); - su2double rnd = RandomToolbox::GetRandomNormal(gen); - rnd *= std::nearbyint(lesSensor); + su2double rnd = 0.0; + if (lesSensor > 0.999) rnd = RandomToolbox::GetRandomNormal(gen); nodes->SetLangevinSourceTermsOld(iPoint, iDim, rnd); nodes->SetLangevinSourceTerms(iPoint, iDim, rnd); } @@ -1664,73 +1682,53 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet const su2double cDelta = config->GetSBS_Cdelta(); const unsigned short maxIter = config->GetSBS_maxIterSmooth(); const unsigned long global_nPointDomain = geometry->GetGlobal_nPointDomain(); - const su2double tol = 1.0e-6; - - /*--- Compute the time step ensuring stability of the pseudo-time integration. ---*/ - - su2double localMinDx = -1.0; - for (unsigned long iPoint = 0; iPoint < nPointDomain; ++iPoint) { - auto coord_i = geometry->nodes->GetCoord(iPoint); - for (unsigned short iNode = 0; iNode < geometry->nodes->GetnPoint(iPoint); ++iNode) { - unsigned long jPoint = geometry->nodes->GetPoint(iPoint, iNode); - auto coord_j = geometry->nodes->GetCoord(jPoint); - su2double dx_ij = GeometryToolbox::Distance(nDim, coord_i, coord_j); - su2double maxDelta = geometry->nodes->GetMaxLength(iPoint); - if (LES_FilterWidth > 0.0) maxDelta = LES_FilterWidth; - su2double b = sqrt(cDelta) * maxDelta; - dx_ij /= b; - if (dx_ij < localMinDx || localMinDx < 0.0) localMinDx = dx_ij; + const su2double tol = -5.0; + const su2double sourceLim = 5.0; + const su2double omega = 0.8; + + /*--- Set the stochastic source terms to zero at boundaries. ---*/ + + for (unsigned short iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { + for (unsigned long iVertex = 0; iVertex < geometry->nVertex[iMarker]; iVertex++ ) { + unsigned long iPoint = geometry->vertex[iMarker][iVertex]->GetNode(); + if (geometry->nodes->GetDomain(iPoint)) { + for (unsigned short iDim = 0; iDim < nDim; iDim++) { + nodes->SetLangevinSourceTerms(iPoint, iDim, 0.0); + nodes->SetLangevinSourceTermsOld(iPoint, iDim, 0.0); + } + } } } - su2double globalMinDx = 0.0; - SU2_MPI::Allreduce(&localMinDx, &globalMinDx, 1, MPI_DOUBLE, MPI_MIN, SU2_MPI::GetComm()); - su2double CFL = 0.8; - su2double dt = CFL * globalMinDx * globalMinDx; - /*--- Start the pseudo-time integration for the Laplacian smoothing. ---*/ + /*--- Start SOR algorithm for the Laplacian smoothing. ---*/ for (unsigned short iDim = 0; iDim < nDim; iDim++) { for (unsigned short iter = 0; iter < maxIter; iter++) { - /*--- Set the stochastic source terms to zero at solid walls. ---*/ - - for (unsigned short iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { - if (config->GetSolid_Wall(iMarker)) { - for (unsigned long iVertex = 0; iVertex < geometry->nVertex[iMarker]; iVertex++ ) { - unsigned long iPoint = geometry->vertex[iMarker][iVertex]->GetNode(); - nodes->SetLangevinSourceTerms(iPoint, iDim, 0.0); - nodes->SetLangevinSourceTermsOld(iPoint, iDim, 0.0); - } - } - } - /*--- MPI communication. ---*/ InitiateComms(geometry, config, MPI_QUANTITIES::STOCH_SOURCE_LANG); CompleteComms(geometry, config, MPI_QUANTITIES::STOCH_SOURCE_LANG); - /*--- Update the solution in pseudo-time. ---*/ - su2double localResNorm = 0.0; SU2_OMP_FOR_DYN(omp_chunk_size) for (unsigned long iPoint = 0; iPoint < nPointDomain; iPoint++) { - unsigned short lesSensor = std::nearbyint(nodes->GetLES_Mode(iPoint)); - if (lesSensor == 0) continue; - su2double maxDelta = geometry->nodes->GetMaxLength(iPoint); if (LES_FilterWidth > 0.0) maxDelta = LES_FilterWidth; su2double b = sqrt(cDelta) * maxDelta; su2double b2 = b * b; su2double volume_iPoint = geometry->nodes->GetVolume(iPoint); su2double source_i = nodes->GetLangevinSourceTerms(iPoint, iDim); + su2double source_i_old = nodes->GetLangevinSourceTermsOld(iPoint, iDim); auto coord_i = geometry->nodes->GetCoord(iPoint); - /*--- Discretize the Laplace operator. ---*/ + /*--- Assemble system matrix. ---*/ - su2double lap = 0.0; + su2double diag = 1.0; + su2double sum = 0.0; for (unsigned short iNode = 0; iNode < geometry->nodes->GetnPoint(iPoint); iNode++) { auto jPoint = geometry->nodes->GetPoint(iPoint, iNode); auto coord_j = geometry->nodes->GetCoord(jPoint); @@ -1739,16 +1737,16 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet su2double area = GeometryToolbox::Norm(nDim, normal); su2double dx_ij = GeometryToolbox::Distance(nDim, coord_i, coord_j); su2double source_j = nodes->GetLangevinSourceTerms(jPoint, iDim); - lap += area/volume_iPoint * (source_j - source_i)/dx_ij; + su2double a_ij = area/volume_iPoint * b2/dx_ij; + diag += a_ij; + sum += a_ij * source_j; } - lap *= b2; - /*--- Update the solution and sum the residual. ---*/ + /*--- Update the solution. ---*/ - su2double source_i_old = nodes->GetLangevinSourceTermsOld(iPoint, iDim); - su2double rhs = (source_i_old - source_i + lap) * dt; - localResNorm += rhs * rhs; - source_i += rhs; + su2double source_tmp = (source_i_old + sum) / diag; + localResNorm += pow(omega * (source_tmp - source_i), 2); + source_i = (1.0-omega)*source_i + omega*source_tmp; nodes->SetLangevinSourceTerms(iPoint, iDim, source_i); } @@ -1777,7 +1775,7 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet } } - if (globalResNorm < tol || iter == maxIter-1) { + if (log10(globalResNorm) < tol || iter == maxIter-1) { if (rank == MASTER_NODE) { cout << " " @@ -1797,7 +1795,9 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet su2double b = sqrt(cDelta) * maxDelta; su2double b3 = b * b * b; su2double volume = geometry->nodes->GetVolume(iPoint); - source *= sqrt(8.0*pi*b3/volume); + su2double scaleFactor = sqrt(8.0 * pi * b3 / volume); + source *= scaleFactor; + source = min(max(source, -sourceLim), sourceLim); nodes->SetLangevinSourceTerms(iPoint, iDim, source); } diff --git a/SU2_CFD/src/variables/CTurbSAVariable.cpp b/SU2_CFD/src/variables/CTurbSAVariable.cpp index c0ad7d6e6ab2..0f9b3ec9821b 100644 --- a/SU2_CFD/src/variables/CTurbSAVariable.cpp +++ b/SU2_CFD/src/variables/CTurbSAVariable.cpp @@ -27,7 +27,6 @@ #include "../../include/variables/CTurbSAVariable.hpp" -#include "../../../Common/include/toolboxes/random_toolbox.hpp" CTurbSAVariable::CTurbSAVariable(su2double val_nu_tilde, su2double val_muT, unsigned long npoint, unsigned long ndim, unsigned long nvar, CConfig *config) : @@ -40,11 +39,8 @@ CTurbSAVariable::CTurbSAVariable(su2double val_nu_tilde, su2double val_muT, unsi } else { for (unsigned long iPoint = 0; iPoint < nPoint; iPoint++) { Solution_Old(iPoint, 0) = Solution(iPoint, 0) = val_nu_tilde; - for (unsigned long iVar = 1; iVar < nVar; iVar++) { - unsigned long seed = RandomToolbox::GetSeed(val_nu_tilde, iVar); - std::mt19937 gen(seed); - su2double val_stoch_var = RandomToolbox::GetRandomNormal(gen); - Solution_Old(iPoint, iVar) = Solution(iPoint, iVar) = val_stoch_var; + for (unsigned short iVar = 1; iVar < nVar; iVar++) { + Solution_Old(iPoint, iVar) = Solution(iPoint, iVar) = 0.0; } } } From 15dbff392df54585180e6860ac0204b66f326bdc Mon Sep 17 00:00:00 2001 From: Angelo Passariello Date: Thu, 20 Nov 2025 17:53:52 +0100 Subject: [PATCH 15/25] Correct scaling of stochastic source terms in Langevin eqs. - Scale source terms in Langevin equations using Bessel functions to preserve the variance. - Compute Bessel integral at the beginning of the simulation for optimization. - Add variance monitoring to screen output. --- Common/include/toolboxes/random_toolbox.hpp | 60 +++++++++++++++ SU2_CFD/include/variables/CTurbSAVariable.hpp | 16 +++- SU2_CFD/include/variables/CVariable.hpp | 12 +++ SU2_CFD/src/solvers/CTurbSASolver.cpp | 77 ++++++++++++++++++- SU2_CFD/src/variables/CTurbSAVariable.cpp | 1 + 5 files changed, 161 insertions(+), 5 deletions(-) diff --git a/Common/include/toolboxes/random_toolbox.hpp b/Common/include/toolboxes/random_toolbox.hpp index fe8bc8ca9ebb..96dbed7314c9 100644 --- a/Common/include/toolboxes/random_toolbox.hpp +++ b/Common/include/toolboxes/random_toolbox.hpp @@ -85,5 +85,65 @@ inline double GetRandomUniform(std::mt19937 gen, double xmin = 0.0, double xmax return rnd(gen); } +/*! + * \brief Compute modified bessel function of first kind (order 0). + * \param[in] x Argument of Bessel funtion. + * \return Value of Bessel function. + */ +inline double GetBesselZero(double x) { + double abx = fabs(x); + if (abx < 3.75) { + double t = x / 3.75; + t = t * t; + return 1.0 + t*(3.5156229 + + t*(3.0899424 + + t*(1.2067492 + + t*(0.2659732 + + t*(0.0360768 + + t*0.0045813))))); + } else { + double t = 3.75/abx; + double ans = (exp(abx)/sqrt(abx)) * + (0.39894228 + + t*(0.01328592 + + t*(0.00225319 + + t*(-0.00157565 + + t*(0.00916281 + + t*(-0.02057706 + + t*(0.02635537 + + t*(-0.01647633 + + t*0.00392377)))))))); + return ans; + } +} + +/*! + * \brief Compute integral involving product of three modified Bessel functions. + * \param[in] beta_x Argument in x-direction. + * \param[in] beta_y Argument in y-direction. + * \param[in] beta_z Argument in z-direction. + * \return Value of the integral. + */ +inline double GetBesselIntegral(double beta_x, double beta_y, double beta_z) { + const double A = 1.0 + 2.0*(beta_x + beta_y + beta_z); + const double Bx = 2.0*beta_x; + const double By = 2.0*beta_y; + const double Bz = 2.0*beta_z; + const int N = 4000; + const double t_max = 40.0; + const double delta_t = t_max / N; + double sum = 0.0; + for (int i = 0; i < N; i++) { + double t = i * delta_t; + double I0x = GetBesselZero(Bx * t); + double I0y = GetBesselZero(By * t); + double I0z = GetBesselZero(Bz * t); + double integrand = t * exp(-A * t) + * (I0x * I0y * I0z); + sum += integrand; + } + return sum * delta_t; +} + /// @} } // namespace RandomToolbox diff --git a/SU2_CFD/include/variables/CTurbSAVariable.hpp b/SU2_CFD/include/variables/CTurbSAVariable.hpp index 0944b5803407..4b625b901012 100644 --- a/SU2_CFD/include/variables/CTurbSAVariable.hpp +++ b/SU2_CFD/include/variables/CTurbSAVariable.hpp @@ -46,6 +46,7 @@ class CTurbSAVariable final : public CTurbVariable { MatrixType stochSource_old; VectorType Vortex_Tilting; MatrixTypeGen stochGen; + VectorType besselIntegral; public: /*! @@ -110,7 +111,7 @@ class CTurbSAVariable final : public CTurbVariable { */ inline void SetLangevinSourceTermsOld(unsigned long iPoint, unsigned short iDim, su2double val_stochSource_old) override { stochSource_old(iPoint, iDim) = val_stochSource_old; } -/*! + /*! * \brief Set the LES sensor. */ inline void SetLES_Mode(unsigned long iPoint, su2double val_les_mode) override { LES_Mode(iPoint) = val_les_mode; } @@ -151,4 +152,17 @@ class CTurbSAVariable final : public CTurbVariable { */ inline void SetLangevinGen(unsigned long iPoint, unsigned short iDim, std::mt19937 val_stochGen) override { stochGen(iPoint, iDim) = val_stochGen; } + /*! + * \brief Set the integral of the product of three Bessel functions appearing in Laplacian smoothing. + * \param[in] iPoint - Point index. + * \param[in] val_integral - Value of the integral. + */ + inline void SetBesselIntegral(unsigned long iPoint, su2double val_integral) override { besselIntegral(iPoint) = val_integral; } + + /*! + * \brief Get the the integral of the product of three Bessel functions appearing in Laplacian smoothing. + * \return Value of the integral. + */ + inline su2double GetBesselIntegral(unsigned long iPoint) const override { return besselIntegral(iPoint); } + }; diff --git a/SU2_CFD/include/variables/CVariable.hpp b/SU2_CFD/include/variables/CVariable.hpp index 4eb19e9dd0b9..5a432dedcbb7 100644 --- a/SU2_CFD/include/variables/CVariable.hpp +++ b/SU2_CFD/include/variables/CVariable.hpp @@ -449,6 +449,18 @@ class CVariable { */ inline virtual std::mt19937 GetLangevinGen(unsigned long iPoint, unsigned short iDim) const {std::mt19937 gen(123); return gen; } + /*! + * \brief A virtual member. + * \param[in] iPoint - Point index. + * \param[in] val_integral - Value of the integral. + */ + inline virtual void SetBesselIntegral(unsigned long iPoint, su2double val_integral) {} + + /*! + * \brief A virtual member. + */ + inline virtual su2double GetBesselIntegral(unsigned long iPoint) const { return 0.0; } + /*! * \brief A virtual member. * \param[in] iPoint - Point index. diff --git a/SU2_CFD/src/solvers/CTurbSASolver.cpp b/SU2_CFD/src/solvers/CTurbSASolver.cpp index da36de5760b8..4f80b2a69925 100644 --- a/SU2_CFD/src/solvers/CTurbSASolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp @@ -1678,13 +1678,14 @@ void CTurbSASolver::SetLangevinGen(CConfig* config, CGeometry* geometry) { void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geometry) { const su2double LES_FilterWidth = config->GetLES_FilterWidth(); - const su2double pi = 4.0*atan(1.0); const su2double cDelta = config->GetSBS_Cdelta(); const unsigned short maxIter = config->GetSBS_maxIterSmooth(); const unsigned long global_nPointDomain = geometry->GetGlobal_nPointDomain(); const su2double tol = -5.0; const su2double sourceLim = 5.0; const su2double omega = 0.8; + unsigned long timeIter = config->GetTimeIter(); + unsigned long restartIter = config->GetRestart_Iter(); /*--- Set the stochastic source terms to zero at boundaries. ---*/ @@ -1788,18 +1789,86 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet /*--- Scale source terms for variance preservation. ---*/ + su2double var_check_old = 0.0; + su2double mean_check_old = 0.0; + su2double var_check_new = 0.0; + su2double mean_check_new = 0.0; + su2double var_check_notSmoothed = 0.0; + su2double mean_check_notSmoothed = 0.0; for (unsigned long iPoint = 0; iPoint < nPointDomain; iPoint++) { su2double source = nodes->GetLangevinSourceTerms(iPoint, iDim); + su2double source_notSmoothed = nodes->GetLangevinSourceTermsOld(iPoint, iDim); + mean_check_old += source; + var_check_old += source * source; + mean_check_notSmoothed += source_notSmoothed; + var_check_notSmoothed += source_notSmoothed * source_notSmoothed; su2double maxDelta = geometry->nodes->GetMaxLength(iPoint); if (LES_FilterWidth > 0.0) maxDelta = LES_FilterWidth; su2double b = sqrt(cDelta) * maxDelta; - su2double b3 = b * b * b; - su2double volume = geometry->nodes->GetVolume(iPoint); - su2double scaleFactor = sqrt(8.0 * pi * b3 / volume); + su2double b2 = b * b; + auto coord_i = geometry->nodes->GetCoord(iPoint); + su2double max_dx = 0.0; + su2double max_dy = 0.0; + su2double max_dz = 0.0; + unsigned short nNeigh = geometry->nodes->GetnPoint(iPoint); + for (unsigned short iNode = 0; iNode < nNeigh; iNode++) { + auto jPoint = geometry->nodes->GetPoint(iPoint, iNode); + auto coord_j = geometry->nodes->GetCoord(jPoint); + su2double dx = fabs(coord_i[0]-coord_j[0]); + su2double dy = fabs(coord_i[1]-coord_j[1]); + su2double dz = 0.0; + if (nDim == 3) dz = fabs(coord_i[2]-coord_j[2]); + if (dx > max_dx) max_dx = dx; + if (dy > max_dy) max_dy = dy; + if (dz > max_dz) max_dz = dz; + } + su2double dx2 = max_dx * max_dx; + su2double dy2 = max_dy * max_dy; + su2double dz2 = max_dz * max_dz; + su2double bx = b2 / dx2; + su2double by = b2 / dy2; + su2double bz = 0.0; + if (nDim == 3) bz = b2 / dz2; + su2double integral = 0.0; + if (timeIter==restartIter) { + integral = RandomToolbox::GetBesselIntegral(bx, by, bz); + nodes->SetBesselIntegral(iPoint, integral); + } else { + integral = nodes->GetBesselIntegral(iPoint); + } + su2double scaleFactor = 1.0 / sqrt(max(integral, 1e-10)); source *= scaleFactor; + mean_check_new += source; + var_check_new += source * source; source = min(max(source, -sourceLim), sourceLim); nodes->SetLangevinSourceTerms(iPoint, iDim, source); } + su2double mean_check_old_G = 0.0; + su2double mean_check_new_G = 0.0; + su2double mean_check_notSmoothed_G = 0.0; + su2double var_check_old_G = 0.0; + su2double var_check_new_G = 0.0; + su2double var_check_notSmoothed_G = 0.0; + SU2_MPI::Allreduce(&mean_check_old, &mean_check_old_G, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); + SU2_MPI::Allreduce(&mean_check_new, &mean_check_new_G, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); + SU2_MPI::Allreduce(&mean_check_notSmoothed, &mean_check_notSmoothed_G, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); + SU2_MPI::Allreduce(&var_check_old, &var_check_old_G, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); + SU2_MPI::Allreduce(&var_check_new, &var_check_new_G, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); + SU2_MPI::Allreduce(&var_check_notSmoothed, &var_check_notSmoothed_G, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); + mean_check_old_G /= global_nPointDomain; + var_check_old_G /= global_nPointDomain; + var_check_old_G -= mean_check_old_G * mean_check_old_G; + mean_check_new_G /= global_nPointDomain; + var_check_new_G /= global_nPointDomain; + var_check_new_G -= mean_check_new_G * mean_check_new_G; + mean_check_notSmoothed_G /= global_nPointDomain; + var_check_notSmoothed_G /= global_nPointDomain; + var_check_notSmoothed_G -= mean_check_notSmoothed_G * mean_check_notSmoothed_G; + if (rank == MASTER_NODE) { + cout << "Mean of stochastic source term before scaling: " << mean_check_old_G <<". After: " << mean_check_new_G << "." << endl; + cout << "Variance of stochastic source term before scaling: " << var_check_old_G/var_check_notSmoothed_G <<". After: " << var_check_new_G/var_check_notSmoothed_G << "." << endl; + cout << endl; + } break; diff --git a/SU2_CFD/src/variables/CTurbSAVariable.cpp b/SU2_CFD/src/variables/CTurbSAVariable.cpp index 0f9b3ec9821b..384cc70e51cc 100644 --- a/SU2_CFD/src/variables/CTurbSAVariable.cpp +++ b/SU2_CFD/src/variables/CTurbSAVariable.cpp @@ -62,6 +62,7 @@ CTurbSAVariable::CTurbSAVariable(su2double val_nu_tilde, su2double val_muT, unsi stochSource.resize(nPoint, nDim) = su2double(0.0); stochSource_old.resize(nPoint, nDim) = su2double(0.0); stochGen.resize(nPoint, nDim); + besselIntegral.resize(nPoint); } From 420e44aadbb02f29a8395421e4de5053a0eb39f8 Mon Sep 17 00:00:00 2001 From: Angelo Passariello Date: Wed, 26 Nov 2025 14:56:50 +0100 Subject: [PATCH 16/25] Add LD2 scheme for the incompressible solver - Add LD2 discretization for the inviscid terms in the main balance equations. Remarks: -- Valid only for the incompressible flow solver, with constant density fluid model. -- JST scheme must be selected in the config file. The novel option LD2_SCHEME must be set to YES. --- Common/include/CConfig.hpp | 7 +++++ Common/src/CConfig.cpp | 15 ++++++++++- .../src/numerics/flow/convection/centered.cpp | 27 +++++++++++++++++++ SU2_CFD/src/solvers/CIncEulerSolver.cpp | 6 +++++ SU2_CFD/src/solvers/CTurbSASolver.cpp | 4 +-- config_template.cfg | 3 +++ 6 files changed, 59 insertions(+), 3 deletions(-) diff --git a/Common/include/CConfig.hpp b/Common/include/CConfig.hpp index c74f56998187..52245b1ffbe3 100644 --- a/Common/include/CConfig.hpp +++ b/Common/include/CConfig.hpp @@ -1095,6 +1095,7 @@ class CConfig { su2double SBS_Cmag; /*!< \brief Stochastic Backscatter Model intensity coefficient. */ bool enforceLES; /*!< \brief Option to enforce LES mode in hybrid RANS-LES simulations. */ su2double LES_FilterWidth; /*!< \brief LES filter width for hybrid RANS-LES simulations. */ + bool LD2_Scheme; /*!< \brief Use the LD2 scheme (incompressible flows, combined with JST discretization). */ unsigned short Kind_RoeLowDiss; /*!< \brief Kind of Roe scheme with low dissipation for unsteady flows. */ unsigned short nSpanWiseSections; /*!< \brief number of span-wise sections */ @@ -9543,6 +9544,12 @@ class CConfig { */ su2double GetLES_FilterWidth(void) const { return LES_FilterWidth; } + /*! + * \brief Get if the LD2 scheme must be employed (incompressible flows, combined with JST discretization). + * \return TRUE if LD2 scheme is enabled. + */ + bool GetLD2_Scheme(void) const { return LD2_Scheme; } + /*! * \brief Get the Kind of Roe Low Dissipation Scheme for Unsteady flows. * \return Value of Low dissipation approach. diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index 6bcc39d1039b..c7e9ed11df9b 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -2945,6 +2945,9 @@ void CConfig::SetConfig_Options() { /* DESCRIPTION: Filter width for LES (if negative, it is computed based on the local cell size) */ addDoubleOption("LES_FILTER_WIDTH", LES_FilterWidth, -1.0); + /* DESCRIPTION: Specify if the LD2 scheme must be employed (incompressible flows, combined with JST discretization). */ + addBoolOption("LD2_OPTION", LD2_Scheme, false); + /* DESCRIPTION: Roe with low dissipation for unsteady flows */ addEnumOption("ROE_LOW_DISSIPATION", Kind_RoeLowDiss, RoeLowDiss_Map, NO_ROELOWDISS); @@ -7079,7 +7082,17 @@ void CConfig::SetOutput(SU2_COMPONENT val_software, unsigned short val_izone) { cout << "First order integration." << endl; } else { - cout << "Jameson-Schmidt-Turkel scheme (2nd order in space) for the flow inviscid terms.\n"; + if (LD2_Scheme) { + cout << "Low-Dissipation Low-Dispersion (LD2) scheme for the flow inviscid terms." << endl; + if (!(Kind_Solver==MAIN_SOLVER::INC_EULER || Kind_Solver==MAIN_SOLVER::INC_NAVIER_STOKES || Kind_Solver==MAIN_SOLVER::INC_RANS)) + SU2_MPI::Error("LD2 option available for incompressible flow simulations only.", CURRENT_FUNCTION); + if (Kind_FluidModel != CONSTANT_DENSITY) + SU2_MPI::Error("LD2 option available for constant density flow simulations only.", CURRENT_FUNCTION); + if (Energy_Equation) + cout << "WARNING: LD2 option not compatible with the energy equation. JST discretization in energy equation employed." << endl; + } else { + cout << "Jameson-Schmidt-Turkel scheme (2nd order in space) for the flow inviscid terms.\n"; + } cout << "JST viscous coefficients (2nd & 4th): " << Kappa_2nd_Flow << ", " << Kappa_4th_Flow << ".\n"; cout << "The method includes a grid stretching correction (p = 0.3)."<< endl; } diff --git a/SU2_CFD/src/numerics/flow/convection/centered.cpp b/SU2_CFD/src/numerics/flow/convection/centered.cpp index ba66bd66c185..2beb4cadc540 100644 --- a/SU2_CFD/src/numerics/flow/convection/centered.cpp +++ b/SU2_CFD/src/numerics/flow/convection/centered.cpp @@ -312,6 +312,8 @@ CNumerics::ResidualType<> CCentJSTInc_Flow::ComputeResidual(const CConfig* confi su2double U_i[5] = {0.0}, U_j[5] = {0.0}; su2double ProjGridVel = 0.0; + bool LD2_Scheme = config->GetLD2_Scheme(); + const su2double alpha_LD2 = 0.36; /*--- Primitive variables at point i and j ---*/ @@ -327,6 +329,31 @@ CNumerics::ResidualType<> CCentJSTInc_Flow::ComputeResidual(const CConfig* confi for (iDim = 0; iDim < nDim; iDim++) { Velocity_i[iDim] = V_i[iDim+1]; Velocity_j[iDim] = V_j[iDim+1]; + } + + if (LD2_Scheme) { + su2double d_ij[3] = {0.0}; + for (iDim = 0; iDim < nDim; iDim++) + d_ij[iDim] = Coord_j[iDim]-Coord_i[iDim]; + su2double velGrad_i[3][3] = {{0.0}}; + su2double velGrad_j[3][3] = {{0.0}}; + for (unsigned short jDim = 0; jDim < nDim; jDim++) { + for (iDim = 0; iDim < nDim; iDim++) { + velGrad_i[iDim][jDim] = PrimVar_Grad_i[iDim+1][jDim]; + velGrad_j[iDim][jDim] = PrimVar_Grad_j[iDim+1][jDim]; + } + } + for (iDim = 0; iDim < nDim; iDim++) { + Velocity_i[iDim] += alpha_LD2 * (velGrad_i[iDim][0] * d_ij[0] + + velGrad_i[iDim][1] * d_ij[1] + + velGrad_i[iDim][2] * d_ij[2]); + Velocity_j[iDim] -= alpha_LD2 * (velGrad_j[iDim][0] * d_ij[0] + + velGrad_j[iDim][1] * d_ij[1] + + velGrad_j[iDim][2] * d_ij[2]); + } + } + + for (iDim = 0; iDim < nDim; iDim++) { MeanVelocity[iDim] = 0.5*(Velocity_i[iDim]+Velocity_j[iDim]); sq_vel_i += 0.5*Velocity_i[iDim]*Velocity_i[iDim]; sq_vel_j += 0.5*Velocity_j[iDim]*Velocity_j[iDim]; diff --git a/SU2_CFD/src/solvers/CIncEulerSolver.cpp b/SU2_CFD/src/solvers/CIncEulerSolver.cpp index 3df1ffbe3dd8..f964b22f74c1 100644 --- a/SU2_CFD/src/solvers/CIncEulerSolver.cpp +++ b/SU2_CFD/src/solvers/CIncEulerSolver.cpp @@ -1125,6 +1125,7 @@ void CIncEulerSolver::Centered_Residual(CGeometry *geometry, CSolver **solver_co const bool implicit = (config->GetKind_TimeIntScheme() == EULER_IMPLICIT); const bool jst_scheme = ((config->GetKind_Centered_Flow() == CENTERED::JST) && (iMesh == MESH_0)); const bool bounded_scalar = config->GetBounded_Scalar(); + const bool LD2_Scheme = config->GetLD2_Scheme(); /*--- For hybrid parallel AD, pause preaccumulation if there is shared reading of * variables, otherwise switch to the faster adjoint evaluation mode. ---*/ @@ -1162,6 +1163,11 @@ void CIncEulerSolver::Centered_Residual(CGeometry *geometry, CSolver **solver_co numerics->SetSensor(nodes->GetSensor(iPoint), nodes->GetSensor(jPoint)); } + if (LD2_Scheme) { + numerics->SetPrimVarGradient(nodes->GetGradient_Primitive(iPoint), nodes->GetGradient_Primitive(jPoint)); + numerics->SetCoord(geometry->nodes->GetCoord(iPoint), geometry->nodes->GetCoord(jPoint)); + } + /*--- Grid movement ---*/ if (dynamic_grid) { diff --git a/SU2_CFD/src/solvers/CTurbSASolver.cpp b/SU2_CFD/src/solvers/CTurbSASolver.cpp index 4f80b2a69925..41b4146d78f0 100644 --- a/SU2_CFD/src/solvers/CTurbSASolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp @@ -1865,8 +1865,8 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet var_check_notSmoothed_G /= global_nPointDomain; var_check_notSmoothed_G -= mean_check_notSmoothed_G * mean_check_notSmoothed_G; if (rank == MASTER_NODE) { - cout << "Mean of stochastic source term before scaling: " << mean_check_old_G <<". After: " << mean_check_new_G << "." << endl; - cout << "Variance of stochastic source term before scaling: " << var_check_old_G/var_check_notSmoothed_G <<". After: " << var_check_new_G/var_check_notSmoothed_G << "." << endl; + cout << "Mean of stochastic source term before scaling: " << mean_check_old_G <<". After scaling: " << mean_check_new_G << "." << endl; + cout << "Variance of stochastic source term before scaling: " << var_check_old_G/var_check_notSmoothed_G <<". After scaling: " << var_check_new_G/var_check_notSmoothed_G << "." << endl; cout << endl; } diff --git a/config_template.cfg b/config_template.cfg index ca8aba44d7a0..b224656e4de8 100644 --- a/config_template.cfg +++ b/config_template.cfg @@ -1636,6 +1636,9 @@ SMOOTH_GEOMETRY= 0 % SW, MSW, FDS, SLAU, SLAU2, L2ROE, LMROE) CONV_NUM_METHOD_FLOW= ROE % +% Option to employ the Low-Dissipation Low-Dispersion (LD2) scheme for incompressible flows (NO, YES) +LD2_SCHEME= NO +% % Roe Low Dissipation function for Hybrid RANS/LES simulations (FD, NTS, NTS_DUCROS) ROE_LOW_DISSIPATION= FD % From 5c3e35ce22e98b159c319f9cdf8c1b774fc69a88 Mon Sep 17 00:00:00 2001 From: Angelo Passariello Date: Fri, 28 Nov 2025 18:28:41 +0100 Subject: [PATCH 17/25] Fix LD2 for periodic boundaries - Retrieve classic second-order central scheme at periodic boundaries. --- Common/src/CConfig.cpp | 4 ++++ SU2_CFD/src/solvers/CIncEulerSolver.cpp | 7 ++++++- config_template.cfg | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index 3fa9e1e0027c..f4a0bef3159e 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -7098,6 +7098,10 @@ void CConfig::SetOutput(SU2_COMPONENT val_software, unsigned short val_izone) { } } + if (Kind_ConvNumScheme_Flow != SPACE_CENTERED || (Kind_ConvNumScheme_Flow == SPACE_CENTERED && Kind_Centered_Flow == CENTERED::LAX)) { + SU2_MPI::Error("LD2 option available for JST scheme only.", CURRENT_FUNCTION); + } + if (Kind_ConvNumScheme_Flow == SPACE_UPWIND) { if (Kind_Upwind_Flow == UPWIND::ROE) cout << "Roe (with entropy fix = "<< EntropyFix_Coeff <<") solver for the flow inviscid terms."<< endl; if (Kind_Upwind_Flow == UPWIND::TURKEL) cout << "Roe-Turkel solver for the flow inviscid terms."<< endl; diff --git a/SU2_CFD/src/solvers/CIncEulerSolver.cpp b/SU2_CFD/src/solvers/CIncEulerSolver.cpp index f964b22f74c1..8e249147d7d4 100644 --- a/SU2_CFD/src/solvers/CIncEulerSolver.cpp +++ b/SU2_CFD/src/solvers/CIncEulerSolver.cpp @@ -1165,7 +1165,12 @@ void CIncEulerSolver::Centered_Residual(CGeometry *geometry, CSolver **solver_co if (LD2_Scheme) { numerics->SetPrimVarGradient(nodes->GetGradient_Primitive(iPoint), nodes->GetGradient_Primitive(jPoint)); - numerics->SetCoord(geometry->nodes->GetCoord(iPoint), geometry->nodes->GetCoord(jPoint)); + if (!geometry->nodes->GetPeriodicBoundary(iPoint) || (geometry->nodes->GetPeriodicBoundary(iPoint) + && !geometry->nodes->GetPeriodicBoundary(jPoint))) { + numerics->SetCoord(geometry->nodes->GetCoord(iPoint), geometry->nodes->GetCoord(jPoint)); + } else { + numerics->SetCoord(geometry->nodes->GetCoord(iPoint), geometry->nodes->GetCoord(iPoint)); + } } /*--- Grid movement ---*/ diff --git a/config_template.cfg b/config_template.cfg index b224656e4de8..60138cc27edb 100644 --- a/config_template.cfg +++ b/config_template.cfg @@ -1637,7 +1637,7 @@ SMOOTH_GEOMETRY= 0 CONV_NUM_METHOD_FLOW= ROE % % Option to employ the Low-Dissipation Low-Dispersion (LD2) scheme for incompressible flows (NO, YES) -LD2_SCHEME= NO +LD2_OPTION= NO % % Roe Low Dissipation function for Hybrid RANS/LES simulations (FD, NTS, NTS_DUCROS) ROE_LOW_DISSIPATION= FD From 63890230d77d3b26e599bc713f6cc8ed8335cc4d Mon Sep 17 00:00:00 2001 From: Angelo Passariello <91474456+AngPass@users.noreply.github.com> Date: Wed, 3 Dec 2025 14:53:54 +0100 Subject: [PATCH 18/25] Include stochastic source term in turb. equation Add option to include stochastic contribution to turbulence model equation. --- Common/include/CConfig.hpp | 7 +++++++ Common/src/CConfig.cpp | 5 +++++ SU2_CFD/include/numerics/turbulent/turb_sources.hpp | 2 +- config_template.cfg | 5 ++++- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Common/include/CConfig.hpp b/Common/include/CConfig.hpp index 52245b1ffbe3..bdd0741aab5f 100644 --- a/Common/include/CConfig.hpp +++ b/Common/include/CConfig.hpp @@ -1093,6 +1093,7 @@ class CConfig { unsigned short SBS_maxIterSmooth; /*!< \brief Maximum number of smoothing iterations for the SBS model. */ su2double SBS_Ctau; /*!< \brief Stochastic Backscatter Model timescale coefficient. */ su2double SBS_Cmag; /*!< \brief Stochastic Backscatter Model intensity coefficient. */ + bool stochSourceNu; /*!< \brief Option for including stochastic source term in turbulence model equation (Stochastic Backscatter Model). */ bool enforceLES; /*!< \brief Option to enforce LES mode in hybrid RANS-LES simulations. */ su2double LES_FilterWidth; /*!< \brief LES filter width for hybrid RANS-LES simulations. */ bool LD2_Scheme; /*!< \brief Use the LD2 scheme (incompressible flows, combined with JST discretization). */ @@ -9538,6 +9539,12 @@ class CConfig { */ bool GetEnforceLES(void) const { return enforceLES; } + /*! + * \brief Get if the stochastic source term must be included in the turbulence model equation. + * \return TRUE if the stochastic source term is included in the turbulence model equation. + */ + bool GetStochSourceNu(void) const { return stochSourceNu; } + /*! * \brief Get the LES Filter Width. * \return Value of LES Filter Width. diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index f4a0bef3159e..ed8ab9bce5ba 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -2942,6 +2942,9 @@ void CConfig::SetConfig_Options() { /* DESCRIPTION: Specify if the LES mode must be enforced */ addBoolOption("ENFORCE_LES", enforceLES, false); + /* DESCRIPTION: Specify if the stochastic source term must be included in the turbulence model equation */ + addBoolOption("STOCH_SOURCE_NU", stochSourceNu, false) + /* DESCRIPTION: Filter width for LES (if negative, it is computed based on the local cell size) */ addDoubleOption("LES_FILTER_WIDTH", LES_FilterWidth, -1.0); @@ -6516,6 +6519,8 @@ void CConfig::SetOutput(SU2_COMPONENT val_software, unsigned short val_izone) { cout << "Maximum number of iterations for implicit smoothing: " << SBS_maxIterSmooth << endl; else cout << "No smoothing applied to source terms in Langevin equations." << endl; + if (stochSourceNu) + cout << "Stochastic source term included in turbulence model equation" << endl; } else { cout << "OFF" << endl; } diff --git a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp index 224f0441a37a..4992ebe60d2e 100644 --- a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp +++ b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp @@ -379,7 +379,7 @@ class CSourceBase_TurbSA : public CNumerics { /*--- Compute residual for Langevin equations (Stochastic Backscatter Model). ---*/ if (backscatter) { - if (lesMode_i > 0.999) + if (lesMode_i > 0.999 && config->GetStochSourceNu()) AddStochSource(var, PrimVar_Grad_i + idx.Velocity(), config->GetSBS_Cmag()); const su2double DES_const = config->GetConst_DES(); const su2double ctau = config->GetSBS_Ctau(); diff --git a/config_template.cfg b/config_template.cfg index 60138cc27edb..2abd0d076242 100644 --- a/config_template.cfg +++ b/config_template.cfg @@ -199,9 +199,12 @@ SBS_TIMESCALE_COEFF= 0.05 % Backscatter intensity coefficient (1.0) SBS_INTENSITY_COEFF= 1.0 % +% Include stochastic source in turbulence model equation (NO, YES) +STOCH_SOURCE_NU= NO +% % Enforce LES mode in Hybrid RANS-LES Simulations (NO, YES) ENFORCE_LES= NO - +% % -------------------- COMPRESSIBLE FREE-STREAM DEFINITION --------------------% % % Mach number (non-dimensional, based on the free-stream values) From d382a2d11dad8ee154915d2e3b1cbe6ab4bf4e06 Mon Sep 17 00:00:00 2001 From: Angelo Passariello Date: Wed, 3 Dec 2025 15:05:50 +0100 Subject: [PATCH 19/25] Minor fixes - Use logarithmic approximation for Bessel function. - Compute relative variance of the stochastic field for verification. --- Common/include/toolboxes/random_toolbox.hpp | 72 ++++++++++++--------- Common/src/CConfig.cpp | 2 +- SU2_CFD/src/solvers/CTurbSASolver.cpp | 6 +- 3 files changed, 46 insertions(+), 34 deletions(-) diff --git a/Common/include/toolboxes/random_toolbox.hpp b/Common/include/toolboxes/random_toolbox.hpp index 96dbed7314c9..28eb02676369 100644 --- a/Common/include/toolboxes/random_toolbox.hpp +++ b/Common/include/toolboxes/random_toolbox.hpp @@ -92,28 +92,30 @@ inline double GetRandomUniform(std::mt19937 gen, double xmin = 0.0, double xmax */ inline double GetBesselZero(double x) { double abx = fabs(x); + if (abx < 3.75) { double t = x / 3.75; - t = t * t; - return 1.0 + t*(3.5156229 + - t*(3.0899424 + - t*(1.2067492 + - t*(0.2659732 + - t*(0.0360768 + - t*0.0045813))))); + double p = 1.0 + + t*t*(3.5156229 + + t*t*(3.0899424 + + t*t*(1.2067492 + + t*t*(0.2659732 + + t*t*(0.0360768 + + t*t*0.0045813))))); + return log(p); } else { - double t = 3.75/abx; - double ans = (exp(abx)/sqrt(abx)) * - (0.39894228 + - t*(0.01328592 + - t*(0.00225319 + - t*(-0.00157565 + - t*(0.00916281 + - t*(-0.02057706 + - t*(0.02635537 + - t*(-0.01647633 + - t*0.00392377)))))))); - return ans; + double t = 3.75 / abx; + double poly = + 0.39894228 + + t*(0.01328592 + + t*(0.00225319 + + t*(-0.00157565 + + t*(0.00916281 + + t*(-0.02057706 + + t*(0.02635537 + + t*(-0.01647633 + + t*0.00392377))))))); + return abx - 0.5*log(abx) + log(poly); } } @@ -125,24 +127,34 @@ inline double GetBesselZero(double x) { * \return Value of the integral. */ inline double GetBesselIntegral(double beta_x, double beta_y, double beta_z) { + const double A = 1.0 + 2.0*(beta_x + beta_y + beta_z); const double Bx = 2.0*beta_x; const double By = 2.0*beta_y; const double Bz = 2.0*beta_z; - const int N = 4000; - const double t_max = 40.0; - const double delta_t = t_max / N; + + const int N = 4000; + const double t_max = 40.0; + const double dt = t_max / N; + double sum = 0.0; - for (int i = 0; i < N; i++) { - double t = i * delta_t; - double I0x = GetBesselZero(Bx * t); - double I0y = GetBesselZero(By * t); - double I0z = GetBesselZero(Bz * t); - double integrand = t * exp(-A * t) - * (I0x * I0y * I0z); + + for (int i = 1; i < N; i++) { + double t = i * dt; + + double e = exp(-A*t); + + double lx = GetBesselZero(Bx*t); + double ly = GetBesselZero(By*t); + double lz = GetBesselZero(Bz*t); + + double lin = log(t) - A*t + lx + ly + lz; + + double integrand = exp(lin); sum += integrand; } - return sum * delta_t; + + return sum * dt; } /// @} diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index ed8ab9bce5ba..8e8d09bedfc8 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -2943,7 +2943,7 @@ void CConfig::SetConfig_Options() { addBoolOption("ENFORCE_LES", enforceLES, false); /* DESCRIPTION: Specify if the stochastic source term must be included in the turbulence model equation */ - addBoolOption("STOCH_SOURCE_NU", stochSourceNu, false) + addBoolOption("STOCH_SOURCE_NU", stochSourceNu, false); /* DESCRIPTION: Filter width for LES (if negative, it is computed based on the local cell size) */ addDoubleOption("LES_FILTER_WIDTH", LES_FilterWidth, -1.0); diff --git a/SU2_CFD/src/solvers/CTurbSASolver.cpp b/SU2_CFD/src/solvers/CTurbSASolver.cpp index 41b4146d78f0..02132c345aa0 100644 --- a/SU2_CFD/src/solvers/CTurbSASolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp @@ -1682,7 +1682,7 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet const unsigned short maxIter = config->GetSBS_maxIterSmooth(); const unsigned long global_nPointDomain = geometry->GetGlobal_nPointDomain(); const su2double tol = -5.0; - const su2double sourceLim = 5.0; + const su2double sourceLim = 3.0; const su2double omega = 0.8; unsigned long timeIter = config->GetTimeIter(); unsigned long restartIter = config->GetRestart_Iter(); @@ -1838,9 +1838,9 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet } su2double scaleFactor = 1.0 / sqrt(max(integral, 1e-10)); source *= scaleFactor; + source = min(max(source, -sourceLim), sourceLim); mean_check_new += source; var_check_new += source * source; - source = min(max(source, -sourceLim), sourceLim); nodes->SetLangevinSourceTerms(iPoint, iDim, source); } su2double mean_check_old_G = 0.0; @@ -1865,7 +1865,7 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet var_check_notSmoothed_G /= global_nPointDomain; var_check_notSmoothed_G -= mean_check_notSmoothed_G * mean_check_notSmoothed_G; if (rank == MASTER_NODE) { - cout << "Mean of stochastic source term before scaling: " << mean_check_old_G <<". After scaling: " << mean_check_new_G << "." << endl; + cout << "Mean of stochastic source term before scaling: " << mean_check_old_G-mean_check_notSmoothed_G <<". After scaling: " << mean_check_new_G-mean_check_notSmoothed_G << "." << endl; cout << "Variance of stochastic source term before scaling: " << var_check_old_G/var_check_notSmoothed_G <<". After scaling: " << var_check_new_G/var_check_notSmoothed_G << "." << endl; cout << endl; } From d1a1ae8f21022bd6e3f50307613bfe8b8f693fc1 Mon Sep 17 00:00:00 2001 From: Angelo Passariello Date: Tue, 9 Dec 2025 11:43:13 +0100 Subject: [PATCH 20/25] Add time-averaged skin friction coefficient - Add option to print time-averaged skin friction coefficient in volume output. --- Common/src/CConfig.cpp | 4 +++- SU2_CFD/src/output/CFlowOutput.cpp | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index 8e8d09bedfc8..60bb5c5e1c8f 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -6520,7 +6520,9 @@ void CConfig::SetOutput(SU2_COMPONENT val_software, unsigned short val_izone) { else cout << "No smoothing applied to source terms in Langevin equations." << endl; if (stochSourceNu) - cout << "Stochastic source term included in turbulence model equation" << endl; + cout << "Stochastic source term included in turbulence model equation." << endl; + else + cout << "Stochastic source term NOT included in turbulence model equation." << endl; } else { cout << "OFF" << endl; } diff --git a/SU2_CFD/src/output/CFlowOutput.cpp b/SU2_CFD/src/output/CFlowOutput.cpp index 106aed24352c..58a53d6b599b 100644 --- a/SU2_CFD/src/output/CFlowOutput.cpp +++ b/SU2_CFD/src/output/CFlowOutput.cpp @@ -4015,6 +4015,11 @@ void CFlowOutput::SetTimeAveragedFields() { AddVolumeOutput("MEAN_VELOCITY-Z", "MeanVelocity_z", "TIME_AVERAGE", "Mean velocity z-component"); AddVolumeOutput("MEAN_PRESSURE", "MeanPressure", "TIME_AVERAGE", "Mean pressure"); + AddVolumeOutput("MEAN_SKIN_FRICTION-X", "MeanSkinFriction_x", "TIME_AVERAGE", "Mean skin friction x-component"); + AddVolumeOutput("MEAN_SKIN_FRICTION-Y", "MeanSkinFriction_y", "TIME_AVERAGE", "Mean skin friction y-component"); + if (nDim==3) + AddVolumeOutput("MEAN_SKIN_FRICTION-Z", "MeanSkinFriction_z", "TIME_AVERAGE", "Mean skin friction z-component"); + AddVolumeOutput("RMS_U", "RMS[u]", "TIME_AVERAGE", "RMS u"); AddVolumeOutput("RMS_V", "RMS[v]", "TIME_AVERAGE", "RMS v"); AddVolumeOutput("RMS_UV", "RMS[uv]", "TIME_AVERAGE", "RMS uv"); @@ -4041,6 +4046,9 @@ void CFlowOutput::LoadTimeAveragedData(unsigned long iPoint, const CVariable *No SetAvgVolumeOutputValue("MEAN_VELOCITY-Z", iPoint, Node_Flow->GetVelocity(iPoint,2)); SetAvgVolumeOutputValue("MEAN_PRESSURE", iPoint, Node_Flow->GetPressure(iPoint)); + SetAvgVolumeOutputValue("MEAN_SKIN_FRICTION-X", iPoint, GetVolumeOutputValue("SKIN_FRICTION-X", iPoint)); + SetAvgVolumeOutputValue("MEAN_SKIN_FRICTION-Y", iPoint, GetVolumeOutputValue("SKIN_FRICTION-Y", iPoint)); + if (nDim == 3) SetAvgVolumeOutputValue("MEAN_SKIN_FRICTION-Z", iPoint, GetVolumeOutputValue("SKIN_FRICTION-Z", iPoint)); SetAvgVolumeOutputValue("RMS_U", iPoint, pow(Node_Flow->GetVelocity(iPoint,0),2)); SetAvgVolumeOutputValue("RMS_V", iPoint, pow(Node_Flow->GetVelocity(iPoint,1),2)); From babfb586e343cea1f9da1fc5fa085f553c8a71d4 Mon Sep 17 00:00:00 2001 From: Angelo Passariello <91474456+AngPass@users.noreply.github.com> Date: Tue, 9 Dec 2025 11:52:51 +0100 Subject: [PATCH 21/25] Minor fix - Minor syntax fix in CConfig.cpp --- Common/src/CConfig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index 60bb5c5e1c8f..68483cef0393 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -6521,7 +6521,7 @@ void CConfig::SetOutput(SU2_COMPONENT val_software, unsigned short val_izone) { cout << "No smoothing applied to source terms in Langevin equations." << endl; if (stochSourceNu) cout << "Stochastic source term included in turbulence model equation." << endl; - else + else cout << "Stochastic source term NOT included in turbulence model equation." << endl; } else { cout << "OFF" << endl; From 562f311cbe57a742aedda65655756b79f6bad34e Mon Sep 17 00:00:00 2001 From: Angelo Passariello <91474456+AngPass@users.noreply.github.com> Date: Tue, 9 Dec 2025 11:55:35 +0100 Subject: [PATCH 22/25] Minor fix - Minor syntax fix in CConfig.cpp --- Common/src/CConfig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index 68483cef0393..511472ff7a6c 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -6521,7 +6521,7 @@ void CConfig::SetOutput(SU2_COMPONENT val_software, unsigned short val_izone) { cout << "No smoothing applied to source terms in Langevin equations." << endl; if (stochSourceNu) cout << "Stochastic source term included in turbulence model equation." << endl; - else + else cout << "Stochastic source term NOT included in turbulence model equation." << endl; } else { cout << "OFF" << endl; From 90ede00b4fb8dbe09792b5b6682ed92a8d07e9be Mon Sep 17 00:00:00 2001 From: Angelo Passariello Date: Mon, 15 Dec 2025 12:01:06 +0100 Subject: [PATCH 23/25] Add fields to VOLUME_OUTPUT - Include time-averaged skin friction coefficient and instantaneous energy backscatter ratio into volume output fields. - Fix boundary conditions for the implicit smoothing of the stochastic source term in Langevin equations. - Fix computation of the source term in turbulence model equation. - Fix computation of the subgrid kinetic energy (divide eddy viscosity by flow density). - Diagonalize Jacobian in turbulence equations for numerical robustness. --- Common/include/toolboxes/random_toolbox.hpp | 85 ++++++------ Common/src/CConfig.cpp | 2 +- SU2_CFD/include/numerics/CNumerics.hpp | 24 ++-- .../numerics/turbulent/turb_sources.hpp | 45 +++--- .../include/solvers/CFVMFlowSolverBase.inl | 7 +- SU2_CFD/src/output/CFlowOutput.cpp | 129 +++++++++++++----- SU2_CFD/src/solvers/CTurbSASolver.cpp | 45 +++--- config_template.cfg | 2 +- 8 files changed, 186 insertions(+), 153 deletions(-) diff --git a/Common/include/toolboxes/random_toolbox.hpp b/Common/include/toolboxes/random_toolbox.hpp index 28eb02676369..aa50baf1e7f6 100644 --- a/Common/include/toolboxes/random_toolbox.hpp +++ b/Common/include/toolboxes/random_toolbox.hpp @@ -91,32 +91,26 @@ inline double GetRandomUniform(std::mt19937 gen, double xmin = 0.0, double xmax * \return Value of Bessel function. */ inline double GetBesselZero(double x) { - double abx = fabs(x); - - if (abx < 3.75) { - double t = x / 3.75; - double p = 1.0 + - t*t*(3.5156229 + - t*t*(3.0899424 + - t*t*(1.2067492 + - t*t*(0.2659732 + - t*t*(0.0360768 + - t*t*0.0045813))))); - return log(p); - } else { - double t = 3.75 / abx; - double poly = - 0.39894228 + - t*(0.01328592 + - t*(0.00225319 + - t*(-0.00157565 + - t*(0.00916281 + - t*(-0.02057706 + - t*(0.02635537 + - t*(-0.01647633 + - t*0.00392377))))))); - return abx - 0.5*log(abx) + log(poly); - } + double abx = fabs(x); + + if (abx < 3.75) { + double t = x / 3.75; + double p = + 1.0 + + t * t * + (3.5156229 + + t * t * (3.0899424 + t * t * (1.2067492 + t * t * (0.2659732 + t * t * (0.0360768 + t * t * 0.0045813))))); + return log(p); + } else { + double t = 3.75 / abx; + double poly = + 0.39894228 + + t * (0.01328592 + + t * (0.00225319 + + t * (-0.00157565 + + t * (0.00916281 + t * (-0.02057706 + t * (0.02635537 + t * (-0.01647633 + t * 0.00392377))))))); + return abx - 0.5 * log(abx) + log(poly); + } } /*! @@ -127,34 +121,33 @@ inline double GetBesselZero(double x) { * \return Value of the integral. */ inline double GetBesselIntegral(double beta_x, double beta_y, double beta_z) { + const double A = 1.0 + 2.0 * (beta_x + beta_y + beta_z); + const double Bx = 2.0 * beta_x; + const double By = 2.0 * beta_y; + const double Bz = 2.0 * beta_z; - const double A = 1.0 + 2.0*(beta_x + beta_y + beta_z); - const double Bx = 2.0*beta_x; - const double By = 2.0*beta_y; - const double Bz = 2.0*beta_z; + const int N = 4000; + const double t_max = 40.0; + const double dt = t_max / N; - const int N = 4000; - const double t_max = 40.0; - const double dt = t_max / N; + double sum = 0.0; - double sum = 0.0; + for (int i = 1; i < N; i++) { + double t = i * dt; - for (int i = 1; i < N; i++) { - double t = i * dt; + double e = exp(-A * t); - double e = exp(-A*t); + double lx = GetBesselZero(Bx * t); + double ly = GetBesselZero(By * t); + double lz = GetBesselZero(Bz * t); - double lx = GetBesselZero(Bx*t); - double ly = GetBesselZero(By*t); - double lz = GetBesselZero(Bz*t); + double lin = log(t) - A * t + lx + ly + lz; - double lin = log(t) - A*t + lx + ly + lz; + double integrand = exp(lin); + sum += integrand; + } - double integrand = exp(lin); - sum += integrand; - } - - return sum * dt; + return sum * dt; } /// @} diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index f6ec93572ded..d34ef9790dae 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -2946,7 +2946,7 @@ void CConfig::SetConfig_Options() { addBoolOption("ENFORCE_LES", enforceLES, false); /* DESCRIPTION: Specify if the stochastic source term must be included in the turbulence model equation */ - addBoolOption("STOCH_SOURCE_NU", stochSourceNu, false); + addBoolOption("STOCH_SOURCE_NU", stochSourceNu, true); /* DESCRIPTION: Filter width for LES (if negative, it is computed based on the local cell size) */ addDoubleOption("LES_FILTER_WIDTH", LES_FilterWidth, -1.0); diff --git a/SU2_CFD/include/numerics/CNumerics.hpp b/SU2_CFD/include/numerics/CNumerics.hpp index 1510f299603e..ba1c7d6dd5e7 100644 --- a/SU2_CFD/include/numerics/CNumerics.hpp +++ b/SU2_CFD/include/numerics/CNumerics.hpp @@ -666,23 +666,15 @@ class CNumerics { /* --- Calculate stochastic tensor --- */ - if (lesSensor > 0.999) { - stochReynStress[1][0] = - Cmag * density * turbKE * rndVec[2]; - stochReynStress[2][0] = Cmag * density * turbKE * rndVec[1]; - stochReynStress[2][1] = - Cmag * density * turbKE * rndVec[0]; - for (size_t iDim = 0; iDim < nDim; iDim++) { - for (size_t jDim = 0; jDim <= iDim; jDim++) { - if (iDim==jDim) { - stochReynStress[iDim][jDim] = 0.0; - } else { - stochReynStress[jDim][iDim] = - stochReynStress[iDim][jDim]; - } - } - } - } else { - for (size_t iDim = 0; iDim < nDim; iDim++) { - for (size_t jDim = 0; jDim < nDim; jDim++) { + stochReynStress[1][0] = - std::nearbyint(lesSensor) * Cmag * density * turbKE * rndVec[2]; + stochReynStress[2][0] = std::nearbyint(lesSensor) * Cmag * density * turbKE * rndVec[1]; + stochReynStress[2][1] = - std::nearbyint(lesSensor) * Cmag * density * turbKE * rndVec[0]; + for (size_t iDim = 0; iDim < nDim; iDim++) { + for (size_t jDim = 0; jDim <= iDim; jDim++) { + if (iDim==jDim) { stochReynStress[iDim][jDim] = 0.0; + } else { + stochReynStress[jDim][iDim] = - stochReynStress[iDim][jDim]; } } } diff --git a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp index 4992ebe60d2e..a377531b72ff 100644 --- a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp +++ b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp @@ -122,18 +122,14 @@ class CSourceBase_TurbSA : public CNumerics { const CSAVariables& var, TIME_MARCHING time_marching) { const su2double& nue = ScalarVar_i[0]; - const auto& density = V_i[idx.Density()]; - const su2double nut_small = 1.0e-10; - - su2double Ji_2 = pow(var.Ji,2); - su2double Ji_3 = Ji_2 * var.Ji; + const su2double nut_small = 1.0e-12; - const su2double nut = nue * var.fv1; + const su2double nut = max(nue * var.fv1, nut_small); - su2double tLES = ct * pow(lengthScale/DES_const, 2) / max(nut, nut_small); - su2double tRANS = pow(lengthScale, 2) / max(nut, nut_small); + su2double tLES = ct * pow(lengthScale/DES_const, 2) / nut; + su2double tRANS = pow(lengthScale, 2) / nut; su2double tLR = tLES / tRANS; su2double tTurb = tLES / (lesMode_i + (1.0-lesMode_i)*tLR); su2double tRat = timeStep / tTurb; @@ -146,7 +142,7 @@ class CSourceBase_TurbSA : public CNumerics { } su2double scaleFactor = 0.0; - if (lesMode_i > 0.999) scaleFactor = 1.0/tTurb * sqrt(2.0/tRat) * density * corrFac; + if (std::nearbyint(lesMode_i) == 1) scaleFactor = 1.0/tTurb * sqrt(2.0/tRat) * density * corrFac; ResidSB[0] = Residual; for (unsigned short iVar = 1; iVar < nVar; iVar++ ) { @@ -157,22 +153,15 @@ class CSourceBase_TurbSA : public CNumerics { for (unsigned short iVar = 0; iVar < nVar; iVar++ ) { for (unsigned short jVar = 0; jVar < nVar; jVar++ ) { JacobianSB_i[iVar][jVar] = 0.0; - if (iVar == jVar) JacobianSB_i[iVar][jVar] = -1.0/tTurb * density * Volume; } } - JacobianSB_i[0][0] = Jacobian_i[0]; - - su2double dnut_dnue = var.fv1 + 3.0 * var.cv1_3 * Ji_3 / pow(Ji_3 + var.cv1_3, 2); - su2double dtTurb_dnut = - ct * pow(lengthScale,2) / (max(nut, nut_small)*max(nut, nut_small)); for (unsigned short iVar = 1; iVar < nVar; iVar++ ) { - JacobianSB_i[iVar][0] = - 1.0/tTurb * scaleFactor * stochSource[iVar-1] - + 1.0/(tTurb*tTurb) * ScalarVar_i[iVar] - + density * corrFac * stochSource[iVar-1] / - (tTurb * sqrt(2.0*tTurb*timeStep)); - JacobianSB_i[iVar][0] *= dtTurb_dnut * dnut_dnue * Volume; + JacobianSB_i[iVar][iVar] = -1.0/tTurb * density * Volume; } + JacobianSB_i[0][0] = Jacobian_i[0]; + } /*! @@ -182,23 +171,21 @@ class CSourceBase_TurbSA : public CNumerics { inline void AddStochSource(const CSAVariables& var, const MatrixType& velGrad, const su2double Cmag) { su2double dist2 = dist_i * dist_i; - const su2double eps = 1.0e-10; + const su2double eps = 1.0e-12; su2double xi3 = pow(var.Ji, 3); su2double factor = dist2 / (2.0 * var.fv1 * ScalarVar_i[0] + eps); factor /= (3.0 * xi3 * var.cv1_3 / pow(xi3 + var.cv1_3, 2) + var.fv1 + eps); - const auto& density = V_i[idx.Density()]; - su2double tke = pow(var.fv1*ScalarVar_i[0]/dist_i, 2); - su2double R12 = Cmag * density * tke * ScalarVar_i[2]; - su2double R13 = - Cmag * density * tke * ScalarVar_i[1]; - su2double R23 = Cmag * density * tke * ScalarVar_i[0]; + su2double R12 = (nDim==3 ? Cmag * tke * ScalarVar_i[3] : 0.0); + su2double R13 = - Cmag * tke * ScalarVar_i[2]; + su2double R23 = Cmag * tke * ScalarVar_i[1]; - su2double RGradU = R12 * (velGrad[0][1] - velGrad[1][0]) + - R13 * (velGrad[0][2] - velGrad[2][0]) + - R23 * (velGrad[1][2] - velGrad[2][1]); + su2double RGradU = R12 * (velGrad[0][1] - velGrad[1][0]) + + (nDim==3 ? R13 * (velGrad[0][2] - velGrad[2][0]) + + R23 * (velGrad[1][2] - velGrad[2][1]) : 0.0); su2double source_k = - RGradU; su2double source_nu = factor * source_k; @@ -379,7 +366,7 @@ class CSourceBase_TurbSA : public CNumerics { /*--- Compute residual for Langevin equations (Stochastic Backscatter Model). ---*/ if (backscatter) { - if (lesMode_i > 0.999 && config->GetStochSourceNu()) + if (std::nearbyint(lesMode_i) == 1 && config->GetStochSourceNu()) AddStochSource(var, PrimVar_Grad_i + idx.Velocity(), config->GetSBS_Cmag()); const su2double DES_const = config->GetConst_DES(); const su2double ctau = config->GetSBS_Ctau(); diff --git a/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl b/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl index b2a42e4c1666..1d7322f0e11c 100644 --- a/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl +++ b/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl @@ -475,10 +475,11 @@ void CFVMFlowSolverBase::Viscous_Residual_impl(unsigned long iEdge, CGeome numerics->SetStochVar(turbNodes->GetSolution(iPoint, 1 + iDim), turbNodes->GetSolution(jPoint, 1 + iDim), iDim); } - su2double eddy_visc_i, eddy_visc_j, DES_length_i, + su2double rho, eddy_visc_i, eddy_visc_j, DES_length_i, DES_length_j, tke_i, tke_j, lesMode_i, lesMode_j; - eddy_visc_i = turbNodes->GetmuT(iPoint); - eddy_visc_j = turbNodes->GetmuT(jPoint); + rho = nodes->GetDensity(iPoint); + eddy_visc_i = turbNodes->GetmuT(iPoint) / rho; + eddy_visc_j = turbNodes->GetmuT(jPoint) / rho; DES_length_i = turbNodes->GetDES_LengthScale(iPoint); DES_length_j = turbNodes->GetDES_LengthScale(jPoint); lesMode_i = turbNodes->GetLES_Mode(iPoint); diff --git a/SU2_CFD/src/output/CFlowOutput.cpp b/SU2_CFD/src/output/CFlowOutput.cpp index 7428dbfaaad3..e53cf67c549f 100644 --- a/SU2_CFD/src/output/CFlowOutput.cpp +++ b/SU2_CFD/src/output/CFlowOutput.cpp @@ -966,11 +966,11 @@ void CFlowOutput::AddHistoryOutputFields_ScalarRMS_RES(const CConfig* config) { AddHistoryOutput("RMS_NU_TILDE", "rms[nu]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of nu tilde (SA model).", HistoryFieldType::RESIDUAL); if (config->GetStochastic_Backscatter()) { /// DESCRIPTION: Root-mean square residual of stochastic vector x-component (Stochastic Backscatter Model). - AddHistoryOutput("RMS_STOCH_VAR_X", "rms[stoch_x]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of stochastic vector x-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); + AddHistoryOutput("RMS_STOCH_VAR-X", "rms[stoch_x]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of stochastic vector x-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); /// DESCRIPTION: Root-mean square residual of stochastic vector y-component (Stochastic Backscatter Model). - AddHistoryOutput("RMS_STOCH_VAR_Y", "rms[stoch_y]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of stochastic vector y-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); + AddHistoryOutput("RMS_STOCH_VAR-Y", "rms[stoch_y]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of stochastic vector y-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); /// DESCRIPTION: Root-mean square residual of stochastic vector z-component (Stochastic Backscatter Model). - if (nDim==3) AddHistoryOutput("RMS_STOCH_VAR_Z", "rms[stoch_z]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of stochastic vector z-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); + if (nDim==3) AddHistoryOutput("RMS_STOCH_VAR-Z", "rms[stoch_z]", ScreenOutputFormat::FIXED, "RMS_RES", "Root-mean square residual of stochastic vector z-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); } break; @@ -1029,11 +1029,11 @@ void CFlowOutput::AddHistoryOutputFields_ScalarMAX_RES(const CConfig* config) { AddHistoryOutput("MAX_NU_TILDE", "max[nu]", ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of nu tilde (SA model).", HistoryFieldType::RESIDUAL); if (config->GetStochastic_Backscatter()) { /// DESCRIPTION: Maximum residual of stochastic vector x-component (Stochastic Backscatter Model). - AddHistoryOutput("MAX_STOCH_VAR_X", "max[stoch_x]", ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of stochastic vector x-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); + AddHistoryOutput("MAX_STOCH_VAR-X", "max[stoch_x]", ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of stochastic vector x-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); /// DESCRIPTION: Maximum residual of stochastic vector y-component (Stochastic Backscatter Model). - AddHistoryOutput("MAX_STOCH_VAR_Y", "max[stoch_y]", ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of stochastic vector y-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); + AddHistoryOutput("MAX_STOCH_VAR-Y", "max[stoch_y]", ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of stochastic vector y-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); /// DESCRIPTION: Maximum residual of stochastic vector z-component (Stochastic Backscatter Model). - if (nDim==3) AddHistoryOutput("MAX_STOCH_VAR_Z", "max[stoch_z]", ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of stochastic vector z-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); + if (nDim==3) AddHistoryOutput("MAX_STOCH_VAR-Z", "max[stoch_z]", ScreenOutputFormat::FIXED, "MAX_RES", "Maximum residual of stochastic vector z-component (Stochastic Backscatter Model).", HistoryFieldType::RESIDUAL); } break; @@ -1177,19 +1177,19 @@ void CFlowOutput::LoadHistoryDataScalar(const CConfig* config, const CSolver* co SetHistoryOutputValue("RMS_NU_TILDE", log10(solver[TURB_SOL]->GetRes_RMS(0))); SetHistoryOutputValue("MAX_NU_TILDE", log10(solver[TURB_SOL]->GetRes_Max(0))); if (config->GetStochastic_Backscatter()) { - SetHistoryOutputValue("RMS_STOCH_VAR_X", log10(solver[TURB_SOL]->GetRes_RMS(1))); - SetHistoryOutputValue("RMS_STOCH_VAR_Y", log10(solver[TURB_SOL]->GetRes_RMS(2))); - if (nDim==3) SetHistoryOutputValue("RMS_STOCH_VAR_Z", log10(solver[TURB_SOL]->GetRes_RMS(3))); - SetHistoryOutputValue("MAX_STOCH_VAR_X", log10(solver[TURB_SOL]->GetRes_Max(1))); - SetHistoryOutputValue("MAX_STOCH_VAR_Y", log10(solver[TURB_SOL]->GetRes_Max(2))); - if (nDim==3) SetHistoryOutputValue("MAX_STOCH_VAR_Z", log10(solver[TURB_SOL]->GetRes_Max(3))); + SetHistoryOutputValue("RMS_STOCH_VAR-X", log10(solver[TURB_SOL]->GetRes_RMS(1))); + SetHistoryOutputValue("RMS_STOCH_VAR-Y", log10(solver[TURB_SOL]->GetRes_RMS(2))); + if (nDim==3) SetHistoryOutputValue("RMS_STOCH_VAR-Z", log10(solver[TURB_SOL]->GetRes_RMS(3))); + SetHistoryOutputValue("MAX_STOCH_VAR-X", log10(solver[TURB_SOL]->GetRes_Max(1))); + SetHistoryOutputValue("MAX_STOCH_VAR-Y", log10(solver[TURB_SOL]->GetRes_Max(2))); + if (nDim==3) SetHistoryOutputValue("MAX_STOCH_VAR-Z", log10(solver[TURB_SOL]->GetRes_Max(3))); } if (multiZone) { SetHistoryOutputValue("BGS_NU_TILDE", log10(solver[TURB_SOL]->GetRes_BGS(0))); if (config->GetStochastic_Backscatter()) { - SetHistoryOutputValue("BGS_STOCH_VAR_X", log10(solver[TURB_SOL]->GetRes_BGS(1))); - SetHistoryOutputValue("BGS_STOCH_VAR_Y", log10(solver[TURB_SOL]->GetRes_BGS(2))); - if (nDim==3) SetHistoryOutputValue("BGS_STOCH_VAR_Z", log10(solver[TURB_SOL]->GetRes_BGS(3))); + SetHistoryOutputValue("BGS_STOCH_VAR-X", log10(solver[TURB_SOL]->GetRes_BGS(1))); + SetHistoryOutputValue("BGS_STOCH_VAR-Y", log10(solver[TURB_SOL]->GetRes_BGS(2))); + if (nDim==3) SetHistoryOutputValue("BGS_STOCH_VAR-Z", log10(solver[TURB_SOL]->GetRes_BGS(3))); } } break; @@ -1514,12 +1514,13 @@ void CFlowOutput::SetVolumeOutputFieldsScalarMisc(const CConfig* config) { AddVolumeOutput("WALL_DISTANCE", "Wall_Distance", "DDES", "Wall distance value"); AddVolumeOutput("LES_SENSOR","LES_Sensor","DDES","LES sensor value"); if (config->GetStochastic_Backscatter()) { - AddVolumeOutput("STOCHASTIC_VAR_X", "Stochastic_Var_X", "BACKSCATTER", "x-component of the stochastic vector potential"); - AddVolumeOutput("STOCHASTIC_VAR_Y", "Stochastic_Var_Y", "BACKSCATTER", "y-component of the stochastic vector potential"); - if (nDim==3) AddVolumeOutput("STOCHASTIC_VAR_Z", "Stochastic_Var_Z", "BACKSCATTER", "z-component of the stochastic vector potential"); - AddVolumeOutput("STOCHASTIC_SOURCE_X", "Stochastic_Source_X", "BACKSCATTER", "x-component of the stochastic source vector"); - AddVolumeOutput("STOCHASTIC_SOURCE_Y", "Stochastic_Source_Y", "BACKSCATTER", "y-component of the stochastic source vector"); - if (nDim==3) AddVolumeOutput("STOCHASTIC_SOURCE_Z", "Stochastic_Source_Z", "BACKSCATTER", "z-component of the stochastic source vector"); + AddVolumeOutput("STOCHVAR_X", "StochVar_x", "BACKSCATTER", "x-component of the stochastic vector potential"); + AddVolumeOutput("STOCHVAR_Y", "StochVar_y", "BACKSCATTER", "y-component of the stochastic vector potential"); + if (nDim==3) AddVolumeOutput("STOCHVAR_Z", "StochVar_z", "BACKSCATTER", "z-component of the stochastic vector potential"); + AddVolumeOutput("STOCHSOURCE_X", "StochSource_x", "BACKSCATTER", "x-component of the stochastic source vector"); + AddVolumeOutput("STOCHSOURCE_Y", "StochSource_y", "BACKSCATTER", "y-component of the stochastic source vector"); + if (nDim==3) AddVolumeOutput("STOCHSOURCE_Z", "StochSource_z", "BACKSCATTER", "z-component of the stochastic source vector"); + AddVolumeOutput("ENERGY_BACKSCATTER_RATIO", "Energy_Backscatter_Ratio", "BACKSCATTER", "Energy backscatter from unresolved to resolved scales (divided by the turbulent dissipation of resolved kinetic energy)"); } } @@ -1622,12 +1623,66 @@ void CFlowOutput::LoadVolumeDataScalar(const CConfig* config, const CSolver* con SetVolumeOutputValue("WALL_DISTANCE", iPoint, Node_Geo->GetWall_Distance(iPoint)); SetVolumeOutputValue("LES_SENSOR", iPoint, Node_Flow->GetLES_Mode(iPoint)); if (config->GetStochastic_Backscatter()) { - SetVolumeOutputValue("STOCHASTIC_VAR_X", iPoint, Node_Turb->GetSolution(iPoint, 1)); - SetVolumeOutputValue("STOCHASTIC_VAR_Y", iPoint, Node_Turb->GetSolution(iPoint, 2)); - if (nDim==3) SetVolumeOutputValue("STOCHASTIC_VAR_Z", iPoint, Node_Turb->GetSolution(iPoint, 3)); - SetVolumeOutputValue("STOCHASTIC_SOURCE_X", iPoint, Node_Turb->GetLangevinSourceTerms(iPoint, 0)); - SetVolumeOutputValue("STOCHASTIC_SOURCE_Y", iPoint, Node_Turb->GetLangevinSourceTerms(iPoint, 1)); - if (nDim==3) SetVolumeOutputValue("STOCHASTIC_SOURCE_Z", iPoint, Node_Turb->GetLangevinSourceTerms(iPoint, 2)); + SetVolumeOutputValue("STOCHVAR_X", iPoint, Node_Turb->GetSolution(iPoint, 1)); + SetVolumeOutputValue("STOCHVAR_Y", iPoint, Node_Turb->GetSolution(iPoint, 2)); + if (nDim==3) SetVolumeOutputValue("STOCHVAR_Z", iPoint, Node_Turb->GetSolution(iPoint, 3)); + SetVolumeOutputValue("STOCHSOURCE_X", iPoint, Node_Turb->GetLangevinSourceTerms(iPoint, 0)); + SetVolumeOutputValue("STOCHSOURCE_Y", iPoint, Node_Turb->GetLangevinSourceTerms(iPoint, 1)); + if (nDim==3) SetVolumeOutputValue("STOCHSOURCE_Z", iPoint, Node_Turb->GetLangevinSourceTerms(iPoint, 2)); + const su2double rho = Node_Flow->GetDensity(iPoint); + const su2double mu_t = Node_Flow->GetEddyViscosity(iPoint) / rho; + const su2double DES_lengthscale = max(Node_Flow->GetDES_LengthScale(iPoint), 1.0E-10); + const su2double lesSensor = Node_Flow->GetLES_Mode(iPoint); + const su2double mag = config->GetSBS_Cmag(); + const su2double tke_estim = pow(mu_t/DES_lengthscale, 2.0); + const su2double csi_x = Node_Turb->GetSolution(iPoint, 1); + const su2double csi_y = Node_Turb->GetSolution(iPoint, 2); + const su2double csi_z = (nDim==3) ? Node_Turb->GetSolution(iPoint, 3) : 0.0; + const su2double R_xy = mag * tke_estim * csi_z * std::nearbyint(lesSensor); + const su2double R_xz = - mag * tke_estim * csi_y * std::nearbyint(lesSensor); + const su2double R_yz = mag * tke_estim * csi_x * std::nearbyint(lesSensor); + const auto vel_grad = Node_Flow->GetVelocityGradient(iPoint); + const su2double vel_div = vel_grad(0,0) + vel_grad(1,1) + (nDim ==3 ? vel_grad(2,2) : 0.0); + const su2double tau_xx = mu_t/rho * (2*vel_grad(0,0) - (2.0/3.0)*vel_div); + const su2double tau_yy = mu_t/rho * (2*vel_grad(1,1) - (2.0/3.0)*vel_div); + const su2double tau_xy = mu_t/rho * (vel_grad(0,1) + vel_grad(1,0)); + su2double tau_zz=0.0, tau_xz=0.0, tau_yz=0.0; + if (nDim == 3){ + tau_zz = mu_t/rho * (2*vel_grad(2,2) - (2.0/3.0)*vel_div); + tau_xz = mu_t/rho * (vel_grad(0,2) + vel_grad(2,0)); + tau_yz = mu_t/rho * (vel_grad(1,2) + vel_grad(2,1)); + } + const su2double energy_res_to_mod = tau_xx * vel_grad(0,0) + tau_yy * vel_grad(1,1) + + (nDim==3 ? tau_zz * vel_grad(2,2) : 0.0) + + tau_xy * (vel_grad(0,1) + vel_grad(1,0)) + + (nDim==3 ? tau_xz * (vel_grad(0,2) + vel_grad(2,0)) + + tau_yz * (vel_grad(1,2) + vel_grad(2,1)) : 0.0); + const su2double energy_backscatter = R_xy * (vel_grad(0,1) - vel_grad(1,0)) + + (nDim==3 ? R_xz * (vel_grad(0,2) - vel_grad(2,0)) + + R_yz * (vel_grad(1,2) - vel_grad(2,1)) : 0.0); + const su2double energy_backscatter_ratio = energy_backscatter / (energy_res_to_mod + 1e-12); + SetVolumeOutputValue("ENERGY_BACKSCATTER_RATIO", iPoint, energy_backscatter_ratio); + } + } + + if (config->GetTime_Domain()) { + const su2double mu_t = Node_Flow->GetEddyViscosity(iPoint); + const su2double rho = Node_Flow->GetDensity(iPoint); + const auto vel_grad = Node_Flow->GetVelocityGradient(iPoint); + const su2double vel_div = vel_grad(0,0) + vel_grad(1,1) + (nDim ==3 ? vel_grad(2,2) : 0.0); + const su2double tau_xx = mu_t/rho * (2*vel_grad(0,0) - (2.0/3.0)*vel_div); + const su2double tau_yy = mu_t/rho * (2*vel_grad(1,1) - (2.0/3.0)*vel_div); + const su2double tau_xy = mu_t/rho * (vel_grad(0,1) + vel_grad(1,0)); + SetAvgVolumeOutputValue("MODELED_REYNOLDS_STRESS_11", iPoint, -tau_xx); + SetAvgVolumeOutputValue("MODELED_REYNOLDS_STRESS_22", iPoint, -tau_yy); + SetAvgVolumeOutputValue("MODELED_REYNOLDS_STRESS_12", iPoint, -tau_xy); + if (nDim == 3){ + const su2double tau_zz = mu_t/rho * (2*vel_grad(2,2) - (2.0/3.0)*vel_div); + const su2double tau_xz = mu_t/rho * (vel_grad(0,2) + vel_grad(2,0)); + const su2double tau_yz = mu_t/rho * (vel_grad(1,2) + vel_grad(2,1)); + SetAvgVolumeOutputValue("MODELED_REYNOLDS_STRESS_33", iPoint, -tau_zz); + SetAvgVolumeOutputValue("MODELED_REYNOLDS_STRESS_13", iPoint, -tau_xz); + SetAvgVolumeOutputValue("MODELED_REYNOLDS_STRESS_23", iPoint, -tau_yz); } } @@ -1708,6 +1763,13 @@ void CFlowOutput::LoadSurfaceData(CConfig *config, CGeometry *geometry, CSolver SetVolumeOutputValue("SKIN_FRICTION-Z", iPoint, solver[FLOW_SOL]->GetCSkinFriction(iMarker, iVertex, 2)); SetVolumeOutputValue("HEAT_FLUX", iPoint, solver[heat_sol]->GetHeatFlux(iMarker, iVertex)); SetVolumeOutputValue("Y_PLUS", iPoint, solver[FLOW_SOL]->GetYPlus(iMarker, iVertex)); + + if (config->GetTime_Domain()) { + SetAvgVolumeOutputValue("MEAN_SKIN_FRICTION-X", iPoint, solver[FLOW_SOL]->GetCSkinFriction(iMarker, iVertex, 0)); + SetAvgVolumeOutputValue("MEAN_SKIN_FRICTION-Y", iPoint, solver[FLOW_SOL]->GetCSkinFriction(iMarker, iVertex, 1)); + if (nDim == 3) + SetAvgVolumeOutputValue("MEAN_SKIN_FRICTION-Z", iPoint, solver[FLOW_SOL]->GetCSkinFriction(iMarker, iVertex, 2)); + } } void CFlowOutput::AddAerodynamicCoefficients(const CConfig* config) { @@ -4036,6 +4098,15 @@ void CFlowOutput::SetTimeAveragedFields() { AddVolumeOutput("UWPRIME", "w'u'", "TIME_AVERAGE", "Mean Reynolds-stress component w'u'"); AddVolumeOutput("VWPRIME", "w'v'", "TIME_AVERAGE", "Mean Reynolds-stress component w'v'"); } + + AddVolumeOutput("MODELED_REYNOLDS_STRESS_11", "ModeledReynoldsStress_11", "TIME_AVERAGE", "Modeled Reynolds stress xx-component"); + AddVolumeOutput("MODELED_REYNOLDS_STRESS_22", "ModeledReynoldsStress_22", "TIME_AVERAGE", "Modeled Reynolds stress yy-component"); + AddVolumeOutput("MODELED_REYNOLDS_STRESS_12", "ModeledReynoldsStress_12", "TIME_AVERAGE", "Modeled Reynolds stress xy-component"); + if (nDim == 3){ + AddVolumeOutput("MODELED_REYNOLDS_STRESS_33", "ModeledReynoldsStress_33", "TIME_AVERAGE", "Modeled Reynolds stress zz-component"); + AddVolumeOutput("MODELED_REYNOLDS_STRESS_13", "ModeledReynoldsStress_13", "TIME_AVERAGE", "Modeled Reynolds stress xz-component"); + AddVolumeOutput("MODELED_REYNOLDS_STRESS_23", "ModeledReynoldsStress_23", "TIME_AVERAGE", "Modeled Reynolds stress yz-component"); + } } void CFlowOutput::LoadTimeAveragedData(unsigned long iPoint, const CVariable *Node_Flow){ @@ -4046,10 +4117,6 @@ void CFlowOutput::LoadTimeAveragedData(unsigned long iPoint, const CVariable *No SetAvgVolumeOutputValue("MEAN_VELOCITY-Z", iPoint, Node_Flow->GetVelocity(iPoint,2)); SetAvgVolumeOutputValue("MEAN_PRESSURE", iPoint, Node_Flow->GetPressure(iPoint)); - SetAvgVolumeOutputValue("MEAN_SKIN_FRICTION-X", iPoint, GetVolumeOutputValue("SKIN_FRICTION-X", iPoint)); - SetAvgVolumeOutputValue("MEAN_SKIN_FRICTION-Y", iPoint, GetVolumeOutputValue("SKIN_FRICTION-Y", iPoint)); - if (nDim == 3) SetAvgVolumeOutputValue("MEAN_SKIN_FRICTION-Z", iPoint, GetVolumeOutputValue("SKIN_FRICTION-Z", iPoint)); - SetAvgVolumeOutputValue("RMS_U", iPoint, pow(Node_Flow->GetVelocity(iPoint,0),2)); SetAvgVolumeOutputValue("RMS_V", iPoint, pow(Node_Flow->GetVelocity(iPoint,1),2)); SetAvgVolumeOutputValue("RMS_UV", iPoint, Node_Flow->GetVelocity(iPoint,0) * Node_Flow->GetVelocity(iPoint,1)); diff --git a/SU2_CFD/src/solvers/CTurbSASolver.cpp b/SU2_CFD/src/solvers/CTurbSASolver.cpp index 02132c345aa0..07585c5b222c 100644 --- a/SU2_CFD/src/solvers/CTurbSASolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp @@ -1648,8 +1648,7 @@ void CTurbSASolver::SetLangevinSourceTerms(CConfig *config, CGeometry* geometry) for (auto iDim = 0u; iDim < nDim; iDim++){ auto gen = nodes->GetLangevinGen(iPoint, iDim); su2double lesSensor = nodes->GetLES_Mode(iPoint); - su2double rnd = 0.0; - if (lesSensor > 0.999) rnd = RandomToolbox::GetRandomNormal(gen); + su2double rnd = RandomToolbox::GetRandomNormal(gen) * std::nearbyint(lesSensor); nodes->SetLangevinSourceTermsOld(iPoint, iDim, rnd); nodes->SetLangevinSourceTerms(iPoint, iDim, rnd); } @@ -1678,29 +1677,15 @@ void CTurbSASolver::SetLangevinGen(CConfig* config, CGeometry* geometry) { void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geometry) { const su2double LES_FilterWidth = config->GetLES_FilterWidth(); + const su2double constDES = config->GetConst_DES(); const su2double cDelta = config->GetSBS_Cdelta(); const unsigned short maxIter = config->GetSBS_maxIterSmooth(); - const unsigned long global_nPointDomain = geometry->GetGlobal_nPointDomain(); const su2double tol = -5.0; const su2double sourceLim = 3.0; const su2double omega = 0.8; unsigned long timeIter = config->GetTimeIter(); unsigned long restartIter = config->GetRestart_Iter(); - /*--- Set the stochastic source terms to zero at boundaries. ---*/ - - for (unsigned short iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { - for (unsigned long iVertex = 0; iVertex < geometry->nVertex[iMarker]; iVertex++ ) { - unsigned long iPoint = geometry->vertex[iMarker][iVertex]->GetNode(); - if (geometry->nodes->GetDomain(iPoint)) { - for (unsigned short iDim = 0; iDim < nDim; iDim++) { - nodes->SetLangevinSourceTerms(iPoint, iDim, 0.0); - nodes->SetLangevinSourceTermsOld(iPoint, iDim, 0.0); - } - } - } - } - /*--- Start SOR algorithm for the Laplacian smoothing. ---*/ for (unsigned short iDim = 0; iDim < nDim; iDim++) { @@ -1713,11 +1698,15 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet CompleteComms(geometry, config, MPI_QUANTITIES::STOCH_SOURCE_LANG); su2double localResNorm = 0.0; + unsigned long local_nPointLES = 0; SU2_OMP_FOR_DYN(omp_chunk_size) for (unsigned long iPoint = 0; iPoint < nPointDomain; iPoint++) { - - su2double maxDelta = geometry->nodes->GetMaxLength(iPoint); + const su2double lesSensor = nodes->GetLES_Mode(iPoint); + if (std::nearbyint(lesSensor) == 0.0) continue; + local_nPointLES += 1; + const su2double DES_LengthScale = nodes->GetDES_LengthScale(iPoint); + su2double maxDelta = DES_LengthScale / constDES; if (LES_FilterWidth > 0.0) maxDelta = LES_FilterWidth; su2double b = sqrt(cDelta) * maxDelta; su2double b2 = b * b; @@ -1756,8 +1745,10 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet /*--- Stop integration if residual drops below tolerance. ---*/ su2double globalResNorm = 0.0; + unsigned long global_nPointLES = 0; + SU2_MPI::Allreduce(&local_nPointLES, &global_nPointLES, 1, MPI_INT, MPI_SUM, SU2_MPI::GetComm()); SU2_MPI::Allreduce(&localResNorm, &globalResNorm, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); - globalResNorm = sqrt(globalResNorm / global_nPointDomain); + globalResNorm = sqrt(globalResNorm / global_nPointLES); if (rank == MASTER_NODE) { if (iter == 0) { @@ -1796,6 +1787,8 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet su2double var_check_notSmoothed = 0.0; su2double mean_check_notSmoothed = 0.0; for (unsigned long iPoint = 0; iPoint < nPointDomain; iPoint++) { + const su2double lesSensor = nodes->GetLES_Mode(iPoint); + if (std::nearbyint(lesSensor) == 0.0) continue; su2double source = nodes->GetLangevinSourceTerms(iPoint, iDim); su2double source_notSmoothed = nodes->GetLangevinSourceTermsOld(iPoint, iDim); mean_check_old += source; @@ -1855,14 +1848,14 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet SU2_MPI::Allreduce(&var_check_old, &var_check_old_G, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); SU2_MPI::Allreduce(&var_check_new, &var_check_new_G, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); SU2_MPI::Allreduce(&var_check_notSmoothed, &var_check_notSmoothed_G, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); - mean_check_old_G /= global_nPointDomain; - var_check_old_G /= global_nPointDomain; + mean_check_old_G /= global_nPointLES; + var_check_old_G /= global_nPointLES; var_check_old_G -= mean_check_old_G * mean_check_old_G; - mean_check_new_G /= global_nPointDomain; - var_check_new_G /= global_nPointDomain; + mean_check_new_G /= global_nPointLES; + var_check_new_G /= global_nPointLES; var_check_new_G -= mean_check_new_G * mean_check_new_G; - mean_check_notSmoothed_G /= global_nPointDomain; - var_check_notSmoothed_G /= global_nPointDomain; + mean_check_notSmoothed_G /= global_nPointLES; + var_check_notSmoothed_G /= global_nPointLES; var_check_notSmoothed_G -= mean_check_notSmoothed_G * mean_check_notSmoothed_G; if (rank == MASTER_NODE) { cout << "Mean of stochastic source term before scaling: " << mean_check_old_G-mean_check_notSmoothed_G <<". After scaling: " << mean_check_new_G-mean_check_notSmoothed_G << "." << endl; diff --git a/config_template.cfg b/config_template.cfg index 2abd0d076242..f70c736d8978 100644 --- a/config_template.cfg +++ b/config_template.cfg @@ -200,7 +200,7 @@ SBS_TIMESCALE_COEFF= 0.05 SBS_INTENSITY_COEFF= 1.0 % % Include stochastic source in turbulence model equation (NO, YES) -STOCH_SOURCE_NU= NO +STOCH_SOURCE_NU= YES % % Enforce LES mode in Hybrid RANS-LES Simulations (NO, YES) ENFORCE_LES= NO From a5b3aa0cfd16b188230650084004a794bb2ed402 Mon Sep 17 00:00:00 2001 From: paan882 Date: Tue, 16 Dec 2025 11:56:48 +0100 Subject: [PATCH 24/25] Fix LD2 option in config file - Allow LD2 option only for JST scheme. --- Common/src/CConfig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index d34ef9790dae..eaf8e93f109b 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -7108,7 +7108,7 @@ void CConfig::SetOutput(SU2_COMPONENT val_software, unsigned short val_izone) { } } - if (Kind_ConvNumScheme_Flow != SPACE_CENTERED || (Kind_ConvNumScheme_Flow == SPACE_CENTERED && Kind_Centered_Flow == CENTERED::LAX)) { + if ((Kind_ConvNumScheme_Flow != SPACE_CENTERED || (Kind_ConvNumScheme_Flow == SPACE_CENTERED && Kind_Centered_Flow == CENTERED::LAX)) && LD2_Scheme) { { SU2_MPI::Error("LD2 option available for JST scheme only.", CURRENT_FUNCTION); } From 0df34ac5e4197ffe5ca341555f1728076fb30e2a Mon Sep 17 00:00:00 2001 From: paan882 Date: Fri, 2 Jan 2026 12:30:32 +0100 Subject: [PATCH 25/25] Enhance numerical robustness - Add option to include diagnostics of the stochastic source term smoothing. - Add relaxation factor for the random forcing term in the main balanca equations. - Add modeled and stochastic Reynolds stresses to volume output files. --- Common/include/CConfig.hpp | 14 +++ Common/src/CConfig.cpp | 12 +- .../numerics/turbulent/turb_sources.hpp | 19 ++- SU2_CFD/src/numerics/flow/flow_diffusion.cpp | 42 ++++++- SU2_CFD/src/output/CFlowOutput.cpp | 49 +++++--- SU2_CFD/src/solvers/CTurbSASolver.cpp | 108 +++++++++--------- config_template.cfg | 6 + 7 files changed, 176 insertions(+), 74 deletions(-) diff --git a/Common/include/CConfig.hpp b/Common/include/CConfig.hpp index 6609268343ce..abb11731a7fa 100644 --- a/Common/include/CConfig.hpp +++ b/Common/include/CConfig.hpp @@ -1103,6 +1103,8 @@ class CConfig { su2double SBS_Ctau; /*!< \brief Stochastic Backscatter Model timescale coefficient. */ su2double SBS_Cmag; /*!< \brief Stochastic Backscatter Model intensity coefficient. */ bool stochSourceNu; /*!< \brief Option for including stochastic source term in turbulence model equation (Stochastic Backscatter Model). */ + bool stochSourceDiagnostics; /*!< \brief Option for writing diagnostics related to stochastic source terms in Langevin equations (Stochastic Backscatter Model). */ + su2double stochSourceRelax; /*!< \brief Relaxation factor for stochastic source term generation (Stochastic Backscatter Model). */ bool enforceLES; /*!< \brief Option to enforce LES mode in hybrid RANS-LES simulations. */ su2double LES_FilterWidth; /*!< \brief LES filter width for hybrid RANS-LES simulations. */ bool LD2_Scheme; /*!< \brief Use the LD2 scheme (incompressible flows, combined with JST discretization). */ @@ -9590,6 +9592,18 @@ class CConfig { */ bool GetStochSourceNu(void) const { return stochSourceNu; } + /*! + * \brief Get if the diagnostics of the stochastic source terms in Langevin equations must be computed. + * \return TRUE if the diagnostics of the stochastic source terms in Langevin equations are computed. + */ + bool GetStochSourceDiagnostics(void) const { return stochSourceDiagnostics; } + + /*! + * \brief Get the relaxation factor for stochastic source term generation. + * \return Relaxation factor for stochastic source term generation. + */ + su2double GetStochSourceRelax(void) const { return stochSourceRelax; } + /*! * \brief Get the LES Filter Width. * \return Value of LES Filter Width. diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index 615b906f03f2..c6a481098310 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -2968,6 +2968,12 @@ void CConfig::SetConfig_Options() { /* DESCRIPTION: Specify if the stochastic source term must be included in the turbulence model equation */ addBoolOption("STOCH_SOURCE_NU", stochSourceNu, true); + /* DESCRIPTION: Enable diagnostics of the stochastic source term in Langevin equations. */ + addBoolOption("STOCH_SOURCE_DIAGNOSTICS", stochSourceDiagnostics, false); + + /* DESCRIPTION: Relaxation factor for the stochastic source term (Stochastic Backscatter Model). */ + addDoubleOption("SBS_RELAXATION_FACTOR", stochSourceRelax, 0.0); + /* DESCRIPTION: Filter width for LES (if negative, it is computed based on the local cell size) */ addDoubleOption("LES_FILTER_WIDTH", LES_FilterWidth, -1.0); @@ -6548,6 +6554,10 @@ void CConfig::SetOutput(SU2_COMPONENT val_software, unsigned short val_izone) { cout << "Stochastic source term included in turbulence model equation." << endl; else cout << "Stochastic source term NOT included in turbulence model equation." << endl; + if (stochSourceRelax > 0.0) + cout << "Relaxation factor for stochastic source term: " << stochSourceRelax << endl; + else + cout << "No relaxation factor for stochastic source term." << endl; } else { cout << "OFF" << endl; } @@ -7130,7 +7140,7 @@ void CConfig::SetOutput(SU2_COMPONENT val_software, unsigned short val_izone) { } } - if ((Kind_ConvNumScheme_Flow != SPACE_CENTERED || (Kind_ConvNumScheme_Flow == SPACE_CENTERED && Kind_Centered_Flow == CENTERED::LAX)) && LD2_Scheme) { { + if ((Kind_ConvNumScheme_Flow != SPACE_CENTERED || (Kind_ConvNumScheme_Flow == SPACE_CENTERED && Kind_Centered_Flow == CENTERED::LAX)) && LD2_Scheme) { SU2_MPI::Error("LD2 option available for JST scheme only.", CURRENT_FUNCTION); } diff --git a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp index a377531b72ff..f9cc15a146c1 100644 --- a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp +++ b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp @@ -366,8 +366,23 @@ class CSourceBase_TurbSA : public CNumerics { /*--- Compute residual for Langevin equations (Stochastic Backscatter Model). ---*/ if (backscatter) { - if (std::nearbyint(lesMode_i) == 1 && config->GetStochSourceNu()) - AddStochSource(var, PrimVar_Grad_i + idx.Velocity(), config->GetSBS_Cmag()); + if (std::nearbyint(lesMode_i) == 1 && config->GetStochSourceNu()) { + su2double SBS_Cmag = config->GetSBS_Cmag(); + su2double lesSensor = max(lesMode_i, lesMode_j); + su2double SBS_RelaxFactor = config->GetStochSourceRelax(); + su2double intensityCoeff = SBS_Cmag; + if (SBS_RelaxFactor > 0.0) { + su2double FS_Vel = config->GetModVel_FreeStream(); + su2double ReynoldsLength = config->GetLength_Reynolds(); + su2double timeScale = ReynoldsLength / FS_Vel; + unsigned long timeIter = config->GetTimeIter(); + unsigned long restartIter = config->GetRestart_Iter(); + su2double timeStep = config->GetTime_Step(); + su2double currentTime = (timeIter - restartIter) * timeStep; + intensityCoeff = SBS_Cmag * (1.0 - exp(-SBS_RelaxFactor * currentTime / timeScale)); + } + AddStochSource(var, PrimVar_Grad_i + idx.Velocity(), intensityCoeff); + } const su2double DES_const = config->GetConst_DES(); const su2double ctau = config->GetSBS_Ctau(); ResidualStochEquations(config->GetDelta_UnstTime(), ctau, dist_i, DES_const, diff --git a/SU2_CFD/src/numerics/flow/flow_diffusion.cpp b/SU2_CFD/src/numerics/flow/flow_diffusion.cpp index d5b224801c73..902b8a6f0f06 100644 --- a/SU2_CFD/src/numerics/flow/flow_diffusion.cpp +++ b/SU2_CFD/src/numerics/flow/flow_diffusion.cpp @@ -462,8 +462,20 @@ CNumerics::ResidualType<> CAvgGrad_Flow::ComputeResidual(const CConfig* config) Mean_StochVar[iVar] = 0.5*(stochVar_i[iVar] + stochVar_j[iVar]); su2double SBS_Cmag = config->GetSBS_Cmag(); su2double lesSensor = max(lesMode_i, lesMode_j); + su2double SBS_RelaxFactor = config->GetStochSourceRelax(); + su2double intensityCoeff = SBS_Cmag; + if (SBS_RelaxFactor > 0.0) { + su2double FS_Vel = config->GetModVel_FreeStream(); + su2double ReynoldsLength = config->GetLength_Reynolds(); + su2double timeScale = ReynoldsLength / FS_Vel; + unsigned long timeIter = config->GetTimeIter(); + unsigned long restartIter = config->GetRestart_Iter(); + su2double timeStep = config->GetTime_Step(); + su2double currentTime = (timeIter - restartIter) * timeStep; + intensityCoeff = SBS_Cmag * (1.0 - exp(-SBS_RelaxFactor * currentTime / timeScale)); + } ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_Eddy_Viscosity, Mean_turb_ke, - Mean_StochVar, lesSensor, stochReynStress, SBS_Cmag); + Mean_StochVar, lesSensor, stochReynStress, intensityCoeff); } /*--- Get projected flux tensor (viscous residual) ---*/ @@ -647,8 +659,20 @@ CNumerics::ResidualType<> CAvgGradInc_Flow::ComputeResidual(const CConfig* confi Mean_StochVar[iVar] = 0.5*(stochVar_i[iVar] + stochVar_j[iVar]); su2double SBS_Cmag = config->GetSBS_Cmag(); su2double lesSensor = max(lesMode_i, lesMode_j); + su2double SBS_RelaxFactor = config->GetStochSourceRelax(); + su2double intensityCoeff = SBS_Cmag; + if (SBS_RelaxFactor > 0.0) { + su2double FS_Vel = config->GetModVel_FreeStream(); + su2double ReynoldsLength = config->GetLength_Reynolds(); + su2double timeScale = ReynoldsLength / FS_Vel; + unsigned long timeIter = config->GetTimeIter(); + unsigned long restartIter = config->GetRestart_Iter(); + su2double timeStep = config->GetTime_Step(); + su2double currentTime = (timeIter - restartIter) * timeStep; + intensityCoeff = SBS_Cmag * (1.0 - exp(-SBS_RelaxFactor * currentTime / timeScale)); + } ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_Eddy_Viscosity, Mean_turb_ke, - Mean_StochVar, lesSensor, stochReynStress, SBS_Cmag); + Mean_StochVar, lesSensor, stochReynStress, intensityCoeff); } /*--- Get projected flux tensor (viscous residual) ---*/ @@ -984,8 +1008,20 @@ CNumerics::ResidualType<> CGeneralAvgGrad_Flow::ComputeResidual(const CConfig* c Mean_StochVar[iVar] = 0.5*(stochVar_i[iVar] + stochVar_j[iVar]); su2double SBS_Cmag = config->GetSBS_Cmag(); su2double lesSensor = max(lesMode_i, lesMode_j); + su2double SBS_RelaxFactor = config->GetStochSourceRelax(); + su2double intensityCoeff = SBS_Cmag; + if (SBS_RelaxFactor > 0.0) { + su2double FS_Vel = config->GetModVel_FreeStream(); + su2double ReynoldsLength = config->GetLength_Reynolds(); + su2double timeScale = ReynoldsLength / FS_Vel; + unsigned long timeIter = config->GetTimeIter(); + unsigned long restartIter = config->GetRestart_Iter(); + su2double timeStep = config->GetTime_Step(); + su2double currentTime = (timeIter - restartIter) * timeStep; + intensityCoeff = SBS_Cmag * (1.0 - exp(-SBS_RelaxFactor * currentTime / timeScale)); + } ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_Eddy_Viscosity, Mean_turb_ke, - Mean_StochVar, lesSensor, stochReynStress, SBS_Cmag); + Mean_StochVar, lesSensor, stochReynStress, intensityCoeff); } /*--- Get projected flux tensor (viscous residual) ---*/ diff --git a/SU2_CFD/src/output/CFlowOutput.cpp b/SU2_CFD/src/output/CFlowOutput.cpp index e53cf67c549f..44338b645a58 100644 --- a/SU2_CFD/src/output/CFlowOutput.cpp +++ b/SU2_CFD/src/output/CFlowOutput.cpp @@ -1630,11 +1630,11 @@ void CFlowOutput::LoadVolumeDataScalar(const CConfig* config, const CSolver* con SetVolumeOutputValue("STOCHSOURCE_Y", iPoint, Node_Turb->GetLangevinSourceTerms(iPoint, 1)); if (nDim==3) SetVolumeOutputValue("STOCHSOURCE_Z", iPoint, Node_Turb->GetLangevinSourceTerms(iPoint, 2)); const su2double rho = Node_Flow->GetDensity(iPoint); - const su2double mu_t = Node_Flow->GetEddyViscosity(iPoint) / rho; + const su2double nu_t = Node_Flow->GetEddyViscosity(iPoint) / rho; const su2double DES_lengthscale = max(Node_Flow->GetDES_LengthScale(iPoint), 1.0E-10); const su2double lesSensor = Node_Flow->GetLES_Mode(iPoint); const su2double mag = config->GetSBS_Cmag(); - const su2double tke_estim = pow(mu_t/DES_lengthscale, 2.0); + const su2double tke_estim = pow(nu_t/DES_lengthscale, 2.0); const su2double csi_x = Node_Turb->GetSolution(iPoint, 1); const su2double csi_y = Node_Turb->GetSolution(iPoint, 2); const su2double csi_z = (nDim==3) ? Node_Turb->GetSolution(iPoint, 3) : 0.0; @@ -1643,14 +1643,14 @@ void CFlowOutput::LoadVolumeDataScalar(const CConfig* config, const CSolver* con const su2double R_yz = mag * tke_estim * csi_x * std::nearbyint(lesSensor); const auto vel_grad = Node_Flow->GetVelocityGradient(iPoint); const su2double vel_div = vel_grad(0,0) + vel_grad(1,1) + (nDim ==3 ? vel_grad(2,2) : 0.0); - const su2double tau_xx = mu_t/rho * (2*vel_grad(0,0) - (2.0/3.0)*vel_div); - const su2double tau_yy = mu_t/rho * (2*vel_grad(1,1) - (2.0/3.0)*vel_div); - const su2double tau_xy = mu_t/rho * (vel_grad(0,1) + vel_grad(1,0)); + const su2double tau_xx = nu_t * (2*vel_grad(0,0) - (2.0/3.0)*vel_div); + const su2double tau_yy = nu_t * (2*vel_grad(1,1) - (2.0/3.0)*vel_div); + const su2double tau_xy = nu_t * (vel_grad(0,1) + vel_grad(1,0)); su2double tau_zz=0.0, tau_xz=0.0, tau_yz=0.0; if (nDim == 3){ - tau_zz = mu_t/rho * (2*vel_grad(2,2) - (2.0/3.0)*vel_div); - tau_xz = mu_t/rho * (vel_grad(0,2) + vel_grad(2,0)); - tau_yz = mu_t/rho * (vel_grad(1,2) + vel_grad(2,1)); + tau_zz = nu_t * (2*vel_grad(2,2) - (2.0/3.0)*vel_div); + tau_xz = nu_t * (vel_grad(0,2) + vel_grad(2,0)); + tau_yz = nu_t * (vel_grad(1,2) + vel_grad(2,1)); } const su2double energy_res_to_mod = tau_xx * vel_grad(0,0) + tau_yy * vel_grad(1,1) + (nDim==3 ? tau_zz * vel_grad(2,2) : 0.0) @@ -1666,24 +1666,39 @@ void CFlowOutput::LoadVolumeDataScalar(const CConfig* config, const CSolver* con } if (config->GetTime_Domain()) { - const su2double mu_t = Node_Flow->GetEddyViscosity(iPoint); const su2double rho = Node_Flow->GetDensity(iPoint); + const su2double nu_t = Node_Flow->GetEddyViscosity(iPoint) / rho; const auto vel_grad = Node_Flow->GetVelocityGradient(iPoint); const su2double vel_div = vel_grad(0,0) + vel_grad(1,1) + (nDim ==3 ? vel_grad(2,2) : 0.0); - const su2double tau_xx = mu_t/rho * (2*vel_grad(0,0) - (2.0/3.0)*vel_div); - const su2double tau_yy = mu_t/rho * (2*vel_grad(1,1) - (2.0/3.0)*vel_div); - const su2double tau_xy = mu_t/rho * (vel_grad(0,1) + vel_grad(1,0)); + const su2double tau_xx = nu_t * (2*vel_grad(0,0) - (2.0/3.0)*vel_div); + const su2double tau_yy = nu_t * (2*vel_grad(1,1) - (2.0/3.0)*vel_div); + const su2double tau_xy = nu_t * (vel_grad(0,1) + vel_grad(1,0)); SetAvgVolumeOutputValue("MODELED_REYNOLDS_STRESS_11", iPoint, -tau_xx); SetAvgVolumeOutputValue("MODELED_REYNOLDS_STRESS_22", iPoint, -tau_yy); SetAvgVolumeOutputValue("MODELED_REYNOLDS_STRESS_12", iPoint, -tau_xy); if (nDim == 3){ - const su2double tau_zz = mu_t/rho * (2*vel_grad(2,2) - (2.0/3.0)*vel_div); - const su2double tau_xz = mu_t/rho * (vel_grad(0,2) + vel_grad(2,0)); - const su2double tau_yz = mu_t/rho * (vel_grad(1,2) + vel_grad(2,1)); + const su2double tau_zz = nu_t * (2*vel_grad(2,2) - (2.0/3.0)*vel_div); + const su2double tau_xz = nu_t * (vel_grad(0,2) + vel_grad(2,0)); + const su2double tau_yz = nu_t * (vel_grad(1,2) + vel_grad(2,1)); SetAvgVolumeOutputValue("MODELED_REYNOLDS_STRESS_33", iPoint, -tau_zz); SetAvgVolumeOutputValue("MODELED_REYNOLDS_STRESS_13", iPoint, -tau_xz); SetAvgVolumeOutputValue("MODELED_REYNOLDS_STRESS_23", iPoint, -tau_yz); } + if (config->GetKind_HybridRANSLES()!=NO_HYBRIDRANSLES && config->GetStochastic_Backscatter()) { + const su2double DES_lengthscale = max(Node_Flow->GetDES_LengthScale(iPoint), 1.0E-10); + const su2double lesSensor = Node_Flow->GetLES_Mode(iPoint); + const su2double mag = config->GetSBS_Cmag(); + const su2double tke_estim = pow(nu_t/DES_lengthscale, 2.0); + const su2double csi_x = Node_Turb->GetSolution(iPoint, 1); + const su2double csi_y = Node_Turb->GetSolution(iPoint, 2); + const su2double csi_z = (nDim==3) ? Node_Turb->GetSolution(iPoint, 3) : 0.0; + const su2double R_xy = mag * tke_estim * csi_z * std::nearbyint(lesSensor); + const su2double R_xz = - mag * tke_estim * csi_y * std::nearbyint(lesSensor); + const su2double R_yz = mag * tke_estim * csi_x * std::nearbyint(lesSensor); + SetAvgVolumeOutputValue("STOCHASTIC_REYNOLDS_STRESS_12", iPoint, -R_xy); + SetAvgVolumeOutputValue("STOCHASTIC_REYNOLDS_STRESS_13", iPoint, -R_xz); + SetAvgVolumeOutputValue("STOCHASTIC_REYNOLDS_STRESS_23", iPoint, -R_yz); + } } switch (config->GetKind_Species_Model()) { @@ -4107,6 +4122,10 @@ void CFlowOutput::SetTimeAveragedFields() { AddVolumeOutput("MODELED_REYNOLDS_STRESS_13", "ModeledReynoldsStress_13", "TIME_AVERAGE", "Modeled Reynolds stress xz-component"); AddVolumeOutput("MODELED_REYNOLDS_STRESS_23", "ModeledReynoldsStress_23", "TIME_AVERAGE", "Modeled Reynolds stress yz-component"); } + + AddVolumeOutput("STOCHASTIC_REYNOLDS_STRESS_12", "StochasticReynoldsStress_12", "TIME_AVERAGE", "Stochastic Reynolds stress xy-component"); + AddVolumeOutput("STOCHASTIC_REYNOLDS_STRESS_13", "StochasticReynoldsStress_13", "TIME_AVERAGE", "Stochastic Reynolds stress xz-component"); + AddVolumeOutput("STOCHASTIC_REYNOLDS_STRESS_23", "StochasticReynoldsStress_23", "TIME_AVERAGE", "Stochastic Reynolds stress yz-component"); } void CFlowOutput::LoadTimeAveragedData(unsigned long iPoint, const CVariable *Node_Flow){ diff --git a/SU2_CFD/src/solvers/CTurbSASolver.cpp b/SU2_CFD/src/solvers/CTurbSASolver.cpp index 07585c5b222c..6b050fb38418 100644 --- a/SU2_CFD/src/solvers/CTurbSASolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp @@ -1795,35 +1795,36 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet var_check_old += source * source; mean_check_notSmoothed += source_notSmoothed; var_check_notSmoothed += source_notSmoothed * source_notSmoothed; - su2double maxDelta = geometry->nodes->GetMaxLength(iPoint); - if (LES_FilterWidth > 0.0) maxDelta = LES_FilterWidth; - su2double b = sqrt(cDelta) * maxDelta; - su2double b2 = b * b; - auto coord_i = geometry->nodes->GetCoord(iPoint); - su2double max_dx = 0.0; - su2double max_dy = 0.0; - su2double max_dz = 0.0; - unsigned short nNeigh = geometry->nodes->GetnPoint(iPoint); - for (unsigned short iNode = 0; iNode < nNeigh; iNode++) { - auto jPoint = geometry->nodes->GetPoint(iPoint, iNode); - auto coord_j = geometry->nodes->GetCoord(jPoint); - su2double dx = fabs(coord_i[0]-coord_j[0]); - su2double dy = fabs(coord_i[1]-coord_j[1]); - su2double dz = 0.0; - if (nDim == 3) dz = fabs(coord_i[2]-coord_j[2]); - if (dx > max_dx) max_dx = dx; - if (dy > max_dy) max_dy = dy; - if (dz > max_dz) max_dz = dz; - } - su2double dx2 = max_dx * max_dx; - su2double dy2 = max_dy * max_dy; - su2double dz2 = max_dz * max_dz; - su2double bx = b2 / dx2; - su2double by = b2 / dy2; - su2double bz = 0.0; - if (nDim == 3) bz = b2 / dz2; su2double integral = 0.0; if (timeIter==restartIter) { + const su2double DES_LengthScale = nodes->GetDES_LengthScale(iPoint); + su2double maxDelta = DES_LengthScale / constDES; + if (LES_FilterWidth > 0.0) maxDelta = LES_FilterWidth; + su2double b = sqrt(cDelta) * maxDelta; + su2double b2 = b * b; + auto coord_i = geometry->nodes->GetCoord(iPoint); + su2double max_dx = 0.0; + su2double max_dy = 0.0; + su2double max_dz = 0.0; + unsigned short nNeigh = geometry->nodes->GetnPoint(iPoint); + for (unsigned short iNode = 0; iNode < nNeigh; iNode++) { + auto jPoint = geometry->nodes->GetPoint(iPoint, iNode); + auto coord_j = geometry->nodes->GetCoord(jPoint); + su2double dx = fabs(coord_i[0]-coord_j[0]); + su2double dy = fabs(coord_i[1]-coord_j[1]); + su2double dz = 0.0; + if (nDim == 3) dz = fabs(coord_i[2]-coord_j[2]); + if (dx > max_dx) max_dx = dx; + if (dy > max_dy) max_dy = dy; + if (dz > max_dz) max_dz = dz; + } + su2double dx2 = max_dx * max_dx; + su2double dy2 = max_dy * max_dy; + su2double dz2 = max_dz * max_dz; + su2double bx = b2 / dx2; + su2double by = b2 / dy2; + su2double bz = 0.0; + if (nDim == 3) bz = b2 / dz2; integral = RandomToolbox::GetBesselIntegral(bx, by, bz); nodes->SetBesselIntegral(iPoint, integral); } else { @@ -1836,33 +1837,34 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet var_check_new += source * source; nodes->SetLangevinSourceTerms(iPoint, iDim, source); } - su2double mean_check_old_G = 0.0; - su2double mean_check_new_G = 0.0; - su2double mean_check_notSmoothed_G = 0.0; - su2double var_check_old_G = 0.0; - su2double var_check_new_G = 0.0; - su2double var_check_notSmoothed_G = 0.0; - SU2_MPI::Allreduce(&mean_check_old, &mean_check_old_G, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); - SU2_MPI::Allreduce(&mean_check_new, &mean_check_new_G, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); - SU2_MPI::Allreduce(&mean_check_notSmoothed, &mean_check_notSmoothed_G, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); - SU2_MPI::Allreduce(&var_check_old, &var_check_old_G, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); - SU2_MPI::Allreduce(&var_check_new, &var_check_new_G, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); - SU2_MPI::Allreduce(&var_check_notSmoothed, &var_check_notSmoothed_G, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); - mean_check_old_G /= global_nPointLES; - var_check_old_G /= global_nPointLES; - var_check_old_G -= mean_check_old_G * mean_check_old_G; - mean_check_new_G /= global_nPointLES; - var_check_new_G /= global_nPointLES; - var_check_new_G -= mean_check_new_G * mean_check_new_G; - mean_check_notSmoothed_G /= global_nPointLES; - var_check_notSmoothed_G /= global_nPointLES; - var_check_notSmoothed_G -= mean_check_notSmoothed_G * mean_check_notSmoothed_G; - if (rank == MASTER_NODE) { - cout << "Mean of stochastic source term before scaling: " << mean_check_old_G-mean_check_notSmoothed_G <<". After scaling: " << mean_check_new_G-mean_check_notSmoothed_G << "." << endl; - cout << "Variance of stochastic source term before scaling: " << var_check_old_G/var_check_notSmoothed_G <<". After scaling: " << var_check_new_G/var_check_notSmoothed_G << "." << endl; - cout << endl; + if (config->GetStochSourceDiagnostics()) { + su2double mean_check_old_G = 0.0; + su2double mean_check_new_G = 0.0; + su2double mean_check_notSmoothed_G = 0.0; + su2double var_check_old_G = 0.0; + su2double var_check_new_G = 0.0; + su2double var_check_notSmoothed_G = 0.0; + SU2_MPI::Allreduce(&mean_check_old, &mean_check_old_G, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); + SU2_MPI::Allreduce(&mean_check_new, &mean_check_new_G, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); + SU2_MPI::Allreduce(&mean_check_notSmoothed, &mean_check_notSmoothed_G, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); + SU2_MPI::Allreduce(&var_check_old, &var_check_old_G, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); + SU2_MPI::Allreduce(&var_check_new, &var_check_new_G, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); + SU2_MPI::Allreduce(&var_check_notSmoothed, &var_check_notSmoothed_G, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); + mean_check_old_G /= global_nPointLES; + var_check_old_G /= global_nPointLES; + var_check_old_G -= mean_check_old_G * mean_check_old_G; + mean_check_new_G /= global_nPointLES; + var_check_new_G /= global_nPointLES; + var_check_new_G -= mean_check_new_G * mean_check_new_G; + mean_check_notSmoothed_G /= global_nPointLES; + var_check_notSmoothed_G /= global_nPointLES; + var_check_notSmoothed_G -= mean_check_notSmoothed_G * mean_check_notSmoothed_G; + if (rank == MASTER_NODE) { + cout << "Mean of stochastic source term before scaling: " << mean_check_old_G-mean_check_notSmoothed_G <<". After scaling: " << mean_check_new_G-mean_check_notSmoothed_G << "." << endl; + cout << "Variance of stochastic source term before scaling: " << var_check_old_G/var_check_notSmoothed_G <<". After scaling: " << var_check_new_G/var_check_notSmoothed_G << "." << endl; + cout << endl; + } } - break; } diff --git a/config_template.cfg b/config_template.cfg index afca061f0330..4d206f2e9b9b 100644 --- a/config_template.cfg +++ b/config_template.cfg @@ -199,9 +199,15 @@ SBS_TIMESCALE_COEFF= 0.05 % Backscatter intensity coefficient (1.0) SBS_INTENSITY_COEFF= 1.0 % +% Relaxation factor for the stochastic source term in the main balance equations (0.0) +SBS_RELAXATION_FACTOR= 0.0 +% % Include stochastic source in turbulence model equation (NO, YES) STOCH_SOURCE_NU= YES % +% Include diagnostics of the stochastic source term in Langevin equations (NO, YES) +STOCH_SOURCE_DIAGNOSTICS= NO +% % Enforce LES mode in Hybrid RANS-LES Simulations (NO, YES) ENFORCE_LES= NO %