Repository navigation
Dual-time restart is inconsistent with the LM transition model: TRANS_SOL time levels are never initialised (v8.5.0) #2993
Description
Version / platform. SU2 v8.5.0 "Harrier" (release tarball), Windows 11 x64. Reproduced with the official v8.5.0 Windows binary
and with a from-source build (GCC 15 / MSYS2 UCRT64, -Dwith-omp=true -Dwith-mpi=disabled). Same behaviour in develop by reading
the source (see "Where").
Summary. With KIND_TRANS_MODEL= LM (SST-LM here) and TIME_MARCHING= DUAL_TIME_STEPPING-2ND_ORDER, the transition solver's
dual-time history (Solution_time_n, Solution_time_n1) is never set when a run starts or restarts. It keeps its zero allocation
until the end of the first physical step, so the BDF2 term of that step acts on intermittency and Re_theta_t as if both were zero at
n and n-1: a sink of strength 3/(2 dt) over the whole field (1/dt for 1ST_ORDER). The flow and turbulence solvers are handled
correctly (PushSolutionBackInTime pushes them back and reloads the RESTART_ITER-1 file); the transition solver is simply left out.
Consequence: a restarted time-accurate SST-LM run does not continue the un-restarted one, and every (re)start injects a transient.
Where (v8.5.0 sources).
SU2_CFD/include/solvers/CFVMFlowSolverBase.inl,CFVMFlowSolverBase::PushSolutionBackInTime(lines 1120-1167):Set_Solution_time_n()
/_n1()and theTimeIter-1reload are done forFLOW_SOLand, ifrans,TURB_SOLonly.TRANS_SOLdoes not appear.SU2_CFD/include/solvers/CScalarSolver.inl:860CScalarSolver::PushSolutionBackInTimeexists but is called only from
CSpeciesSolver.cpp:674andCSpeciesFlameletSolver.cpp:328.SU2_CFD/src/variables/CTransLMVariable.cpp:31-46: the constructor setsSolutionandSolution_Oldonly (compare
CTurbSAVariable.cpp:50-57, which initialises the time levels whendual_time).SU2_CFD/src/iteration/CFluidIteration.cpp:194: the TRANS time levels are first set bySetDualTime_Solverat the END of step 1.- The BDF residual that consumes the zero levels:
CScalarSolver.inldual-time source(3 U^{n+1} - 4 U^n + U^{n-1}) V / (2 dt).
Minimal reproducer (the "9 + 1 vs 10" test, as in #2707). Any 2-D airfoil SST-LM case; the public one is the E387 transition
tutorial (compressible_flow/Transitional_Airfoil/Langtry_and_Menter/E387). Steps:
- Converge the steady case (
RESTART_SOL= NO), keeprestart_flow.dat; copy it torestart_flow_00000.datandrestart_flow_00001.dat. - Time-accurate config:
TIME_DOMAIN= YES,TIME_MARCHING= DUAL_TIME_STEPPING-2ND_ORDER,TIME_STEP= <c/U/200>,INNER_ITER= 40,
RESTART_SOL= YES,RESTART_ITER= 2,SOLUTION_FILENAME= restart_flow.dat,OUTPUT_WRT_FREQ= 1,WRT_RESTART_COMPACT= NO,
HISTORY_OUTPUT= (ITER, RMS_RES, AERO_COEFF, TIME_DOMAIN); removeITER. - Run A:
TIME_ITER= 12(10 physical steps, continuous). - Run B:
TIME_ITER= 11(9 steps), then restart withRESTART_ITER= 11,TIME_ITER= 12(1 step; reads_00009and_00010). - Compare the history row of Time_Iter 11 (the 10th step) between A and B.
Expected. An exact restart reproduces step 10 to round-off (the flow and SST residuals do).
Actual (our case: 2-D airfoil, SST-LM MENTER_LANGTRY, Re 2e5, M 0.15, Tu 0.1 %, medium C-mesh, dt = c/U/200, 6 OpenMP threads).
At the restarted step the log10 RMS residuals jump and the record never rejoins the continuous run:
| quantity at Time_Iter 11 | continuous (A) | 9 + restart + 1 (B) | difference |
|---|---|---|---|
| log10 rms[LM_1] (intermittency) | -3.5708 | -0.3549 | +3.22 orders |
| log10 rms[LM_2] (Re_theta_t) | -0.5694 | +0.5454 | +1.11 orders |
| log10 rms[k] | +0.45 orders | ||
| log10 rms[Rho] | -0.024 | ||
| CD | -2.6e-4 relative |
The same numbers result with WRT_RESTART_COMPACT= YES. This is the behaviour described in discussion #2615 (LM_1 about -0.24 vs
-3.8 after a restart).
With the patch below (three guarded additions, nothing else changed) the same test gives identical A and B rows to the printed
precision (all differences 0 at 1e-6), and the continuous run itself starts cleanly: log10 rms[LM_1] at step 10 is -6.47 instead of
-3.57, because the stock run also receives the kick at its own first step.
Proposed patch (against v8.5.0; TRANS_SOL treated exactly like TURB_SOL):
--- a/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl
+++ b/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl
@@ PushSolutionBackInTime
+ const bool trans = rans && (config->GetKind_Trans_Model() != TURB_TRANS_MODEL::NONE) &&
+ (solver_container[MESH_0][TRANS_SOL] != nullptr);
for (unsigned short iMesh = 0; iMesh <= config->GetnMGLevels(); iMesh++) {
...
if (rans) { TURB_SOL->Set_Solution_time_n(); TURB_SOL->Set_Solution_time_n1(); }
+ if (trans) {
+ solver_container[iMesh][TRANS_SOL]->GetNodes()->Set_Solution_time_n();
+ solver_container[iMesh][TRANS_SOL]->GetNodes()->Set_Solution_time_n1();
+ }
...
if (restart && DT_STEPPING_2ND) {
FLOW_SOL->LoadRestart(..., TimeIter-1, true);
if (rans) TURB_SOL->LoadRestart(..., TimeIter-1, false);
+ if (trans) solver_container[MESH_0][TRANS_SOL]->LoadRestart(geometry, solver_container, config, TimeIter-1, false);
for (iMesh ...) {
FLOW_SOL->Set_Solution_time_n();
if (rans) TURB_SOL->Set_Solution_time_n();
+ if (trans) solver_container[iMesh][TRANS_SOL]->GetNodes()->Set_Solution_time_n();Optional backstop in CTransLMVariable::CTransLMVariable (as CTurbSAVariable): if (dual_time) { Solution_time_n = Solution_time_n1 = Solution; }.
(An alternative with the same effect would be to call CScalarSolver::PushSolutionBackInTime for the transition solver, the way
the species solvers do.) I'm happy to share the complete unified diff against v8.5.0 or open a PR if that helps.
Related: CTransLMSolver::LoadRestart (v8.5.0 lines 560-561) reads Intermittency_Sep / _Eff from Restart_Data[index + 2]
and [index + 3], i.e. from whatever variables follow the two LM solution variables in the file; Postprocessing recomputes both
anyway. PR #1901 drops these reads; with the patch above the second LoadRestart call would otherwise repeat them, so we ported that
change as well. Steady results are unaffected by either change (two steady SST-LM cases reproduce the stock histories to 1e-7).
Verification done. (a) steady path: two 6000-iteration SST-LM cases identical to the stock binary to 1.2e-7 relative in CD;
(b) the 9 + 1 vs 10 test above, 2 x 2 (stock / patched x compact NO / YES).
Activity