https://mooseframework.inl.gov
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends
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  "verbose_stitching", false, "Whether mesh stitching should have verbose output.");
48  params.addClassDescription(
49  "Allows multiple mesh files to be stitched together to form a single mesh.");
50 
51  return params;
52 }
53 
55  : MeshGenerator(parameters),
56  _mesh_ptrs(getMeshes("inputs")),
57  _input_names(getParam<std::vector<MeshGeneratorName>>("inputs")),
58  _clear_stitched_boundary_ids(getParam<bool>("clear_stitched_boundary_ids")),
59  _stitch_boundaries_pairs(
60  getParam<std::vector<std::vector<std::string>>>("stitch_boundaries_pairs")),
61  _algorithm(parameters.get<MooseEnum>("algorithm")),
62  _prevent_boundary_ids_overlap(getParam<bool>("prevent_boundary_ids_overlap")),
63  _merge_boundaries_with_same_name(getParam<bool>("merge_boundaries_with_same_name"))
64 {
65 }
66 
67 std::unique_ptr<MeshBase>
69 {
70  // We put the first mesh in a local pointer
71  std::unique_ptr<UnstructuredMesh> mesh = dynamic_pointer_cast<UnstructuredMesh>(*_mesh_ptrs[0]);
72  if (!mesh) // This should never happen until libMesh implements on-the-fly-Elem mesh types
73  mooseError("StitchedMeshGenerator is only implemented for unstructured meshes");
74 
75  // Reserve spaces for the other meshes (no need to store the first one another time)
76  std::vector<std::unique_ptr<UnstructuredMesh>> meshes(_mesh_ptrs.size() - 1);
77 
78  // Read in all of the other meshes
79  for (MooseIndex(_mesh_ptrs) i = 1; i < _mesh_ptrs.size(); ++i)
80  meshes[i - 1] = dynamic_pointer_cast<UnstructuredMesh>(*_mesh_ptrs[i]);
81 
82  // Stitch all the meshes to the first one
83  for (MooseIndex(meshes) i = 0; i < meshes.size(); i++)
84  {
85  auto boundary_pair = _stitch_boundaries_pairs[i];
86 
87  boundary_id_type first, second;
88 
89  try
90  {
91  first = MooseUtils::convert<boundary_id_type>(boundary_pair[0], true);
92  }
93  catch (...)
94  {
95  first = mesh->get_boundary_info().get_id_by_name(boundary_pair[0]);
96 
97  if (first == BoundaryInfo::invalid_id)
98  {
99  std::stringstream error;
100 
101  error << "Boundary " << boundary_pair[0] << " doesn't exist on mesh '" << _input_names[0]
102  << "' in generator " << name() << "\n";
103  error << "Boundary (sideset) names that do exist: \n";
104  error << " ID : Name\n";
105 
106  auto & sideset_id_name_map = mesh->get_boundary_info().get_sideset_name_map();
107 
108  for (auto & ss_name_map_pair : sideset_id_name_map)
109  error << " " << ss_name_map_pair.first << " : " << ss_name_map_pair.second << "\n";
110 
111  error << "\nBoundary (nodeset) names that do exist: \n";
112  error << " ID : Name\n";
113 
114  auto & nodeset_id_name_map = mesh->get_boundary_info().get_nodeset_name_map();
115 
116  for (auto & ns_name_map_pair : nodeset_id_name_map)
117  error << " " << ns_name_map_pair.first << " : " << ns_name_map_pair.second << "\n";
118 
119  paramError("stitch_boundaries_pairs", error.str());
120  }
121  }
122 
123  try
124  {
125  second = MooseUtils::convert<boundary_id_type>(boundary_pair[1], true);
126  }
127  catch (...)
128  {
129  second = meshes[i]->get_boundary_info().get_id_by_name(boundary_pair[1]);
130 
131  if (second == BoundaryInfo::invalid_id)
132  {
133  meshes[i]->print_info();
134 
135  std::stringstream error;
136 
137  error << "Boundary " << boundary_pair[1] << " doesn't exist on mesh '" << _input_names[i]
138  << "' in generator " << name() << "\n";
139  error << "Boundary (sideset) names that do exist: \n";
140  error << " ID : Name\n";
141 
142  auto & sideset_id_name_map = meshes[i]->get_boundary_info().get_sideset_name_map();
143 
144  for (auto & ss_name_map_pair : sideset_id_name_map)
145  error << " " << ss_name_map_pair.first << " : " << ss_name_map_pair.second << "\n";
146 
147  error << "\nBoundary (nodeset) names that do exist: \n";
148  error << " ID : Name\n";
149 
150  auto & nodeset_id_name_map = mesh->get_boundary_info().get_nodeset_name_map();
151 
152  for (auto & ns_name_map_pair : nodeset_id_name_map)
153  error << " " << ns_name_map_pair.first << " : " << ns_name_map_pair.second << "\n";
154 
155  paramError("stitch_boundaries_pairs", error.str());
156  }
157  }
158 
159  const bool use_binary_search = (_algorithm == "BINARY");
160 
161  // Meshes must be prepared to get the global boundary ids
162  if (!mesh->is_prepared())
163  mesh->prepare_for_use();
164  if (!meshes[i]->is_prepared())
165  meshes[i]->prepare_for_use();
166 
167  // Avoid chaotic boundary merging due to overlapping ids in meshes by simply renumbering the
168  // boundaries in the meshes we are going to stitch onto the base mesh
169  // This is only done if there is an overlap
171  {
172  const auto & base_mesh_bids = mesh->get_boundary_info().get_global_boundary_ids();
173  const auto stitched_mesh_bids = meshes[i]->get_boundary_info().get_global_boundary_ids();
174 
175  // Check for an overlap
176  bool overlap_found = false;
177  for (const auto & bid : stitched_mesh_bids)
178  if (base_mesh_bids.count(bid))
179  overlap_found = true;
180 
181  if (overlap_found)
182  {
183  const auto max_boundary_id = *base_mesh_bids.rbegin();
184  BoundaryID new_index = 1;
185  for (const auto bid : stitched_mesh_bids)
186  {
187  const auto new_bid = max_boundary_id + (new_index++);
188  meshes[i]->get_boundary_info().renumber_id(bid, new_bid);
189  if (bid == second)
190  second = new_bid;
191  }
192  }
193  }
194  else
195  {
196  // If we don't have renumbering, we can get into situations where the same boundary ID is
197  // associated with different boundary names. In this case when we stitch, we assign the
198  // boundary name of the first mesh to the ID everywhere on the domain. We throw a warning
199  // here to warn the user if this is the case.
200  const auto & base_mesh_bids = mesh->get_boundary_info().get_global_boundary_ids();
201  const auto & other_mesh_bids = meshes[i]->get_boundary_info().get_global_boundary_ids();
202 
203  // We check if the same ID is present with different names
204  std::set<boundary_id_type> bd_id_intersection;
205  std::set_intersection(base_mesh_bids.begin(),
206  base_mesh_bids.end(),
207  other_mesh_bids.begin(),
208  other_mesh_bids.end(),
209  std::inserter(bd_id_intersection, bd_id_intersection.begin()));
210 
211  for (const auto bid : bd_id_intersection)
212  {
213  const auto & sideset_name_on_first_mesh = mesh->get_boundary_info().get_sideset_name(bid);
214  const auto & sideset_name_on_second_mesh =
215  meshes[i]->get_boundary_info().get_sideset_name(bid);
216 
217  if (sideset_name_on_first_mesh != sideset_name_on_second_mesh)
218  mooseWarning(
219  "Boundary ID ",
220  bid,
221  " corresponds to different boundary names on the input meshes! On the first "
222  "mesh it corresponds to `",
223  sideset_name_on_first_mesh,
224  "` while on the second mesh it corresponds to `",
225  sideset_name_on_second_mesh,
226  "`. The final mesh will replace boundary `",
227  sideset_name_on_second_mesh,
228  "` with `",
229  sideset_name_on_first_mesh,
230  "`. To avoid this situation, use the `prevent_boundary_ids_overlap` parameter!");
231  }
232  }
233 
234  mesh->stitch_meshes(*meshes[i],
235  first,
236  second,
237  TOLERANCE,
239  getParam<bool>("verbose_stitching"),
240  use_binary_search);
241 
244  }
245 
246  mesh->set_isnt_prepared();
247  return dynamic_pointer_cast<MeshBase>(mesh);
248 }
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:1122
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)