diff --git a/framework/include/meshgenerators/MeshDiagnosticsGenerator.h b/framework/include/meshgenerators/MeshDiagnosticsGenerator.h index 7a7ef2cab0ee..44b7d7e770da 100644 --- a/framework/include/meshgenerators/MeshDiagnosticsGenerator.h +++ b/framework/include/meshgenerators/MeshDiagnosticsGenerator.h @@ -32,9 +32,11 @@ class MeshDiagnosticsGenerator : public MeshGenerator /// Routine to check sideset orientation near subdomains 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; + 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; + /// Helper function that finds the intersection of the _watertight_boundaries vector and the provided one + std::vector findBoundaryOverlap(std::vector, std::vector boundary_ids) 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,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 _watertight_boundaries; /// 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 01fdbfbf9f36..6291a05376d1 100644 --- a/framework/src/meshgenerators/MeshDiagnosticsGenerator.C +++ b/framework/src/meshgenerators/MeshDiagnosticsGenerator.C @@ -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>( + "boundaries_to_check", + {}, + "IDs of boundaries to be checked for watertight checks"); 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"); @@ -85,6 +89,7 @@ MeshDiagnosticsGenerator::MeshDiagnosticsGenerator(const InputParameters & param _check_sidesets_orientation(getParam("examine_sidesets_orientation")), _check_watertight_sidesets(getParam("check_for_watertight_sidesets")), _check_watertight_nodesets(getParam("check_for_watertight_nodesets")), + _watertight_boundaries(getParam>("boundaries_to_check")), _check_element_volumes(getParam("examine_element_volumes")), _min_volume(getParam("minimum_element_volumes")), _max_volume(getParam("maximum_element_volumes")), @@ -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); @@ -284,7 +289,7 @@ MeshDiagnosticsGenerator::checkSidesetsOrientation(const std::unique_ptr & mesh) const +MeshDiagnosticsGenerator::checkWatertightSidesets(const std::unique_ptr & mesh) const { /* Algorithm Overview: @@ -300,21 +305,27 @@ MeshDiagnosticsGenerator::checkWaterTightSidesets(const std::unique_ptr boundary_copy = _watertight_boundaries; + std::sort(boundary_copy.begin(), boundary_copy.end()); + for (const auto elem : mesh->active_element_ptr_range()) { - std::vector> 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_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 intersections = findBoundaryOverlap(boundary_copy, boundary_ids); + if (boundary_ids.empty()) { // This side does not have a sideset!!! std::string message; @@ -330,6 +341,22 @@ MeshDiagnosticsGenerator::checkWaterTightSidesets(const std::unique_ptrmesh_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++; + } + } } } } @@ -361,6 +388,10 @@ MeshDiagnosticsGenerator::checkWatertightNodesets(const std::unique_ptr checked_nodes_id; + // Make copy of _watertight_boundaries and sort it for later use + std::vector 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()) @@ -376,6 +407,10 @@ MeshDiagnosticsGenerator::checkWatertightNodesets(const std::unique_ptrid())) continue; + // get vector of node's boundaries (in most cases it will only have one) + std::vector boundary_ids; + boundary_info.boundary_ids(node, boundary_ids); + std::vector 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) { @@ -391,6 +426,20 @@ MeshDiagnosticsGenerator::checkWatertightNodesets(const std::unique_ptrid()); + 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; + } + } } } } @@ -401,6 +450,17 @@ MeshDiagnosticsGenerator::checkWatertightNodesets(const std::unique_ptr +MeshDiagnosticsGenerator::findBoundaryOverlap(std::vector boundary_copy, std::vector 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_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 & mesh) const {