Skip to content

Commit

Permalink
Added parameters for watertight sideset and nodeset diagnostic check
Browse files Browse the repository at this point in the history
-Diagnostics can now take an additional vector parameter _watertight_boundaries
-Users can use it to input a list of sidesets/nodesets
-If supplied the diagnostic check functionality changes
-It will instead check if an external side/node is assigned to one of the provided boundary ids
-The error message for these instances is worded differently to reflect what the diagnostic check is looking for
-The t in checkWaterTightSidesets is now lowercase because it was bothering me and to match checkWatertightNodesets

closes #29077
  • Loading branch information
DanielYankura committed Nov 25, 2024
1 parent 9446d5f commit 3c505cd
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 10 deletions.
10 changes: 7 additions & 3 deletions framework/include/meshgenerators/MeshDiagnosticsGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ class MeshDiagnosticsGenerator : public MeshGenerator
/// Routine to check sideset orientation near subdomains
void checkSidesetsOrientation(const std::unique_ptr<MeshBase> & mesh) const;
//// Routine to check is mesh is fully covered in sidesets
void checkWaterTightSidesets(const std::unique_ptr<MeshBase> & mesh) const;
void checkWatertightSidesets(const std::unique_ptr<MeshBase> & mesh) const;
//// Routine to check is mesh is fully covered in nodesets
void checkWatertightNodesets(const std::unique_ptr<MeshBase> & mesh) const;
/// Helper function that finds the intersection of the _watertight_boundaries vector and the provided one
std::vector<boundary_id_type> findBoundaryOverlap(std::vector<boundary_id_type>, std::vector<boundary_id_type> boundary_ids) const;
/// Routine to check the element volumes
void checkElementVolumes(const std::unique_ptr<MeshBase> & mesh) const;
/// Routine to check the element types in each subdomain
Expand Down Expand Up @@ -62,10 +64,12 @@ class MeshDiagnosticsGenerator : public MeshGenerator

