From b26277bc447f3d983215e42f7f65411df02b0e8c Mon Sep 17 00:00:00 2001 From: Daniel Yankura Date: Wed, 13 Nov 2024 11:01:46 -0700 Subject: [PATCH 1/5] Added diagnostic check for watertight nodesets -Loops through all elements and sides -If side is external it checks if all its nodes are assigned to a nodeset closes #28822 --- .../meshgenerators/MeshDiagnosticsGenerator.h | 4 + .../meshgenerators/MeshDiagnosticsGenerator.C | 73 ++++++++++++++++++- .../2D_missing_nodeset.i | 22 ++++++ .../3D_missing_nodeset.i | 23 ++++++ .../mesh_diagnostics_generator/all_at_once.i | 1 + .../mesh_diagnostics_generator/tests | 17 +++++ 6 files changed, 136 insertions(+), 4 deletions(-) create mode 100644 test/tests/meshgenerators/mesh_diagnostics_generator/2D_missing_nodeset.i create mode 100644 test/tests/meshgenerators/mesh_diagnostics_generator/3D_missing_nodeset.i diff --git a/framework/include/meshgenerators/MeshDiagnosticsGenerator.h b/framework/include/meshgenerators/MeshDiagnosticsGenerator.h index bcb44374ad34..7a7ef2cab0ee 100644 --- a/framework/include/meshgenerators/MeshDiagnosticsGenerator.h +++ b/framework/include/meshgenerators/MeshDiagnosticsGenerator.h @@ -33,6 +33,8 @@ class MeshDiagnosticsGenerator : public MeshGenerator void checkSidesetsOrientation(const std::unique_ptr & mesh) const; //// Routine to check is mesh is fully covered in sidesets void checkWaterTightSidesets(const std::unique_ptr & mesh) const; + //// Routine to check is mesh is fully covered in nodesets + void checkWatertightNodesets(const std::unique_ptr & mesh) const; /// Routine to check the element volumes void checkElementVolumes(const std::unique_ptr & mesh) const; /// Routine to check the element types in each subdomain @@ -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 diff --git a/framework/src/meshgenerators/MeshDiagnosticsGenerator.C b/framework/src/meshgenerators/MeshDiagnosticsGenerator.C index 0d6d453ed833..7db7a32b692c 100644 --- a/framework/src/meshgenerators/MeshDiagnosticsGenerator.C +++ b/framework/src/meshgenerators/MeshDiagnosticsGenerator.C @@ -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( + "check_for_watertight_nodesets", + chk_option, + "whether to check for external nodes that are not assigned to any nodeset"); params.addParam( "examine_element_volumes", chk_option, "whether to examine volume of the elements"); params.addParam("minimum_element_volumes", 1e-16, "minimum size for element volume"); @@ -80,6 +84,7 @@ MeshDiagnosticsGenerator::MeshDiagnosticsGenerator(const InputParameters & param _input(getMesh("input")), _check_sidesets_orientation(getParam("examine_sidesets_orientation")), _check_watertight_sidesets(getParam("check_for_watertight_sidesets")), + _check_watertight_nodesets(getParam("check_for_watertight_nodesets")), _check_element_volumes(getParam("examine_element_volumes")), _min_volume(getParam("minimum_element_volumes")), _max_volume(getParam("maximum_element_volumes")), @@ -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?"); } @@ -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); @@ -335,6 +343,63 @@ MeshDiagnosticsGenerator::checkWaterTightSidesets(const std::unique_ptr & 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(); + unsigned int num_nodes_without_nodeset = 0; + std::set checked_nodes; + + for (const auto elem : mesh->active_element_ptr_range()) + { + for (auto i : elem->side_index_range()) + { + // Check if side is external + if (elem->neighbor_ptr(i) == nullptr) + { + // Side is externa, 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.count(*node)) + 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.insert(*node); + std::string message; + if (num_nodes_without_nodeset < _num_outputs) + { + message = "Node " + std::to_string(node->id()) + " is external, 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 & mesh) const { diff --git a/test/tests/meshgenerators/mesh_diagnostics_generator/2D_missing_nodeset.i b/test/tests/meshgenerators/mesh_diagnostics_generator/2D_missing_nodeset.i new file mode 100644 index 000000000000..137db302f9f2 --- /dev/null +++ b/test/tests/meshgenerators/mesh_diagnostics_generator/2D_missing_nodeset.i @@ -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 +[] diff --git a/test/tests/meshgenerators/mesh_diagnostics_generator/3D_missing_nodeset.i b/test/tests/meshgenerators/mesh_diagnostics_generator/3D_missing_nodeset.i new file mode 100644 index 000000000000..648819074502 --- /dev/null +++ b/test/tests/meshgenerators/mesh_diagnostics_generator/3D_missing_nodeset.i @@ -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 +[] diff --git a/test/tests/meshgenerators/mesh_diagnostics_generator/all_at_once.i b/test/tests/meshgenerators/mesh_diagnostics_generator/all_at_once.i index 418832ac8a21..fe649af20b3a 100644 --- a/test/tests/meshgenerators/mesh_diagnostics_generator/all_at_once.i +++ b/test/tests/meshgenerators/mesh_diagnostics_generator/all_at_once.i @@ -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 [] diff --git a/test/tests/meshgenerators/mesh_diagnostics_generator/tests b/test/tests/meshgenerators/mesh_diagnostics_generator/tests index ea6443301c29..3831ce62102e 100644 --- a/test/tests/meshgenerators/mesh_diagnostics_generator/tests +++ b/test/tests/meshgenerators/mesh_diagnostics_generator/tests @@ -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 missing a nodeset on one side' + [] + [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 missing a nodeset on one side' + [] [overlap] type = 'RunApp' input = 'node_based_test.i' @@ -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.' From ab61eb35f645259570d6bca4efb9510ea11541ed Mon Sep 17 00:00:00 2001 From: Daniel Yankura Date: Wed, 13 Nov 2024 11:31:54 -0700 Subject: [PATCH 2/5] Made formatting changes for Civet --- .../meshgenerators/MeshDiagnosticsGenerator.C | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/framework/src/meshgenerators/MeshDiagnosticsGenerator.C b/framework/src/meshgenerators/MeshDiagnosticsGenerator.C index 7db7a32b692c..275c4bc0d6ec 100644 --- a/framework/src/meshgenerators/MeshDiagnosticsGenerator.C +++ b/framework/src/meshgenerators/MeshDiagnosticsGenerator.C @@ -108,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_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") + _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?"); } @@ -343,7 +343,7 @@ MeshDiagnosticsGenerator::checkWaterTightSidesets(const std::unique_ptr & mesh) const { /* @@ -354,7 +354,7 @@ MeshDiagnosticsGenerator::checkWatertightNodesets(const std::unique_ptrmesh_dimension() < 2) mooseError("The nodeset check only works for 2D and 3D meshes"); auto & boundary_info = mesh->get_boundary_info(); @@ -378,8 +378,8 @@ MeshDiagnosticsGenerator::checkWatertightNodesets(const std::unique_ptrid()) + " is external, but has not been assigned to a nodeset"; + message = "Node " + std::to_string(node->id()) + + " is external, 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); + 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); } From d7a8ed051328cf35cfc1ae075d3bfe0e7c04b511 Mon Sep 17 00:00:00 2001 From: Daniel Yankura Date: Wed, 13 Nov 2024 15:50:07 -0700 Subject: [PATCH 3/5] Made changes for PR -Fixed spelling mistake -made iterator const -improved error messages --- framework/src/meshgenerators/MeshDiagnosticsGenerator.C | 6 +++--- test/tests/meshgenerators/mesh_diagnostics_generator/tests | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/framework/src/meshgenerators/MeshDiagnosticsGenerator.C b/framework/src/meshgenerators/MeshDiagnosticsGenerator.C index 275c4bc0d6ec..079e11dc8d07 100644 --- a/framework/src/meshgenerators/MeshDiagnosticsGenerator.C +++ b/framework/src/meshgenerators/MeshDiagnosticsGenerator.C @@ -365,12 +365,12 @@ MeshDiagnosticsGenerator::checkWatertightNodesets(const std::unique_ptractive_element_ptr_range()) { - for (auto i : elem->side_index_range()) + for (const auto i : elem->side_index_range()) { // Check if side is external if (elem->neighbor_ptr(i) == nullptr) { - // Side is externa, now check nodes + // 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++) @@ -388,7 +388,7 @@ MeshDiagnosticsGenerator::checkWatertightNodesets(const std::unique_ptrid()) + - " is external, but has not been assigned to a nodeset"; + " is on an external boundary of the mesh, but has not been assigned to a nodeset"; _console << message << std::endl; } } diff --git a/test/tests/meshgenerators/mesh_diagnostics_generator/tests b/test/tests/meshgenerators/mesh_diagnostics_generator/tests index 3831ce62102e..655af16e3e4e 100644 --- a/test/tests/meshgenerators/mesh_diagnostics_generator/tests +++ b/test/tests/meshgenerators/mesh_diagnostics_generator/tests @@ -51,7 +51,7 @@ 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 missing a nodeset on one side' + detail = '2D meshed square with one external node that does not belong to a nodeset' [] [3D_watertight_nodesets] type = 'RunApp' @@ -59,7 +59,7 @@ 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 missing a nodeset on one side' + detail = '3D meshed cube with one external node that does not belong to a nodeset' [] [overlap] type = 'RunApp' From 20068788aca5730be9d48a9a4b78d3a66e1eb83b Mon Sep 17 00:00:00 2001 From: Daniel Yankura Date: Wed, 13 Nov 2024 15:59:12 -0700 Subject: [PATCH 4/5] Style changes for Civte again --- framework/src/meshgenerators/MeshDiagnosticsGenerator.C | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/framework/src/meshgenerators/MeshDiagnosticsGenerator.C b/framework/src/meshgenerators/MeshDiagnosticsGenerator.C index 079e11dc8d07..ebb917ed6055 100644 --- a/framework/src/meshgenerators/MeshDiagnosticsGenerator.C +++ b/framework/src/meshgenerators/MeshDiagnosticsGenerator.C @@ -387,8 +387,9 @@ MeshDiagnosticsGenerator::checkWatertightNodesets(const std::unique_ptrid()) + - " is on an external boundary of the mesh, but has not been assigned to a nodeset"; + 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; } } From 600447490436b6e48c46551d92b0afcd526e36fd Mon Sep 17 00:00:00 2001 From: Daniel Yankura Date: Thu, 14 Nov 2024 15:01:12 -0700 Subject: [PATCH 5/5] checked_nodes array now takes node id instead of node to avoid libmesh deprecation error --- framework/src/meshgenerators/MeshDiagnosticsGenerator.C | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/src/meshgenerators/MeshDiagnosticsGenerator.C b/framework/src/meshgenerators/MeshDiagnosticsGenerator.C index ebb917ed6055..faa98b5ee44e 100644 --- a/framework/src/meshgenerators/MeshDiagnosticsGenerator.C +++ b/framework/src/meshgenerators/MeshDiagnosticsGenerator.C @@ -361,7 +361,7 @@ MeshDiagnosticsGenerator::checkWatertightNodesets(const std::unique_ptr checked_nodes; + std::set checked_nodes_id; for (const auto elem : mesh->active_element_ptr_range()) { @@ -376,14 +376,14 @@ MeshDiagnosticsGenerator::checkWatertightNodesets(const std::unique_ptrn_nodes(); j++) { const auto node = node_list[j]; - if (checked_nodes.count(*node)) + 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.insert(*node); + checked_nodes_id.insert(node->id()); std::string message; if (num_nodes_without_nodeset < _num_outputs) {