@@ -2456,16 +2456,66 @@ su2double CGeometry::GetSurfaceArea(const CConfig* config, unsigned short val_ma
24562456void 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+
25232690void 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,
0 commit comments