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 : }