Skip to content

Commit 8a591aa

Browse files
bigfootedgithub-advanced-security[bot]pcarruscag
authored
Fix curved symmetries (#2460)
* fix curved symmetries Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Pedro Gomes <[email protected]> Co-authored-by: Pedro Gomes <[email protected]>
1 parent dc371cf commit 8a591aa

File tree

13 files changed

+326
-43
lines changed

13 files changed

+326
-43
lines changed

Common/include/geometry/CGeometry.hpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,8 @@ class CGeometry {
243243
/*!< \brief Corrected normals on nodes with shared symmetry markers. */
244244
vector<std::unordered_map<unsigned long, std::array<su2double, MAXNDIM>>> symmetryNormals;
245245

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

248250
/*--- Partitioning-specific variables ---*/
@@ -1005,6 +1007,15 @@ class CGeometry {
10051007
*/
10061008
su2double GetSurfaceArea(const CConfig* config, unsigned short val_marker) const;
10071009

1010+
/*!
1011+
* \brief Check if a boundary is straight(2D) / plane(3D) for EULER_WALL and SYMMETRY_PLANE
1012+
* only and store the information in boundIsStraight. For all other boundary types
1013+
* this will return false and could therfore be wrong. Used ultimately for BC_Slip_Wall.
1014+
* \param[in] config - Definition of the particular problem.
1015+
* \param[in] print_on_screen - Boolean whether to print result on screen.
1016+
*/
1017+
void ComputeSurfStraightness(const CConfig* config, bool print_on_screen);
1018+
10081019
/*!
10091020
* \brief Find and store all vertices on a sharp corner in the geometry.
10101021
* \param[in] config - Definition of the particular problem.

Common/src/geometry/CGeometry.cpp

Lines changed: 190 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2456,16 +2456,66 @@ su2double CGeometry::GetSurfaceArea(const CConfig* config, unsigned short val_ma
24562456
void CGeometry::ComputeModifiedSymmetryNormals(const CConfig* config) {
24572457
/* Check how many symmetry planes there are and use the first (lowest ID) as the basis to orthogonalize against.
24582458
* All nodes that are shared by multiple symmetries have to get a corrected normal. */
2459+
2460+
/*--- Compute if markers are straight lines or planes. ---*/
2461+
ComputeSurfStraightness(config, false);
2462+
2463+
symmetryNormals.clear();
24592464
symmetryNormals.resize(nMarker);
2460-
std::vector<unsigned short> symMarkers;
2465+
std::vector<unsigned short> symMarkers, curvedSymMarkers;
24612466

2467+
/*--- Check which markers are symmetry or Euler wall and store in a list. ---*/
24622468
for (auto iMarker = 0u; iMarker < nMarker; ++iMarker) {
24632469
if ((config->GetMarker_All_KindBC(iMarker) == SYMMETRY_PLANE) ||
24642470
(config->GetMarker_All_KindBC(iMarker) == EULER_WALL)) {
24652471
symMarkers.push_back(iMarker);
2472+
if (!boundIsStraight[iMarker]) curvedSymMarkers.push_back(iMarker);
2473+
}
2474+
}
2475+
2476+
/*--- Merge the normals of curved markers to be independent of how surfaces are divided. ---*/
2477+
2478+
std::unordered_map<unsigned long, std::array<su2double, MAXNDIM>> mergedNormals;
2479+
2480+
for (const auto iMarker : curvedSymMarkers) {
2481+
for (auto iVertex = 0ul; iVertex < nVertex[iMarker]; iVertex++) {
2482+
const auto iPoint = vertex[iMarker][iVertex]->GetNode();
2483+
2484+
/*--- Determine if this point is shared with other curved symmetries. ---*/
2485+
int count = 0;
2486+
for (const auto jMarker : curvedSymMarkers) {
2487+
count += static_cast<int>(nodes->GetVertex(iPoint, jMarker) >= 0);
2488+
}
2489+
if (count < 2) continue;
2490+
2491+
std::array<su2double, MAXNDIM> normal = {};
2492+
vertex[iMarker][iVertex]->GetNormal(normal.data());
2493+
2494+
auto result = mergedNormals.emplace(iPoint, normal);
2495+
const auto inserted = result.second;
2496+
auto it = result.first;
2497+
if (!inserted) {
2498+
for (auto iDim = 0ul; iDim < MAXNDIM; ++iDim) {
2499+
it->second[iDim] += normal[iDim];
2500+
}
2501+
}
24662502
}
24672503
}
24682504

2505+
for (const auto& item : mergedNormals) {
2506+
const auto iPoint = item.first;
2507+
auto normal = item.second;
2508+
const su2double area = GeometryToolbox::Norm(int(MAXNDIM), normal.data());
2509+
for (auto iDim = 0ul; iDim < MAXNDIM; ++iDim) normal[iDim] /= area;
2510+
2511+
for (const auto iMarker : curvedSymMarkers) {
2512+
const auto iVertex = nodes->GetVertex(iPoint, iMarker);
2513+
if (iVertex >= 0) symmetryNormals[iMarker][iVertex] = normal;
2514+
}
2515+
}
2516+
2517+
/*--- Now do Gramm-Schmidt Process for symmetries that are not both curved lines or planes. ---*/
2518+
24692519
/*--- Loop over all markers and find nodes on symmetry planes that are shared with other symmetries. ---*/
24702520
/*--- The first symmetry does not need a corrected normal vector, hence start at 1. ---*/
24712521
for (size_t i = 1; i < symMarkers.size(); ++i) {
@@ -2477,49 +2527,166 @@ void CGeometry::ComputeModifiedSymmetryNormals(const CConfig* config) {
24772527
/*--- Halo points do not need to be considered. ---*/
24782528
if (!nodes->GetDomain(iPoint)) continue;
24792529

2480-
/*--- Get the vertex normal on the current symmetry. ---*/
2530+
/*--- Get the vertex normal on the current symmetry, which may be a merged normal. ---*/
2531+
auto GetNormal = [&](unsigned long iMarker, unsigned long iVertex, std::array<su2double, MAXNDIM>& normal) {
2532+
const auto it = symmetryNormals[iMarker].find(iVertex);
2533+
if (it != symmetryNormals[iMarker].end()) {
2534+
normal = it->second;
2535+
} else {
2536+
vertex[iMarker][iVertex]->GetNormal(normal.data());
2537+
const su2double jarea = GeometryToolbox::Norm(int(MAXNDIM), normal.data());
2538+
for (auto iDim = 0ul; iDim < MAXNDIM; ++iDim) normal[iDim] /= jarea;
2539+
}
2540+
};
24812541
std::array<su2double, MAXNDIM> iNormal = {};
2482-
vertex[iMarker][iVertex]->GetNormal(iNormal.data());
2542+
GetNormal(iMarker, iVertex, iNormal);
24832543

2484-
/*--- Loop over previous symmetries and if this point shares them, make this normal orthogonal to them. ---*/
2485-
bool isShared = false;
2544+
/*--- Loop over previous symmetries and if this point shares them, make this normal orthogonal to them.
2545+
* It's ok if we normalize merged normals against themselves, we get 0 area and this becomes a no-op. ---*/
24862546

24872547
for (size_t j = 0; j < i; ++j) {
24882548
const auto jMarker = symMarkers[j];
24892549
const auto jVertex = nodes->GetVertex(iPoint, jMarker);
24902550
if (jVertex < 0) continue;
24912551

2492-
isShared = true;
2493-
24942552
std::array<su2double, MAXNDIM> jNormal = {};
2495-
const auto it = symmetryNormals[jMarker].find(jVertex);
2496-
2497-
if (it != symmetryNormals[jMarker].end()) {
2498-
jNormal = it->second;
2499-
} else {
2500-
vertex[jMarker][jVertex]->GetNormal(jNormal.data());
2501-
const su2double area = GeometryToolbox::Norm(nDim, jNormal.data());
2502-
for (auto iDim = 0u; iDim < nDim; iDim++) jNormal[iDim] /= area;
2503-
}
2553+
GetNormal(jMarker, jVertex, jNormal);
25042554

2505-
const auto proj = GeometryToolbox::DotProduct(nDim, jNormal.data(), iNormal.data());
2506-
for (auto iDim = 0u; iDim < nDim; iDim++) iNormal[iDim] -= proj * jNormal[iDim];
2555+
const su2double proj = GeometryToolbox::DotProduct(int(MAXNDIM), jNormal.data(), iNormal.data());
2556+
for (auto iDim = 0ul; iDim < MAXNDIM; ++iDim) iNormal[iDim] -= proj * jNormal[iDim];
25072557
}
25082558

2509-
if (!isShared) continue;
2510-
25112559
/*--- Normalize. If the norm is close to zero it means the normal is a linear combination of previous
25122560
* normals, in this case we don't need to store the corrected normal, using the original in the gradient
25132561
* correction will have no effect since previous corrections will remove components in this direction). ---*/
2514-
const su2double area = GeometryToolbox::Norm(nDim, iNormal.data());
2515-
if (area > EPS) {
2516-
for (auto iDim = 0u; iDim < nDim; iDim++) iNormal[iDim] /= area;
2562+
const su2double area = GeometryToolbox::Norm(int(MAXNDIM), iNormal.data());
2563+
if (area > 1e-12) {
2564+
for (auto iDim = 0ul; iDim < MAXNDIM; ++iDim) iNormal[iDim] /= area;
25172565
symmetryNormals[iMarker][iVertex] = iNormal;
25182566
}
25192567
}
25202568
}
25212569
}
25222570

2571+
void CGeometry::ComputeSurfStraightness(const CConfig* config, bool print_on_screen) {
2572+
bool RefUnitNormal_defined;
2573+
unsigned short iDim, iMarker, iMarker_Global, nMarker_Global = config->GetnMarker_CfgFile();
2574+
unsigned long iVertex;
2575+
constexpr passivedouble epsilon = 1.0e-6;
2576+
su2double Area;
2577+
string Local_TagBound, Global_TagBound;
2578+
2579+
vector<su2double> Normal(nDim), UnitNormal(nDim), RefUnitNormal(nDim);
2580+
2581+
/*--- Assume now that this boundary marker is straight. As soon as one
2582+
AreaElement is found that is not aligend with a Reference then it is
2583+
certain that the boundary marker is not straight and one can stop
2584+
searching. Another possibility is that this process doesn't own
2585+
any nodes of that boundary, in that case we also have to assume the
2586+
boundary is straight.
2587+
Any boundary type other than SYMMETRY_PLANE or EULER_WALL gets
2588+
the value false (or see cases specified in the conditional below)
2589+
which could be wrong. ---*/
2590+
boundIsStraight.resize(nMarker);
2591+
fill(boundIsStraight.begin(), boundIsStraight.end(), true);
2592+
2593+
/*--- Loop over all local markers ---*/
2594+
for (iMarker = 0; iMarker < nMarker; iMarker++) {
2595+
Local_TagBound = config->GetMarker_All_TagBound(iMarker);
2596+
2597+
/*--- Marker has to be Symmetry or Euler. Additionally marker can't be a
2598+
moving surface and Grid Movement Elasticity is forbidden as well. All
2599+
other GridMovements are rigid. ---*/
2600+
if ((config->GetMarker_All_KindBC(iMarker) == SYMMETRY_PLANE ||
2601+
config->GetMarker_All_KindBC(iMarker) == EULER_WALL) &&
2602+
!config->GetMarker_Moving_Bool(Local_TagBound) && !config->GetMarker_Deform_Mesh_Bool(Local_TagBound)) {
2603+
/*--- Loop over all global markers, and find the local-global pair via
2604+
matching unique string tags. ---*/
2605+
for (iMarker_Global = 0; iMarker_Global < nMarker_Global; iMarker_Global++) {
2606+
Global_TagBound = config->GetMarker_CfgFile_TagBound(iMarker_Global);
2607+
if (Local_TagBound == Global_TagBound) {
2608+
RefUnitNormal_defined = false;
2609+
iVertex = 0;
2610+
2611+
while (boundIsStraight[iMarker] && iVertex < nVertex[iMarker]) {
2612+
vertex[iMarker][iVertex]->GetNormal(Normal.data());
2613+
UnitNormal = Normal;
2614+
2615+
/*--- Compute unit normal. ---*/
2616+
Area = 0.0;
2617+
for (iDim = 0; iDim < nDim; iDim++) Area += Normal[iDim] * Normal[iDim];
2618+
Area = sqrt(Area);
2619+
2620+
/*--- Negate for outward convention. ---*/
2621+
for (iDim = 0; iDim < nDim; iDim++) UnitNormal[iDim] /= -Area;
2622+
2623+
/*--- Check if unit normal is within tolerance of the Reference unit normal.
2624+
Reference unit normal = first unit normal found. ---*/
2625+
if (RefUnitNormal_defined) {
2626+
for (iDim = 0; iDim < nDim; iDim++) {
2627+
if (abs(RefUnitNormal[iDim] - UnitNormal[iDim]) > epsilon) {
2628+
boundIsStraight[iMarker] = false;
2629+
break;
2630+
}
2631+
}
2632+
} else {
2633+
RefUnitNormal = UnitNormal; // deep copy of values
2634+
RefUnitNormal_defined = true;
2635+
}
2636+
2637+
iVertex++;
2638+
} // while iVertex
2639+
} // if Local == Global
2640+
} // for iMarker_Global
2641+
} else {
2642+
/*--- Enforce default value: false ---*/
2643+
boundIsStraight[iMarker] = false;
2644+
} // if sym or euler ...
2645+
} // for iMarker
2646+
2647+
/*--- Communicate results and print on screen. ---*/
2648+
if (print_on_screen) {
2649+
/*--- Additional vector which can later be MPI::Allreduce(d) to pring the results
2650+
on screen as nMarker (local) can vary across ranks. Default 'true' as it can
2651+
happen that a local rank does not contain an element of each surface marker. ---*/
2652+
vector<bool> boundIsStraightGlobal(nMarker_Global, true);
2653+
/*--- Match local with global tag bound and fill a Global Marker vector. ---*/
2654+
for (iMarker = 0; iMarker < nMarker; iMarker++) {
2655+
Local_TagBound = config->GetMarker_All_TagBound(iMarker);
2656+
for (iMarker_Global = 0; iMarker_Global < nMarker_Global; iMarker_Global++) {
2657+
Global_TagBound = config->GetMarker_CfgFile_TagBound(iMarker_Global);
2658+
2659+
if (Local_TagBound == Global_TagBound) boundIsStraightGlobal[iMarker_Global] = boundIsStraight[iMarker];
2660+
2661+
} // for iMarker_Global
2662+
} // for iMarker
2663+
2664+
vector<int> Buff_Send_isStraight(nMarker_Global), Buff_Recv_isStraight(nMarker_Global);
2665+
2666+
/*--- Cast to int as std::vector<boolean> can be a special construct. MPI handling using <int>
2667+
is more straight-forward. ---*/
2668+
for (iMarker_Global = 0; iMarker_Global < nMarker_Global; iMarker_Global++)
2669+
Buff_Send_isStraight[iMarker_Global] = static_cast<int>(boundIsStraightGlobal[iMarker_Global]);
2670+
2671+
/*--- Product of type <int>(bool) is equivalnt to a 'logical and' ---*/
2672+
SU2_MPI::Allreduce(Buff_Send_isStraight.data(), Buff_Recv_isStraight.data(), nMarker_Global, MPI_INT, MPI_PROD,
2673+
SU2_MPI::GetComm());
2674+
2675+
/*--- Print results on screen. ---*/
2676+
if (rank == MASTER_NODE) {
2677+
for (iMarker_Global = 0; iMarker_Global < nMarker_Global; iMarker_Global++) {
2678+
if (config->GetMarker_CfgFile_KindBC(config->GetMarker_CfgFile_TagBound(iMarker_Global)) == SYMMETRY_PLANE ||
2679+
config->GetMarker_CfgFile_KindBC(config->GetMarker_CfgFile_TagBound(iMarker_Global)) == EULER_WALL) {
2680+
cout << "Boundary marker " << config->GetMarker_CfgFile_TagBound(iMarker_Global) << " is";
2681+
if (!static_cast<bool>(Buff_Recv_isStraight[iMarker_Global])) cout << " NOT";
2682+
if (nDim == 2) cout << " a single straight." << endl;
2683+
if (nDim == 3) cout << " a single plane." << endl;
2684+
} // if sym or euler
2685+
} // for iMarker_Global
2686+
} // if rank==MASTER
2687+
} // if print_on_scren
2688+
}
2689+
25232690
void CGeometry::ComputeSurf_Curvature(CConfig* config) {
25242691
unsigned short iMarker, iNeigh_Point, iDim, iNode, iNeighbor_Nodes, Neighbor_Node;
25252692
unsigned long Neighbor_Point, iVertex, iPoint, jPoint, iElem_Bound, iEdge, nLocalVertex, MaxLocalVertex,
Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
INDEX GRAD
2-
0 -5.239274142145693e-03
3-
1 -2.822866261832595e-03
4-
2 -1.251166631047082e-03
5-
3 -4.746312021502222e-04
6-
4 -4.726421390156573e-04
7-
5 -1.242307153964931e-03
8-
6 -2.797799332331688e-03
9-
7 -5.181304648315885e-03
2+
0 -3.533163525398154e-03
3+
1 -1.879941699367452e-03
4+
2 -8.078436962531523e-04
5+
3 -2.794845540524826e-04
6+
4 -2.790760674146328e-04
7+
5 -8.045490756184532e-04
8+
6 -1.867166054373919e-03
9+
7 -3.498473613679959e-03

TestCases/hybrid_regression.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ def main():
471471
cosine_gust.cfg_dir = "gust"
472472
cosine_gust.cfg_file = "cosine_gust_zdir.cfg"
473473
cosine_gust.test_iter = 79
474-
cosine_gust.test_vals = [-2.418805, 0.002013, -0.001504, 0.000445, -0.000558]
474+
cosine_gust.test_vals = [-2.418806, 0.001963, -0.001264, 0.000415, -0.000592]
475475
cosine_gust.unsteady = True
476476
cosine_gust.enabled_with_tsan = False
477477
test_list.append(cosine_gust)
@@ -543,7 +543,7 @@ def main():
543543
edge_PPR.cfg_dir = "nicf/edge"
544544
edge_PPR.cfg_file = "edge_PPR.cfg"
545545
edge_PPR.test_iter = 40
546-
edge_PPR.test_vals = [-6.922907, -0.757793, -0.000034, 0.000000]
546+
edge_PPR.test_vals = [-6.923001, -0.757902, -0.000034, 0.000000]
547547
edge_PPR.test_vals_aarch64 = [-8.573595, -2.391849, -0.000034, 0.000000]
548548
test_list.append(edge_PPR)
549549

TestCases/hybrid_regression_AD.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def main():
5858
discadj_cylinder3D.cfg_dir = "disc_adj_euler/cylinder3D"
5959
discadj_cylinder3D.cfg_file = "inv_cylinder3D.cfg"
6060
discadj_cylinder3D.test_iter = 5
61-
discadj_cylinder3D.test_vals = [-3.764562, -3.719982, -0.000000, 0.000000]
61+
discadj_cylinder3D.test_vals = [-3.773755, -3.737672, -0.000000, 0.000000]
6262
test_list.append(discadj_cylinder3D)
6363

6464
# Arina nozzle 2D

0 commit comments

Comments
 (0)