From 36b6f7a3ce7da466c2bcfad4d652c39be3cfa5f7 Mon Sep 17 00:00:00 2001 From: Angelo Passariello Date: Mon, 15 Sep 2025 18:26:39 +0200 Subject: [PATCH 01/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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 % From 443838f8022765a59972181d5328533d39689ff2 Mon Sep 17 00:00:00 2001 From: paan882 Date: Tue, 6 Jan 2026 16:13:17 +0100 Subject: [PATCH 26/30] Enhance numerical efficiency - Random generators removed from static variables. - Random generators re-initialized at every time step using deterministic seeds. --- Common/include/toolboxes/random_toolbox.hpp | 11 ++++---- .../numerics/turbulent/turb_sources.hpp | 6 +++-- .../include/solvers/CFVMFlowSolverBase.inl | 2 +- SU2_CFD/include/variables/CTurbSAVariable.hpp | 17 ------------ SU2_CFD/include/variables/CVariable.hpp | 16 ------------ SU2_CFD/src/solvers/CTurbSASolver.cpp | 26 +++++-------------- SU2_CFD/src/variables/CTurbSAVariable.cpp | 1 - 7 files changed, 17 insertions(+), 62 deletions(-) diff --git a/Common/include/toolboxes/random_toolbox.hpp b/Common/include/toolboxes/random_toolbox.hpp index aa50baf1e7f6..cf268b63bcb8 100644 --- a/Common/include/toolboxes/random_toolbox.hpp +++ b/Common/include/toolboxes/random_toolbox.hpp @@ -57,9 +57,12 @@ inline unsigned long ToUInt64(double x) { return std::hash{}(x); } * \brief Build a deterministic seed from physical time. * \param[in] x First integer value. * \param[in] y Second integer value. + * \param[in] z Third integer value. * \return 64-bit seed value. */ -inline unsigned long GetSeed(unsigned long x, unsigned long y) { return HashCombine(x, y); } +inline unsigned long GetSeed(unsigned long x, unsigned long y, unsigned long z) { + return HashCombine(HashCombine(x, y), z); +} /*! * \brief Generate a standard normally-distributed random number. @@ -68,7 +71,7 @@ inline unsigned long GetSeed(unsigned long x, unsigned long y) { return HashComb * \param[in] stddev Standard deviation of the normal distribution (default 1). * \return Normally-distributed random number. */ -inline double GetRandomNormal(std::mt19937 gen, double mean = 0.0, double stddev = 1.0) { +inline double GetRandomNormal(std::mt19937& gen, double mean = 0.0, double stddev = 1.0) { std::normal_distribution rnd(mean, stddev); return rnd(gen); } @@ -80,7 +83,7 @@ inline double GetRandomNormal(std::mt19937 gen, double mean = 0.0, double stddev * \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) { +inline double GetRandomUniform(std::mt19937& gen, double xmin = 0.0, double xmax = 1.0) { std::uniform_real_distribution rnd(xmin, xmax); return rnd(gen); } @@ -135,8 +138,6 @@ inline double GetBesselIntegral(double beta_x, double beta_y, double beta_z) { 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); diff --git a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp index f9cc15a146c1..084a4fc087e8 100644 --- a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp +++ b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp @@ -171,13 +171,15 @@ 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-12; + const su2double eps = 1e-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); - su2double tke = pow(var.fv1*ScalarVar_i[0]/dist_i, 2); + su2double tke = 0.0; + if (dist_i >= eps) + tke = pow(var.fv1*ScalarVar_i[0]/dist_i, 2); su2double R12 = (nDim==3 ? Cmag * tke * ScalarVar_i[3] : 0.0); su2double R13 = - Cmag * tke * ScalarVar_i[2]; diff --git a/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl b/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl index 1d7322f0e11c..0293b3bf6d40 100644 --- a/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl +++ b/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl @@ -484,7 +484,7 @@ void CFVMFlowSolverBase::Viscous_Residual_impl(unsigned long iEdge, CGeome DES_length_j = turbNodes->GetDES_LengthScale(jPoint); lesMode_i = turbNodes->GetLES_Mode(iPoint); lesMode_j = turbNodes->GetLES_Mode(jPoint); - const su2double tol = 1e-12; + const su2double tol = 1e-10; if (DES_length_i < tol || DES_length_j < tol) { tke_i = tke_j = 0.0; } else { diff --git a/SU2_CFD/include/variables/CTurbSAVariable.hpp b/SU2_CFD/include/variables/CTurbSAVariable.hpp index 4b625b901012..0d345b0611e3 100644 --- a/SU2_CFD/include/variables/CTurbSAVariable.hpp +++ b/SU2_CFD/include/variables/CTurbSAVariable.hpp @@ -45,7 +45,6 @@ class CTurbSAVariable final : public CTurbVariable { MatrixType stochSource; MatrixType stochSource_old; VectorType Vortex_Tilting; - MatrixTypeGen stochGen; VectorType besselIntegral; public: @@ -136,22 +135,6 @@ 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 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_stochGen - Value of the seed for the stochastic equations. - */ - 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. diff --git a/SU2_CFD/include/variables/CVariable.hpp b/SU2_CFD/include/variables/CVariable.hpp index 5a432dedcbb7..68bf526ad8e2 100644 --- a/SU2_CFD/include/variables/CVariable.hpp +++ b/SU2_CFD/include/variables/CVariable.hpp @@ -52,7 +52,6 @@ class CVariable { protected: using VectorType = C2DContainer; using MatrixType = C2DContainer; - using MatrixTypeGen = C2DContainer; MatrixType Solution; /*!< \brief Solution of the problem. */ MatrixType Solution_Old; /*!< \brief Old solution of the problem R-K. */ @@ -442,13 +441,6 @@ class CVariable { */ inline virtual void SetLangevinSourceTermsOld(unsigned long iPoint, unsigned short iDim, su2double val_stochSource_old) {} - /*! - * \brief A virtual member. - * \param[in] iPoint - Point index. - * \param[in] iDim - Dimension index. - */ - 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. @@ -461,14 +453,6 @@ class CVariable { */ inline virtual su2double GetBesselIntegral(unsigned long iPoint) const { return 0.0; } - /*! - * \brief A virtual member. - * \param[in] iPoint - Point index. - * \param[in] iDim - Dimension index. - * \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) {} - /*! * \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 6b050fb38418..f04c95fb75bc 100644 --- a/SU2_CFD/src/solvers/CTurbSASolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp @@ -242,7 +242,6 @@ void CTurbSASolver::Preprocessing(CGeometry *geometry, CSolver **solver_containe unsigned long restartIter = config->GetRestart_Iter(); if (backscatter && innerIter==0) { - SetLangevinGen(config, geometry); SetLangevinSourceTerms(config, geometry); const unsigned short maxIter = config->GetSBS_maxIterSmooth(); bool dual_time = ((config->GetTime_Marching() == TIME_MARCHING::DT_STEPPING_1ST) || @@ -443,7 +442,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); + numerics->SetLES_Mode(nodes->GetLES_Mode(iPoint), 0.0); } } @@ -1643,10 +1642,14 @@ void CTurbSASolver::SetDES_LengthScale(CSolver **solver, CGeometry *geometry, CC void CTurbSASolver::SetLangevinSourceTerms(CConfig *config, CGeometry* geometry) { + unsigned long timeIter = 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++){ - auto gen = nodes->GetLangevinGen(iPoint, iDim); + unsigned long seed = RandomToolbox::GetSeed(iGlobalPoint+1, iDim+1, timeIter+1); + std::mt19937 gen(seed); su2double lesSensor = nodes->GetLES_Mode(iPoint); su2double rnd = RandomToolbox::GetRandomNormal(gen) * std::nearbyint(lesSensor); nodes->SetLangevinSourceTermsOld(iPoint, iDim, rnd); @@ -1657,23 +1660,6 @@ void CTurbSASolver::SetLangevinSourceTerms(CConfig *config, 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); - std::mt19937 gen(seed + timeStep); - nodes->SetLangevinGen(iPoint, iDim, gen); - } - } - END_SU2_OMP_FOR - -} - void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geometry) { const su2double LES_FilterWidth = config->GetLES_FilterWidth(); diff --git a/SU2_CFD/src/variables/CTurbSAVariable.cpp b/SU2_CFD/src/variables/CTurbSAVariable.cpp index 384cc70e51cc..26f150cc7111 100644 --- a/SU2_CFD/src/variables/CTurbSAVariable.cpp +++ b/SU2_CFD/src/variables/CTurbSAVariable.cpp @@ -61,7 +61,6 @@ CTurbSAVariable::CTurbSAVariable(su2double val_nu_tilde, su2double val_muT, unsi Vortex_Tilting.resize(nPoint); stochSource.resize(nPoint, nDim) = su2double(0.0); stochSource_old.resize(nPoint, nDim) = su2double(0.0); - stochGen.resize(nPoint, nDim); besselIntegral.resize(nPoint); } From fb3f8ec6d979b9ed36e0ffb1867e3286f7f03b71 Mon Sep 17 00:00:00 2001 From: paan882 Date: Thu, 8 Jan 2026 18:15:01 +0100 Subject: [PATCH 27/30] Fix bug in viscous flux computation - Account for zero DES length scale in ghost cells. - Fix threshold (=0.9) for the activation of the backscatter model. --- SU2_CFD/include/numerics/CNumerics.hpp | 26 +++---- .../numerics/turbulent/turb_sources.hpp | 72 +++++++++---------- .../include/solvers/CFVMFlowSolverBase.inl | 27 ++++--- SU2_CFD/include/solvers/CTurbSASolver.hpp | 7 -- SU2_CFD/src/numerics/flow/flow_diffusion.cpp | 24 +++---- SU2_CFD/src/output/CFlowOutput.cpp | 24 ++++--- SU2_CFD/src/solvers/CTurbSASolver.cpp | 15 ++-- 7 files changed, 94 insertions(+), 101 deletions(-) diff --git a/SU2_CFD/include/numerics/CNumerics.hpp b/SU2_CFD/include/numerics/CNumerics.hpp index cacc6b356585..8f9d121095f7 100644 --- a/SU2_CFD/include/numerics/CNumerics.hpp +++ b/SU2_CFD/include/numerics/CNumerics.hpp @@ -660,24 +660,20 @@ class CNumerics { * \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, - Scalar turbKE, Vector rndVec, Scalar lesSensor, - Mat& stochReynStress, Scalar Cmag) { + NEVERINLINE static void ComputeStochReynStress(size_t nDim, Scalar density, Scalar tke, + Vector rndVec, Mat& stochReynStress, Scalar Cmag) { /* --- Calculate stochastic tensor --- */ - 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]; - } - } - } + stochReynStress[0][0] = 0.0; + stochReynStress[1][1] = 0.0; + stochReynStress[2][2] = 0.0; + stochReynStress[0][1] = Cmag * density * tke * rndVec[2]; + stochReynStress[0][2] = - Cmag * density * tke * rndVec[1]; + stochReynStress[1][2] = Cmag * density * tke * rndVec[0]; + stochReynStress[1][0] = - stochReynStress[1][0]; + stochReynStress[2][0] = - stochReynStress[2][0]; + stochReynStress[2][1] = - stochReynStress[2][1]; } diff --git a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp index 084a4fc087e8..ae05251f458d 100644 --- a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp +++ b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp @@ -123,44 +123,47 @@ class CSourceBase_TurbSA : public CNumerics { const su2double& nue = ScalarVar_i[0]; const auto& density = V_i[idx.Density()]; + const su2double threshold = 0.9; + const su2double nut = max(nue*var.fv1, 1e-10); + const su2double delta = lengthScale/DES_const; - const su2double nut_small = 1.0e-12; - - const su2double nut = max(nue * var.fv1, 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; - - 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); + for (unsigned short iVar = 0; iVar < nVar; iVar++ ) { + ResidSB[iVar] = 0.0; } - - su2double scaleFactor = 0.0; - 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++ ) { - ResidSB[iVar] = scaleFactor * stochSource[iVar-1] - 1.0/tTurb * density * ScalarVar_i[iVar]; - ResidSB[iVar] *= Volume; - } for (unsigned short iVar = 0; iVar < nVar; iVar++ ) { for (unsigned short jVar = 0; jVar < nVar; jVar++ ) { JacobianSB_i[iVar][jVar] = 0.0; } } + JacobianSB_i[0][0] = Jacobian_i[0]; - for (unsigned short iVar = 1; iVar < nVar; iVar++ ) { - JacobianSB_i[iVar][iVar] = -1.0/tTurb * density * Volume; - } + if (delta > 1e-10) { - JacobianSB_i[0][0] = Jacobian_i[0]; + su2double tTurb = ct*pow(delta, 2)/nut; + su2double tRat = timeStep / tTurb; + + 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); + } + + su2double scaleFactor = 0.0; + if (lesMode_i > threshold) scaleFactor = 1.0/tTurb * sqrt(2.0/tRat) * density * corrFac; + + for (unsigned short iVar = 1; iVar < nVar; iVar++ ) { + ResidSB[iVar] = scaleFactor * stochSource[iVar-1] - 1.0/tTurb * density * ScalarVar_i[iVar]; + ResidSB[iVar] *= Volume; + } + + for (unsigned short iVar = 1; iVar < nVar; iVar++ ) { + JacobianSB_i[iVar][iVar] = -1.0/tTurb * density * Volume; + } + + } } @@ -171,15 +174,13 @@ 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 = 1e-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); + su2double factor = dist2 / (2.0 * var.fv1 * ScalarVar_i[0]); + factor /= (3.0 * xi3 * var.cv1_3 / pow(xi3 + var.cv1_3, 2) + var.fv1); su2double tke = 0.0; - if (dist_i >= eps) - tke = pow(var.fv1*ScalarVar_i[0]/dist_i, 2); + const su2double threshold = 0.9; + if (lesMode_i > threshold) tke = pow(var.fv1*ScalarVar_i[0]/dist_i, 2); su2double R12 = (nDim==3 ? Cmag * tke * ScalarVar_i[3] : 0.0); su2double R13 = - Cmag * tke * ScalarVar_i[2]; @@ -368,9 +369,8 @@ class CSourceBase_TurbSA : public CNumerics { /*--- Compute residual for Langevin equations (Stochastic Backscatter Model). ---*/ if (backscatter) { - if (std::nearbyint(lesMode_i) == 1 && config->GetStochSourceNu()) { + if (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) { @@ -381,7 +381,7 @@ class CSourceBase_TurbSA : public CNumerics { 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)); + intensityCoeff = SBS_Cmag * (1.0 - exp(- currentTime / (timeScale*SBS_RelaxFactor))); } AddStochSource(var, PrimVar_Grad_i + idx.Velocity(), intensityCoeff); } diff --git a/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl b/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl index 0293b3bf6d40..673c037aef00 100644 --- a/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl +++ b/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl @@ -475,24 +475,21 @@ void CFVMFlowSolverBase::Viscous_Residual_impl(unsigned long iEdge, CGeome numerics->SetStochVar(turbNodes->GetSolution(iPoint, 1 + iDim), turbNodes->GetSolution(jPoint, 1 + iDim), iDim); } - su2double rho, eddy_visc_i, eddy_visc_j, DES_length_i, - DES_length_j, tke_i, tke_j, lesMode_i, lesMode_j; - 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); - lesMode_j = turbNodes->GetLES_Mode(jPoint); - const su2double tol = 1e-10; - if (DES_length_i < tol || DES_length_j < tol) { - tke_i = tke_j = 0.0; - } else { + su2double rho = nodes->GetDensity(iPoint); + su2double eddy_visc_i = turbNodes->GetmuT(iPoint) / rho; + su2double eddy_visc_j = turbNodes->GetmuT(jPoint) / rho; + su2double DES_length_i = turbNodes->GetDES_LengthScale(iPoint); + su2double DES_length_j = turbNodes->GetDES_LengthScale(jPoint); + su2double lesMode_i = (DES_length_i > 1e-10) ? turbNodes->GetLES_Mode(iPoint) : 0.0; + su2double lesMode_j = (DES_length_j > 1e-10) ? turbNodes->GetLES_Mode(jPoint) : 0.0; + const su2double threshold = 0.9; + su2double tke_i = 0.0, tke_j = 0.0; + if (max(lesMode_i, lesMode_j) > threshold) { tke_i = pow(eddy_visc_i/DES_length_i, 2); - tke_j = pow(eddy_visc_j/DES_length_j, 2); + tke_j = tke_i; + if (geometry->nodes->GetDomain(jPoint)) 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/include/solvers/CTurbSASolver.hpp b/SU2_CFD/include/solvers/CTurbSASolver.hpp index 0aaf50225462..71004ee5504c 100644 --- a/SU2_CFD/include/solvers/CTurbSASolver.hpp +++ b/SU2_CFD/include/solvers/CTurbSASolver.hpp @@ -64,13 +64,6 @@ class CTurbSASolver final : public CTurbSolver { */ 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 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. diff --git a/SU2_CFD/src/numerics/flow/flow_diffusion.cpp b/SU2_CFD/src/numerics/flow/flow_diffusion.cpp index 902b8a6f0f06..be11dabccdc2 100644 --- a/SU2_CFD/src/numerics/flow/flow_diffusion.cpp +++ b/SU2_CFD/src/numerics/flow/flow_diffusion.cpp @@ -146,8 +146,9 @@ void CAvgGrad_Base::SetStressTensor(const su2double *val_primvar, if (config->GetStochastic_Backscatter()) { for (unsigned short iDim = 0 ; iDim < nDim; iDim++) - for (unsigned short jDim = 0 ; jDim < nDim; jDim++) + for (unsigned short jDim = 0 ; jDim < nDim; jDim++) { tau[iDim][jDim] += stochReynStress[iDim][jDim]; + } } } @@ -461,7 +462,6 @@ 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); su2double SBS_RelaxFactor = config->GetStochSourceRelax(); su2double intensityCoeff = SBS_Cmag; if (SBS_RelaxFactor > 0.0) { @@ -472,10 +472,10 @@ CNumerics::ResidualType<> CAvgGrad_Flow::ComputeResidual(const CConfig* config) 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)); + intensityCoeff = SBS_Cmag * (1.0 - exp(- currentTime / (timeScale*SBS_RelaxFactor))); } - ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_Eddy_Viscosity, Mean_turb_ke, - Mean_StochVar, lesSensor, stochReynStress, intensityCoeff); + ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_turb_ke, + Mean_StochVar, stochReynStress, intensityCoeff); } /*--- Get projected flux tensor (viscous residual) ---*/ @@ -658,7 +658,6 @@ 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); su2double SBS_RelaxFactor = config->GetStochSourceRelax(); su2double intensityCoeff = SBS_Cmag; if (SBS_RelaxFactor > 0.0) { @@ -669,10 +668,10 @@ CNumerics::ResidualType<> CAvgGradInc_Flow::ComputeResidual(const CConfig* confi 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)); + intensityCoeff = SBS_Cmag * (1.0 - exp(- currentTime / (timeScale*SBS_RelaxFactor))); } - ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_Eddy_Viscosity, Mean_turb_ke, - Mean_StochVar, lesSensor, stochReynStress, intensityCoeff); + ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_turb_ke, + Mean_StochVar, stochReynStress, intensityCoeff); } /*--- Get projected flux tensor (viscous residual) ---*/ @@ -1007,7 +1006,6 @@ 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); su2double SBS_RelaxFactor = config->GetStochSourceRelax(); su2double intensityCoeff = SBS_Cmag; if (SBS_RelaxFactor > 0.0) { @@ -1018,10 +1016,10 @@ CNumerics::ResidualType<> CGeneralAvgGrad_Flow::ComputeResidual(const CConfig* c 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)); + intensityCoeff = SBS_Cmag * (1.0 - exp(- currentTime / (timeScale*SBS_RelaxFactor))); } - ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_Eddy_Viscosity, Mean_turb_ke, - Mean_StochVar, lesSensor, stochReynStress, intensityCoeff); + ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_turb_ke, + Mean_StochVar, 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 44338b645a58..1331363a90f8 100644 --- a/SU2_CFD/src/output/CFlowOutput.cpp +++ b/SU2_CFD/src/output/CFlowOutput.cpp @@ -1631,16 +1631,18 @@ void CFlowOutput::LoadVolumeDataScalar(const CConfig* config, const CSolver* con if (nDim==3) SetVolumeOutputValue("STOCHSOURCE_Z", iPoint, Node_Turb->GetLangevinSourceTerms(iPoint, 2)); const su2double rho = Node_Flow->GetDensity(iPoint); const su2double nu_t = Node_Flow->GetEddyViscosity(iPoint) / rho; - const su2double DES_lengthscale = max(Node_Flow->GetDES_LengthScale(iPoint), 1.0E-10); + const su2double DES_lengthscale = max(Node_Flow->GetDES_LengthScale(iPoint), 1e-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 threshold = 0.9; + su2double tke_estim = 0.0; + if (lesSensor > threshold) 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); + const su2double R_xy = mag * tke_estim * csi_z; + const su2double R_xz = - mag * tke_estim * csi_y; + const su2double R_yz = mag * tke_estim * csi_x; 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 = nu_t * (2*vel_grad(0,0) - (2.0/3.0)*vel_div); @@ -1685,16 +1687,18 @@ void CFlowOutput::LoadVolumeDataScalar(const CConfig* config, const CSolver* con 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 DES_lengthscale = max(Node_Flow->GetDES_LengthScale(iPoint), 1e-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 threshold = 0.9; + su2double tke_estim = 0.0; + if (lesSensor > threshold) 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); + const su2double R_xy = mag * tke_estim * csi_z; + const su2double R_xz = - mag * tke_estim * csi_y; + const su2double R_yz = mag * tke_estim * csi_x; SetAvgVolumeOutputValue("STOCHASTIC_REYNOLDS_STRESS_12", iPoint, -R_xy); SetAvgVolumeOutputValue("STOCHASTIC_REYNOLDS_STRESS_13", iPoint, -R_xz); SetAvgVolumeOutputValue("STOCHASTIC_REYNOLDS_STRESS_23", iPoint, -R_yz); diff --git a/SU2_CFD/src/solvers/CTurbSASolver.cpp b/SU2_CFD/src/solvers/CTurbSASolver.cpp index f04c95fb75bc..ff3115ab8c87 100644 --- a/SU2_CFD/src/solvers/CTurbSASolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp @@ -1643,15 +1643,19 @@ void CTurbSASolver::SetDES_LengthScale(CSolver **solver, CGeometry *geometry, CC void CTurbSASolver::SetLangevinSourceTerms(CConfig *config, CGeometry* geometry) { unsigned long timeIter = config->GetTimeIter(); + const su2double threshold = 0.9; SU2_OMP_FOR_DYN(omp_chunk_size) for (auto iPoint = 0ul; iPoint < nPointDomain; iPoint++){ - const auto iGlobalPoint = geometry->nodes->GetGlobalIndex(iPoint); + const auto coord = geometry->nodes->GetCoord(iPoint); + su2double coordSum = (coord[0] + coord[1] + ((nDim == 3) ? coord[2] : 0.0)) * 1e6; + unsigned long coordHash = std::nearbyint(coordSum); for (auto iDim = 0u; iDim < nDim; iDim++){ - unsigned long seed = RandomToolbox::GetSeed(iGlobalPoint+1, iDim+1, timeIter+1); + unsigned long seed = RandomToolbox::GetSeed(coordHash, iDim+1, timeIter+1); std::mt19937 gen(seed); su2double lesSensor = nodes->GetLES_Mode(iPoint); - su2double rnd = RandomToolbox::GetRandomNormal(gen) * std::nearbyint(lesSensor); + su2double rnd = 0.0; + if (lesSensor > threshold) rnd = RandomToolbox::GetRandomNormal(gen); nodes->SetLangevinSourceTermsOld(iPoint, iDim, rnd); nodes->SetLangevinSourceTerms(iPoint, iDim, rnd); } @@ -1671,6 +1675,7 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet const su2double omega = 0.8; unsigned long timeIter = config->GetTimeIter(); unsigned long restartIter = config->GetRestart_Iter(); + const su2double threshold = 0.9; /*--- Start SOR algorithm for the Laplacian smoothing. ---*/ @@ -1689,7 +1694,7 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet SU2_OMP_FOR_DYN(omp_chunk_size) for (unsigned long iPoint = 0; iPoint < nPointDomain; iPoint++) { const su2double lesSensor = nodes->GetLES_Mode(iPoint); - if (std::nearbyint(lesSensor) == 0.0) continue; + if (lesSensor < threshold) continue; local_nPointLES += 1; const su2double DES_LengthScale = nodes->GetDES_LengthScale(iPoint); su2double maxDelta = DES_LengthScale / constDES; @@ -1774,7 +1779,7 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet 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; + if (lesSensor < threshold) continue; su2double source = nodes->GetLangevinSourceTerms(iPoint, iDim); su2double source_notSmoothed = nodes->GetLangevinSourceTermsOld(iPoint, iDim); mean_check_old += source; From 77de85b1fbb64d648858ad12685cd79d7a198f90 Mon Sep 17 00:00:00 2001 From: paan882 Date: Tue, 27 Jan 2026 12:30:11 +0100 Subject: [PATCH 28/30] First major revision - Add LD2 scheme to CONV_NUM_METHOD enum. - Change scaling of the backscatter term. - Increase the size of residual and Jacobian in turb_sources.hpp. - Remove dynamic allocation in CTurbSASolver.cpp. - Add helper function to compute relaxed backscatter intensity coefficient. - Include pressure extrapolation in LD2 scheme formulation. - Make code style homogeneous. --- Common/include/CConfig.hpp | 9 +- Common/include/option_structure.hpp | 4 +- Common/include/toolboxes/random_toolbox.hpp | 124 +++++++------- Common/src/CConfig.cpp | 30 ++-- SU2_CFD/include/numerics/CNumerics.hpp | 45 ++++- .../numerics/scalar/scalar_convection.hpp | 6 +- .../numerics/turbulent/turb_convection.hpp | 11 +- .../numerics/turbulent/turb_sources.hpp | 154 ++++++------------ .../include/numerics_simd/CNumericsSIMD.cpp | 1 + .../include/solvers/CFVMFlowSolverBase.inl | 19 +-- SU2_CFD/include/solvers/CTurbSASolver.hpp | 26 +-- SU2_CFD/include/solvers/CTurbSolver.hpp | 1 - SU2_CFD/include/variables/CIncNSVariable.hpp | 6 +- SU2_CFD/include/variables/CNEMONSVariable.hpp | 2 +- SU2_CFD/include/variables/CNSVariable.hpp | 6 +- SU2_CFD/include/variables/CTurbSAVariable.hpp | 12 +- SU2_CFD/include/variables/CTurbVariable.hpp | 15 ++ SU2_CFD/include/variables/CVariable.hpp | 15 -- SU2_CFD/src/drivers/CDriver.cpp | 1 + .../src/numerics/flow/convection/centered.cpp | 8 +- SU2_CFD/src/numerics/flow/flow_diffusion.cpp | 68 +++----- SU2_CFD/src/output/CFlowCompOutput.cpp | 9 - SU2_CFD/src/output/CFlowIncOutput.cpp | 9 - SU2_CFD/src/output/CFlowOutput.cpp | 53 +++--- SU2_CFD/src/solvers/CIncEulerSolver.cpp | 6 +- SU2_CFD/src/solvers/CTurbSASolver.cpp | 109 +++++++------ SU2_CFD/src/variables/CIncNSVariable.cpp | 2 +- SU2_CFD/src/variables/CNEMONSVariable.cpp | 2 +- SU2_CFD/src/variables/CNSVariable.cpp | 2 +- SU2_CFD/src/variables/CTurbSAVariable.cpp | 6 +- config_template.cfg | 5 +- 31 files changed, 339 insertions(+), 427 deletions(-) diff --git a/Common/include/CConfig.hpp b/Common/include/CConfig.hpp index a18d6584246b..46a557e17c2b 100644 --- a/Common/include/CConfig.hpp +++ b/Common/include/CConfig.hpp @@ -1107,7 +1107,6 @@ class CConfig { 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). */ unsigned short Kind_RoeLowDiss; /*!< \brief Kind of Roe scheme with low dissipation for unsteady flows. */ unsigned short nSpanWiseSections; /*!< \brief number of span-wise sections */ @@ -9642,12 +9641,6 @@ 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. @@ -9670,7 +9663,7 @@ class CConfig { * \brief Get the SBS timescale coefficient. * \return Value of SBS timescale coefficient. */ - su2double GetSBS_maxIterSmooth(void) const { return SBS_maxIterSmooth; } + unsigned short GetSBS_maxIterSmooth(void) const { return SBS_maxIterSmooth; } /*! * \brief Get the SBS timescale coefficient. diff --git a/Common/include/option_structure.hpp b/Common/include/option_structure.hpp index 9f9f851c7142..548871ba9d17 100644 --- a/Common/include/option_structure.hpp +++ b/Common/include/option_structure.hpp @@ -820,7 +820,8 @@ enum class CENTERED { JST, /*!< \brief Jameson-Smith-Turkel centered numerical method. */ LAX, /*!< \brief Lax-Friedrich centered numerical method. */ JST_MAT, /*!< \brief JST with matrix dissipation. */ - JST_KE /*!< \brief Kinetic Energy preserving Jameson-Smith-Turkel centered numerical method. */ + JST_KE, /*!< \brief Kinetic Energy preserving Jameson-Smith-Turkel centered numerical method. */ + LD2 /*!< \brief Low-Dissipation Low-Dispersion (LD2) centered scheme. */ }; static const MapType Centered_Map = { MakePair("NONE", CENTERED::NONE) @@ -828,6 +829,7 @@ static const MapType Centered_Map = { MakePair("JST_KE", CENTERED::JST_KE) MakePair("JST_MAT", CENTERED::JST_MAT) MakePair("LAX-FRIEDRICH", CENTERED::LAX) + MakePair("LD2", CENTERED::LD2) }; diff --git a/Common/include/toolboxes/random_toolbox.hpp b/Common/include/toolboxes/random_toolbox.hpp index cf268b63bcb8..0249e1c5076f 100644 --- a/Common/include/toolboxes/random_toolbox.hpp +++ b/Common/include/toolboxes/random_toolbox.hpp @@ -25,67 +25,80 @@ */ #pragma once - -#include -#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. + * \brief SplitMix64 hash function for 64-bit integers. + * \param[in] x Input value to hash. + * \return Hashed 64-bit output. */ -inline unsigned long HashCombine(unsigned long v1, unsigned long v2) { - const unsigned long prime = 1099511628211ULL; - v1 ^= v2; - v1 *= prime; - return v1; +static inline uint64_t splitmix64(uint64_t x) { + x += 0x9e3779b97f4a7c15ULL; // golden ratio offset + x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL; // first mixing step + x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL; // second mixing step + return x ^ (x >> 31); // final avalanche } /*! - * \brief Convert a double to a 64-bit integer suitable for hashing. - * \param[in] x Double to integer. - * \return Hash value of the double (not portable). + * \brief Generate a deterministic 64-bit hash from three integers. + * \param[in] nodeIndex Global node index. + * \param[in] iDim Dimension index. + * \param[in] timeIter Current time iteration of the simulation. + * \return 64-bit hash value. */ -inline unsigned long ToUInt64(double x) { return std::hash{}(x); } +inline uint64_t GetHash(unsigned long nodeIndex, unsigned short iDim, unsigned long timeIter) { + uint64_t x = nodeIndex; + x ^= splitmix64(iDim); + x ^= splitmix64(timeIter); + return splitmix64(x); +} /*! - * \brief Build a deterministic seed from physical time. - * \param[in] x First integer value. - * \param[in] y Second integer value. - * \param[in] z Third integer value. - * \return 64-bit seed value. + * \brief Convert a 64-bit hash into a uniform double in (0,1]. + * Uses the top 53 bits of the hash to fill the mantissa of a double. + * Ensures the result is never zero, suitable for Box-Muller transform. + * \param[in] x 64-bit hash. + * \return Uniform double in the interval (0,1]. */ -inline unsigned long GetSeed(unsigned long x, unsigned long y, unsigned long z) { - return HashCombine(HashCombine(x, y), z); +inline double HashToUniform(uint64_t x) { + constexpr double inv53 = 1.0 / 9007199254740992.0; // 1/2^53 + uint64_t uInt = x >> 11; // top 53 bits + return (uInt + 1) * inv53; // map to (0,1] } /*! - * \brief Generate a standard normally-distributed random number. - * \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. + * \brief Generate a standard normal random number from a 64-bit hash. + * Uses two deterministic uniforms derived from the hash and its bitwise NOT + * as inputs to the Box-Muller transform. + * \param[in] x 64-bit hash. + * \return Standard normal random number (mean=0, stddev=1). */ -inline double GetRandomNormal(std::mt19937& gen, double mean = 0.0, double stddev = 1.0) { - std::normal_distribution rnd(mean, stddev); - return rnd(gen); +inline double HashToNormal(uint64_t x) { + constexpr double pi = 3.14159265358979323846; + double u = HashToUniform(x); // first uniform + double v = HashToUniform(~x); // second uniform (bitwise NOT) + double r = sqrt(-2.0 * log(u)); + double theta = 2.0 * pi * v; + return r * cos(theta); // one normal sample } /*! - * \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. + * \brief Generate a deterministic standard normal number for a cell, dimension, and timestep. + * + * Combines hashing and Box-Muller in one function. + * + * \param[in] nodeIndex Global node index. + * \param[in] dim Dimension index. + * \param[in] timeIter Simulation timestep (1-based). + * \return Standard normal 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); +inline double GetNormal(unsigned long nodeIndex, unsigned long dim, unsigned long timeIter) { + uint64_t hash = GetHash(nodeIndex, dim, timeIter); + return HashToNormal(hash); } /*! @@ -95,29 +108,20 @@ 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; - 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))))); + double t = abx / 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))))))); + 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); } } /*! - * \brief Compute integral involving product of three modified Bessel functions. + * \brief Compute integral involving the product of three modified Bessel functions. + * Useful for scaling the smoothed stochastic source terms in Langevin equations. * \param[in] beta_x Argument in x-direction. * \param[in] beta_y Argument in y-direction. * \param[in] beta_z Argument in z-direction. @@ -128,28 +132,22 @@ inline double GetBesselIntegral(double beta_x, double beta_y, double 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 t_max = 20.0; const double dt = t_max / N; - double sum = 0.0; - - for (int i = 1; i < N; i++) { + for (int i = 1; i <= N; i++) { double t = i * dt; - 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); + if (i==N) integrand *= 0.5; sum += integrand; } - return sum * dt; } /// @} -} // namespace RandomToolbox +} // namespace RandomToolbox \ No newline at end of file diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index 3c737ec34b17..f9673d4ae153 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -2993,9 +2993,6 @@ 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); @@ -7161,25 +7158,20 @@ void CConfig::SetOutput(SU2_COMPONENT val_software, unsigned short val_izone) { cout << "Lax viscous coefficients (1st): " << Kappa_1st_Flow << ".\n"; cout << "First order integration." << endl; } + else if (Kind_Centered_Flow == CENTERED::LD2) { + 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 scheme not yet implemented for the compressible flow solver.", CURRENT_FUNCTION); + if (Kind_FluidModel != CONSTANT_DENSITY) + SU2_MPI::Error("LD2 scheme available for constant density flows only.", CURRENT_FUNCTION); + if (Energy_Equation) + cout << "WARNING: Current implementation of the LD2 scheme not compatible with the energy equation. JST employed in energy equation instead." << endl; + } else { - 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; } - } - - 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); + 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; } if (Kind_ConvNumScheme_Flow == SPACE_UPWIND) { diff --git a/SU2_CFD/include/numerics/CNumerics.hpp b/SU2_CFD/include/numerics/CNumerics.hpp index 1888d035db83..0ec06ced3662 100644 --- a/SU2_CFD/include/numerics/CNumerics.hpp +++ b/SU2_CFD/include/numerics/CNumerics.hpp @@ -185,14 +185,14 @@ 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. */ + su2double stochReynStress[3][3] = {{0.0}}; /*!< \brief Stochastic contribution to Reynolds stress tensor for 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. */ + stochVar_i[3] = {0.0}, /*!< \brief Stochastic variables at point i for Stochastic Backscatter Model. */ + stochVar_j[3] = {0.0}; /*!< \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. */ + lesMode_i = 0.0, /*!< \brief LES sensor at point i for hybrid RANS-LES methods. */ + lesMode_j = 0.0; /*!< \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 */ @@ -656,13 +656,14 @@ class CNumerics { * 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[in] tke - Turbulent kinetic energy. + * \param[in] rndVec - Vector of stochastic variables from Langevin equations. + * \param[in] Cmag - Stochastic backscatter intensity coefficient. * \param[out] stochReynStress - Stochastic tensor (to be added to the Reynolds stress tensor). */ template NEVERINLINE static void ComputeStochReynStress(size_t nDim, Scalar density, Scalar tke, - Vector rndVec, Mat& stochReynStress, Scalar Cmag) { + Vector rndVec, Scalar Cmag, Mat& stochReynStress) { /* --- Calculate stochastic tensor --- */ @@ -676,6 +677,32 @@ class CNumerics { stochReynStress[2][0] = - stochReynStress[2][0]; stochReynStress[2][1] = - stochReynStress[2][1]; + } + + /*! + * \brief Compute relaxation factor for stochastic source term in momentum equations (Stochastic Backscatter Model). + * \param[in] config - Definition of the particular problem. + * \param[out] intensityCoeff - Relaxation factor for backscatter intensity. + */ + NEVERINLINE static su2double ComputeStochRelaxFactor(const CConfig* config) { + + unsigned long timeIter = config->GetTimeIter(); + unsigned long restartIter = config->GetRestart_Iter(); + su2double SBS_Cmag = config->GetSBS_Cmag(); + 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(- currentTime / (timeScale*SBS_RelaxFactor))); + } + return intensityCoeff; + } /*! @@ -881,7 +908,7 @@ class CNumerics { * \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, unsigned short iDim) { + inline void SetStochVar(unsigned short iDim, su2double val_stochvar_i, su2double val_stochvar_j) { stochVar_i[iDim] = val_stochvar_i; stochVar_j[iDim] = val_stochvar_j; } diff --git a/SU2_CFD/include/numerics/scalar/scalar_convection.hpp b/SU2_CFD/include/numerics/scalar/scalar_convection.hpp index e6a38b9a2105..41d0ca2c5ac9 100644 --- a/SU2_CFD/include/numerics/scalar/scalar_convection.hpp +++ b/SU2_CFD/include/numerics/scalar/scalar_convection.hpp @@ -52,7 +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 m_ij = 0.0; /*!< \brief Face-normal momentum (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. */ @@ -142,9 +142,9 @@ class CUpwScalar : public CNumerics { } if (config->GetStochastic_Backscatter()) { - qij = 0.0; + m_ij = 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]; + m_ij += 0.5 * (V_i[idx.Density()]*V_i[iDim + idx.Velocity()] + V_j[idx.Density()]*V_j[iDim + idx.Velocity()]) * Normal[iDim]; } } diff --git a/SU2_CFD/include/numerics/turbulent/turb_convection.hpp b/SU2_CFD/include/numerics/turbulent/turb_convection.hpp index 7a2d86fca677..2df13c7bd55c 100644 --- a/SU2_CFD/include/numerics/turbulent/turb_convection.hpp +++ b/SU2_CFD/include/numerics/turbulent/turb_convection.hpp @@ -52,7 +52,7 @@ class CUpwSca_TurbSA final : public CUpwScalar { using Base::V_j; using Base::idx; using Base::nVar; - using Base::qij; + using Base::m_ij; /*! * \brief Adds any extra variables to AD. @@ -64,15 +64,14 @@ class CUpwSca_TurbSA final : public CUpwScalar { * \param[in] config - Definition of the particular problem. */ void FinishResidualCalc(const CConfig* config) override { - bool backscatter = config->GetStochastic_Backscatter(); - if (backscatter) { + if (config->GetStochastic_Backscatter()) { for (unsigned short iVar = 1; iVar < nVar; iVar++) { - Flux[iVar] = qij * 0.5 * (ScalarVar_i[iVar] + ScalarVar_j[iVar]); + Flux[iVar] = m_ij * 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) ? 0.5*qij : 0.0; - Jacobian_j[iVar][jVar] = (iVar == jVar) ? 0.5*qij : 0.0; + Jacobian_i[iVar][jVar] = (iVar == jVar) ? 0.5*m_ij : 0.0; + Jacobian_j[iVar][jVar] = (iVar == jVar) ? 0.5*m_ij : 0.0; } } } diff --git a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp index efaaf5609d57..a662a315856a 100644 --- a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp +++ b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp @@ -72,10 +72,8 @@ class CSourceBase_TurbSA : public CNumerics { protected: /*--- 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 = nullptr; - su2double** JacobianSB_i = nullptr; + su2double Residual[4], *Jacobian_i[4]; /*!< \brief Increase the size of residual and Jacobian for Langevin equations (Stochastic Backscatter Model).*/ + su2double Jacobian_Buffer[16]; /*!< \brief Static storage for the Jacobian (which needs to be pointer for return type). */ const FlowIndices idx; /*!< \brief Object to manage the access to the flow primitives. */ const SA_ParsedOptions options; /*!< \brief Struct with SA options. */ @@ -111,7 +109,7 @@ class CSourceBase_TurbSA : public CNumerics { /* Diffusion source term */ const su2double dv_axi = (1.0/sigma)*nu_e*ScalarVar_Grad_i[0][1]; - Residual += yinv * dv_axi * Volume; + Residual[0] += yinv * dv_axi * Volume; } /*! @@ -127,18 +125,6 @@ class CSourceBase_TurbSA : public CNumerics { const su2double nut = max(nue*var.fv1, 1e-10); const su2double delta = lengthScale/DES_const; - for (unsigned short iVar = 0; iVar < nVar; iVar++ ) { - ResidSB[iVar] = 0.0; - } - ResidSB[0] = Residual; - - for (unsigned short iVar = 0; iVar < nVar; iVar++ ) { - for (unsigned short jVar = 0; jVar < nVar; jVar++ ) { - JacobianSB_i[iVar][jVar] = 0.0; - } - } - JacobianSB_i[0][0] = Jacobian_i[0]; - if (delta > 1e-10) { su2double tTurb = ct*pow(delta, 2)/nut; @@ -154,14 +140,13 @@ class CSourceBase_TurbSA : public CNumerics { su2double scaleFactor = 0.0; if (lesMode_i > threshold) scaleFactor = 1.0/tTurb * sqrt(2.0/tRat) * density * corrFac; - for (unsigned short iVar = 1; iVar < nVar; iVar++ ) { - ResidSB[iVar] = scaleFactor * stochSource[iVar-1] - 1.0/tTurb * density * ScalarVar_i[iVar]; - ResidSB[iVar] *= Volume; + for (unsigned short iVar = 1; iVar < nVar; iVar++) { + Residual[iVar] = scaleFactor * stochSource[iVar-1] - 1.0/tTurb * density * ScalarVar_i[iVar]; + Residual[iVar] *= Volume; } - for (unsigned short iVar = 1; iVar < nVar; iVar++ ) { - JacobianSB_i[iVar][iVar] = -1.0/tTurb * density * Volume; - } + for (unsigned short iVar = 1; iVar < nVar; iVar++ ) + Jacobian_i[iVar][iVar] = -1.0/tTurb * density * Volume; } @@ -170,30 +155,25 @@ class CSourceBase_TurbSA : public CNumerics { /*! * \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; - su2double xi3 = pow(var.Ji, 3); - su2double factor = dist2 / (2.0 * var.fv1 * ScalarVar_i[0]); - factor /= (3.0 * xi3 * var.cv1_3 / pow(xi3 + var.cv1_3, 2) + var.fv1); + inline void AddStochSource(CSAVariables& var, const su2double Cmag, su2double& prod) { + su2double nut = ScalarVar_i[0] * var.fv1; su2double tke = 0.0; const su2double threshold = 0.9; - if (lesMode_i > threshold) tke = pow(var.fv1*ScalarVar_i[0]/dist_i, 2); + const su2double limiter = 10.0; + if (lesMode_i > threshold) tke = nut * StrainMag_i; - 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 R12 = Cmag * tke * ScalarVar_i[3]; + su2double R13 = - Cmag * tke * ScalarVar_i[2]; + su2double R23 = Cmag * tke * ScalarVar_i[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 RGradU = R12*Vorticity_i[2] - R13*Vorticity_i[1] + R23*Vorticity_i[0]; - su2double source_k = - RGradU; - su2double source_nu = factor * source_k; + su2double stoch_prod_k = - RGradU; + su2double prod_k = nut * StrainMag_i * StrainMag_i; + su2double stoch_prod_nut = min(limiter, max(-limiter, stoch_prod_k/max(prod_k,1e-10))); - Residual += source_nu * Volume; + prod *= (1.0 + stoch_prod_nut); } @@ -204,37 +184,17 @@ class CSourceBase_TurbSA : public CNumerics { * \param[in] config - Definition of the particular problem. */ CSourceBase_TurbSA(unsigned short nDim, const CConfig* config) - : CNumerics(nDim, config->GetStochastic_Backscatter() ? 1+nDim : 1, config), + : CNumerics(nDim, config->GetStochastic_Backscatter() ? 4 : 1, config), idx(nDim, config->GetnSpecies()), options(config->GetSAParsedOptions()), axisymmetric(config->GetAxisymmetric()), transition_LM(config->GetKind_Trans_Model() == TURB_TRANS_MODEL::LM) { /*--- 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 for Stochastic Backscatter Model. ---*/ - if (config->GetStochastic_Backscatter()) { - 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] (); - } - } - } - - /*! - * \brief Destructor of the class. - */ - ~CSourceBase_TurbSA() { - if (JacobianSB_i) { - for (unsigned short iVar = 0; iVar < 1+nDim; iVar++) { - delete [] JacobianSB_i[iVar]; - } - delete [] JacobianSB_i; - } - if (ResidSB) { - delete [] ResidSB; - } + //Jacobian_i = &Jacobian_Buffer; + /*--- Setup the Jacobian pointer (size increased for Stochastic Backscatter Model). ---*/ + for (unsigned short iVar = 0; iVar < 4; iVar++) + Jacobian_i[iVar] = Jacobian_Buffer + 4*iVar; } /*! @@ -252,17 +212,17 @@ class CSourceBase_TurbSA : public CNumerics { AD::SetPreaccIn(Vorticity_i, 3); AD::SetPreaccIn(PrimVar_Grad_i + idx.Velocity(), nDim, nDim); AD::SetPreaccIn(ScalarVar_Grad_i, nVar, nDim); - - bool backscatter = config->GetStochastic_Backscatter(); - if (backscatter) { - AD::SetPreaccIn(stochSource, 3); - } + AD::SetPreaccIn(stochSource, 3); /*--- Common auxiliary variables and constants of the model. ---*/ CSAVariables var; - Residual = 0.0; - Jacobian_i[0] = 0.0; + for (unsigned short iVar = 0; iVar < 4; iVar++) { + Residual[iVar] = 0.0; + for (unsigned short jVar = 0; jVar < 4; jVar++) { + Jacobian_i[iVar][jVar] = 0.0; + } + } if (dist_i > 1e-10) { @@ -358,33 +318,22 @@ class CSourceBase_TurbSA : public CNumerics { /*--- Compute production, destruction and jacobian ---*/ su2double Production = 0.0, Destruction = 0.0; - SourceTerms::get(ScalarVar_i[0], var, Production, Destruction, Jacobian_i[0]); + SourceTerms::get(ScalarVar_i[0], var, Production, Destruction, Jacobian_i[0][0]); - Residual = (Production - Destruction) * Volume; + if (config->GetStochastic_Backscatter() && config->GetStochSourceNu()) { + su2double intensityCoeff = ComputeStochRelaxFactor(config); + AddStochSource(var, intensityCoeff, Production); + } + + Residual[0] = (Production - Destruction) * Volume; if (axisymmetric) ResidualAxisymmetricDiffusion(var.sigma); - Jacobian_i[0] *= Volume; + Jacobian_i[0][0] *= Volume; /*--- Compute residual for Langevin equations (Stochastic Backscatter Model). ---*/ - if (backscatter) { - if (config->GetStochSourceNu()) { - su2double SBS_Cmag = config->GetSBS_Cmag(); - 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(- currentTime / (timeScale*SBS_RelaxFactor))); - } - AddStochSource(var, PrimVar_Grad_i + idx.Velocity(), intensityCoeff); - } + if (config->GetStochastic_Backscatter()) { const su2double DES_const = config->GetConst_DES(); const su2double ctau = config->GetSBS_Ctau(); ResidualStochEquations(config->GetDelta_UnstTime(), ctau, dist_i, DES_const, @@ -392,17 +341,10 @@ class CSourceBase_TurbSA : public CNumerics { } } - if (backscatter) { - AD::SetPreaccOut(ResidSB, nVar); - } else { - AD::SetPreaccOut(Residual); - } + AD::SetPreaccOut(Residual, 4); AD::EndPreacc(); - if (backscatter) - return ResidualType<>(ResidSB, JacobianSB_i, nullptr); - else - return ResidualType<>(&Residual, &Jacobian_i, nullptr); + return ResidualType<>(Residual, Jacobian_i, nullptr); } }; @@ -698,14 +640,14 @@ class CCompressibilityCorrection final : public ParentClass { const su2double d_axiCorrection = 2.0 * c5 * nue * pow(v * yinv / sound_speed, 2) * Volume; const su2double axiCorrection = 0.5 * nue * d_axiCorrection; - this->Residual -= axiCorrection; - this->Jacobian_i[0] -= d_axiCorrection; + this->Residual[0] -= axiCorrection; + this->Jacobian_i[0][0] -= d_axiCorrection; } - this->Residual -= CompCorrection; - this->Jacobian_i[0] -= d_CompCorrection; + this->Residual[0] -= CompCorrection; + this->Jacobian_i[0][0] -= d_CompCorrection; - return ResidualType(&this->Residual, &this->Jacobian_i, nullptr); + return ResidualType(this->Residual, this->Jacobian_i, nullptr); } }; diff --git a/SU2_CFD/include/numerics_simd/CNumericsSIMD.cpp b/SU2_CFD/include/numerics_simd/CNumericsSIMD.cpp index 09e71002f8ae..69101654c99c 100644 --- a/SU2_CFD/include/numerics_simd/CNumericsSIMD.cpp +++ b/SU2_CFD/include/numerics_simd/CNumericsSIMD.cpp @@ -71,6 +71,7 @@ CNumericsSIMD* createCenteredNumerics(const CConfig& config, int iMesh, const CV obj = new CLaxScheme(config, iMesh, turbVars); break; case CENTERED::JST: + case CENTERED::LD2: // Just to silence compiler warnings (LD2 implemented in the incompressible solver only). obj = new CJSTScheme(config, iMesh, turbVars); break; case CENTERED::JST_KE: diff --git a/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl b/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl index a3b5f5a31ecd..92f35c463f6e 100644 --- a/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl +++ b/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl @@ -471,13 +471,9 @@ void CFVMFlowSolverBase::Viscous_Residual_impl(unsigned long iEdge, CGeome /*--- Stochastic variables from Langevin equations (Stochastic Backscatter Model). ---*/ if (backscatter) { - for (unsigned short iDim = 0; iDim < nDim; iDim++) { - numerics->SetStochVar(turbNodes->GetSolution(iPoint, 1 + iDim), - turbNodes->GetSolution(jPoint, 1 + iDim), iDim); - } - su2double rho = nodes->GetDensity(iPoint); - su2double eddy_visc_i = turbNodes->GetmuT(iPoint) / rho; - su2double eddy_visc_j = turbNodes->GetmuT(jPoint) / rho; + for (unsigned short iDim = 0; iDim < nDim; iDim++) + numerics->SetStochVar(iDim, turbNodes->GetSolution(iPoint, iDim+1), + turbNodes->GetSolution(jPoint, iDim+1)); su2double DES_length_i = turbNodes->GetDES_LengthScale(iPoint); su2double DES_length_j = turbNodes->GetDES_LengthScale(jPoint); su2double lesMode_i = (DES_length_i > 1e-10) ? turbNodes->GetLES_Mode(iPoint) : 0.0; @@ -485,9 +481,12 @@ void CFVMFlowSolverBase::Viscous_Residual_impl(unsigned long iEdge, CGeome const su2double threshold = 0.9; su2double tke_i = 0.0, tke_j = 0.0; if (max(lesMode_i, lesMode_j) > threshold) { - tke_i = pow(eddy_visc_i/DES_length_i, 2); - tke_j = tke_i; - if (geometry->nodes->GetDomain(jPoint)) tke_j = pow(eddy_visc_j/DES_length_j, 2); + su2double eddyVisc_i = turbNodes->GetmuT(iPoint) / nodes->GetDensity(iPoint); + su2double eddyVisc_j = turbNodes->GetmuT(jPoint) / nodes->GetDensity(jPoint); + su2double strainMag_i = nodes->GetStrainMag(iPoint); + su2double strainMag_j = nodes->GetStrainMag(jPoint); + tke_i = strainMag_i * eddyVisc_i; + tke_j = strainMag_j * eddyVisc_j; } numerics->SetTurbKineticEnergy(tke_i, tke_j); } diff --git a/SU2_CFD/include/solvers/CTurbSASolver.hpp b/SU2_CFD/include/solvers/CTurbSASolver.hpp index 70ad3f60bc15..a19ffb3dd9e4 100644 --- a/SU2_CFD/include/solvers/CTurbSASolver.hpp +++ b/SU2_CFD/include/solvers/CTurbSASolver.hpp @@ -39,13 +39,9 @@ class CTurbSASolver final : public CTurbSolver { private: - 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; + + su2double nu_tilde_Engine[4] = {0.0}; + su2double nu_tilde_ActDisk[4] = {0.0}; /*! * \brief A virtual member. @@ -105,21 +101,7 @@ class CTurbSASolver final : public CTurbSolver { /*! * \brief Destructor of the class. */ - ~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; - - } + ~CTurbSASolver() = default; /*! * \brief Restart residual and compute gradients. diff --git a/SU2_CFD/include/solvers/CTurbSolver.hpp b/SU2_CFD/include/solvers/CTurbSolver.hpp index dc4676186eb7..56b7b96b3b26 100644 --- a/SU2_CFD/include/solvers/CTurbSolver.hpp +++ b/SU2_CFD/include/solvers/CTurbSolver.hpp @@ -152,7 +152,6 @@ 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/CIncNSVariable.hpp b/SU2_CFD/include/variables/CIncNSVariable.hpp index feffcc349287..5aa1ebaa75e9 100644 --- a/SU2_CFD/include/variables/CIncNSVariable.hpp +++ b/SU2_CFD/include/variables/CIncNSVariable.hpp @@ -40,7 +40,7 @@ class CIncNSVariable final : public CIncEulerVariable { private: 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 lesMode; /*!< \brief Sensor for local simulation mode (0=RANS, 1=LES).*/ const bool Energy; /*!< \brief Flag for Energy equation in incompressible flows. */ public: @@ -138,13 +138,13 @@ class CIncNSVariable final : public CIncEulerVariable { * \brief Set the LES sensor. */ inline void SetLES_Mode(unsigned long iPoint, su2double val_les_mode) override { - LES_Mode(iPoint) = val_les_mode; + lesMode(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); } + inline su2double GetLES_Mode(unsigned long iPoint) const override { return lesMode(iPoint); } }; diff --git a/SU2_CFD/include/variables/CNEMONSVariable.hpp b/SU2_CFD/include/variables/CNEMONSVariable.hpp index fdf3e9229e54..4585c1c6b9b6 100644 --- a/SU2_CFD/include/variables/CNEMONSVariable.hpp +++ b/SU2_CFD/include/variables/CNEMONSVariable.hpp @@ -52,7 +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 lesMode; /*!< \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 19f2d8924e90..9c3ab8dabf50 100644 --- a/SU2_CFD/include/variables/CNSVariable.hpp +++ b/SU2_CFD/include/variables/CNSVariable.hpp @@ -41,7 +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 lesMode; /*!< \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. */ @@ -190,14 +190,14 @@ class CNSVariable final : public CEulerVariable { * \brief Set the LES sensor. */ inline void SetLES_Mode(unsigned long iPoint, su2double val_les_mode) override { - LES_Mode(iPoint) = val_les_mode; + lesMode(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); } + inline su2double GetLES_Mode(unsigned long iPoint) const override { return lesMode(iPoint); } /*! * \brief Set the new solution for Roe Dissipation. diff --git a/SU2_CFD/include/variables/CTurbSAVariable.hpp b/SU2_CFD/include/variables/CTurbSAVariable.hpp index 90be6f1e0472..7e4d88df4d2c 100644 --- a/SU2_CFD/include/variables/CTurbSAVariable.hpp +++ b/SU2_CFD/include/variables/CTurbSAVariable.hpp @@ -41,9 +41,9 @@ class CTurbSAVariable final : public CTurbVariable { private: VectorType DES_LengthScale; - VectorType LES_Mode; + VectorType lesMode; MatrixType stochSource; - MatrixType stochSource_old; + MatrixType stochSourceOld; VectorType Vortex_Tilting; VectorType besselIntegral; @@ -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 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 stochSourceOld(iPoint, iDim); } /*! * \brief Set the old value of source terms for the stochastic equations. @@ -108,18 +108,18 @@ class CTurbSAVariable final : public CTurbVariable { * \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; } + inline void SetLangevinSourceTermsOld(unsigned long iPoint, unsigned short iDim, su2double val_stochSource_old) override { stochSourceOld(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; } + inline void SetLES_Mode(unsigned long iPoint, su2double val_les_mode) override { lesMode(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); } + inline su2double GetLES_Mode(unsigned long iPoint) const override { return lesMode(iPoint); } /*! * \brief Set the vortex tilting measure for computation of the EDDES length scale diff --git a/SU2_CFD/include/variables/CTurbVariable.hpp b/SU2_CFD/include/variables/CTurbVariable.hpp index 9db56e771274..34bf86e04e76 100644 --- a/SU2_CFD/include/variables/CTurbVariable.hpp +++ b/SU2_CFD/include/variables/CTurbVariable.hpp @@ -111,4 +111,19 @@ class CTurbVariable : public CScalarVariable { * \param[in] input - Boolean whether In- or Output should be registered. */ void RegisterEddyViscosity(bool input); + + /*! + * \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) {} }; diff --git a/SU2_CFD/include/variables/CVariable.hpp b/SU2_CFD/include/variables/CVariable.hpp index cc46938b43e0..a520ed27f465 100644 --- a/SU2_CFD/include/variables/CVariable.hpp +++ b/SU2_CFD/include/variables/CVariable.hpp @@ -426,21 +426,6 @@ class CVariable { */ 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. diff --git a/SU2_CFD/src/drivers/CDriver.cpp b/SU2_CFD/src/drivers/CDriver.cpp index 961ce04832ff..f2e1e5b953e8 100644 --- a/SU2_CFD/src/drivers/CDriver.cpp +++ b/SU2_CFD/src/drivers/CDriver.cpp @@ -1658,6 +1658,7 @@ void CDriver::InitializeNumerics(CConfig *config, CGeometry **geometry, CSolver /*--- Incompressible flow, use preconditioning method ---*/ switch (config->GetKind_Centered_Flow()) { case CENTERED::LAX : numerics[MESH_0][FLOW_SOL][conv_term] = new CCentLaxInc_Flow(nDim, nVar_Flow, config); break; + case CENTERED::LD2 : case CENTERED::JST : numerics[MESH_0][FLOW_SOL][conv_term] = new CCentJSTInc_Flow(nDim, nVar_Flow, config); break; default: SU2_MPI::Error("Invalid centered scheme or not implemented.\n Currently, only JST and LAX-FRIEDRICH are available for incompressible flows.", CURRENT_FUNCTION); diff --git a/SU2_CFD/src/numerics/flow/convection/centered.cpp b/SU2_CFD/src/numerics/flow/convection/centered.cpp index 4ad35559df9a..fb3c8ac7b4c4 100644 --- a/SU2_CFD/src/numerics/flow/convection/centered.cpp +++ b/SU2_CFD/src/numerics/flow/convection/centered.cpp @@ -311,7 +311,7 @@ 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(); + bool LD2_Scheme = (config->GetKind_Centered_Flow() == CENTERED::LD2); const su2double alpha_LD2 = 0.36; /*--- Primitive variables at point i and j ---*/ @@ -336,13 +336,19 @@ CNumerics::ResidualType<> CCentJSTInc_Flow::ComputeResidual(const CConfig* confi d_ij[iDim] = Coord_j[iDim]-Coord_i[iDim]; su2double velGrad_i[3][3] = {{0.0}}; su2double velGrad_j[3][3] = {{0.0}}; + su2double pressGrad_i[3] = {0.0}; + su2double pressGrad_j[3] = {0.0}; for (unsigned short jDim = 0; jDim < nDim; jDim++) { + pressGrad_i[jDim] = PrimVar_Grad_i[0][jDim]; + pressGrad_j[jDim] = PrimVar_Grad_j[0][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++) { + Pressure_i += alpha_LD2 * pressGrad_i[iDim] * d_ij[iDim]; + Pressure_j += alpha_LD2 * pressGrad_j[iDim] * d_ij[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]); diff --git a/SU2_CFD/src/numerics/flow/flow_diffusion.cpp b/SU2_CFD/src/numerics/flow/flow_diffusion.cpp index ecd88810ecce..15ba9809b375 100644 --- a/SU2_CFD/src/numerics/flow/flow_diffusion.cpp +++ b/SU2_CFD/src/numerics/flow/flow_diffusion.cpp @@ -458,24 +458,15 @@ CNumerics::ResidualType<> CAvgGrad_Flow::ComputeResidual(const CConfig* config) /* --- If the Stochastic Backscatter Model is active, add random contribution to stress tensor ---*/ + + unsigned long timeIter = config->GetTimeIter(); + unsigned long restartIter = config->GetRestart_Iter(); if (config->GetStochastic_Backscatter()) { - for (iVar = 0; iVar < nDim; iVar++) - Mean_StochVar[iVar] = 0.5*(stochVar_i[iVar] + stochVar_j[iVar]); - su2double SBS_Cmag = config->GetSBS_Cmag(); - 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(- currentTime / (timeScale*SBS_RelaxFactor))); - } + for (iDim = 0; iDim < nDim; iDim++) + Mean_StochVar[iDim] = 0.5*(stochVar_i[iDim] + stochVar_j[iDim]); + su2double intensityCoeff = ComputeStochRelaxFactor(config); ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_turb_ke, - Mean_StochVar, stochReynStress, intensityCoeff); + Mean_StochVar, intensityCoeff, stochReynStress); } /*--- Get projected flux tensor (viscous residual) ---*/ @@ -654,24 +645,15 @@ CNumerics::ResidualType<> CAvgGradInc_Flow::ComputeResidual(const CConfig* confi /* --- If the Stochastic Backscatter Model is active, add random contribution to stress tensor ---*/ + + unsigned long timeIter = config->GetTimeIter(); + unsigned long restartIter = config->GetRestart_Iter(); if (config->GetStochastic_Backscatter()) { - for (iVar = 0; iVar < nDim; iVar++) - Mean_StochVar[iVar] = 0.5*(stochVar_i[iVar] + stochVar_j[iVar]); - su2double SBS_Cmag = config->GetSBS_Cmag(); - 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(- currentTime / (timeScale*SBS_RelaxFactor))); - } + for (iDim = 0; iDim < nDim; iDim++) + Mean_StochVar[iDim] = 0.5*(stochVar_i[iDim] + stochVar_j[iDim]); + su2double intensityCoeff = ComputeStochRelaxFactor(config); ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_turb_ke, - Mean_StochVar, stochReynStress, intensityCoeff); + Mean_StochVar, intensityCoeff, stochReynStress); } /*--- Get projected flux tensor (viscous residual) ---*/ @@ -1002,24 +984,14 @@ CNumerics::ResidualType<> CGeneralAvgGrad_Flow::ComputeResidual(const CConfig* c /* --- If the Stochastic Backscatter Model is active, add random contribution to stress tensor ---*/ + unsigned long timeIter = config->GetTimeIter(); + unsigned long restartIter = config->GetRestart_Iter(); if (config->GetStochastic_Backscatter()) { - for (iVar = 0; iVar < nDim; iVar++) - Mean_StochVar[iVar] = 0.5*(stochVar_i[iVar] + stochVar_j[iVar]); - su2double SBS_Cmag = config->GetSBS_Cmag(); - 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(- currentTime / (timeScale*SBS_RelaxFactor))); - } + for (iDim = 0; iDim < nDim; iDim++) + Mean_StochVar[iDim] = 0.5*(stochVar_i[iDim] + stochVar_j[iDim]); + su2double intensityCoeff = ComputeStochRelaxFactor(config); ComputeStochReynStress(nDim, Mean_PrimVar[nDim+2], Mean_turb_ke, - Mean_StochVar, stochReynStress, intensityCoeff); + Mean_StochVar, intensityCoeff, stochReynStress); } /*--- Get projected flux tensor (viscous residual) ---*/ diff --git a/SU2_CFD/src/output/CFlowCompOutput.cpp b/SU2_CFD/src/output/CFlowCompOutput.cpp index 0f31e5c5dc38..4429cdfd7a05 100644 --- a/SU2_CFD/src/output/CFlowCompOutput.cpp +++ b/SU2_CFD/src/output/CFlowCompOutput.cpp @@ -234,8 +234,6 @@ 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"); @@ -327,13 +325,6 @@ 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 de2bde987bcc..b13edb7732fb 100644 --- a/SU2_CFD/src/output/CFlowIncOutput.cpp +++ b/SU2_CFD/src/output/CFlowIncOutput.cpp @@ -322,8 +322,6 @@ 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"); @@ -435,13 +433,6 @@ 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/output/CFlowOutput.cpp b/SU2_CFD/src/output/CFlowOutput.cpp index fe625fbf343d..e8b7abd04f3f 100644 --- a/SU2_CFD/src/output/CFlowOutput.cpp +++ b/SU2_CFD/src/output/CFlowOutput.cpp @@ -1703,34 +1703,29 @@ void CFlowOutput::LoadVolumeDataScalar(const CConfig* config, const CSolver* con const su2double lesSensor = Node_Flow->GetLES_Mode(iPoint); const su2double mag = config->GetSBS_Cmag(); const su2double threshold = 0.9; + const auto VelocityGradient = Node_Flow->GetVelocityGradient(iPoint); + su2double strainMag2 = 0.0; + for (unsigned long iDim = 0; iDim < nDim; iDim++) { + strainMag2 += pow(VelocityGradient(iDim, iDim), 2); + } + strainMag2 += 2.0*pow(0.5*(VelocityGradient(0,1) + VelocityGradient(1,0)), 2); + if (nDim == 3) { + strainMag2 += 2.0*pow(0.5*(VelocityGradient(0,2) + VelocityGradient(2,0)), 2); + strainMag2 += 2.0*pow(0.5*(VelocityGradient(1,2) + VelocityGradient(2,1)), 2); + } + strainMag2 *= 2.0; su2double tke_estim = 0.0; - if (lesSensor > threshold) tke_estim = pow(nu_t/DES_lengthscale, 2.0); + if (lesSensor > threshold) tke_estim = sqrt(strainMag2) * nu_t; 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 csi_z = Node_Turb->GetSolution(iPoint, 3); const su2double R_xy = mag * tke_estim * csi_z; const su2double R_xz = - mag * tke_estim * csi_y; const su2double R_yz = mag * tke_estim * csi_x; - 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 = 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 = 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) - + 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); + const su2double energy_res_to_mod = nu_t * strainMag2; + const auto vorticity = Node_Flow->GetVorticity(iPoint); + const su2double energy_backscatter = R_xy*vorticity[2] - R_xz*vorticity[1] + R_yz*vorticity[0]; + const su2double energy_backscatter_ratio = energy_backscatter / (energy_res_to_mod + 1e-10); SetVolumeOutputValue("ENERGY_BACKSCATTER_RATIO", iPoint, energy_backscatter_ratio); } } @@ -1760,10 +1755,20 @@ void CFlowOutput::LoadVolumeDataScalar(const CConfig* config, const CSolver* con const su2double mag = config->GetSBS_Cmag(); const su2double threshold = 0.9; su2double tke_estim = 0.0; - if (lesSensor > threshold) tke_estim = pow(nu_t/DES_lengthscale, 2.0); + su2double strainMag2 = 0.0; + for (unsigned long iDim = 0; iDim < nDim; iDim++) { + strainMag2 += pow(vel_grad(iDim, iDim), 2); + } + strainMag2 += 2.0*pow(0.5*(vel_grad(0,1) + vel_grad(1,0)), 2); + if (nDim == 3) { + strainMag2 += 2.0*pow(0.5*(vel_grad(0,2) + vel_grad(2,0)), 2); + strainMag2 += 2.0*pow(0.5*(vel_grad(1,2) + vel_grad(2,1)), 2); + } + strainMag2 *= 2.0; + if (lesSensor > threshold) tke_estim = sqrt(strainMag2) * nu_t; 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 csi_z = Node_Turb->GetSolution(iPoint, 3); const su2double R_xy = mag * tke_estim * csi_z; const su2double R_xz = - mag * tke_estim * csi_y; const su2double R_yz = mag * tke_estim * csi_x; diff --git a/SU2_CFD/src/solvers/CIncEulerSolver.cpp b/SU2_CFD/src/solvers/CIncEulerSolver.cpp index 5b25796947c5..f771594113de 100644 --- a/SU2_CFD/src/solvers/CIncEulerSolver.cpp +++ b/SU2_CFD/src/solvers/CIncEulerSolver.cpp @@ -959,7 +959,7 @@ void CIncEulerSolver::CommonPreprocessing(CGeometry *geometry, CSolver **solver_ const bool implicit = (config->GetKind_TimeIntScheme() == EULER_IMPLICIT); const bool center = (config->GetKind_ConvNumScheme_Flow() == SPACE_CENTERED); - const bool center_jst = (config->GetKind_Centered_Flow() == CENTERED::JST) && (iMesh == MESH_0); + const bool center_jst = (config->GetKind_Centered_Flow() == CENTERED::JST || config->GetKind_Centered_Flow() == CENTERED::LD2) && (iMesh == MESH_0); const bool outlet = (config->GetnMarker_Outlet() != 0); /*--- Set the primitive variables ---*/ @@ -1132,9 +1132,9 @@ void CIncEulerSolver::Centered_Residual(CGeometry *geometry, CSolver **solver_co unsigned long iPoint, jPoint; const bool implicit = (config->GetKind_TimeIntScheme() == EULER_IMPLICIT); - const bool jst_scheme = ((config->GetKind_Centered_Flow() == CENTERED::JST) && (iMesh == MESH_0)); + const bool jst_scheme = ((config->GetKind_Centered_Flow() == CENTERED::JST || config->GetKind_Centered_Flow() == CENTERED::LD2) && (iMesh == MESH_0)); const bool bounded_scalar = config->GetBounded_Scalar(); - const bool LD2_Scheme = config->GetLD2_Scheme(); + const bool LD2_Scheme = (config->GetKind_Centered_Flow() == CENTERED::LD2); /*--- For hybrid parallel AD, pause preaccumulation if there is shared reading of * variables, otherwise switch to the faster adjoint evaluation mode. ---*/ diff --git a/SU2_CFD/src/solvers/CTurbSASolver.cpp b/SU2_CFD/src/solvers/CTurbSASolver.cpp index b798d98c3c6c..6bb898f89e5d 100644 --- a/SU2_CFD/src/solvers/CTurbSASolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp @@ -57,8 +57,13 @@ CTurbSASolver::CTurbSASolver(CGeometry *geometry, CConfig *config, unsigned shor /*--- Add Langevin equations if the Stochastic Backscatter Model is used ---*/ - bool backscatter = config->GetStochastic_Backscatter(); - if (backscatter) { nVar += nDim; nVarGrad = nPrimVar = nVar; } + if (config->GetStochastic_Backscatter()) { + if (nDim == 3) { + nVar += nDim; nVarGrad = nPrimVar = nVar; + } else { + SU2_MPI::Error("Stochastic Backscatter Model available for 3D flows only.", CURRENT_FUNCTION);; + } + } /*--- Single grid simulation ---*/ @@ -115,7 +120,7 @@ CTurbSASolver::CTurbSASolver(CGeometry *geometry, CConfig *config, unsigned shor } Solution_Inf[0] = nu_tilde_Inf; - if (backscatter) { + if (config->GetStochastic_Backscatter()) { for (unsigned short iVar = 1; iVar < nVar; iVar++) { Solution_Inf[iVar] = 0.0; } @@ -123,7 +128,6 @@ CTurbSASolver::CTurbSASolver(CGeometry *geometry, CConfig *config, unsigned shor /*--- Factor_nu_Engine ---*/ 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] = 0.005*Factor_nu_Engine*Viscosity_Inf/Density_Inf; @@ -131,7 +135,6 @@ CTurbSASolver::CTurbSASolver(CGeometry *geometry, CConfig *config, unsigned shor /*--- Factor_nu_ActDisk ---*/ 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 ---*/ @@ -142,16 +145,6 @@ 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. ---*/ nodes = new CTurbSAVariable(nu_tilde_Inf, muT_Inf, nPoint, nDim, nVar, config); @@ -180,7 +173,7 @@ CTurbSASolver::CTurbSASolver(CGeometry *geometry, CConfig *config, unsigned shor Inlet_TurbVars.resize(nMarker); for (unsigned long iMarker = 0; iMarker < nMarker; iMarker++) { Inlet_TurbVars[iMarker].resize(nVertex[iMarker],nVar) = nu_tilde_Inf; - if (backscatter) { + if (config->GetStochastic_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; @@ -570,6 +563,7 @@ 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[MAXNVAR] = {0.0}; Res_Wall[0] = coeff*RoughWallBC*Area; LinSysRes.SubtractBlock(iPoint, Res_Wall); @@ -1143,9 +1137,8 @@ void CTurbSASolver::BC_Inlet_MixingPlane(CGeometry *geometry, CSolver **solver_c /*--- Loop over all the vertices on this boundary marker ---*/ for (auto iSpan = 0u; iSpan < nSpanWiseSections ; iSpan++){ - su2double extAverageNu = solver_container[FLOW_SOL]->GetExtAverageNu(val_marker, iSpan); - - ext_AverageNu[0] = extAverageNu; + su2double extAverageNu[MAXNVAR] = {0.0}; + extAverageNu[0] = solver_container[FLOW_SOL]->GetExtAverageNu(val_marker, iSpan); /*--- Loop over all the vertices on this boundary marker ---*/ @@ -1181,7 +1174,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), ext_AverageNu); + conv_numerics->SetScalarVar(nodes->GetSolution(iPoint), extAverageNu); /*--- Set various other quantities in the conv_numerics class ---*/ @@ -1211,7 +1204,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), ext_AverageNu); + visc_numerics->SetScalarVar(nodes->GetSolution(iPoint), extAverageNu); visc_numerics->SetScalarVarGradient(nodes->GetGradient(iPoint), nodes->GetGradient(iPoint)); @@ -1250,7 +1243,8 @@ void CTurbSASolver::BC_Inlet_Turbo(CGeometry *geometry, CSolver **solver_contain FluidModel->SetTDState_Prho(pressure, rho); su2double muLam = FluidModel->GetLaminarViscosity(); - su2double nu_tilde = Factor_nu_Inf*muLam/rho; + su2double nu_tilde[MAXNVAR] = {0.0}; + nu_tilde[0] = Factor_nu_Inf*muLam/rho; /*--- Loop over all the vertices on this boundary marker ---*/ @@ -1286,9 +1280,7 @@ void CTurbSASolver::BC_Inlet_Turbo(CGeometry *geometry, CSolver **solver_contain /*--- Set the turbulent variable states (prescribed for an inflow) ---*/ - nu_tilde_inturb[0] = nu_tilde; - - conv_numerics->SetScalarVar(nodes->GetSolution(iPoint), nu_tilde_inturb); + conv_numerics->SetScalarVar(nodes->GetSolution(iPoint), nu_tilde); if (dynamic_grid) conv_numerics->SetGridVel(geometry->nodes->GetGridVel(iPoint), @@ -1318,7 +1310,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_inturb); + visc_numerics->SetScalarVar(nodes->GetSolution(iPoint), nu_tilde); visc_numerics->SetScalarVarGradient(nodes->GetGradient(iPoint), nodes->GetGradient(iPoint)); @@ -1409,9 +1401,9 @@ void CTurbSASolver::SetTurbVars_WF(CGeometry *geometry, CSolver **solver_contain if (counter > max_iter) break; } - nu_tilde_WF[0] = nu_til; - - nodes->SetSolution_Old(iPoint_Neighbor, nu_tilde_WF); + su2double nuTil[MAXNVAR] = {0.0}; + nuTil[0] = nu_til; + nodes->SetSolution_Old(iPoint_Neighbor, nuTil); LinSysRes.SetBlock_Zero(iPoint_Neighbor); /*--- includes 1 in the diagonal ---*/ @@ -1646,22 +1638,31 @@ void CTurbSASolver::SetLangevinSourceTerms(CConfig *config, CGeometry* geometry) const su2double threshold = 0.9; SU2_OMP_FOR_DYN(omp_chunk_size) - for (auto iPoint = 0ul; iPoint < nPointDomain; iPoint++){ - const auto coord = geometry->nodes->GetCoord(iPoint); - su2double coordSum = (coord[0] + coord[1] + ((nDim == 3) ? coord[2] : 0.0)) * 1e6; - unsigned long coordHash = std::nearbyint(coordSum); - for (auto iDim = 0u; iDim < nDim; iDim++){ - unsigned long seed = RandomToolbox::GetSeed(coordHash, iDim+1, timeIter+1); - std::mt19937 gen(seed); + for (unsigned long iPoint = 0; iPoint < nPointDomain; iPoint++){ + unsigned long iPointGlobal = geometry->nodes->GetGlobalIndex(iPoint); + for (unsigned short iDim = 0; iDim < nDim; iDim++){ su2double lesSensor = nodes->GetLES_Mode(iPoint); - su2double rnd = 0.0; - if (lesSensor > threshold) rnd = RandomToolbox::GetRandomNormal(gen); + su2double rnd = (lesSensor>threshold) ? RandomToolbox::GetNormal(iPointGlobal, iDim, timeIter) : 0.0; nodes->SetLangevinSourceTermsOld(iPoint, iDim, rnd); nodes->SetLangevinSourceTerms(iPoint, iDim, rnd); } } END_SU2_OMP_FOR + SU2_OMP_FOR_DYN(omp_chunk_size) + 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 (config->GetMarker_All_KindBC(iMarker) != SEND_RECEIVE) { + for (unsigned short iDim = 0; iDim < nDim; iDim++) { + nodes->SetLangevinSourceTermsOld(iPoint, iDim, 1e3); + nodes->SetLangevinSourceTerms(iPoint, iDim, 0.0); + } + } + } + } + END_SU2_OMP_FOR + } void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geometry) { @@ -1682,7 +1683,7 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet for (unsigned short iDim = 0; iDim < nDim; iDim++) { for (unsigned short iter = 0; iter < maxIter; iter++) { - + /*--- MPI communication. ---*/ InitiateComms(geometry, config, MPI_QUANTITIES::STOCH_SOURCE_LANG); @@ -1694,7 +1695,8 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet SU2_OMP_FOR_DYN(omp_chunk_size) for (unsigned long iPoint = 0; iPoint < nPointDomain; iPoint++) { const su2double lesSensor = nodes->GetLES_Mode(iPoint); - if (lesSensor < threshold) continue; + su2double source_i_old = nodes->GetLangevinSourceTermsOld(iPoint, iDim); + if (lesSensor < threshold || source_i_old > 3.0*sourceLim ) continue; local_nPointLES += 1; const su2double DES_LengthScale = nodes->GetDES_LengthScale(iPoint); su2double maxDelta = DES_LengthScale / constDES; @@ -1703,7 +1705,6 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet 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); /*--- Assemble system matrix. ---*/ @@ -1737,9 +1738,9 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet 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(&local_nPointLES, &global_nPointLES, 1, MPI_UNSIGNED_LONG, MPI_SUM, SU2_MPI::GetComm()); SU2_MPI::Allreduce(&localResNorm, &globalResNorm, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); - globalResNorm = sqrt(globalResNorm / global_nPointLES); + globalResNorm = (global_nPointLES==0) ? 0.0 : sqrt(globalResNorm / global_nPointLES); if (rank == MASTER_NODE) { if (iter == 0) { @@ -1779,9 +1780,9 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet su2double mean_check_notSmoothed = 0.0; for (unsigned long iPoint = 0; iPoint < nPointDomain; iPoint++) { const su2double lesSensor = nodes->GetLES_Mode(iPoint); - if (lesSensor < threshold) continue; - su2double source = nodes->GetLangevinSourceTerms(iPoint, iDim); su2double source_notSmoothed = nodes->GetLangevinSourceTermsOld(iPoint, iDim); + if (lesSensor < threshold || source_notSmoothed > 3.0*sourceLim) continue; + su2double source = nodes->GetLangevinSourceTerms(iPoint, iDim); mean_check_old += source; var_check_old += source * source; mean_check_notSmoothed += source_notSmoothed; @@ -1851,10 +1852,24 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet 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 << "Mean of stochastic source term in Langevin equations: " << endl; + cout << " Uncorrelated --> " << mean_check_notSmoothed_G << "." << endl; + cout << " Smoothed before scaling --> " << mean_check_old_G << "." << endl; + cout << " Smoothed after scaling --> " << mean_check_new_G << "." << endl; + cout << "Variance of stochastic source term in Langevin equations: " << endl; + cout << " Uncorrelated --> " << var_check_notSmoothed_G << "." << endl; + cout << " Smoothed before scaling --> " << var_check_old_G << "." << endl; + cout << " Smoothed after scaling --> " << var_check_new_G << "." << endl; cout << endl; } + for (unsigned long iPoint = 0; iPoint < nPointDomain; iPoint++) { + const su2double lesSensor = nodes->GetLES_Mode(iPoint); + su2double source_notSmoothed = nodes->GetLangevinSourceTermsOld(iPoint, iDim); + su2double source = nodes->GetLangevinSourceTerms(iPoint, iDim); + if (lesSensor < threshold || source_notSmoothed > 3.0*sourceLim) continue; + source -= mean_check_new_G; + nodes->SetLangevinSourceTerms(iPoint, iDim, source); + } } break; diff --git a/SU2_CFD/src/variables/CIncNSVariable.cpp b/SU2_CFD/src/variables/CIncNSVariable.cpp index c2b019aa47f7..fd2cb97c60bc 100644 --- a/SU2_CFD/src/variables/CIncNSVariable.cpp +++ b/SU2_CFD/src/variables/CIncNSVariable.cpp @@ -37,7 +37,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); + lesMode.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 31d40c355f2d..3ae287e9d4fc 100644 --- a/SU2_CFD/src/variables/CNEMONSVariable.cpp +++ b/SU2_CFD/src/variables/CNEMONSVariable.cpp @@ -70,7 +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); + lesMode.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 81aab2e70e96..951c1c0cff8b 100644 --- a/SU2_CFD/src/variables/CNSVariable.cpp +++ b/SU2_CFD/src/variables/CNSVariable.cpp @@ -39,7 +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); + lesMode.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 70651696ed3a..8f107936725e 100644 --- a/SU2_CFD/src/variables/CTurbSAVariable.cpp +++ b/SU2_CFD/src/variables/CTurbSAVariable.cpp @@ -39,7 +39,7 @@ 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 short iVar = 1; iVar < nVar; iVar++) { + for (unsigned long iVar = 1; iVar < nVar; iVar++) { Solution_Old(iPoint, iVar) = Solution(iPoint, iVar) = 0.0; } } @@ -57,10 +57,10 @@ CTurbSAVariable::CTurbSAVariable(su2double val_nu_tilde, su2double val_muT, unsi } DES_LengthScale.resize(nPoint) = su2double(0.0); - LES_Mode.resize(nPoint) = su2double(0.0); + lesMode.resize(nPoint) = su2double(0.0); Vortex_Tilting.resize(nPoint); stochSource.resize(nPoint, nDim) = su2double(0.0); - stochSource_old.resize(nPoint, nDim) = su2double(0.0); + stochSourceOld.resize(nPoint, nDim) = su2double(0.0); besselIntegral.resize(nPoint); } diff --git a/config_template.cfg b/config_template.cfg index 529487563137..c04f48910b79 100644 --- a/config_template.cfg +++ b/config_template.cfg @@ -1670,12 +1670,9 @@ SMOOTH_GEOMETRY= 0 % % Convective numerical method (JST, JST_KE, JST_MAT, LAX-FRIEDRICH, ROE, AUSM, % AUSMPLUSUP, AUSMPLUSUP2, AUSMPLUSM, HLLC, TURKEL_PREC, -% SW, MSW, FDS, SLAU, SLAU2, L2ROE, LMROE) +% SW, MSW, FDS, SLAU, SLAU2, L2ROE, LMROE, LD2) CONV_NUM_METHOD_FLOW= ROE % -% Option to employ the Low-Dissipation Low-Dispersion (LD2) scheme for incompressible flows (NO, YES) -LD2_OPTION= NO -% % Roe Low Dissipation function for Hybrid RANS/LES simulations (FD, NTS, NTS_DUCROS) ROE_LOW_DISSIPATION= FD % From 84c42d25e95c6dd0b25a849388da0d8e34945fc4 Mon Sep 17 00:00:00 2001 From: paan882 Date: Fri, 30 Jan 2026 14:42:45 +0100 Subject: [PATCH 29/30] Minor fixes - Redefine default values for backscatter parameters. - Introduce RANS-LES blending factor for source terms in Langevin equations. - Fix bug in MPI communication of slope limiters between periodic faces. --- Common/src/CConfig.cpp | 4 +-- .../numerics/turbulent/turb_sources.hpp | 5 +++ SU2_CFD/src/solvers/CSolver.cpp | 4 +-- SU2_CFD/src/solvers/CTurbSASolver.cpp | 36 +++++++++---------- 4 files changed, 27 insertions(+), 22 deletions(-) diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index f9673d4ae153..0148eb90dbd6 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -2961,7 +2961,7 @@ void CConfig::SetConfig_Options() { addDoubleOption("DES_CONST", Const_DES, 0.65); /* DESCRIPTION: SBS lengthscale coefficient */ - addDoubleOption("SBS_LENGTHSCALE_COEFF", SBS_Cdelta, 0.1); + addDoubleOption("SBS_LENGTHSCALE_COEFF", SBS_Cdelta, 0.02); /* DESCRIPTION: Maximum number of smoothing iterations for SBS model. */ addUnsignedShortOption("SBS_MAX_ITER_SMOOTH", SBS_maxIterSmooth, 100); @@ -2982,7 +2982,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, true); + addBoolOption("STOCH_SOURCE_NU", stochSourceNu, false); /* DESCRIPTION: Enable diagnostics of the stochastic source term in Langevin equations. */ addBoolOption("STOCH_SOURCE_DIAGNOSTICS", stochSourceDiagnostics, false); diff --git a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp index a662a315856a..28961c0d2704 100644 --- a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp +++ b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp @@ -122,12 +122,17 @@ class CSourceBase_TurbSA : public CNumerics { const su2double& nue = ScalarVar_i[0]; const auto& density = V_i[idx.Density()]; const su2double threshold = 0.9; + const su2double limiterRANS = 0.1; + const su2double timeStepFrac = 0.2; const su2double nut = max(nue*var.fv1, 1e-10); const su2double delta = lengthScale/DES_const; if (delta > 1e-10) { su2double tTurb = ct*pow(delta, 2)/nut; + su2double blendingFactor = DES_const*DES_const/ct*(1.0-lesMode_i) + lesMode_i; + tTurb *= blendingFactor; + tTurb = max(timeStep*timeStepFrac, min(tTurb, timeStep/timeStepFrac)); su2double tRat = timeStep / tTurb; su2double corrFac = 1.0; diff --git a/SU2_CFD/src/solvers/CSolver.cpp b/SU2_CFD/src/solvers/CSolver.cpp index f40b9453d41f..d4a23d6de3f3 100644 --- a/SU2_CFD/src/solvers/CSolver.cpp +++ b/SU2_CFD/src/solvers/CSolver.cpp @@ -363,8 +363,8 @@ void CSolver::InitiatePeriodicComms(CGeometry *geometry, auto *Diff = new su2double[nVar]; auto *Und_Lapl = new su2double[nVar]; - auto *Sol_Min = new su2double[nPrimVarGrad]; - auto *Sol_Max = new su2double[nPrimVarGrad]; + auto *Sol_Min = new su2double[nVar]; + auto *Sol_Max = new su2double[nVar]; auto *rotPrim_i = new su2double[nPrimVar]; auto *rotPrim_j = new su2double[nPrimVar]; diff --git a/SU2_CFD/src/solvers/CTurbSASolver.cpp b/SU2_CFD/src/solvers/CTurbSASolver.cpp index 6bb898f89e5d..57a39f00e7b3 100644 --- a/SU2_CFD/src/solvers/CTurbSASolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp @@ -61,7 +61,7 @@ CTurbSASolver::CTurbSASolver(CGeometry *geometry, CConfig *config, unsigned shor if (nDim == 3) { nVar += nDim; nVarGrad = nPrimVar = nVar; } else { - SU2_MPI::Error("Stochastic Backscatter Model available for 3D flows only.", CURRENT_FUNCTION);; + SU2_MPI::Error("Stochastic Backscatter Model available for 3D flows only.", CURRENT_FUNCTION); } } @@ -1829,15 +1829,24 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet var_check_new += source * source; nodes->SetLangevinSourceTerms(iPoint, iDim, source); } + su2double mean_check_new_G = 0.0; + SU2_MPI::Allreduce(&mean_check_new, &mean_check_new_G, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); + mean_check_new_G /= global_nPointLES; + for (unsigned long iPoint = 0; iPoint < nPointDomain; iPoint++) { + const su2double lesSensor = nodes->GetLES_Mode(iPoint); + su2double source_notSmoothed = nodes->GetLangevinSourceTermsOld(iPoint, iDim); + su2double source = nodes->GetLangevinSourceTerms(iPoint, iDim); + if (lesSensor < threshold || source_notSmoothed > 3.0*sourceLim) continue; + source -= mean_check_new_G; + nodes->SetLangevinSourceTerms(iPoint, iDim, source); + } 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()); @@ -1845,7 +1854,6 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet 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; @@ -1853,23 +1861,15 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet var_check_notSmoothed_G -= mean_check_notSmoothed_G * mean_check_notSmoothed_G; if (rank == MASTER_NODE) { cout << "Mean of stochastic source term in Langevin equations: " << endl; - cout << " Uncorrelated --> " << mean_check_notSmoothed_G << "." << endl; - cout << " Smoothed before scaling --> " << mean_check_old_G << "." << endl; - cout << " Smoothed after scaling --> " << mean_check_new_G << "." << endl; + cout << " Uncorrelated --> " << mean_check_notSmoothed_G << endl; + cout << " Smoothed before scaling --> " << mean_check_old_G << endl; + cout << " Smoothed after scaling --> " << mean_check_new_G << " (subtracted from stochastic field to guarantee zero mean)" << endl; cout << "Variance of stochastic source term in Langevin equations: " << endl; - cout << " Uncorrelated --> " << var_check_notSmoothed_G << "." << endl; - cout << " Smoothed before scaling --> " << var_check_old_G << "." << endl; - cout << " Smoothed after scaling --> " << var_check_new_G << "." << endl; + cout << " Uncorrelated --> " << var_check_notSmoothed_G << endl; + cout << " Smoothed before scaling --> " << var_check_old_G << endl; + cout << " Smoothed after scaling --> " << var_check_new_G << endl; cout << endl; } - for (unsigned long iPoint = 0; iPoint < nPointDomain; iPoint++) { - const su2double lesSensor = nodes->GetLES_Mode(iPoint); - su2double source_notSmoothed = nodes->GetLangevinSourceTermsOld(iPoint, iDim); - su2double source = nodes->GetLangevinSourceTerms(iPoint, iDim); - if (lesSensor < threshold || source_notSmoothed > 3.0*sourceLim) continue; - source -= mean_check_new_G; - nodes->SetLangevinSourceTerms(iPoint, iDim, source); - } } break; From dc5189daedd562b1a6b77c2622dd8fbb9ec10100 Mon Sep 17 00:00:00 2001 From: paan882 Date: Fri, 13 Feb 2026 17:25:20 +0100 Subject: [PATCH 30/30] Add new features - Add the option to apply the backscatter model only in a bounded box. - Add the option to enforce backscatter where shielding function exceeds user-defined value. - Correct bug in LD2 implementation. - Make style consistent and update configuration template. --- Common/include/CConfig.hpp | 21 ++++++++ Common/src/CConfig.cpp | 31 +++++++++-- SU2_CFD/include/numerics/CNumerics.hpp | 8 +-- .../numerics/turbulent/turb_sources.hpp | 24 +++++---- .../include/solvers/CFVMFlowSolverBase.inl | 3 +- .../src/numerics/flow/convection/centered.cpp | 52 +++++++++---------- SU2_CFD/src/output/CFlowOutput.cpp | 4 +- SU2_CFD/src/solvers/CTurbSASolver.cpp | 36 ++++++++----- config_template.cfg | 10 +++- 9 files changed, 126 insertions(+), 63 deletions(-) diff --git a/Common/include/CConfig.hpp b/Common/include/CConfig.hpp index 46a557e17c2b..d2435111601d 100644 --- a/Common/include/CConfig.hpp +++ b/Common/include/CConfig.hpp @@ -1104,6 +1104,9 @@ class CConfig { 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). */ + bool StochBackscatterInBox; /*!< \brief Option for activating the Stochastic Backscatter Model only in a bounded box. */ + su2double StochBackscatterBoxBounds[6]; /*!< \brief Bounds of the box where the Stochastic Backscatter Model is active. */ + su2double stochFdThreshold; /*!< \brief Shielding function lower threshold for application of 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. */ @@ -9677,6 +9680,24 @@ class CConfig { */ su2double GetSBS_Cmag(void) const { return SBS_Cmag; } + /*! + * \brief Get if the Stochastic Backscatter Model must be applied only in a bounded box. + * \return True if the model is applied in a bounded box. + */ + bool GetStochBackscatterInBox(void) const { return StochBackscatterInBox; } + + /*! + * \brief Get the bounds of the box where the Stochastic Backscatter Model is active. + * \return Bounds of the box where the model is active. + */ + const su2double* GetStochBackscatterBoxBounds(void) const { return StochBackscatterBoxBounds; } + + /*! + * \brief Get shielding function lower threshold for application of Stochastic Backscatter Model. + * \return Shielding function threshold for application of the model. + */ + su2double GetStochFdThreshold(void) const { return stochFdThreshold; } + /*! * \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 0148eb90dbd6..3b1304c73e66 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -2982,14 +2982,23 @@ 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("SBS_SOURCE_NU_EQUATION", stochSourceNu, false); - /* DESCRIPTION: Enable diagnostics of the stochastic source term in Langevin equations. */ - addBoolOption("STOCH_SOURCE_DIAGNOSTICS", stochSourceDiagnostics, false); + /* DESCRIPTION: Enable diagnostics of the stochastic source term in Langevin equation. */ + addBoolOption("SBS_SOURCE_DIAGNOSTICS", stochSourceDiagnostics, false); - /* DESCRIPTION: Relaxation factor for the stochastic source term (Stochastic Backscatter Model). */ + /* DESCRIPTION: Relaxation factor for the stochastic source term (Stochastic Backscatter Model) */ addDoubleOption("SBS_RELAXATION_FACTOR", stochSourceRelax, 0.0); + /* DESCRIPTION: Apply Stochastic Backscatter Model only in a bounded box */ + addBoolOption("SBS_IN_BOX", StochBackscatterInBox, false); + + /* DESCRIPTION: Specify extents of box where Stochastic Backscatter Model is active */ + addDoubleArrayOption("SBS_BOX_BOUNDS", 6, false, StochBackscatterBoxBounds); + + /* DESCRIPTION: Shielding function lower threshold for application of Stochastic Backscatter Model */ + addDoubleOption("SBS_FD_LOWER_THRESHOLD", stochFdThreshold, 0.9); + /* DESCRIPTION: Filter width for LES (if negative, it is computed based on the local cell size) */ addDoubleOption("LES_FILTER_WIDTH", LES_FilterWidth, -1.0); @@ -6578,6 +6587,20 @@ void CConfig::SetOutput(SU2_COMPONENT val_software, unsigned short val_izone) { cout << "Relaxation factor for stochastic source term: " << stochSourceRelax << endl; else cout << "No relaxation factor for stochastic source term." << endl; + if (StochBackscatterInBox) { + cout << "Stochastic Backscatter Model activated only in a bounded box." << endl; + cout << "Box bounds: " << endl; + cout << " X: " << setw(10) << fixed << setprecision(4) << StochBackscatterBoxBounds[0] << " , " + << setw(10) << fixed << setprecision(4) << StochBackscatterBoxBounds[1] << endl; + cout << " Y: " << setw(10) << fixed << setprecision(4) << StochBackscatterBoxBounds[2] << " , " + << setw(10) << fixed << setprecision(4) << StochBackscatterBoxBounds[3] << endl; + cout << " Z: " << setw(10) << fixed << setprecision(4) << StochBackscatterBoxBounds[4] << " , " + << setw(10) << fixed << setprecision(4) << StochBackscatterBoxBounds[5] << endl; + } else { + cout << "Stochastic Backscatter Model activated in the whole computational domain." << endl; + } + if (Kind_HybridRANSLES != SA_DES) + cout << "Stochastic source terms suppressed where the shielding function is lower than: " << setw(5) << setprecision(3) << stochFdThreshold << endl; } else { cout << "OFF" << endl; } diff --git a/SU2_CFD/include/numerics/CNumerics.hpp b/SU2_CFD/include/numerics/CNumerics.hpp index 0ec06ced3662..d5c11569698c 100644 --- a/SU2_CFD/include/numerics/CNumerics.hpp +++ b/SU2_CFD/include/numerics/CNumerics.hpp @@ -667,12 +667,14 @@ class CNumerics { /* --- Calculate stochastic tensor --- */ + su2double stochLim = 3.0; + stochReynStress[0][0] = 0.0; stochReynStress[1][1] = 0.0; stochReynStress[2][2] = 0.0; - stochReynStress[0][1] = Cmag * density * tke * rndVec[2]; - stochReynStress[0][2] = - Cmag * density * tke * rndVec[1]; - stochReynStress[1][2] = Cmag * density * tke * rndVec[0]; + stochReynStress[0][1] = Cmag * density * tke * max(-stochLim, min(stochLim, rndVec[2])); + stochReynStress[0][2] = - Cmag * density * tke * max(-stochLim, min(stochLim, rndVec[1])); + stochReynStress[1][2] = Cmag * density * tke * max(-stochLim, min(stochLim, rndVec[0])); stochReynStress[1][0] = - stochReynStress[1][0]; stochReynStress[2][0] = - stochReynStress[2][0]; stochReynStress[2][1] = - stochReynStress[2][1]; diff --git a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp index 28961c0d2704..e9462a572741 100644 --- a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp +++ b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp @@ -117,11 +117,11 @@ class CSourceBase_TurbSA : public CNumerics { */ inline void ResidualStochEquations(su2double timeStep, const su2double ct, su2double lengthScale, su2double DES_const, - const CSAVariables& var, TIME_MARCHING time_marching) { + const CSAVariables& var, TIME_MARCHING time_marching, + su2double threshold) { const su2double& nue = ScalarVar_i[0]; const auto& density = V_i[idx.Density()]; - const su2double threshold = 0.9; const su2double limiterRANS = 0.1; const su2double timeStepFrac = 0.2; const su2double nut = max(nue*var.fv1, 1e-10); @@ -160,12 +160,11 @@ class CSourceBase_TurbSA : public CNumerics { /*! * \brief Include stochastic source term in the Spalart-Allmaras turbulence model equation (Stochastic Backscatter Model). */ - inline void AddStochSource(CSAVariables& var, const su2double Cmag, su2double& prod) { + inline void AddStochSource(CSAVariables& var, const su2double Cmag, su2double threshold, su2double& prod) { su2double nut = ScalarVar_i[0] * var.fv1; su2double tke = 0.0; - const su2double threshold = 0.9; - const su2double limiter = 10.0; + const su2double limiter = 5.0; if (lesMode_i > threshold) tke = nut * StrainMag_i; su2double R12 = Cmag * tke * ScalarVar_i[3]; @@ -174,11 +173,14 @@ class CSourceBase_TurbSA : public CNumerics { su2double RGradU = R12*Vorticity_i[2] - R13*Vorticity_i[1] + R23*Vorticity_i[0]; - su2double stoch_prod_k = - RGradU; - su2double prod_k = nut * StrainMag_i * StrainMag_i; - su2double stoch_prod_nut = min(limiter, max(-limiter, stoch_prod_k/max(prod_k,1e-10))); + su2double stochProdK = - RGradU; + su2double Ji_3 = pow(var.Ji, 3); + su2double Dfv1Dnut = 3.0 * var.fv1 * var.cv1_3 / (var.cv1_3 + Ji_3); + su2double fac = 1.0 / (var.fv1 + ScalarVar_i[0]*Dfv1Dnut); + su2double stochProdNut = stochProdK * dist_i*dist_i/(2.0*ScalarVar_i[0]) * fac; + stochProdNut = max(-limiter*prod, min(limiter*prod, stochProdNut)); - prod *= (1.0 + stoch_prod_nut); + prod += stochProdNut; } @@ -327,7 +329,7 @@ class CSourceBase_TurbSA : public CNumerics { if (config->GetStochastic_Backscatter() && config->GetStochSourceNu()) { su2double intensityCoeff = ComputeStochRelaxFactor(config); - AddStochSource(var, intensityCoeff, Production); + AddStochSource(var, intensityCoeff, config->GetStochFdThreshold(), Production); } Residual[0] = (Production - Destruction) * Volume; @@ -342,7 +344,7 @@ class CSourceBase_TurbSA : public CNumerics { const su2double DES_const = config->GetConst_DES(); const su2double ctau = config->GetSBS_Ctau(); ResidualStochEquations(config->GetDelta_UnstTime(), ctau, dist_i, DES_const, - var, config->GetTime_Marching()); + var, config->GetTime_Marching(), config->GetStochFdThreshold()); } } diff --git a/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl b/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl index 92f35c463f6e..5129e9b8b8ec 100644 --- a/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl +++ b/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl @@ -478,9 +478,8 @@ void CFVMFlowSolverBase::Viscous_Residual_impl(unsigned long iEdge, CGeome su2double DES_length_j = turbNodes->GetDES_LengthScale(jPoint); su2double lesMode_i = (DES_length_i > 1e-10) ? turbNodes->GetLES_Mode(iPoint) : 0.0; su2double lesMode_j = (DES_length_j > 1e-10) ? turbNodes->GetLES_Mode(jPoint) : 0.0; - const su2double threshold = 0.9; su2double tke_i = 0.0, tke_j = 0.0; - if (max(lesMode_i, lesMode_j) > threshold) { + if (max(lesMode_i, lesMode_j) > config->GetStochFdThreshold()) { su2double eddyVisc_i = turbNodes->GetmuT(iPoint) / nodes->GetDensity(iPoint); su2double eddyVisc_j = turbNodes->GetmuT(jPoint) / nodes->GetDensity(jPoint); su2double strainMag_i = nodes->GetStrainMag(iPoint); diff --git a/SU2_CFD/src/numerics/flow/convection/centered.cpp b/SU2_CFD/src/numerics/flow/convection/centered.cpp index fb3c8ac7b4c4..59d904c56050 100644 --- a/SU2_CFD/src/numerics/flow/convection/centered.cpp +++ b/SU2_CFD/src/numerics/flow/convection/centered.cpp @@ -328,7 +328,26 @@ 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]; + } + + 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]; + ProjVelocity_i += Velocity_i[iDim]*Normal[iDim]; + ProjVelocity_j += Velocity_j[iDim]*Normal[iDim]; + Area += Normal[iDim]*Normal[iDim]; } + Area = sqrt(Area); + + /*--- Compute mean values of the variables ---*/ + + MeanDensity = 0.5*(DensityInc_i + DensityInc_j); + MeanPressure = 0.5*(Pressure_i + Pressure_j); + MeanBetaInc2 = 0.5*(BetaInc2_i + BetaInc2_j); + MeanEnthalpy = 0.5*(Enthalpy_i + Enthalpy_j); + MeanCp = 0.5*(Cp_i + Cp_j); + MeanTemperature = 0.5*(Temperature_i + Temperature_j); if (LD2_Scheme) { su2double d_ij[3] = {0.0}; @@ -347,36 +366,15 @@ CNumerics::ResidualType<> CCentJSTInc_Flow::ComputeResidual(const CConfig* confi } } for (iDim = 0; iDim < nDim; iDim++) { - Pressure_i += alpha_LD2 * pressGrad_i[iDim] * d_ij[iDim]; - Pressure_j += alpha_LD2 * pressGrad_j[iDim] * d_ij[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]); + MeanVelocity[iDim] += 0.5 * alpha_LD2 * ((velGrad_i[iDim][0] - velGrad_j[iDim][0]) * d_ij[0] + + (velGrad_i[iDim][1] - velGrad_j[iDim][1]) * d_ij[1] + + (velGrad_i[iDim][2] - velGrad_j[iDim][2]) * d_ij[2]); } + MeanPressure += 0.5 * alpha_LD2 * ((pressGrad_i[0] - pressGrad_j[0]) * d_ij[0] + + (pressGrad_i[1] - pressGrad_j[1]) * d_ij[1] + + (pressGrad_i[2] - pressGrad_j[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]; - ProjVelocity_i += Velocity_i[iDim]*Normal[iDim]; - ProjVelocity_j += Velocity_j[iDim]*Normal[iDim]; - Area += Normal[iDim]*Normal[iDim]; - } - Area = sqrt(Area); - - /*--- Compute mean values of the variables ---*/ - - MeanDensity = 0.5*(DensityInc_i + DensityInc_j); - MeanPressure = 0.5*(Pressure_i + Pressure_j); - MeanBetaInc2 = 0.5*(BetaInc2_i + BetaInc2_j); - MeanEnthalpy = 0.5*(Enthalpy_i + Enthalpy_j); - MeanCp = 0.5*(Cp_i + Cp_j); - MeanTemperature = 0.5*(Temperature_i + Temperature_j); - /*--- We need the derivative of the equation of state to build the preconditioning matrix. For now, the only option is the ideal gas law, but in the future, dRhodT should be in the fluid model. ---*/ diff --git a/SU2_CFD/src/output/CFlowOutput.cpp b/SU2_CFD/src/output/CFlowOutput.cpp index e8b7abd04f3f..81573093d745 100644 --- a/SU2_CFD/src/output/CFlowOutput.cpp +++ b/SU2_CFD/src/output/CFlowOutput.cpp @@ -1702,7 +1702,7 @@ void CFlowOutput::LoadVolumeDataScalar(const CConfig* config, const CSolver* con const su2double DES_lengthscale = max(Node_Flow->GetDES_LengthScale(iPoint), 1e-10); const su2double lesSensor = Node_Flow->GetLES_Mode(iPoint); const su2double mag = config->GetSBS_Cmag(); - const su2double threshold = 0.9; + const su2double threshold = config->GetStochFdThreshold(); const auto VelocityGradient = Node_Flow->GetVelocityGradient(iPoint); su2double strainMag2 = 0.0; for (unsigned long iDim = 0; iDim < nDim; iDim++) { @@ -1753,7 +1753,7 @@ void CFlowOutput::LoadVolumeDataScalar(const CConfig* config, const CSolver* con const su2double DES_lengthscale = max(Node_Flow->GetDES_LengthScale(iPoint), 1e-10); const su2double lesSensor = Node_Flow->GetLES_Mode(iPoint); const su2double mag = config->GetSBS_Cmag(); - const su2double threshold = 0.9; + const su2double threshold = config->GetStochFdThreshold(); su2double tke_estim = 0.0; su2double strainMag2 = 0.0; for (unsigned long iDim = 0; iDim < nDim; iDim++) { diff --git a/SU2_CFD/src/solvers/CTurbSASolver.cpp b/SU2_CFD/src/solvers/CTurbSASolver.cpp index 57a39f00e7b3..b197f8f8a0e1 100644 --- a/SU2_CFD/src/solvers/CTurbSASolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp @@ -1635,16 +1635,32 @@ void CTurbSASolver::SetDES_LengthScale(CSolver **solver, CGeometry *geometry, CC void CTurbSASolver::SetLangevinSourceTerms(CConfig *config, CGeometry* geometry) { unsigned long timeIter = config->GetTimeIter(); - const su2double threshold = 0.9; + const su2double threshold = config->GetStochFdThreshold(); + const su2double dummySource = 1e3; + bool stochBackscatterInBox = config->GetStochBackscatterInBox(); + bool insideBox = true; SU2_OMP_FOR_DYN(omp_chunk_size) for (unsigned long iPoint = 0; iPoint < nPointDomain; iPoint++){ unsigned long iPointGlobal = geometry->nodes->GetGlobalIndex(iPoint); for (unsigned short iDim = 0; iDim < nDim; iDim++){ su2double lesSensor = nodes->GetLES_Mode(iPoint); - su2double rnd = (lesSensor>threshold) ? RandomToolbox::GetNormal(iPointGlobal, iDim, timeIter) : 0.0; - nodes->SetLangevinSourceTermsOld(iPoint, iDim, rnd); - nodes->SetLangevinSourceTerms(iPoint, iDim, rnd); + const auto coord = geometry->nodes->GetCoord(iPoint); + if (stochBackscatterInBox) { + auto sbsBoxBounds = config->GetStochBackscatterBoxBounds(); + bool insideBoxX = (coord[0]>=sbsBoxBounds[0] && coord[0]<=sbsBoxBounds[1]); + bool insideBoxY = (coord[1]>=sbsBoxBounds[2] && coord[1]<=sbsBoxBounds[3]); + bool insideBoxZ = (coord[2]>=sbsBoxBounds[4] && coord[2]<=sbsBoxBounds[5]); + insideBox = (insideBoxX && insideBoxY && insideBoxZ); + } + if (lesSensor>threshold && insideBox) { + su2double rnd = RandomToolbox::GetNormal(iPointGlobal, iDim, timeIter); + nodes->SetLangevinSourceTermsOld(iPoint, iDim, rnd); + nodes->SetLangevinSourceTerms(iPoint, iDim, rnd); + } else { + nodes->SetLangevinSourceTermsOld(iPoint, iDim, dummySource); + nodes->SetLangevinSourceTerms(iPoint, iDim, 0.0); + } } } END_SU2_OMP_FOR @@ -1655,7 +1671,7 @@ void CTurbSASolver::SetLangevinSourceTerms(CConfig *config, CGeometry* geometry) unsigned long iPoint = geometry->vertex[iMarker][iVertex]->GetNode(); if (config->GetMarker_All_KindBC(iMarker) != SEND_RECEIVE) { for (unsigned short iDim = 0; iDim < nDim; iDim++) { - nodes->SetLangevinSourceTermsOld(iPoint, iDim, 1e3); + nodes->SetLangevinSourceTermsOld(iPoint, iDim, dummySource); nodes->SetLangevinSourceTerms(iPoint, iDim, 0.0); } } @@ -1676,7 +1692,6 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet const su2double omega = 0.8; unsigned long timeIter = config->GetTimeIter(); unsigned long restartIter = config->GetRestart_Iter(); - const su2double threshold = 0.9; /*--- Start SOR algorithm for the Laplacian smoothing. ---*/ @@ -1694,9 +1709,8 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet SU2_OMP_FOR_DYN(omp_chunk_size) for (unsigned long iPoint = 0; iPoint < nPointDomain; iPoint++) { - const su2double lesSensor = nodes->GetLES_Mode(iPoint); su2double source_i_old = nodes->GetLangevinSourceTermsOld(iPoint, iDim); - if (lesSensor < threshold || source_i_old > 3.0*sourceLim ) continue; + if (source_i_old > 3.0*sourceLim) continue; local_nPointLES += 1; const su2double DES_LengthScale = nodes->GetDES_LengthScale(iPoint); su2double maxDelta = DES_LengthScale / constDES; @@ -1779,9 +1793,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); su2double source_notSmoothed = nodes->GetLangevinSourceTermsOld(iPoint, iDim); - if (lesSensor < threshold || source_notSmoothed > 3.0*sourceLim) continue; + if (source_notSmoothed > 3.0*sourceLim) continue; su2double source = nodes->GetLangevinSourceTerms(iPoint, iDim); mean_check_old += source; var_check_old += source * source; @@ -1833,10 +1846,9 @@ void CTurbSASolver::SmoothLangevinSourceTerms(CConfig* config, CGeometry* geomet SU2_MPI::Allreduce(&mean_check_new, &mean_check_new_G, 1, MPI_DOUBLE, MPI_SUM, SU2_MPI::GetComm()); mean_check_new_G /= global_nPointLES; for (unsigned long iPoint = 0; iPoint < nPointDomain; iPoint++) { - const su2double lesSensor = nodes->GetLES_Mode(iPoint); su2double source_notSmoothed = nodes->GetLangevinSourceTermsOld(iPoint, iDim); su2double source = nodes->GetLangevinSourceTerms(iPoint, iDim); - if (lesSensor < threshold || source_notSmoothed > 3.0*sourceLim) continue; + if (source_notSmoothed > 3.0*sourceLim) continue; source -= mean_check_new_G; nodes->SetLangevinSourceTerms(iPoint, iDim, source); } diff --git a/config_template.cfg b/config_template.cfg index c04f48910b79..468ccadcc127 100644 --- a/config_template.cfg +++ b/config_template.cfg @@ -203,10 +203,16 @@ SBS_INTENSITY_COEFF= 1.0 SBS_RELAXATION_FACTOR= 0.0 % % Include stochastic source in turbulence model equation (NO, YES) -STOCH_SOURCE_NU= YES +SBS_SOURCE_NU_EQUATION= YES % % Include diagnostics of the stochastic source term in Langevin equations (NO, YES) -STOCH_SOURCE_DIAGNOSTICS= NO +SBS_SOURCE_DIAGNOSTICS= NO +% +% Apply Stochastic Backscatter Model only in a bounded box (NO, YES) +SBS_IN_BOX= NO +% +% Bounds of the box where the Stochastic Backscatter Model is activated (x_min, x_max, y_min, y_max, z_min, z_max) +SBS_BOX_BOUNDS= (0.0, 0.0, 0.0, 0.0, 0.0, 0.0) % % Enforce LES mode in Hybrid RANS-LES Simulations (NO, YES) ENFORCE_LES= NO