LCOV - code coverage report
Current view: top level - src/meshgenerators - ManifoldSubdomainGenerator.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 52 56 92.9 %
Date: 2026-05-29 20:35:17 Functions: 3 3 100.0 %
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 "ManifoldSubdomainGenerator.h"
      11             : 
      12             : #include "CastUniquePointer.h"
      13             : #include "MooseMeshUtils.h"
      14             : #include "TriangleManifold.h"
      15             : 
      16             : #include "libmesh/mesh_serializer.h"
      17             : #include "libmesh/elem.h"
      18             : 
      19             : #include <set>
      20             : 
      21             : registerMooseObject("MooseApp", ManifoldSubdomainGenerator);
      22             : 
      23             : InputParameters
      24        3245 : ManifoldSubdomainGenerator::validParams()
      25             : {
      26             :   // This interface intentionally mirrors other point-sampling subdomain tagging generators.
      27        9735 :   MooseEnum location("INSIDE OUTSIDE", "INSIDE");
      28             : 
      29        3245 :   InputParameters params = MeshGenerator::validParams();
      30       12980 :   params.addRequiredParam<MeshGeneratorName>("input", "The mesh we want to modify.");
      31       12980 :   params.addRequiredParam<MeshGeneratorName>("manifold", "The mesh defining a closed manifold.");
      32       12980 :   params.addRequiredParam<subdomain_id_type>("block_id", "Subdomain id to assign.");
      33       12980 :   params.addParam<SubdomainName>("block_name", "Optional subdomain name to assign.");
      34       12980 :   params.addParam<MooseEnum>(
      35             :       "location", location, "Control whether the manifold interior or exterior is tagged.");
      36       12980 :   params.addParam<std::vector<SubdomainName>>("restricted_subdomains",
      37             :                                               "Only reset subdomain ID for given subdomains.");
      38       16225 :   params.addRangeCheckedParam<Real>(
      39             :       "surface_tolerance",
      40        6490 :       1e-10,
      41             :       "surface_tolerance>0",
      42             :       "Absolute geometric tolerance used for manifold validation and near-surface classification. "
      43             :       "Choose this relative to the STL length scale and expected coordinate noise.");
      44        3245 :   params.addClassDescription(
      45             :       "Changes the subdomain ID of elements whose vertex-average point lies inside or outside a "
      46             :       "closed manifold defined by surface mesh.");
      47        6490 :   return params;
      48        3245 : }
      49             : 
      50          92 : ManifoldSubdomainGenerator::ManifoldSubdomainGenerator(const InputParameters & parameters)
      51             :   : MeshGenerator(parameters),
      52          92 :     _input(getMesh("input")),
      53         184 :     _manifold(getMesh("manifold")),
      54          92 :     _location(parameters.get<MooseEnum>("location")),
      55          92 :     _block_id(parameters.get<subdomain_id_type>("block_id")),
      56         184 :     _has_restriction(isParamValid("restricted_subdomains")),
      57         276 :     _surface_tolerance(getParam<Real>("surface_tolerance"))
      58             : {
      59             : #if LIBMESH_DOF_ID_BYTES != 8
      60             :   mooseDocumentedError(
      61             :       "moose", 32898, "ManifoldSubdomainGenerator currently only works with 64-bit DoF IDs");
      62             : #endif
      63          92 : }
      64             : 
      65             : std::unique_ptr<MeshBase>
      66          87 : ManifoldSubdomainGenerator::generate()
      67             : {
      68          87 :   std::unique_ptr<MeshBase> mesh = std::move(_input);
      69          87 :   std::unique_ptr<MeshBase> manifold_mesh = std::move(_manifold);
      70             : 
      71             :   // The manifold describes a volume boundary, so the target mesh must also be volumetric.
      72          87 :   if (mesh->mesh_dimension() != 3)
      73           6 :     paramError("input", "Only 3D meshes are supported.");
      74             : 
      75             :   // Make sure the mesh is prepared
      76          84 :   if (!manifold_mesh->is_prepared())
      77          61 :     manifold_mesh->prepare_for_use();
      78             :   // Must be serialized, for now
      79          84 :   libMesh::MeshSerializer serial_manifold(*manifold_mesh);
      80             :   // The manifold must also be 2D
      81         165 :   if (*(manifold_mesh->elem_dimensions().begin()) != 2 ||
      82         165 :       *(manifold_mesh->elem_dimensions().rbegin()) != 2)
      83           6 :     paramError("manifold", "Only 2D meshes are supported.");
      84             : 
      85          81 :   std::set<SubdomainID> restricted_ids;
      86          81 :   if (_has_restriction)
      87             :   {
      88             :     // Resolve user block names to ids once before the element loop.
      89          22 :     const auto & names = getParam<std::vector<SubdomainName>>("restricted_subdomains");
      90          22 :     for (const auto & name : names)
      91             :     {
      92          11 :       if (!MooseMeshUtils::hasSubdomainName(*mesh, name))
      93           0 :         paramError("restricted_subdomains", "The block '", name, "' was not found in the mesh");
      94             : 
      95          11 :       restricted_ids.insert(MooseMeshUtils::getSubdomainID(name, *mesh));
      96             :     }
      97             :   }
      98             : 
      99          81 :   if (MooseMeshUtils::hasSubdomainID(*mesh, _block_id) && mesh->processor_id() == 0)
     100           0 :     mooseInfo("The requested block_id ",
     101           0 :               _block_id,
     102             :               " already exists on the input mesh. Elements outside the closed manifold that are "
     103             :               "already assigned to this block will remain unchanged.");
     104             : 
     105             :   // Build the classifier up front so the per-element loop only performs point-in-manifold queries.
     106          81 :   TriangleManifold manifold(*manifold_mesh, _surface_tolerance);
     107             : 
     108             :   // On distributed meshes, only locally owned active elements are modified on each rank.
     109             :   // TODO: This could technically be over local elements, but changing subdomains asynchronously
     110             :   // causes issues in downstream mesh generators like BlockDeletionGenerator.
     111       21835 :   for (const auto & elem : mesh->active_element_ptr_range())
     112             :   {
     113       21760 :     if (_has_restriction && restricted_ids.count(elem->subdomain_id()) == 0)
     114         336 :       continue;
     115             : 
     116             :     // This generator intentionally samples at the vertex average instead of the true geometric
     117             :     // centroid, matching the inexpensive behavior of other subdomain tagging generators.
     118       21424 :     const bool contains = manifold.contains(elem->vertex_average());
     119       21424 :     if ((contains && _location == "INSIDE") || (!contains && _location == "OUTSIDE"))
     120       10676 :       elem->subdomain_id() = _block_id;
     121          75 :   }
     122             : 
     123             :   // Preserve the optional user-facing subdomain name for the assigned id.
     124         225 :   if (isParamValid("block_name"))
     125           0 :     mesh->subdomain_name(_block_id) = getParam<SubdomainName>("block_name");
     126             : 
     127             :   // Mark derived mesh data as stale because element subdomain assignments have changed.
     128          75 :   mesh->unset_has_cached_elem_data();
     129         150 :   return dynamic_pointer_cast<MeshBase>(mesh);
     130          75 : }

Generated by: LCOV version 1.14