Skip to content

Commit

Permalink
PatternedMeshGenerator boundary stiching patch
Browse files Browse the repository at this point in the history
This commit patches the bug described in idaholab#23312 which results in incorrect mesh
stitching in the PatternedMeshGenerator when the mesh tiles provided in the
'inputs' parameter do not have the same boundary ids.
A check is added to detect this issue, and if present, the boundary ids of all
tile meshes are changed to be common, previously unused boundary ids.
This allows for the tiles to be stitched together correctly.
Additionally, the class was cleaned up to incorporate recommended code
standards.

closes idaholab#23312
  • Loading branch information
pbehne committed Feb 16, 2023
1 parent ec30cd9 commit bde57d4
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 52 deletions.
6 changes: 0 additions & 6 deletions framework/include/meshgenerators/PatternedMeshGenerator.h
Expand Up @@ -41,12 +41,6 @@ class PatternedMeshGenerator : public MeshGenerator
/// The pattern, starting with the upper left corner
const std::vector<std::vector<unsigned int>> & _pattern;

/// Holds the pointers to the input generated meshes
std::vector<std::unique_ptr<ReplicatedMesh>> _meshes;

/// Holds a mesh for each row, these will be stitched together in the end
std::vector<std::unique_ptr<ReplicatedMesh>> _row_meshes;

/**
* Merges the subdomain name maps between two meshes, throws an error
* if input maps contain shared subdomain name with conflicting subdomain ids
Expand Down
137 changes: 91 additions & 46 deletions framework/src/meshgenerators/PatternedMeshGenerator.C
Expand Up @@ -74,10 +74,23 @@ std::unique_ptr<MeshBase>
PatternedMeshGenerator::generate()
{
// Reserve spaces for all the meshes
_meshes.reserve(_input_names.size());
std::vector<std::unique_ptr<ReplicatedMesh>> meshes(_input_names.size());

// Getting the boundaries provided by the user
std::vector<BoundaryName> boundary_names = {getParam<BoundaryName>("left_boundary"),
getParam<BoundaryName>("right_boundary"),
getParam<BoundaryName>("top_boundary"),
getParam<BoundaryName>("bottom_boundary")};

boundary_id_type left_id, right_id, top_id, bottom_id;
std::vector<boundary_id_type> left_ids(_input_names.size()), right_ids(_input_names.size()),
top_ids(_input_names.size()), bottom_ids(_input_names.size());
bool have_common_ids = true;

std::set<boundary_id_type> used_boundary_ids;

// Read in all of the meshes
for (MooseIndex(_input_names) i = 0; i < _input_names.size(); ++i)
for (const auto i : index_range(_input_names))
{
std::unique_ptr<ReplicatedMesh> mesh = dynamic_pointer_cast<ReplicatedMesh>(*_mesh_ptrs[i]);
if (!mesh)
Expand All @@ -88,46 +101,69 @@ PatternedMeshGenerator::generate()
type(),
" only works with inputs that are replicated.\n\n",
"Try running without distributed mesh.");
_meshes.push_back(std::move(mesh));
meshes[i] = dynamic_pointer_cast<ReplicatedMesh>(mesh->clone());

std::vector<boundary_id_type> ids =
MooseMeshUtils::getBoundaryIDs(*meshes[i], boundary_names, true);
mooseAssert(ids.size() == boundary_names.size(),
"Unexpected number of ids returned for MooseMeshUtils::getBoundaryIDs");

// Keep track of used IDs so we can find IDs that are unused across all meshes
used_boundary_ids.insert(ids.begin(), ids.end());

left_id = ids[0], right_id = ids[1], top_id = ids[2], bottom_id = ids[3];

// Check if all the boundaries have been initialized
if (left_id == -123)
mooseError("The left boundary has not been initialized properly.");
if (right_id == -123)
mooseError("The right boundary has not been initialized properly.");
if (top_id == -123)
mooseError("The top boundary has not been initialized properly.");
if (bottom_id == -123)
mooseError("The bottom boundary has not been initialized properly.");

left_ids[i] = left_id, right_ids[i] = right_id, top_ids[i] = top_id, bottom_ids[i] = bottom_id;

// Check if boundary ids are same accross all meshes
if (i > 0 && (left_id != left_ids[i - 1] || right_id != right_ids[i - 1] ||
top_id != top_ids[i - 1] || bottom_id != bottom_ids[i - 1]))
have_common_ids = false;
}

// Data structure that holds each row
_row_meshes.resize(_pattern.size());

// Getting the boundaries provided by the user
std::vector<BoundaryName> boundary_names = {getParam<BoundaryName>("left_boundary"),
getParam<BoundaryName>("right_boundary"),
getParam<BoundaryName>("top_boundary"),
getParam<BoundaryName>("bottom_boundary")};

std::vector<boundary_id_type> ids =
MooseMeshUtils::getBoundaryIDs(*_meshes[0], boundary_names, true);

mooseAssert(ids.size() == boundary_names.size(),
"Unexpected number of ids returned for MooseMeshUtils::getBoundaryIDs");

boundary_id_type left = ids[0], right = ids[1], top = ids[2], bottom = ids[3];

// Check if all the boundaries have been initialized
if (left == -123)
mooseError("The left boundary has not been initialized properly.");
if (right == -123)
mooseError("The right boundary has not been initialized properly.");
if (top == -123)
mooseError("The top boundary has not been initialized properly.");
if (bottom == -123)
mooseError("The bottom boundary has not been initialized properly.");

// Check if the user has provided the x, y and z widths.
// If not (their value is 0 by default), compute them
auto bbox = MeshTools::create_bounding_box(*_meshes[0]);
auto bbox = MeshTools::create_bounding_box(*meshes[0]);
if (_x_width == 0)
_x_width = bbox.max()(0) - bbox.min()(0);
if (_y_width == 0)
_y_width = bbox.max()(1) - bbox.min()(1);
if (_z_width == 0)
_z_width = bbox.max()(2) - bbox.min()(2);

// Make boundary ids same accross all meshes
if (!have_common_ids)
{
// Generate some unused_boundary ids to use as common ids accross meshes
std::vector<boundary_id_type> ids;
ids.reserve(4);
for (boundary_id_type id = 0; id != Moose::INVALID_BOUNDARY_ID; ++id)
if (!used_boundary_ids.count(id))
ids.push_back(id);

left_id = ids[0], right_id = ids[1], top_id = ids[2], bottom_id = ids[3];
for (const auto i : index_range(meshes))
{
MeshTools::Modification::change_boundary_id(*meshes[i], left_ids[i], left_id);
MeshTools::Modification::change_boundary_id(*meshes[i], right_ids[i], right_id);
MeshTools::Modification::change_boundary_id(*meshes[i], top_ids[i], top_id);
MeshTools::Modification::change_boundary_id(*meshes[i], bottom_ids[i], bottom_id);
}
}

// Data structure that holds each row
std::vector<std::unique_ptr<ReplicatedMesh>> row_meshes(_pattern.size());

// Build each row mesh
for (MooseIndex(_pattern) i = 0; i < _pattern.size(); ++i)
for (MooseIndex(_pattern[i]) j = 0; j < _pattern[i].size(); ++j)
Expand All @@ -137,31 +173,31 @@ PatternedMeshGenerator::generate()
// If this is the first cell of the row initialize the row mesh
if (j == 0)
{
auto clone = _meshes[_pattern[i][j]]->clone();
_row_meshes[i] = dynamic_pointer_cast<ReplicatedMesh>(clone);
auto clone = meshes[_pattern[i][j]]->clone();
row_meshes[i] = dynamic_pointer_cast<ReplicatedMesh>(clone);

MeshTools::Modification::translate(*_row_meshes[i], deltax, -deltay, 0);
MeshTools::Modification::translate(*row_meshes[i], deltax, -deltay, 0);

continue;
}

ReplicatedMesh & cell_mesh = *_meshes[_pattern[i][j]];
ReplicatedMesh & cell_mesh = *meshes[_pattern[i][j]];

// Move the mesh into the right spot. -i because we are starting at the top
MeshTools::Modification::translate(cell_mesh, deltax, -deltay, 0);

// Subdomain map is aggregated on each row first. This retrieves a writable reference
auto & main_subdomain_map = _row_meshes[i]->set_subdomain_name_map();
auto & main_subdomain_map = row_meshes[i]->set_subdomain_name_map();
// Retrieve subdomain name map from the mesh to be stitched and merge into the row's
// subdomain map
const auto & increment_subdomain_map = cell_mesh.get_subdomain_name_map();
mergeSubdomainNameMaps(main_subdomain_map, increment_subdomain_map);

_row_meshes[i]->stitch_meshes(cell_mesh,
right,
left,
TOLERANCE,
/*clear_stitched_boundary_ids=*/true);
row_meshes[i]->stitch_meshes(cell_mesh,
right_id,
left_id,
TOLERANCE,
/*clear_stitched_boundary_ids=*/true);

