diff --git a/Common/include/geometry/CMultiGridGeometry.hpp b/Common/include/geometry/CMultiGridGeometry.hpp
index ba635b40f8c..3658dc0ea2f 100644
--- a/Common/include/geometry/CMultiGridGeometry.hpp
+++ b/Common/include/geometry/CMultiGridGeometry.hpp
@@ -48,7 +48,16 @@ class CMultiGridGeometry final : public CGeometry {
* \return TRUE or FALSE depending if the control volume can be agglomerated.
*/
bool SetBoundAgglomeration(unsigned long CVPoint, vector marker_seed, const CGeometry* fine_grid,
- const CConfig* config) const;
+ const CConfig* config, const vector& mixedBC) const;
+
+ /*!
+ * \brief Find nodes where two boundary conditions of different type meet. These are never
+ * agglomerated, since a coarse CV holding one would average both conditions.
+ * \param[in] fine_grid - Geometrical definition of the problem.
+ * \param[in] config - Definition of the particular problem.
+ * \return One flag per fine grid point, set where that point must stay on its own.
+ */
+ vector FindMixedBoundaryNodes(const CGeometry* fine_grid, const CConfig* config) const;
/*!
* \brief Determine if a Point can be agglomerated using geometrical criteria.
@@ -62,11 +71,10 @@ class CMultiGridGeometry final : public CGeometry {
* \brief Determine if a CVPoint can be agglomerated, if it has the same marker point as the seed.
* \param[out] Suitable_Indirect_Neighbors - List of Indirect Neighbours that can be agglomerated.
* \param[in] iPoint - Seed point.
- * \param[in] Index_CoarseCV - Index of agglomerated point.
* \param[in] fine_grid - Geometrical definition of the problem.
*/
void SetSuitableNeighbors(vector& Suitable_Indirect_Neighbors, unsigned long iPoint,
- unsigned long Index_CoarseCV, const CGeometry* fine_grid) const;
+ const CGeometry* fine_grid) const;
/*!
* \brief Compute local curvature at a boundary vertex on Euler wall.
@@ -78,16 +86,75 @@ class CMultiGridGeometry final : public CGeometry {
su2double ComputeLocalCurvature(const CGeometry* fine_grid, unsigned long iPoint, unsigned short iMarker) const;
/*!
- * \brief Agglomerate high-aspect-ratio interior cells along implicit lines from wall vertices.
+ * \brief Pave the domain with columns grown from the boundary patches by one breadth first walk,
+ * so that every node goes to the patch that reaches it in the fewest steps.
* \param[in,out] Index_CoarseCV - Current coarse CV index, incremented as new coarse CVs are created.
* \param[in] fine_grid - Fine grid geometry.
* \param[in] config - Configuration.
- * \param[in,out] MGQueue_InnerCV - Queue for domain agglomeration; processed points are removed.
+ * \param[in] iMesh - Multigrid level being built, used to label the summary.
+ * \param[in] mixedBC - Nodes that must stay on their own, from FindMixedBoundaryNodes.
+ * \param[in] onPhysBoundary - Nodes carrying a physical boundary condition, excluding SEND_RECEIVE.
+ * \param[in] onPeriodic - Nodes on a periodic marker, which a column never claims.
+ * \param[out] neverGrewCV - Coarse CV index of every column that never grew past its seed layer.
+ * \return Summary of the paving, empty except on the master rank.
+ */
+ string PaveAdvancingFronts(unsigned long& Index_CoarseCV, const CGeometry* fine_grid, const CConfig* config,
+ unsigned short iMesh, const vector& mixedBC, const vector& onPhysBoundary,
+ const vector& onPeriodic, vector& neverGrewCV);
+
+ /*!
+ * \brief Boundary nodes that seed a column, ordered with the most anisotropic first.
+ */
+ struct CFrontSeeds {
+ vector node; /*!< \brief Seed node on the boundary. */
+ vector> normal; /*!< \brief Unit normal there, pointing into the domain. */
+ vector strength; /*!< \brief Local anisotropy, the ordering key. */
+ vector tier; /*!< \brief Paving order: 0 viscous wall, 1 Euler
+ wall, 2 anything else. */
+ unsigned long nRefusedCurvature = 0; /*!< \brief Euler wall nodes the curvature limit kept out. */
+ };
+
+ /*!
+ * \brief Collect the boundary nodes that seed a column: those where the stiffest edge is also the
+ * edge most nearly along the boundary normal, so the mesh is layered against this boundary.
+ * \param[in] fine_grid - Fine grid geometry.
+ * \param[in] config - Definition of the particular problem.
+ * \return Seed nodes and their inward boundary normals, most anisotropic first.
*/
- void AgglomerateImplicitLines(unsigned long& Index_CoarseCV, const CGeometry* fine_grid, const CConfig* config,
- CMultiGridQueue& MGQueue_InnerCV);
+ CFrontSeeds SeedFrontNodes(const CGeometry* fine_grid, const CConfig* config) const;
+
+ /*!
+ * \brief Element strip a column follows while the mesh is extruded along it.
+ */
+ struct CWalkState {
+ unsigned long elem; /*!< \brief Element the column stands in, or NO_ELEM once it is lost. */
+ unsigned short face; /*!< \brief Local face it entered that element through. */
+ };
+
+ /*!
+ * \brief Partition the seed nodes into compact surface patches. A patch is a boundary face where
+ * the primal grid gives one, otherwise it is built by repeated pairwise matching.
+ * \param[in] seeds - Seed nodes from SeedFrontNodes.
+ * \param[in] fine_grid - Fine grid geometry.
+ * \param[in] config - Definition of the particular problem.
+ * \param[in] mixedBC - Nodes that must stay on their own, from FindMixedBoundaryNodes.
+ * \param[out] walk - Element and face each patch starts from, NO_ELEM where it has none.
+ * \return One vector of indices into seeds.node per patch, at most two entries in 2D, four in 3D.
+ */
+ vector> BuildFrontPatches(const CFrontSeeds& seeds, const CGeometry* fine_grid,
+ const CConfig* config, const vector& mixedBC,
+ vector& walk) const;
+
+ string levelReport; /*!< \brief Console summary for this level. */
public:
+ /*!
+ * \brief Get the console summary for this level, held back so it does not interleave with the
+ * multigrid table.
+ * \return Summary text, empty except on the master rank.
+ */
+ const string& GetLevelReport() const { return levelReport; }
+
/*--- This is to suppress Woverloaded-virtual, omitting it has no negative impact. ---*/
using CGeometry::SetBoundControlVolume;
using CGeometry::SetControlVolume;
diff --git a/Common/include/geometry/dual_grid/CPoint.hpp b/Common/include/geometry/dual_grid/CPoint.hpp
index 73e6f8fb047..bac83ccbfd2 100644
--- a/Common/include/geometry/dual_grid/CPoint.hpp
+++ b/Common/include/geometry/dual_grid/CPoint.hpp
@@ -656,6 +656,15 @@ class CPoint {
Children_CV[iPoint][nchildren_CV] = children_CV;
}
+ /*!
+ * \brief Set all the children control volumes of an agglomerated control volume at once.
+ * \param[in] iPoint - Index of the point.
+ * \param[in] children_CV - Indices of the children control volumes.
+ */
+ inline void SetChildren_CV(unsigned long iPoint, const vector& children_CV) {
+ Children_CV[iPoint] = children_CV;
+ }
+
/*!
* \brief Get the parent control volume of an agglomerated control volume.
* \param[in] iPoint - Index of the point.
@@ -673,6 +682,14 @@ class CPoint {
return Children_CV[iPoint][nchildren_CV];
}
+ /*!
+ * \brief Get the children control volumes of an agglomerated control volume. Only the first
+ * GetnChildren_CV entries are meaningful, the storage may be longer.
+ * \param[in] iPoint - Index of the point.
+ * \return Indices of the children control volumes.
+ */
+ inline const vector& GetChildren_CV(unsigned long iPoint) const { return Children_CV[iPoint]; }
+
/*!
* \brief Get information about if a control volume has been agglomerated.
* \param[in] iPoint - Index of the point.
diff --git a/Common/include/option_structure.hpp b/Common/include/option_structure.hpp
index 5669a6ec32a..bcd2e9c0632 100644
--- a/Common/include/option_structure.hpp
+++ b/Common/include/option_structure.hpp
@@ -1225,7 +1225,7 @@ inline SST_ParsedOptions ParseSSTOptions(const SST_OPTIONS *SST_Options, unsigne
struct CMGOptions {
su2double MG_Smooth_Res_Threshold{0.0}; /*!< \brief RMS reduction threshold for MG smoothing early exit. */
su2double MG_Smooth_Coeff{0.0}; /*!< \brief Jacobi smoother coefficient for coarse-grid correction. */
- unsigned long MG_Min_MeshSize{0}; /*!< \brief Minimum CVs on coarsest MG level. */
+ unsigned long MG_Min_MeshSize{0}; /*!< \brief Minimum CVs on coarsest MG level, over all ranks. */
std::vector MG_PreSmooth; /*!< \brief Multigrid pre-smoothing iterations per level. */
std::vector MG_PostSmooth; /*!< \brief Multigrid post-smoothing iterations per level. */
std::vector MG_CorrecSmooth; /*!< \brief Multigrid Jacobi correction-smoothing per level. */
@@ -1234,7 +1234,6 @@ struct CMGOptions {
bool MG_Smooth_Output{false}; /*!< \brief Output compact per-cycle smoothing summary. */
su2double MG_Smooth_StagnationTol{0.0}; /*!< \brief Stagnation early exit: stop if current_rms >= prev_rms * tol. 0 = disabled. */
bool MG_Implicit_Lines{false}; /*!< \brief Enable implicit-lines agglomeration from walls. */
- unsigned long MG_Implicit_Lines_MaxLength{20}; /*!< \brief Maximum nodes on a wall-normal implicit line (including wall seed). */
unsigned long MG_Startup_Iter{100}; /*!< \brief Iterations per mesh during FMG startup, and the length of each level's CFL ramp. 0 = no iteration budget. */
su2double MG_Startup_Convergence{-2.0}; /*!< \brief FMG: orders of magnitude (log10) that CONV_FIELD must drop on the
active level before promoting to the next finer one. Negative is a
diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp
index 0d257b77148..fd4207b0c5f 100644
--- a/Common/src/CConfig.cpp
+++ b/Common/src/CConfig.cpp
@@ -2093,16 +2093,17 @@ void CConfig::SetConfig_Options() {
addDoubleOption("MG_SMOOTH_RES_THRESHOLD", MGOptions.MG_Smooth_Res_Threshold, 0.9);
/*!\brief MG_SMOOTH_OUTPUT\n DESCRIPTION: Print compact per-cycle smoothing iteration summary. DEFAULT: NO \ingroup Config*/
addBoolOption("MG_SMOOTH_OUTPUT", MGOptions.MG_Smooth_Output, false);
- /*!\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*/
+ /*!\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*/
addDoubleOption("MG_SMOOTH_STAGNATION_TOL", MGOptions.MG_Smooth_StagnationTol, 0.99);
/*!\brief MG_SMOOTH_COEFF\n DESCRIPTION: Smoothing coefficient for the correction prolongation Jacobi smoother. DEFAULT: 1.25 \ingroup Config*/
addDoubleOption("MG_SMOOTH_COEFF", MGOptions.MG_Smooth_Coeff, 1.25);
- /*!\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*/
+ /*!\brief MG_MIN_MESHSIZE\n DESCRIPTION: Minimum number of CVs on the coarsest multigrid level, counted over the
+ * whole level rather than per rank, so the hierarchy does not change with the number of ranks. DEFAULT: 500 \ingroup Config*/
addUnsignedLongOption("MG_MIN_MESHSIZE", MGOptions.MG_Min_MeshSize, 500);
- /*!\brief MG_IMPLICIT_LINES\n DESCRIPTION: Enable agglomeration along implicit lines from wall seeds. DEFAULT: NO \ingroup Config*/
+ /*!\brief MG_IMPLICIT_LINES\n DESCRIPTION: Pave the coarse grid with advancing fronts raised from boundaries
+ * that carry a stretched layer normal to themselves. DEFAULT: NO \ingroup Config*/
addBoolOption("MG_IMPLICIT_LINES", MGOptions.MG_Implicit_Lines, false);
- /*!\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*/
- addUnsignedLongOption("MG_IMPLICIT_LINES_MAX_LENGTH", MGOptions.MG_Implicit_Lines_MaxLength, 20);
/*!\brief MG_STARTUP_ITER\n DESCRIPTION: Max number of iterations spent on each mesh during the Full
* Multigrid (FMG) startup phase. DEFAULT: 100 \ingroup Config*/
addUnsignedLongOption("MG_STARTUP_ITER", MGOptions.MG_Startup_Iter, 100);
@@ -2117,7 +2118,8 @@ void CConfig::SetConfig_Options() {
/*!\brief MG_STARTUP_STAGNATION_ITER\n DESCRIPTION: Consecutive stalled iterations required before Full-MG promotes
* on stagnation. 0 disables it, as MG_STARTUP_STAGNATION= 0 does. DEFAULT: 5 \ingroup Config*/
addUnsignedLongOption("MG_STARTUP_STAGNATION_ITER", MGOptions.MG_Startup_Stagnation_Iter, 5);
- /*!\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*/
+ /*!\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*/
addDoubleListOption("MG_CFL_SCALING", nMG_CflScaling_p, MG_CflScaling_p);
/*!\par CONFIG_CATEGORY: Spatial Discretization \ingroup Config*/
diff --git a/Common/src/geometry/CMultiGridGeometry.cpp b/Common/src/geometry/CMultiGridGeometry.cpp
index 684da742b13..d3bca729bf5 100644
--- a/Common/src/geometry/CMultiGridGeometry.cpp
+++ b/Common/src/geometry/CMultiGridGeometry.cpp
@@ -30,11 +30,51 @@
#include "../../include/toolboxes/printing_toolbox.hpp"
#include "../../../Common/include/toolboxes/geometry_toolbox.hpp"
+#include
+#include
+#include
+#include