https://mooseframework.inl.gov
PatternedMeshGenerator.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 "PatternedMeshGenerator.h"
11 
12 #include "CastUniquePointer.h"
13 
14 #include "libmesh/replicated_mesh.h"
15 #include "libmesh/distributed_mesh.h"
16 #include "libmesh/boundary_info.h"
17 #include "libmesh/mesh_modification.h"
18 #include "libmesh/mesh_tools.h"
19 #include "MooseMeshUtils.h"
20 
22 
25 {
27 
28  params.addRequiredParam<std::vector<MeshGeneratorName>>("inputs", "The input MeshGenerators.");
29  params.addRangeCheckedParam<Real>(
30  "x_width", 0, "x_width>=0", "The tile width in the x direction");
31  params.addRangeCheckedParam<Real>(
32  "y_width", 0, "y_width>=0", "The tile width in the y direction");
33  params.addRangeCheckedParam<Real>(
34  "z_width", 0, "z_width>=0", "The tile width in the z direction");
35 
36  // Boundaries : user has to provide id or name for each boundary
37 
38  // x boundary names
39  params.addParam<BoundaryName>("left_boundary", "left", "name of the left (x) boundary");
40  params.addParam<BoundaryName>("right_boundary", "right", "name of the right (x) boundary");
41 
42  // y boundary names
43  params.addParam<BoundaryName>("top_boundary", "top", "name of the top (y) boundary");
44  params.addParam<BoundaryName>("bottom_boundary", "bottom", "name of the bottom (y) boundary");
45 
46  params.addRequiredParam<std::vector<std::vector<unsigned int>>>(
47  "pattern", "A double-indexed array starting with the upper-left corner");
48 
49  params.addClassDescription("Creates a 2D mesh from a specified set of unique 'tiles' meshes and "
50  "a two-dimensional pattern.");
51 
52  return params;
53 }
54 
56  : MeshGenerator(parameters),
57  _input_names(getParam<std::vector<MeshGeneratorName>>("inputs")),
58  _mesh_ptrs(getMeshes("inputs")),
59  _pattern(getParam<std::vector<std::vector<unsigned int>>>("pattern")),
60  _x_width(getParam<Real>("x_width")),
61  _y_width(getParam<Real>("y_width")),
62  _z_width(getParam<Real>("z_width"))
63 {
64  for (MooseIndex(_pattern) i = 0; i < _pattern.size(); ++i)
65  for (MooseIndex(_pattern[i]) j = 0; j < _pattern[i].size(); ++j)
66  if (_pattern[i][j] >= _input_names.size())
67  paramError("pattern",
68  "Index " + Moose::stringify(_pattern[i][j]) +
69  " is larger than the the maximum possible index, which is determined by the "
70  "number of MeshGenerators provided in inputs");
71 }
72 
73 std::unique_ptr<MeshBase>
75 {
76  return generate(nullptr);
77 }
78 
79 std::unique_ptr<MeshBase>
80 PatternedMeshGenerator::generate(std::vector<std::unique_ptr<ReplicatedMesh>> * input_meshes)
81 {
82  // Reserve spaces for all the meshes
83  std::vector<std::unique_ptr<ReplicatedMesh>> local_meshes;
84  auto & meshes = input_meshes ? *input_meshes : local_meshes;
85  meshes.clear();
86  meshes.reserve(_input_names.size());
87 
88  // Getting the boundaries provided by the user
89  const std::vector<BoundaryName> boundary_names = {getParam<BoundaryName>("left_boundary"),
90  getParam<BoundaryName>("right_boundary"),
91  getParam<BoundaryName>("top_boundary"),
92  getParam<BoundaryName>("bottom_boundary")};
93  const std::vector<std::string> boundary_param_names = {
94  "left_boundary", "right_boundary", "top_boundary", "bottom_boundary"};
95 
96  // IDs for each input (indexed by input mesh and then left/right/top/bottom)
97  std::vector<std::vector<boundary_id_type>> input_bids(
98  _input_names.size(), std::vector<boundary_id_type>(4, Moose::INVALID_BOUNDARY_ID));
99  bool have_common_ids = true;
100 
101  // Using a vector of vectors instead of vector of sets to preserve insertion order
102  std::vector<std::vector<boundary_id_type>> input_bids_unique(_input_names.size());
103 
104  // For an error check on the number of uniquely named boundaries to stitch
105  size_t set_length = 0;
106 
107  // Given a boundary name (i.e., 'left_boundary'), this will give the correct
108  // index to get the correct boundary id to later use for boundary stitching
109  std::map<std::string, size_t> boundary_name_to_index_map;
110 
111  // Keep track of used boundary ids to generate new, unused ones later (if needed)
112  std::set<boundary_id_type> all_boundary_ids;
113 
114  // Read in all of the meshes
115  meshes.resize(_input_names.size());
116  for (const auto i : index_range(_input_names))
117  {
118  std::unique_ptr<ReplicatedMesh> mesh = dynamic_pointer_cast<ReplicatedMesh>(*_mesh_ptrs[i]);
119  if (!mesh)
120  paramError("inputs",
121  "The input mesh '",
122  _input_names[i],
123  "' is not a replicated mesh.\n\n",
124  type(),
125  " only works with inputs that are replicated.\n\n",
126  "Try running without distributed mesh.");
127  meshes[i] = dynamic_pointer_cast<ReplicatedMesh>(mesh);
128 
129  // List of boundary ids corresponsind to left/right/top/bottom boundary names
130  const auto ids = MooseMeshUtils::getBoundaryIDs(*meshes[i], boundary_names, false);
131  mooseAssert(ids.size() == boundary_names.size(),
132  "Unexpected number of ids returned for MooseMeshUtils::getBoundaryIDs");
133 
134  // Keep track of indices of first instance of each unique boundary id
135  std::map<boundary_id_type, size_t> seen_bid_to_index_map;
136 
137  size_t index = 0;
138  for (const auto side : make_range(4))
139  {
140  // Check if the boundary has been initialized
141  if (ids[side] == Moose::INVALID_BOUNDARY_ID)
142  paramError("inputs",
143  "The '",
144  boundary_param_names[side],
145  "' parameter with value '",
146  boundary_names[side],
147  "' does not exist in input mesh '",
148  _input_names[i],
149  "'");
150 
151  input_bids[i][side] = ids[side];
152 
153  // We only do this when i == 0 because all input meshes should have the
154  // same index map. Allowing different index maps for different input
155  // meshes results in undefined behaviour when stitching
156  if (i == 0)
157  {
158  if (std::count(input_bids_unique[i].begin(), input_bids_unique[i].end(), ids[side]) == 0)
159  {
160  input_bids_unique[i].push_back(ids[side]);
161  seen_bid_to_index_map[ids[side]] = index;
162  boundary_name_to_index_map[boundary_param_names[side]] = index++;
163  }
164  else
165  boundary_name_to_index_map[boundary_param_names[side]] = seen_bid_to_index_map[ids[side]];
166  }
167 
168  else // i > 0
169  {
170  if (ids[side] != input_bids[i - 1][side])
171  have_common_ids = false;
172 
173  if (std::count(input_bids_unique[i].begin(), input_bids_unique[i].end(), ids[side]) == 0)
174  input_bids_unique[i].push_back(ids[side]);
175  }
176  }
177 
178  // Error check on lengths of input_bids_unique
179  if (i > 0 && set_length != input_bids_unique.size())
180  mooseError(
181  "Input meshes have incompatible boundary ids. This can occur when input meshes have "
182  "the same boundary id for multiple boundaries, but in a way that is different "
183  "between the meshes. Try assigning each left/right/top/bottom to its own boundary id.");
184 
185  set_length = input_bids_unique.size();
186 
187  // List of all boundary ids used in meshes[i] so we don't reuse any existing boundary ids.
188  const auto all_ids = meshes[i]->get_boundary_info().get_boundary_ids();
189 
190  // Keep track of used IDs so we can later find IDs that are unused across all meshes
191  all_boundary_ids.insert(all_ids.begin(), all_ids.end());
192  }
193 
194  // Check if the user has provided the x, y and z widths.
195  // If not (their value is 0 by default), compute them
196  auto bbox = MeshTools::create_bounding_box(*meshes[0]);
197  if (_x_width == 0)
198  _x_width = bbox.max()(0) - bbox.min()(0);
199  if (_y_width == 0)
200  _y_width = bbox.max()(1) - bbox.min()(1);
201  if (_z_width == 0)
202  _z_width = bbox.max()(2) - bbox.min()(2);
203 
204  // stitch_bids will hold boundary ids passed to mesh stitcher
205  std::vector<boundary_id_type> stitch_bids;
206 
207  if (have_common_ids) // No need to change existing boundary ids
208  stitch_bids = input_bids_unique[0];
209 
210  else // Need to make boundary ids common accross all inputs
211  {
212  // Generate previously unused boundary ids
213  for (boundary_id_type id = 0; id != Moose::INVALID_BOUNDARY_ID; ++id)
214  if (!all_boundary_ids.count(id))
215  {
216  stitch_bids.push_back(id);
217  // It is okay to only use the 0th index here, since we ensure all entries in
218  // input_bids_unique have the same size through the above error check.
219  if (stitch_bids.size() == input_bids_unique[0].size())
220  break;
221  }
222 
223  // Make all inputs have common boundary ids
224  for (const auto i : index_range(meshes))
225  for (const auto side : index_range(stitch_bids))
226  MeshTools::Modification::change_boundary_id(
227  *meshes[i], input_bids_unique[i][side], stitch_bids[side]);
228  }
229 
230  // Data structure that holds each row
231  std::vector<std::unique_ptr<ReplicatedMesh>> row_meshes(_pattern.size());
232 
233  // Aliases
234  const boundary_id_type &left_bid(stitch_bids[boundary_name_to_index_map["left_boundary"]]),
235  right_bid(stitch_bids[boundary_name_to_index_map["right_boundary"]]),
236  top_bid(stitch_bids[boundary_name_to_index_map["top_boundary"]]),
237  bottom_bid(stitch_bids[boundary_name_to_index_map["bottom_boundary"]]);
238 
239  // Build each row mesh
240  for (MooseIndex(_pattern) i = 0; i < _pattern.size(); ++i)
241  for (MooseIndex(_pattern[i]) j = 0; j < _pattern[i].size(); ++j)
242  {
243  Real deltax = j * _x_width, deltay = i * _y_width;
244 
245  // If this is the first cell of the row initialize the row mesh
246  if (j == 0)
247  {
248  auto clone = meshes[_pattern[i][j]]->clone();
249  row_meshes[i] = dynamic_pointer_cast<ReplicatedMesh>(clone);
250 
251  MeshTools::Modification::translate(*row_meshes[i], deltax, -deltay, 0);
252 
253  continue;
254  }
255 
256  ReplicatedMesh & cell_mesh = *meshes[_pattern[i][j]];
257 
258  // Move the mesh into the right spot. -i because we are starting at the top
259  MeshTools::Modification::translate(cell_mesh, deltax, -deltay, 0);
260 
261  // Subdomain map is aggregated on each row first. This retrieves a writable reference
262  auto & main_subdomain_map = row_meshes[i]->set_subdomain_name_map();
263  // Retrieve subdomain name map from the mesh to be stitched and merge into the row's
264  // subdomain map
265  const auto & increment_subdomain_map = cell_mesh.get_subdomain_name_map();
266  mergeSubdomainNameMaps(main_subdomain_map, increment_subdomain_map);
267 
268  row_meshes[i]->stitch_meshes(cell_mesh,
269  right_bid,
270  left_bid,
271  TOLERANCE,
272  /*clear_stitched_boundary_ids=*/true,
273  /*verbose=*/false);
274 
275  // Undo the translation
276  MeshTools::Modification::translate(cell_mesh, -deltax, deltay, 0);
277  }
278 
279  // Now stitch together the rows
280  // We're going to stitch them all to row 0 (which is the real mesh)
281  for (MooseIndex(_pattern) i = 1; i < _pattern.size(); i++)
282  {
283  // Get a writeable reference subdomain-name map for the main mesh to which the other rows are
284  // stitched
285  auto & main_subdomain_map = row_meshes[0]->set_subdomain_name_map();
286  // Retrieve subdomain name map from the mesh to be stitched and merge into the main
287  // subdomain map
288  const auto & increment_subdomain_map = row_meshes[i]->get_subdomain_name_map();
289  mergeSubdomainNameMaps(main_subdomain_map, increment_subdomain_map);
290 
291  row_meshes[0]->stitch_meshes(*row_meshes[i],
292  bottom_bid,
293  top_bid,
294  TOLERANCE,
295  /*clear_stitched_boundary_ids=*/true,
296  /*verbose=*/false);
297  }
298 
299  // Change boundary ids back to those of meshes[0] to not surprise user
300  if (!have_common_ids)
301  for (const auto side : index_range(stitch_bids))
302  MeshTools::Modification::change_boundary_id(
303  *row_meshes[0], stitch_bids[side], input_bids_unique[0][side]);
304 
305  row_meshes[0]->unset_is_prepared();
306  return dynamic_pointer_cast<MeshBase>(row_meshes[0]);
307 }
308 
309 void
311  std::map<subdomain_id_type, std::string> & main_subdomain_map,
312  const std::map<subdomain_id_type, std::string> & increment_subdomain_map)
313 {
314  // Insert secondary subdomain map into main subdomain map
315  main_subdomain_map.insert(increment_subdomain_map.begin(), increment_subdomain_map.end());
316  // Check if one SubdomainName is shared by more than one subdomain ids
317  std::set<SubdomainName> main_subdomain_map_name_list;
318  for (auto const & id_name_pair : main_subdomain_map)
319  {
320  const auto name_to_insert = id_name_pair.second;
321  if (main_subdomain_map_name_list.find(name_to_insert) != main_subdomain_map_name_list.end())
322  paramError("inputs",
323  "Two of the input meshes contain a subdomain with the name '" + name_to_insert +
324  "' which corresponds to two conflicting subdomain ids.");
325  main_subdomain_map_name_list.emplace(name_to_insert);
326  }
327 }
Reads one or more 2D mesh files and stitches them together based on a provided two-dimensional patter...
void mergeSubdomainNameMaps(std::map< subdomain_id_type, std::string > &main_subdomain_map, const std::map< subdomain_id_type, std::string > &increment_subdomain_map)
Merges the subdomain name maps between two meshes, throws an error if input maps contain shared subdo...
PatternedMeshGenerator(const InputParameters &parameters)
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 ...
Definition: MooseBase.h:457
const std::vector< std::vector< unsigned int > > & _pattern
The pattern, starting with the upper left corner.
const BoundaryID INVALID_BOUNDARY_ID
Definition: MooseTypes.C:22
registerMooseObject("MooseApp", PatternedMeshGenerator)
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.
static InputParameters validParams()
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...
const std::vector< std::unique_ptr< MeshBase > * > _mesh_ptrs
Holds pointers to the meshes before they are generated.
int8_t boundary_id_type
unsigned int count
Definition: MortarUtils.C:53
const std::string & type() const
Get the type of this class.
Definition: MooseBase.h:93
std::vector< BoundaryID > getBoundaryIDs(const libMesh::MeshBase &mesh, const std::vector< BoundaryName > &boundary_name, bool generate_unknown, const std::set< BoundaryID > &mesh_boundary_ids)
Gets the boundary IDs with their names.
static InputParameters validParams()
Definition: MeshGenerator.C:23
std::string stringify(const T &t)
conversion to string
Definition: Conversion.h:64
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
IntRange< T > make_range(T beg, T end)
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type and optionally a file path to the top-level block p...
Definition: MooseBase.h:271
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 addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
MeshGenerators are objects that can modify or add to an existing mesh.
Definition: MeshGenerator.h:33
void ErrorVector unsigned int
auto index_range(const T &sizable)
const std::vector< MeshGeneratorName > & _input_names
The mesh generators to read.