// Undo the translation
MeshTools::Modification::translate(cell_mesh, -deltax, deltay, 0);
Expand All @@ -173,17 +209,26 @@ PatternedMeshGenerator::generate()
{
// Get a writeable reference subdomain-name map for the main mesh to which the other rows are
// stitched
auto & main_subdomain_map = _row_meshes[0]->set_subdomain_name_map();
auto & main_subdomain_map = row_meshes[0]->set_subdomain_name_map();
// Retrieve subdomain name map from the mesh to be stitched and merge into the main
// subdomain map
const auto & increment_subdomain_map = _row_meshes[i]->get_subdomain_name_map();
const auto & increment_subdomain_map = row_meshes[i]->get_subdomain_name_map();
mergeSubdomainNameMaps(main_subdomain_map, increment_subdomain_map);

_row_meshes[0]->stitch_meshes(
*_row_meshes[i], bottom, top, TOLERANCE, /*clear_stitched_boundary_ids=*/true);
row_meshes[0]->stitch_meshes(
*row_meshes[i], bottom_id, top_id, TOLERANCE, /*clear_stitched_boundary_ids=*/true);
}

// Change boundary ids back to those of meshes[0] to not surprise user
if (!have_common_ids)
{
MeshTools::Modification::change_boundary_id(*row_meshes[0], left_id, left_ids[0]);
MeshTools::Modification::change_boundary_id(*row_meshes[0], right_id, right_ids[0]);
MeshTools::Modification::change_boundary_id(*row_meshes[0], top_id, top_ids[0]);
MeshTools::Modification::change_boundary_id(*row_meshes[0], bottom_id, bottom_ids[0]);
}

return dynamic_pointer_cast<MeshBase>(_row_meshes[0]);
return dynamic_pointer_cast<MeshBase>(row_meshes[0]);
}

void
Expand Down

0 comments on commit bde57d4

Please sign in to comment.