Skip to content

Commit 54ccbeb

Browse files
bigfootedpcarruscaggithub-advanced-security[bot]
authored
Fix multigrid early exit functionality (#2831)
Base early exit on proper residual fix some implicit line agglomeration add more debugging info --------- Co-authored-by: Pedro Gomes <pcarruscag@gmail.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
1 parent 732eaef commit 54ccbeb

27 files changed

Lines changed: 953 additions & 1124 deletions

Common/include/CConfig.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,6 +1147,8 @@ class CConfig {
11471147
/*--- Multigrid options ---*/
11481148
unsigned short nMG_PreSmooth_p{0}, nMG_PostSmooth_p{0}, nMG_CorrecSmooth_p{0};
11491149
unsigned short *MG_PreSmooth_p{nullptr}, *MG_PostSmooth_p{nullptr}, *MG_CorrecSmooth_p{nullptr};
1150+
unsigned short nMG_CflScaling_p{0};
1151+
su2double *MG_CflScaling_p{nullptr};
11501152

11511153
ENUM_STREAMWISE_PERIODIC Kind_Streamwise_Periodic; /*!< \brief Kind of Streamwise periodic flow (pressure drop or massflow) */
11521154
bool Streamwise_Periodic_Temperature; /*!< \brief Use real periodicity for Energy equation or otherwise outlet source term. */

Common/include/option_structure.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,9 +1117,12 @@ struct CMGOptions {
11171117
std::vector<unsigned short> MG_PreSmooth; /*!< \brief Multigrid pre-smoothing iterations per level. */
11181118
std::vector<unsigned short> MG_PostSmooth; /*!< \brief Multigrid post-smoothing iterations per level. */
11191119
std::vector<unsigned short> MG_CorrecSmooth; /*!< \brief Multigrid Jacobi correction-smoothing per level. */
1120+
std::vector<su2double> MG_CflScaling; /*!< \brief Per-level CFL scaling factors relative to the previous (finer) level. Entry [i] scales level i+1 from level i. Size = nMGLevels. */
11201121
bool MG_Smooth_EarlyExit{false}; /*!< \brief Enable early exit for MG smoothing iterations. */
11211122
bool MG_Smooth_Output{false}; /*!< \brief Output compact per-cycle smoothing summary. */
1123+
su2double MG_Smooth_StagnationTol{0.0}; /*!< \brief Stagnation early exit: stop if current_rms >= prev_rms * tol. 0 = disabled. */
11221124
bool MG_Implicit_Lines{false}; /*!< \brief Enable implicit-lines agglomeration from walls. */
1125+
unsigned long MG_Implicit_Lines_MaxLength{20}; /*!< \brief Maximum nodes on a wall-normal implicit line (including wall seed). */
11231126
};
11241127

11251128
/*!

Common/src/CConfig.cpp

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1985,16 +1985,22 @@ void CConfig::SetConfig_Options() {
19851985
addDoubleOption("MG_DAMP_PROLONGATION", Damp_Correc_Prolong, 0.5);
19861986
/*!\brief MG_SMOOTH_EARLY_EXIT\n DESCRIPTION: Enable early exit for MG smoothing when RMS drops below threshold. DEFAULT: NO \ingroup Config*/
19871987
addBoolOption("MG_SMOOTH_EARLY_EXIT", MGOptions.MG_Smooth_EarlyExit, true);
1988-
/*!\brief MG_SMOOTH_RES_THRESHOLD\n DESCRIPTION: Smoothing stops when current_rms < threshold * initial_rms. DEFAULT: 0.1 \ingroup Config*/
1989-
addDoubleOption("MG_SMOOTH_RES_THRESHOLD", MGOptions.MG_Smooth_Res_Threshold, 0.5);
1988+
/*!\brief MG_SMOOTH_RES_THRESHOLD\n DESCRIPTION: Early exit smoothing when current_rms drops below threshold * initial_rms. DEFAULT: 0.9 \ingroup Config*/
1989+
addDoubleOption("MG_SMOOTH_RES_THRESHOLD", MGOptions.MG_Smooth_Res_Threshold, 0.9);
19901990
/*!\brief MG_SMOOTH_OUTPUT\n DESCRIPTION: Print compact per-cycle smoothing iteration summary. DEFAULT: NO \ingroup Config*/
19911991
addBoolOption("MG_SMOOTH_OUTPUT", MGOptions.MG_Smooth_Output, false);
1992+
/*!\brief MG_SMOOTH_STAGNATION_TOL\n DESCRIPTION: Stop smoothing if current_rms >= previous_rms * this value. Values < 1.0 enable early exit on stagnation, 1.0 only exits on defect growth. DEFAULT: 0.99 \ingroup Config*/
1993+
addDoubleOption("MG_SMOOTH_STAGNATION_TOL", MGOptions.MG_Smooth_StagnationTol, 0.99);
19921994
/*!\brief MG_SMOOTH_COEFF\n DESCRIPTION: Smoothing coefficient for the correction prolongation Jacobi smoother. DEFAULT: 1.25 \ingroup Config*/
19931995
addDoubleOption("MG_SMOOTH_COEFF", MGOptions.MG_Smooth_Coeff, 1.25);
19941996
/*!\brief MG_MIN_MESHSIZE\n DESCRIPTION: Minimum number of CVs on the coarsest multigrid level. Levels that would produce fewer CVs are not created. DEFAULT: 50 \ingroup Config*/
19951997
addUnsignedLongOption("MG_MIN_MESHSIZE", MGOptions.MG_Min_MeshSize, 500);
19961998
/*!\brief MG_IMPLICIT_LINES\n DESCRIPTION: Enable agglomeration along implicit lines from wall seeds. DEFAULT: NO \ingroup Config*/
19971999
addBoolOption("MG_IMPLICIT_LINES", MGOptions.MG_Implicit_Lines, false);
2000+
/*!\brief MG_IMPLICIT_LINES_MAX_LENGTH\n DESCRIPTION: Maximum number of nodes on a wall-normal implicit agglomeration line (including the wall seed node). DEFAULT: 20 \ingroup Config*/
2001+
addUnsignedLongOption("MG_IMPLICIT_LINES_MAX_LENGTH", MGOptions.MG_Implicit_Lines_MaxLength, 20);
2002+
/*!\brief MG_CFL_SCALING\n DESCRIPTION: Per-level CFL scaling factors for coarse MG levels. Entry i is the ratio CFL(i+1)/CFL(i). If fewer values than nMGLevels are given, the last value is repeated. DEFAULT: 0.25 (i.e., 1/4 per level) \ingroup Config*/
2003+
addDoubleListOption("MG_CFL_SCALING", nMG_CflScaling_p, MG_CflScaling_p);
19982004

19992005
/*!\par CONFIG_CATEGORY: Spatial Discretization \ingroup Config*/
20002006
/*--- Options related to the spatial discretization ---*/
@@ -4789,6 +4795,16 @@ void CConfig::SetPostprocessing(SU2_COMPONENT val_software, unsigned short val_i
47894795
[](unsigned short ) { return (unsigned short)0; });
47904796
fillSmooth(nMG_CorrecSmooth_p, MG_CorrecSmooth_p, MGOptions.MG_CorrecSmooth,
47914797
[](unsigned short ) { return (unsigned short)0; });
4798+
4799+
/*--- Fill MG_CflScaling to size nMGLevels (one entry per coarse level transition). ---*/
4800+
MGOptions.MG_CflScaling.resize(nMGLevels);
4801+
if (nMG_CflScaling_p != 0) {
4802+
for (unsigned short i = 0; i < nMGLevels; ++i)
4803+
MGOptions.MG_CflScaling[i] = (i < nMG_CflScaling_p) ? MG_CflScaling_p[i] : MG_CflScaling_p[nMG_CflScaling_p - 1];
4804+
} else {
4805+
for (unsigned short i = 0; i < nMGLevels; ++i)
4806+
MGOptions.MG_CflScaling[i] = 0.25;
4807+
}
47924808
}
47934809

47944810
/*--- Override MG Smooth parameters ---*/
@@ -7534,10 +7550,12 @@ void CConfig::SetOutput(SU2_COMPONENT val_software, unsigned short val_izone) {
75347550
MGTable.AddColumn("Presmooth", 10);
75357551
MGTable.AddColumn("PostSmooth", 10);
75367552
MGTable.AddColumn("CorrectSmooth", 10);
7553+
MGTable.AddColumn("CFL Scaling", 12);
75377554
MGTable.SetAlign(PrintingToolbox::CTablePrinter::RIGHT);
75387555
MGTable.PrintHeader();
75397556
for (unsigned short iLevel = 0; iLevel < nMGLevels+1; iLevel++) {
7540-
MGTable << iLevel << MGOptions.MG_PreSmooth[iLevel] << MGOptions.MG_PostSmooth[iLevel] << MGOptions.MG_CorrecSmooth[iLevel];
7557+
const string cflStr = (iLevel == 0) ? "-" : std::to_string(MGOptions.MG_CflScaling[iLevel-1]).substr(0,6);
7558+
MGTable << iLevel << MGOptions.MG_PreSmooth[iLevel] << MGOptions.MG_PostSmooth[iLevel] << MGOptions.MG_CorrecSmooth[iLevel] << cflStr;
75417559
}
75427560
MGTable.PrintFooter();
75437561
}

Common/src/geometry/CMultiGridGeometry.cpp

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1280,24 +1280,28 @@ su2double CMultiGridGeometry::ComputeLocalCurvature(const CGeometry* fine_grid,
12801280
void CMultiGridGeometry::AgglomerateImplicitLines(unsigned long& Index_CoarseCV, const CGeometry* fine_grid,
12811281
const CConfig* config, CMultiGridQueue& MGQueue_InnerCV) {
12821282
/*--- Parameters ---*/
1283-
const su2double ANGLE_THRESHOLD_DEG = 20.0; /*!< Stop line if direction deviates more than this. */
1284-
constexpr unsigned long MAX_LINE_LENGTH = 20; /*!< Max nodes on implicit line (including wall). */
1283+
const su2double ANGLE_THRESHOLD_DEG = 20.0; /*!< Stop line if direction deviates more than this. */
1284+
const unsigned long MAX_LINE_LENGTH = config->GetMGOptions().MG_Implicit_Lines_MaxLength;
12851285
const su2double cos_threshold = cos(ANGLE_THRESHOLD_DEG * PI_NUMBER / 180.0);
12861286

12871287
const unsigned long nPointFine = fine_grid->GetnPoint();
12881288

1289-
/*--- Collect implicit lines starting at wall vertices.
1289+
/*--- Collect implicit lines starting at viscous (no-slip) wall vertices only.
1290+
* Seeding from non-wall boundaries (farfield, inlet, outlet, symmetry) would
1291+
* claim interior BL cells before the wall lines can reach them, leaving
1292+
* wall-seeded lines with length < 3 (discarded). Restricting to viscous walls
1293+
* ensures the boundary-layer cells are agglomerated wall-first.
12901294
* Each line: [wall_node, interior_1, interior_2, ...].
12911295
* The wall node (index 0) is already agglomerated by boundary agglomeration;
12921296
* only interior nodes (index >= 1) are paired into coarse CVs. ---*/
12931297
vector<vector<unsigned long>> lines;
12941298

12951299
for (auto iMarker = 0u; iMarker < fine_grid->GetnMarker(); iMarker++) {
1296-
/*--- Skip non-physical markers ---*/
1297-
if (config->GetMarker_All_KindBC(iMarker) == SEND_RECEIVE ||
1298-
config->GetMarker_All_KindBC(iMarker) == INTERNAL_BOUNDARY ||
1299-
config->GetMarker_All_KindBC(iMarker) == NEARFIELD_BOUNDARY)
1300-
continue;
1300+
/*--- Only seed lines from viscous (no-slip) wall markers.
1301+
* Non-wall boundaries (farfield, inlet, outlet, symmetry) must NOT seed
1302+
* lines because they would prematurely claim boundary-layer interior nodes. ---*/
1303+
const auto bc = config->GetMarker_All_KindBC(iMarker);
1304+
if (bc != HEAT_FLUX && bc != ISOTHERMAL && bc != CHT_WALL_INTERFACE && bc != SMOLUCHOWSKI_MAXWELL) continue;
13011305

13021306
for (auto iVertex = 0ul; iVertex < fine_grid->GetnVertex(iMarker); iVertex++) {
13031307
const auto iPoint = fine_grid->vertex[iMarker][iVertex]->GetNode();

SU2_CFD/include/integration/CMultiGridIntegration.hpp

Lines changed: 40 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -217,90 +217,64 @@ class CMultiGridIntegration final : public CIntegration {
217217
unsigned short RunTime_EqSystem, unsigned long Iteration, unsigned short iZone);
218218

219219
/*!
220-
* \brief Compute adaptive CFL for multigrid coarse levels.
221-
* \param[in] config - Problem configuration.
222-
* \param[in] solver_coarse - Coarse grid solver.
223-
* \param[in] geometry_coarse - Coarse grid geometry.
224-
* \param[in] iMesh - Current multigrid level.
225-
* \param[in] CFL_fine - Fine grid CFL value (passive).
226-
* \param[in] CFL_coarse_current - Current coarse grid CFL value (passive).
227-
* \param[in] rms_res_coarse - Coarse-grid RMS residual (already MPI-reduced, from lastPreSmoothRMS).
228-
* \return New CFL value for the coarse grid.
229-
*/
230-
passivedouble computeMultigridCFL(CConfig* config, unsigned short iMesh,
231-
passivedouble CFL_fine, passivedouble CFL_coarse_current,
232-
passivedouble rms_res_coarse);
233-
234-
/*!
235-
* \brief Adapt the residual restriction damping factor.
220+
* \brief Adapt both restriction and prolongation damping factors from the global-trend signal.
236221
*
237-
* Uses \c lastPreSmoothIters[] (filled by the previous multigrid cycle) to assess
238-
* whether the pre-smoother is converging fast or slow on coarse levels, then adjusts
239-
* \c Damp_Res_Restric in \p config accordingly.
222+
* Uses the cross-cycle EMA ratio (crossCycleRatio = fine_d0 / EMA(fine_d0)) to detect
223+
* long-term convergence or divergence, then adjusts both \c Damp_Res_Restric and
224+
* \c Damp_Correc_Prolong with a single shared signal. The EMA filters per-cycle noise;
225+
* no per-level aggregation or floor counter is needed.
240226
*
241-
* Signal logic:
242-
* - any coarse level ran its full configured iterations: reduce damping
243-
* - all coarse levels exited early: increase damping
244-
* - mixed (some full, some partial): no change
245-
*
246-
* \param[in,out] config - Problem configuration.
227+
* \param[in,out] config - Problem configuration.
228+
* \param[in] crossCycleRatio - Current fine_d0 divided by the EMA of fine_d0.
247229
*/
248-
void adaptRestrictionDamping(CConfig* config);
230+
void adaptDampingFactors(CConfig* config, passivedouble crossCycleRatio);
249231

250232
/*!
251-
* \brief Adapt the correction prolongation damping factor.
252-
*
253-
* Uses \c lastCorrecSmoothIters[] (filled by the previous multigrid cycle) to assess
254-
* whether the correction smoother is struggling or converging fast,
255-
* then adjusts \c Damp_Correc_Prolong in \p config accordingly.
256-
*
257-
* Signal logic:
258-
* - any level ran its full correction-smooth iterations: reduce damping
259-
* - all levels exited early: increase damping
260-
* - mixed: no change
261-
*
262-
* \param[in,out] config - Problem configuration; \c SetDamp_Correc_Prolong is called to persist the result.
233+
* \brief Helper function for early-exit logic during pre/post-smoothing.
234+
* \param[in] iSmooth - Current smoothing iteration index.
235+
* \param[in] iMesh - Index of the mesh in multigrid computations.
236+
* \param[in] defect - Current RMS defect value.
237+
* \param[in] mgOpts - Reference to multigrid options.
238+
* \param[in] stag_tol - Stagnation tolerance value.
239+
* \param[in] early_exit - Whether early exit is enabled.
240+
* \param[out] lastRMS - Array to store RMS values [start, end].
241+
* \param[out] exitReason - Character for early exit reason ('T', 'S', 'A', or ' ').
242+
* \param[out] worstStepRatio - Worst step-to-step ratio seen.
243+
* \param[out] worstStep - Iteration number of worst step.
263244
*/
264-
void adaptProlongationDamping(CConfig* config);
245+
void prePostEarlyExit(unsigned short iSmooth, unsigned short iMesh,
246+
passivedouble defect, const CMGOptions& mgOpts,
247+
passivedouble stag_tol, bool early_exit,
248+
passivedouble lastRMS[2], char& exitReason,
249+
passivedouble& worstStepRatio, unsigned short& worstStep);
265250

266-
/*--- CFL adaptation state variables.
267-
* These must be passivedouble: AD::Reset() clears the tape between adjoint recordings,
268-
* but class members survive. If these were su2double their stale AD indices would
269-
* reference the cleared tape, causing invalid memory access during the backward pass. ---*/
270251
static constexpr int MAX_MG_LEVELS = 10;
271-
passivedouble current_avg[MAX_MG_LEVELS] = {};
272-
passivedouble prev_avg[MAX_MG_LEVELS] = {};
273-
passivedouble last_res[MAX_MG_LEVELS] = {};
274-
bool last_was_increase[MAX_MG_LEVELS] = {};
275-
int oscillation_count[MAX_MG_LEVELS] = {};
276-
unsigned long last_check_iter[MAX_MG_LEVELS] = {};
277-
unsigned long last_update_iter[MAX_MG_LEVELS] = {};
278-
unsigned long last_reset_iter = std::numeric_limits<unsigned long>::max();
279252

280253
/*--- Early-exit smoothing state (shared across OMP threads via master write + barrier). ---*/
281-
bool mg_early_exit_flag = false; /*!< \brief Shared flag for early exit across OMP threads. */
282-
passivedouble mg_initial_smooth_rms = 0.0; /*!< \brief Initial RMS before current smoothing phase. */
283-
passivedouble mg_last_smooth_rms = 0.0; /*!< \brief Last computed RMS; cached to avoid redundant Allreduce. */
254+
bool mg_early_exit_flag = false; /*!< \brief Shared flag for early exit across OMP threads. */
255+
passivedouble mg_initial_smooth_rms = 0.0; /*!< \brief Initial RMS residual before current smoothing phase (FAS). */
256+
passivedouble mg_prev_smooth_rms = 0.0; /*!< \brief RMS residual from previous smoothing step; used for stagnation detection. */
257+
passivedouble mg_fine_rms_ema = 0.0; /*!< \brief EMA of fine-grid pre-smooth RMS across cycles; cross-cycle trend signal. */
258+
passivedouble last_crossCycleRatio = 1.0; /*!< \brief crossCycleRatio from the most recent cycle; stored for display only. */
284259

285260
/*--- Actual iteration counts per MG level, filled each cycle for the compact output summary. ---*/
286261
unsigned short lastPreSmoothIters[MAX_MG_LEVELS+1] = {};
287262
unsigned short lastPostSmoothIters[MAX_MG_LEVELS+1] = {};
288263
unsigned short lastCorrecSmoothIters[MAX_MG_LEVELS+1] = {};
264+
/*--- Early-exit reason per level: 'T'=threshold, 'S'=stagnation, ' '=ran to completion. ---*/
265+
char lastPreSmoothExitReason[MAX_MG_LEVELS+1] = {};
266+
char lastPostSmoothExitReason[MAX_MG_LEVELS+1] = {};
289267

290-
/*--- Per-level residual progress flags: true if the final RMS after that phase was lower
291-
* than the initial RMS. Used by the adaptive damping routines to distinguish
292-
* "hit max iters but still converging" from "hit max iters and stagnated". ---*/
293-
bool lastPreSmoothProgress[MAX_MG_LEVELS+1] = {};
294-
bool lastPostSmoothProgress[MAX_MG_LEVELS+1] = {};
295-
bool lastCorrecSmoothProgress[MAX_MG_LEVELS+1] = {};
296-
297-
/*--- Per-level start/end RMS for the compact output summary.
298-
* [0] = initial RMS before smoothing, [1] = final RMS after smoothing.
299-
* Filled unconditionally (early-exit path and exhaustion path).
300-
* Must be passivedouble: class members survive tape resets; su2double would
301-
* carry stale AD indices referencing a cleared tape. ---*/
268+
/*--- Per-level start/end RMS residual for adaptive damping. ---*/
302269
passivedouble lastPreSmoothRMS[MAX_MG_LEVELS+1][2] = {};
303270
passivedouble lastPostSmoothRMS[MAX_MG_LEVELS+1][2] = {};
304271
passivedouble lastCorrecSmoothRMS[MAX_MG_LEVELS+1][2] = {};
305272

273+
/*--- Per-level worst step-to-step amplification seen inside a smoothing phase.
274+
* step==0 means no intra-smoother ratio was available (fewer than 2 sweeps). ---*/
275+
passivedouble lastPreSmoothWorstStepRatio[MAX_MG_LEVELS+1] = {};
276+
passivedouble lastPostSmoothWorstStepRatio[MAX_MG_LEVELS+1] = {};
277+
unsigned short lastPreSmoothWorstStep[MAX_MG_LEVELS+1] = {};
278+
unsigned short lastPostSmoothWorstStep[MAX_MG_LEVELS+1] = {};
279+
306280
};

SU2_CFD/include/integration/ComputeLinSysResRMS.hpp

Lines changed: 0 additions & 63 deletions
This file was deleted.

0 commit comments

Comments
 (0)