https://mooseframework.inl.gov
StitchedMeshGenerator.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 "StitchedMeshGenerator.h"
11 
12 #include "CastUniquePointer.h"
13 #include "MooseUtils.h"
14 #include "MooseMeshUtils.h"
15 
16 #include "libmesh/unstructured_mesh.h"
17 
19 
22 {
24 
25  MooseEnum algorithm("BINARY EXHAUSTIVE", "BINARY");
26 
27  params.addRequiredParam<std::vector<MeshGeneratorName>>("inputs", "The input MeshGenerators.");
28  params.addParam<bool>(
29  "clear_stitched_boundary_ids", true, "Whether or not to clear the stitched boundary IDs");
30  params.addRequiredParam<std::vector<std::vector<std::string>>>(
31  "stitch_boundaries_pairs",
32  "Pairs of boundaries to be stitched together between the 1st mesh in inputs and each "
33  "consecutive mesh");
34  params.addParam<MooseEnum>(
35  "algorithm",
36  algorithm,
37  "Control the use of binary search for the nodes of the stitched surfaces.");
38  params.addParam<bool>("prevent_boundary_ids_overlap",
39  true,
40  "Whether to re-number boundaries in stitched meshes to prevent merging of "
41  "unrelated boundaries");
42  params.addParam<bool>(
43  "merge_boundaries_with_same_name",
44  true,
45  "If the input meshes have boundaries with the same name (but different IDs), merge them");
46  params.addParam<bool>(
47  "subdomain_remapping",
48  true,
49  "Treat input subdomain names as primary, preserving them and remapping IDs as needed");
50  params.addParam<bool>(
51  "verbose_stitching", false, "Whether mesh stitching should have verbose output.");
52  params.addClassDescription(
53  "Allows multiple mesh files to be stitched together to form a single mesh.");
54 
55  return params;
56 }
57 
59  : MeshGenerator(parameters),
60  _mesh_ptrs(getMeshes("inputs")),
61  _input_names(getParam<std::vector<MeshGeneratorName>>("inputs")),
62  _clear_stitched_boundary_ids(getParam<bool>("clear_stitched_boundary_ids")),
63  _stitch_boundaries_pairs(
64  getParam<std::vector<std::vector<std::string>>>("stitch_boundaries_pairs")),
65  _algorithm(parameters.get<MooseEnum>("algorithm")),
66  _prevent_boundary_ids_overlap(getParam<bool>("prevent_boundary_ids_overlap")),
67  _merge_boundaries_with_same_name(getParam<bool>("merge_boundaries_with_same_name"))
68 {
69 }
70 
71 std::unique_ptr<MeshBase>
73 {
74  // We put the first mesh in a local pointer
75  std::unique_ptr<UnstructuredMesh> mesh = dynamic_pointer_cast<UnstructuredMesh>(*_mesh_ptrs[0]);
76  if (!mesh) // This should never happen until libMesh implements on-the-fly-Elem mesh types
77  mooseError("StitchedMeshGenerator is only implemented for unstructured meshes");
78 
79  // Reserve spaces for the other meshes (no need to store the first one another time)
80  std::vector<std::unique_ptr<UnstructuredMesh>> meshes(_mesh_ptrs.size() - 1);
81 
82  // Read in all of the other meshes
83  for (MooseIndex(_mesh_ptrs) i = 1; i < _mesh_ptrs.size(); ++i)
84  meshes[i - 1] = dynamic_pointer_cast<UnstructuredMesh>(*_mesh_ptrs[i]);
85 
86  // Stitch all the meshes to the first one
87  for (MooseIndex(meshes) i = 0; i < meshes.size(); i++)
88  {
89  auto boundary_pair = _stitch_boundaries_pairs[i];
90 
91  boundary_id_type first, second;
92 
93  try
94  {
95  first = MooseUtils::convert<boundary_id_type>(boundary_pair[0], true);
96  }
97  catch (...)
98  {
99  first = mesh->get_boundary_info().get_id_by_name(boundary_pair[0]);
100 
101  if (first == BoundaryInfo::invalid_id)
102  {
103  std::stringstream error;
104 
105  error << "Boundary " << boundary_pair[0] << " doesn't exist on mesh '" << _input_names[0]
106  << "' in generator " << name() << "\n";
107  error << "Boundary (sideset) names that do exist: \n";
108  error << " ID : Name\n";
109 
110  auto & sideset_id_name_map = mesh->get_boundary_info().get_sideset_name_map();
111 
112  for (auto & ss_name_map_pair : sideset_id_name_map)
113  error << " " << ss_name_map_pair.first << " : " << ss_name_map_pair.second << "\n";
114 
115  error << "\nBoundary (nodeset) names that do exist: \n";
116  error << " ID : Name\n";
117 
118  auto & nodeset_id_name_map = mesh->get_boundary_info().get_nodeset_name_map();
119 
120  for (auto & ns_name_map_pair : nodeset_id_name_map)
121  error << " " << ns_name_map_pair.first << " : " << ns_name_map_pair.second << "\n";
122 
123  paramError("stitch_boundaries_pairs", error.str());
124  }
125  }
126 
127  try
128  {
129  second = MooseUtils::convert<boundary_id_type>(boundary_pair[1], true);
130  }
131  catch (...)
132  {
133  second = meshes[i]->get_boundary_info().get_id_by_name(boundary_pair[1]);
134 
135  if (second == BoundaryInfo::invalid_id)
136  {
137  meshes[i]->print_info();
138 
139  std::stringstream error;
140 
141  error << "Boundary " << boundary_pair[1] << " doesn't exist on mesh '" << _input_names[i]
142  << "' in generator " << name() << "\n";
143  error << "Boundary (sideset) names that do exist: \n";
144  error << " ID : Name\n";
145 
146  auto & sideset_id_name_map = meshes[i]->get_boundary_info().get_sideset_name_map();
147 
148  for (auto & ss_name_map_pair : sideset_id_name_map)
149  error << " " << ss_name_map_pair.first << " : " << ss_name_map_pair.second << "\n";
150 
151  error << "\nBoundary (nodeset) names that do exist: \n";
152  error << " ID : Name\n";
153 
154  auto & nodeset_id_name_map = mesh->get_boundary_info().get_nodeset_name_map();
155 
156  for (auto & ns_name_map_pair : nodeset_id_name_map)
157  error << " " << ns_name_map_pair.first << " : " << ns_name_map_pair.second << "\n";
158 
159  paramError("stitch_boundaries_pairs", error.str());
160  }
161  }
162 
163  const bool use_binary_search = (_algorithm == "BINARY");
164 
165  // Meshes must be prepared to get the global boundary ids
166  if (!mesh->is_prepared())
167  mesh->prepare_for_use();
168  if (!meshes[i]->is_prepared())
169  meshes[i]->prepare_for_use();
170 
171  // Avoid chaotic boundary merging due to overlapping ids in meshes by simply renumbering the
172  // boundaries in the meshes we are going to stitch onto the base mesh
173  // This is only done if there is an overlap
175  {
176  const auto & base_mesh_bids = mesh->get_boundary_info().get_global_boundary_ids();
177  const auto stitched_mesh_bids = meshes[i]->get_boundary_info().get_global_boundary_ids();
178 
179  // Check for an overlap
180  bool overlap_found = false;
181  for (const auto & bid : stitched_mesh_bids)
182  if (base_mesh_bids.count(bid))
183  overlap_found = true;
184 
185  if (overlap_found)
186  {
187  const auto max_boundary_id = *base_mesh_bids.rbegin();
188  BoundaryID new_index = 1;
189  for (const auto bid : stitched_mesh_bids)
190  {
191  const auto new_bid = max_boundary_id + (new_index++);
192  meshes[i]->get_boundary_info().renumber_id(bid, new_bid);
193  if (bid == second)
194  second = new_bid;
195  }
196  }
197  }
198  else
199  {
200  // If we don't have renumbering, we can get into situations where the same boundary ID is
201  // associated with different boundary names. In this case when we stitch, we assign the
202  // boundary name of the first mesh to the ID everywhere on the domain. We throw a warning
203  // here to warn the user if this is the case.
204  const auto & base_mesh_bids = mesh->get_boundary_info().get_global_boundary_ids();
205  const auto & other_mesh_bids = meshes[i]->get_boundary_info().get_global_boundary_ids();
206 
207  // We check if the same ID is present with different names
208  std::set<boundary_id_type> bd_id_intersection;
209  std::set_intersection(base_mesh_bids.begin(),
210  base_mesh_bids.end(),
211  other_mesh_bids.begin(),
212  other_mesh_bids.end(),
213  std::inserter(bd_id_intersection, bd_id_intersection.begin()));
214 
215  for (const auto bid : bd_id_intersection)
216  {
217  const auto & sideset_name_on_first_mesh = mesh->get_boundary_info().get_sideset_name(bid);
218  const auto & sideset_name_on_second_mesh =
219  meshes[i]->get_boundary_info().get_sideset_name(bid);
220 
221  if (sideset_name_on_first_mesh != sideset_name_on_second_mesh)
222  mooseWarning(
223  "Boundary ID ",
224  bid,
225  " corresponds to different boundary names on the input meshes! On the first "
226  "mesh it corresponds to `",
227  sideset_name_on_first_mesh,
228  "` while on the second mesh it corresponds to `",
229  sideset_name_on_second_mesh,
230  "`. The final mesh will replace boundary `",
231  sideset_name_on_second_mesh,
232  "` with `",
233  sideset_name_on_first_mesh,
234  "`. To avoid this situation, use the `prevent_boundary_ids_overlap` parameter!");
235  }
236  }
237 
238  mesh->stitch_meshes(*meshes[i],
239  first,
240  second,
241  TOLERANCE,
243  getParam<bool>("verbose_stitching"),
244  use_binary_search,
245  /*enforce_all_nodes_match_on_boundaries=*/false,
246  /*merge_boundary_nodes_all_or_nothing=*/false,
247  getParam<bool>("subdomain_remapping"));
248 
251  }
252 
253  mesh->set_isnt_prepared();
254  return dynamic_pointer_cast<MeshBase>(mesh);
255 }
const bool _prevent_boundary_ids_overlap
Whether to renumber all boundaries in stitched meshes to prevent accidental merging of sidesets with ...
static InputParameters validParams()
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
T * get(const std::unique_ptr< T > &u)
The MooseUtils::get() specializations are used to support making forwards-compatible code changes fro...
Definition: MooseUtils.h:1155
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
void mooseWarning(Args &&... args) const
Emits a warning prefixed with object name and type.
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...
std::vector< std::vector< std::string > > _stitch_boundaries_pairs
A transformed version of _stitch_boundaries into a more logical "pairwise" structure.
int8_t boundary_id_type
std::vector< std::unique_ptr< MeshBase > * > _mesh_ptrs
boundary_id_type BoundaryID
StitchedMeshGenerator(const InputParameters &parameters)
const bool & _clear_stitched_boundary_ids
Whether or not to clear (remove) the stitched boundary IDs.
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition: MooseEnum.h:33
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 ...
static InputParameters validParams()
Definition: MeshGenerator.C:23
const bool _merge_boundaries_with_same_name
Whether to merge boundaries if they have the same name but different boundary IDs.
const std::vector< MeshGeneratorName > & _input_names
The mesh generator inputs to read.
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. ...
registerMooseObject("MooseApp", StitchedMeshGenerator)
MeshGenerators are objects that can modify or add to an existing mesh.
Definition: MeshGenerator.h:32
Allows multiple mesh files to be "stitched" together to form a single mesh.
MooseEnum _algorithm
Type of algorithm used to find matching nodes (binary or exhaustive)