Skip to content

Commit

Permalink
Merge pull request #27647 from GiudGiud/PR_fix_combiner
Browse files Browse the repository at this point in the history
  • Loading branch information
GiudGiud committed May 29, 2024
2 parents fc91b41 + e224d50 commit 9eafad3
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 19 deletions.
5 changes: 5 additions & 0 deletions framework/include/meshgenerators/CombinerGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,9 @@ class CombinerGenerator : public MeshGenerator

/// The (offsets) positions for each mesh
std::vector<Point> _positions;

/// Boolean to control whether to prevent merging subdomains
const bool _avoid_merging_subdomains;
/// Boolean to control whether to prevent merging boundaries
const bool _avoid_merging_boundaries;
};
123 changes: 106 additions & 17 deletions framework/src/meshgenerators/CombinerGenerator.C
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,26 @@ CombinerGenerator::validParams()
params.addParam<std::vector<FileName>>(
"positions_file", "Alternative way to provide the position of each given mesh.");

params.addParam<bool>("avoid_merging_subdomains",
false,
"Whether to prevent merging subdomains by offsetting ids. The first mesh "
"in the input will keep the same subdomains ids, the others will have "
"offsets. All subdomain names will remain valid");
params.addParam<bool>("avoid_merging_boundaries",
false,
"Whether to prevent merging sidesets by offsetting ids. The first mesh "
"in the input will keep the same boundary ids, the others will have "
"offsets. All boundary names will remain valid");

return params;
}

