www.mooseframework.org
PatternedMeshGenerator.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://www.mooseframework.org
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  // Reserve spaces for all the meshes
77  _meshes.reserve(_input_names.size());
78 
79  // Getting the boundaries provided by the user
80  const std::vector<BoundaryName> boundary_names = {getParam<BoundaryName>("left_boundary"),
81  getParam<BoundaryName>("right_boundary"),
82  getParam<BoundaryName>("top_boundary"),
83  getParam<BoundaryName>("bottom_boundary")};
84  const std::vector<std::string> boundary_param_names = {
85  "left_boundary", "right_boundary", "top_boundary", "bottom_boundary"};
86 
87  // IDs for each input (indexed by input mesh and then left/right/top/bottom)
88  std::vector<std::vector<boundary_id_type>> input_bids(
89  _input_names.size(), std::vector<boundary_id_type>(4, Moose::INVALID_BOUNDARY_ID));
90  bool have_common_ids = true;
91 
92  // Using a vector of vectors instead of vector of sets to preserve insertion order
93  std::vector<std::vector<boundary_id_type>> input_bids_unique(_input_names.size());
94 
95  // For an error check on the number of uniquely named boundaries to stitch
96  size_t set_length = 0;
97 
98  // Given a boundary name (i.e., 'left_boundary'), this will give the correct
99  // index to get the correct boundary id to later use for boundary stitching
100  std::map<std::string, size_t> boundary_name_to_index_map;
101 
102  // Keep track of used boundary ids to generate new, unused ones later (if needed)
103  std::set<boundary_id_type> all_boundary_ids;
104 
105  // Read in all of the meshes
106  _meshes.resize(_input_names.size());
107  for (const auto i : index_range(_input_names))
108  {
109  std::unique_ptr<ReplicatedMesh> mesh = dynamic_pointer_cast<ReplicatedMesh>(*_mesh_ptrs[i]);
110  if (!mesh)
111  paramError("inputs",
112  "The input mesh '",
113  _input_names[i],
114  "' is not a replicated mesh.\n\n",
115  type(),
116  " only works with inputs that are replicated.\n\n",
117  "Try running without distributed mesh.");
118  _meshes[i] = dynamic_pointer_cast<ReplicatedMesh>(mesh);
119 
120  // List of boundary ids corresponsind to left/right/top/bottom boundary names
121  const auto ids = MooseMeshUtils::getBoundaryIDs(*_meshes[i], boundary_names, false);
122  mooseAssert(ids.size() == boundary_names.size(),
123  "Unexpected number of ids returned for MooseMeshUtils::getBoundaryIDs");
124 
125  // Keep track of indices of first instance of each unique boundary id
126  std::map<boundary_id_type, size_t> seen_bid_to_index_map;
127 
128  size_t index = 0;
129  for (const auto side : make_range(4))
130  {
131  // Check if the boundary has been initialized
132  if (ids[side] == Moose::INVALID_BOUNDARY_ID)
133  paramError("inputs",
134  "The '",
135  boundary_param_names[side],
136  "' parameter with value '",
137  boundary_names[side],
138  "' does not exist in input mesh '",
139  _input_names[i],
140  "'");
141 
142  input_bids[i][side] = ids[side];
143 
144  // We only do this when i == 0 because all input meshes should have the
145  // same index map. Allowing different index maps for different input
146  // meshes results in undefined behaviour when stitching
147  if (i == 0)
148  {
149  if (std::count(input_bids_unique[i].begin(), input_bids_unique[i].end(), ids[side]) == 0)
150  {
151  input_bids_unique[i].push_back(ids[side]);
152  seen_bid_to_index_map[ids[side]] = index;
153  boundary_name_to_index_map[boundary_param_names[side]] = index++;
154  }
155  else
156  boundary_name_to_index_map[boundary_param_names[side]] = seen_bid_to_index_map[ids[side]];
157  }
158 
159  else // i > 0
160  {
161  if (ids[side] != input_bids[i - 1][side])
162  have_common_ids = false;
163 
164  if (std::count(input_bids_unique[i].begin(), input_bids_unique[i].end(), ids[side]) == 0)
165  input_bids_unique[i].push_back(ids[side]);
166  }
167  }
168 
169  // Error check on lengths of input_bids_unique
170  if (i > 0 && set_length != input_bids_unique.size())
171  mooseError(
172  "Input meshes have incompatible boundary ids. This can occur when input meshes have "
173  "the same boundary id for multiple boundaries, but in a way that is different "
174  "between the meshes. Try assigning each left/right/top/bottom to its own boundary id.");
175 
176  set_length = input_bids_unique.size();
177 
178  // List of all boundary ids used in _meshes[i] so we don't reuse any existing boundary ids.
179  const auto all_ids = _meshes[i]->get_boundary_info().get_boundary_ids();
180 
181  // Keep track of used IDs so we can later find IDs that are unused across all meshes
182  all_boundary_ids.insert(all_ids.begin(), all_ids.end());
183  }
184 
185  // Check if the user has provided the x, y and z widths.
186  // If not (their value is 0 by default), compute them
187  auto bbox = MeshTools::create_bounding_box(*_meshes[0]);
188  if (_x_width == 0)
189  _x_width = bbox.max()(0) - bbox.min()(0);
190  if (_y_width == 0)
191  _y_width = bbox.max()(1) - bbox.min()(1);
192  if (_z_width == 0)
193  _z_width = bbox.max()(2) - bbox.min()(2);
194 
195  // stitch_bids will hold boundary ids passed to mesh stitcher
196  std::vector<boundary_id_type> stitch_bids;
197 
198  if (have_common_ids) // No need to change existing boundary ids
199  stitch_bids = input_bids_unique[0];
200 
201  else // Need to make boundary ids common accross all inputs
202  {
203  // Generate previously unused boundary ids
204  for (boundary_id_type id = 0; id != Moose::INVALID_BOUNDARY_ID; ++id)
205  if (!all_boundary_ids.count(id))
206  {
207  stitch_bids.push_back(id);
208  // It is okay to only use the 0th index here, since we ensure all entries in
209  // input_bids_unique have the same size through the above error check.
210  if (stitch_bids.size() == input_bids_unique[0].size())
211  break;
212  }
213 
214  // Make all inputs have common boundary ids
215  for (const auto i : index_range(_meshes))
216  for (const auto side : index_range(stitch_bids))
217  MeshTools::Modification::change_boundary_id(
218  *_meshes[i], input_bids_unique[i][side], stitch_bids[side]);
219  }
220 
221  // Data structure that holds each row
222  _row_meshes.resize(_pattern.size());
223 
224  // Aliases
225  const boundary_id_type &left_bid(stitch_bids[boundary_name_to_index_map["left_boundary"]]),
226  right_bid(stitch_bids[boundary_name_to_index_map["right_boundary"]]),
227  top_bid(stitch_bids[boundary_name_to_index_map["top_boundary"]]),
228  bottom_bid(stitch_bids[boundary_name_to_index_map["bottom_boundary"]]);
229 
230  // Build each row mesh
231  for (MooseIndex(_pattern) i = 0; i < _pattern.size(); ++i)
232  for (MooseIndex(_pattern[i]) j = 0; j < _pattern[i].size(); ++j)
233  {
234  Real deltax = j * _x_width, deltay = i * _y_width;
235 
236  // If this is the first cell of the row initialize the row mesh
237  if (j == 0)
238  {
239  auto clone = _meshes[_pattern[i][j]]->clone();
240  _row_meshes[i] = dynamic_pointer_cast<ReplicatedMesh>(clone);
241 
242  MeshTools::Modification::translate(*_row_meshes[i], deltax, -deltay, 0);
243 
244  continue;
245  }
246 
247  ReplicatedMesh & cell_mesh = *_meshes[_pattern[i][j]];
248 
249  // Move the mesh into the right spot. -i because we are starting at the top
250  MeshTools::Modification::translate(cell_mesh, deltax, -deltay, 0);
251 
252  // Subdomain map is aggregated on each row first. This retrieves a writable reference
253  auto & main_subdomain_map = _row_meshes[i]->set_subdomain_name_map();
254  // Retrieve subdomain name map from the mesh to be stitched and merge into the row's
255  // subdomain map
256  const auto & increment_subdomain_map = cell_mesh.get_subdomain_name_map();
257  mergeSubdomainNameMaps(main_subdomain_map, increment_subdomain_map);
258 
259  _row_meshes[i]->stitch_meshes(cell_mesh,
260  right_bid,
261  left_bid,
262  TOLERANCE,
263  /*clear_stitched_boundary_ids=*/true,
264  /*verbose=*/false);
265 
266  // Undo the translation
267  MeshTools::Modification::translate(cell_mesh, -deltax, deltay, 0);
268  }
269 
270  // Now stitch together the rows
271  // We're going to stitch them all to row 0 (which is the real mesh)
272  for (MooseIndex(_pattern) i = 1; i < _pattern.size(); i++)
273  {
274  // Get a writeable reference subdomain-name map for the main mesh to which the other rows are
275  // stitched
276  auto & main_subdomain_map = _row_meshes[0]->set_subdomain_name_map();
277  // Retrieve subdomain name map from the mesh to be stitched and merge into the main
278  // subdomain map
279  const auto & increment_subdomain_map = _row_meshes[i]->get_subdomain_name_map();
280  mergeSubdomainNameMaps(main_subdomain_map, increment_subdomain_map);
281 
282  _row_meshes[0]->stitch_meshes(*_row_meshes[i],
283  bottom_bid,
284  top_bid,
285  TOLERANCE,
286  /*clear_stitched_boundary_ids=*/true,
287  /*verbose=*/false);
288  }
289 
290  // Change boundary ids back to those of meshes[0] to not surprise user
291  if (!have_common_ids)
292  for (const auto side : index_range(stitch_bids))
293  MeshTools::Modification::change_boundary_id(
294  *_row_meshes[0], stitch_bids[side], input_bids_unique[0][side]);
295 
296  _row_meshes[0]->set_isnt_prepared();
297  return dynamic_pointer_cast<MeshBase>(_row_meshes[0]);
298 }
299 
300 void
302  std::map<subdomain_id_type, std::string> & main_subdomain_map,
303  const std::map<subdomain_id_type, std::string> & increment_subdomain_map)
304 {
305  // Insert secondary subdomain map into main subdomain map
306  main_subdomain_map.insert(increment_subdomain_map.begin(), increment_subdomain_map.end());
307  // Check if one SubdomainName is shared by more than one subdomain ids
308  std::set<SubdomainName> main_subdomain_map_name_list;
309  for (auto const & id_name_pair : main_subdomain_map)
310  {
311  const auto name_to_insert = id_name_pair.second;
312  if (main_subdomain_map_name_list.find(name_to_insert) != main_subdomain_map_name_list.end())
313  paramError("inputs",
314  "Two of the input meshes contain a subdomain with the name '" + name_to_insert +
315  "' which corresponds to two conflicting subdomain ids.");
316  main_subdomain_map_name_list.emplace(name_to_insert);
317  }
318 }
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)
std::vector< std::unique_ptr< ReplicatedMesh > > _meshes
Holds the pointers to the input generated meshes.
const std::vector< std::vector< unsigned int > > & _pattern
The pattern, starting with the upper left corner.
const BoundaryID INVALID_BOUNDARY_ID
Definition: MooseTypes.C:24
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.
std::vector< std::unique_ptr< ReplicatedMesh > > _row_meshes
Holds a mesh for each row, these will be stitched together in the end.
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
const std::string & type() const
Get the type of this class.
Definition: MooseBase.h:50
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.
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
std::string stringify(const T &t)
conversion to string
Definition: Conversion.h:62
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.
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 option 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:32
void ErrorVector unsigned int
auto index_range(const T &sizable)
const std::vector< MeshGeneratorName > & _input_names
The mesh generators to read.