16 #include "libmesh/replicated_mesh.h" 17 #include "libmesh/unstructured_mesh.h" 18 #include "libmesh/mesh_modification.h" 19 #include "libmesh/point.h" 20 #include "libmesh/node.h" 30 "Combine multiple meshes (or copies of one mesh) together into one (disjoint) mesh. Can " 31 "optionally translate those meshes before combining them.");
35 "The input MeshGenerators. This can either be N generators or 1 generator. If only 1 is " 36 "given then 'positions' must also be given.");
40 "The (optional) position of each given mesh. If N 'inputs' were given then this must either " 41 "be left blank or N positions must be given. If 1 input was given then this MUST be " 44 params.
addParam<std::vector<FileName>>(
45 "positions_file",
"Alternative way to provide the position of each given mesh.");
47 params.
addParam<
bool>(
"avoid_merging_subdomains",
49 "Whether to prevent merging subdomains by offsetting ids. The first mesh " 50 "in the input will keep the same subdomains ids, the others will have " 51 "offsets. All subdomain names will remain valid");
52 params.
addParam<
bool>(
"avoid_merging_boundaries",
54 "Whether to prevent merging sidesets by offsetting ids. The first mesh " 55 "in the input will keep the same boundary ids, the others will have " 56 "offsets. All boundary names will remain valid");
63 _meshes(getMeshes(
"inputs")),
64 _input_names(getParam<
std::vector<MeshGeneratorName>>(
"inputs")),
65 _avoid_merging_subdomains(getParam<bool>(
"avoid_merging_subdomains")),
66 _avoid_merging_boundaries(getParam<bool>(
"avoid_merging_boundaries"))
69 paramError(
"input_names",
"You need to specify at least one MeshGenerator as an input.");
72 mooseError(
"Both 'positions' and 'positions_file' cannot be specified simultaneously in " 79 "If only one input mesh is given, then 'positions' or 'positions_file' must also " 88 _positions = getParam<std::vector<Point>>(
"positions");
92 paramError(
"positions",
"If only one input mesh is given, then 'positions' cannot be empty.");
98 "If more than one input mesh is provided then the number of positions provided must " 99 "exactly match the number of input meshes.");
103 std::vector<FileName> positions_file = getParam<std::vector<FileName>>(
"positions_file");
106 if ((
_input_names.size() == 1) && positions_file.empty())
108 "If only one input mesh is given, then 'positions_file' cannot be empty.");
110 for (
const auto & f : positions_file)
119 if (data.size() && (
_input_names.size() != data.size()))
121 "If more than one input mesh is provided then the number of positions must " 122 "exactly match the number of input meshes.");
124 for (
const auto & d : data)
130 std::unique_ptr<MeshBase>
150 MeshTools::Modification::translate(
165 MeshTools::Modification::translate(
172 mesh->set_isnt_prepared();
190 MeshTools::Modification::translate(
204 copy = input_mesh->clone();
207 if (!translated_mesh)
213 MeshTools::Modification::translate(
220 for (
auto translated_node_ptr : translated_mesh->node_ptr_range())
222 auto & translated_node = *translated_node_ptr;
223 auto & input_node = input_mesh->node_ref(translated_node_ptr->id());
225 for (MooseIndex(LIBMESH_DIM) i = 0; i < LIBMESH_DIM; i++)
226 translated_node(i) = input_node(i);
230 final_mesh->set_isnt_prepared();
238 dof_id_type node_delta = destination.max_node_id();
239 dof_id_type elem_delta = destination.max_elem_id();
242 #ifdef LIBMESH_ENABLE_UNIQUE_ID 243 destination.parallel_max_unique_id();
249 std::unordered_map<subdomain_id_type, subdomain_id_type> id_remapping;
250 unsigned int block_offset = 0;
254 std::set<subdomain_id_type> source_ids;
255 std::set<subdomain_id_type> dest_ids;
256 source.subdomain_ids(source_ids,
true);
257 destination.subdomain_ids(dest_ids,
true);
258 mooseAssert(source_ids.size(),
"Should have a subdomain");
259 mooseAssert(dest_ids.size(),
"Should have a subdomain");
260 unsigned int max_dest_bid = *dest_ids.rbegin();
261 unsigned int min_source_bid = *source_ids.begin();
264 block_offset = 1 + max_dest_bid - min_source_bid;
265 for (
const auto bid : source_ids)
266 id_remapping[bid] = block_offset + bid;
270 destination.copy_nodes_and_elements(source,
281 BoundaryInfo & boundary = destination.get_boundary_info();
282 const BoundaryInfo & other_boundary = source.get_boundary_info();
284 unsigned int bid_offset = 0;
287 const auto boundary_ids = boundary.get_boundary_ids();
288 const auto other_boundary_ids = other_boundary.get_boundary_ids();
289 unsigned int max_dest_bid = boundary_ids.size() ? *boundary_ids.rbegin() : 0;
290 unsigned int min_source_bid = other_boundary_ids.size() ? *other_boundary_ids.begin() : 0;
293 bid_offset = 1 + max_dest_bid - min_source_bid;
301 for (
const auto & t : other_boundary.build_node_list())
302 boundary.add_node(std::get<0>(t) + node_delta, bid_offset + std::get<1>(t));
304 for (
const auto & t : other_boundary.build_side_list())
305 boundary.add_side(std::get<0>(t) + elem_delta, std::get<1>(t), bid_offset + std::get<2>(t));
307 for (
const auto & t : other_boundary.build_edge_list())
308 boundary.add_edge(std::get<0>(t) + elem_delta, std::get<1>(t), bid_offset + std::get<2>(t));
310 for (
const auto & t : other_boundary.build_shellface_list())
311 boundary.add_shellface(
312 std::get<0>(t) + elem_delta, std::get<1>(t), bid_offset + std::get<2>(t));
317 for (
const auto & [block_id, block_name] : destination.get_subdomain_name_map())
318 for (
const auto & [source_id, source_name] : source.get_subdomain_name_map())
319 if (block_name == source_name)
321 "Not merging subdomains is creating two subdomains with the same name '" +
322 block_name +
"' but different ids: " + std::to_string(source_id) +
323 " & " + std::to_string(block_id + block_offset) +
324 ".\n We recommend using a RenameBlockGenerator to prevent this as you " 325 "will get errors reading the Exodus output later.");
328 for (
const auto & [block_id, block_name] : source.get_subdomain_name_map())
329 destination.set_subdomain_name_map().insert(
330 std::make_pair<SubdomainID, SubdomainName>(block_id + block_offset, block_name));
335 for (
const auto & [b_id, b_name] : other_boundary.get_sideset_name_map())
336 for (
const auto & [source_id, source_name] : boundary.get_sideset_name_map())
337 if (b_name == source_name)
339 "avoid_merging_boundaries",
340 "Not merging boundaries is creating two sidesets with the same name '" + b_name +
341 "' but different ids: " + std::to_string(source_id) +
" & " +
342 std::to_string(b_id + bid_offset) +
343 ".\n We recommend using a RenameBoundaryGenerator to prevent this as you " 344 "will get errors reading the Exodus output later.");
345 for (
const auto & [b_id, b_name] : other_boundary.get_nodeset_name_map())
346 for (
const auto & [source_id, source_name] : boundary.get_nodeset_name_map())
347 if (b_name == source_name)
349 "avoid_merging_boundaries",
350 "Not merging boundaries is creating two nodesets with the same name '" + b_name +
351 "' but different ids: " + std::to_string(source_id) +
" & " +
352 std::to_string(b_id + bid_offset) +
353 ".\n We recommend using a RenameBoundaryGenerator to prevent this as you " 354 "will get errors reading the Exodus output later.");
357 for (
const auto & [nodeset_id, nodeset_name] : other_boundary.get_nodeset_name_map())
358 boundary.set_nodeset_name_map().insert(
359 std::make_pair<BoundaryID, BoundaryName>(nodeset_id + bid_offset, nodeset_name));
361 for (
const auto & [sideset_id, sideset_name] : other_boundary.get_sideset_name_map())
362 boundary.set_sideset_name_map().insert(
363 std::make_pair<BoundaryID, BoundaryName>(sideset_id + bid_offset, sideset_name));
365 for (
const auto & [edgeset_id, edgeset_name] : other_boundary.get_edgeset_name_map())
366 boundary.set_edgeset_name_map().insert(
367 std::make_pair<BoundaryID, BoundaryName>(edgeset_id + bid_offset, edgeset_name));
const bool _avoid_merging_boundaries
Boolean to control whether to prevent merging boundaries.
Collects multiple meshes into a single (unconnected) mesh.
const std::vector< Point > getDataAsPoints() const
Get the data in Point format.
void fillPositions()
Fill the offset positions for each mesh.
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.
const Parallel::Communicator & _communicator
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
void min(const T &r, T &o, Request &req) const
const bool _avoid_merging_subdomains
Boolean to control whether to prevent merging subdomains.
registerMooseObject("MooseApp", CombinerGenerator)
const std::vector< MeshGeneratorName > & _input_names
The mesh generators to use.
void setFormatFlag(FormatFlag value)
void paramError(const std::string ¶m, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
void read()
Perform the actual data reading.
static InputParameters validParams()
const std::string _name
The name of this class.
static InputParameters validParams()
void copyIntoMesh(UnstructuredMesh &destination, const UnstructuredMesh &source)
Helper funciton for copying one mesh into another.
Utility class for reading delimited data (e.g., CSV data).
void max(const T &r, T &o, Request &req) const
std::vector< Point > _positions
The (offsets) positions for each mesh.
const std::vector< std::unique_ptr< MeshBase > * > _meshes
CombinerGenerator(const InputParameters ¶meters)
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type.
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
void paramWarning(const std::string ¶m, Args... args) const
Emits a warning prefixed with the file and line number of the given param (from the input file) along...
MeshGenerators are objects that can modify or add to an existing mesh.