Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added diagnostic check for watertight nodesets #29068

Open
wants to merge 5 commits into
base: next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions framework/include/meshgenerators/MeshDiagnosticsGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class MeshDiagnosticsGenerator : public MeshGenerator
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;
//// Routine to check is mesh is fully covered in nodesets
void checkWatertightNodesets(const std::unique_ptr<MeshBase> & mesh) 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,6 +64,8 @@ class MeshDiagnosticsGenerator : public MeshGenerator
const MooseEnum _check_sidesets_orientation;
//// 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
const MooseEnum _check_watertight_nodesets;
/// whether to check element volumes
const MooseEnum _check_element_volumes;
/// minimum size for element volume to be counted as a tiny element
Expand Down
76 changes: 72 additions & 4 deletions framework/src/meshgenerators/MeshDiagnosticsGenerator.C
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ MeshDiagnosticsGenerator::validParams()
"check_for_watertight_sidesets",
chk_option,
"whether to check for external sides that are not assigned to any sidesets");
params.addParam<MooseEnum>(
"check_for_watertight_nodesets",
chk_option,
"whether to check for external nodes that are not assigned to any nodeset");
params.addParam<MooseEnum>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let s add a parameter for the nodesets that are supposed to englobe the entire mesh. when this parameter is in use, the nodes are only checked against those "blessed" nodesets

same for sidsets
actually now that I see that this is not an option for sidesets, let s keep it for future work

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. I'll open a new issue for adding that parameter to the sideset and nodeset checks.

"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 @@ -80,6 +84,7 @@ MeshDiagnosticsGenerator::MeshDiagnosticsGenerator(const InputParameters & param
_input(getMesh("input")),
_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")),
_check_element_volumes(getParam<MooseEnum>("examine_element_volumes")),
_min_volume(getParam<Real>("minimum_element_volumes")),
_max_volume(getParam<Real>("maximum_element_volumes")),
Expand All @@ -103,10 +108,10 @@ MeshDiagnosticsGenerator::MeshDiagnosticsGenerator(const InputParameters & param
paramError("examine_non_conformality",
"You must set this parameter to true to trigger mesh conformality check");
if (_check_sidesets_orientation == "NO_CHECK" && _check_watertight_sidesets == "NO_CHECK" &&
_check_element_volumes == "NO_CHECK" && _check_element_types == "NO_CHECK" &&
_check_element_overlap == "NO_CHECK" && _check_non_planar_sides == "NO_CHECK" &&
_check_non_conformal_mesh == "NO_CHECK" && _check_adaptivity_non_conformality == "NO_CHECK" &&
_check_local_jacobian == "NO_CHECK")
_check_watertight_nodesets == "NO_CHECK" && _check_element_volumes == "NO_CHECK" &&
_check_element_types == "NO_CHECK" && _check_element_overlap == "NO_CHECK" &&
_check_non_planar_sides == "NO_CHECK" && _check_non_conformal_mesh == "NO_CHECK" &&
_check_adaptivity_non_conformality == "NO_CHECK" && _check_local_jacobian == "NO_CHECK")
mooseError("You need to turn on at least one diagnostic. Did you misspell a parameter?");
}

Expand All @@ -129,6 +134,9 @@ MeshDiagnosticsGenerator::generate()
if (_check_watertight_sidesets != "NO_CHECK")
checkWaterTightSidesets(mesh);

if (_check_watertight_nodesets != "NO_CHECK")
checkWatertightNodesets(mesh);

if (_check_element_volumes != "NO_CHECK")
checkElementVolumes(mesh);

Expand Down Expand Up @@ -335,6 +343,66 @@ MeshDiagnosticsGenerator::checkWaterTightSidesets(const std::unique_ptr<MeshBase
diagnosticsLog(message, _check_watertight_sidesets, num_faces_without_sideset);
}

