Skip to content

Commit fb8e4ee

Browse files
committed
fix euler wall agglomeration
1 parent 944a2fc commit fb8e4ee

File tree

7 files changed

+204
-21
lines changed

7 files changed

+204
-21
lines changed

Common/include/geometry/CGeometry.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ class CGeometry {
245245

246246
/*!< \brief Bool if boundary-marker is straight(2D)/plane(3D) for each local marker. */
247247
vector<bool> boundIsStraight;
248+
248249
vector<su2double> SurfaceAreaCfgFile; /*!< \brief Total Surface area for all markers. */
249250

250251
/*--- Partitioning-specific variables ---*/

Common/include/geometry/CMultiGridGeometry.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,15 @@ class CMultiGridGeometry final : public CGeometry {
8484
*/
8585
void ComputeSurfStraightness(CConfig* config);
8686

87+
/*!
88+
* \brief Compute local curvature at a boundary vertex on Euler wall.
89+
* \param[in] fine_grid - Fine grid geometry.
90+
* \param[in] iPoint - Point index.
91+
* \param[in] iMarker - Marker index.
92+
* \return Maximum angle (in degrees) between this vertex normal and adjacent vertex normals.
93+
*/
94+
su2double ComputeLocalCurvature(const CGeometry* fine_grid, unsigned long iPoint, unsigned short iMarker) const;
95+
8796
public:
8897
/*--- This is to suppress Woverloaded-virtual, omitting it has no negative impact. ---*/
8998
using CGeometry::SetBoundControlVolume;

Common/src/geometry/CGeometry.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2588,11 +2588,10 @@ void CGeometry::ComputeSurfStraightness(const CConfig* config, bool print_on_scr
25882588
constexpr passivedouble epsilon = 1.0e-6;
25892589
su2double Area;
25902590
string Local_TagBound, Global_TagBound;
2591-
cout << "Computing surface straightness for symmetry and Euler wall boundary markers..." << endl;
2592-
vector<su2double> Normal(nDim), UnitNormal(nDim), RefUnitNormal(nDim);
2591+
cout << "Computing surface straightness for symmetry and Euler wall boundary markers..." << endl;
25932592

2594-
/*--- Assume now that this boundary marker is straight. As soon as one
2595-
AreaElement is found that is not aligend with a Reference then it is
2593+
vector<su2double> Normal(nDim), UnitNormal(nDim), RefUnitNormal(nDim); /*--- Assume now that this boundary marker is straight. As soon as one
2594+
AreaElement is found that is not aligned with a Reference then it is
25962595
certain that the boundary marker is not straight and one can stop
25972596
searching. Another possibility is that this process doesn't own
25982597
any nodes of that boundary, in that case we also have to assume the
@@ -2701,6 +2700,8 @@ void CGeometry::ComputeSurfStraightness(const CConfig* config, bool print_on_scr
27012700
} // if print_on_scren
27022701
}
27032702

2703+
2704+
27042705
void CGeometry::ComputeSurf_Curvature(CConfig* config) {
27052706
unsigned short iMarker, iNeigh_Point, iDim, iNode, iNeighbor_Nodes, Neighbor_Node;
27062707
unsigned long Neighbor_Point, iVertex, iPoint, jPoint, iElem_Bound, iEdge, nLocalVertex, MaxLocalVertex,

Common/src/geometry/CMultiGridGeometry.cpp

Lines changed: 157 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,18 @@
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? ---*/
3941
CMultiGridGeometry::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+
}

SU2_CFD/include/solvers/CFVMFlowSolverBase.inl

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,6 +1130,35 @@ void CFVMFlowSolverBase<V, FlowRegime>::BC_Sym_Plane(CGeometry* geometry, CSolve
11301130
/*--- Halo points do not need to be considered. ---*/
11311131
if (!geometry->nodes->GetDomain(iPoint)) continue;
11321132

1133+
/*--- On coarse multigrid levels with agglomerated Euler walls, skip flux computation
1134+
* and directly zero momentum residuals to avoid errors from averaged normals. ---*/
1135+
if (config->GetMarker_All_KindBC(val_marker) == EULER_WALL && geometry->GetMGLevel() > MESH_0) {
1136+
for (auto iDim = 0u; iDim < nDim; iDim++) {
1137+
LinSysRes(iPoint, iVel + iDim) = 0.0;
1138+
}
1139+
if (implicit) {
1140+
/*--- Zero momentum rows in Jacobian ---*/
1141+
for (auto iDim = 0u; iDim < nDim; iDim++) {
1142+
auto* block = Jacobian.GetBlock(iPoint, iPoint);
1143+
for (auto jVar = 0u; jVar < nVar; jVar++) {
1144+
block[(iVel + iDim) * nVar + jVar] = 0.0;
1145+
}
1146+
block[(iVel + iDim) * nVar + (iVel + iDim)] = 1.0; // Diagonal
1147+
}
1148+
/*--- Zero momentum columns in off-diagonal blocks ---*/
1149+
for (size_t iNeigh = 0; iNeigh < geometry->nodes->GetnPoint(iPoint); ++iNeigh) {
1150+
auto jPoint = geometry->nodes->GetPoint(iPoint, iNeigh);
1151+
auto* block = Jacobian.GetBlock(iPoint, jPoint);
1152+
for (auto iDim = 0u; iDim < nDim; iDim++) {
1153+
for (auto jVar = 0u; jVar < nVar; jVar++) {
1154+
block[(iVel + iDim) * nVar + jVar] = 0.0;
1155+
}
1156+
}
1157+
}
1158+
}
1159+
continue; // Skip normal flux computation
1160+
}
1161+
11331162
/*--- Get the normal of the current symmetry. This may be the original normal of the vertex
11341163
* or a modified normal if there are intersecting symmetries. ---*/
11351164

SU2_CFD/src/drivers/CDriver.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,8 @@ void CDriver::InitializeGeometryFVM(CConfig *config, CGeometry **&geometry) {
841841
geometry[MESH_0]->ComputeSurf_Curvature(config);
842842
}
843843

844+
845+
844846
/*--- Compute the global surface areas for all markers. ---*/
845847

846848
geometry[MESH_0]->ComputeSurfaceAreaCfgFile(config);

SU2_CFD/src/integration/CMultiGridIntegration.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,7 @@ void CMultiGridIntegration::SetForcing_Term(CSolver *sol_fine, CSolver *sol_coar
645645

646646
delete [] Residual;
647647

648+
/*--- Zero momentum components of truncation error on viscous walls ---*/
648649
for (iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) {
649650
if (config->GetViscous_Wall(iMarker)) {
650651
SU2_OMP_FOR_STAT(32)

0 commit comments

Comments
 (0)