LCOV - code coverage report
Current view: top level - src/meshgenerators - SideSetsGeneratorBase.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 148 151 98.0 %
Date: 2025-07-17 01:28:37 Functions: 11 12 91.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #include "SideSetsGeneratorBase.h"
      11             : #include "Parser.h"
      12             : #include "InputParameters.h"
      13             : #include "MooseMesh.h"
      14             : #include "MooseMeshUtils.h"
      15             : 
      16             : #include "libmesh/mesh_generation.h"
      17             : #include "libmesh/mesh.h"
      18             : #include "libmesh/string_to_enum.h"
      19             : #include "libmesh/quadrature_gauss.h"
      20             : #include "libmesh/point_locator_base.h"
      21             : #include "libmesh/elem.h"
      22             : #include "libmesh/remote_elem.h"
      23             : 
      24             : InputParameters
      25      110269 : SideSetsGeneratorBase::validParams()
      26             : {
      27      110269 :   InputParameters params = MeshGenerator::validParams();
      28      110269 :   params.addRequiredParam<MeshGeneratorName>("input", "The mesh we want to modify");
      29      110269 :   params.addRequiredParam<std::vector<BoundaryName>>(
      30             :       "new_boundary", "The list of boundary names to create on the supplied subdomain");
      31      330807 :   params.addParam<bool>("fixed_normal",
      32      220538 :                         false,
      33             :                         "This Boolean determines whether we fix our normal "
      34             :                         "or allow it to vary to \"paint\" around curves");
      35             : 
      36      330807 :   params.addParam<bool>("replace",
      37      220538 :                         false,
      38             :                         "If true, replace the old sidesets. If false, the current sidesets (if "
      39             :                         "any) will be preserved.");
      40             : 
      41      110269 :   params.addParam<std::vector<BoundaryName>>(
      42             :       "included_boundaries",
      43             :       "A set of boundary names or ids whose sides will be included in the new sidesets.  A side "
      44             :       "is only added if it also belongs to one of these boundaries.");
      45      110269 :   params.addParam<std::vector<BoundaryName>>(
      46             :       "excluded_boundaries",
      47             :       "A set of boundary names or ids whose sides will be excluded from the new sidesets.  A side "
      48             :       "is only added if does not belong to any of these boundaries.");
      49      110269 :   params.addParam<std::vector<SubdomainName>>(
      50             :       "included_subdomains",
      51             :       "A set of subdomain names or ids whose sides will be included in the new sidesets. A side "
      52             :       "is only added if the subdomain id of the corresponding element is in this set.");
      53      110269 :   params.addParam<std::vector<SubdomainName>>("included_neighbors",
      54             :                                               "A set of neighboring subdomain names or ids. A face "
      55             :                                               "is only added if the subdomain id of the "
      56             :                                               "neighbor is in this set");
      57      330807 :   params.addParam<bool>(
      58             :       "include_only_external_sides",
      59      220538 :       false,
      60             :       "Whether to only include external sides when considering sides to add to the sideset");
      61             : 
      62      330807 :   params.addParam<Point>("normal",
      63      220538 :                          Point(),
      64             :                          "If supplied, only faces with normal equal to this, up to "
      65             :                          "normal_tol, will be added to the sidesets specified");
      66      330807 :   params.addRangeCheckedParam<Real>("normal_tol",
      67      220538 :                                     0.1,
      68             :                                     "normal_tol>=0 & normal_tol<=2",
      69             :                                     "If normal is supplied then faces are "
      70             :                                     "only added if face_normal.normal_hat >= "
      71             :                                     "1 - normal_tol, where normal_hat = "
      72             :                                     "normal/|normal|");
      73      110269 :   params.addParam<Real>("variance", "The variance allowed when comparing normals");
      74      110269 :   params.deprecateParam("variance", "normal_tol", "4/01/2025");
      75             : 
      76             :   // Sideset restriction param group
      77      110269 :   params.addParamNamesToGroup(
      78             :       "included_boundaries excluded_boundaries included_subdomains included_neighbors "
      79             :       "include_only_external_sides normal normal_tol",
      80             :       "Sideset restrictions");
      81             : 
      82      110269 :   return params;
      83           0 : }
      84             : 
      85        5185 : SideSetsGeneratorBase::SideSetsGeneratorBase(const InputParameters & parameters)
      86             :   : MeshGenerator(parameters),
      87        5185 :     _input(getMesh("input")),
      88        5185 :     _boundary_names(std::vector<BoundaryName>()),
      89        5185 :     _fixed_normal(getParam<bool>("fixed_normal")),
      90        5185 :     _replace(getParam<bool>("replace")),
      91        5185 :     _check_included_boundaries(isParamValid("included_boundaries")),
      92        5185 :     _check_excluded_boundaries(isParamValid("excluded_boundaries")),
      93        5185 :     _check_subdomains(isParamValid("included_subdomains")),
      94        5185 :     _check_neighbor_subdomains(isParamValid("included_neighbors")),
      95        5185 :     _included_boundary_ids(std::vector<boundary_id_type>()),
      96        5185 :     _excluded_boundary_ids(std::vector<boundary_id_type>()),
      97        5185 :     _included_subdomain_ids(std::vector<subdomain_id_type>()),
      98        5185 :     _included_neighbor_subdomain_ids(std::vector<subdomain_id_type>()),
      99        5185 :     _include_only_external_sides(getParam<bool>("include_only_external_sides")),
     100        5185 :     _using_normal(isParamSetByUser("normal")),
     101       10370 :     _normal(_using_normal ? Point(getParam<Point>("normal") / getParam<Point>("normal").norm())
     102        5185 :                           : getParam<Point>("normal")),
     103       10370 :     _normal_tol(getParam<Real>("normal_tol"))
     104             : {
     105        5185 :   if (isParamValid("new_boundary"))
     106        4675 :     _boundary_names = getParam<std::vector<BoundaryName>>("new_boundary");
     107        5185 : }
     108             : 
     109        4941 : SideSetsGeneratorBase::~SideSetsGeneratorBase() {}
     110             : 
     111             : void
     112        4962 : SideSetsGeneratorBase::setup(MeshBase & mesh)
     113             : {
     114             :   mooseAssert(_fe_face == nullptr, "FE Face has already been initialized");
     115             : 
     116             :   // To know the dimension of the mesh
     117        4962 :   if (!mesh.is_prepared())
     118        4344 :     mesh.prepare_for_use();
     119        4962 :   const auto dim = mesh.mesh_dimension();
     120             : 
     121             :   // Setup the FE Object so we can calculate normals
     122        9924 :   libMesh::FEType fe_type(Utility::string_to_enum<Order>("CONSTANT"),
     123        4962 :                           Utility::string_to_enum<libMesh::FEFamily>("MONOMIAL"));
     124        4962 :   _fe_face = libMesh::FEBase::build(dim, fe_type);
     125        4962 :   _qface = std::make_unique<libMesh::QGauss>(dim - 1, FIRST);
     126        4962 :   _fe_face->attach_quadrature_rule(_qface.get());
     127             :   // Must always pre-request quantities you want to compute
     128        4962 :   _fe_face->get_normals();
     129             : 
     130             :   // Handle incompatible parameters
     131        4962 :   if (_include_only_external_sides && _check_neighbor_subdomains)
     132           0 :     paramError("include_only_external_sides", "External sides dont have neighbors");
     133             : 
     134        4962 :   if (_check_included_boundaries)
     135             :   {
     136         239 :     const auto & included_boundaries = getParam<std::vector<BoundaryName>>("included_boundaries");
     137         474 :     for (const auto & boundary_name : _boundary_names)
     138         239 :       if (std::find(included_boundaries.begin(), included_boundaries.end(), boundary_name) !=
     139         478 :           included_boundaries.end())
     140           4 :         paramError(
     141             :             "new_boundary",
     142             :             "A boundary cannot be both the new boundary and be included in the list of included "
     143             :             "boundaries. If you are trying to restrict an existing boundary, you must use a "
     144             :             "different name for 'new_boundary', delete the old boundary, and then rename the "
     145             :             "new boundary to the old boundary.");
     146             : 
     147         235 :     _included_boundary_ids = MooseMeshUtils::getBoundaryIDs(mesh, included_boundaries, false);
     148             : 
     149             :     // Check that the included boundary ids/names exist in the mesh
     150         630 :     for (const auto i : index_range(_included_boundary_ids))
     151         399 :       if (_included_boundary_ids[i] == Moose::INVALID_BOUNDARY_ID)
     152           4 :         paramError("included_boundaries",
     153             :                    "The boundary '",
     154           4 :                    included_boundaries[i],
     155             :                    "' was not found within the mesh");
     156             :   }
     157             : 
     158        4954 :   if (_check_excluded_boundaries)
     159             :   {
     160          20 :     const auto & excluded_boundaries = getParam<std::vector<BoundaryName>>("excluded_boundaries");
     161          36 :     for (const auto & boundary_name : _boundary_names)
     162          20 :       if (std::find(excluded_boundaries.begin(), excluded_boundaries.end(), boundary_name) !=
     163          40 :           excluded_boundaries.end())
     164           4 :         paramError(
     165             :             "new_boundary",
     166             :             "A boundary cannot be both the new boundary and be excluded in the list of excluded "
     167             :             "boundaries.");
     168          16 :     _excluded_boundary_ids = MooseMeshUtils::getBoundaryIDs(mesh, excluded_boundaries, false);
     169             : 
     170             :     // Check that the excluded boundary ids/names exist in the mesh
     171          28 :     for (const auto i : index_range(_excluded_boundary_ids))
     172          16 :       if (_excluded_boundary_ids[i] == Moose::INVALID_BOUNDARY_ID)
     173           4 :         paramError("excluded_boundaries",
     174             :                    "The boundary '",
     175           4 :                    excluded_boundaries[i],
     176             :                    "' was not found within the mesh");
     177             : 
     178          12 :     if (_check_included_boundaries)
     179             :     {
     180             :       // Check that included and excluded boundary lists do not overlap
     181           4 :       for (const auto & boundary_id : _included_boundary_ids)
     182           4 :         if (std::find(_excluded_boundary_ids.begin(), _excluded_boundary_ids.end(), boundary_id) !=
     183           8 :             _excluded_boundary_ids.end())
     184           4 :           paramError("excluded_boundaries",
     185             :                      "'included_boundaries' and 'excluded_boundaries' lists should not overlap");
     186             :     }
     187             :   }
     188             : 
     189             :   // Get the boundary ids from the names
     190        4942 :   if (parameters().isParamValid("included_subdomains"))
     191             :   {
     192             :     // check that the subdomains exist in the mesh
     193        4479 :     const auto subdomains = getParam<std::vector<SubdomainName>>("included_subdomains");
     194        9016 :     for (const auto & name : subdomains)
     195        4549 :       if (!MooseMeshUtils::hasSubdomainName(mesh, name))
     196          12 :         paramError("included_subdomains", "The block '", name, "' was not found in the mesh");
     197             : 
     198        4467 :     _included_subdomain_ids = MooseMeshUtils::getSubdomainIDs(mesh, subdomains);
     199        4467 :   }
     200             : 
     201        4930 :   if (parameters().isParamValid("included_neighbors"))
     202             :   {
     203             :     // check that the subdomains exist in the mesh
     204        3288 :     const auto subdomains = getParam<std::vector<SubdomainName>>("included_neighbors");
     205        6578 :     for (const auto & name : subdomains)
     206        3298 :       if (!MooseMeshUtils::hasSubdomainName(mesh, name))
     207           8 :         paramError("included_neighbors", "The block '", name, "' was not found in the mesh");
     208             : 
     209        3280 :     _included_neighbor_subdomain_ids = MooseMeshUtils::getSubdomainIDs(mesh, subdomains);
     210        3280 :   }
     211             : 
     212             :   // We will want to Change the below code when we have more fine-grained control over advertising
     213             :   // what we need and how we satisfy those needs. For now we know we need to have neighbors per
     214             :   // #15823...and we do have an explicit `find_neighbors` call...but we don't have a
     215             :   // `neighbors_found` API and it seems off to do:
     216             :   //
     217             :   // if (!mesh.is_prepared())
     218             :   //   mesh.find_neighbors()
     219        4922 : }
     220             : 
     221             : void
     222         319 : SideSetsGeneratorBase::finalize()
     223             : {
     224         319 :   _qface.reset();
     225         319 :   _fe_face.reset();
     226         319 : }
     227             : 
     228             : void
     229     1978518 : SideSetsGeneratorBase::flood(const Elem * elem,
     230             :                              const Point & normal,
     231             :                              const boundary_id_type & side_id,
     232             :                              MeshBase & mesh)
     233             : {
     234     3585276 :   if (elem == nullptr || elem == remote_elem ||
     235     3585276 :       (_visited[side_id].find(elem) != _visited[side_id].end()))
     236     1303976 :     return;
     237             : 
     238             :   // Skip if element is not in specified subdomains
     239      674542 :   if (_check_subdomains && !elementSubdomainIdInList(elem, _included_subdomain_ids))
     240           0 :     return;
     241             : 
     242      674542 :   _visited[side_id].insert(elem);
     243             : 
     244             :   // Request to compute normal vectors
     245      674542 :   const std::vector<Point> & face_normals = _fe_face->get_normals();
     246             : 
     247     4064150 :   for (const auto side : make_range(elem->n_sides()))
     248             :   {
     249             : 
     250     3389608 :     _fe_face->reinit(elem, side);
     251             :     // We'll just use the normal of the first qp
     252     3389608 :     const Point face_normal = face_normals[0];
     253             : 
     254     3389608 :     if (!elemSideSatisfiesRequirements(elem, side, mesh, normal, face_normal))
     255     3079430 :       continue;
     256             : 
     257      310178 :     if (_replace)
     258        1280 :       mesh.get_boundary_info().remove_side(elem, side);
     259             : 
     260      310178 :     mesh.get_boundary_info().add_side(elem, side, side_id);
     261     1872794 :     for (const auto neighbor : make_range(elem->n_sides()))
     262             :     {
     263             :       // Flood to the neighboring elements using the current matching side normal from this
     264             :       // element.
     265             :       // This will allow us to tolerate small changes in the normals so we can "paint" around a
     266             :       // curve.
     267     1562616 :       flood(elem->neighbor_ptr(neighbor), _fixed_normal ? normal : face_normal, side_id, mesh);
     268             :     }
     269             :   }
     270             : }
     271             : 
     272             : bool
     273     2093118 : SideSetsGeneratorBase::normalsWithinTol(const Point & normal_1,
     274             :                                         const Point & normal_2,
     275             :                                         const Real & tol) const
     276             : {
     277     2093118 :   return (1.0 - normal_1 * normal_2) <= tol;
     278             : }
     279             : 
     280             : bool
     281      940856 : SideSetsGeneratorBase::elementSubdomainIdInList(
     282             :     const Elem * const elem, const std::vector<subdomain_id_type> & subdomain_id_list) const
     283             : {
     284      940856 :   subdomain_id_type curr_subdomain = elem->subdomain_id();
     285      940856 :   return std::find(subdomain_id_list.begin(), subdomain_id_list.end(), curr_subdomain) !=
     286     1881712 :          subdomain_id_list.end();
     287             : }
     288             : 
     289             : bool
     290       17792 : SideSetsGeneratorBase::elementSideInIncludedBoundaries(const Elem * const elem,
     291             :                                                        const unsigned int side,
     292             :                                                        const MeshBase & mesh) const
     293             : {
     294       38936 :   for (const auto & bid : _included_boundary_ids)
     295       22032 :     if (mesh.get_boundary_info().has_boundary_id(elem, side, bid))
     296         888 :       return true;
     297       16904 :   return false;
     298             : }
     299             : 
     300             : bool
     301        1728 : SideSetsGeneratorBase::elementSideInExcludedBoundaries(const Elem * const elem,
     302             :                                                        const unsigned int side,
     303             :                                                        const MeshBase & mesh) const
     304             : {
     305        3440 :   for (const auto bid : _excluded_boundary_ids)
     306        1728 :     if (mesh.get_boundary_info().has_boundary_id(elem, side, bid))
     307          16 :       return true;
     308        1712 :   return false;
     309             : }
     310             : 
     311             : bool
     312     4031226 : SideSetsGeneratorBase::elemSideSatisfiesRequirements(const Elem * const elem,
     313             :                                                      const unsigned int side,
     314             :                                                      const MeshBase & mesh,
     315             :                                                      const Point & desired_normal,
     316             :                                                      const Point & face_normal)
     317             : {
     318             :   // Skip if side has neighbor and we only want external sides
     319     4031226 :   if ((elem->neighbor_ptr(side) && _include_only_external_sides))
     320     3074426 :     return false;
     321             : 
     322             :   // Skip if side is not part of included boundaries
     323      956800 :   if (_check_included_boundaries && !elementSideInIncludedBoundaries(elem, side, mesh))
     324       16904 :     return false;
     325             :   // Skip if side is part of excluded boundaries
     326      939896 :   if (_check_excluded_boundaries && elementSideInExcludedBoundaries(elem, side, mesh))
     327          16 :     return false;
     328             : 
     329             :   // Skip if element does not have neighbor in specified subdomains
     330      939880 :   if (_check_neighbor_subdomains)
     331             :   {
     332      566464 :     const Elem * const neighbor = elem->neighbor_ptr(side);
     333             :     // if the neighbor does not exist, then skip this face; we only add sidesets
     334             :     // between existing elems if _check_neighbor_subdomains is true
     335      566464 :     if (!(neighbor && elementSubdomainIdInList(neighbor, _included_neighbor_subdomain_ids)))
     336      531326 :       return false;
     337             :   }
     338             : 
     339      408554 :   if (_using_normal && !normalsWithinTol(desired_normal, face_normal, _normal_tol))
     340       34959 :     return false;
     341             : 
     342      373595 :   return true;
     343             : }

Generated by: LCOV version 1.14