3333#include < unordered_map>
3434#include < climits>
3535#include < iostream>
36+ #include < iomanip>
3637#include < cstdlib>
38+ #include < map>
3739
3840/* --- Nijso says: this could perhaps be replaced by metis partitioning? ---*/
3941CMultiGridGeometry::CMultiGridGeometry (CGeometry* fine_grid, CConfig* config, unsigned short iMesh) : CGeometry() {
4042 nDim = fine_grid->GetnDim (); // Write the number of dimensions of the coarse grid.
4143 /* --- Maximum agglomeration size in 2D is 4 nodes, in 3D is 8 nodes. ---*/
4244 const short int maxAgglomSize = 4 ;
4345
44- /* --- Compute surface straightness to determine straight boundaries ---*/
45- if (iMesh == MESH_0)
46- ComputeSurfStraightness (config);
47- else
48- boundIsStraight = fine_grid->boundIsStraight ;
46+ /* --- Inherit boundary properties from fine grid ---*/
47+ boundIsStraight = fine_grid->boundIsStraight ;
4948
5049 /* --- Agglomeration Scheme II (Nishikawa, Diskin, Thomas)
5150 Create a queue system to do the agglomeration
@@ -80,8 +79,19 @@ CMultiGridGeometry::CMultiGridGeometry(CGeometry* fine_grid, CConfig* config, un
8079
8180 unsigned long Index_CoarseCV = 0 ;
8281
82+ /* --- Statistics for Euler wall agglomeration ---*/
83+ map<unsigned short , unsigned long > euler_wall_agglomerated, euler_wall_rejected_curvature, euler_wall_rejected_straight;
84+ for (unsigned short iMarker = 0 ; iMarker < fine_grid->GetnMarker (); iMarker++) {
85+ if (config->GetMarker_All_KindBC (iMarker) == EULER_WALL) {
86+ euler_wall_agglomerated[iMarker] = 0 ;
87+ euler_wall_rejected_curvature[iMarker] = 0 ;
88+ euler_wall_rejected_straight[iMarker] = 0 ;
89+ }
90+ }
91+
8392 /* --- STEP 1: The first step is the boundary agglomeration. ---*/
8493 for (auto iMarker = 0u ; iMarker < fine_grid->GetnMarker (); iMarker++) {
94+ cout << " marker name = " << config->GetMarker_All_TagBound (iMarker) << endl;
8595 for (auto iVertex = 0ul ; iVertex < fine_grid->GetnVertex (iMarker); iVertex++) {
8696 const auto iPoint = fine_grid->vertex [iMarker][iVertex]->GetNode ();
8797
@@ -130,13 +140,31 @@ CMultiGridGeometry::CMultiGridGeometry(CGeometry* fine_grid, CConfig* config, un
130140 /* --- Valley -> Valley : conditionally allowed when both points are on the same marker. ---*/
131141 /* --- ! Note that in the case of MPI SEND_RECEIVE markers, we might need other conditions ---*/
132142 if (counter == 1 ) {
143+ cout << " we have exactly one marker at point " << iPoint << endl;
144+ cout << " marker is " << marker_seed[0 ] << " , marker name = "
145+ << config->GetMarker_All_TagBound (marker_seed[0 ]);
146+ cout << " , marker type = " << config->GetMarker_All_KindBC (marker_seed[0 ]) << endl;
133147 // The seed/parent is one valley, so we set this part to true
134148 // if the child is only on this same valley, we set it to true as well.
135149 agglomerate_seed = true ;
136- /* --- Euler walls can be curved and agglomerating them leads to difficulties ---*/
137- // if (config->GetMarker_All_KindBC(marker_seed[0]) == EULER_WALL) agglomerate_seed = false;
138- if (config->GetMarker_All_KindBC (marker_seed[0 ]) == EULER_WALL && !boundIsStraight[marker_seed[0 ]])
139- agglomerate_seed = false ;
150+ /* --- Euler walls: check curvature-based agglomeration criterion ---*/
151+ if (config->GetMarker_All_KindBC (marker_seed[0 ]) == EULER_WALL) {
152+ /* --- Allow agglomeration if marker is straight OR local curvature is small ---*/
153+ if (!boundIsStraight[marker_seed[0 ]]) {
154+ /* --- Compute local curvature at this point ---*/
155+ su2double local_curvature = ComputeLocalCurvature (fine_grid, iPoint, marker_seed[0 ]);
156+ // limit to 30 degrees
157+ if (local_curvature >= 30.0 ) {
158+ agglomerate_seed = false ; // High curvature: do not agglomerate
159+ euler_wall_rejected_curvature[marker_seed[0 ]]++;
160+ } else {
161+ euler_wall_agglomerated[marker_seed[0 ]]++;
162+ }
163+ } else {
164+ /* --- Straight wall: agglomerate ---*/
165+ euler_wall_agglomerated[marker_seed[0 ]]++;
166+ }
167+ }
140168 /* --- Note that if the marker is a SEND_RECEIVE, then the node is actually an interior point.
141169 In that case it can only be agglomerated with another interior point. ---*/
142170 }
@@ -151,14 +179,25 @@ CMultiGridGeometry::CMultiGridGeometry(CGeometry* fine_grid, CConfig* config, un
151179 agglomerate_seed = (config->GetMarker_All_KindBC (copy_marker[0 ]) == SEND_RECEIVE) ||
152180 (config->GetMarker_All_KindBC (copy_marker[1 ]) == SEND_RECEIVE);
153181
154- /* --- Euler walls can also not be agglomerated when the point has 2 markers or if curved ---*/
155- // if ((config->GetMarker_All_KindBC(copy_marker[0]) == EULER_WALL) ||
156- // (config->GetMarker_All_KindBC(copy_marker[1]) == EULER_WALL)) {
157- // agglomerate_seed = false;
158- // }
159- if ((config->GetMarker_All_KindBC (copy_marker[0 ]) == EULER_WALL && !boundIsStraight[copy_marker[0 ]]) ||
160- (config->GetMarker_All_KindBC (copy_marker[1 ]) == EULER_WALL && !boundIsStraight[copy_marker[1 ]])) {
161- agglomerate_seed = false ;
182+ /* --- Euler walls: check curvature-based agglomeration criterion for both markers ---*/
183+ bool euler_wall_rejected_here = false ;
184+ for (unsigned short i = 0 ; i < 2 ; i++) {
185+ if (config->GetMarker_All_KindBC (copy_marker[i]) == EULER_WALL) {
186+ if (!boundIsStraight[copy_marker[i]]) {
187+ /* --- Compute local curvature at this point ---*/
188+ su2double local_curvature = ComputeLocalCurvature (fine_grid, iPoint, copy_marker[i]);
189+ // limit to 30 degrees
190+ if (local_curvature >= 30.0 ) {
191+ agglomerate_seed = false ; // High curvature: do not agglomerate
192+ euler_wall_rejected_curvature[copy_marker[i]]++;
193+ euler_wall_rejected_here = true ;
194+ }
195+ }
196+ /* --- Track agglomeration if not rejected ---*/
197+ if (agglomerate_seed && !euler_wall_rejected_here) {
198+ euler_wall_agglomerated[copy_marker[i]]++;
199+ }
200+ }
162201 }
163202
164203 /* --- In 2D, corners are not agglomerated, but in 3D counter=2 means we are on the
@@ -644,6 +683,43 @@ CMultiGridGeometry::CMultiGridGeometry(CGeometry* fine_grid, CConfig* config, un
644683 }
645684 }
646685
686+ /* --- Output Euler wall agglomeration statistics ---*/
687+ if (rank == MASTER_NODE) {
688+ /* --- Gather global statistics for Euler walls ---*/
689+ bool has_euler_walls = false ;
690+ for (unsigned short iMarker = 0 ; iMarker < fine_grid->GetnMarker (); iMarker++) {
691+ if (config->GetMarker_All_KindBC (iMarker) == EULER_WALL) {
692+ has_euler_walls = true ;
693+ break ;
694+ }
695+ }
696+
697+ if (has_euler_walls) {
698+ cout << endl;
699+ cout << " Euler Wall Agglomeration Statistics (45° curvature threshold):" << endl;
700+ cout << " ----------------------------------------------------------------" << endl;
701+
702+ for (unsigned short iMarker = 0 ; iMarker < fine_grid->GetnMarker (); iMarker++) {
703+ if (config->GetMarker_All_KindBC (iMarker) == EULER_WALL) {
704+ string marker_name = config->GetMarker_All_TagBound (iMarker);
705+ unsigned long agglomerated = euler_wall_agglomerated[iMarker];
706+ unsigned long rejected = euler_wall_rejected_curvature[iMarker];
707+ unsigned long total = agglomerated + rejected;
708+
709+ if (total > 0 ) {
710+ su2double accept_rate = 100.0 * su2double (agglomerated) / su2double (total);
711+ cout << " Marker: " << marker_name << endl;
712+ cout << " Seeds agglomerated: " << agglomerated << " ("
713+ << std::setprecision (1 ) << std::fixed << accept_rate << " %)" << endl;
714+ cout << " Seeds rejected (>45° curv): " << rejected << " ("
715+ << std::setprecision (1 ) << std::fixed << (100.0 - accept_rate) << " %)" << endl;
716+ }
717+ }
718+ }
719+ cout << " ----------------------------------------------------------------" << endl;
720+ }
721+ }
722+
647723 edgeColorGroupSize = config->GetEdgeColoringGroupSize ();
648724}
649725
@@ -1571,3 +1647,67 @@ void CMultiGridGeometry::FindNormal_Neighbor(const CConfig* config) {
15711647 }
15721648 }
15731649}
1650+
1651+ su2double CMultiGridGeometry::ComputeLocalCurvature (const CGeometry* fine_grid, unsigned long iPoint,
1652+ unsigned short iMarker) const {
1653+ /* --- Compute local curvature (maximum angle between adjacent face normals) at a boundary vertex.
1654+ This is used to determine if agglomeration is safe based on a curvature threshold. ---*/
1655+
1656+ /* --- Get the vertex index for this point on this marker ---*/
1657+ long iVertex = fine_grid->nodes ->GetVertex (iPoint, iMarker);
1658+ if (iVertex < 0 ) return 0.0 ; // Point not on this marker
1659+
1660+ /* --- Get the normal at this vertex ---*/
1661+ su2double Normal_i[MAXNDIM] = {0.0 };
1662+ fine_grid->vertex [iMarker][iVertex]->GetNormal (Normal_i);
1663+ su2double Area_i = GeometryToolbox::Norm (int (nDim), Normal_i);
1664+
1665+ if (Area_i < 1e-12 ) return 0.0 ; // Skip degenerate vertices
1666+
1667+ /* --- Normalize the normal ---*/
1668+ for (unsigned short iDim = 0 ; iDim < nDim; iDim++) {
1669+ Normal_i[iDim] /= Area_i;
1670+ }
1671+
1672+ /* --- Find maximum angle with neighboring vertices on the same marker ---*/
1673+ su2double max_angle = 0.0 ;
1674+
1675+ /* --- Loop over edges connected to this point ---*/
1676+ for (unsigned short iEdge = 0 ; iEdge < fine_grid->nodes ->GetnPoint (iPoint); iEdge++) {
1677+ unsigned long jPoint = fine_grid->nodes ->GetPoint (iPoint, iEdge);
1678+
1679+ /* --- Check if neighbor is also on this marker ---*/
1680+ long jVertex = fine_grid->nodes ->GetVertex (jPoint, iMarker);
1681+ if (jVertex < 0 ) continue ; // Not on this marker
1682+
1683+ /* --- Get normal at neighbor vertex ---*/
1684+ su2double Normal_j[MAXNDIM] = {0.0 };
1685+ fine_grid->vertex [iMarker][jVertex]->GetNormal (Normal_j);
1686+ su2double Area_j = GeometryToolbox::Norm (int (nDim), Normal_j);
1687+
1688+ if (Area_j < 1e-12 ) continue ; // Skip degenerate neighbor
1689+
1690+ /* --- Normalize the neighbor normal ---*/
1691+ for (unsigned short iDim = 0 ; iDim < nDim; iDim++) {
1692+ Normal_j[iDim] /= Area_j;
1693+ }
1694+
1695+ /* --- Compute dot product: cos(angle) = n_i · n_j ---*/
1696+ su2double dot_product = 0.0 ;
1697+ for (unsigned short iDim = 0 ; iDim < nDim; iDim++) {
1698+ dot_product += Normal_i[iDim] * Normal_j[iDim];
1699+ }
1700+
1701+ /* --- Clamp to [-1, 1] to avoid numerical issues with acos ---*/
1702+ dot_product = max (-1.0 , min (1.0 , dot_product));
1703+
1704+ /* --- Compute angle in degrees ---*/
1705+ su2double angle_rad = acos (dot_product);
1706+ su2double angle_deg = angle_rad * 180.0 / PI_NUMBER;
1707+
1708+ /* --- Track maximum angle ---*/
1709+ max_angle = max (max_angle, angle_deg);
1710+ }
1711+
1712+ return max_angle;
1713+ }
0 commit comments