/// whether to check that sidesets are consistently oriented using neighbor subdomains
const MooseEnum _check_sidesets_orientation;
//// whether to check that each external side is assigned to a sideset
/// whether to check that each external side is assigned to a sideset
const MooseEnum _check_watertight_sidesets;
//// whether to check that each external node is assigned to a nodeset
/// whether to check that each external node is assigned to a nodeset
const MooseEnum _check_watertight_nodesets;
/// boundaries to be checked in watertight checks
const std::vector<BoundaryID> _watertight_boundaries;
/// whether to check element volumes
const MooseEnum _check_element_volumes;
/// minimum size for element volume to be counted as a tiny element
Expand Down
74 changes: 67 additions & 7 deletions framework/src/meshgenerators/MeshDiagnosticsGenerator.C
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ MeshDiagnosticsGenerator::validParams()
"check_for_watertight_nodesets",
chk_option,
"whether to check for external nodes that are not assigned to any nodeset");
params.addParam<std::vector<BoundaryID>>(
"boundaries_to_check",
{},
"IDs of boundaries to be checked for watertight checks");
params.addParam<MooseEnum>(
"examine_element_volumes", chk_option, "whether to examine volume of the elements");
params.addParam<Real>("minimum_element_volumes", 1e-16, "minimum size for element volume");
Expand Down Expand Up @@ -85,6 +89,7 @@ MeshDiagnosticsGenerator::MeshDiagnosticsGenerator(const InputParameters & param
_check_sidesets_orientation(getParam<MooseEnum>("examine_sidesets_orientation")),
_check_watertight_sidesets(getParam<MooseEnum>("check_for_watertight_sidesets")),
_check_watertight_nodesets(getParam<MooseEnum>("check_for_watertight_nodesets")),
_watertight_boundaries(getParam<std::vector<BoundaryID>>("boundaries_to_check")),
_check_element_volumes(getParam<MooseEnum>("examine_element_volumes")),
_min_volume(getParam<Real>("minimum_element_volumes")),
_max_volume(getParam<Real>("maximum_element_volumes")),
Expand Down Expand Up @@ -132,7 +137,7 @@ MeshDiagnosticsGenerator::generate()
checkSidesetsOrientation(mesh);

if (_check_watertight_sidesets != "NO_CHECK")
checkWaterTightSidesets(mesh);
checkWatertightSidesets(mesh);

if (_check_watertight_nodesets != "NO_CHECK")
checkWatertightNodesets(mesh);
Expand Down Expand Up @@ -284,7 +289,7 @@ MeshDiagnosticsGenerator::checkSidesetsOrientation(const std::unique_ptr<MeshBas
}

void
MeshDiagnosticsGenerator::checkWaterTightSidesets(const std::unique_ptr<MeshBase> & mesh) const
MeshDiagnosticsGenerator::checkWatertightSidesets(const std::unique_ptr<MeshBase> & mesh) const
{
/*
Algorithm Overview:
Expand All @@ -300,21 +305,27 @@ MeshDiagnosticsGenerator::checkWaterTightSidesets(const std::unique_ptr<MeshBase
const auto sideset_map = boundary_info.get_sideset_map();
unsigned int num_faces_without_sideset = 0;

// Make copy of _watertight_boundaries and sort it for later use
std::vector<BoundaryID> boundary_copy = _watertight_boundaries;
std::sort(boundary_copy.begin(), boundary_copy.end());

for (const auto elem : mesh->active_element_ptr_range())
{
std::vector<std::unique_ptr<Elem>> elem_sides(elem->n_sides());
for (auto i : elem->side_index_range())
{
// Check if side is external
if (elem->neighbor_ptr(i) == nullptr)
{
// If external check if that side had a sideset
// If external get the boundary ids associated with this side
std::vector<boundary_id_type> boundary_ids;
//boundary_info.boundary_ids(elem, i, boundary_ids);
auto side_range = sideset_map.equal_range(elem);
bool has_boundary = false;
for (const auto & itr : as_range(side_range))
if (itr.second.first == i)
has_boundary = true;
if (!has_boundary)
boundary_ids.push_back(i);
// get intersection of boundary_ids and _watertight_boundaries
std::vector<boundary_id_type> intersections = findBoundaryOverlap(boundary_copy, boundary_ids);
if (boundary_ids.empty())
{
// This side does not have a sideset!!!
std::string message;
Expand All @@ -330,6 +341,22 @@ MeshDiagnosticsGenerator::checkWaterTightSidesets(const std::unique_ptr<MeshBase
num_faces_without_sideset++;
}
}
else if (!_watertight_boundaries.empty() && intersections.empty())
{
// This means that at least 1 boundary was specified, and that this side was not assigned to it
std::string message;
if (num_faces_without_sideset < _num_outputs)
{
if (mesh->mesh_dimension() == 3)
message = "Element " + std::to_string(elem->id()) +
" contains an external face which has not been assigned to one of the specified sidesets";
else
message = "Element " + std::to_string(elem->id()) +
" contains an external edge which has not been assigned to one of the specified sidesets";
_console << message << std::endl;
num_faces_without_sideset++;
}
}
}
}
}
Expand Down Expand Up @@ -361,6 +388,10 @@ MeshDiagnosticsGenerator::checkWatertightNodesets(const std::unique_ptr<MeshBase
unsigned int num_nodes_without_nodeset = 0;
std::set<dof_id_type> checked_nodes_id;

// Make copy of _watertight_boundaries and sort it for later use
std::vector<BoundaryID> boundary_copy = _watertight_boundaries;
std::sort(boundary_copy.begin(), boundary_copy.end());

for (const auto elem : mesh->active_element_ptr_range())
{
for (const auto i : elem->side_index_range())
Expand All @@ -376,6 +407,10 @@ MeshDiagnosticsGenerator::checkWatertightNodesets(const std::unique_ptr<MeshBase
const auto node = node_list[j];
if (checked_nodes_id.count(node->id()))
continue;
// get vector of node's boundaries (in most cases it will only have one)
std::vector<boundary_id_type> boundary_ids;
boundary_info.boundary_ids(node, boundary_ids);
std::vector<boundary_id_type> intersection = findBoundaryOverlap(boundary_copy, boundary_ids);
// if node has no nodeset, add it to list of bad nodes
if (boundary_info.n_boundary_ids(node) == 0)
{
Expand All @@ -391,6 +426,20 @@ MeshDiagnosticsGenerator::checkWatertightNodesets(const std::unique_ptr<MeshBase
_console << message << std::endl;
}
}
else if (!_watertight_boundaries.empty() && intersection.empty())
{
// This indicates that at least one boundary was specified, and it does not match with any
num_nodes_without_nodeset++;
checked_nodes_id.insert(node->id());
std::string message;
if (num_nodes_without_nodeset < _num_outputs)
{
message =
"Node " + std::to_string(node->id()) +
" is on an external boundary of the mesh, but has not been assigned to one of the specified nodesets";
_console << message << std::endl;
}
}
}
}
}
Expand All @@ -401,6 +450,17 @@ MeshDiagnosticsGenerator::checkWatertightNodesets(const std::unique_ptr<MeshBase
diagnosticsLog(message, _check_watertight_nodesets, num_nodes_without_nodeset);
}

std::vector<boundary_id_type>
MeshDiagnosticsGenerator::findBoundaryOverlap(std::vector<boundary_id_type> boundary_copy, std::vector<boundary_id_type> boundary_ids) const
{
// Both vectors must be sorted first
// Returns their intersection (elements that they share)
std::sort(boundary_ids.begin(), boundary_ids.end());
std::vector<boundary_id_type> boundary_intersection;
std::set_intersection(boundary_copy.begin(), boundary_copy.end(), boundary_ids.begin(), boundary_ids.end(), std::back_inserter(boundary_intersection));
return boundary_intersection;
}

void
MeshDiagnosticsGenerator::checkElementVolumes(const std::unique_ptr<MeshBase> & mesh) const
{
Expand Down

0 comments on commit 3c505cd

Please sign in to comment.