LCOV - code coverage report
Current view: top level - src/utils - FillBetweenPointVectorsTools.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 312 341 91.5 %
Date: 2025-07-17 01:28:37 Functions: 17 22 77.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #include "FillBetweenPointVectorsTools.h"
      11             : #include "MooseMeshUtils.h"
      12             : #include "MooseMesh.h"
      13             : #include "MeshGenerator.h"
      14             : #include "MooseError.h"
      15             : 
      16             : // libMesh includes
      17             : #include "libmesh/int_range.h"
      18             : #include "libmesh/mesh_base.h"
      19             : #include "libmesh/mesh_generation.h"
      20             : #include "libmesh/mesh_serializer.h"
      21             : #include "libmesh/point.h"
      22             : #include "libmesh/elem.h"
      23             : #include "libmesh/node.h"
      24             : #include "libmesh/face_tri3.h"
      25             : #include "libmesh/face_quad4.h"
      26             : 
      27             : using namespace libMesh;
      28             : 
      29             : namespace FillBetweenPointVectorsTools
      30             : {
      31             : void
      32         143 : fillBetweenPointVectorsGenerator(MeshBase & mesh, // an empty mesh is expected
      33             :                                  const std::vector<Point> & boundary_points_vec_1,
      34             :                                  const std::vector<Point> & boundary_points_vec_2,
      35             :                                  const unsigned int num_layers,
      36             :                                  const subdomain_id_type transition_layer_id,
      37             :                                  const boundary_id_type input_boundary_1_id,
      38             :                                  const boundary_id_type input_boundary_2_id,
      39             :                                  const boundary_id_type begin_side_boundary_id,
      40             :                                  const boundary_id_type end_side_boundary_id,
      41             :                                  const std::string type,
      42             :                                  const std::string name,
      43             :                                  const bool quad_elem,
      44             :                                  const Real bias_parameter,
      45             :                                  const Real sigma)
      46             : {
      47         286 :   if (!MooseMeshUtils::isCoPlanar(
      48         429 :           boundary_points_vec_1, Point(0.0, 0.0, 1.0), Point(0.0, 0.0, 0.0)) ||
      49         286 :       !MooseMeshUtils::isCoPlanar(
      50             :           boundary_points_vec_2, Point(0.0, 0.0, 1.0), Point(0.0, 0.0, 0.0)))
      51           4 :     mooseError("In ",
      52             :                type,
      53             :                " ",
      54             :                name,
      55             :                ", the input vectors of points for "
      56             :                "FillBetweenPointVectorsTools::fillBetweenPointVectorsGenerator "
      57             :                "must be in XY plane.");
      58             : 
      59         139 :   const unsigned int vec_1_node_num = boundary_points_vec_1.size();
      60         139 :   const unsigned int vec_2_node_num = boundary_points_vec_2.size();
      61             : 
      62         139 :   if (vec_1_node_num < 2 || vec_2_node_num < 2)
      63           4 :     mooseError("In ",
      64             :                type,
      65             :                " ",
      66             :                name,
      67             :                ", the two input vectors of points for "
      68             :                "FillBetweenPointVectorsTools::fillBetweenPointVectorsGenerator "
      69             :                "must respectively contain at least two elements.");
      70             : 
      71         135 :   if (quad_elem && boundary_points_vec_1.size() != boundary_points_vec_2.size())
      72           4 :     mooseError("In ",
      73             :                type,
      74             :                " ",
      75             :                name,
      76             :                ", QUAD4 elements option can only be selected when the two input vectors of Points "
      77             :                "have the same length. In the current instance, the first vector has ",
      78           4 :                boundary_points_vec_1.size(),
      79             :                " points and the second ",
      80           4 :                boundary_points_vec_2.size(),
      81             :                " points");
      82             : 
      83         131 :   std::vector<Point> possibly_reoriented_boundary_points_vec_2;
      84         131 :   const std::vector<Point> * oriented_boundary_points_vec_2 = &boundary_points_vec_2;
      85             : 
      86         131 :   if (needFlip(boundary_points_vec_1, boundary_points_vec_2))
      87             :   {
      88           0 :     possibly_reoriented_boundary_points_vec_2.assign(boundary_points_vec_2.rbegin(),
      89           0 :                                                      boundary_points_vec_2.rend());
      90           0 :     oriented_boundary_points_vec_2 = &possibly_reoriented_boundary_points_vec_2;
      91             : 
      92             :     // This isn't worth warning about.  The way
      93             :     // MooseMeshUtils::makeOrderedNodeList works, we can end up
      94             :     // finding a flip necessary on one element numbering and
      95             :     // unnecessary on another.
      96             :     //
      97             :     // mooseWarning(
      98             :     //     "In FillBetweenPointVectorsTools, one of the vector of Points must be flipped to ensure "
      99             :     //     "correct transition layer shape.");
     100             :   }
     101             : 
     102         131 :   std::vector<Real> vec_1_index; // Unweighted index
     103         131 :   std::vector<Real> vec_2_index; // Unweighted index
     104             : 
     105         131 :   std::vector<Real> wt_1;
     106         131 :   std::vector<Real> index_1; // Weighted index
     107         131 :   std::vector<Real> wt_2;
     108         131 :   std::vector<Real> index_2; // Weighted index
     109             : 
     110             :   // create interpolations
     111         131 :   std::unique_ptr<LinearInterpolation> linear_vec_1_x;
     112         131 :   std::unique_ptr<LinearInterpolation> linear_vec_1_y;
     113         131 :   std::unique_ptr<SplineInterpolation> spline_vec_1_l;
     114         131 :   std::unique_ptr<LinearInterpolation> linear_vec_2_x;
     115         131 :   std::unique_ptr<LinearInterpolation> linear_vec_2_y;
     116         131 :   std::unique_ptr<SplineInterpolation> spline_vec_2_l;
     117             : 
     118         131 :   weightedInterpolator(vec_1_node_num,
     119             :                        boundary_points_vec_1,
     120             :                        vec_1_index,
     121             :                        wt_1,
     122             :                        index_1,
     123             :                        sigma,
     124             :                        linear_vec_1_x,
     125             :                        linear_vec_1_y,
     126             :                        spline_vec_1_l);
     127         131 :   weightedInterpolator(vec_2_node_num,
     128             :                        *oriented_boundary_points_vec_2,
     129             :                        vec_2_index,
     130             :                        wt_2,
     131             :                        index_2,
     132             :                        sigma,
     133             :                        linear_vec_2_x,
     134             :                        linear_vec_2_y,
     135             :                        spline_vec_2_l);
     136             : 
     137             :   // If the two input vectors have different sizes
     138             :   // The node numbers of the intermediate layers change linearly
     139         131 :   const Real increment = ((Real)vec_2_node_num - (Real)vec_1_node_num) / (Real)(num_layers);
     140             :   // Number of nodes in each sublayer
     141         131 :   std::vector<unsigned int> node_number_vec;
     142             :   // 2D vector of nodes
     143         131 :   std::vector<std::vector<Node *>> nodes(num_layers + 1);
     144             :   // Node counter
     145         131 :   unsigned int node_counter = 0;
     146             : 
     147         720 :   for (const auto i : make_range(num_layers + 1))
     148             :   {
     149             :     // calculate number of nodes in each sublayer
     150         589 :     node_number_vec.push_back(
     151         589 :         (unsigned int)(vec_1_node_num + (long)(increment * i + 0.5 - (increment < 0))));
     152             :     // Reserve memory for new nodes
     153         589 :     nodes[i] = std::vector<Node *>(node_number_vec[i]);
     154             : 
     155             :     // Calculate vectors of weighted surrogated index for side #1
     156         589 :     std::vector<Real> weighted_surrogate_index_1;
     157         589 :     std::vector<Real> unweighted_surrogate_index_1;
     158             : 
     159         589 :     surrogateGenerator(weighted_surrogate_index_1,
     160             :                        unweighted_surrogate_index_1,
     161             :                        node_number_vec,
     162             :                        wt_1,
     163             :                        vec_1_index,
     164             :                        vec_1_node_num,
     165             :                        i);
     166             : 
     167             :     // Calculate vectors of weighted surrogated index for side #2
     168         589 :     std::vector<Real> weighted_surrogate_index_2;
     169         589 :     std::vector<Real> unweighted_surrogate_index_2;
     170             : 
     171         589 :     surrogateGenerator(weighted_surrogate_index_2,
     172             :                        unweighted_surrogate_index_2,
     173             :                        node_number_vec,
     174             :                        wt_2,
     175             :                        vec_2_index,
     176             :                        vec_2_node_num,
     177             :                        i);
     178             : 
     179        5312 :     for (const auto j : make_range(node_number_vec[i]))
     180             :     {
     181             :       // Create surrogate Points on side #1 for Point #j on the sublayer
     182        4723 :       Point surrogate_pos_1 = Point(linear_vec_1_x->sample(weighted_surrogate_index_1[j]),
     183        4723 :                                     linear_vec_1_y->sample(weighted_surrogate_index_1[j]),
     184       14169 :                                     0.0);
     185             :       // Create surrogate Points on side #2 for Point #j on the sublayer
     186        4723 :       Point surrogate_pos_2 = Point(linear_vec_2_x->sample(weighted_surrogate_index_2[j]),
     187        4723 :                                     linear_vec_2_y->sample(weighted_surrogate_index_2[j]),
     188       14169 :                                     0.0);
     189             :       const Real l_ratio = bias_parameter <= 0.0
     190        5871 :                                ? std::pow(spline_vec_2_l->sample(weighted_surrogate_index_2[j]) /
     191        1148 :                                               spline_vec_1_l->sample(weighted_surrogate_index_1[j]),
     192        1148 :                                           1.0 / ((Real)num_layers - 1.0))
     193        4723 :                                : bias_parameter;
     194             :       const Real index_factor =
     195        4723 :           MooseUtils::absoluteFuzzyEqual(l_ratio, 1.0)
     196        4723 :               ? (Real)i / (Real)num_layers
     197        4723 :               : (1.0 - std::pow(l_ratio, (Real)i)) / (1.0 - std::pow(l_ratio, (Real)num_layers));
     198        4723 :       Point tmp_point = surrogate_pos_2 * index_factor + surrogate_pos_1 * (1.0 - index_factor);
     199        4723 :       nodes[i][j] = mesh.add_point(tmp_point, j + node_counter);
     200             :     }
     201         589 :     node_counter += node_number_vec[i];
     202         589 :   }
     203             :   // Create triangular elements based on the 2D Node vector
     204         131 :   if (quad_elem)
     205          49 :     elementsCreationFromNodesVectorsQuad(mesh,
     206             :                                          nodes,
     207             :                                          num_layers,
     208             :                                          node_number_vec,
     209             :                                          transition_layer_id,
     210             :                                          input_boundary_1_id,
     211             :                                          input_boundary_2_id,
     212             :                                          begin_side_boundary_id,
     213             :                                          end_side_boundary_id);
     214             :   else
     215          82 :     elementsCreationFromNodesVectors(mesh,
     216             :                                      nodes,
     217             :                                      num_layers,
     218             :                                      node_number_vec,
     219             :                                      transition_layer_id,
     220             :                                      input_boundary_1_id,
     221             :                                      input_boundary_2_id,
     222             :                                      begin_side_boundary_id,
     223             :                                      end_side_boundary_id);
     224         131 : }
     225             : 
     226             : void
     227           0 : fillBetweenPointVectorsGenerator(MeshBase & mesh,
     228             :                                  const std::vector<Point> & boundary_points_vec_1,
     229             :                                  const std::vector<Point> & boundary_points_vec_2,
     230             :                                  const unsigned int num_layers,
     231             :                                  const subdomain_id_type transition_layer_id,
     232             :                                  const boundary_id_type external_boundary_id,
     233             :                                  const std::string type,
     234             :                                  const std::string name,
     235             :                                  const bool quad_elem)
     236             : {
     237           0 :   fillBetweenPointVectorsGenerator(mesh,
     238             :                                    boundary_points_vec_1,
     239             :                                    boundary_points_vec_2,
     240             :                                    num_layers,
     241             :                                    transition_layer_id,
     242             :                                    external_boundary_id,
     243             :                                    external_boundary_id,
     244             :                                    external_boundary_id,
     245             :                                    external_boundary_id,
     246             :                                    type,
     247             :                                    name,
     248             :                                    quad_elem);
     249           0 : }
     250             : 
     251             : void
     252          49 : elementsCreationFromNodesVectorsQuad(MeshBase & mesh,
     253             :                                      const std::vector<std::vector<Node *>> & nodes,
     254             :                                      const unsigned int num_layers,
     255             :                                      const std::vector<unsigned int> & node_number_vec,
     256             :                                      const subdomain_id_type transition_layer_id,
     257             :                                      const boundary_id_type input_boundary_1_id,
     258             :                                      const boundary_id_type input_boundary_2_id,
     259             :                                      const boundary_id_type begin_side_boundary_id,
     260             :                                      const boundary_id_type end_side_boundary_id)
     261             : {
     262          49 :   const unsigned int node_number = node_number_vec.front();
     263          49 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
     264             : 
     265         261 :   for (const auto i : make_range(num_layers))
     266        1736 :     for (unsigned int j = 1; j < node_number; j++)
     267             :     {
     268        1524 :       Elem * elem = mesh.add_elem(new Quad4);
     269        1524 :       bool is_elem_flip = buildQuadElement(elem,
     270        1524 :                                            nodes[i][j - 1],
     271        1524 :                                            nodes[i + 1][j - 1],
     272        1524 :                                            nodes[i + 1][j],
     273        1524 :                                            nodes[i][j],
     274             :                                            transition_layer_id);
     275        1524 :       if (i == 0)
     276         326 :         boundary_info.add_side(elem, is_elem_flip ? 0 : 3, input_boundary_1_id);
     277        1524 :       if (i == num_layers - 1)
     278         326 :         boundary_info.add_side(elem, is_elem_flip ? 2 : 1, input_boundary_2_id);
     279        1524 :       if (j == 1)
     280         212 :         boundary_info.add_side(elem, is_elem_flip ? 3 : 0, begin_side_boundary_id);
     281        1524 :       if (j == node_number - 1)
     282         212 :         boundary_info.add_side(elem, is_elem_flip ? 1 : 2, end_side_boundary_id);
     283             :     }
     284          49 : }
     285             : 
     286             : void
     287          82 : elementsCreationFromNodesVectors(MeshBase & mesh,
     288             :                                  const std::vector<std::vector<Node *>> & nodes,
     289             :                                  const unsigned int num_layers,
     290             :                                  const std::vector<unsigned int> & node_number_vec,
     291             :                                  const subdomain_id_type transition_layer_id,
     292             :                                  const boundary_id_type input_boundary_1_id,
     293             :                                  const boundary_id_type input_boundary_2_id,
     294             :                                  const boundary_id_type begin_side_boundary_id,
     295             :                                  const boundary_id_type end_side_boundary_id)
     296             : {
     297          82 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
     298             : 
     299         328 :   for (const auto i : make_range(num_layers))
     300             :   {
     301         246 :     unsigned int nodes_up_it = 0;
     302         246 :     unsigned int nodes_down_it = 0;
     303         246 :     const unsigned int node_number_up = node_number_vec[i + 1];
     304         246 :     const unsigned int node_number_down = node_number_vec[i];
     305             : 
     306        3318 :     while (nodes_up_it < node_number_up - 1 && nodes_down_it < node_number_down - 1 &&
     307        3072 :            nodes_up_it + nodes_down_it < node_number_up + node_number_down - 3)
     308             :     {
     309             :       // Define the two possible options and chose the one with shorter distance
     310        3072 :       Real dis1 = (*nodes[i + 1][nodes_up_it] - *nodes[i][nodes_down_it + 1]).norm();
     311        3072 :       Real dis2 = (*nodes[i + 1][nodes_up_it + 1] - *nodes[i][nodes_down_it]).norm();
     312        3072 :       if (MooseUtils::absoluteFuzzyGreaterThan(dis1, dis2))
     313             :       {
     314        1552 :         Elem * elem = mesh.add_elem(new Tri3);
     315        1552 :         bool is_elem_flip = buildTriElement(elem,
     316        1552 :                                             nodes[i + 1][nodes_up_it],
     317        1552 :                                             nodes[i][nodes_down_it],
     318        1552 :                                             nodes[i + 1][nodes_up_it + 1],
     319             :                                             transition_layer_id);
     320        1552 :         if (i == num_layers - 1)
     321         452 :           boundary_info.add_side(elem, is_elem_flip ? 0 : 2, input_boundary_2_id);
     322        1552 :         if (nodes_up_it == 0 && nodes_down_it == 0)
     323          80 :           boundary_info.add_side(elem, is_elem_flip ? 2 : 0, begin_side_boundary_id);
     324        1552 :         nodes_up_it++;
     325             :       }
     326             :       else
     327             :       {
     328        1520 :         Elem * elem = mesh.add_elem(new Tri3);
     329        1520 :         bool is_elem_flip = buildTriElement(elem,
     330        1520 :                                             nodes[i + 1][nodes_up_it],
     331        1520 :                                             nodes[i][nodes_down_it],
     332        1520 :                                             nodes[i][nodes_down_it + 1],
     333             :                                             transition_layer_id);
     334        1520 :         if (i == 0)
     335         496 :           boundary_info.add_side(elem, 1, input_boundary_1_id);
     336        1520 :         if (nodes_up_it == 0 && nodes_down_it == 0)
     337         166 :           boundary_info.add_side(elem, is_elem_flip ? 2 : 0, begin_side_boundary_id);
     338        1520 :         nodes_down_it++;
     339             :       }
     340             :     }
     341             :     // Handle the end
     342         376 :     while (nodes_up_it < node_number_up - 1)
     343             :     {
     344         130 :       Elem * elem = mesh.add_elem(new Tri3);
     345         130 :       bool is_elem_flip = buildTriElement(elem,
     346         130 :                                           nodes[i + 1][nodes_up_it],
     347         130 :                                           nodes[i][nodes_down_it],
     348         130 :                                           nodes[i + 1][nodes_up_it + 1],
     349             :                                           transition_layer_id);
     350         130 :       nodes_up_it++;
     351         130 :       if (i == num_layers - 1)
     352          88 :         boundary_info.add_side(elem, is_elem_flip ? 0 : 2, input_boundary_2_id);
     353         130 :       if (nodes_up_it == node_number_up - 1 && nodes_down_it == node_number_down - 1)
     354          66 :         boundary_info.add_side(elem, 1, end_side_boundary_id);
     355             :     }
     356         470 :     while (nodes_down_it < node_number_down - 1)
     357             :     {
     358         224 :       Elem * elem = mesh.add_elem(new Tri3);
     359         224 :       bool is_elem_flip = buildTriElement(elem,
     360         224 :                                           nodes[i + 1][nodes_up_it],
     361         224 :                                           nodes[i][nodes_down_it],
     362         224 :                                           nodes[i][nodes_down_it + 1],
     363             :                                           transition_layer_id);
     364         224 :       nodes_down_it++;
     365         224 :       if (i == 0)
     366         106 :         boundary_info.add_side(elem, 1, input_boundary_1_id);
     367         224 :       if (nodes_up_it == node_number_up - 1 && nodes_down_it == node_number_down - 1)
     368         180 :         boundary_info.add_side(elem, is_elem_flip ? 0 : 2, end_side_boundary_id);
     369             :     }
     370             :   }
     371          82 : }
     372             : 
     373             : void
     374         262 : weightedInterpolator(const unsigned int vec_node_num,
     375             :                      const std::vector<Point> & boundary_points_vec,
     376             :                      std::vector<Real> & vec_index,
     377             :                      std::vector<Real> & wt,
     378             :                      std::vector<Real> & index,
     379             :                      const Real sigma,
     380             :                      std::unique_ptr<LinearInterpolation> & linear_vec_x,
     381             :                      std::unique_ptr<LinearInterpolation> & linear_vec_y,
     382             :                      std::unique_ptr<SplineInterpolation> & spline_vec_l)
     383             : {
     384         262 :   std::vector<Real> pos_x;
     385         262 :   std::vector<Real> pos_y;
     386         262 :   std::vector<Real> dist_vec;
     387         262 :   std::vector<Real> pos_l;
     388             : 
     389        2318 :   for (const auto i : make_range(vec_node_num))
     390             :   {
     391             :     // Unweighted, the index interval is just uniform
     392             :     // Normalized range 0~1
     393        2056 :     vec_index.push_back((Real)i / ((Real)vec_node_num - 1.0));
     394             :     // X and Y coordinates cooresponding to the index
     395        2056 :     pos_x.push_back(boundary_points_vec[i](0));
     396        2056 :     pos_y.push_back(boundary_points_vec[i](1));
     397             :     // Use Point-to-Point distance as unnormalized weight
     398        2056 :     if (i > 0)
     399             :     {
     400        1794 :       wt.push_back((boundary_points_vec[i] - boundary_points_vec[i - 1]).norm());
     401        1794 :       dist_vec.push_back(wt.back());
     402             :     }
     403             :     // Accumulated unnormalized weights to get unnormalized weighted index
     404        2056 :     index.push_back(std::accumulate(wt.begin(), wt.end(), 0.0));
     405             :   }
     406         262 :   const Real dist_vec_total = index.back(); // Total accumulated distances
     407         262 :   const Real wt_norm_factor = dist_vec_total / ((Real)vec_node_num - 1.0); // Normalization factor
     408             :   // Normalization for both weights and weighted indices
     409         262 :   std::transform(
     410        1794 :       wt.begin(), wt.end(), wt.begin(), [wt_norm_factor](Real & c) { return c / wt_norm_factor; });
     411         262 :   std::transform(index.begin(),
     412             :                  index.end(),
     413             :                  index.begin(),
     414        2056 :                  [dist_vec_total](Real & c) { return c / dist_vec_total; });
     415             :   // Use Gaussian blurring to smoothen local density
     416        2318 :   for (const auto i : make_range(vec_node_num))
     417             :   {
     418        2056 :     Real gaussian_factor(0.0);
     419        2056 :     Real sum_tmp(0.0);
     420             :     // Use interval as parameter now, consider distance in the future
     421       17384 :     for (const auto j : make_range(vec_node_num - 1))
     422             :     {
     423             :       // dis_vec and index are off by 0.5
     424             :       const Real tmp_factor =
     425       15328 :           exp(-((Real)(i - j) - 0.5) * ((Real)(i - j) - 0.5) / 2.0 / sigma / sigma);
     426       15328 :       gaussian_factor += tmp_factor;
     427       15328 :       sum_tmp += tmp_factor * dist_vec[j];
     428             :     }
     429        2056 :     pos_l.push_back(sum_tmp / gaussian_factor);
     430             :   }
     431             :   // Interpolate positions based on weighted indices
     432         262 :   linear_vec_x = std::make_unique<LinearInterpolation>(index, pos_x);
     433         262 :   linear_vec_y = std::make_unique<LinearInterpolation>(index, pos_y);
     434         262 :   spline_vec_l = std::make_unique<SplineInterpolation>(index, pos_l);
     435         262 : }
     436             : 
     437             : void
     438        1178 : surrogateGenerator(std::vector<Real> & weighted_surrogate_index,
     439             :                    std::vector<Real> & unweighted_surrogate_index,
     440             :                    const std::vector<unsigned int> & node_number_vec,
     441             :                    const std::vector<Real> & wt,
     442             :                    const std::vector<Real> & index,
     443             :                    const unsigned int boundary_node_num,
     444             :                    const unsigned int i)
     445             : {
     446             :   // First element is trivial
     447        1178 :   weighted_surrogate_index.push_back(0.0);
     448        1178 :   unweighted_surrogate_index.push_back(0.0);
     449        9446 :   for (unsigned int j = 1; j < node_number_vec[i]; j++)
     450             :   {
     451             :     // uniform interval for unweighted index
     452        8268 :     unweighted_surrogate_index.push_back((Real)j / ((Real)node_number_vec[i] - 1.0));
     453             :     // >
     454             :     const auto it_0 =
     455        8268 :         std::upper_bound(index.begin(), index.end(), unweighted_surrogate_index[j - 1]);
     456             :     // >=
     457        8268 :     const auto it_1 = std::lower_bound(index.begin(), index.end(), unweighted_surrogate_index[j]);
     458             :     //
     459        8268 :     const auto it_dist = std::distance(it_0, it_1);
     460             :     //
     461        8268 :     const auto it_start = std::distance(index.begin(), it_0);
     462             : 
     463        8268 :     if (it_0 == it_1)
     464       11700 :       weighted_surrogate_index.push_back(weighted_surrogate_index[j - 1] +
     465        5850 :                                          wt[it_start - 1] / ((Real)node_number_vec[i] - 1.0));
     466             :     else
     467             :     {
     468        2418 :       weighted_surrogate_index.push_back(weighted_surrogate_index[j - 1]);
     469        2418 :       weighted_surrogate_index[j] += (*it_0 - unweighted_surrogate_index[j - 1]) * wt[it_start - 1];
     470        4836 :       weighted_surrogate_index[j] +=
     471        2418 :           (unweighted_surrogate_index[j] - *(it_1 - 1)) * wt[it_start + it_dist - 1];
     472        2716 :       for (unsigned int k = 1; k < it_dist; k++)
     473         298 :         weighted_surrogate_index[j] += wt[it_start + k - 1] / ((Real)boundary_node_num - 1.0);
     474             :     }
     475             :   }
     476        1178 : }
     477             : 
     478             : bool
     479         131 : needFlip(const std::vector<Point> & vec_pts_1, const std::vector<Point> & vec_pts_2)
     480             : {
     481             :   const Real th1 =
     482         131 :       acos((vec_pts_1.back() - vec_pts_1.front()) * (vec_pts_2.front() - vec_pts_1.front()) /
     483         131 :            (vec_pts_1.back() - vec_pts_1.front()).norm() /
     484         131 :            (vec_pts_2.front() - vec_pts_1.front()).norm());
     485         131 :   const Real th2 = acos(
     486         131 :       (vec_pts_2.back() - vec_pts_1.back()) * (vec_pts_1.front() - vec_pts_1.back()) /
     487         131 :       (vec_pts_2.back() - vec_pts_1.back()).norm() / (vec_pts_1.front() - vec_pts_1.back()).norm());
     488         131 :   const Real th3 = acos(
     489         131 :       (vec_pts_2.front() - vec_pts_2.back()) * (vec_pts_1.back() - vec_pts_2.back()) /
     490         131 :       (vec_pts_2.front() - vec_pts_2.back()).norm() / (vec_pts_1.back() - vec_pts_2.back()).norm());
     491             :   const Real th4 =
     492         131 :       acos((vec_pts_1.front() - vec_pts_2.front()) * (vec_pts_2.back() - vec_pts_2.front()) /
     493         131 :            (vec_pts_1.front() - vec_pts_2.front()).norm() /
     494         131 :            (vec_pts_2.back() - vec_pts_2.front()).norm());
     495         131 :   if (MooseUtils::absoluteFuzzyEqual(th1 + th2 + th3 + th4, 2 * M_PI))
     496         131 :     return false;
     497           0 :   return true;
     498             : }
     499             : 
     500             : bool
     501         150 : isBoundarySimpleClosedLoop(MeshBase & mesh,
     502             :                            Real & max_node_radius,
     503             :                            std::vector<dof_id_type> & boundary_ordered_node_list,
     504             :                            const Point origin_pt,
     505             :                            const boundary_id_type bid)
     506             : {
     507             :   // This has no communication and expects elem_ptr to find any
     508             :   // element, so it only works on serialized meshes
     509         150 :   libMesh::MeshSerializer serial(mesh);
     510             : 
     511         150 :   max_node_radius = 0.0;
     512         150 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
     513         150 :   auto side_list_tmp = boundary_info.build_side_list();
     514         150 :   std::vector<std::pair<dof_id_type, dof_id_type>> boundary_node_assm;
     515         150 :   std::vector<dof_id_type> boundary_midpoint_node_list;
     516        3454 :   for (const auto i : index_range(side_list_tmp))
     517             :   {
     518        3304 :     if (std::get<2>(side_list_tmp[i]) == bid)
     519             :     {
     520             :       // store two nodes of each side
     521        1156 :       const auto elem = mesh.elem_ptr(std::get<0>(side_list_tmp[i]));
     522        1156 :       const auto side = elem->side_ptr(std::get<1>(side_list_tmp[i]));
     523        1156 :       boundary_node_assm.push_back(std::make_pair(side->node_id(0), side->node_id(1)));
     524             :       // see if there is a midpoint
     525        1156 :       const auto & side_type = elem->side_type(std::get<1>(side_list_tmp[i]));
     526        1156 :       if (side_type == EDGE3)
     527           0 :         boundary_midpoint_node_list.push_back(
     528           0 :             elem->node_id(elem->n_vertices() + std::get<1>(side_list_tmp[i])));
     529             :       else
     530        1156 :         boundary_midpoint_node_list.push_back(DofObject::invalid_id);
     531        1156 :     }
     532             :   }
     533             :   bool is_closed_loop;
     534         442 :   isClosedLoop(mesh,
     535             :                max_node_radius,
     536             :                boundary_ordered_node_list,
     537             :                boundary_node_assm,
     538             :                boundary_midpoint_node_list,
     539             :                origin_pt,
     540             :                "external boundary",
     541             :                is_closed_loop);
     542           4 :   return is_closed_loop;
     543         588 : }
     544             : 
     545             : bool
     546           0 : isBoundarySimpleClosedLoop(MeshBase & mesh,
     547             :                            Real & max_node_radius,
     548             :                            const Point origin_pt,
     549             :                            const boundary_id_type bid)
     550             : {
     551           0 :   std::vector<dof_id_type> dummy_boundary_ordered_node_list;
     552           0 :   return isBoundarySimpleClosedLoop(
     553           0 :       mesh, max_node_radius, dummy_boundary_ordered_node_list, origin_pt, bid);
     554           0 : }
     555             : 
     556             : bool
     557           0 : isBoundarySimpleClosedLoop(MeshBase & mesh, const Point origin_pt, const boundary_id_type bid)
     558             : {
     559             :   Real dummy_max_node_radius;
     560           0 :   return isBoundarySimpleClosedLoop(mesh, dummy_max_node_radius, origin_pt, bid);
     561             : }
     562             : 
     563             : bool
     564         150 : isBoundaryOpenSingleSegment(MeshBase & mesh,
     565             :                             Real & max_node_radius,
     566             :                             std::vector<dof_id_type> & boundary_ordered_node_list,
     567             :                             const Point origin_pt,
     568             :                             const boundary_id_type bid)
     569             : {
     570             :   try
     571             :   {
     572         150 :     isBoundarySimpleClosedLoop(mesh, max_node_radius, boundary_ordered_node_list, origin_pt, bid);
     573             :   }
     574         146 :   catch (MooseException & e)
     575             :   {
     576         292 :     if (((std::string)e.what())
     577         146 :             .compare("This mesh generator does not work for the provided external boundary as it "
     578         146 :                      "is not a closed loop.") != 0)
     579           4 :       throw MooseException("The provided boundary is not an open single-segment boundary.");
     580             :     else
     581         142 :       return true;
     582         146 :   }
     583             : 
     584           4 :   throw MooseException("The provided boundary is closed loop, which is not supported.");
     585             : }
     586             : 
     587             : bool
     588         158 : isExternalBoundary(MeshBase & mesh, const boundary_id_type bid)
     589             : {
     590             :   // This has no communication and expects elem_ptr to find any
     591             :   // element, so it only works on serialized meshes
     592         158 :   libMesh::MeshSerializer serial(mesh);
     593             : 
     594         158 :   if (!mesh.is_prepared())
     595         158 :     mesh.find_neighbors();
     596         158 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
     597         158 :   auto side_list = boundary_info.build_side_list();
     598        3622 :   for (const auto i : index_range(side_list))
     599             :   {
     600        3464 :     if (std::get<2>(side_list[i]) == bid)
     601        1236 :       if (mesh.elem_ptr(std::get<0>(side_list[i]))->neighbor_ptr(std::get<1>(side_list[i])) !=
     602             :           nullptr)
     603           0 :         return false;
     604             :   }
     605         158 :   return true;
     606         158 : }
     607             : 
     608             : bool
     609          40 : isCurveSimpleClosedLoop(MeshBase & mesh,
     610             :                         Real & max_node_radius,
     611             :                         std::vector<dof_id_type> & ordered_node_list,
     612             :                         const Point origin_pt)
     613             : {
     614             :   // This has no communication and expects to loop over all elements
     615             :   // on every processor, so it only works on serialized meshes
     616          40 :   libMesh::MeshSerializer serial(mesh);
     617             : 
     618          40 :   max_node_radius = 0.0;
     619          40 :   std::vector<std::pair<dof_id_type, dof_id_type>> node_assm;
     620         280 :   for (auto it = mesh.active_elements_begin(); it != mesh.active_elements_end(); it++)
     621         280 :     node_assm.push_back(std::make_pair((*it)->node_id(0), (*it)->node_id(1)));
     622             :   bool is_closed_loop;
     623         120 :   isClosedLoop(
     624             :       mesh, max_node_radius, ordered_node_list, node_assm, origin_pt, "curve", is_closed_loop);
     625           0 :   return is_closed_loop;
     626          80 : }
     627             : 
     628             : bool
     629           0 : isCurveSimpleClosedLoop(MeshBase & mesh, Real & max_node_radius, const Point origin_pt)
     630             : {
     631           0 :   std::vector<dof_id_type> dummy_ordered_node_list;
     632           0 :   return isCurveSimpleClosedLoop(mesh, max_node_radius, dummy_ordered_node_list, origin_pt);
     633           0 : }
     634             : 
     635             : bool
     636           0 : isCurveSimpleClosedLoop(MeshBase & mesh, const Point origin_pt)
     637             : {
     638             :   Real dummy_max_node_radius;
     639           0 :   return isCurveSimpleClosedLoop(mesh, dummy_max_node_radius, origin_pt);
     640             : }
     641             : 
     642             : bool
     643          40 : isCurveOpenSingleSegment(MeshBase & mesh,
     644             :                          Real & max_node_radius,
     645             :                          std::vector<dof_id_type> & ordered_node_list,
     646             :                          const Point origin_pt)
     647             : {
     648             :   try
     649             :   {
     650          40 :     isCurveSimpleClosedLoop(mesh, max_node_radius, ordered_node_list, origin_pt);
     651             :   }
     652          40 :   catch (MooseException & e)
     653             :   {
     654          80 :     if (((std::string)e.what())
     655          40 :             .compare("This mesh generator does not work for the provided curve as it is not a "
     656          40 :                      "closed loop.") != 0)
     657           0 :       throw MooseException("The provided curve is not an open single-segment boundary.");
     658             :     else
     659          40 :       return true;
     660          40 :   }
     661           0 :   throw MooseException("The provided curve is closed loop, which is not supported.");
     662             :   return false;
     663             : }
     664             : 
     665             : void
     666         246 : isClosedLoop(MeshBase & mesh,
     667             :              Real & max_node_radius,
     668             :              std::vector<dof_id_type> & ordered_node_list,
     669             :              std::vector<std::pair<dof_id_type, dof_id_type>> & node_assm,
     670             :              std::vector<dof_id_type> & midpoint_node_list,
     671             :              const Point origin_pt,
     672             :              const std::string input_type,
     673             :              bool & is_closed_loop,
     674             :              const bool suppress_exception)
     675             : {
     676             :   // This has no communication and expects node_ptr to find any
     677             :   // node, so it only works on serialized meshes
     678         246 :   libMesh::MeshSerializer serial(mesh);
     679             : 
     680         246 :   std::vector<dof_id_type> dummy_elem_list = std::vector<dof_id_type>(node_assm.size(), 0);
     681         246 :   std::vector<dof_id_type> ordered_dummy_elem_list;
     682         246 :   is_closed_loop = false;
     683         246 :   MooseMeshUtils::makeOrderedNodeList(
     684             :       node_assm, dummy_elem_list, midpoint_node_list, ordered_node_list, ordered_dummy_elem_list);
     685             :   // If the code ever gets here, node_assm is empty.
     686             :   // If the ordered_node_list front and back are not the same, the boundary is not a loop.
     687             :   // This is not done inside the loop just for some potential applications in the future.
     688         242 :   if (ordered_node_list.front() != ordered_node_list.back())
     689             :   {
     690             :     // This is invalid type #2
     691         202 :     if (!suppress_exception)
     692         182 :       throw MooseException("This mesh generator does not work for the provided ",
     693             :                            input_type,
     694         364 :                            " as it is not a closed loop.");
     695             :   }
     696             :   // It the curve is a loop, check if azimuthal angles change monotonically
     697             :   else
     698             :   {
     699             :     // Utilize cross product here.
     700             :     // If azimuthal angles change monotonically,
     701             :     // the z components of the cross products are always negative or positive.
     702          40 :     std::vector<Real> ordered_node_azi_list;
     703         538 :     for (const auto i : make_range(ordered_node_list.size() - 1))
     704             :     {
     705         498 :       ordered_node_azi_list.push_back(
     706         498 :           (*mesh.node_ptr(ordered_node_list[i]) - origin_pt)
     707         498 :               .cross(*mesh.node_ptr(ordered_node_list[i + 1]) - origin_pt)(2));
     708             :       // Use this opportunity to calculate maximum radius
     709         498 :       max_node_radius =
     710         498 :           std::max((*mesh.node_ptr(ordered_node_list[i]) - origin_pt).norm(), max_node_radius);
     711             :     }
     712          40 :     std::sort(ordered_node_azi_list.begin(), ordered_node_azi_list.end());
     713          40 :     if (ordered_node_azi_list.front() * ordered_node_azi_list.back() < 0.0)
     714             :     {
     715             :       // This is invalid type #3
     716           0 :       if (!suppress_exception)
     717           0 :         throw MooseException(
     718             :             "This mesh generator does not work for the provided ",
     719             :             input_type,
     720           0 :             " as azimuthal angles of consecutive nodes do not change monotonically.");
     721             :     }
     722             :     else
     723          40 :       is_closed_loop = true;
     724          40 :   }
     725         618 : }
     726             : 
     727             : void
     728          96 : isClosedLoop(MeshBase & mesh,
     729             :              Real & max_node_radius,
     730             :              std::vector<dof_id_type> & ordered_node_list,
     731             :              std::vector<std::pair<dof_id_type, dof_id_type>> & node_assm,
     732             :              const Point origin_pt,
     733             :              const std::string input_type,
     734             :              bool & is_closed_loop,
     735             :              const bool suppress_exception)
     736             : {
     737          96 :   std::vector<dof_id_type> dummy_midpoint_node_list(node_assm.size(), DofObject::invalid_id);
     738         136 :   isClosedLoop(mesh,
     739             :                max_node_radius,
     740             :                ordered_node_list,
     741             :                node_assm,
     742             :                dummy_midpoint_node_list,
     743             :                origin_pt,
     744             :                input_type,
     745             :                is_closed_loop,
     746             :                suppress_exception);
     747          96 : }
     748             : 
     749             : bool
     750        1524 : buildQuadElement(Elem * elem,
     751             :                  Node * nd_0,
     752             :                  Node * nd_1,
     753             :                  Node * nd_2,
     754             :                  Node * nd_3,
     755             :                  const subdomain_id_type transition_layer_id)
     756             : {
     757             :   // Adjust the order of nodes in an element so that the mesh can be extruded in (0 0 1)
     758             :   // direction.
     759        1524 :   elem->subdomain_id() = transition_layer_id;
     760        1524 :   if (((*nd_1 - *nd_0).cross((*nd_2 - *nd_0)).unit())(2) > 0)
     761             :   {
     762         936 :     elem->set_node(0, nd_0);
     763         936 :     elem->set_node(1, nd_1);
     764         936 :     elem->set_node(2, nd_2);
     765         936 :     elem->set_node(3, nd_3);
     766         936 :     return false;
     767             :   }
     768             :   else
     769             :   {
     770         588 :     elem->set_node(0, nd_0);
     771         588 :     elem->set_node(3, nd_1);
     772         588 :     elem->set_node(2, nd_2);
     773         588 :     elem->set_node(1, nd_3);
     774         588 :     return true;
     775             :   }
     776             : }
     777             : 
     778             : bool
     779        3426 : buildTriElement(
     780             :     Elem * elem, Node * nd_0, Node * nd_1, Node * nd_2, const subdomain_id_type transition_layer_id)
     781             : {
     782             :   // Adjust the order of nodes in an element so that the mesh can be extruded in (0 0 1)
     783             :   // direction.
     784        3426 :   elem->subdomain_id() = transition_layer_id;
     785        3426 :   if (((*nd_1 - *nd_0).cross((*nd_2 - *nd_0)).unit())(2) > 0)
     786             :   {
     787        1170 :     elem->set_node(0, nd_0);
     788        1170 :     elem->set_node(1, nd_1);
     789        1170 :     elem->set_node(2, nd_2);
     790        1170 :     return false;
     791             :   }
     792             :   else
     793             :   {
     794        2256 :     elem->set_node(0, nd_0);
     795        2256 :     elem->set_node(2, nd_1);
     796        2256 :     elem->set_node(1, nd_2);
     797        2256 :     return true;
     798             :   }
     799             : }
     800             : }

Generated by: LCOV version 1.14