https://mooseframework.inl.gov
StitchMeshGenerator.C
Go to the documentation of this file.
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 "StitchMeshGenerator.h"
11 
12 #include "CastUniquePointer.h"
13 #include "MooseUtils.h"
14 #include "MooseMeshUtils.h"
15 
16 #include "libmesh/unstructured_mesh.h"
17 
19  StitchedMeshGenerator,
20  "06/30/2026 24:00",
23 
26 {
28  params.addRequiredParam<std::vector<MeshGeneratorName>>("inputs", "The input MeshGenerators.");
29  params.addParam<bool>("prevent_boundary_ids_overlap",
30  true,
31  "Whether to re-number boundaries in stitched meshes to prevent merging of "
32  "unrelated boundaries");
33  params.addParam<bool>(
34  "merge_boundaries_with_same_name",
35  true,
36  "If the input meshes have boundaries with the same name (but different IDs), merge them");
37  params.addParam<bool>(
38  "subdomain_remapping",
39  true,
40  "Treat input subdomain names as primary, preserving them and remapping IDs as needed");
41 
42  params.addClassDescription(
43  "Allows multiple mesh files to be stitched together to form a single mesh.");
44 
45  // This is used for compatibility with TestSubGenerators
46  params.addPrivateParam<bool>("_check_inputs", true);
47  return params;
48 }
49 
51  : StitchMeshGeneratorBase(parameters),
52  _mesh_ptrs(getMeshes("inputs")),
53  _input_names(getParam<std::vector<MeshGeneratorName>>("inputs")),
54  _prevent_boundary_ids_overlap(getParam<bool>("prevent_boundary_ids_overlap")),
55  _merge_boundaries_with_same_name(getParam<bool>("merge_boundaries_with_same_name"))
56 {
57  // Check inputs
58  if (_input_names.size() - 1 != _stitch_boundaries_pairs.size() && getParam<bool>("_check_inputs"))
59  paramError("stitch_boundaries_pairs",
60  "Can only stitch one pair of boundary per pair of mesh. We have '" +
61  std::to_string(_input_names.size()) +
62  "' meshes specified (=" + std::to_string(_input_names.size() - 1) +
63  " pairs) and " + std::to_string(_stitch_boundaries_pairs.size()) +
64  " pairs of boundaries specified");
65  for (const auto & pair : _stitch_boundaries_pairs)
66  if (pair.size() != 2)
67  paramError("stitch_boundaries_pairs",
68  "Stitched boundary pair '" + Moose::stringify(pair) +
69  "' is not of length 2, but of length: " + std::to_string(pair.size()));
70 }
71 
72 std::unique_ptr<MeshBase>
74 {
75  // We put the first mesh in a local pointer
76  std::unique_ptr<UnstructuredMesh> mesh = dynamic_pointer_cast<UnstructuredMesh>(*_mesh_ptrs[0]);
77  if (!mesh) // This should never happen until libMesh implements on-the-fly-Elem mesh types
78  mooseError("StitchMeshGenerator is only implemented for unstructured meshes");
79 
80  // Reserve spaces for the other meshes (no need to store the first one another time)
81  std::vector<std::unique_ptr<UnstructuredMesh>> meshes(_mesh_ptrs.size() - 1);
82 
83  // Read in all of the other meshes
84  for (MooseIndex(_mesh_ptrs) i = 1; i < _mesh_ptrs.size(); ++i)
85  meshes[i - 1] = dynamic_pointer_cast<UnstructuredMesh>(*_mesh_ptrs[i]);
86 
87  // Stitch all the meshes to the first one
88  for (MooseIndex(meshes) i = 0; i < meshes.size(); i++)
89  {
90  const auto boundary_pair = _stitch_boundaries_pairs[i];
91 
92  // Check the input boundaries
93  const auto first = getBoundaryIdToStitch(
94  *mesh,
95  (_input_names.size() ? _input_names[0] : "(unknown)") +
96  ((i == 0) ? "" : (" (stitched with " + std::to_string(i) + " previous meshes)")),
97  boundary_pair[0]);
98  auto second = getBoundaryIdToStitch(*meshes[i], name(), boundary_pair[1]);
99 
100  const bool use_binary_search = (_algorithm == "BINARY");
101 
102  // Meshes must be prepared to get the global boundary ids
103  if (!mesh->is_prepared())
104  mesh->prepare_for_use();
105  if (!meshes[i]->is_prepared())
106  meshes[i]->prepare_for_use();
107 
108  // Avoid chaotic boundary merging due to overlapping ids in meshes by simply renumbering the
109  // boundaries in the meshes we are going to stitch onto the base mesh
110  // This is only done if there is an overlap
112  {
113  const auto & base_mesh_bids = mesh->get_boundary_info().get_global_boundary_ids();
114  const auto stitched_mesh_bids = meshes[i]->get_boundary_info().get_global_boundary_ids();
115 
116  // Check for an overlap
117  bool overlap_found = false;
118  for (const auto & bid : stitched_mesh_bids)
119  if (base_mesh_bids.count(bid))
120  overlap_found = true;
121 
122  if (overlap_found)
123  {
124  const auto max_boundary_id =
125  std::max(*base_mesh_bids.rbegin(), *stitched_mesh_bids.rbegin());
126  BoundaryID new_index = 1;
127  for (const auto bid : stitched_mesh_bids)
128  {
129  const auto new_bid = max_boundary_id + (new_index++);
130  meshes[i]->get_boundary_info().renumber_id(bid, new_bid);
131  if (bid == second)
132  second = new_bid;
133  }
134  }
135  }
136  else
137  {
138  // If we don't have renumbering, we can get into situations where the same boundary ID is
139  // associated with different boundary names. In this case when we stitch, we assign the
140  // boundary name of the first mesh to the ID everywhere on the domain. We throw a warning
141  // here to warn the user if this is the case.
142  const auto & base_mesh_bids = mesh->get_boundary_info().get_global_boundary_ids();
143  const auto & other_mesh_bids = meshes[i]->get_boundary_info().get_global_boundary_ids();
144 
145  // We check if the same ID is present with different names
146  std::set<boundary_id_type> bd_id_intersection;
147  std::set_intersection(base_mesh_bids.begin(),
148  base_mesh_bids.end(),
149  other_mesh_bids.begin(),
150  other_mesh_bids.end(),
151  std::inserter(bd_id_intersection, bd_id_intersection.begin()));
152 
153  for (const auto bid : bd_id_intersection)
154  {
155  const auto & sideset_name_on_first_mesh = mesh->get_boundary_info().get_sideset_name(bid);
156  const auto & sideset_name_on_second_mesh =
157  meshes[i]->get_boundary_info().get_sideset_name(bid);
158 
159  if (sideset_name_on_first_mesh != sideset_name_on_second_mesh)
160  mooseWarning(
161  "Boundary ID ",
162  bid,
163  " corresponds to different boundary names on the input meshes! On the first "
164  "mesh it corresponds to `",
165  sideset_name_on_first_mesh,
166  "` while on the second mesh it corresponds to `",
167  sideset_name_on_second_mesh,
168  "`. The final mesh will replace boundary `",
169  sideset_name_on_second_mesh,
170  "` with `",
171  sideset_name_on_first_mesh,
172  "`. To avoid this situation, use the `prevent_boundary_ids_overlap` parameter!");
173  }
174  }
175 
176  mesh->stitch_meshes(*meshes[i],
177  first,
178  second,
179  getParam<Real>("stitching_hmin_tolerance_factor"),
181  getParam<bool>("verbose_stitching"),
182  use_binary_search,
183  /*enforce_all_nodes_match_on_boundaries=*/false,
184  /*merge_boundary_nodes_all_or_nothing=*/false,
185  getParam<bool>("subdomain_remapping"));
186 
189  }
190 
191  mesh->set_isnt_prepared();
192  return dynamic_pointer_cast<MeshBase>(mesh);
193 }
registerMooseObjectRenamed("MooseApp", StitchedMeshGenerator, "06/30/2026 24:00", StitchMeshGenerator)
registerMooseObject("MooseApp", StitchMeshGenerator)
boundary_id_type getBoundaryIdToStitch(const MeshBase &mesh, const std::string &input_mg_name, const BoundaryName &bname) const
Get the boundary id from the name of a boundary to stitch.
void addPrivateParam(const std::string &name, const T &value)
These method add a parameter to the InputParameters object which can be retrieved like any other para...
MeshBase & mesh
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
std::unique_ptr< T_DEST, T_DELETER > dynamic_pointer_cast(std::unique_ptr< T_SRC, T_DELETER > &src)
These are reworked from https://stackoverflow.com/a/11003103.
virtual const std::string & name() const
Get the name of the class.
Definition: MooseBase.h:57
static InputParameters validParams()
void mooseWarning(Args &&... args) const
Emits a warning prefixed with object name and type.
const bool _clear_stitched_boundary_ids
Whether or not to clear (remove) the stitched boundary IDs.
void addRequiredParam(const std::string &name, const std::string &doc_string)
This method adds a parameter and documentation string to the InputParameters object that will be extr...
static InputParameters validParams()
auto max(const L &left, const R &right)
const bool _merge_boundaries_with_same_name
Whether to merge boundaries if they have the same name but different boundary IDs.
const bool _prevent_boundary_ids_overlap
Whether to renumber all boundaries in stitched meshes to prevent accidental merging of sidesets with ...
boundary_id_type BoundaryID
A base class for mesh generators that stitch boundaries together.
Allows multiple mesh files to be "stitched" together to form a single mesh.
void paramError(const std::string &param, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
std::string stringify(const T &t)
conversion to string
Definition: Conversion.h:64
const MooseEnum _algorithm
Type of algorithm used to find matching nodes (binary or exhaustive)
const std::vector< MeshGeneratorName > & _input_names
The mesh generator inputs to read.
StitchMeshGenerator(const InputParameters &parameters)
std::vector< std::unique_ptr< MeshBase > * > _mesh_ptrs
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type.
void addClassDescription(const std::string &doc_string)
This method adds a description of the class that will be displayed in the input file syntax dump...
void addParam(const std::string &name, const S &value, const std::string &doc_string)
These methods add an optional parameter and a documentation string to the InputParameters object...
void mergeBoundaryIDsWithSameName(MeshBase &mesh)
Merges the boundary IDs of boundaries that have the same names but different IDs. ...
const std::vector< std::vector< std::string > > _stitch_boundaries_pairs
A transformed version of _stitch_boundaries into a more logical "pairwise" structure.