CombinerGenerator::CombinerGenerator(const InputParameters & parameters)
: MeshGenerator(parameters),
_meshes(getMeshes("inputs")),
_input_names(getParam<std::vector<MeshGeneratorName>>("inputs"))
_input_names(getParam<std::vector<MeshGeneratorName>>("inputs")),
_avoid_merging_subdomains(getParam<bool>("avoid_merging_subdomains")),
_avoid_merging_boundaries(getParam<bool>("avoid_merging_boundaries"))
{
if (_input_names.empty())
paramError("input_names", "You need to specify at least one MeshGenerator as an input.");
Expand Down Expand Up @@ -225,44 +238,120 @@ CombinerGenerator::copyIntoMesh(UnstructuredMesh & destination, const Unstructur
0;
#endif

// Prevent overlaps by offsetting the subdomains in
std::unordered_map<subdomain_id_type, subdomain_id_type> id_remapping;
unsigned int block_offset = 0;
if (_avoid_merging_subdomains)
{
// Note: if performance becomes an issue, this is overkill for just getting the max node id
std::set<subdomain_id_type> source_ids;
std::set<subdomain_id_type> dest_ids;
source.subdomain_ids(source_ids, false);
destination.subdomain_ids(dest_ids, false);
unsigned int max_dest_bid = *dest_ids.rbegin();
unsigned int min_source_bid = *source_ids.begin();
_communicator.max(max_dest_bid);
_communicator.min(min_source_bid);
block_offset = 1 + max_dest_bid - min_source_bid;
for (const auto bid : source_ids)
id_remapping[bid] = block_offset + bid;
}

// Copy mesh data over from the other mesh
destination.copy_nodes_and_elements(source,
// Skipping this should cause the neighors
// Skipping this should cause the neighbors
// to simply be copied from the other mesh
// (which makes sense and is way faster)
/*skip_find_neighbors = */ true,
elem_delta,
node_delta,
unique_delta);
unique_delta,
_avoid_merging_subdomains ? &id_remapping : nullptr);

// Get an offset to prevent overlaps / wild merging between boundaries
BoundaryInfo & boundary = destination.get_boundary_info();
const BoundaryInfo & other_boundary = source.get_boundary_info();

unsigned int bid_offset = 0;
if (_avoid_merging_boundaries)
{
unsigned int max_dest_bid = *boundary.get_boundary_ids().rbegin();
unsigned int min_source_bid = *other_boundary.get_boundary_ids().begin();
_communicator.max(max_dest_bid);
_communicator.min(min_source_bid);
bid_offset = 1 + max_dest_bid - min_source_bid;
}

// Note: the code below originally came from ReplicatedMesh::stitch_mesh_helper()
// in libMesh replicated_mesh.C around line 1203

// Copy BoundaryInfo from other_mesh too. We do this via the
// list APIs rather than element-by-element for speed.
BoundaryInfo & boundary = destination.get_boundary_info();
const BoundaryInfo & other_boundary = source.get_boundary_info();
for (const auto & t : other_boundary.build_node_list())
boundary.add_node(std::get<0>(t) + node_delta, std::get<1>(t));
boundary.add_node(std::get<0>(t) + node_delta, bid_offset + std::get<1>(t));

for (const auto & t : other_boundary.build_side_list())
boundary.add_side(std::get<0>(t) + elem_delta, std::get<1>(t), std::get<2>(t));
boundary.add_side(std::get<0>(t) + elem_delta, std::get<1>(t), bid_offset + std::get<2>(t));

for (const auto & t : other_boundary.build_edge_list())
boundary.add_edge(std::get<0>(t) + elem_delta, std::get<1>(t), std::get<2>(t));
boundary.add_edge(std::get<0>(t) + elem_delta, std::get<1>(t), bid_offset + std::get<2>(t));

for (const auto & t : other_boundary.build_shellface_list())
boundary.add_shellface(std::get<0>(t) + elem_delta, std::get<1>(t), std::get<2>(t));
boundary.add_shellface(
std::get<0>(t) + elem_delta, std::get<1>(t), bid_offset + std::get<2>(t));

for (auto block_name_id_pair : source.get_subdomain_name_map())
destination.set_subdomain_name_map().insert(block_name_id_pair);
// Check for the case with two block ids sharing the same name
if (_avoid_merging_subdomains)
{
for (const auto & [block_id, block_name] : destination.get_subdomain_name_map())
for (const auto & [source_id, source_name] : source.get_subdomain_name_map())
if (block_name == source_name)
paramWarning("avoid_merging_subdomains",
"Not merging subdomains is creating two subdomains with the same name '" +
block_name + "' but different ids: " + std::to_string(source_id) +
" & " + std::to_string(block_id + block_offset) +
".\n We recommend using a RenameBlockGenerator to prevent this as you "
"will get errors reading the Exodus output later.");
}

for (const auto & [block_id, block_name] : source.get_subdomain_name_map())
destination.set_subdomain_name_map().insert(
std::make_pair<SubdomainID, SubdomainName>(block_id + block_offset, block_name));

// Check for the case with two boundary ids sharing the same name
if (_avoid_merging_boundaries)
{
for (const auto & [b_id, b_name] : other_boundary.get_sideset_name_map())
for (const auto & [source_id, source_name] : boundary.get_sideset_name_map())
if (b_name == source_name)
paramWarning(
"avoid_merging_boundaries",
"Not merging boundaries is creating two sidesets with the same name '" + b_name +
"' but different ids: " + std::to_string(source_id) + " & " +
std::to_string(b_id + bid_offset) +
".\n We recommend using a RenameBoundaryGenerator to prevent this as you "
"will get errors reading the Exodus output later.");
for (const auto & [b_id, b_name] : other_boundary.get_nodeset_name_map())
for (const auto & [source_id, source_name] : boundary.get_nodeset_name_map())
if (b_name == source_name)
paramWarning(
"avoid_merging_boundaries",
"Not merging boundaries is creating two nodesets with the same name '" + b_name +
"' but different ids: " + std::to_string(source_id) + " & " +
std::to_string(b_id + bid_offset) +
".\n We recommend using a RenameBoundaryGenerator to prevent this as you "
"will get errors reading the Exodus output later.");
}

for (auto nodeset_name_id_pair : other_boundary.get_nodeset_name_map())
boundary.set_nodeset_name_map().insert(nodeset_name_id_pair);
for (const auto & [nodeset_id, nodeset_name] : other_boundary.get_nodeset_name_map())
boundary.set_nodeset_name_map().insert(
std::make_pair<BoundaryID, BoundaryName>(nodeset_id + bid_offset, nodeset_name));

for (auto sideset_name_id_pair : other_boundary.get_sideset_name_map())
boundary.set_sideset_name_map().insert(sideset_name_id_pair);
for (const auto & [sideset_id, sideset_name] : other_boundary.get_sideset_name_map())
boundary.set_sideset_name_map().insert(
std::make_pair<BoundaryID, BoundaryName>(sideset_id + bid_offset, sideset_name));

for (auto edgeset_name_id_pair : other_boundary.get_edgeset_name_map())
boundary.set_edgeset_name_map().insert(edgeset_name_id_pair);
for (const auto & [edgeset_id, edgeset_name] : other_boundary.get_edgeset_name_map())
boundary.set_edgeset_name_map().insert(
std::make_pair<BoundaryID, BoundaryName>(edgeset_id + bid_offset, edgeset_name));
}
Binary file not shown.
Binary file not shown.
26 changes: 24 additions & 2 deletions test/tests/meshgenerators/combiner_generator/tests
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,29 @@
cli_args = '--mesh-only'
recover = false

detail = 'while keeping name(s) of sidesets intact.'
detail = 'while keeping name(s) of sidesets intact, and'
[]

[no_merge_subdomains]
type = 'Exodiff'
input = 'combiner_merge_names.i'
cli_args = 'Mesh/Combine/avoid_merging_subdomains=true --mesh-only no_merge_blocks.e'
exodiff = 'no_merge_blocks.e'
recover = false
exodiff_opts = '-pedantic'

detail = 'not merging subdomains, and'
[]

[no_merge_boundaries]
type = 'Exodiff'
input = 'combiner_merge_names.i'
cli_args = 'Mesh/Top_Block/boundary_id_offset=0 Mesh/Combine/avoid_merging_boundaries=true --mesh-only no_merge_boundaries.e'
exodiff = 'no_merge_boundaries.e'
recover = false
exodiff_opts = '-pedantic'

detail = 'not merging boundaries.'
[]
[]

Expand Down Expand Up @@ -100,4 +122,4 @@
detail = 'the number of meshes and the number of positions in the file is mismatched.'
[]
[]
[]
[]

0 comments on commit 9eafad3

Please sign in to comment.