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 std::string & _name
The name of this class.
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 ...
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
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 read()
Perform the actual data reading.
static InputParameters validParams()
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
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...
CombinerGenerator(const InputParameters ¶meters)
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
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.