https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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
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
73std::unique_ptr<MeshBase>
75{
76 return generate(nullptr);
77}
78
79std::unique_ptr<MeshBase>
80PatternedMeshGenerator::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())
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
309void
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}
registerMooseObject("MooseApp", PatternedMeshGenerator)
void ErrorVector unsigned int
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
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 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...
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 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.
static InputParameters validParams()
const std::string & type() const
Get the type of this class.
Definition MooseBase.h:93
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
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
Reads one or more 2D mesh files and stitches them together based on a provided two-dimensional patter...
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
const std::vector< std::unique_ptr< MeshBase > * > _mesh_ptrs
Holds pointers to the meshes before they are generated.
const std::vector< std::vector< unsigned int > > & _pattern
The pattern, starting with the upper left corner.
PatternedMeshGenerator(const InputParameters &parameters)
static InputParameters validParams()
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...
const std::vector< MeshGeneratorName > & _input_names
The mesh generators to read.
MeshBase & mesh
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.
std::string stringify(const T &t)
conversion to string
Definition Conversion.h:64
const BoundaryID INVALID_BOUNDARY_ID
Definition MooseTypes.C:22
libMesh::BoundingBox create_bounding_box(const MeshBase &mesh)