void
MeshDiagnosticsGenerator::checkWatertightNodesets(const std::unique_ptr<MeshBase> & mesh) const
{
/*
Diagnostic Overview:
1) Mesh precheck
2) Loop through all elements
3) Loop through all sides of that element
4) If side is external loop through its nodes
5) If node is not associated with any nodeset add to list
6) Print out node id
*/
if (mesh->mesh_dimension() < 2)
mooseError("The nodeset check only works for 2D and 3D meshes");
auto & boundary_info = mesh->get_boundary_info();
boundary_info.build_side_list();
const auto nodeset_map = boundary_info.get_nodeset_map();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where is the _boundary_node_id being initialized in libmesh? is the previous call doing it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that gets initialized when the mesh is first read in or made?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let s be sure we dont need to "prepare_for_use" for this diagnostics to work

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I looked into it. The node ids get added to _boundary_node_id after execute_mesh_generators is called in ActionWarehouse.C. From what I can tell the mesh is always initialized before this test could ever be called.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after execute_mesh_generators

wouldnt that be too late then?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, it's the execute_mesh_generators task that initializes _boundary_node_id. After initialization mesh diagnostics runs. So that task runs both I believe, but it should initialize first.

unsigned int num_nodes_without_nodeset = 0;
std::set<dof_id_type> checked_nodes_id;

for (const auto elem : mesh->active_element_ptr_range())
{
for (const auto i : elem->side_index_range())
{
// Check if side is external
if (elem->neighbor_ptr(i) == nullptr)
{
// Side is external, now check nodes
auto side = elem->side_ptr(i);
const auto & node_list = side->get_nodes();
for (unsigned int j = 0; j < side->n_nodes(); j++)
{
const auto node = node_list[j];
if (checked_nodes_id.count(node->id()))
continue;
// if node is not part of nodeset map add it to list of bad nodes
if (nodeset_map.count(node) == 0)
{
// This node does not have a nodeset!!!
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 a nodeset";
_console << message << std::endl;
}
}
}
}
}
}
std::string message;
message = "Number of external nodes that have not been assigned to a nodeset: " +
std::to_string(num_nodes_without_nodeset);
diagnosticsLog(message, _check_watertight_nodesets, num_nodes_without_nodeset);
}

void
MeshDiagnosticsGenerator::checkElementVolumes(const std::unique_ptr<MeshBase> & mesh) const
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[Mesh]
[gmg]
type = GeneratedMeshGenerator
dim = 2
nx = 2
ny = 2
[]
[deletion]
type = BoundaryDeletionGenerator
input = gmg
boundary_names = 'right'
[]
[diag]
type = MeshDiagnosticsGenerator
input = deletion
check_for_watertight_nodesets = INFO
[]
[]

[Outputs]
exodus = true
[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[Mesh]
[gmg]
type = GeneratedMeshGenerator
dim = 3
nx = 2
ny = 2
nz = 2
[]
[deletion]
type = BoundaryDeletionGenerator
input = gmg
boundary_names = 'right'
[]
[diag]
type = MeshDiagnosticsGenerator
input = deletion
check_for_watertight_nodesets = INFO
[]
[]

[Outputs]
exodus = true
[]
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
examine_nonplanar_sides = INFO
examine_sidesets_orientation = WARNING
check_for_watertight_sidesets = WARNING
check_for_watertight_nodesets = WARNING
search_for_adaptivity_nonconformality = WARNING
check_local_jacobian = WARNING
[]
Expand Down
17 changes: 17 additions & 0 deletions test/tests/meshgenerators/mesh_diagnostics_generator/tests
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,22 @@
expect_out = 'Number of external element faces that have not been assigned to a sideset: 4'
detail = '3D meshed cube missing a sideset on one side'
[]
[2D_watertight_nodesets]
type = 'RunApp'
input = '2D_missing_nodeset.i'
cli_args = '--mesh-only'
mesh_mode = 'replicated'
expect_out = 'Number of external nodes that have not been assigned to a nodeset: 1'
detail = '2D meshed square with one external node that does not belong to a nodeset'
[]
[3D_watertight_nodesets]
type = 'RunApp'
input = '3D_missing_nodeset.i'
cli_args = '--mesh-only'
mesh_mode = 'replicated'
expect_out = 'Number of external nodes that have not been assigned to a nodeset: 1'
detail = '3D meshed cube with one external node that does not belong to a nodeset'
[]
[overlap]
type = 'RunApp'
input = 'node_based_test.i'
Expand Down Expand Up @@ -253,6 +269,7 @@
Mesh/diag/examine_nonplanar_sides=NO_CHECK
Mesh/diag/examine_sidesets_orientation=NO_CHECK
Mesh/diag/check_for_watertight_sidesets=NO_CHECK
Mesh/diag/check_for_watertight_nodesets=NO_CHECK
Mesh/diag/search_for_adaptivity_nonconformality=NO_CHECK
Mesh/diag/check_local_jacobian=NO_CHECK --mesh-only"
detail = 'a diagnostics object is created but no diagnostics are requested.'
Expand Down