LCOV - code coverage report
Current view: top level - src/meshgenerators - XYDelaunayGenerator.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 9a5f1f Lines: 258 277 93.1 %
Date: 2026-06-21 21:23:42 Functions: 4 4 100.0 %
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 "XYDelaunayGenerator.h"
      11             : 
      12             : #include "CastUniquePointer.h"
      13             : #include "MooseMeshUtils.h"
      14             : #include "MeshTriangulationUtils.h"
      15             : #include "BoundaryLayerUtils.h"
      16             : #include "MooseUtils.h"
      17             : 
      18             : #include "libmesh/int_range.h"
      19             : #include "libmesh/mesh_modification.h"
      20             : #include "libmesh/mesh_serializer.h"
      21             : #include "libmesh/unstructured_mesh.h"
      22             : #include "DelimitedFileReader.h"
      23             : 
      24             : registerMooseObject("MooseApp", XYDelaunayGenerator);
      25             : 
      26             : InputParameters
      27        3798 : XYDelaunayGenerator::validParams()
      28             : {
      29        3798 :   InputParameters params = SurfaceDelaunayGeneratorBase::validParams();
      30             : 
      31       15192 :   MooseEnum algorithm("BINARY EXHAUSTIVE", "BINARY");
      32       15192 :   MooseEnum tri_elem_type("TRI3 TRI6 TRI7 DEFAULT", "DEFAULT");
      33             : 
      34       15192 :   params.addRequiredParam<MeshGeneratorName>(
      35             :       "boundary",
      36             :       "The input MeshGenerator defining the output outer boundary and required Steiner points.");
      37       15192 :   params.addParam<std::vector<BoundaryName>>(
      38             :       "input_boundary_names", "2D-input-mesh boundaries defining the output mesh outer boundary");
      39       15192 :   params.addParam<std::vector<SubdomainName>>(
      40             :       "input_subdomain_names", "1D-input-mesh subdomains defining the output mesh outer boundary");
      41       11394 :   params.addParam<unsigned int>("add_nodes_per_boundary_segment",
      42        7596 :                                 0,
      43             :                                 "How many more nodes to add in each outer boundary segment.");
      44       11394 :   params.addParam<bool>(
      45        7596 :       "refine_boundary", true, "Whether to allow automatically refining the outer boundary.");
      46             : 
      47       15192 :   params.addParam<SubdomainName>("output_subdomain_name",
      48             :                                  "Subdomain name to set on new triangles.");
      49       15192 :   params.addParam<SubdomainID>("output_subdomain_id", "Subdomain id to set on new triangles.");
      50             : 
      51       15192 :   params.addParam<BoundaryName>(
      52             :       "output_boundary",
      53             :       "Boundary name to set on new outer boundary.  Default ID: 0 if no hole meshes are stitched; "
      54             :       "or maximum boundary ID of all the stitched hole meshes + 1.");
      55       15192 :   params.addParam<std::vector<BoundaryName>>(
      56             :       "hole_boundaries",
      57             :       "Boundary names to set on holes.  Default IDs are numbered up from 1 if no hole meshes are "
      58             :       "stitched; or from maximum boundary ID of all the stitched hole meshes + 2.");
      59             : 
      60       11394 :   params.addParam<bool>(
      61             :       "verify_holes",
      62        7596 :       true,
      63             :       "Verify holes do not intersect boundary or each other.  Asymptotically costly.");
      64             : 
      65       11394 :   params.addParam<bool>("smooth_triangulation",
      66        7596 :                         false,
      67             :                         "Whether to do Laplacian mesh smoothing on the generated triangles.");
      68       11394 :   params.addParam<std::vector<MeshGeneratorName>>(
      69        7596 :       "holes", std::vector<MeshGeneratorName>(), "The MeshGenerators that define mesh holes.");
      70       11394 :   params.addParam<std::vector<bool>>(
      71        7596 :       "stitch_holes", std::vector<bool>(), "Whether to stitch to the mesh defining each hole.");
      72       11394 :   params.addParam<std::vector<bool>>("refine_holes",
      73        7596 :                                      std::vector<bool>(),
      74             :                                      "Whether to allow automatically refining each hole boundary.");
      75       22788 :   params.addRangeCheckedParam<Real>(
      76             :       "desired_area",
      77             :       0,
      78             :       "desired_area>=0",
      79             :       "Desired (maximum) triangle area, or 0 to skip uniform refinement");
      80       11394 :   params.addParam<std::string>(
      81             :       "desired_area_func",
      82        7596 :       std::string(),
      83             :       "Desired area as a function of x,y; omit to skip non-uniform refinement");
      84             : 
      85       15192 :   params.addParam<MooseEnum>(
      86             :       "algorithm",
      87             :       algorithm,
      88             :       "Control the use of binary search for the nodes of the stitched surfaces.");
      89       15192 :   params.addParam<MooseEnum>(
      90             :       "tri_element_type", tri_elem_type, "Type of the triangular elements to be generated.");
      91       11394 :   params.addParam<bool>(
      92        7596 :       "verbose_stitching", false, "Whether mesh stitching should have verbose output.");
      93       15192 :   params.addParam<std::vector<Point>>("interior_points",
      94             :                                       {},
      95             :                                       "Interior node locations, if no smoothing is used. Any point "
      96             :                                       "outside the surface will not be meshed.");
      97       15192 :   params.addParam<std::vector<FileName>>(
      98             :       "interior_point_files", {}, "Text file(s) with the interior points, one per line");
      99        7596 :   params.addClassDescription("Triangulates meshes within boundaries defined by input meshes.");
     100             : 
     101       22788 :   params.addRangeCheckedParam<Real>("outer_boundary_layer_thickness",
     102             :                                     0,
     103             :                                     "outer_boundary_layer_thickness>=0",
     104             :                                     "Thickness of the outer boundary layer to be added.");
     105       11394 :   params.addParam<unsigned int>(
     106        7596 :       "outer_boundary_layer_num", 0, "Number of layers for the outer boundary layer.");
     107       18990 :   params.addRangeCheckedParam<Real>(
     108             :       "outer_boundary_layer_bias",
     109        7596 :       1.0,
     110             :       "outer_boundary_layer_bias>0",
     111             :       "Bias factor for the thickness of each layer in the outer boundary layer.");
     112             : 
     113       22788 :   params.addRangeCheckedParam<std::vector<Real>>(
     114             :       "holes_boundary_layer_thickness",
     115             :       "holes_boundary_layer_thickness>=0",
     116             :       "Thickness of the hole boundary layers to be added.");
     117       15192 :   params.addParam<std::vector<unsigned int>>("holes_boundary_layer_num",
     118             :                                              "Numbers of layers for the hole boundary layers.");
     119       22788 :   params.addRangeCheckedParam<std::vector<Real>>(
     120             :       "holes_boundary_layer_bias",
     121             :       "holes_boundary_layer_bias>0",
     122             :       "Bias factors for the thickness of each layer in the hole boundary layers.");
     123             : 
     124       15192 :   params.addParamNamesToGroup("interior_points interior_point_files",
     125             :                               "Mandatory mesh interior nodes");
     126             : 
     127       11394 :   params.addParamNamesToGroup("outer_boundary_layer_thickness outer_boundary_layer_num "
     128             :                               "outer_boundary_layer_bias holes_boundary_layer_thickness "
     129             :                               "holes_boundary_layer_num holes_boundary_layer_bias",
     130             :                               "Boundary layer");
     131             : 
     132        7596 :   return params;
     133        3798 : }
     134             : 
     135         373 : XYDelaunayGenerator::XYDelaunayGenerator(const InputParameters & parameters)
     136             :   : SurfaceDelaunayGeneratorBase(parameters),
     137         373 :     _bdy_ptr(getMesh("boundary")),
     138         746 :     _add_nodes_per_boundary_segment(getParam<unsigned int>("add_nodes_per_boundary_segment")),
     139         746 :     _refine_bdy(getParam<bool>("refine_boundary")),
     140         373 :     _output_subdomain_id(0),
     141         746 :     _smooth_tri(getParam<bool>("smooth_triangulation")),
     142         746 :     _verify_holes(getParam<bool>("verify_holes")),
     143         746 :     _hole_ptrs(getMeshes("holes")),
     144         746 :     _stitch_holes(getParam<std::vector<bool>>("stitch_holes")),
     145         746 :     _refine_holes(getParam<std::vector<bool>>("refine_holes")),
     146         746 :     _desired_area(getParam<Real>("desired_area")),
     147         746 :     _desired_area_func(getParam<std::string>("desired_area_func")),
     148         373 :     _algorithm(parameters.get<MooseEnum>("algorithm")),
     149         373 :     _tri_elem_type(parameters.get<MooseEnum>("tri_element_type")),
     150         373 :     _verbose_stitching(parameters.get<bool>("verbose_stitching")),
     151         746 :     _interior_points(getParam<std::vector<Point>>("interior_points")),
     152         746 :     _outer_boundary_layer_thickness(getParam<Real>("outer_boundary_layer_thickness")),
     153         746 :     _outer_boundary_layer_num(getParam<unsigned int>("outer_boundary_layer_num")),
     154         746 :     _outer_boundary_layer_bias(getParam<Real>("outer_boundary_layer_bias")),
     155         383 :     _holes_boundary_layer_thickness(
     156        1119 :         isParamValid("holes_boundary_layer_thickness")
     157         373 :             ? getParam<std::vector<Real>>("holes_boundary_layer_thickness")
     158             :             : std::vector<Real>()),
     159        1139 :     _holes_boundary_layer_num(isParamValid("holes_boundary_layer_num")
     160         373 :                                   ? getParam<std::vector<unsigned int>>("holes_boundary_layer_num")
     161             :                                   : std::vector<unsigned int>()),
     162        1139 :     _holes_boundary_layer_bias(isParamValid("holes_boundary_layer_bias")
     163         373 :                                    ? getParam<std::vector<Real>>("holes_boundary_layer_bias")
     164         373 :                                    : std::vector<Real>())
     165             : {
     166         310 :   if ((_desired_area > 0.0 && !_desired_area_func.empty()) ||
     167        1053 :       (_desired_area > 0.0 && _use_auto_area_func) ||
     168         370 :       (!_desired_area_func.empty() && _use_auto_area_func))
     169           6 :     paramError("desired_area_func",
     170             :                "Only one of the three methods ('desired_area', 'desired_area_func', and "
     171             :                "'_use_auto_area_func') to set element area limit should be used.");
     172             : 
     173         370 :   if (!_use_auto_area_func)
     174        1080 :     if (isParamSetByUser("auto_area_func_default_size") ||
     175        1440 :         isParamSetByUser("auto_area_func_default_size_dist") ||
     176        2520 :         isParamSetByUser("auto_area_function_num_points") ||
     177        1440 :         isParamSetByUser("auto_area_function_power"))
     178           6 :       paramError("use_auto_area_func",
     179             :                  "If this parameter is set to false, the following parameters should not be set: "
     180             :                  "'auto_area_func_default_size', 'auto_area_func_default_size_dist', "
     181             :                  "'auto_area_function_num_points', 'auto_area_function_power'.");
     182             : 
     183         367 :   if (!_stitch_holes.empty() && _stitch_holes.size() != _hole_ptrs.size())
     184           0 :     paramError("stitch_holes", "Need one stitch_holes entry per hole, if specified.");
     185             : 
     186         546 :   for (auto hole_i : index_range(_stitch_holes))
     187         179 :     if (_stitch_holes[hole_i] && (hole_i >= _refine_holes.size() || _refine_holes[hole_i]))
     188           0 :       paramError("refine_holes", "Disable auto refine of any hole boundary to be stitched.");
     189             : 
     190        1101 :   if (isParamValid("hole_boundaries"))
     191             :   {
     192          76 :     auto & hole_boundaries = getParam<std::vector<BoundaryName>>("hole_boundaries");
     193          38 :     if (hole_boundaries.size() != _hole_ptrs.size())
     194           0 :       paramError("hole_boundaries", "Need one hole_boundaries entry per hole, if specified.");
     195             :   }
     196             :   // Copied from MultiApp.C
     197         734 :   const auto & positions_files = getParam<std::vector<FileName>>("interior_point_files");
     198         380 :   for (const auto p_file_it : index_range(positions_files))
     199             :   {
     200          13 :     const std::string positions_file = positions_files[p_file_it];
     201          13 :     MooseUtils::DelimitedFileReader file(positions_file, &_communicator);
     202          13 :     file.setFormatFlag(MooseUtils::DelimitedFileReader::FormatFlag::ROWS);
     203          13 :     file.read();
     204             : 
     205          13 :     const std::vector<Point> & data = file.getDataAsPoints();
     206          65 :     for (const auto & d : data)
     207          52 :       _interior_points.push_back(d);
     208          13 :   }
     209             :   bool has_duplicates =
     210         367 :       std::any_of(_interior_points.begin(),
     211             :                   _interior_points.end(),
     212         113 :                   [&](const Point & p)
     213         113 :                   { return std::count(_interior_points.begin(), _interior_points.end(), p) > 1; });
     214         367 :   if (has_duplicates)
     215           6 :     paramError("interior_points", "Duplicate points were found in the provided interior points.");
     216             : 
     217         364 :   if ((_outer_boundary_layer_thickness > 0 && _outer_boundary_layer_num == 0) ||
     218         364 :       (_outer_boundary_layer_thickness == 0 && _outer_boundary_layer_num > 0))
     219           0 :     paramError(
     220             :         "outer_boundary_layer_thickness",
     221             :         "this parameter must be set as non-zero along with a non-zero outer_boundary_layer_num.");
     222             : 
     223         364 :   if (_outer_boundary_layer_num > 0)
     224             :   {
     225          10 :     if (_add_nodes_per_boundary_segment)
     226           0 :       paramError("add_nodes_per_boundary_segment",
     227             :                  "Cannot add nodes per boundary segment when using an outer boundary layer.");
     228          10 :     if (_refine_bdy)
     229           0 :       paramError("refine_boundary", "Cannot refine boundary when using an outer boundary layer.");
     230             :   }
     231             : 
     232         374 :   if ((_holes_boundary_layer_thickness.size() && _holes_boundary_layer_num.size() &&
     233          10 :        _holes_boundary_layer_bias.size()) !=
     234         718 :       (_holes_boundary_layer_thickness.size() || _holes_boundary_layer_num.size() ||
     235         354 :        _holes_boundary_layer_bias.size()))
     236           0 :     paramError("holes_boundary_layer_num",
     237             :                "holes_boundary_layer_thickness, holes_boundary_layer_bias and this parameter must "
     238             :                "be specified or not specified together.");
     239         374 :   if (_holes_boundary_layer_thickness.size() &&
     240          10 :       _holes_boundary_layer_thickness.size() != _hole_ptrs.size())
     241           0 :     paramError("holes_boundary_layer_thickness",
     242             :                "If specified, this parameter must have the same length as 'holes'.");
     243         364 :   if (_holes_boundary_layer_num.size() && _holes_boundary_layer_num.size() != _hole_ptrs.size())
     244           0 :     paramError("holes_boundary_layer_num",
     245             :                "If specified, this parameter must have the same length as 'holes'.");
     246         364 :   if (_holes_boundary_layer_bias.size() && _holes_boundary_layer_bias.size() != _hole_ptrs.size())
     247           0 :     paramError("holes_boundary_layer_bias",
     248             :                "If specified, this parameter must have the same length as 'holes'.");
     249         384 :   for (const auto & i : index_range(_holes_boundary_layer_thickness))
     250             :   {
     251          40 :     if ((_holes_boundary_layer_thickness[i] > 0 && _holes_boundary_layer_num[i] == 0) ||
     252          20 :         (_holes_boundary_layer_thickness[i] == 0 && _holes_boundary_layer_num[i] > 0))
     253           0 :       paramError(
     254             :           "holes_boundary_layer_thickness",
     255           0 :           "entry " + std::to_string(i) +
     256             :               " must be set as non-zero along with a non-zero holes_boundary_layer_num entry.");
     257          20 :     if (_holes_boundary_layer_thickness[i] > 0)
     258             :     {
     259          20 :       if ((_refine_holes.size() > i && _refine_holes[i]) || _refine_holes.empty())
     260           0 :         paramError("refine_holes", "Cannot refine hole boundary when using a hole boundary layer.");
     261             :     }
     262             :   }
     263         364 : }
     264             : 
     265             : std::unique_ptr<MeshBase>
     266         352 : XYDelaunayGenerator::generate()
     267             : {
     268         352 :   MeshTriangulationUtils::XYDelaunayOptions xyd_opts;
     269        1056 :   if (isParamValid("input_boundary_names"))
     270          30 :     xyd_opts.input_boundary_names = getParam<std::vector<BoundaryName>>("input_boundary_names");
     271        1056 :   if (isParamValid("input_subdomain_names"))
     272          30 :     xyd_opts.input_subdomain_names = getParam<std::vector<SubdomainName>>("input_subdomain_names");
     273         352 :   xyd_opts.add_nodes_per_boundary_segment = _add_nodes_per_boundary_segment;
     274         352 :   xyd_opts.refine_bdy = _refine_bdy;
     275         352 :   xyd_opts.verify_holes = _verify_holes;
     276         352 :   xyd_opts.smooth_tri = _smooth_tri;
     277         352 :   xyd_opts.desired_area = _desired_area;
     278         352 :   xyd_opts.desired_area_func = _desired_area_func;
     279         352 :   xyd_opts.use_auto_area_func = _use_auto_area_func;
     280         352 :   xyd_opts.auto_area_func_default_size = _auto_area_func_default_size;
     281         352 :   xyd_opts.auto_area_func_default_size_dist = _auto_area_func_default_size_dist;
     282         352 :   xyd_opts.auto_area_function_num_points = _auto_area_function_num_points;
     283         352 :   xyd_opts.auto_area_function_power = _auto_area_function_power;
     284         352 :   xyd_opts.interior_points = _interior_points;
     285         352 :   xyd_opts.tri_elem_type = std::string(_tri_elem_type);
     286         352 :   xyd_opts.stitch_holes = _stitch_holes;
     287         352 :   xyd_opts.refine_holes = _refine_holes;
     288         352 :   xyd_opts.use_binary_search = (_algorithm == "BINARY");
     289         352 :   xyd_opts.verbose_stitching = _verbose_stitching;
     290        1056 :   if (isParamValid("output_subdomain_id"))
     291             :   {
     292          20 :     xyd_opts.has_output_subdomain_id = true;
     293          60 :     xyd_opts.output_subdomain_id = getParam<SubdomainID>("output_subdomain_id");
     294             :   }
     295        1056 :   if (isParamValid("output_subdomain_name"))
     296             :   {
     297          97 :     xyd_opts.has_output_subdomain_name = true;
     298         291 :     xyd_opts.output_subdomain_name = getParam<SubdomainName>("output_subdomain_name");
     299             :   }
     300        1056 :   if (isParamValid("output_boundary"))
     301             :   {
     302          10 :     xyd_opts.has_output_boundary = true;
     303          30 :     xyd_opts.output_boundary = getParam<BoundaryName>("output_boundary");
     304             :   }
     305        1056 :   if (isParamValid("hole_boundaries"))
     306          96 :     xyd_opts.hole_boundaries = getParam<std::vector<BoundaryName>>("hole_boundaries");
     307             : 
     308         352 :   std::vector<std::unique_ptr<MeshBase>> hole_meshes(_hole_ptrs.size());
     309         673 :   for (auto hole_i : index_range(_hole_ptrs))
     310         321 :     hole_meshes[hole_i] = std::move(*_hole_ptrs[hole_i]);
     311             : 
     312         352 :   std::unique_ptr<MeshBase> boundary_mesh = std::move(_bdy_ptr);
     313             : 
     314             :   // Preserve the user-facing output_boundary so we can restore it after the outer-ring stitch-back
     315             :   // (we'll temporarily replace it with a sentinel name to locate the seam).
     316         352 :   const bool user_has_output_boundary = xyd_opts.has_output_boundary;
     317         352 :   const BoundaryName user_output_boundary = xyd_opts.output_boundary;
     318         352 :   const BoundaryName tmp_outer_name("__xyd_bdry_layer_tmp_outer__");
     319             : 
     320         352 :   const bool using_outer_layer = (_outer_boundary_layer_num > 0);
     321         352 :   const SubdomainID layer_sd_id =
     322         352 :       xyd_opts.has_output_subdomain_id ? xyd_opts.output_subdomain_id : SubdomainID(0);
     323             :   const SubdomainName layer_sd_name =
     324         352 :       xyd_opts.has_output_subdomain_name ? xyd_opts.output_subdomain_name : SubdomainName();
     325             : 
     326             :   // Build outer boundary-layer ring if requested. The ring's innermost side (bcid 1) becomes the
     327             :   // outer constraint for the interior triangulation; we then stitch a clone of the ring back onto
     328             :   // the result at that seam.
     329         352 :   std::unique_ptr<MeshBase> outer_ring_clone;
     330         352 :   if (using_outer_layer)
     331             :   {
     332             :     auto outer_ring = BoundaryLayerUtils::buildBoundaryLayerRing(*this,
     333          10 :                                                                  *boundary_mesh,
     334             :                                                                  xyd_opts.input_boundary_names,
     335          10 :                                                                  _outer_boundary_layer_num,
     336          10 :                                                                  _outer_boundary_layer_thickness,
     337          10 :                                                                  _outer_boundary_layer_bias,
     338             :                                                                  /*outward=*/false,
     339          10 :                                                                  _tri_elem_type,
     340             :                                                                  layer_sd_id,
     341          10 :                                                                  layer_sd_name);
     342          10 :     outer_ring_clone = outer_ring->clone();
     343          10 :     boundary_mesh = std::move(outer_ring);
     344          20 :     xyd_opts.input_boundary_names = {BoundaryName("1")};
     345          10 :     xyd_opts.has_output_boundary = true;
     346          10 :     xyd_opts.output_boundary = tmp_outer_name;
     347          10 :   }
     348             : 
     349             :   // Build hole boundary-layer rings if requested. Each ring is stitched into the result by the
     350             :   // standard hole-stitching path in triangulateWithDelaunay (with stitch_holes forced true).
     351         673 :   for (auto hole_i : index_range(_hole_ptrs))
     352             :   {
     353         321 :     if (hole_i < _holes_boundary_layer_num.size() && _holes_boundary_layer_num[hole_i] > 0)
     354             :     {
     355          20 :       const bool keep_input = (hole_i < _stitch_holes.size() && _stitch_holes[hole_i]);
     356             :       auto hole_ring =
     357             :           BoundaryLayerUtils::buildBoundaryLayerRing(*this,
     358          20 :                                                      *hole_meshes[hole_i],
     359          20 :                                                      std::vector<BoundaryName>(),
     360          20 :                                                      _holes_boundary_layer_num[hole_i],
     361          20 :                                                      _holes_boundary_layer_thickness[hole_i],
     362          20 :                                                      _holes_boundary_layer_bias[hole_i],
     363             :                                                      /*outward=*/true,
     364          20 :                                                      _tri_elem_type,
     365             :                                                      layer_sd_id,
     366          40 :                                                      layer_sd_name);
     367             : 
     368          20 :       if (keep_input)
     369             :       {
     370             :         // Stitch the original hole mesh into the ring at ring's innermost side (bcid 1).
     371          10 :         auto & ring_u = dynamic_cast<UnstructuredMesh &>(*hole_ring);
     372          10 :         auto & inp_u = dynamic_cast<UnstructuredMesh &>(*hole_meshes[hole_i]);
     373          10 :         libMesh::MeshSerializer s1(ring_u), s2(inp_u);
     374          10 :         if (!ring_u.is_prepared())
     375          10 :           ring_u.prepare_for_use();
     376          10 :         if (!inp_u.is_prepared())
     377          10 :           inp_u.prepare_for_use();
     378             :         // Renumber input mesh boundary ids so they don't overlap with the ring's, mirroring the
     379             :         // approach in StitchMeshGenerator. This avoids degenerate stitching when both meshes
     380             :         // contain the same bcids on different sides.
     381          10 :         const auto & ring_bids = ring_u.get_boundary_info().get_global_boundary_ids();
     382          10 :         const auto inp_bids = inp_u.get_boundary_info().get_global_boundary_ids();
     383          10 :         const auto max_bid = std::max(*ring_bids.rbegin(),
     384          10 :                                       inp_bids.empty() ? boundary_id_type(0) : *inp_bids.rbegin());
     385          10 :         BoundaryID ext_id = 1;
     386          10 :         bool overlap = false;
     387          50 :         for (auto b : inp_bids)
     388          40 :           if (ring_bids.count(b))
     389          10 :             overlap = true;
     390          10 :         if (overlap)
     391             :         {
     392          10 :           BoundaryID idx = 1;
     393          50 :           for (auto b : inp_bids)
     394             :           {
     395          40 :             const auto new_b = max_bid + (idx++);
     396          40 :             inp_u.get_boundary_info().renumber_id(b, new_b);
     397             :           }
     398          10 :           ext_id = max_bid + idx;
     399             :         }
     400             :         else
     401           0 :           ext_id = max_bid + 1;
     402          10 :         inp_u.comm().max(ext_id);
     403          10 :         bool has_ext = false;
     404          10 :         MooseMeshUtils::addExternalBoundary(inp_u, ext_id, has_ext);
     405             :         mooseAssert(has_ext, "A 2D-XY mesh should have an external boundary.");
     406             :         const auto ring_u_ext_id =
     407          10 :             std::max(MooseMeshUtils::getNextFreeBoundaryID(ring_u), BoundaryID(ext_id + 1));
     408          10 :         MooseMeshUtils::changeBoundaryId(ring_u, 1, ring_u_ext_id, false);
     409          10 :         if (xyd_opts.hole_boundary_inner_id_defaults.size() <= hole_i)
     410          10 :           xyd_opts.hole_boundary_inner_id_defaults.resize(_hole_ptrs.size());
     411          10 :         xyd_opts.hole_boundary_inner_id_defaults[hole_i] = {ring_u_ext_id};
     412             :         // we want to keep the ring's original inner bcid (1) for later use.
     413          10 :         ring_u.stitch_meshes(inp_u,
     414             :                              1,
     415             :                              ext_id,
     416             :                              TOLERANCE,
     417             :                              /*clear_stitched_bcids=*/true,
     418          10 :                              _verbose_stitching,
     419          10 :                              _algorithm == "BINARY",
     420             :                              /*enforce_all_nodes_match_on_boundaries=*/false,
     421             :                              /*merge_boundary_nodes_all_or_nothing=*/false,
     422             :                              /*remap_subdomain_ids=*/false);
     423          10 :       }
     424             : 
     425          20 :       hole_meshes[hole_i] = std::move(hole_ring);
     426          20 :       if (xyd_opts.stitch_holes.size() <= hole_i)
     427           0 :         xyd_opts.stitch_holes.resize(_hole_ptrs.size(), false);
     428          20 :       xyd_opts.stitch_holes[hole_i] = true;
     429             :       // The ring presents multiple external manifolds; tell triangulateWithDelaunay to use the
     430             :       // outermost (bcid (num_layers - 1) * 2) as the hole's outer boundary.
     431          20 :       if (xyd_opts.hole_boundary_id_filters.size() <= hole_i)
     432          10 :         xyd_opts.hole_boundary_id_filters.resize(_hole_ptrs.size());
     433          20 :       xyd_opts.hole_boundary_id_filters[hole_i] = {
     434          20 :           std::size_t((_holes_boundary_layer_num[hole_i] - 1) * 2)};
     435          20 :     }
     436             :   }
     437             : 
     438             :   auto result = MeshTriangulationUtils::triangulateWithDelaunay(
     439         364 :       *this, std::move(boundary_mesh), std::move(hole_meshes), xyd_opts);
     440             : 
     441             :   // Stitch the outer ring clone back onto the interior triangulation at the recorded seam.
     442         340 :   if (outer_ring_clone)
     443             :   {
     444          30 :     auto sentinel_ids = MooseMeshUtils::getBoundaryIDs(*result, {tmp_outer_name}, false);
     445          10 :     const boundary_id_type sentinel_id = sentinel_ids[0];
     446             : 
     447             :     // Preserve the ring's outermost bcid (= (num_layers - 1) * 2) by renaming it to a high temp
     448             :     // value so the post-stitch rename can recover it as the final outer bcid.
     449          10 :     const boundary_id_type ring_outermost_orig =
     450          10 :         boundary_id_type((_outer_boundary_layer_num - 1) * 2);
     451             :     // The maximum boundary ID in the outer ring is _outer_boundary_layer_num * 2 - 1, so
     452             :     // _outer_boundary_layer_num * 2 is safe for itself. We need the maximum boundary ID of the
     453             :     // result mesh too.
     454             :     const boundary_id_type ring_outermost_temp =
     455          20 :         std::max(boundary_id_type(_outer_boundary_layer_num * 2),
     456          10 :                  MooseMeshUtils::getNextFreeBoundaryID(*result));
     457          10 :     libMesh::MeshTools::Modification::change_boundary_id(
     458          10 :         *outer_ring_clone, ring_outermost_orig, ring_outermost_temp);
     459             : 
     460          10 :     auto & result_u = dynamic_cast<UnstructuredMesh &>(*result);
     461          10 :     auto & clone_u = dynamic_cast<UnstructuredMesh &>(*outer_ring_clone);
     462          10 :     libMesh::MeshSerializer s1(result_u), s2(clone_u);
     463          10 :     result_u.stitch_meshes(clone_u,
     464             :                            sentinel_id,
     465             :                            1,
     466             :                            TOLERANCE,
     467             :                            /*clear_stitched_bcids=*/true,
     468          10 :                            _verbose_stitching,
     469          10 :                            _algorithm == "BINARY");
     470             : 
     471          10 :     libMesh::MeshTools::Modification::change_boundary_id(*result, ring_outermost_temp, sentinel_id);
     472             : 
     473          10 :     if (user_has_output_boundary)
     474             :     {
     475           0 :       auto user_id = MooseMeshUtils::getBoundaryIDs(*result, {user_output_boundary}, true).front();
     476           0 :       if (user_id != sentinel_id)
     477           0 :         libMesh::MeshTools::Modification::change_boundary_id(*result, sentinel_id, user_id);
     478           0 :       result->get_boundary_info().sideset_name(user_id) = user_output_boundary;
     479             :     }
     480             : 
     481          10 :     result->unset_is_prepared();
     482          10 :   }
     483             : 
     484         680 :   return result;
     485         402 : }

Generated by: LCOV version 1.14