LCOV - code coverage report
Current view: top level - src/mesh - MooseMesh.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 1814 2133 85.0 %
Date: 2026-05-29 20:35:17 Functions: 186 236 78.8 %
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 "MooseError.h"
      11             : #include "MooseMesh.h"
      12             : #include "Factory.h"
      13             : #include "CacheChangedListsThread.h"
      14             : #include "MooseUtils.h"
      15             : #include "MooseApp.h"
      16             : #include "RelationshipManager.h"
      17             : #include "PointListAdaptor.h"
      18             : #include "Executioner.h"
      19             : #include "NonlinearSystemBase.h"
      20             : #include "LinearSystem.h"
      21             : #include "AuxiliarySystem.h"
      22             : #include "Assembly.h"
      23             : #include "SubProblem.h"
      24             : #include "MooseVariableBase.h"
      25             : #include "MooseMeshUtils.h"
      26             : #include "MooseAppCoordTransform.h"
      27             : #include "FEProblemBase.h"
      28             : 
      29             : #include <utility>
      30             : 
      31             : // libMesh
      32             : #include "libmesh/bounding_box.h"
      33             : #include "libmesh/boundary_info.h"
      34             : #include "libmesh/mesh_tools.h"
      35             : #include "libmesh/parallel.h"
      36             : #include "libmesh/mesh_communication.h"
      37             : #include "libmesh/periodic_boundary_base.h"
      38             : #include "libmesh/fe_base.h"
      39             : #include "libmesh/fe_interface.h"
      40             : #include "libmesh/mesh_communication.h"
      41             : #include "libmesh/mesh_tools.h"
      42             : #include "libmesh/parallel.h"
      43             : #include "libmesh/parallel_elem.h"
      44             : #include "libmesh/parallel_node.h"
      45             : #include "libmesh/parallel_ghost_sync.h"
      46             : #include "libmesh/utility.h"
      47             : #include "libmesh/remote_elem.h"
      48             : #include "libmesh/linear_partitioner.h"
      49             : #include "libmesh/centroid_partitioner.h"
      50             : #include "libmesh/parmetis_partitioner.h"
      51             : #include "libmesh/hilbert_sfc_partitioner.h"
      52             : #include "libmesh/morton_sfc_partitioner.h"
      53             : #include "libmesh/edge_edge2.h"
      54             : #include "libmesh/mesh_refinement.h"
      55             : #include "libmesh/quadrature.h"
      56             : #include "libmesh/boundary_info.h"
      57             : #include "libmesh/periodic_boundaries.h"
      58             : #include "libmesh/quadrature_gauss.h"
      59             : #include "libmesh/point_locator_base.h"
      60             : #include "libmesh/default_coupling.h"
      61             : #include "libmesh/ghost_point_neighbors.h"
      62             : #include "libmesh/fe_type.h"
      63             : #include "libmesh/enum_to_string.h"
      64             : #include "libmesh/elem_side_builder.h"
      65             : 
      66             : static const int GRAIN_SIZE =
      67             :     1; // the grain_size does not have much influence on our execution speed
      68             : 
      69             : using namespace libMesh;
      70             : 
      71             : // Make newer nanoflann API compatible with older nanoflann versions
      72             : #if NANOFLANN_VERSION < 0x150
      73             : namespace nanoflann
      74             : {
      75             : typedef SearchParams SearchParameters;
      76             : 
      77             : template <typename T, typename U>
      78             : using ResultItem = std::pair<T, U>;
      79             : }
      80             : #endif
      81             : 
      82             : const std::array<bool, 3> MooseMesh::periodic_dim_default{false, false, false};
      83             : 
      84             : InputParameters
      85      199638 : MooseMesh::validParams()
      86             : {
      87      199638 :   InputParameters params = MooseObject::validParams();
      88             : 
      89      798552 :   MooseEnum parallel_type("DEFAULT REPLICATED DISTRIBUTED", "DEFAULT");
      90      798552 :   params.addParam<MooseEnum>("parallel_type",
      91             :                              parallel_type,
      92             :                              "DEFAULT: Use libMesh::ReplicatedMesh unless --distributed-mesh is "
      93             :                              "specified on the command line "
      94             :                              "REPLICATED: Always use libMesh::ReplicatedMesh "
      95             :                              "DISTRIBUTED: Always use libMesh::DistributedMesh");
      96             : 
      97      598914 :   params.addParam<bool>(
      98             :       "allow_renumbering",
      99      399276 :       true,
     100             :       "If allow_renumbering=false, node and element numbers are kept fixed until deletion");
     101             : 
     102      598914 :   params.addParam<MooseEnum>(
     103             :       "partitioner",
     104      399276 :       partitioning(),
     105             :       "Specifies a mesh partitioner to use when splitting the mesh for a parallel computation.");
     106      798552 :   MooseEnum direction("x y z radial");
     107      798552 :   params.addParam<MooseEnum>("centroid_partitioner_direction",
     108             :                              direction,
     109             :                              "Specifies the sort direction if using the centroid partitioner. "
     110             :                              "Available options: x, y, z, radial");
     111             : 
     112      798552 :   MooseEnum patch_update_strategy("never always auto iteration", "never");
     113      798552 :   params.addParam<MooseEnum>(
     114             :       "patch_update_strategy",
     115             :       patch_update_strategy,
     116             :       "How often to update the geometric search 'patch'.  The default is to "
     117             :       "never update it (which is the most efficient but could be a problem "
     118             :       "with lots of relative motion). 'always' will update the patch for all "
     119             :       "secondary nodes at the beginning of every timestep which might be time "
     120             :       "consuming. 'auto' will attempt to determine at the start of which "
     121             :       "timesteps the patch for all secondary nodes needs to be updated automatically."
     122             :       "'iteration' updates the patch at every nonlinear iteration for a "
     123             :       "subset of secondary nodes for which penetration is not detected. If there "
     124             :       "can be substantial relative motion between the primary and secondary surfaces "
     125             :       "during the nonlinear iterations within a timestep, it is advisable to use "
     126             :       "'iteration' option to ensure accurate contact detection.");
     127             : 
     128             :   // Note: This parameter is named to match 'construct_side_list_from_node_list' in SetupMeshAction
     129      598914 :   params.addParam<bool>(
     130             :       "construct_node_list_from_side_list",
     131      399276 :       true,
     132             :       "Whether or not to generate nodesets from the sidesets (currently often required).");
     133      598914 :   params.addParam<bool>(
     134             :       "displace_node_list_by_side_list",
     135      399276 :       true,
     136             :       "Whether to renumber existing nodesets with ids matching sidesets that "
     137             :       "lack names matching sidesets, when constructing nodesets from sidesets via the default "
     138             :       "'construct_node_list_from_side_list' option, rather than to merge them with the sideset.");
     139      598914 :   params.addParam<unsigned int>(
     140      399276 :       "patch_size", 40, "The number of nodes to consider in the NearestNode neighborhood.");
     141      798552 :   params.addParam<unsigned int>("ghosting_patch_size",
     142             :                                 "The number of nearest neighbors considered "
     143             :                                 "for ghosting purposes when 'iteration' "
     144             :                                 "patch update strategy is used. Default is "
     145             :                                 "5 * patch_size.");
     146      598914 :   params.addParam<unsigned int>("max_leaf_size",
     147      399276 :                                 10,
     148             :                                 "The maximum number of points in each leaf of the KDTree used in "
     149             :                                 "the nearest neighbor search. As the leaf size becomes larger,"
     150             :                                 "KDTree construction becomes faster but the nearest neighbor search"
     151             :                                 "becomes slower.");
     152             : 
     153      598914 :   params.addParam<bool>("build_all_side_lowerd_mesh",
     154      399276 :                         false,
     155             :                         "True to build the lower-dimensional mesh for all sides.");
     156             : 
     157      598914 :   params.addParam<bool>("skip_refine_when_use_split",
     158      399276 :                         true,
     159             :                         "True to skip uniform refinements when using a pre-split mesh.");
     160             : 
     161      798552 :   params.addParam<std::vector<SubdomainID>>(
     162             :       "add_subdomain_ids",
     163             :       "The listed subdomain ids will be assumed valid for the mesh. This permits setting up "
     164             :       "subdomain restrictions for subdomains initially containing no elements, which can occur, "
     165             :       "for example, in additive manufacturing simulations which dynamically add and remove "
     166             :       "elements. Names for this subdomains may be provided using add_subdomain_names. In this case "
     167             :       "this list and add_subdomain_names must contain the same number of items.");
     168      798552 :   params.addParam<std::vector<SubdomainName>>(
     169             :       "add_subdomain_names",
     170             :       "The listed subdomain names will be assumed valid for the mesh. This permits setting up "
     171             :       "subdomain restrictions for subdomains initially containing no elements, which can occur, "
     172             :       "for example, in additive manufacturing simulations which dynamically add and remove "
     173             :       "elements. IDs for this subdomains may be provided using add_subdomain_ids. Otherwise IDs "
     174             :       "are automatically assigned. In case add_subdomain_ids is set too, both lists must contain "
     175             :       "the same number of items.");
     176             : 
     177      798552 :   params.addParam<std::vector<BoundaryID>>(
     178             :       "add_sideset_ids",
     179             :       "The listed sideset ids will be assumed valid for the mesh. This permits setting up boundary "
     180             :       "restrictions for sidesets initially containing no sides. Names for this sidesets may be "
     181             :       "provided using add_sideset_names. In this case this list and add_sideset_names must contain "
     182             :       "the same number of items.");
     183      798552 :   params.addParam<std::vector<BoundaryName>>(
     184             :       "add_sideset_names",
     185             :       "The listed sideset names will be assumed valid for the mesh. This permits setting up "
     186             :       "boundary restrictions for sidesets initially containing no sides. Ids for this sidesets may "
     187             :       "be provided using add_sideset_ids. In this case this list and add_sideset_ids must contain "
     188             :       "the same number of items.");
     189             : 
     190      798552 :   params.addParam<std::vector<BoundaryID>>(
     191             :       "add_nodeset_ids",
     192             :       "The listed nodeset ids will be assumed valid for the mesh. This permits setting up boundary "
     193             :       "restrictions for node initially containing no sides. Names for this nodesets may be "
     194             :       "provided using add_nodeset_names. In this case this list and add_nodeset_names must contain "
     195             :       "the same number of items.");
     196      598914 :   params.addParam<std::vector<BoundaryName>>(
     197             :       "add_nodeset_names",
     198             :       "The listed nodeset names will be assumed valid for the mesh. This permits setting up "
     199             :       "boundary restrictions for nodesets initially containing no sides. Ids for this nodesets may "
     200             :       "be provided using add_nodesets_ids. In this case this list and add_nodesets_ids must "
     201             :       "contain the same number of items.");
     202             : 
     203      199638 :   params += MooseAppCoordTransform::validParams();
     204             : 
     205             :   // This indicates that the derived mesh type accepts a MeshGenerator, and should be set to true in
     206             :   // derived types that do so.
     207      399276 :   params.addPrivateParam<bool>("_mesh_generator_mesh", false);
     208             : 
     209             :   // Whether or not the mesh is pre split
     210      598914 :   params.addPrivateParam<bool>("_is_split", false);
     211             : 
     212      399276 :   params.registerBase("MooseMesh");
     213             : 
     214             :   // groups
     215      798552 :   params.addParamNamesToGroup("patch_update_strategy patch_size max_leaf_size", "Geometric search");
     216      798552 :   params.addParamNamesToGroup("add_subdomain_ids add_subdomain_names add_sideset_ids "
     217             :                               "add_sideset_names add_nodeset_ids add_nodeset_names",
     218             :                               "Pre-declaration of future mesh sub-entities");
     219      798552 :   params.addParamNamesToGroup("construct_node_list_from_side_list build_all_side_lowerd_mesh "
     220             :                               "displace_node_list_by_side_list",
     221             :                               "Automatic definition of mesh element sides entities");
     222      598914 :   params.addParamNamesToGroup("partitioner centroid_partitioner_direction", "Partitioning");
     223             : 
     224      399276 :   return params;
     225      199638 : }
     226             : 
     227       65591 : MooseMesh::MooseMesh(const InputParameters & parameters)
     228             :   : MooseObject(parameters),
     229             :     Restartable(this, "Mesh"),
     230             :     PerfGraphInterface(this),
     231       65591 :     _parallel_type(getParam<MooseEnum>("parallel_type").getEnum<MooseMesh::ParallelType>()),
     232       65591 :     _use_distributed_mesh(false),
     233       65591 :     _distribution_overridden(false),
     234       65591 :     _parallel_type_overridden(false),
     235       65591 :     _mesh(nullptr),
     236      131182 :     _partitioner_name(getParam<MooseEnum>("partitioner")),
     237       65591 :     _partitioner_overridden(false),
     238       65591 :     _custom_partitioner_requested(false),
     239       65591 :     _uniform_refine_level(0),
     240      131182 :     _skip_refine_when_use_split(getParam<bool>("skip_refine_when_use_split")),
     241       65591 :     _skip_deletion_repartition_after_refine(false),
     242       65591 :     _is_nemesis(false),
     243       65591 :     _node_to_elem_map_built(false),
     244       65591 :     _node_to_active_semilocal_elem_map_built(false),
     245      131182 :     _patch_size(getParam<unsigned int>("patch_size")),
     246      131182 :     _ghosting_patch_size(isParamValid("ghosting_patch_size")
     247      131182 :                              ? getParam<unsigned int>("ghosting_patch_size")
     248       65591 :                              : 5 * _patch_size),
     249      131182 :     _max_leaf_size(getParam<unsigned int>("max_leaf_size")),
     250       65591 :     _patch_update_strategy(
     251      131182 :         getParam<MooseEnum>("patch_update_strategy").getEnum<Moose::PatchUpdateType>()),
     252       65591 :     _regular_orthogonal_mesh(false),
     253      131182 :     _is_split(getParam<bool>("_is_split")),
     254       65591 :     _allow_recovery(true),
     255      131182 :     _construct_node_list_from_side_list(getParam<bool>("construct_node_list_from_side_list")),
     256      131182 :     _displace_node_list_by_side_list(getParam<bool>("displace_node_list_by_side_list")),
     257       65591 :     _need_delete(false),
     258       65591 :     _allow_remote_element_removal(true),
     259       65591 :     _need_ghost_ghosted_boundaries(true),
     260       65591 :     _is_displaced(false),
     261       65591 :     _coord_sys(
     262      131182 :         declareRestartableData<std::map<SubdomainID, Moose::CoordinateSystemType>>("coord_sys")),
     263      131182 :     _rz_coord_axis(getParam<MooseEnum>("rz_coord_axis")),
     264       65591 :     _coord_system_set(false),
     265      639522 :     _doing_p_refinement(false)
     266             : {
     267      196773 :   if (isParamValid("ghosting_patch_size") && (_patch_update_strategy != Moose::Iteration))
     268           0 :     mooseError("Ghosting patch size parameter has to be set in the mesh block "
     269             :                "only when 'iteration' patch update strategy is used.");
     270             : 
     271      196773 :   if (isParamValid("coord_block"))
     272             :   {
     273          72 :     if (isParamValid("block"))
     274           0 :       paramWarning("block",
     275             :                    "You set both 'Mesh/block' and 'Mesh/coord_block'. The value of "
     276             :                    "'Mesh/coord_block' will be used.");
     277             : 
     278          72 :     _provided_coord_blocks = getParam<std::vector<SubdomainName>>("coord_block");
     279             :   }
     280      196701 :   else if (isParamValid("block"))
     281         765 :     _provided_coord_blocks = getParam<std::vector<SubdomainName>>("block");
     282             : 
     283      196773 :   if (getParam<bool>("build_all_side_lowerd_mesh"))
     284             :     // Do not initially allow removal of remote elements
     285         223 :     allowRemoteElementRemoval(false);
     286             : 
     287       65591 :   determineUseDistributedMesh();
     288             : 
     289             : #ifdef MOOSE_KOKKOS_ENABLED
     290       49203 :   if (_app.isKokkosAvailable())
     291       49203 :     _kokkos_mesh = std::make_unique<Moose::Kokkos::Mesh>(*this);
     292             : #endif
     293       65591 : }
     294             : 
     295        2987 : MooseMesh::MooseMesh(const MooseMesh & other_mesh)
     296             :   : MooseObject(other_mesh._pars),
     297             :     Restartable(this, "Mesh"),
     298             :     PerfGraphInterface(this, "CopiedMesh"),
     299        2987 :     _built_from_other_mesh(true),
     300        2987 :     _parallel_type(other_mesh._parallel_type),
     301        2987 :     _use_distributed_mesh(other_mesh._use_distributed_mesh),
     302        2987 :     _distribution_overridden(other_mesh._distribution_overridden),
     303        2987 :     _parallel_type_overridden(other_mesh._parallel_type_overridden),
     304        2987 :     _mesh(other_mesh.getMesh().clone()),
     305        2987 :     _partitioner_name(other_mesh._partitioner_name),
     306        2987 :     _partitioner_overridden(other_mesh._partitioner_overridden),
     307        2987 :     _custom_partitioner_requested(other_mesh._custom_partitioner_requested),
     308        2987 :     _uniform_refine_level(other_mesh.uniformRefineLevel()),
     309        2987 :     _skip_refine_when_use_split(other_mesh._skip_refine_when_use_split),
     310        2987 :     _skip_deletion_repartition_after_refine(other_mesh._skip_deletion_repartition_after_refine),
     311        2987 :     _is_nemesis(other_mesh._is_nemesis),
     312        2987 :     _node_to_elem_map_built(false),
     313        2987 :     _node_to_active_semilocal_elem_map_built(false),
     314        2987 :     _patch_size(other_mesh._patch_size),
     315        2987 :     _ghosting_patch_size(other_mesh._ghosting_patch_size),
     316        2987 :     _max_leaf_size(other_mesh._max_leaf_size),
     317        2987 :     _patch_update_strategy(other_mesh._patch_update_strategy),
     318        2987 :     _regular_orthogonal_mesh(false),
     319        2987 :     _is_split(other_mesh._is_split),
     320        2987 :     _lower_d_interior_blocks(other_mesh._lower_d_interior_blocks),
     321        2987 :     _lower_d_boundary_blocks(other_mesh._lower_d_boundary_blocks),
     322        2987 :     _allow_recovery(other_mesh._allow_recovery),
     323        2987 :     _construct_node_list_from_side_list(other_mesh._construct_node_list_from_side_list),
     324        2987 :     _displace_node_list_by_side_list(other_mesh._displace_node_list_by_side_list),
     325        2987 :     _need_delete(other_mesh._need_delete),
     326        2987 :     _allow_remote_element_removal(other_mesh._allow_remote_element_removal),
     327        2987 :     _need_ghost_ghosted_boundaries(other_mesh._need_ghost_ghosted_boundaries),
     328        2987 :     _coord_sys(other_mesh._coord_sys),
     329        2987 :     _rz_coord_axis(other_mesh._rz_coord_axis),
     330        2987 :     _subdomain_id_to_rz_coord_axis(other_mesh._subdomain_id_to_rz_coord_axis),
     331        2987 :     _coord_system_set(other_mesh._coord_system_set),
     332        2987 :     _provided_coord_blocks(other_mesh._provided_coord_blocks),
     333       29110 :     _doing_p_refinement(other_mesh._doing_p_refinement)
     334             : {
     335             :   // Note: this calls BoundaryInfo::operator= without changing the
     336             :   // ownership semantics of either Mesh's BoundaryInfo object.
     337        2987 :   getMesh().get_boundary_info() = other_mesh.getMesh().get_boundary_info();
     338             : 
     339        2987 :   const std::set<SubdomainID> & subdomains = other_mesh.meshSubdomains();
     340        4064 :   for (const auto & sbd_id : subdomains)
     341        1077 :     setSubdomainName(sbd_id, other_mesh.getMesh().subdomain_name(sbd_id));
     342             : 
     343             :   // Get references to BoundaryInfo objects to make the code below cleaner...
     344        2987 :   const BoundaryInfo & other_boundary_info = other_mesh.getMesh().get_boundary_info();
     345        2987 :   BoundaryInfo & boundary_info = getMesh().get_boundary_info();
     346             : 
     347             :   // Use the other BoundaryInfo object to build the list of side boundary ids
     348        2987 :   std::vector<BoundaryID> side_boundaries;
     349        2987 :   other_boundary_info.build_side_boundary_ids(side_boundaries);
     350             : 
     351             :   // Assign those boundary ids in our BoundaryInfo object
     352       14136 :   for (const auto & side_bnd_id : side_boundaries)
     353       11149 :     boundary_info.sideset_name(side_bnd_id) = other_boundary_info.get_sideset_name(side_bnd_id);
     354             : 
     355             :   // Do the same thing for node boundary ids
     356        2987 :   std::vector<BoundaryID> node_boundaries;
     357        2987 :   other_boundary_info.build_node_boundary_ids(node_boundaries);
     358             : 
     359       12890 :   for (const auto & node_bnd_id : node_boundaries)
     360        9903 :     boundary_info.nodeset_name(node_bnd_id) = other_boundary_info.get_nodeset_name(node_bnd_id);
     361             : 
     362        2987 :   _bounds.resize(other_mesh._bounds.size());
     363        3296 :   for (std::size_t i = 0; i < _bounds.size(); ++i)
     364             :   {
     365         309 :     _bounds[i].resize(other_mesh._bounds[i].size());
     366         927 :     for (std::size_t j = 0; j < _bounds[i].size(); ++j)
     367         618 :       _bounds[i][j] = other_mesh._bounds[i][j];
     368             :   }
     369             : 
     370        2987 :   updateCoordTransform();
     371             : 
     372             : #ifdef MOOSE_KOKKOS_ENABLED
     373        2227 :   if (_app.isKokkosAvailable())
     374        2227 :     _kokkos_mesh = std::make_unique<Moose::Kokkos::Mesh>(*this);
     375             : #endif
     376        2987 : }
     377             : 
     378       64669 : MooseMesh::~MooseMesh()
     379             : {
     380       64669 :   freeBndNodes();
     381       64669 :   freeBndElems();
     382       64669 :   clearQuadratureNodes();
     383       64669 : }
     384             : 
     385             : void
     386      216754 : MooseMesh::freeBndNodes()
     387             : {
     388             :   // free memory
     389    12273481 :   for (auto & bnode : _bnd_nodes)
     390    12056727 :     delete bnode;
     391             : 
     392      796613 :   for (auto & it : _node_set_nodes)
     393      579859 :     it.second.clear();
     394             : 
     395      216754 :   _node_set_nodes.clear();
     396             : 
     397      796764 :   for (auto & it : _bnd_node_ids)
     398      580010 :     it.second.clear();
     399             : 
     400      216754 :   _bnd_node_ids.clear();
     401      216754 :   _bnd_node_range.reset();
     402      216754 : }
     403             : 
     404             : void
     405      216754 : MooseMesh::freeBndElems()
     406             : {
     407             :   // free memory
     408     9423668 :   for (auto & belem : _bnd_elems)
     409     9206914 :     delete belem;
     410             : 
     411      775944 :   for (auto & it : _bnd_elem_ids)
     412      559190 :     it.second.clear();
     413             : 
     414      216754 :   _bnd_elem_ids.clear();
     415      216754 :   _bnd_elem_range.reset();
     416      216754 : }
     417             : 
     418             : bool
     419      134505 : MooseMesh::prepare(const MeshBase * const mesh_to_clone)
     420             : {
     421      672525 :   TIME_SECTION("prepare", 2, "Preparing Mesh", true);
     422             : 
     423      134505 :   bool called_prepare_for_use = false;
     424             : 
     425             :   mooseAssert(_mesh, "The MeshBase has not been constructed");
     426             : 
     427      134505 :   if (!dynamic_cast<DistributedMesh *>(&getMesh()) || _is_nemesis)
     428             :     // For whatever reason we do not want to allow renumbering here nor ever in the future?
     429      111983 :     getMesh().allow_renumbering(false);
     430             : 
     431      134505 :   if (mesh_to_clone)
     432             :   {
     433             :     mooseAssert(mesh_to_clone->is_prepared(),
     434             :                 "The mesh we wish to clone from must already be prepared");
     435         149 :     _mesh = mesh_to_clone->clone();
     436         149 :     _moose_mesh_prepared = false;
     437             :   }
     438      134356 :   else if (!_mesh->is_prepared())
     439             :   {
     440       17451 :     _mesh->prepare_for_use();
     441       17451 :     _moose_mesh_prepared = false;
     442       17451 :     called_prepare_for_use = true;
     443             :   }
     444             : 
     445      134505 :   if (_moose_mesh_prepared)
     446       67321 :     return called_prepare_for_use;
     447             : 
     448             :   // Collect (local) subdomain IDs
     449       67184 :   _mesh_subdomains.clear();
     450    13236794 :   for (const auto & elem : getMesh().element_ptr_range())
     451    13236794 :     _mesh_subdomains.insert(elem->subdomain_id());
     452             : 
     453             :   // add explicitly requested subdomains
     454      201820 :   if (isParamValid("add_subdomain_ids") && !isParamValid("add_subdomain_names"))
     455             :   {
     456             :     // only subdomain ids are explicitly given
     457          72 :     const auto & add_subdomain_id = getParam<std::vector<SubdomainID>>("add_subdomain_ids");
     458          36 :     _mesh_subdomains.insert(add_subdomain_id.begin(), add_subdomain_id.end());
     459             :   }
     460      201640 :   else if (isParamValid("add_subdomain_ids") && isParamValid("add_subdomain_names"))
     461             :   {
     462             :     const auto add_subdomain =
     463         392 :         getParam<SubdomainID, SubdomainName>("add_subdomain_ids", "add_subdomain_names");
     464         244 :     for (const auto & [sub_id, sub_name] : add_subdomain)
     465             :     {
     466             :       // add subdomain id
     467         146 :       _mesh_subdomains.insert(sub_id);
     468             :       // set name of the subdomain just added
     469         146 :       setSubdomainName(sub_id, sub_name);
     470             :     }
     471          98 :   }
     472      201150 :   else if (isParamValid("add_subdomain_names"))
     473             :   {
     474             :     // the user has defined add_subdomain_names, but not add_subdomain_ids
     475          24 :     const auto & add_subdomain_names = getParam<std::vector<SubdomainName>>("add_subdomain_names");
     476             : 
     477             :     // to define subdomain ids, we need the largest subdomain id defined yet.
     478          12 :     subdomain_id_type offset = 0;
     479          12 :     if (!_mesh_subdomains.empty())
     480          12 :       offset = *_mesh_subdomains.rbegin();
     481             : 
     482             :     // add all subdomains (and auto-assign ids)
     483          48 :     for (const SubdomainName & sub_name : add_subdomain_names)
     484             :     {
     485             :       // to avoid two subdomains with the same ID (notably on recover)
     486          36 :       if (getSubdomainID(sub_name) != libMesh::Elem::invalid_subdomain_id)
     487           3 :         continue;
     488          33 :       const auto sub_id = ++offset;
     489             :       // add subdomain id
     490          33 :       _mesh_subdomains.insert(sub_id);
     491             :       // set name of the subdomain just added
     492          33 :       setSubdomainName(sub_id, sub_name);
     493             :     }
     494             :   }
     495             : 
     496             :   // Make sure nodesets have been generated
     497       67184 :   buildNodeListFromSideList();
     498             : 
     499             :   // Collect (local) boundary IDs
     500       67184 :   const std::set<BoundaryID> & local_bids = getMesh().get_boundary_info().get_boundary_ids();
     501       67184 :   _mesh_boundary_ids.insert(local_bids.begin(), local_bids.end());
     502             : 
     503             :   const std::set<BoundaryID> & local_node_bids =
     504       67184 :       getMesh().get_boundary_info().get_node_boundary_ids();
     505       67184 :   _mesh_nodeset_ids.insert(local_node_bids.begin(), local_node_bids.end());
     506             : 
     507             :   const std::set<BoundaryID> & local_side_bids =
     508       67184 :       getMesh().get_boundary_info().get_side_boundary_ids();
     509       67184 :   _mesh_sideset_ids.insert(local_side_bids.begin(), local_side_bids.end());
     510             : 
     511             :   // Add explicitly requested sidesets/nodesets
     512             :   // This is done *after* the side boundaries (e.g. "right", ...) have been generated.
     513      134368 :   auto add_sets = [this](const bool sidesets, auto & set_ids)
     514             :   {
     515      134368 :     const std::string type = sidesets ? "sideset" : "nodeset";
     516      134368 :     const std::string id_param = "add_" + type + "_ids";
     517      134368 :     const std::string name_param = "add_" + type + "_names";
     518             : 
     519      134368 :     if (isParamValid(id_param))
     520             :     {
     521          54 :       const auto & add_ids = getParam<std::vector<BoundaryID>>(id_param);
     522          54 :       _mesh_boundary_ids.insert(add_ids.begin(), add_ids.end());
     523          54 :       set_ids.insert(add_ids.begin(), add_ids.end());
     524          54 :       if (isParamValid(name_param))
     525             :       {
     526          42 :         const auto & add_names = getParam<std::vector<BoundaryName>>(name_param);
     527             :         mooseAssert(add_names.size() == add_ids.size(),
     528             :                     "Id and name sets must be the same size when adding.");
     529         114 :         for (const auto i : index_range(add_ids))
     530          72 :           setBoundaryName(add_ids[i], add_names[i]);
     531             :       }
     532             :     }
     533      134314 :     else if (isParamValid(name_param))
     534             :     {
     535             :       // the user has defined names, but not ids
     536          12 :       const auto & add_names = getParam<std::vector<BoundaryName>>(name_param);
     537             : 
     538          12 :       auto & mesh_ids = sidesets ? _mesh_sideset_ids : _mesh_nodeset_ids;
     539             : 
     540             :       // to define ids, we need the largest id defined yet.
     541          12 :       boundary_id_type offset = 0;
     542          12 :       if (!mesh_ids.empty())
     543          12 :         offset = *mesh_ids.rbegin();
     544          12 :       if (!_mesh_boundary_ids.empty())
     545          12 :         offset = std::max(offset, *_mesh_boundary_ids.rbegin());
     546             : 
     547             :       // add all sidesets/nodesets (and auto-assign ids)
     548          24 :       for (const auto & name : add_names)
     549             :       {
     550             :         // to avoid two sets with the same ID (notably on recover)
     551          12 :         if (getBoundaryID(name) != Moose::INVALID_BOUNDARY_ID)
     552           1 :           continue;
     553          11 :         const auto id = ++offset;
     554             :         // add sideset id
     555          11 :         _mesh_boundary_ids.insert(id);
     556          11 :         set_ids.insert(id);
     557             :         // set name of the sideset just added
     558          11 :         setBoundaryName(id, name);
     559             :       }
     560             :     }
     561      134368 :   };
     562             : 
     563       67184 :   add_sets(true, _mesh_sideset_ids);
     564       67184 :   add_sets(false, _mesh_nodeset_ids);
     565             : 
     566             :   // Communicate subdomain and boundary IDs if this is a parallel mesh
     567       67184 :   if (!getMesh().is_serial())
     568             :   {
     569        8727 :     _communicator.set_union(_mesh_subdomains);
     570        8727 :     _communicator.set_union(_mesh_boundary_ids);
     571        8727 :     _communicator.set_union(_mesh_nodeset_ids);
     572        8727 :     _communicator.set_union(_mesh_sideset_ids);
     573             :   }
     574             : 
     575       67184 :   if (!_built_from_other_mesh)
     576             :   {
     577       64371 :     if (!_coord_system_set)
     578      192891 :       setCoordSystem(_provided_coord_blocks, getParam<MultiMooseEnum>("coord_type"));
     579         222 :     else if (_pars.isParamSetByUser("coord_type"))
     580           0 :       mooseError(
     581             :           "Trying to set coordinate system type information based on the user input file, but "
     582             :           "the coordinate system type information has already been set programmatically! "
     583             :           "Either remove your coordinate system type information from the input file, or contact "
     584             :           "your application developer");
     585             :   }
     586             : 
     587             :   // Set general axisymmetric axes if provided
     588      268787 :   if (isParamValid("rz_coord_blocks") && isParamValid("rz_coord_origins") &&
     589       67235 :       isParamValid("rz_coord_directions"))
     590             :   {
     591          34 :     const auto rz_coord_blocks = getParam<std::vector<SubdomainName>>("rz_coord_blocks");
     592          34 :     const auto rz_coord_origins = getParam<std::vector<Point>>("rz_coord_origins");
     593          34 :     const auto rz_coord_directions = getParam<std::vector<RealVectorValue>>("rz_coord_directions");
     594          34 :     if (rz_coord_origins.size() == rz_coord_blocks.size() &&
     595          17 :         rz_coord_directions.size() == rz_coord_blocks.size())
     596             :     {
     597          17 :       std::vector<std::pair<Point, RealVectorValue>> rz_coord_axes;
     598          58 :       for (unsigned int i = 0; i < rz_coord_origins.size(); ++i)
     599          41 :         rz_coord_axes.push_back(std::make_pair(rz_coord_origins[i], rz_coord_directions[i]));
     600             : 
     601          17 :       setGeneralAxisymmetricCoordAxes(rz_coord_blocks, rz_coord_axes);
     602             : 
     603          51 :       if (isParamSetByUser("rz_coord_axis"))
     604           0 :         mooseError("The parameter 'rz_coord_axis' may not be provided if 'rz_coord_blocks', "
     605             :                    "'rz_coord_origins', and 'rz_coord_directions' are provided.");
     606          17 :     }
     607             :     else
     608           0 :       mooseError("The parameters 'rz_coord_blocks', 'rz_coord_origins', and "
     609             :                  "'rz_coord_directions' must all have the same size.");
     610          17 :   }
     611      470169 :   else if (isParamValid("rz_coord_blocks") || isParamValid("rz_coord_origins") ||
     612      268668 :            isParamValid("rz_coord_directions"))
     613           0 :     mooseError("If any of the parameters 'rz_coord_blocks', 'rz_coord_origins', and "
     614             :                "'rz_coord_directions' are provided, then all must be provided.");
     615             : 
     616       67184 :   detectOrthogonalDimRanges();
     617             : 
     618       67184 :   update();
     619             : 
     620             :   // Check if there is subdomain name duplication for the same subdomain ID
     621       67184 :   checkDuplicateSubdomainNames();
     622             : 
     623       67181 :   _moose_mesh_prepared = true;
     624             : 
     625       67181 :   return called_prepare_for_use;
     626      134502 : }
     627             : 
     628             : void
     629      152085 : MooseMesh::update()
     630             : {
     631      760425 :   TIME_SECTION("update", 3, "Updating Mesh", true);
     632             : 
     633             :   // Rebuild the boundary conditions
     634      152085 :   buildNodeListFromSideList();
     635             : 
     636             :   // Clear the node to elem maps
     637      152085 :   _node_to_elem_map.clear();
     638      152085 :   _node_to_active_semilocal_elem_map.clear();
     639             : 
     640      152085 :   buildNodeList();
     641      152085 :   buildBndElemList();
     642      152085 :   cacheInfo();
     643      152085 :   buildElemIDInfo();
     644             : 
     645             :   // this will make moose mesh aware of p-refinement added by mesh generators including
     646             :   // a file mesh generator loading a restart checkpoint file
     647      152085 :   _max_p_level = 0;
     648      152085 :   _max_h_level = 0;
     649    27616009 :   for (const auto & elem : getMesh().active_local_element_ptr_range())
     650             :   {
     651    27463924 :     if (elem->p_level() > _max_p_level)
     652         658 :       _max_p_level = elem->p_level();
     653    27463924 :     if (elem->level() > _max_h_level)
     654       25448 :       _max_h_level = elem->level();
     655      152085 :   }
     656      152085 :   comm().max(_max_p_level);
     657      152085 :   comm().max(_max_h_level);
     658             : 
     659             :   // the flag might have been set by calling doingPRefinement(true)
     660      152085 :   _doing_p_refinement = _doing_p_refinement || (_max_p_level > 0);
     661             : 
     662      152085 :   computeMaxPerElemAndSide();
     663             : 
     664             : #ifdef MOOSE_KOKKOS_ENABLED
     665      126309 :   if (_app.getExecutioner() && _app.feProblem().initialized() &&
     666       12561 :       _app.feProblem().hasKokkosObjects())
     667           0 :     _kokkos_mesh->update();
     668             : #endif
     669             : 
     670      152085 :   _finite_volume_info_dirty = true;
     671             : 
     672             :   // Rebuild the node to elem maps, in case the object(s) who got references to the maps
     673             :   // actually do need to use them
     674      152085 :   if (_node_to_elem_map_built)
     675             :   {
     676             :     // it won't stay false
     677        9392 :     _node_to_elem_map_built = false;
     678        9392 :     nodeToElemMap();
     679             :   }
     680      152085 :   if (_node_to_active_semilocal_elem_map_built)
     681             :   {
     682       13805 :     _node_to_active_semilocal_elem_map_built = false;
     683       13805 :     nodeToActiveSemilocalElemMap();
     684             :   }
     685      152085 : }
     686             : 
     687             : void
     688         205 : MooseMesh::buildLowerDMesh()
     689             : {
     690         205 :   auto & mesh = getMesh();
     691             : 
     692         205 :   if (!mesh.is_serial())
     693           0 :     mooseError(
     694             :         "Hybrid finite element method must use replicated mesh.\nCurrently lower-dimensional mesh "
     695             :         "does not support mesh re-partitioning and a debug assertion being hit related with "
     696             :         "neighbors of lower-dimensional element, with distributed mesh.");
     697             : 
     698             :   // Lower-D element build requires neighboring element information
     699         205 :   if (!mesh.is_prepared())
     700         194 :     mesh.find_neighbors();
     701             : 
     702             :   // maximum number of sides of all elements
     703         205 :   unsigned int max_n_sides = 0;
     704             : 
     705             :   // remove existing lower-d element first
     706         205 :   std::set<Elem *> deleteable_elems;
     707        4347 :   for (auto & elem : mesh.element_ptr_range())
     708        4142 :     if (_lower_d_interior_blocks.count(elem->subdomain_id()) ||
     709        2071 :         _lower_d_boundary_blocks.count(elem->subdomain_id()))
     710           0 :       deleteable_elems.insert(elem);
     711        2071 :     else if (elem->n_sides() > max_n_sides)
     712         410 :       max_n_sides = elem->n_sides();
     713             : 
     714         205 :   for (auto & elem : deleteable_elems)
     715           0 :     mesh.delete_elem(elem);
     716         205 :   for (const auto & id : _lower_d_interior_blocks)
     717           0 :     _mesh_subdomains.erase(id);
     718         205 :   for (const auto & id : _lower_d_boundary_blocks)
     719           0 :     _mesh_subdomains.erase(id);
     720         205 :   _lower_d_interior_blocks.clear();
     721         205 :   _lower_d_boundary_blocks.clear();
     722             : 
     723         205 :   mesh.comm().max(max_n_sides);
     724             : 
     725         205 :   deleteable_elems.clear();
     726             : 
     727             :   // get all side types
     728         205 :   std::set<int> interior_side_types;
     729         205 :   std::set<int> boundary_side_types;
     730        4347 :   for (const auto & elem : mesh.active_element_ptr_range())
     731       11173 :     for (const auto side : elem->side_index_range())
     732             :     {
     733        9102 :       Elem * neig = elem->neighbor_ptr(side);
     734        9102 :       std::unique_ptr<Elem> side_elem(elem->build_side_ptr(side));
     735        9102 :       if (neig)
     736        5956 :         interior_side_types.insert(side_elem->type());
     737             :       else
     738        3146 :         boundary_side_types.insert(side_elem->type());
     739        9307 :     }
     740         205 :   mesh.comm().set_union(interior_side_types);
     741         205 :   mesh.comm().set_union(boundary_side_types);
     742             : 
     743             :   // assign block ids for different side types
     744         205 :   std::map<ElemType, SubdomainID> interior_block_ids;
     745         205 :   std::map<ElemType, SubdomainID> boundary_block_ids;
     746             :   // we assume this id is not used by the mesh
     747         205 :   auto id = libMesh::Elem::invalid_subdomain_id - 2;
     748         424 :   for (const auto & tpid : interior_side_types)
     749             :   {
     750         219 :     const auto type = ElemType(tpid);
     751         219 :     mesh.subdomain_name(id) = "INTERNAL_SIDE_LOWERD_SUBDOMAIN_" + Utility::enum_to_string(type);
     752         219 :     interior_block_ids[type] = id;
     753         219 :     _lower_d_interior_blocks.insert(id);
     754         219 :     if (_mesh_subdomains.count(id) > 0)
     755           0 :       mooseError("Trying to add a mesh block with id ", id, " that has existed in the mesh");
     756         219 :     _mesh_subdomains.insert(id);
     757         219 :     --id;
     758             :   }
     759         424 :   for (const auto & tpid : boundary_side_types)
     760             :   {
     761         219 :     const auto type = ElemType(tpid);
     762         219 :     mesh.subdomain_name(id) = "BOUNDARY_SIDE_LOWERD_SUBDOMAIN_" + Utility::enum_to_string(type);
     763         219 :     boundary_block_ids[type] = id;
     764         219 :     _lower_d_boundary_blocks.insert(id);
     765         219 :     if (_mesh_subdomains.count(id) > 0)
     766           0 :       mooseError("Trying to add a mesh block with id ", id, " that has existed in the mesh");
     767         219 :     _mesh_subdomains.insert(id);
     768         219 :     --id;
     769             :   }
     770             : 
     771         205 :   dof_id_type max_elem_id = mesh.max_elem_id();
     772         205 :   unique_id_type max_unique_id = mesh.parallel_max_unique_id();
     773             : 
     774         205 :   std::vector<Elem *> side_elems;
     775         205 :   _higher_d_elem_side_to_lower_d_elem.clear();
     776        4347 :   for (const auto & elem : mesh.active_element_ptr_range())
     777             :   {
     778             :     // skip existing lower-d elements
     779        2071 :     if (elem->interior_parent())
     780           0 :       continue;
     781             : 
     782       11173 :     for (const auto side : elem->side_index_range())
     783             :     {
     784        9102 :       Elem * neig = elem->neighbor_ptr(side);
     785             : 
     786        9102 :       bool build_side = false;
     787        9102 :       if (!neig)
     788        3146 :         build_side = true;
     789             :       else
     790             :       {
     791             :         mooseAssert(!neig->is_remote(), "We error if the mesh is not serial");
     792        5956 :         if (!neig->active())
     793           0 :           build_side = true;
     794        5956 :         else if (neig->level() == elem->level() && elem->id() < neig->id())
     795        2978 :           build_side = true;
     796             :       }
     797             : 
     798        9102 :       if (build_side)
     799             :       {
     800        6124 :         std::unique_ptr<Elem> side_elem(elem->build_side_ptr(side));
     801             : 
     802             :         // The side will be added with the same processor id as the parent.
     803        6124 :         side_elem->processor_id() = elem->processor_id();
     804             : 
     805             :         // Add subdomain ID
     806        6124 :         if (neig)
     807        2978 :           side_elem->subdomain_id() = interior_block_ids.at(side_elem->type());
     808             :         else
     809        3146 :           side_elem->subdomain_id() = boundary_block_ids.at(side_elem->type());
     810             : 
     811             :         // set ids consistently across processors (these ids will be temporary)
     812        6124 :         side_elem->set_id(max_elem_id + elem->id() * max_n_sides + side);
     813        6124 :         side_elem->set_unique_id(max_unique_id + elem->id() * max_n_sides + side);
     814             : 
     815             :         // Also assign the side's interior parent, so it is always
     816             :         // easy to figure out the Elem we came from.
     817             :         // Note: the interior parent could be a ghost element.
     818        6124 :         side_elem->set_interior_parent(elem);
     819             : 
     820        6124 :         side_elems.push_back(side_elem.release());
     821             : 
     822             :         // add link between higher d element to lower d element
     823        6124 :         auto pair = std::make_pair(elem, side);
     824        6124 :         auto link = std::make_pair(pair, side_elems.back());
     825        6124 :         auto ilink = std::make_pair(side_elems.back(), side);
     826        6124 :         _lower_d_elem_to_higher_d_elem_side.insert(ilink);
     827        6124 :         _higher_d_elem_side_to_lower_d_elem.insert(link);
     828        6124 :       }
     829             :     }
     830         205 :   }
     831             : 
     832             :   // finally, add the lower-dimensional element to the mesh
     833             :   // Note: lower-d interior element will exist on a processor if its associated interior
     834             :   //       parent exists on a processor whether or not being a ghost. Lower-d elements will
     835             :   //       get its interior parent's processor id.
     836        6329 :   for (auto & elem : side_elems)
     837        6124 :     mesh.add_elem(elem);
     838             : 
     839             :   // we do all the stuff in prepare_for_use such as renumber_nodes_and_elements(),
     840             :   // update_parallel_id_counts(), cache_elem_dims(), etc. except partitioning here.
     841         205 :   const bool skip_partitioning_old = mesh.skip_partitioning();
     842         205 :   mesh.skip_partitioning(true);
     843             :   // Finding neighbors is ambiguous for lower-dimensional elements on interior faces
     844         205 :   mesh.allow_find_neighbors(false);
     845         205 :   mesh.prepare_for_use();
     846         205 :   mesh.skip_partitioning(skip_partitioning_old);
     847         205 : }
     848             : 
     849             : const Node &
     850           0 : MooseMesh::node(const dof_id_type i) const
     851             : {
     852           0 :   mooseDeprecated("MooseMesh::node() is deprecated, please use MooseMesh::nodeRef() instead");
     853           0 :   return nodeRef(i);
     854             : }
     855             : 
     856             : Node &
     857           0 : MooseMesh::node(const dof_id_type i)
     858             : {
     859           0 :   mooseDeprecated("MooseMesh::node() is deprecated, please use MooseMesh::nodeRef() instead");
     860           0 :   return nodeRef(i);
     861             : }
     862             : 
     863             : const Node &
     864    42686681 : MooseMesh::nodeRef(const dof_id_type i) const
     865             : {
     866    42686681 :   const auto node_ptr = queryNodePtr(i);
     867             :   mooseAssert(node_ptr, "Missing node");
     868    42686681 :   return *node_ptr;
     869             : }
     870             : 
     871             : Node &
     872    24014948 : MooseMesh::nodeRef(const dof_id_type i)
     873             : {
     874    24014948 :   return const_cast<Node &>(const_cast<const MooseMesh *>(this)->nodeRef(i));
     875             : }
     876             : 
     877             : const Node *
     878           0 : MooseMesh::nodePtr(const dof_id_type i) const
     879             : {
     880           0 :   return &nodeRef(i);
     881             : }
     882             : 
     883             : Node *
     884        2096 : MooseMesh::nodePtr(const dof_id_type i)
     885             : {
     886        2096 :   return &nodeRef(i);
     887             : }
     888             : 
     889             : const Node *
     890    42689425 : MooseMesh::queryNodePtr(const dof_id_type i) const
     891             : {
     892    42689425 :   if (i > getMesh().max_node_id())
     893             :   {
     894      196773 :     auto it = _quadrature_nodes.find(i);
     895      196773 :     if (it == _quadrature_nodes.end())
     896           0 :       return nullptr;
     897      196773 :     auto & node_ptr = it->second;
     898             :     mooseAssert(node_ptr, "Uninitialized quadrature node");
     899      196773 :     return node_ptr;
     900             :   }
     901             : 
     902    42492652 :   return getMesh().query_node_ptr(i);
     903             : }
     904             : 
     905             : Node *
     906        2744 : MooseMesh::queryNodePtr(const dof_id_type i)
     907             : {
     908        2744 :   return const_cast<Node *>(const_cast<const MooseMesh *>(this)->queryNodePtr(i));
     909             : }
     910             : 
     911             : void
     912       82270 : MooseMesh::meshChanged()
     913             : {
     914      411350 :   TIME_SECTION("meshChanged", 3, "Updating Because Mesh Changed");
     915             : 
     916       82270 :   update();
     917             : 
     918             :   // Delete all of the cached ranges
     919       82270 :   _active_local_elem_range.reset();
     920       82270 :   _active_node_range.reset();
     921       82270 :   _active_semilocal_node_range.reset();
     922       82270 :   _local_node_range.reset();
     923       82270 :   _bnd_node_range.reset();
     924       82270 :   _bnd_elem_range.reset();
     925             : 
     926             :   // Rebuild the ranges
     927       82270 :   getActiveLocalElementRange();
     928       82270 :   getActiveNodeRange();
     929       82270 :   getLocalNodeRange();
     930       82270 :   getBoundaryNodeRange();
     931       82270 :   getBoundaryElementRange();
     932             : 
     933             :   // Call the callback function onMeshChanged
     934       82270 :   onMeshChanged();
     935       82270 : }
     936             : 
     937             : void
     938       82270 : MooseMesh::onMeshChanged()
     939             : {
     940       82270 : }
     941             : 
     942             : void
     943         208 : MooseMesh::cacheChangedLists()
     944             : {
     945        1040 :   TIME_SECTION("cacheChangedLists", 5, "Caching Changed Lists");
     946             : 
     947         208 :   ConstElemRange elem_range(getMesh().local_elements_begin(), getMesh().local_elements_end(), 1);
     948         208 :   CacheChangedListsThread cclt(*this);
     949         208 :   Threads::parallel_reduce(elem_range, cclt);
     950             : 
     951         208 :   _coarsened_element_children.clear();
     952             : 
     953         416 :   _refined_elements = std::make_unique<ConstElemPointerRange>(cclt._refined_elements.begin(),
     954         416 :                                                               cclt._refined_elements.end());
     955         416 :   _coarsened_elements = std::make_unique<ConstElemPointerRange>(cclt._coarsened_elements.begin(),
     956         416 :                                                                 cclt._coarsened_elements.end());
     957         208 :   _coarsened_element_children = cclt._coarsened_element_children;
     958         208 : }
     959             : 
     960             : ConstElemPointerRange *
     961         208 : MooseMesh::refinedElementRange() const
     962             : {
     963         208 :   return _refined_elements.get();
     964             : }
     965             : 
     966             : ConstElemPointerRange *
     967         208 : MooseMesh::coarsenedElementRange() const
     968             : {
     969         208 :   return _coarsened_elements.get();
     970             : }
     971             : 
     972             : const std::vector<const Elem *> &
     973        2468 : MooseMesh::coarsenedElementChildren(const Elem * elem) const
     974             : {
     975        2468 :   auto elem_to_child_pair = _coarsened_element_children.find(elem);
     976             :   mooseAssert(elem_to_child_pair != _coarsened_element_children.end(), "Missing element in map");
     977        4936 :   return elem_to_child_pair->second;
     978             : }
     979             : 
     980             : void
     981       70344 : MooseMesh::updateActiveSemiLocalNodeRange(std::set<dof_id_type> & ghosted_elems)
     982             : {
     983      351720 :   TIME_SECTION("updateActiveSemiLocalNodeRange", 5, "Updating ActiveSemiLocalNode Range");
     984             : 
     985       70344 :   _semilocal_node_list.clear();
     986             : 
     987             :   // First add the nodes connected to local elems
     988       70344 :   ConstElemRange * active_local_elems = getActiveLocalElementRange();
     989    12999164 :   for (const auto & elem : *active_local_elems)
     990             :   {
     991    81633218 :     for (unsigned int n = 0; n < elem->n_nodes(); ++n)
     992             :     {
     993             :       // Since elem is const here but we require a non-const Node * to
     994             :       // store in the _semilocal_node_list (otherwise things like
     995             :       // UpdateDisplacedMeshThread don't work), we are using a
     996             :       // const_cast. A more long-term fix would be to have
     997             :       // getActiveLocalElementRange return a non-const ElemRange.
     998    68704398 :       Node * node = const_cast<Node *>(elem->node_ptr(n));
     999             : 
    1000    68704398 :       _semilocal_node_list.insert(node);
    1001             :     }
    1002             :   }
    1003             : 
    1004             :   // Now add the nodes connected to ghosted_elems
    1005      115496 :   for (const auto & ghost_elem_id : ghosted_elems)
    1006             :   {
    1007       45152 :     Elem * elem = getMesh().elem_ptr(ghost_elem_id);
    1008      249933 :     for (unsigned int n = 0; n < elem->n_nodes(); n++)
    1009             :     {
    1010      204781 :       Node * node = elem->node_ptr(n);
    1011             : 
    1012      204781 :       _semilocal_node_list.insert(node);
    1013             :     }
    1014             :   }
    1015             : 
    1016             :   // Now create the actual range
    1017      140688 :   _active_semilocal_node_range = std::make_unique<SemiLocalNodeRange>(_semilocal_node_list.begin(),
    1018      140688 :                                                                       _semilocal_node_list.end());
    1019       70344 : }
    1020             : 
    1021             : bool
    1022       26305 : MooseMesh::isSemiLocal(Node * const node) const
    1023             : {
    1024       26305 :   return _semilocal_node_list.find(node) != _semilocal_node_list.end();
    1025             : }
    1026             : 
    1027             : /**
    1028             :  * Helper class for sorting Boundary Nodes so that we always get the same
    1029             :  * order of application for boundary conditions.
    1030             :  */
    1031             : class BndNodeCompare
    1032             : {
    1033             : public:
    1034      152085 :   BndNodeCompare() {}
    1035             : 
    1036   118291409 :   bool operator()(const BndNode * const & lhs, const BndNode * const & rhs)
    1037             :   {
    1038   118291409 :     if (lhs->_bnd_id < rhs->_bnd_id)
    1039    22064949 :       return true;
    1040             : 
    1041    96226460 :     if (lhs->_bnd_id > rhs->_bnd_id)
    1042    10021140 :       return false;
    1043             : 
    1044    86205320 :     if (lhs->_node->id() < rhs->_node->id())
    1045    55707853 :       return true;
    1046             : 
    1047    30497467 :     if (lhs->_node->id() > rhs->_node->id())
    1048    30497467 :       return false;
    1049             : 
    1050           0 :     return false;
    1051             :   }
    1052             : };
    1053             : 
    1054             : void
    1055      152085 : MooseMesh::buildNodeList()
    1056             : {
    1057      760425 :   TIME_SECTION("buildNodeList", 5, "Building Node List");
    1058             : 
    1059      152085 :   freeBndNodes();
    1060             : 
    1061      152085 :   auto bc_tuples = getMesh().get_boundary_info().build_node_list();
    1062             : 
    1063      152085 :   int n = bc_tuples.size();
    1064      152085 :   _bnd_nodes.clear();
    1065      152085 :   _bnd_nodes.reserve(n);
    1066    12334517 :   for (const auto & t : bc_tuples)
    1067             :   {
    1068    12182432 :     auto node_id = std::get<0>(t);
    1069    12182432 :     auto bc_id = std::get<1>(t);
    1070             : 
    1071    12182432 :     _bnd_nodes.push_back(new BndNode(getMesh().node_ptr(node_id), bc_id));
    1072    12182432 :     _node_set_nodes[bc_id].push_back(node_id);
    1073    12182432 :     _bnd_node_ids[bc_id].insert(node_id);
    1074             :   }
    1075             : 
    1076      152085 :   _bnd_nodes.reserve(_bnd_nodes.size() + _extra_bnd_nodes.size());
    1077      152139 :   for (unsigned int i = 0; i < _extra_bnd_nodes.size(); i++)
    1078             :   {
    1079          54 :     BndNode * bnode = new BndNode(_extra_bnd_nodes[i]._node, _extra_bnd_nodes[i]._bnd_id);
    1080          54 :     _bnd_nodes.push_back(bnode);
    1081          54 :     _bnd_node_ids[std::get<1>(bc_tuples[i])].insert(_extra_bnd_nodes[i]._node->id());
    1082             :   }
    1083             : 
    1084             :   // This sort is here so that boundary conditions are always applied in the same order
    1085      152085 :   std::sort(_bnd_nodes.begin(), _bnd_nodes.end(), BndNodeCompare());
    1086      152085 : }
    1087             : 
    1088             : void
    1089      152085 : MooseMesh::computeMaxPerElemAndSide()
    1090             : {
    1091      152085 :   auto & mesh = getMesh();
    1092             : 
    1093      152085 :   _max_sides_per_elem = 0;
    1094      152085 :   _max_nodes_per_elem = 0;
    1095      152085 :   _max_nodes_per_side = 0;
    1096             : 
    1097    59237067 :   for (auto & elem : as_range(mesh.local_elements_begin(), mesh.local_elements_end()))
    1098             :   {
    1099    29542491 :     _max_sides_per_elem = std::max(_max_sides_per_elem, elem->n_sides());
    1100    29542491 :     _max_nodes_per_elem = std::max(_max_nodes_per_elem, elem->n_nodes());
    1101             : 
    1102   161517487 :     for (unsigned int side = 0; side < elem->n_sides(); ++side)
    1103   131974996 :       _max_nodes_per_side = std::max(_max_nodes_per_side, elem->side_ptr(side)->n_nodes());
    1104      152085 :   }
    1105             : 
    1106      152085 :   mesh.comm().max(_max_sides_per_elem);
    1107      152085 :   mesh.comm().max(_max_nodes_per_elem);
    1108      152085 :   mesh.comm().max(_max_nodes_per_side);
    1109      152085 : }
    1110             : 
    1111             : void
    1112      152085 : MooseMesh::buildElemIDInfo()
    1113             : {
    1114      152085 :   unsigned int n = getMesh().n_elem_integers() + 1;
    1115             : 
    1116      152085 :   _block_id_mapping.clear();
    1117      152085 :   _max_ids.clear();
    1118      152085 :   _min_ids.clear();
    1119      152085 :   _id_identical_flag.clear();
    1120             : 
    1121      152085 :   _block_id_mapping.resize(n);
    1122      152085 :   _max_ids.resize(n, std::numeric_limits<dof_id_type>::min());
    1123      152085 :   _min_ids.resize(n, std::numeric_limits<dof_id_type>::max());
    1124      304170 :   _id_identical_flag.resize(n, std::vector<bool>(n, true));
    1125    27616009 :   for (const auto & elem : getMesh().active_local_element_ptr_range())
    1126    59049147 :     for (unsigned int i = 0; i < n; ++i)
    1127             :     {
    1128    31585223 :       auto id = (i == n - 1 ? elem->subdomain_id() : elem->get_extra_integer(i));
    1129    31585223 :       _block_id_mapping[i][elem->subdomain_id()].insert(id);
    1130    31585223 :       if (id > _max_ids[i])
    1131      117169 :         _max_ids[i] = id;
    1132    31585223 :       if (id < _min_ids[i])
    1133      157113 :         _min_ids[i] = id;
    1134    76100972 :       for (unsigned int j = 0; j < n; ++j)
    1135             :       {
    1136    44515749 :         auto idj = (j == n - 1 ? elem->subdomain_id() : elem->get_extra_integer(j));
    1137    44515749 :         if (i != j && _id_identical_flag[i][j] && id != idj)
    1138        6718 :           _id_identical_flag[i][j] = false;
    1139             :       }
    1140      152085 :     }
    1141             : 
    1142      306595 :   for (unsigned int i = 0; i < n; ++i)
    1143             :   {
    1144      376155 :     for (auto & blk : meshSubdomains())
    1145      221645 :       comm().set_union(_block_id_mapping[i][blk]);
    1146      154510 :     comm().min(_id_identical_flag[i]);
    1147             :   }
    1148      152085 :   comm().max(_max_ids);
    1149      152085 :   comm().min(_min_ids);
    1150      152085 : }
    1151             : 
    1152             : std::unordered_map<dof_id_type, std::set<dof_id_type>>
    1153          11 : MooseMesh::getElemIDMapping(const std::string & from_id_name, const std::string & to_id_name) const
    1154             : {
    1155          11 :   auto & mesh_base = getMesh();
    1156             : 
    1157          11 :   if (!mesh_base.has_elem_integer(from_id_name))
    1158           0 :     mooseError("Mesh does not have the element integer name '", from_id_name, "'");
    1159          11 :   if (!mesh_base.has_elem_integer(to_id_name))
    1160           0 :     mooseError("Mesh does not have the element integer name '", to_id_name, "'");
    1161             : 
    1162          11 :   const auto id1 = mesh_base.get_elem_integer_index(from_id_name);
    1163          11 :   const auto id2 = mesh_base.get_elem_integer_index(to_id_name);
    1164             : 
    1165          11 :   std::unordered_map<dof_id_type, std::set<dof_id_type>> id_map;
    1166          33 :   for (const auto id : getAllElemIDs(id1))
    1167          33 :     id_map[id] = std::set<dof_id_type>();
    1168             : 
    1169         811 :   for (const auto & elem : mesh_base.active_local_element_ptr_range())
    1170         811 :     id_map[elem->get_extra_integer(id1)].insert(elem->get_extra_integer(id2));
    1171             : 
    1172          33 :   for (auto & [id, ids] : id_map)
    1173             :   {
    1174          22 :     libmesh_ignore(id); // avoid overzealous gcc 9.4 unused var warning
    1175          22 :     comm().set_union(ids);
    1176             :   }
    1177             : 
    1178          11 :   return id_map;
    1179           0 : }
    1180             : 
    1181             : std::set<dof_id_type>
    1182          50 : MooseMesh::getAllElemIDs(unsigned int elem_id_index) const
    1183             : {
    1184          50 :   std::set<dof_id_type> unique_ids;
    1185         139 :   for (auto & pair : _block_id_mapping[elem_id_index])
    1186         319 :     for (auto & id : pair.second)
    1187         230 :       unique_ids.insert(id);
    1188          50 :   return unique_ids;
    1189           0 : }
    1190             : 
    1191             : std::set<dof_id_type>
    1192         152 : MooseMesh::getElemIDsOnBlocks(unsigned int elem_id_index, const std::set<SubdomainID> & blks) const
    1193             : {
    1194         152 :   std::set<dof_id_type> unique_ids;
    1195         379 :   for (auto & blk : blks)
    1196             :   {
    1197         227 :     auto it = _block_id_mapping[elem_id_index].find(blk);
    1198         227 :     if (it == _block_id_mapping[elem_id_index].end())
    1199           0 :       mooseError("Block ", blk, " is not available on the mesh");
    1200             : 
    1201         532 :     for (auto & mid : it->second)
    1202         305 :       unique_ids.insert(mid);
    1203             :   }
    1204         152 :   return unique_ids;
    1205           0 : }
    1206             : 
    1207             : void
    1208      152085 : MooseMesh::buildBndElemList()
    1209             : {
    1210      760425 :   TIME_SECTION("buildBndElemList", 5, "Building Boundary Elements List");
    1211             : 
    1212      152085 :   freeBndElems();
    1213             : 
    1214      152085 :   auto bc_tuples = getMesh().get_boundary_info().build_active_side_list();
    1215             : 
    1216      152085 :   int n = bc_tuples.size();
    1217      152085 :   _bnd_elems.clear();
    1218      152085 :   _bnd_elems.reserve(n);
    1219     9471767 :   for (const auto & t : bc_tuples)
    1220             :   {
    1221     9319682 :     auto elem_id = std::get<0>(t);
    1222     9319682 :     auto side_id = std::get<1>(t);
    1223     9319682 :     auto bc_id = std::get<2>(t);
    1224             : 
    1225     9319682 :     _bnd_elems.push_back(new BndElement(getMesh().elem_ptr(elem_id), side_id, bc_id));
    1226     9319682 :     _bnd_elem_ids[bc_id].insert(elem_id);
    1227             :   }
    1228      152085 : }
    1229             : 
    1230             : const std::map<dof_id_type, std::vector<dof_id_type>> &
    1231      964160 : MooseMesh::nodeToElemMap()
    1232             : {
    1233      964160 :   if (!_node_to_elem_map_built) // Guard the creation with a double checked lock
    1234             :   {
    1235       12638 :     Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
    1236             : 
    1237       12638 :     if (!_node_to_elem_map_built)
    1238             :     {
    1239             :       // This is allowing the timing to be run even with threads
    1240             :       // This is safe because all threads will be waiting on this section when it runs
    1241             :       // NOTE: Do not copy this construction to other places without thinking REALLY hard about it
    1242             :       // The PerfGraph is NOT threadsafe and will cause all kinds of havok if care isn't taken
    1243       12624 :       auto in_threads = Threads::in_threads;
    1244       12624 :       Threads::in_threads = false;
    1245       63120 :       TIME_SECTION("nodeToElemMap", 5, "Building Node To Elem Map");
    1246       12624 :       Threads::in_threads = in_threads;
    1247             : 
    1248     2992676 :       for (const auto & elem : getMesh().active_element_ptr_range())
    1249    17530359 :         for (unsigned int n = 0; n < elem->n_nodes(); n++)
    1250    14562931 :           _node_to_elem_map[elem->node_id(n)].push_back(elem->id());
    1251             : 
    1252       12624 :       _node_to_elem_map_built = true; // MUST be set at the end for double-checked locking to work!
    1253       12624 :     }
    1254       12638 :   }
    1255      964160 :   return _node_to_elem_map;
    1256             : }
    1257             : 
    1258             : const std::map<dof_id_type, std::vector<dof_id_type>> &
    1259      133215 : MooseMesh::nodeToActiveSemilocalElemMap()
    1260             : {
    1261      133215 :   if (!_node_to_active_semilocal_elem_map_built) // Guard the creation with a double checked lock
    1262             :   {
    1263       72860 :     Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
    1264             : 
    1265             :     // This is allowing the timing to be run even with threads
    1266             :     // This is safe because all threads will be waiting on this section when it runs
    1267             :     // NOTE: Do not copy this construction to other places without thinking REALLY hard about it
    1268             :     // The PerfGraph is NOT threadsafe and will cause all kinds of havok if care isn't taken
    1269       72860 :     auto in_threads = Threads::in_threads;
    1270       72860 :     Threads::in_threads = false;
    1271      364300 :     TIME_SECTION("nodeToActiveSemilocalElemMap", 5, "Building SemiLocalElemMap");
    1272       72860 :     Threads::in_threads = in_threads;
    1273             : 
    1274       72860 :     if (!_node_to_active_semilocal_elem_map_built)
    1275             :     {
    1276       72860 :       for (const auto & elem :
    1277    14731310 :            as_range(getMesh().semilocal_elements_begin(), getMesh().semilocal_elements_end()))
    1278    14585590 :         if (elem->active())
    1279    92670902 :           for (unsigned int n = 0; n < elem->n_nodes(); n++)
    1280    78158172 :             _node_to_active_semilocal_elem_map[elem->node_id(n)].push_back(elem->id());
    1281             : 
    1282       72860 :       _node_to_active_semilocal_elem_map_built =
    1283             :           true; // MUST be set at the end for double-checked locking to work!
    1284             :     }
    1285       72860 :   }
    1286             : 
    1287      133215 :   return _node_to_active_semilocal_elem_map;
    1288             : }
    1289             : 
    1290             : ConstElemRange *
    1291    10351875 : MooseMesh::getActiveLocalElementRange()
    1292             : {
    1293    10351875 :   if (!_active_local_elem_range)
    1294             :   {
    1295      419718 :     TIME_SECTION("getActiveLocalElementRange", 5);
    1296             : 
    1297      279812 :     _active_local_elem_range = std::make_unique<ConstElemRange>(
    1298      419718 :         getMesh().active_local_elements_begin(), getMesh().active_local_elements_end(), GRAIN_SIZE);
    1299      139906 :   }
    1300             : 
    1301    10351875 :   return _active_local_elem_range.get();
    1302             : }
    1303             : 
    1304             : NodeRange *
    1305       82334 : MooseMesh::getActiveNodeRange()
    1306             : {
    1307       82334 :   if (!_active_node_range)
    1308             :   {
    1309      246810 :     TIME_SECTION("getActiveNodeRange", 5);
    1310             : 
    1311      164540 :     _active_node_range = std::make_unique<NodeRange>(
    1312      246810 :         getMesh().active_nodes_begin(), getMesh().active_nodes_end(), GRAIN_SIZE);
    1313       82270 :   }
    1314             : 
    1315       82334 :   return _active_node_range.get();
    1316             : }
    1317             : 
    1318             : SemiLocalNodeRange *
    1319           0 : MooseMesh::getActiveSemiLocalNodeRange() const
    1320             : {
    1321             :   mooseAssert(_active_semilocal_node_range,
    1322             :               "_active_semilocal_node_range has not been created yet!");
    1323             : 
    1324           0 :   return _active_semilocal_node_range.get();
    1325             : }
    1326             : 
    1327             : ConstNodeRange *
    1328      314358 : MooseMesh::getLocalNodeRange()
    1329             : {
    1330      314358 :   if (!_local_node_range)
    1331             :   {
    1332      246810 :     TIME_SECTION("getLocalNodeRange", 5);
    1333             : 
    1334      164540 :     _local_node_range = std::make_unique<ConstNodeRange>(
    1335      246810 :         getMesh().local_nodes_begin(), getMesh().local_nodes_end(), GRAIN_SIZE);
    1336       82270 :   }
    1337             : 
    1338      314358 :   return _local_node_range.get();
    1339             : }
    1340             : 
    1341             : ConstBndNodeRange *
    1342     3655759 : MooseMesh::getBoundaryNodeRange()
    1343             : {
    1344     3655759 :   if (!_bnd_node_range)
    1345             :   {
    1346      247326 :     TIME_SECTION("getBoundaryNodeRange", 5);
    1347             : 
    1348             :     _bnd_node_range =
    1349       82442 :         std::make_unique<ConstBndNodeRange>(bndNodesBegin(), bndNodesEnd(), GRAIN_SIZE);
    1350       82442 :   }
    1351             : 
    1352     3655759 :   return _bnd_node_range.get();
    1353             : }
    1354             : 
    1355             : ConstBndElemRange *
    1356      185928 : MooseMesh::getBoundaryElementRange()
    1357             : {
    1358      185928 :   if (!_bnd_elem_range)
    1359             :   {
    1360      246810 :     TIME_SECTION("getBoundaryElementRange", 5);
    1361             : 
    1362             :     _bnd_elem_range =
    1363       82270 :         std::make_unique<ConstBndElemRange>(bndElemsBegin(), bndElemsEnd(), GRAIN_SIZE);
    1364       82270 :   }
    1365             : 
    1366      185928 :   return _bnd_elem_range.get();
    1367             : }
    1368             : 
    1369             : const std::unordered_map<boundary_id_type, std::unordered_set<dof_id_type>> &
    1370           0 : MooseMesh::getBoundariesToElems() const
    1371             : {
    1372           0 :   mooseDeprecated("MooseMesh::getBoundariesToElems is deprecated, "
    1373             :                   "use MooseMesh::getBoundariesToActiveSemiLocalElemIds");
    1374           0 :   return getBoundariesToActiveSemiLocalElemIds();
    1375             : }
    1376             : 
    1377             : const std::unordered_map<boundary_id_type, std::unordered_set<dof_id_type>> &
    1378          61 : MooseMesh::getBoundariesToActiveSemiLocalElemIds() const
    1379             : {
    1380          61 :   return _bnd_elem_ids;
    1381             : }
    1382             : 
    1383             : std::unordered_set<dof_id_type>
    1384        2963 : MooseMesh::getBoundaryActiveSemiLocalElemIds(BoundaryID bid) const
    1385             : {
    1386             :   // The boundary to element map is computed on every mesh update
    1387        2963 :   const auto it = _bnd_elem_ids.find(bid);
    1388        2963 :   if (it == _bnd_elem_ids.end())
    1389             :     // Boundary is not local to this domain, return an empty set
    1390          94 :     return std::unordered_set<dof_id_type>{};
    1391        2869 :   return it->second;
    1392             : }
    1393             : 
    1394             : std::unordered_set<dof_id_type>
    1395           0 : MooseMesh::getBoundaryActiveNeighborElemIds(BoundaryID bid) const
    1396             : {
    1397             :   // Vector of boundary elems is updated every mesh update
    1398           0 :   std::unordered_set<dof_id_type> neighbor_elems;
    1399           0 :   for (const auto & bnd_elem : _bnd_elems)
    1400             :   {
    1401           0 :     const auto & [elem_ptr, elem_side, elem_bid] = *bnd_elem;
    1402           0 :     if (elem_bid == bid)
    1403             :     {
    1404           0 :       const auto * neighbor = elem_ptr->neighbor_ptr(elem_side);
    1405             :       // Dont add fully remote elements, ghosted is fine
    1406           0 :       if (neighbor && neighbor != libMesh::remote_elem)
    1407             :       {
    1408             :         // handle mesh refinement, only return active elements near the boundary
    1409           0 :         if (neighbor->active())
    1410           0 :           neighbor_elems.insert(neighbor->id());
    1411             :         else
    1412             :         {
    1413           0 :           std::vector<const Elem *> family;
    1414           0 :           neighbor->active_family_tree_by_neighbor(family, elem_ptr);
    1415           0 :           for (const auto & child_neighbor : family)
    1416           0 :             neighbor_elems.insert(child_neighbor->id());
    1417           0 :         }
    1418             :       }
    1419             :     }
    1420             :   }
    1421             : 
    1422           0 :   return neighbor_elems;
    1423           0 : }
    1424             : 
    1425             : bool
    1426           0 : MooseMesh::isBoundaryFullyExternalToSubdomains(BoundaryID bid,
    1427             :                                                const std::set<SubdomainID> & blk_group) const
    1428             : {
    1429             :   mooseAssert(_bnd_elem_range, "Boundary element range is not initialized");
    1430             : 
    1431             :   // Loop over all side elements of the mesh, select those on the boundary
    1432           0 :   for (const auto & bnd_elem : *_bnd_elem_range)
    1433             :   {
    1434           0 :     const auto & [elem_ptr, elem_side, elem_bid] = *bnd_elem;
    1435           0 :     if (elem_bid == bid)
    1436             :     {
    1437             :       // If an element is internal to the group of subdomain, check the neighbor
    1438           0 :       if (blk_group.find(elem_ptr->subdomain_id()) != blk_group.end())
    1439             :       {
    1440           0 :         const auto * const neighbor = elem_ptr->neighbor_ptr(elem_side);
    1441             : 
    1442             :         // If we did not ghost the neighbor, we cannot decide
    1443           0 :         if (neighbor == libMesh::remote_elem)
    1444           0 :           mooseError("Insufficient level of geometrical ghosting to determine "
    1445             :                      "if a boundary is internal to the mesh");
    1446             :         // If the neighbor does not exist, then we are on the edge of the mesh
    1447           0 :         if (!neighbor)
    1448           0 :           continue;
    1449             :         // If the neighbor is also in the group of subdomain,
    1450             :         // then the boundary cuts the subdomains
    1451           0 :         if (blk_group.find(neighbor->subdomain_id()) != blk_group.end())
    1452           0 :           return false;
    1453             :       }
    1454             :     }
    1455             :   }
    1456           0 :   return true;
    1457             : }
    1458             : 
    1459             : void
    1460      152085 : MooseMesh::cacheInfo()
    1461             : {
    1462      456255 :   TIME_SECTION("cacheInfo", 3);
    1463             : 
    1464      152085 :   _sub_to_data.clear();
    1465      152085 :   _neighbor_subdomain_boundary_ids.clear();
    1466      152085 :   _block_node_list.clear();
    1467      152085 :   _higher_d_elem_side_to_lower_d_elem.clear();
    1468      152085 :   _lower_d_elem_to_higher_d_elem_side.clear();
    1469      152085 :   _lower_d_interior_blocks.clear();
    1470      152085 :   _lower_d_boundary_blocks.clear();
    1471             : 
    1472      152085 :   auto & mesh = getMesh();
    1473             : 
    1474             :   // Cache higher and lowerD element information
    1475    38056658 :   for (const auto & elem : mesh.element_ptr_range())
    1476             :   {
    1477    37904573 :     const Elem * ip_elem = elem->interior_parent();
    1478             : 
    1479    37904573 :     if (ip_elem)
    1480             :     {
    1481       73321 :       unsigned int ip_side = ip_elem->which_side_am_i(elem);
    1482             : 
    1483             :       // For some grid sequencing tests: ip_side == libMesh::invalid_uint
    1484       73321 :       if (ip_side != libMesh::invalid_uint)
    1485             :       {
    1486       73161 :         auto pair = std::make_pair(ip_elem, ip_side);
    1487       73161 :         _higher_d_elem_side_to_lower_d_elem.insert(
    1488       73161 :             std::pair<std::pair<const Elem *, unsigned short int>, const Elem *>(pair, elem));
    1489       73161 :         _lower_d_elem_to_higher_d_elem_side.insert(
    1490       73161 :             std::pair<const Elem *, unsigned short int>(elem, ip_side));
    1491             : 
    1492       73161 :         auto id = elem->subdomain_id();
    1493       73161 :         if (ip_elem->neighbor_ptr(ip_side))
    1494             :         {
    1495        6680 :           if (mesh.subdomain_name(id).find("INTERNAL_SIDE_LOWERD_SUBDOMAIN_") != std::string::npos)
    1496        6580 :             _lower_d_interior_blocks.insert(id);
    1497             :         }
    1498             :         else
    1499             :         {
    1500       66481 :           if (mesh.subdomain_name(id).find("BOUNDARY_SIDE_LOWERD_SUBDOMAIN_") != std::string::npos)
    1501        6890 :             _lower_d_boundary_blocks.insert(id);
    1502             :         }
    1503             :       }
    1504             :     }
    1505             : 
    1506   241468446 :     for (unsigned int nd = 0; nd < elem->n_nodes(); ++nd)
    1507             :     {
    1508   203563873 :       Node & node = *elem->node_ptr(nd);
    1509   203563873 :       _block_node_list[node.id()].insert(elem->subdomain_id());
    1510             :     }
    1511      152085 :   }
    1512      152085 :   _communicator.set_union(_lower_d_interior_blocks);
    1513      152085 :   _communicator.set_union(_lower_d_boundary_blocks);
    1514             : 
    1515             :   // Cache the boundaries next to each subdomain
    1516    27616009 :   for (const auto & elem : mesh.active_local_element_ptr_range())
    1517             :   {
    1518    27463924 :     SubdomainID subdomain_id = elem->subdomain_id();
    1519    27463924 :     auto & sub_data = _sub_to_data[subdomain_id];
    1520    27463924 :     const auto elem_boundary_ids = getBoundaryIDs(elem);
    1521   151056660 :     for (unsigned int side = 0; side < elem->n_sides(); side++)
    1522             :     {
    1523   123592736 :       const auto & boundary_ids = elem_boundary_ids[side];
    1524   123592736 :       sub_data.boundary_ids.insert(boundary_ids.begin(), boundary_ids.end());
    1525             : 
    1526   123592736 :       Elem * neig = elem->neighbor_ptr(side);
    1527   123592736 :       if (neig)
    1528             :       {
    1529   116509391 :         _neighbor_subdomain_boundary_ids[neig->subdomain_id()].insert(boundary_ids.begin(),
    1530             :                                                                       boundary_ids.end());
    1531   116509391 :         SubdomainID neighbor_subdomain_id = neig->subdomain_id();
    1532   116509391 :         if (neighbor_subdomain_id != subdomain_id)
    1533     1815380 :           sub_data.neighbor_subs.insert(neighbor_subdomain_id);
    1534             :       }
    1535             :     }
    1536    27616009 :   }
    1537             : 
    1538      367522 :   for (const auto blk_id : _mesh_subdomains)
    1539             :   {
    1540      215437 :     auto & sub_data = _sub_to_data[blk_id];
    1541      215437 :     _communicator.set_union(sub_data.neighbor_subs);
    1542      215437 :     _communicator.set_union(sub_data.boundary_ids);
    1543      215437 :     _communicator.set_union(_neighbor_subdomain_boundary_ids[blk_id]);
    1544             :   }
    1545      152085 : }
    1546             : 
    1547             : const std::set<SubdomainID> &
    1548    94928723 : MooseMesh::getNodeBlockIds(const Node & node) const
    1549             : {
    1550    94928723 :   auto it = _block_node_list.find(node.id());
    1551             : 
    1552    94928723 :   if (it == _block_node_list.end())
    1553           0 :     mooseError("Unable to find node: ", node.id(), " in any block list.");
    1554             : 
    1555   189857446 :   return it->second;
    1556             : }
    1557             : 
    1558             : MooseMesh::face_info_iterator
    1559      165347 : MooseMesh::ownedFaceInfoBegin()
    1560             : {
    1561             :   return face_info_iterator(
    1562      165347 :       _face_info.begin(),
    1563      165347 :       _face_info.end(),
    1564      330694 :       libMesh::Predicates::pid<std::vector<const FaceInfo *>::iterator>(this->processor_id()));
    1565             : }
    1566             : 
    1567             : MooseMesh::face_info_iterator
    1568      165347 : MooseMesh::ownedFaceInfoEnd()
    1569             : {
    1570             :   return face_info_iterator(
    1571      165347 :       _face_info.end(),
    1572      165347 :       _face_info.end(),
    1573      330694 :       libMesh::Predicates::pid<std::vector<const FaceInfo *>::iterator>(this->processor_id()));
    1574             : }
    1575             : 
    1576             : MooseMesh::elem_info_iterator
    1577       85537 : MooseMesh::ownedElemInfoBegin()
    1578             : {
    1579       85537 :   return elem_info_iterator(_elem_info.begin(),
    1580       85537 :                             _elem_info.end(),
    1581      171074 :                             Predicates::NotNull<std::vector<const ElemInfo *>::iterator>());
    1582             : }
    1583             : 
    1584             : MooseMesh::elem_info_iterator
    1585       85537 : MooseMesh::ownedElemInfoEnd()
    1586             : {
    1587       85537 :   return elem_info_iterator(_elem_info.end(),
    1588       85537 :                             _elem_info.end(),
    1589      171074 :                             Predicates::NotNull<std::vector<const ElemInfo *>::iterator>());
    1590             : }
    1591             : 
    1592             : // default begin() accessor
    1593             : MooseMesh::bnd_node_iterator
    1594       84707 : MooseMesh::bndNodesBegin()
    1595             : {
    1596       84707 :   Predicates::NotNull<bnd_node_iterator_imp> p;
    1597      169414 :   return bnd_node_iterator(_bnd_nodes.begin(), _bnd_nodes.end(), p);
    1598       84707 : }
    1599             : 
    1600             : // default end() accessor
    1601             : MooseMesh::bnd_node_iterator
    1602       84707 : MooseMesh::bndNodesEnd()
    1603             : {
    1604       84707 :   Predicates::NotNull<bnd_node_iterator_imp> p;
    1605      169414 :   return bnd_node_iterator(_bnd_nodes.end(), _bnd_nodes.end(), p);
    1606       84707 : }
    1607             : 
    1608             : // default begin() accessor
    1609             : MooseMesh::bnd_elem_iterator
    1610       82424 : MooseMesh::bndElemsBegin()
    1611             : {
    1612       82424 :   Predicates::NotNull<bnd_elem_iterator_imp> p;
    1613      164848 :   return bnd_elem_iterator(_bnd_elems.begin(), _bnd_elems.end(), p);
    1614       82424 : }
    1615             : 
    1616             : // default end() accessor
    1617             : MooseMesh::bnd_elem_iterator
    1618       82424 : MooseMesh::bndElemsEnd()
    1619             : {
    1620       82424 :   Predicates::NotNull<bnd_elem_iterator_imp> p;
    1621      164848 :   return bnd_elem_iterator(_bnd_elems.end(), _bnd_elems.end(), p);
    1622       82424 : }
    1623             : 
    1624             : const Node *
    1625           0 : MooseMesh::addUniqueNode(const Point & p, Real tol)
    1626             : {
    1627             :   /**
    1628             :    * Looping through the mesh nodes each time we add a point is very slow.  To speed things
    1629             :    * up we keep a local data structure
    1630             :    */
    1631           0 :   if (getMesh().n_nodes() != _node_map.size())
    1632             :   {
    1633           0 :     _node_map.clear();
    1634           0 :     _node_map.reserve(getMesh().n_nodes());
    1635           0 :     for (const auto & node : getMesh().node_ptr_range())
    1636           0 :       _node_map.push_back(node);
    1637             :   }
    1638             : 
    1639           0 :   Node * node = nullptr;
    1640           0 :   for (unsigned int i = 0; i < _node_map.size(); ++i)
    1641             :   {
    1642           0 :     if (p.relative_fuzzy_equals(*_node_map[i], tol))
    1643             :     {
    1644           0 :       node = _node_map[i];
    1645           0 :       break;
    1646             :     }
    1647             :   }
    1648           0 :   if (node == nullptr)
    1649             :   {
    1650           0 :     node = getMesh().add_node(new Node(p));
    1651           0 :     _node_map.push_back(node);
    1652             :   }
    1653             : 
    1654             :   mooseAssert(node != nullptr, "Node is NULL");
    1655           0 :   return node;
    1656             : }
    1657             : 
    1658             : Node *
    1659        5357 : MooseMesh::addQuadratureNode(const Elem * elem,
    1660             :                              const unsigned short int side,
    1661             :                              const unsigned int qp,
    1662             :                              BoundaryID bid,
    1663             :                              const Point & point)
    1664             : {
    1665             :   Node * qnode;
    1666             : 
    1667        5357 :   if (_elem_to_side_to_qp_to_quadrature_nodes[elem->id()][side].find(qp) ==
    1668       10714 :       _elem_to_side_to_qp_to_quadrature_nodes[elem->id()][side].end())
    1669             :   {
    1670             :     // Create a new node id starting from the max node id and counting down.  This will be the least
    1671             :     // likely to collide with an existing node id.
    1672             :     // Note that we are using numeric_limits<unsigned>::max even
    1673             :     // though max_id is stored as a dof_id_type.  I tried this with
    1674             :     // numeric_limits<dof_id_type>::max and it broke several tests in
    1675             :     // MOOSE.  So, this is some kind of a magic number that we will
    1676             :     // just continue to use...
    1677        5357 :     dof_id_type max_id = std::numeric_limits<unsigned int>::max() - 100;
    1678        5357 :     dof_id_type new_id = max_id - _quadrature_nodes.size();
    1679             : 
    1680        5357 :     if (new_id <= getMesh().max_node_id())
    1681           0 :       mooseError("Quadrature node id collides with existing node id!");
    1682             : 
    1683        5357 :     qnode = new Node(point, new_id);
    1684             : 
    1685             :     // Keep track of this new node in two different ways for easy lookup
    1686        5357 :     _quadrature_nodes[new_id] = qnode;
    1687        5357 :     _elem_to_side_to_qp_to_quadrature_nodes[elem->id()][side][qp] = qnode;
    1688             : 
    1689        5357 :     if (elem->active())
    1690             :     {
    1691             :       // If they have not been built, no need to start building an incomplete one
    1692        5357 :       if (_node_to_elem_map_built)
    1693        5357 :         _node_to_elem_map[new_id].push_back(elem->id());
    1694        5357 :       if (_node_to_active_semilocal_elem_map_built)
    1695           0 :         _node_to_active_semilocal_elem_map[new_id].push_back(elem->id());
    1696             :     }
    1697             :   }
    1698             :   else
    1699           0 :     qnode = _elem_to_side_to_qp_to_quadrature_nodes[elem->id()][side][qp];
    1700             : 
    1701        5357 :   BndNode * bnode = new BndNode(qnode, bid);
    1702        5357 :   _bnd_nodes.push_back(bnode);
    1703        5357 :   _bnd_node_ids[bid].insert(qnode->id());
    1704             : 
    1705        5357 :   _extra_bnd_nodes.push_back(*bnode);
    1706             : 
    1707             :   // Do this so the range will be regenerated next time it is accessed
    1708        5357 :   _bnd_node_range.reset();
    1709             : 
    1710        5357 :   return qnode;
    1711             : }
    1712             : 
    1713             : Node *
    1714      137880 : MooseMesh::getQuadratureNode(const Elem * elem,
    1715             :                              const unsigned short int side,
    1716             :                              const unsigned int qp)
    1717             : {
    1718             :   mooseAssert(_elem_to_side_to_qp_to_quadrature_nodes.find(elem->id()) !=
    1719             :                   _elem_to_side_to_qp_to_quadrature_nodes.end(),
    1720             :               "Elem has no quadrature nodes!");
    1721             :   mooseAssert(_elem_to_side_to_qp_to_quadrature_nodes[elem->id()].find(side) !=
    1722             :                   _elem_to_side_to_qp_to_quadrature_nodes[elem->id()].end(),
    1723             :               "Side has no quadrature nodes!");
    1724             :   mooseAssert(_elem_to_side_to_qp_to_quadrature_nodes[elem->id()][side].find(qp) !=
    1725             :                   _elem_to_side_to_qp_to_quadrature_nodes[elem->id()][side].end(),
    1726             :               "qp not found on side!");
    1727             : 
    1728      137880 :   return _elem_to_side_to_qp_to_quadrature_nodes[elem->id()][side][qp];
    1729             : }
    1730             : 
    1731             : void
    1732       72805 : MooseMesh::clearQuadratureNodes()
    1733             : {
    1734             :   // Delete all the quadrature nodes
    1735       78150 :   for (auto & it : _quadrature_nodes)
    1736        5345 :     delete it.second;
    1737             : 
    1738       72805 :   _quadrature_nodes.clear();
    1739       72805 :   _elem_to_side_to_qp_to_quadrature_nodes.clear();
    1740       72805 :   _extra_bnd_nodes.clear();
    1741             : 
    1742             :   // NOTE: this does not clear them from the nodeToElem and nodeToActiveSemiLocalElem maps
    1743       72805 : }
    1744             : 
    1745             : BoundaryID
    1746      360992 : MooseMesh::getBoundaryID(const BoundaryName & boundary_name) const
    1747             : {
    1748      360992 :   if (boundary_name == "ANY_BOUNDARY_ID")
    1749           0 :     mooseError("Please use getBoundaryIDs() when passing \"ANY_BOUNDARY_ID\"");
    1750             : 
    1751      360992 :   return MooseMeshUtils::getBoundaryID(boundary_name, getMesh());
    1752             : }
    1753             : 
    1754             : const Elem *
    1755  1575128740 : MooseMesh::getLowerDElem(const Elem * elem, unsigned short int side) const
    1756             : {
    1757  1575128740 :   auto it = _higher_d_elem_side_to_lower_d_elem.find(std::make_pair(elem, side));
    1758             : 
    1759  1575128740 :   if (it != _higher_d_elem_side_to_lower_d_elem.end())
    1760      231482 :     return it->second;
    1761             :   else
    1762  1574897258 :     return nullptr;
    1763             : }
    1764             : 
    1765             : unsigned int
    1766         260 : MooseMesh::getHigherDSide(const Elem * elem) const
    1767             : {
    1768         260 :   auto it = _lower_d_elem_to_higher_d_elem_side.find(elem);
    1769             : 
    1770         260 :   if (it != _lower_d_elem_to_higher_d_elem_side.end())
    1771         260 :     return it->second;
    1772             :   else
    1773           0 :     return libMesh::invalid_uint;
    1774             : }
    1775             : 
    1776             : std::vector<BoundaryID>
    1777      115370 : MooseMesh::getBoundaryIDs(const std::vector<BoundaryName> & boundary_name,
    1778             :                           bool generate_unknown) const
    1779             : {
    1780             :   return MooseMeshUtils::getBoundaryIDs(
    1781      115370 :       getMesh(), boundary_name, generate_unknown, _mesh_boundary_ids);
    1782             : }
    1783             : 
    1784             : SubdomainID
    1785      346197 : MooseMesh::getSubdomainID(const SubdomainName & subdomain_name) const
    1786             : {
    1787      346197 :   return MooseMeshUtils::getSubdomainID(subdomain_name, getMesh());
    1788             : }
    1789             : 
    1790             : std::vector<SubdomainID>
    1791      235247 : MooseMesh::getSubdomainIDs(const std::vector<SubdomainName> & subdomain_name) const
    1792             : {
    1793      235247 :   return MooseMeshUtils::getSubdomainIDs(getMesh(), subdomain_name);
    1794             : }
    1795             : 
    1796             : std::set<SubdomainID>
    1797           0 : MooseMesh::getSubdomainIDs(const std::set<SubdomainName> & subdomain_name) const
    1798             : {
    1799           0 :   return MooseMeshUtils::getSubdomainIDs(getMesh(), subdomain_name);
    1800             : }
    1801             : 
    1802             : void
    1803        1330 : MooseMesh::setSubdomainName(SubdomainID subdomain_id, const SubdomainName & name)
    1804             : {
    1805             :   mooseAssert(name != "ANY_BLOCK_ID", "Cannot set subdomain name to 'ANY_BLOCK_ID'");
    1806        1330 :   getMesh().subdomain_name(subdomain_id) = name;
    1807        1330 : }
    1808             : 
    1809             : void
    1810           0 : MooseMesh::setSubdomainName(MeshBase & mesh, SubdomainID subdomain_id, const SubdomainName & name)
    1811             : {
    1812             :   mooseAssert(name != "ANY_BLOCK_ID", "Cannot set subdomain name to 'ANY_BLOCK_ID'");
    1813           0 :   mesh.subdomain_name(subdomain_id) = name;
    1814           0 : }
    1815             : 
    1816             : const std::string &
    1817     4626869 : MooseMesh::getSubdomainName(SubdomainID subdomain_id) const
    1818             : {
    1819     4626869 :   return getMesh().subdomain_name(subdomain_id);
    1820             : }
    1821             : 
    1822             : std::vector<SubdomainName>
    1823          71 : MooseMesh::getSubdomainNames(const std::vector<SubdomainID> & subdomain_ids) const
    1824             : {
    1825          71 :   std::vector<SubdomainName> names(subdomain_ids.size());
    1826             : 
    1827         142 :   for (unsigned int i = 0; i < subdomain_ids.size(); i++)
    1828          71 :     names[i] = getSubdomainName(subdomain_ids[i]);
    1829             : 
    1830          71 :   return names;
    1831           0 : }
    1832             : 
    1833             : void
    1834         110 : MooseMesh::setBoundaryName(BoundaryID boundary_id, BoundaryName name)
    1835             : {
    1836         110 :   BoundaryInfo & boundary_info = getMesh().get_boundary_info();
    1837             : 
    1838             :   // We need to figure out if this boundary is a sideset or nodeset
    1839         110 :   if (boundary_info.get_side_boundary_ids().count(boundary_id))
    1840          30 :     boundary_info.sideset_name(boundary_id) = name;
    1841             :   else
    1842          80 :     boundary_info.nodeset_name(boundary_id) = name;
    1843         110 : }
    1844             : 
    1845             : const std::string &
    1846     8147371 : MooseMesh::getBoundaryName(const BoundaryID boundary_id) const
    1847             : {
    1848     8147371 :   const BoundaryInfo & boundary_info = getMesh().get_boundary_info();
    1849             : 
    1850             :   // We need to figure out if this boundary is a sideset or nodeset
    1851     8147371 :   if (boundary_info.get_side_boundary_ids().count(boundary_id))
    1852     7982613 :     return boundary_info.get_sideset_name(boundary_id);
    1853             :   else
    1854      164758 :     return boundary_info.get_nodeset_name(boundary_id);
    1855             : }
    1856             : 
    1857             : std::string
    1858           0 : MooseMesh::getBoundaryString(BoundaryID boundary_id) const
    1859             : {
    1860           0 :   const auto name = getBoundaryName(boundary_id);
    1861           0 :   return name.size() ? name : std::to_string(boundary_id);
    1862           0 : }
    1863             : 
    1864             : // specialization for PointListAdaptor<MooseMesh::PeriodicNodeInfo>
    1865             : template <>
    1866             : inline const Point &
    1867      173430 : PointListAdaptor<MooseMesh::PeriodicNodeInfo>::getPoint(
    1868             :     const MooseMesh::PeriodicNodeInfo & item) const
    1869             : {
    1870      173430 :   return *(item.first);
    1871             : }
    1872             : 
    1873             : void
    1874          27 : MooseMesh::buildPeriodicNodeMap(std::multimap<dof_id_type, dof_id_type> & periodic_node_map,
    1875             :                                 unsigned int var_number,
    1876             :                                 libMesh::PeriodicBoundaries * pbs) const
    1877             : {
    1878          81 :   TIME_SECTION("buildPeriodicNodeMap", 5);
    1879             : 
    1880             :   // clear existing map
    1881          27 :   periodic_node_map.clear();
    1882             : 
    1883             :   // get periodic nodes
    1884          27 :   std::vector<PeriodicNodeInfo> periodic_nodes;
    1885        1575 :   for (const auto & t : getMesh().get_boundary_info().build_node_list())
    1886             :   {
    1887             :     // unfortunately libMesh does not give us a pointer, so we have to look it up ourselves
    1888        1548 :     auto node = _mesh->node_ptr(std::get<0>(t));
    1889             :     mooseAssert(node != nullptr,
    1890             :                 "libMesh::BoundaryInfo::build_node_list() returned an ID for a non-existing node");
    1891        1548 :     auto bc_id = std::get<1>(t);
    1892        1548 :     periodic_nodes.emplace_back(node, bc_id);
    1893          27 :   }
    1894             : 
    1895             :   // sort by boundary id
    1896          27 :   std::sort(periodic_nodes.begin(),
    1897             :             periodic_nodes.end(),
    1898        8658 :             [](const PeriodicNodeInfo & a, const PeriodicNodeInfo & b) -> bool
    1899        8658 :             { return a.second > b.second; });
    1900             : 
    1901             :   // build kd-tree
    1902             :   using KDTreeType = nanoflann::KDTreeSingleIndexAdaptor<
    1903             :       nanoflann::L2_Simple_Adaptor<Real, PointListAdaptor<PeriodicNodeInfo>, Real, std::size_t>,
    1904             :       PointListAdaptor<PeriodicNodeInfo>,
    1905             :       LIBMESH_DIM,
    1906             :       std::size_t>;
    1907          27 :   const unsigned int max_leaf_size = 20; // slightly affects runtime
    1908             :   auto point_list =
    1909          27 :       PointListAdaptor<PeriodicNodeInfo>(periodic_nodes.begin(), periodic_nodes.end());
    1910             :   auto kd_tree = std::make_unique<KDTreeType>(
    1911          27 :       LIBMESH_DIM, point_list, nanoflann::KDTreeSingleIndexAdaptorParams(max_leaf_size));
    1912             :   mooseAssert(kd_tree != nullptr, "KDTree was not properly initialized.");
    1913          27 :   kd_tree->buildIndex();
    1914             : 
    1915             :   // data structures for kd-tree search
    1916          27 :   nanoflann::SearchParameters search_params;
    1917          27 :   std::vector<nanoflann::ResultItem<std::size_t, Real>> ret_matches;
    1918             : 
    1919             :   // iterate over periodic nodes (boundary ids are in contiguous blocks)
    1920          27 :   libMesh::PeriodicBoundaryBase * periodic = nullptr;
    1921          27 :   BoundaryID current_bc_id = BoundaryInfo::invalid_id;
    1922        1575 :   for (auto & pair : periodic_nodes)
    1923             :   {
    1924             :     // entering a new block of boundary IDs
    1925        1548 :     if (pair.second != current_bc_id)
    1926             :     {
    1927         108 :       current_bc_id = pair.second;
    1928         108 :       periodic = pbs->boundary(current_bc_id);
    1929         108 :       if (periodic && !periodic->is_my_variable(var_number))
    1930           0 :         periodic = nullptr;
    1931             :     }
    1932             : 
    1933             :     // variable is not periodic at this node, skip
    1934        1548 :     if (!periodic)
    1935           0 :       continue;
    1936             : 
    1937             :     // clear result buffer
    1938        1548 :     ret_matches.clear();
    1939             : 
    1940             :     // id of the current node
    1941        1548 :     const auto id = pair.first->id();
    1942             : 
    1943             :     // position where we expect a periodic partner for the current node and boundary
    1944        1548 :     Point search_point = periodic->get_corresponding_pos(*pair.first);
    1945             : 
    1946             :     // search at the expected point
    1947        1548 :     kd_tree->radiusSearch(&(search_point)(0), libMesh::TOLERANCE, ret_matches, search_params);
    1948        4248 :     for (auto & match_pair : ret_matches)
    1949             :     {
    1950        2700 :       const auto & match = periodic_nodes[match_pair.first];
    1951             :       // add matched node if the boundary id is the corresponding id in the periodic pair
    1952        2700 :       if (match.second == periodic->pairedboundary)
    1953        1548 :         periodic_node_map.emplace(id, match.first->id());
    1954             :     }
    1955             :   }
    1956          27 : }
    1957             : 
    1958             : void
    1959           0 : MooseMesh::buildPeriodicNodeSets(std::map<BoundaryID, std::set<dof_id_type>> & periodic_node_sets,
    1960             :                                  unsigned int var_number,
    1961             :                                  libMesh::PeriodicBoundaries * pbs) const
    1962             : {
    1963           0 :   TIME_SECTION("buildPeriodicNodeSets", 5);
    1964             : 
    1965           0 :   periodic_node_sets.clear();
    1966             : 
    1967             :   // Loop over all the boundary nodes adding the periodic nodes to the appropriate set
    1968           0 :   for (const auto & t : getMesh().get_boundary_info().build_node_list())
    1969             :   {
    1970           0 :     auto node_id = std::get<0>(t);
    1971           0 :     auto bc_id = std::get<1>(t);
    1972             : 
    1973             :     // Is this current node on a known periodic boundary?
    1974           0 :     if (periodic_node_sets.find(bc_id) != periodic_node_sets.end())
    1975           0 :       periodic_node_sets[bc_id].insert(node_id);
    1976             :     else // This still might be a periodic node but we just haven't seen this boundary_id yet
    1977             :     {
    1978           0 :       const libMesh::PeriodicBoundaryBase * periodic = pbs->boundary(bc_id);
    1979           0 :       if (periodic && periodic->is_my_variable(var_number))
    1980           0 :         periodic_node_sets[bc_id].insert(node_id);
    1981             :     }
    1982           0 :   }
    1983           0 : }
    1984             : 
    1985             : bool
    1986       67286 : MooseMesh::detectOrthogonalDimRanges(Real tol)
    1987             : {
    1988      201858 :   TIME_SECTION("detectOrthogonalDimRanges", 5);
    1989             : 
    1990       67286 :   if (_regular_orthogonal_mesh)
    1991       34206 :     return true;
    1992             : 
    1993       33080 :   std::vector<Real> min(3, std::numeric_limits<Real>::max());
    1994       33080 :   std::vector<Real> max(3, std::numeric_limits<Real>::min());
    1995       33080 :   unsigned int dim = getMesh().mesh_dimension();
    1996             : 
    1997             :   // Find the bounding box of our mesh
    1998    10054751 :   for (const auto & node : getMesh().node_ptr_range())
    1999             :     // Check all coordinates, we don't know if this mesh might be lying in a higher dim even if the
    2000             :     // mesh dimension is lower.
    2001    40086684 :     for (const auto i : make_range(Moose::dim))
    2002             :     {
    2003    30065013 :       if ((*node)(i) < min[i])
    2004      202295 :         min[i] = (*node)(i);
    2005    30065013 :       if ((*node)(i) > max[i])
    2006      443829 :         max[i] = (*node)(i);
    2007       33080 :     }
    2008             : 
    2009       33080 :   this->comm().max(max);
    2010       33080 :   this->comm().min(min);
    2011             : 
    2012       33080 :   _extreme_nodes.resize(8); // 2^LIBMESH_DIM
    2013             :   // Now make sure that there are actual nodes at all of the extremes
    2014       33080 :   std::vector<bool> extreme_matches(8, false);
    2015       33080 :   std::vector<unsigned int> comp_map(3);
    2016    10054751 :   for (const auto & node : getMesh().node_ptr_range())
    2017             :   {
    2018             :     // See if the current node is located at one of the extremes
    2019    10021671 :     unsigned int coord_match = 0;
    2020             : 
    2021    40086684 :     for (const auto i : make_range(Moose::dim))
    2022             :     {
    2023    30065013 :       if (std::abs((*node)(i)-min[i]) < tol)
    2024             :       {
    2025     6290704 :         comp_map[i] = MIN;
    2026     6290704 :         ++coord_match;
    2027             :       }
    2028    23774309 :       else if (std::abs((*node)(i)-max[i]) < tol)
    2029             :       {
    2030     1361511 :         comp_map[i] = MAX;
    2031     1361511 :         ++coord_match;
    2032             :       }
    2033             :     }
    2034             : 
    2035    10021671 :     if (coord_match == LIBMESH_DIM) // Found a coordinate at one of the extremes
    2036             :     {
    2037      117259 :       _extreme_nodes[comp_map[X] * 4 + comp_map[Y] * 2 + comp_map[Z]] = node;
    2038      117259 :       extreme_matches[comp_map[X] * 4 + comp_map[Y] * 2 + comp_map[Z]] = true;
    2039             :     }
    2040       33080 :   }
    2041             : 
    2042             :   // See if we matched all of the extremes for the mesh dimension
    2043       33080 :   this->comm().max(extreme_matches);
    2044       33080 :   if (std::count(extreme_matches.begin(), extreme_matches.end(), true) == (1 << dim))
    2045       28929 :     _regular_orthogonal_mesh = true;
    2046             : 
    2047             :   // Set the bounds
    2048       33080 :   _bounds.resize(LIBMESH_DIM);
    2049      132320 :   for (const auto i : make_range(Moose::dim))
    2050             :   {
    2051       99240 :     _bounds[i].resize(2);
    2052       99240 :     _bounds[i][MIN] = min[i];
    2053       99240 :     _bounds[i][MAX] = max[i];
    2054             :   }
    2055             : 
    2056       33080 :   return _regular_orthogonal_mesh;
    2057       67286 : }
    2058             : 
    2059             : void
    2060         538 : MooseMesh::detectPairedSidesets()
    2061             : {
    2062        1614 :   TIME_SECTION("detectPairedSidesets", 5);
    2063             : 
    2064         538 :   _paired_boundary = std::vector<std::pair<BoundaryID, BoundaryID>>();
    2065             : 
    2066             :   // Loop over level-0 elements (since boundary condition information
    2067             :   // is only directly stored for them) and find sidesets with normals
    2068             :   // that point in the -x, +x, -y, +y, and -z, +z direction.  If there
    2069             :   // is a unique sideset id for each direction, then the paired
    2070             :   // sidesets consist of (-x,+x), (-y,+y), (-z,+z).  If there are
    2071             :   // multiple sideset ids for a given direction, then we can't pick a
    2072             :   // single pair for that direction.  In that case, we'll just return
    2073             :   // as was done in the original algorithm.
    2074             : 
    2075             :   // we need to test all element dimensions from dim down to 1
    2076         538 :   const unsigned int mesh_dim = getMesh().mesh_dimension();
    2077             : 
    2078             :   // Helper for iterating through unit dimensions (0=x, 1=y, 2=z)
    2079             :   static constexpr std::array<std::size_t, 3> unit_dims{0, 1, 2};
    2080             :   // Helper for mapping from unit dim -> name
    2081        1614 :   static const std::array<std::string, 3> unit_dim_names{"x", "y", "z"};
    2082             : 
    2083             :   // Boundary id sets for elements of different dimensions
    2084             :   // First index: side dimension; 0=1D, 1=2D, 2=3D
    2085             :   // Second index: unit dimension; 0=x, 1=y, 2=z
    2086             :   // Third index: false for minus, true for plus
    2087       16678 :   std::array<std::array<std::array<std::set<BoundaryID>, 2>, 3>, 3> ids{};
    2088             : 
    2089             :   // Build quadrature needed to evaluate side normals
    2090         538 :   std::array<std::unique_ptr<FEBase>, 3> fe_faces{};
    2091         538 :   std::array<std::unique_ptr<libMesh::QGauss>, 3> qfaces{};
    2092        1596 :   for (const auto side_dim : make_range(mesh_dim))
    2093             :   {
    2094             :     // Face is assumed to be flat, therefore normal is assumed to be
    2095             :     // constant over the face, therefore only compute it at 1 qp.
    2096        1058 :     qfaces[side_dim] = std::unique_ptr<libMesh::QGauss>(new libMesh::QGauss(side_dim, CONSTANT));
    2097             : 
    2098             :     // A first-order Lagrange FE for the face.
    2099        1058 :     fe_faces[side_dim] = FEBase::build(side_dim + 1, FEType(FIRST, libMesh::LAGRANGE));
    2100        1058 :     fe_faces[side_dim]->attach_quadrature_rule(qfaces[side_dim].get());
    2101        1058 :     fe_faces[side_dim]->get_normals();
    2102             :   }
    2103             : 
    2104             :   // Get boundary IDs for each dimension that are in the unit normal
    2105         538 :   const auto & boundary_info = getMesh().get_boundary_info();
    2106             :   // Temporary for evaluating boundary_ids
    2107         538 :   std::vector<boundary_id_type> face_ids;
    2108             :   // The side dimensions we've come across, so that we only report
    2109             :   // warnings for side dimensions that we have
    2110         538 :   std::set<unsigned int> side_dims;
    2111             :   // Normal dimensions that we found that were nonzero; lets us
    2112             :   // skip warnings for dimensions that we don't have
    2113         538 :   std::array<bool, 3> nonzero_dims = periodic_dim_default;
    2114      336871 :   for (auto & elem : as_range(getMesh().level_elements_begin(0), getMesh().level_elements_end(0)))
    2115             :   {
    2116             :     // If not on the boundary, nothing to do
    2117      336333 :     if (!elem->on_boundary())
    2118      286656 :       continue;
    2119             : 
    2120       49677 :     const auto side_dim = elem->dim() - 1;
    2121       49677 :     side_dims.insert(side_dim);
    2122             : 
    2123             :     // Check for unit normals on each boundary side
    2124      276146 :     for (const auto s : elem->side_index_range())
    2125      226469 :       if (!elem->neighbor_ptr(s))
    2126             :       {
    2127             :         // Reinit to get the normal
    2128       55092 :         fe_faces[side_dim]->reinit(elem, s);
    2129       55092 :         const auto & normal = fe_faces[side_dim]->get_normals()[0];
    2130             : 
    2131             :         // Get the boundary ID(s) for this side.  If there is more
    2132             :         // than 1 boundary id, then we already can't determine a
    2133             :         // unique pairing of sides in this direction, but we'll just
    2134             :         // keep going to keep the logic simple.
    2135       55092 :         boundary_info.boundary_ids(elem, s, face_ids);
    2136             : 
    2137       55092 :         bool found = false;
    2138      220368 :         for (const auto unit_dim : unit_dims)
    2139             :         {
    2140      165276 :           if (libMesh::absolute_fuzzy_equals(normal(unit_dim), 0.0))
    2141      108284 :             continue;
    2142       56992 :           nonzero_dims[unit_dim] = true;
    2143       56992 :           if (!found)
    2144       91312 :             for (const auto plus : {false, true})
    2145             :             {
    2146       87512 :               if (libMesh::absolute_fuzzy_equals(normal(unit_dim), plus ? 1.0 : -1.0))
    2147             :               {
    2148       53192 :                 ids[side_dim][unit_dim][plus].insert(face_ids.begin(), face_ids.end());
    2149       53192 :                 found = true;
    2150       53192 :                 break;
    2151             :               }
    2152             :             }
    2153             :         }
    2154             :       }
    2155         538 :   }
    2156             : 
    2157             :   // For a distributed mesh, boundaries may be distributed as well. We therefore collect information
    2158             :   // from everyone. If the mesh is already serial, then there is no need to do an allgather. Note
    2159             :   // that this is just going to gather information about what the periodic bc ids are. We are not
    2160             :   // gathering any remote elements or anything like that. It's just that the GhostPointNeighbors
    2161             :   // ghosting functor currently relies on the fact that every process agrees on whether we have
    2162             :   // periodic boundaries; every process that thinks there are periodic boundaries will call
    2163             :   // MeshBase::sub_point_locator which makes a parallel_object_only() assertion (right or wrong). So
    2164             :   // we all need to go there (or not go there)
    2165         538 :   if (_use_distributed_mesh && !_mesh->is_serial())
    2166             :   {
    2167             :     // Communicate id data by packing as [side dim, unit dim, plus (as a char), boundary id]
    2168         122 :     std::vector<std::tuple<unsigned int, unsigned int, unsigned char, boundary_id_type>> id_data;
    2169         244 :     for (const auto side_dim : side_dims)
    2170         488 :       for (const auto unit_dim : unit_dims)
    2171        1098 :         for (const auto plus : {false, true})
    2172        1192 :           for (const auto bd : ids[side_dim][unit_dim][plus])
    2173         460 :             id_data.emplace_back(side_dim, unit_dim, plus, bd);
    2174         122 :     _communicator.allgather(id_data, false);
    2175        1170 :     for (const auto & [side_dim, unit_dim, plus_char, bd] : id_data)
    2176        1048 :       ids[side_dim][unit_dim][bool(plus_char)].insert(bd);
    2177             : 
    2178             :     // Gather true-ness of nonzero_dims
    2179         488 :     for (auto & entry : nonzero_dims)
    2180         366 :       _communicator.max(entry);
    2181             : 
    2182             :     // Gather found side dimensions
    2183         122 :     _communicator.set_union(side_dims);
    2184         122 :   } // end if (_use_distributed_mesh && !_need_ghost_ghosted_boundaries)
    2185             : 
    2186             :   // Find pairings that have exactly one boundary on each side
    2187         538 :   std::ostringstream oss_found, oss_missing;
    2188        1076 :   for (const auto side_dim : side_dims)
    2189             :   {
    2190        2152 :     for (const auto unit_dim : unit_dims)
    2191        1614 :       if (nonzero_dims[unit_dim])
    2192             :       {
    2193        1061 :         const auto & unit_name = unit_dim_names[unit_dim];
    2194        1061 :         const auto & minus = ids[side_dim][unit_dim][false];
    2195        1061 :         const auto & plus = ids[side_dim][unit_dim][true];
    2196             : 
    2197        1061 :         if (minus.size() == 1 && plus.size() == 1)
    2198             :         {
    2199        1904 :           const auto get_boundary_name = [this](const auto id)
    2200             :           {
    2201        1904 :             const auto & name = getBoundaryName(id);
    2202        1904 :             return name.size() ? name : std::to_string(id);
    2203         952 :           };
    2204             : 
    2205         952 :           oss_found << "\n  " << side_dim + 1 << "D " << unit_name
    2206         952 :                     << "-direction: " << get_boundary_name(*minus.begin()) << " <-> "
    2207        1904 :                     << get_boundary_name(*plus.begin());
    2208         952 :           _paired_boundary->emplace_back(std::make_pair(*minus.begin(), *plus.begin()));
    2209             :         }
    2210             :         else
    2211         109 :           oss_missing << "\n  " << side_dim + 1 << "D -" << unit_name << "/+" << unit_name
    2212         109 :                       << ": Found " << minus.size() << " -" << unit_name << " boundaries and "
    2213         109 :                       << plus.size() << " +" << unit_name << " boundaries";
    2214             :       }
    2215             :   }
    2216             : 
    2217         538 :   std::ostringstream oss;
    2218         538 :   const auto found = oss_found.str();
    2219         538 :   const auto missing = oss_missing.str();
    2220         538 :   if (found.size())
    2221             :     oss << "The following paired boundaries were automatically detected for periodicity:\n"
    2222         504 :         << found << "\n";
    2223         538 :   if (missing.size())
    2224             :   {
    2225          75 :     if (found.size())
    2226          41 :       oss << "\n";
    2227             :     oss << "Paired boundaries were not automatically detected for the following:\n"
    2228             :         << missing
    2229             :         << "\n\nAutomatic detection requires that exactly one boundary is found in each unit "
    2230          75 :            "direction.\n";
    2231             :   }
    2232             : 
    2233         538 :   mooseInfoRepeated(oss.str());
    2234         538 : }
    2235             : 
    2236             : Real
    2237       72624 : MooseMesh::dimensionWidth(unsigned int component) const
    2238             : {
    2239       72624 :   return getMaxInDimension(component) - getMinInDimension(component);
    2240             : }
    2241             : 
    2242             : Real
    2243       33235 : MooseMesh::getMinInDimension(unsigned int component) const
    2244             : {
    2245             :   mooseAssert(_mesh, "The MeshBase has not been constructed");
    2246             :   mooseAssert(component < _bounds.size(), "Requested dimension out of bounds");
    2247             : 
    2248       33235 :   return _bounds[component][MIN];
    2249             : }
    2250             : 
    2251             : Real
    2252       33235 : MooseMesh::getMaxInDimension(unsigned int component) const
    2253             : {
    2254             :   mooseAssert(_mesh, "The MeshBase has not been constructed");
    2255             :   mooseAssert(component < _bounds.size(), "Requested dimension out of bounds");
    2256             : 
    2257       33235 :   return _bounds[component][MAX];
    2258             : }
    2259             : 
    2260             : void
    2261         873 : MooseMesh::addPeriodicVariable(const unsigned int sys_num,
    2262             :                                const unsigned int var_num,
    2263             :                                const BoundaryID primary,
    2264             :                                const BoundaryID secondary)
    2265             : {
    2266         873 :   if (!_regular_orthogonal_mesh)
    2267           0 :     return;
    2268             : 
    2269         873 :   const auto key = std::make_pair(sys_num, var_num);
    2270         873 :   auto & entry = _periodic_dim.try_emplace(key, periodic_dim_default).first->second;
    2271             : 
    2272         873 :   _half_range = Point(dimensionWidth(0) / 2.0, dimensionWidth(1) / 2.0, dimensionWidth(2) / 2.0);
    2273             : 
    2274         873 :   bool component_found = false;
    2275        2707 :   for (const auto component : make_range(dimension()))
    2276             :   {
    2277        1834 :     const std::pair<BoundaryID, BoundaryID> * boundary_ids = getPairedBoundaryMapping(component);
    2278             : 
    2279        1834 :     if (boundary_ids && ((boundary_ids->first == primary && boundary_ids->second == secondary) ||
    2280         976 :                          (boundary_ids->first == secondary && boundary_ids->second == primary)))
    2281             :     {
    2282         858 :       entry[component] = true;
    2283         858 :       component_found = true;
    2284             :     }
    2285             :   }
    2286             : 
    2287         873 :   if (!component_found)
    2288          30 :     mooseWarning("Could not find a match between boundary '",
    2289          15 :                  getBoundaryName(primary),
    2290             :                  "' and '",
    2291          15 :                  getBoundaryName(secondary),
    2292             :                  "' to set periodic boundary conditions for variable (index:",
    2293             :                  var_num,
    2294             :                  ") in either the X, Y or Z direction. The periodic dimension of the mesh for this "
    2295             :                  "variable will not be stored.");
    2296             : }
    2297             : 
    2298             : const std::array<bool, 3> &
    2299     5074173 : MooseMesh::queryPeriodicDimensions(const unsigned int sys_num, const unsigned int var_num) const
    2300             : {
    2301     5074173 :   const auto key = std::make_pair(sys_num, var_num);
    2302     5074173 :   if (const auto it = _periodic_dim.find(key); it != _periodic_dim.end())
    2303     4970489 :     return it->second;
    2304      103684 :   return periodic_dim_default;
    2305             : }
    2306             : 
    2307             : const std::array<bool, 3> &
    2308          27 : MooseMesh::queryPeriodicDimensions(const MooseVariableBase & var) const
    2309             : {
    2310          27 :   return queryPeriodicDimensions(var.sys().number(), var.number());
    2311             : }
    2312             : 
    2313             : bool
    2314           0 : MooseMesh::isTranslatedPeriodic(const unsigned int sys_num,
    2315             :                                 const unsigned int var_num,
    2316             :                                 const unsigned int component) const
    2317             : {
    2318             :   mooseAssert(component < dimension(), "Requested dimension out of bounds");
    2319           0 :   return queryPeriodicDimensions(sys_num, var_num)[component];
    2320             : }
    2321             : 
    2322             : bool
    2323           0 : MooseMesh::isTranslatedPeriodic(const MooseVariableBase & var, const unsigned int component) const
    2324             : {
    2325           0 :   return isTranslatedPeriodic(var.sys().number(), var.number(), component);
    2326             : }
    2327             : 
    2328             : bool
    2329           0 : MooseMesh::isTranslatedPeriodic(const unsigned int var_num, const unsigned int component) const
    2330             : {
    2331           0 :   mooseDoOnce(mooseDeprecated(
    2332             :       "MooseMesh::isTranslatedPeriodic(const unsigned int, const unsigned int) is deprecated. Use "
    2333             :       "the method that additionally takes the system number or the MooseVariableBase instead."));
    2334           0 :   return isTranslatedPeriodic(0, var_num, component);
    2335             : }
    2336             : 
    2337             : RealVectorValue
    2338     5074146 : MooseMesh::minPeriodicVector(const unsigned int sys_num,
    2339             :                              const unsigned int var_num,
    2340             :                              Point p,
    2341             :                              Point q) const
    2342             : {
    2343     5074146 :   const auto & periodic_dims = queryPeriodicDimensions(sys_num, var_num);
    2344             : 
    2345    15157394 :   for (const auto i : make_range(dimension()))
    2346             :   {
    2347             :     // check to see if we're closer in real or periodic space in x, y, and z
    2348    10083248 :     if (periodic_dims[i])
    2349             :     {
    2350             :       // Need to test order before differencing
    2351     9875880 :       if (p(i) > q(i))
    2352             :       {
    2353     6389720 :         if (p(i) - q(i) > _half_range(i))
    2354     2344164 :           p(i) -= _half_range(i) * 2;
    2355             :       }
    2356             :       else
    2357             :       {
    2358     3486160 :         if (q(i) - p(i) > _half_range(i))
    2359      925956 :           p(i) += _half_range(i) * 2;
    2360             :       }
    2361             :     }
    2362             :   }
    2363             : 
    2364     5074146 :   return q - p;
    2365             : }
    2366             : 
    2367             : RealVectorValue
    2368           0 : MooseMesh::minPeriodicVector(const MooseVariableBase & var, const Point & p, const Point & q) const
    2369             : {
    2370           0 :   return minPeriodicVector(var.sys().number(), var.number(), p, q);
    2371             : }
    2372             : 
    2373             : RealVectorValue
    2374           0 : MooseMesh::minPeriodicVector(const unsigned int var_num, const Point & p, const Point & q) const
    2375             : {
    2376           0 :   mooseDoOnce(mooseDeprecated("MooseMesh::minPeriodicVector(const unsigned int, const Point &, "
    2377             :                               "const Point &) is deprecated. Use the method that additionally "
    2378             :                               "takes the system number or the MooseVariableBase instead."));
    2379           0 :   return minPeriodicVector(0, var_num, p, q);
    2380             : }
    2381             : 
    2382             : Real
    2383     5074146 : MooseMesh::minPeriodicDistance(const unsigned int sys_num,
    2384             :                                const unsigned int var_num,
    2385             :                                const Point & p,
    2386             :                                const Point & q) const
    2387             : {
    2388     5074146 :   return minPeriodicVector(sys_num, var_num, p, q).norm();
    2389             : }
    2390             : 
    2391             : Real
    2392       25600 : MooseMesh::minPeriodicDistance(const MooseVariableBase & var,
    2393             :                                const Point & p,
    2394             :                                const Point & q) const
    2395             : {
    2396       25600 :   return minPeriodicDistance(var.sys().number(), var.number(), p, q);
    2397             : }
    2398             : 
    2399             : Real
    2400           0 : MooseMesh::minPeriodicDistance(const unsigned int var_num, const Point & p, const Point & q) const
    2401             : {
    2402           0 :   mooseDoOnce(mooseDeprecated("MooseMesh::minPeriodicDistance(const unsigned int, const Point &, "
    2403             :                               "const Point &) is deprecated. Use the method that additionally "
    2404             :                               "takes the system number or the MooseVariableBase instead."));
    2405           0 :   return minPeriodicDistance(0, var_num, p, q);
    2406             : }
    2407             : 
    2408             : const std::pair<BoundaryID, BoundaryID> *
    2409        2383 : MooseMesh::getPairedBoundaryMapping(unsigned int component) const
    2410             : {
    2411        2383 :   if (!_regular_orthogonal_mesh)
    2412           0 :     mooseError("Trying to retrieve automatic paired mapping for a mesh that is not regular and "
    2413             :                "orthogonal");
    2414             : 
    2415             :   mooseAssert(component < dimension(), "Requested dimension out of bounds");
    2416             : 
    2417        2383 :   if (!hasDetectedPairedSidesets())
    2418           0 :     mooseError("MooseMesh::getPairedBoundaryMapping(): Paired boundaries not built; must call "
    2419             :                "detectPairedSidesets() first");
    2420             : 
    2421        2383 :   if (component < _paired_boundary->size())
    2422        2380 :     return &(*_paired_boundary)[component];
    2423             :   else
    2424           3 :     return nullptr;
    2425             : }
    2426             : 
    2427             : void
    2428          33 : MooseMesh::buildHRefinementAndCoarseningMaps(Assembly * const assembly)
    2429             : {
    2430          33 :   std::map<ElemType, Elem *> canonical_elems;
    2431             : 
    2432             :   // First, loop over all elements and find a canonical element for each type
    2433             :   // Doing it this way guarantees that this is going to work in parallel
    2434       19937 :   for (const auto & elem : getMesh().element_ptr_range()) // TODO: Thread this
    2435             :   {
    2436        9952 :     ElemType type = elem->type();
    2437             : 
    2438        9952 :     if (canonical_elems.find(type) ==
    2439       19904 :         canonical_elems.end()) // If we haven't seen this type of elem before save it
    2440          42 :       canonical_elems[type] = elem;
    2441             :     else
    2442             :     {
    2443        9910 :       Elem * stored = canonical_elems[type];
    2444        9910 :       if (elem->id() < stored->id()) // Arbitrarily keep the one with a lower id
    2445           0 :         canonical_elems[type] = elem;
    2446             :     }
    2447          33 :   }
    2448             :   // Now build the maps using these templates
    2449             :   // Note: This MUST be done NOT threaded!
    2450          75 :   for (const auto & can_it : canonical_elems)
    2451             :   {
    2452          42 :     Elem * elem = can_it.second;
    2453             : 
    2454             :     // Need to do this just once to get the right qrules put in place
    2455          42 :     assembly->setCurrentSubdomainID(elem->subdomain_id());
    2456          42 :     assembly->reinit(elem);
    2457          42 :     assembly->reinit(elem, 0);
    2458          42 :     auto && qrule = assembly->writeableQRule();
    2459          42 :     auto && qrule_face = assembly->writeableQRuleFace();
    2460             : 
    2461             :     // Volume to volume projection for refinement
    2462          42 :     buildRefinementMap(*elem, *qrule, *qrule_face, -1, -1, -1);
    2463             : 
    2464             :     // Volume to volume projection for coarsening
    2465          42 :     buildCoarseningMap(*elem, *qrule, *qrule_face, -1);
    2466             : 
    2467             :     // Map the sides of children
    2468         216 :     for (unsigned int side = 0; side < elem->n_sides(); side++)
    2469             :     {
    2470             :       // Side to side for sides that match parent's sides
    2471         174 :       buildRefinementMap(*elem, *qrule, *qrule_face, side, -1, side);
    2472         174 :       buildCoarseningMap(*elem, *qrule, *qrule_face, side);
    2473             :     }
    2474             : 
    2475             :     // Child side to parent volume mapping for "internal" child sides
    2476         240 :     for (unsigned int child = 0; child < elem->n_children(); ++child)
    2477        1146 :       for (unsigned int side = 0; side < elem->n_sides();
    2478             :            ++side)                                // Assume children have the same number of sides!
    2479         948 :         if (!elem->is_child_on_side(child, side)) // Otherwise we already computed that map
    2480         474 :           buildRefinementMap(*elem, *qrule, *qrule_face, -1, child, side);
    2481             :   }
    2482          33 : }
    2483             : 
    2484             : void
    2485          90 : MooseMesh::buildPRefinementAndCoarseningMaps(Assembly * const assembly)
    2486             : {
    2487          90 :   _elem_type_to_p_refinement_map.clear();
    2488          90 :   _elem_type_to_p_refinement_side_map.clear();
    2489          90 :   _elem_type_to_p_coarsening_map.clear();
    2490          90 :   _elem_type_to_p_coarsening_side_map.clear();
    2491             : 
    2492          90 :   std::map<ElemType, std::pair<Elem *, unsigned int>> elems_and_max_p_level;
    2493             : 
    2494       32218 :   for (const auto & elem : getMesh().active_element_ptr_range())
    2495             :   {
    2496       32128 :     const auto type = elem->type();
    2497       32128 :     auto & [picked_elem, max_p_level] = elems_and_max_p_level[type];
    2498       32128 :     if (!picked_elem)
    2499          90 :       picked_elem = elem;
    2500       32128 :     max_p_level = std::max(max_p_level, elem->p_level());
    2501          90 :   }
    2502             : 
    2503             :   // The only requirement on the FEType is that it can be arbitrarily p-refined
    2504          90 :   const FEType p_refinable_fe_type(CONSTANT, libMesh::MONOMIAL);
    2505          90 :   std::vector<Point> volume_ref_points_coarse, volume_ref_points_fine, face_ref_points_coarse,
    2506          90 :       face_ref_points_fine;
    2507          90 :   std::vector<unsigned int> p_levels;
    2508             : 
    2509         180 :   for (auto & [elem_type, elem_p_level_pair] : elems_and_max_p_level)
    2510             :   {
    2511          90 :     auto & [moose_elem, max_p_level] = elem_p_level_pair;
    2512          90 :     const auto dim = moose_elem->dim();
    2513             :     // Need to do this just once to get the right qrules put in place
    2514          90 :     assembly->setCurrentSubdomainID(moose_elem->subdomain_id());
    2515          90 :     assembly->reinit(moose_elem);
    2516          90 :     assembly->reinit(moose_elem, 0);
    2517          90 :     auto & qrule = assembly->writeableQRule();
    2518          90 :     auto & qrule_face = assembly->writeableQRuleFace();
    2519             : 
    2520          90 :     libMesh::Parallel::Communicator self_comm{};
    2521          90 :     ReplicatedMesh mesh(self_comm);
    2522          90 :     mesh.set_mesh_dimension(dim);
    2523         630 :     for (const auto & nd : moose_elem->node_ref_range())
    2524         540 :       mesh.add_point(nd);
    2525             : 
    2526          90 :     Elem * const elem = mesh.add_elem(Elem::build(elem_type).release());
    2527         630 :     for (const auto i : elem->node_index_range())
    2528         540 :       elem->set_node(i, mesh.node_ptr(i));
    2529             : 
    2530          90 :     std::unique_ptr<FEBase> fe_face(FEBase::build(dim, p_refinable_fe_type));
    2531          90 :     fe_face->get_phi();
    2532          90 :     const auto & face_phys_points = fe_face->get_xyz();
    2533          90 :     fe_face->attach_quadrature_rule(qrule_face);
    2534             : 
    2535          90 :     qrule->init(*elem);
    2536          90 :     volume_ref_points_coarse = qrule->get_points();
    2537          90 :     fe_face->reinit(elem, (unsigned int)0);
    2538          90 :     libMesh::FEMap::inverse_map(dim, elem, face_phys_points, face_ref_points_coarse);
    2539             : 
    2540          90 :     p_levels.resize(max_p_level + 1);
    2541          90 :     std::iota(p_levels.begin(), p_levels.end(), 0);
    2542          90 :     libMesh::MeshRefinement mesh_refinement(mesh);
    2543             : 
    2544         306 :     for (const auto p_level : p_levels)
    2545             :     {
    2546         216 :       mesh_refinement.uniformly_p_refine(1);
    2547         216 :       qrule->init(*elem);
    2548         216 :       volume_ref_points_fine = qrule->get_points();
    2549         216 :       fe_face->reinit(elem, (unsigned int)0);
    2550         216 :       libMesh::FEMap::inverse_map(dim, elem, face_phys_points, face_ref_points_fine);
    2551             : 
    2552         216 :       const auto map_key = std::make_pair(elem_type, p_level);
    2553         216 :       auto & volume_refine_map = _elem_type_to_p_refinement_map[map_key];
    2554         216 :       auto & face_refine_map = _elem_type_to_p_refinement_side_map[map_key];
    2555         216 :       auto & volume_coarsen_map = _elem_type_to_p_coarsening_map[map_key];
    2556         216 :       auto & face_coarsen_map = _elem_type_to_p_coarsening_side_map[map_key];
    2557             : 
    2558         432 :       auto fill_maps = [this](const auto & coarse_ref_points,
    2559             :                               const auto & fine_ref_points,
    2560             :                               auto & coarsen_map,
    2561             :                               auto & refine_map)
    2562             :       {
    2563         432 :         mapPoints(fine_ref_points, coarse_ref_points, refine_map);
    2564         432 :         mapPoints(coarse_ref_points, fine_ref_points, coarsen_map);
    2565         648 :       };
    2566             : 
    2567         216 :       fill_maps(
    2568             :           volume_ref_points_coarse, volume_ref_points_fine, volume_coarsen_map, volume_refine_map);
    2569         216 :       fill_maps(face_ref_points_coarse, face_ref_points_fine, face_coarsen_map, face_refine_map);
    2570             : 
    2571             :       // With this level's maps filled our fine points now become our coarse points
    2572         216 :       volume_ref_points_fine.swap(volume_ref_points_coarse);
    2573         216 :       face_ref_points_fine.swap(face_ref_points_coarse);
    2574             :     }
    2575          90 :   }
    2576          90 : }
    2577             : 
    2578             : void
    2579          57 : MooseMesh::buildRefinementAndCoarseningMaps(Assembly * const assembly)
    2580             : {
    2581         285 :   TIME_SECTION("buildRefinementAndCoarseningMaps", 5, "Building Refinement And Coarsening Maps");
    2582          57 :   if (doingPRefinement())
    2583          24 :     buildPRefinementAndCoarseningMaps(assembly);
    2584             :   else
    2585          33 :     buildHRefinementAndCoarseningMaps(assembly);
    2586          57 : }
    2587             : 
    2588             : void
    2589         690 : MooseMesh::buildRefinementMap(const Elem & elem,
    2590             :                               QBase & qrule,
    2591             :                               QBase & qrule_face,
    2592             :                               int parent_side,
    2593             :                               int child,
    2594             :                               int child_side)
    2595             : {
    2596        3450 :   TIME_SECTION("buildRefinementMap", 5, "Building Refinement Map");
    2597             : 
    2598         690 :   if (child == -1) // Doing volume mapping or parent side mapping
    2599             :   {
    2600             :     mooseAssert(parent_side == child_side,
    2601             :                 "Parent side must match child_side if not passing a specific child!");
    2602             : 
    2603         216 :     std::pair<int, ElemType> the_pair(parent_side, elem.type());
    2604             : 
    2605         216 :     if (_elem_type_to_refinement_map.find(the_pair) != _elem_type_to_refinement_map.end())
    2606           0 :       mooseError("Already built a qp refinement map!");
    2607             : 
    2608         216 :     std::vector<std::pair<unsigned int, QpMap>> coarsen_map;
    2609         216 :     std::vector<std::vector<QpMap>> & refinement_map = _elem_type_to_refinement_map[the_pair];
    2610         216 :     findAdaptivityQpMaps(
    2611             :         &elem, qrule, qrule_face, refinement_map, coarsen_map, parent_side, child, child_side);
    2612         216 :   }
    2613             :   else // Need to map a child side to parent volume qps
    2614             :   {
    2615         474 :     std::pair<int, int> child_pair(child, child_side);
    2616             : 
    2617         474 :     if (_elem_type_to_child_side_refinement_map.find(elem.type()) !=
    2618        1380 :             _elem_type_to_child_side_refinement_map.end() &&
    2619         432 :         _elem_type_to_child_side_refinement_map[elem.type()].find(child_pair) !=
    2620         906 :             _elem_type_to_child_side_refinement_map[elem.type()].end())
    2621           0 :       mooseError("Already built a qp refinement map!");
    2622             : 
    2623         474 :     std::vector<std::pair<unsigned int, QpMap>> coarsen_map;
    2624             :     std::vector<std::vector<QpMap>> & refinement_map =
    2625         474 :         _elem_type_to_child_side_refinement_map[elem.type()][child_pair];
    2626         474 :     findAdaptivityQpMaps(
    2627             :         &elem, qrule, qrule_face, refinement_map, coarsen_map, parent_side, child, child_side);
    2628         474 :   }
    2629         690 : }
    2630             : 
    2631             : const std::vector<std::vector<QpMap>> &
    2632        3422 : MooseMesh::getRefinementMap(const Elem & elem, int parent_side, int child, int child_side)
    2633             : {
    2634        3422 :   if (child == -1) // Doing volume mapping or parent side mapping
    2635             :   {
    2636             :     mooseAssert(parent_side == child_side,
    2637             :                 "Parent side must match child_side if not passing a specific child!");
    2638             : 
    2639        3422 :     std::pair<int, ElemType> the_pair(parent_side, elem.type());
    2640             : 
    2641        3422 :     if (_elem_type_to_refinement_map.find(the_pair) == _elem_type_to_refinement_map.end())
    2642           0 :       mooseError("Could not find a suitable qp refinement map!");
    2643             : 
    2644        3422 :     return _elem_type_to_refinement_map[the_pair];
    2645             :   }
    2646             :   else // Need to map a child side to parent volume qps
    2647             :   {
    2648           0 :     std::pair<int, int> child_pair(child, child_side);
    2649             : 
    2650           0 :     if (_elem_type_to_child_side_refinement_map.find(elem.type()) ==
    2651           0 :             _elem_type_to_child_side_refinement_map.end() ||
    2652           0 :         _elem_type_to_child_side_refinement_map[elem.type()].find(child_pair) ==
    2653           0 :             _elem_type_to_child_side_refinement_map[elem.type()].end())
    2654           0 :       mooseError("Could not find a suitable qp refinement map!");
    2655             : 
    2656           0 :     return _elem_type_to_child_side_refinement_map[elem.type()][child_pair];
    2657             :   }
    2658             : 
    2659             :   /**
    2660             :    *  TODO: When running with parallel mesh + stateful adaptivty we will need to make sure that each
    2661             :    *  processor has a complete map.  This may require parallel communication.  This is likely to
    2662             :    * happen
    2663             :    *  when running on a mixed element mesh.
    2664             :    */
    2665             : }
    2666             : 
    2667             : void
    2668         216 : MooseMesh::buildCoarseningMap(const Elem & elem, QBase & qrule, QBase & qrule_face, int input_side)
    2669             : {
    2670        1080 :   TIME_SECTION("buildCoarseningMap", 5, "Building Coarsening Map");
    2671             : 
    2672         216 :   std::pair<int, ElemType> the_pair(input_side, elem.type());
    2673             : 
    2674         216 :   if (_elem_type_to_coarsening_map.find(the_pair) != _elem_type_to_coarsening_map.end())
    2675           0 :     mooseError("Already built a qp coarsening map!");
    2676             : 
    2677         216 :   std::vector<std::vector<QpMap>> refinement_map;
    2678             :   std::vector<std::pair<unsigned int, QpMap>> & coarsen_map =
    2679         216 :       _elem_type_to_coarsening_map[the_pair];
    2680             : 
    2681             :   // The -1 here is for a specific child.  We don't do that for coarsening maps
    2682             :   // Also note that we're always mapping the same side to the same side (which is guaranteed by
    2683             :   // libMesh).
    2684         216 :   findAdaptivityQpMaps(
    2685             :       &elem, qrule, qrule_face, refinement_map, coarsen_map, input_side, -1, input_side);
    2686             : 
    2687             :   /**
    2688             :    *  TODO: When running with parallel mesh + stateful adaptivty we will need to make sure that each
    2689             :    *  processor has a complete map.  This may require parallel communication.  This is likely to
    2690             :    * happen
    2691             :    *  when running on a mixed element mesh.
    2692             :    */
    2693         216 : }
    2694             : 
    2695             : const std::vector<std::pair<unsigned int, QpMap>> &
    2696        1288 : MooseMesh::getCoarseningMap(const Elem & elem, int input_side)
    2697             : {
    2698        1288 :   std::pair<int, ElemType> the_pair(input_side, elem.type());
    2699             : 
    2700        1288 :   if (_elem_type_to_coarsening_map.find(the_pair) == _elem_type_to_coarsening_map.end())
    2701           0 :     mooseError("Could not find a suitable qp refinement map!");
    2702             : 
    2703        2576 :   return _elem_type_to_coarsening_map[the_pair];
    2704             : }
    2705             : 
    2706             : void
    2707        7038 : MooseMesh::mapPoints(const std::vector<Point> & from,
    2708             :                      const std::vector<Point> & to,
    2709             :                      std::vector<QpMap> & qp_map)
    2710             : {
    2711        7038 :   unsigned int n_from = from.size();
    2712        7038 :   unsigned int n_to = to.size();
    2713             : 
    2714        7038 :   qp_map.resize(n_from);
    2715             : 
    2716       61340 :   for (unsigned int i = 0; i < n_from; ++i)
    2717             :   {
    2718       54302 :     const Point & from_point = from[i];
    2719             : 
    2720       54302 :     QpMap & current_map = qp_map[i];
    2721             : 
    2722     1247054 :     for (unsigned int j = 0; j < n_to; ++j)
    2723             :     {
    2724     1192752 :       const Point & to_point = to[j];
    2725     1192752 :       Real distance = (from_point - to_point).norm();
    2726             : 
    2727     1192752 :       if (distance < current_map._distance)
    2728             :       {
    2729      167558 :         current_map._distance = distance;
    2730      167558 :         current_map._from = i;
    2731      167558 :         current_map._to = j;
    2732             :       }
    2733             :     }
    2734             :   }
    2735        7038 : }
    2736             : 
    2737             : void
    2738         906 : MooseMesh::findAdaptivityQpMaps(const Elem * template_elem,
    2739             :                                 QBase & qrule,
    2740             :                                 QBase & qrule_face,
    2741             :                                 std::vector<std::vector<QpMap>> & refinement_map,
    2742             :                                 std::vector<std::pair<unsigned int, QpMap>> & coarsen_map,
    2743             :                                 int parent_side,
    2744             :                                 int child,
    2745             :                                 int child_side)
    2746             : {
    2747        2718 :   TIME_SECTION("findAdaptivityQpMaps", 5);
    2748             : 
    2749         906 :   ReplicatedMesh mesh(_communicator);
    2750         906 :   mesh.skip_partitioning(true);
    2751             : 
    2752         906 :   unsigned int dim = template_elem->dim();
    2753         906 :   mesh.set_mesh_dimension(dim);
    2754             : 
    2755        7092 :   for (unsigned int i = 0; i < template_elem->n_nodes(); ++i)
    2756        6186 :     mesh.add_point(template_elem->point(i));
    2757             : 
    2758         906 :   Elem * elem = mesh.add_elem(Elem::build(template_elem->type()).release());
    2759             : 
    2760        7092 :   for (unsigned int i = 0; i < template_elem->n_nodes(); ++i)
    2761        6186 :     elem->set_node(i, mesh.node_ptr(i));
    2762             : 
    2763         906 :   std::unique_ptr<FEBase> fe(FEBase::build(dim, FEType()));
    2764         906 :   fe->get_phi();
    2765         906 :   const std::vector<Point> & q_points_volume = fe->get_xyz();
    2766             : 
    2767         906 :   std::unique_ptr<FEBase> fe_face(FEBase::build(dim, FEType()));
    2768         906 :   fe_face->get_phi();
    2769         906 :   const std::vector<Point> & q_points_face = fe_face->get_xyz();
    2770             : 
    2771         906 :   fe->attach_quadrature_rule(&qrule);
    2772         906 :   fe_face->attach_quadrature_rule(&qrule_face);
    2773             : 
    2774             :   // The current q_points (locations in *physical* space)
    2775             :   const std::vector<Point> * q_points;
    2776             : 
    2777         906 :   if (parent_side != -1)
    2778             :   {
    2779         348 :     fe_face->reinit(elem, parent_side);
    2780         348 :     q_points = &q_points_face;
    2781             :   }
    2782             :   else
    2783             :   {
    2784         558 :     fe->reinit(elem);
    2785         558 :     q_points = &q_points_volume;
    2786             :   }
    2787             : 
    2788         906 :   std::vector<Point> parent_ref_points;
    2789             : 
    2790         906 :   libMesh::FEMap::inverse_map(elem->dim(), elem, *q_points, parent_ref_points);
    2791         906 :   libMesh::MeshRefinement mesh_refinement(mesh);
    2792         906 :   mesh_refinement.uniformly_refine(1);
    2793             : 
    2794             :   // A map from the child element index to the locations of all the child's quadrature points in
    2795             :   // *reference* space. Note that we use a map here instead of a vector because the caller can
    2796             :   // pass an explicit child index. We are not guaranteed to have a sequence from [0, n_children)
    2797         906 :   std::map<unsigned int, std::vector<Point>> child_to_ref_points;
    2798             : 
    2799         906 :   unsigned int n_children = elem->n_children();
    2800             : 
    2801         906 :   refinement_map.resize(n_children);
    2802             : 
    2803         906 :   std::vector<unsigned int> children;
    2804             : 
    2805         906 :   if (child != -1) // Passed in a child explicitly
    2806         474 :     children.push_back(child);
    2807             :   else
    2808             :   {
    2809         432 :     children.resize(n_children);
    2810        2724 :     for (unsigned int child = 0; child < n_children; ++child)
    2811        2292 :       children[child] = child;
    2812             :   }
    2813             : 
    2814        3672 :   for (unsigned int i = 0; i < children.size(); ++i)
    2815             :   {
    2816        2766 :     unsigned int child = children[i];
    2817             : 
    2818        2766 :     if ((parent_side != -1 && !elem->is_child_on_side(child, parent_side)))
    2819         948 :       continue;
    2820             : 
    2821        1818 :     const Elem * child_elem = elem->child_ptr(child);
    2822             : 
    2823        1818 :     if (child_side != -1)
    2824             :     {
    2825        1422 :       fe_face->reinit(child_elem, child_side);
    2826        1422 :       q_points = &q_points_face;
    2827             :     }
    2828             :     else
    2829             :     {
    2830         396 :       fe->reinit(child_elem);
    2831         396 :       q_points = &q_points_volume;
    2832             :     }
    2833             : 
    2834        1818 :     std::vector<Point> child_ref_points;
    2835             : 
    2836        1818 :     libMesh::FEMap::inverse_map(elem->dim(), elem, *q_points, child_ref_points);
    2837        1818 :     child_to_ref_points[child] = child_ref_points;
    2838             : 
    2839        1818 :     std::vector<QpMap> & qp_map = refinement_map[child];
    2840             : 
    2841             :     // Find the closest parent_qp to each child_qp
    2842        1818 :     mapPoints(child_ref_points, parent_ref_points, qp_map);
    2843        1818 :   }
    2844             : 
    2845         906 :   coarsen_map.resize(parent_ref_points.size());
    2846             : 
    2847             :   // For each parent qp find the closest child qp
    2848        6210 :   for (unsigned int child = 0; child < n_children; child++)
    2849             :   {
    2850        5304 :     if (parent_side != -1 && !elem->is_child_on_side(child, child_side))
    2851         948 :       continue;
    2852             : 
    2853        4356 :     std::vector<Point> & child_ref_points = child_to_ref_points[child];
    2854             : 
    2855        4356 :     std::vector<QpMap> qp_map;
    2856             : 
    2857             :     // Find all of the closest points from parent_qp to _THIS_ child's qp
    2858        4356 :     mapPoints(parent_ref_points, child_ref_points, qp_map);
    2859             : 
    2860             :     // Check those to see if they are closer than what we currently have for each point
    2861       32856 :     for (unsigned int parent_qp = 0; parent_qp < parent_ref_points.size(); ++parent_qp)
    2862             :     {
    2863       28500 :       std::pair<unsigned int, QpMap> & child_and_map = coarsen_map[parent_qp];
    2864       28500 :       unsigned int & closest_child = child_and_map.first;
    2865       28500 :       QpMap & closest_map = child_and_map.second;
    2866             : 
    2867       28500 :       QpMap & current_map = qp_map[parent_qp];
    2868             : 
    2869       28500 :       if (current_map._distance < closest_map._distance)
    2870             :       {
    2871        6300 :         closest_child = child;
    2872        6300 :         closest_map = current_map;
    2873             :       }
    2874             :     }
    2875        4356 :   }
    2876         906 : }
    2877             : 
    2878             : void
    2879           0 : MooseMesh::changeBoundaryId(const boundary_id_type old_id,
    2880             :                             const boundary_id_type new_id,
    2881             :                             bool delete_prev)
    2882             : {
    2883           0 :   TIME_SECTION("changeBoundaryId", 6);
    2884           0 :   changeBoundaryId(getMesh(), old_id, new_id, delete_prev);
    2885           0 : }
    2886             : 
    2887             : void
    2888           0 : MooseMesh::changeBoundaryId(MeshBase & mesh,
    2889             :                             const boundary_id_type old_id,
    2890             :                             const boundary_id_type new_id,
    2891             :                             bool delete_prev)
    2892             : {
    2893             :   // Get a reference to our BoundaryInfo object, we will use it several times below...
    2894           0 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
    2895             : 
    2896             :   // Container to catch ids passed back from BoundaryInfo
    2897           0 :   std::vector<boundary_id_type> old_ids;
    2898             : 
    2899             :   // Only level-0 elements store BCs.  Loop over them.
    2900           0 :   for (auto & elem : as_range(mesh.level_elements_begin(0), mesh.level_elements_end(0)))
    2901             :   {
    2902           0 :     unsigned int n_sides = elem->n_sides();
    2903           0 :     for (unsigned int s = 0; s != n_sides; ++s)
    2904             :     {
    2905           0 :       boundary_info.boundary_ids(elem, s, old_ids);
    2906           0 :       if (std::find(old_ids.begin(), old_ids.end(), old_id) != old_ids.end())
    2907             :       {
    2908           0 :         std::vector<boundary_id_type> new_ids(old_ids);
    2909           0 :         std::replace(new_ids.begin(), new_ids.end(), old_id, new_id);
    2910           0 :         if (delete_prev)
    2911             :         {
    2912           0 :           boundary_info.remove_side(elem, s);
    2913           0 :           boundary_info.add_side(elem, s, new_ids);
    2914             :         }
    2915             :         else
    2916           0 :           boundary_info.add_side(elem, s, new_ids);
    2917           0 :       }
    2918             :     }
    2919           0 :   }
    2920             : 
    2921             :   // Remove any remaining references to the old ID from the
    2922             :   // BoundaryInfo object.  This prevents things like empty sidesets
    2923             :   // from showing up when printing information, etc.
    2924           0 :   if (delete_prev)
    2925           0 :     boundary_info.remove_id(old_id);
    2926             : 
    2927             :   // The cached boundary id sets will need re-preparation
    2928           0 :   mesh.unset_has_boundary_id_sets();
    2929           0 : }
    2930             : 
    2931             : const RealVectorValue &
    2932           0 : MooseMesh::getNormalByBoundaryID(BoundaryID id) const
    2933             : {
    2934             :   mooseAssert(_boundary_to_normal_map.get() != nullptr, "Boundary To Normal Map not built!");
    2935             : 
    2936             :   // Note: Boundaries that are not in the map (existing boundaries) will default
    2937             :   // construct a new RealVectorValue - (x,y,z)=(0, 0, 0)
    2938           0 :   return (*_boundary_to_normal_map)[id];
    2939             : }
    2940             : 
    2941             : MooseMesh &
    2942           0 : MooseMesh::clone() const
    2943             : {
    2944           0 :   mooseError("MooseMesh::clone() is no longer supported, use MooseMesh::safeClone() instead.");
    2945             : }
    2946             : 
    2947             : void
    2948       72312 : MooseMesh::determineUseDistributedMesh()
    2949             : {
    2950       72312 :   switch (_parallel_type)
    2951             :   {
    2952       67480 :     case ParallelType::DEFAULT:
    2953             :       // The user did not specify 'parallel_type = XYZ' in the input file,
    2954             :       // so we allow the --distributed-mesh command line arg to possibly turn
    2955             :       // on DistributedMesh.  If the command line arg is not present, we pick ReplicatedMesh.
    2956       67480 :       if (_app.getDistributedMeshOnCommandLine())
    2957       10520 :         _use_distributed_mesh = true;
    2958       67480 :       break;
    2959        3493 :     case ParallelType::REPLICATED:
    2960        3493 :       if (_app.getDistributedMeshOnCommandLine() || _is_nemesis || _is_split)
    2961         719 :         _parallel_type_overridden = true;
    2962        3493 :       _use_distributed_mesh = false;
    2963        3493 :       break;
    2964        1339 :     case ParallelType::DISTRIBUTED:
    2965        1339 :       _use_distributed_mesh = true;
    2966        1339 :       break;
    2967             :   }
    2968             : 
    2969             :   // If the user specifies 'nemesis = true' in the Mesh block, or they are using --use-split,
    2970             :   // we must use DistributedMesh.
    2971       72312 :   if (_is_nemesis || _is_split)
    2972         562 :     _use_distributed_mesh = true;
    2973       72312 : }
    2974             : 
    2975             : std::unique_ptr<MeshBase>
    2976       67836 : MooseMesh::buildMeshBaseObject(unsigned int dim)
    2977             : {
    2978       67836 :   std::unique_ptr<MeshBase> mesh;
    2979       67836 :   if (_use_distributed_mesh)
    2980       10618 :     mesh = buildTypedMesh<DistributedMesh>(dim);
    2981             :   else
    2982       57218 :     mesh = buildTypedMesh<ReplicatedMesh>(dim);
    2983             : 
    2984       67836 :   return mesh;
    2985           0 : }
    2986             : 
    2987             : void
    2988       64741 : MooseMesh::setMeshBase(std::unique_ptr<MeshBase> mesh_base)
    2989             : {
    2990       64741 :   _mesh = std::move(mesh_base);
    2991       64741 :   _mesh->allow_remote_element_removal(_allow_remote_element_removal);
    2992       64741 : }
    2993             : 
    2994             : void
    2995       64309 : MooseMesh::init()
    2996             : {
    2997             :   /**
    2998             :    * If the mesh base hasn't been constructed by the time init is called, just do it here.
    2999             :    * This can happen if somebody builds a mesh outside of the normal Action system. Forcing
    3000             :    * developers to create, construct the MeshBase, and then init separately is a bit much for casual
    3001             :    * use but it gives us the ability to run MeshGenerators in-between.
    3002             :    */
    3003       64309 :   if (!_mesh)
    3004          10 :     _mesh = buildMeshBaseObject();
    3005             : 
    3006       64309 :   if (_app.isSplitMesh() && _use_distributed_mesh)
    3007           0 :     mooseError("You cannot use the mesh splitter capability with DistributedMesh!");
    3008             : 
    3009      192927 :   TIME_SECTION("init", 2);
    3010             : 
    3011       64309 :   if (_app.isRecovering() && _allow_recovery && _app.isUltimateMaster())
    3012             :   {
    3013             :     // Some partitioners are not idempotent.  Some recovery data
    3014             :     // files require partitioning to match mesh partitioning.  This
    3015             :     // means that, when recovering, we can't safely repartition.
    3016        3292 :     const bool skip_partitioning_later = getMesh().skip_partitioning();
    3017        3292 :     getMesh().skip_partitioning(true);
    3018        3292 :     const bool allow_renumbering_later = getMesh().allow_renumbering();
    3019        3292 :     getMesh().allow_renumbering(false);
    3020             : 
    3021             :     // For now, only read the recovery mesh on the Ultimate Master..
    3022             :     // sub-apps need to just build their mesh like normal
    3023             :     {
    3024        9876 :       TIME_SECTION("readRecoveredMesh", 2);
    3025        3292 :       getMesh().read(_app.getRestartRecoverFileBase() + MooseApp::checkpointSuffix());
    3026        3292 :     }
    3027             : 
    3028        3292 :     getMesh().allow_renumbering(allow_renumbering_later);
    3029        3292 :     getMesh().skip_partitioning(skip_partitioning_later);
    3030             :   }
    3031             :   else // Normally just build the mesh
    3032             :   {
    3033             :     // Don't allow partitioning during building
    3034       61017 :     if (_app.isSplitMesh())
    3035          89 :       getMesh().skip_partitioning(true);
    3036       61017 :     buildMesh();
    3037             : 
    3038      183033 :     if (getParam<bool>("build_all_side_lowerd_mesh"))
    3039         205 :       buildLowerDMesh();
    3040             :   }
    3041       64303 : }
    3042             : 
    3043             : unsigned int
    3044    92442737 : MooseMesh::dimension() const
    3045             : {
    3046    92442737 :   return getMesh().mesh_dimension();
    3047             : }
    3048             : 
    3049             : unsigned int
    3050       37628 : MooseMesh::effectiveSpatialDimension() const
    3051             : {
    3052       37628 :   const Real abs_zero = 1e-12;
    3053             : 
    3054             :   // See if the mesh is completely containd in the z and y planes to calculate effective spatial
    3055             :   // dim
    3056       69459 :   for (unsigned int dim = LIBMESH_DIM; dim >= 1; --dim)
    3057       69459 :     if (dimensionWidth(dim - 1) >= abs_zero)
    3058       37628 :       return dim;
    3059             : 
    3060             :   // If we get here, we have a 1D mesh on the x-axis.
    3061           0 :   return 1;
    3062             : }
    3063             : 
    3064             : unsigned int
    3065       88408 : MooseMesh::getBlocksMaxDimension(const std::vector<SubdomainName> & blocks) const
    3066             : {
    3067       88408 :   const auto & mesh = getMesh();
    3068             : 
    3069             :   // Take a shortcut if possible
    3070       88408 :   if (const auto & elem_dims = mesh.elem_dimensions(); mesh.is_prepared() && elem_dims.size() == 1)
    3071       78385 :     return *elem_dims.begin();
    3072             : 
    3073       10023 :   unsigned short dim = 0;
    3074       10023 :   const auto subdomain_ids = getSubdomainIDs(blocks);
    3075       10023 :   const std::set<SubdomainID> subdomain_ids_set(subdomain_ids.begin(), subdomain_ids.end());
    3076     1848511 :   for (const auto & elem : mesh.active_subdomain_set_elements_ptr_range(subdomain_ids_set))
    3077     1848511 :     dim = std::max(dim, elem->dim());
    3078             : 
    3079             :   // Get the maximumal globally
    3080       10023 :   _communicator.max(dim);
    3081       10023 :   return dim;
    3082       10023 : }
    3083             : 
    3084             : std::vector<BoundaryID>
    3085   106057440 : MooseMesh::getBoundaryIDs(const Elem * const elem, const unsigned short int side) const
    3086             : {
    3087   106057440 :   std::vector<BoundaryID> ids;
    3088   106057440 :   getMesh().get_boundary_info().boundary_ids(elem, side, ids);
    3089   106057440 :   return ids;
    3090           0 : }
    3091             : 
    3092             : std::vector<std::vector<BoundaryID>>
    3093   406303020 : MooseMesh::getBoundaryIDs(const Elem * const elem) const
    3094             : {
    3095   406303020 :   std::vector<std::vector<BoundaryID>> ids;
    3096   406303020 :   getMesh().get_boundary_info().side_boundary_ids(elem, ids);
    3097   406303020 :   return ids;
    3098           0 : }
    3099             : 
    3100             : const std::set<BoundaryID> &
    3101      502980 : MooseMesh::getBoundaryIDs() const
    3102             : {
    3103      502980 :   return getMesh().get_boundary_info().get_boundary_ids();
    3104             : }
    3105             : 
    3106             : void
    3107      219269 : MooseMesh::buildNodeListFromSideList()
    3108             : {
    3109      219269 :   auto & boundary_info = getMesh().get_boundary_info();
    3110             : 
    3111      219269 :   if (_construct_node_list_from_side_list)
    3112             :   {
    3113      219243 :     const std::set<boundary_id_type> & side_bcids = boundary_info.get_side_boundary_ids();
    3114             : 
    3115      219243 :     if (_displace_node_list_by_side_list)
    3116             :     {
    3117             :       // Don't want to use auto here - the rbegin trick relies on a
    3118             :       // sorted set and we want the compiler to scream if libMesh ever
    3119             :       // switches type
    3120      219243 :       const std::set<boundary_id_type> & node_bcids = boundary_info.get_node_boundary_ids();
    3121             : 
    3122             :       // If we've got a reasonable largest BC id, we can just use the
    3123             :       // subsequent unused ones
    3124      219243 :       boundary_id_type next_bcid = 0;
    3125      219243 :       if (!node_bcids.empty())
    3126      207663 :         next_bcid = std::max(next_bcid, cast_int<boundary_id_type>(*node_bcids.rbegin() + 1));
    3127      219243 :       if (!side_bcids.empty())
    3128      210979 :         next_bcid = std::max(next_bcid, cast_int<boundary_id_type>(*side_bcids.rbegin() + 1));
    3129             : 
    3130             :       // We need all processors to agree on the id to use, even when
    3131             :       // each only sees the bcids on their own portions of a
    3132             :       // distributed mesh.
    3133      219243 :       _communicator.max(next_bcid);
    3134             : 
    3135             :       // If we've got an unreasonably high largest BC id, we should
    3136             :       // probably just search for unused ones with moderate values, so we
    3137             :       // don't risk wrapping.
    3138      219243 :       if (next_bcid > 1000 || next_bcid <= 0)
    3139        7025 :         next_bcid = 1000;
    3140             : 
    3141             :       // If any side bcid is already a node bcid with a different name,
    3142             :       // that's a different boundary condition that we need to reassign
    3143             :       // rather than overwrite or merge to.
    3144     1043420 :       for (auto bcid : side_bcids)
    3145     1616962 :         if (node_bcids.count(bcid) &&
    3146      792785 :             (boundary_info.get_sideset_name(bcid) != boundary_info.get_nodeset_name(bcid)))
    3147             :         {
    3148        2002 :           boundary_info.renumber_node_id(bcid, next_bcid);
    3149             :           do
    3150             :           {
    3151        2012 :             ++next_bcid;
    3152        2012 :           } while (node_bcids.count(next_bcid) || side_bcids.count(next_bcid));
    3153             :         }
    3154             :     }
    3155             : 
    3156             :     // For any side bcid that has a name, make sure that our new node
    3157             :     // bcid is given the same name.  We need to iterate over the
    3158             :     // actual name map (which is global) here, not over side_bcids
    3159             :     // (which only includes local ids on a distributed mesh).
    3160     1034551 :     for (auto & [id, name] : boundary_info.get_sideset_name_map())
    3161      815308 :       boundary_info.nodeset_name(id) = name;
    3162             : 
    3163      219243 :     boundary_info.build_node_list_from_side_list();
    3164             :   }
    3165      219269 : }
    3166             : 
    3167             : std::vector<std::tuple<dof_id_type, unsigned short int, boundary_id_type>>
    3168         218 : MooseMesh::buildSideList()
    3169             : {
    3170         218 :   return getMesh().get_boundary_info().build_side_list();
    3171             : }
    3172             : 
    3173             : std::vector<std::tuple<dof_id_type, unsigned short int, boundary_id_type>>
    3174        4528 : MooseMesh::buildActiveSideList() const
    3175             : {
    3176        4528 :   return getMesh().get_boundary_info().build_active_side_list();
    3177             : }
    3178             : 
    3179             : unsigned int
    3180       20736 : MooseMesh::sideWithBoundaryID(const Elem * const elem, const BoundaryID boundary_id) const
    3181             : {
    3182       20736 :   return getMesh().get_boundary_info().side_with_boundary_id(elem, boundary_id);
    3183             : }
    3184             : 
    3185             : MeshBase::node_iterator
    3186        3045 : MooseMesh::localNodesBegin()
    3187             : {
    3188        3045 :   return getMesh().local_nodes_begin();
    3189             : }
    3190             : 
    3191             : MeshBase::node_iterator
    3192        3045 : MooseMesh::localNodesEnd()
    3193             : {
    3194        3045 :   return getMesh().local_nodes_end();
    3195             : }
    3196             : 
    3197             : MeshBase::const_node_iterator
    3198           0 : MooseMesh::localNodesBegin() const
    3199             : {
    3200           0 :   return getMesh().local_nodes_begin();
    3201             : }
    3202             : 
    3203             : MeshBase::const_node_iterator
    3204           0 : MooseMesh::localNodesEnd() const
    3205             : {
    3206           0 :   return getMesh().local_nodes_end();
    3207             : }
    3208             : 
    3209             : MeshBase::element_iterator
    3210      144990 : MooseMesh::activeLocalElementsBegin()
    3211             : {
    3212      144990 :   return getMesh().active_local_elements_begin();
    3213             : }
    3214             : 
    3215             : const MeshBase::element_iterator
    3216      144990 : MooseMesh::activeLocalElementsEnd()
    3217             : {
    3218      144990 :   return getMesh().active_local_elements_end();
    3219             : }
    3220             : 
    3221             : MeshBase::const_element_iterator
    3222           0 : MooseMesh::activeLocalElementsBegin() const
    3223             : {
    3224           0 :   return getMesh().active_local_elements_begin();
    3225             : }
    3226             : 
    3227             : const MeshBase::const_element_iterator
    3228           0 : MooseMesh::activeLocalElementsEnd() const
    3229             : {
    3230           0 :   return getMesh().active_local_elements_end();
    3231             : }
    3232             : 
    3233             : dof_id_type
    3234       54201 : MooseMesh::nNodes() const
    3235             : {
    3236       54201 :   return getMesh().n_nodes();
    3237             : }
    3238             : 
    3239             : dof_id_type
    3240        1562 : MooseMesh::nElem() const
    3241             : {
    3242        1562 :   return getMesh().n_elem();
    3243             : }
    3244             : 
    3245             : dof_id_type
    3246           0 : MooseMesh::maxNodeId() const
    3247             : {
    3248           0 :   return getMesh().max_node_id();
    3249             : }
    3250             : 
    3251             : dof_id_type
    3252           0 : MooseMesh::maxElemId() const
    3253             : {
    3254           0 :   return getMesh().max_elem_id();
    3255             : }
    3256             : 
    3257             : Elem *
    3258           0 : MooseMesh::elem(const dof_id_type i)
    3259             : {
    3260           0 :   mooseDeprecated("MooseMesh::elem() is deprecated, please use MooseMesh::elemPtr() instead");
    3261           0 :   return elemPtr(i);
    3262             : }
    3263             : 
    3264             : const Elem *
    3265           0 : MooseMesh::elem(const dof_id_type i) const
    3266             : {
    3267           0 :   mooseDeprecated("MooseMesh::elem() is deprecated, please use MooseMesh::elemPtr() instead");
    3268           0 :   return elemPtr(i);
    3269             : }
    3270             : 
    3271             : Elem *
    3272    15517186 : MooseMesh::elemPtr(const dof_id_type i)
    3273             : {
    3274    15517186 :   return getMesh().elem_ptr(i);
    3275             : }
    3276             : 
    3277             : const Elem *
    3278     1244484 : MooseMesh::elemPtr(const dof_id_type i) const
    3279             : {
    3280     1244484 :   return getMesh().elem_ptr(i);
    3281             : }
    3282             : 
    3283             : Elem *
    3284       21601 : MooseMesh::queryElemPtr(const dof_id_type i)
    3285             : {
    3286       21601 :   return getMesh().query_elem_ptr(i);
    3287             : }
    3288             : 
    3289             : const Elem *
    3290       36374 : MooseMesh::queryElemPtr(const dof_id_type i) const
    3291             : {
    3292       36374 :   return getMesh().query_elem_ptr(i);
    3293             : }
    3294             : 
    3295             : bool
    3296           0 : MooseMesh::prepared() const
    3297             : {
    3298           0 :   return _mesh->is_prepared() && _moose_mesh_prepared;
    3299             : }
    3300             : 
    3301             : void
    3302           0 : MooseMesh::prepared(bool state)
    3303             : {
    3304           0 :   if (state)
    3305           0 :     mooseError("We don't have any right to tell the libmesh mesh that it *is* prepared. Only a "
    3306             :                "call to prepare_for_use should tell us that");
    3307             : 
    3308             :   // Some people may call this even before we have a MeshBase object. This isn't dangerous really
    3309             :   // because when the MeshBase object is born, it knows it's in an unprepared state
    3310           0 :   if (_mesh)
    3311           0 :     _mesh->unset_is_prepared();
    3312             : 
    3313             :   // If the libMesh mesh isn't preparead, then our MooseMesh wrapper is also no longer prepared
    3314           0 :   _moose_mesh_prepared = false;
    3315             : 
    3316             :   /**
    3317             :    * If we are explicitly setting the mesh to not prepared, then we've likely modified the mesh
    3318             :    * and can no longer make assumptions about orthogonality. We really should recheck.
    3319             :    */
    3320           0 :   _regular_orthogonal_mesh = false;
    3321           0 : }
    3322             : 
    3323             : void
    3324           0 : MooseMesh::needsPrepareForUse()
    3325             : {
    3326           0 :   prepared(false);
    3327           0 : }
    3328             : 
    3329             : const std::set<SubdomainID> &
    3330     8177827 : MooseMesh::meshSubdomains() const
    3331             : {
    3332     8177827 :   return _mesh_subdomains;
    3333             : }
    3334             : 
    3335             : const std::set<BoundaryID> &
    3336       11197 : MooseMesh::meshBoundaryIds() const
    3337             : {
    3338       11197 :   return _mesh_boundary_ids;
    3339             : }
    3340             : 
    3341             : const std::set<BoundaryID> &
    3342       28925 : MooseMesh::meshSidesetIds() const
    3343             : {
    3344       28925 :   return _mesh_sideset_ids;
    3345             : }
    3346             : 
    3347             : const std::set<BoundaryID> &
    3348      146825 : MooseMesh::meshNodesetIds() const
    3349             : {
    3350      146825 :   return _mesh_nodeset_ids;
    3351             : }
    3352             : 
    3353             : void
    3354           0 : MooseMesh::setMeshBoundaryIDs(std::set<BoundaryID> boundary_IDs)
    3355             : {
    3356           0 :   _mesh_boundary_ids = boundary_IDs;
    3357           0 : }
    3358             : 
    3359             : void
    3360           0 : MooseMesh::setBoundaryToNormalMap(
    3361             :     std::unique_ptr<std::map<BoundaryID, RealVectorValue>> boundary_map)
    3362             : {
    3363           0 :   _boundary_to_normal_map = std::move(boundary_map);
    3364           0 : }
    3365             : 
    3366             : void
    3367           0 : MooseMesh::setBoundaryToNormalMap(std::map<BoundaryID, RealVectorValue> * boundary_map)
    3368             : {
    3369           0 :   mooseDeprecated("setBoundaryToNormalMap(std::map<BoundaryID, RealVectorValue> * boundary_map) is "
    3370             :                   "deprecated, use the unique_ptr version instead");
    3371           0 :   _boundary_to_normal_map.reset(boundary_map);
    3372           0 : }
    3373             : 
    3374             : unsigned int
    3375      122214 : MooseMesh::uniformRefineLevel() const
    3376             : {
    3377      122214 :   return _uniform_refine_level;
    3378             : }
    3379             : 
    3380             : void
    3381       66325 : MooseMesh::setUniformRefineLevel(unsigned int level, bool deletion)
    3382             : {
    3383       66325 :   _uniform_refine_level = level;
    3384       66325 :   _skip_deletion_repartition_after_refine = deletion;
    3385       66325 : }
    3386             : 
    3387             : void
    3388       56080 : MooseMesh::addGhostedBoundary(BoundaryID boundary_id)
    3389             : {
    3390       56080 :   _ghosted_boundaries.insert(boundary_id);
    3391       56080 : }
    3392             : 
    3393             : void
    3394           0 : MooseMesh::setGhostedBoundaryInflation(const std::vector<Real> & inflation)
    3395             : {
    3396           0 :   _ghosted_boundaries_inflation = inflation;
    3397           0 : }
    3398             : 
    3399             : const std::set<unsigned int> &
    3400           0 : MooseMesh::getGhostedBoundaries() const
    3401             : {
    3402           0 :   return _ghosted_boundaries;
    3403             : }
    3404             : 
    3405             : const std::vector<Real> &
    3406       11448 : MooseMesh::getGhostedBoundaryInflation() const
    3407             : {
    3408       11448 :   return _ghosted_boundaries_inflation;
    3409             : }
    3410             : 
    3411             : namespace // Anonymous namespace for helpers
    3412             : {
    3413             : // A class for templated methods that expect output iterator
    3414             : // arguments, which adds objects to the Mesh.
    3415             : // Although extra_ghost_elem_inserter can add any object, we
    3416             : // template it around object type so that type inference and
    3417             : // iterator_traits will work.
    3418             : // This object specifically is used to insert extra ghost elems into the mesh
    3419             : template <typename T>
    3420             : struct extra_ghost_elem_inserter
    3421             : {
    3422             :   using iterator_category = std::output_iterator_tag;
    3423             :   using value_type = T;
    3424             : 
    3425       43896 :   extra_ghost_elem_inserter(DistributedMesh & m) : mesh(m) {}
    3426             : 
    3427       18777 :   void operator=(const Elem * e) { mesh.add_extra_ghost_elem(const_cast<Elem *>(e)); }
    3428             : 
    3429       35594 :   void operator=(Node * n) { mesh.add_node(n); }
    3430             : 
    3431             :   void operator=(Point * p) { mesh.add_point(*p); }
    3432             : 
    3433             :   extra_ghost_elem_inserter & operator++() { return *this; }
    3434             : 
    3435       54371 :   extra_ghost_elem_inserter operator++(int) { return extra_ghost_elem_inserter(*this); }
    3436             : 
    3437             :   // We don't return a reference-to-T here because we don't want to
    3438             :   // construct one or have any of its methods called.  We just want
    3439             :   // to allow the returned object to be able to do mesh insertions
    3440             :   // with operator=().
    3441       54371 :   extra_ghost_elem_inserter & operator*() { return *this; }
    3442             : 
    3443             : private:
    3444             :   DistributedMesh & mesh;
    3445             : };
    3446             : 
    3447             : /**
    3448             :  * Specific weak ordering for Elem *'s to be used in a set.
    3449             :  * We use the id, but first sort by level.  This guarantees
    3450             :  * when traversing the set from beginning to end the lower
    3451             :  * level (parent) elements are encountered first.
    3452             :  *
    3453             :  * This was swiped from libMesh mesh_communication.C, and ought to be
    3454             :  * replaced with libMesh::CompareElemIdsByLevel just as soon as I refactor to
    3455             :  * create that - @roystgnr
    3456             :  */
    3457             : struct CompareElemsByLevel
    3458             : {
    3459      103489 :   bool operator()(const Elem * a, const Elem * b) const
    3460             :   {
    3461             :     libmesh_assert(a);
    3462             :     libmesh_assert(b);
    3463      103489 :     const unsigned int al = a->level(), bl = b->level();
    3464      103489 :     const dof_id_type aid = a->id(), bid = b->id();
    3465             : 
    3466      103489 :     return (al == bl) ? aid < bid : al < bl;
    3467             :   }
    3468             : };
    3469             : 
    3470             : } // anonymous namespace
    3471             : 
    3472             : void
    3473      135795 : MooseMesh::ghostGhostedBoundaries()
    3474             : {
    3475             :   // No need to do this if using a serial mesh
    3476             :   // We do not need to ghost boundary elements when _need_ghost_ghosted_boundaries
    3477             :   // is not true. _need_ghost_ghosted_boundaries can be set by a mesh generator
    3478             :   // where boundaries are already ghosted accordingly
    3479      135795 :   if (!_use_distributed_mesh || !_need_ghost_ghosted_boundaries)
    3480      113847 :     return;
    3481             : 
    3482       65844 :   TIME_SECTION("GhostGhostedBoundaries", 3);
    3483             : 
    3484             :   parallel_object_only();
    3485             : 
    3486       21948 :   DistributedMesh & mesh = dynamic_cast<DistributedMesh &>(getMesh());
    3487             : 
    3488             :   // We clear ghosted elements that were added by previous invocations of this
    3489             :   // method but leave ghosted elements that were added by other code, e.g.
    3490             :   // OversampleOutput, untouched
    3491       21948 :   mesh.clear_extra_ghost_elems(_ghost_elems_from_ghost_boundaries);
    3492       21948 :   _ghost_elems_from_ghost_boundaries.clear();
    3493             : 
    3494       21948 :   std::set<const Elem *, CompareElemsByLevel> boundary_elems_to_ghost;
    3495       21948 :   std::set<Node *> connected_nodes_to_ghost;
    3496             : 
    3497       21948 :   std::vector<const Elem *> family_tree;
    3498             : 
    3499      879188 :   for (const auto & t : mesh.get_boundary_info().build_side_list())
    3500             :   {
    3501      857240 :     auto elem_id = std::get<0>(t);
    3502      857240 :     auto bc_id = std::get<2>(t);
    3503             : 
    3504      857240 :     if (_ghosted_boundaries.find(bc_id) != _ghosted_boundaries.end())
    3505             :     {
    3506        5725 :       Elem * elem = mesh.elem_ptr(elem_id);
    3507             : 
    3508             : #ifdef LIBMESH_ENABLE_AMR
    3509        5725 :       elem->family_tree(family_tree);
    3510        5725 :       Elem * parent = elem->parent();
    3511        5725 :       while (parent)
    3512             :       {
    3513           0 :         family_tree.push_back(parent);
    3514           0 :         parent = parent->parent();
    3515             :       }
    3516             : #else
    3517             :       family_tree.clear();
    3518             :       family_tree.push_back(elem);
    3519             : #endif
    3520       15098 :       for (const auto & felem : family_tree)
    3521             :       {
    3522        9373 :         boundary_elems_to_ghost.insert(felem);
    3523             : 
    3524             :         // The entries of connected_nodes_to_ghost need to be
    3525             :         // non-constant, so that they will work in things like
    3526             :         // UpdateDisplacedMeshThread. The container returned by
    3527             :         // family_tree contains const Elems even when the Elem
    3528             :         // it is called on is non-const, so once that interface
    3529             :         // gets fixed we can remove this const_cast.
    3530       56448 :         for (unsigned int n = 0; n < felem->n_nodes(); ++n)
    3531       47075 :           connected_nodes_to_ghost.insert(const_cast<Node *>(felem->node_ptr(n)));
    3532             :       }
    3533             :     }
    3534       21948 :   }
    3535             : 
    3536             :   // We really do want to store this by value instead of by reference
    3537       21948 :   const auto prior_ghost_elems = mesh.extra_ghost_elems();
    3538             : 
    3539       21948 :   mesh.comm().allgather_packed_range(&mesh,
    3540             :                                      connected_nodes_to_ghost.begin(),
    3541             :                                      connected_nodes_to_ghost.end(),
    3542             :                                      extra_ghost_elem_inserter<Node>(mesh));
    3543             : 
    3544       21948 :   mesh.comm().allgather_packed_range(&mesh,
    3545             :                                      boundary_elems_to_ghost.begin(),
    3546             :                                      boundary_elems_to_ghost.end(),
    3547             :                                      extra_ghost_elem_inserter<Elem>(mesh));
    3548             : 
    3549       21948 :   const auto & current_ghost_elems = mesh.extra_ghost_elems();
    3550             : 
    3551       43896 :   std::set_difference(current_ghost_elems.begin(),
    3552             :                       current_ghost_elems.end(),
    3553             :                       prior_ghost_elems.begin(),
    3554             :                       prior_ghost_elems.end(),
    3555       21948 :                       std::inserter(_ghost_elems_from_ghost_boundaries,
    3556             :                                     _ghost_elems_from_ghost_boundaries.begin()));
    3557       21948 : }
    3558             : 
    3559             : unsigned int
    3560       11448 : MooseMesh::getPatchSize() const
    3561             : {
    3562       11448 :   return _patch_size;
    3563             : }
    3564             : 
    3565             : void
    3566           0 : MooseMesh::setPatchUpdateStrategy(Moose::PatchUpdateType patch_update_strategy)
    3567             : {
    3568           0 :   _patch_update_strategy = patch_update_strategy;
    3569           0 : }
    3570             : 
    3571             : const Moose::PatchUpdateType &
    3572       36377 : MooseMesh::getPatchUpdateStrategy() const
    3573             : {
    3574       36377 :   return _patch_update_strategy;
    3575             : }
    3576             : 
    3577             : BoundingBox
    3578      114395 : MooseMesh::getInflatedProcessorBoundingBox(Real inflation_multiplier) const
    3579             : {
    3580             :   // Grab a bounding box to speed things up.  Note that
    3581             :   // local_bounding_box is *not* equivalent to processor_bounding_box
    3582             :   // with processor_id() except in serial.
    3583      114395 :   BoundingBox bbox = MeshTools::create_local_bounding_box(getMesh());
    3584             : 
    3585             :   // Inflate the bbox just a bit to deal with roundoff
    3586             :   // Adding 1% of the diagonal size in each direction on each end
    3587      114395 :   Real inflation_amount = inflation_multiplier * (bbox.max() - bbox.min()).norm();
    3588      114395 :   Point inflation(inflation_amount, inflation_amount, inflation_amount);
    3589             : 
    3590      114395 :   bbox.first -= inflation;  // min
    3591      114395 :   bbox.second += inflation; // max
    3592             : 
    3593      228790 :   return bbox;
    3594             : }
    3595             : 
    3596      157290 : MooseMesh::operator libMesh::MeshBase &() { return getMesh(); }
    3597             : 
    3598        2990 : MooseMesh::operator const libMesh::MeshBase &() const { return getMesh(); }
    3599             : 
    3600             : const MeshBase *
    3601      345863 : MooseMesh::getMeshPtr() const
    3602             : {
    3603      345863 :   return _mesh.get();
    3604             : }
    3605             : 
    3606             : MeshBase &
    3607    55827311 : MooseMesh::getMesh()
    3608             : {
    3609             :   mooseAssert(_mesh, "Mesh hasn't been created");
    3610    55827311 :   return *_mesh;
    3611             : }
    3612             : 
    3613             : const MeshBase &
    3614   707944689 : MooseMesh::getMesh() const
    3615             : {
    3616             :   mooseAssert(_mesh, "Mesh hasn't been created");
    3617   707944689 :   return *_mesh;
    3618             : }
    3619             : 
    3620             : void
    3621           0 : MooseMesh::printInfo(std::ostream & os, const unsigned int verbosity /* = 0 */) const
    3622             : {
    3623           0 :   os << '\n';
    3624           0 :   getMesh().print_info(os, verbosity);
    3625           0 :   os << std::flush;
    3626           0 : }
    3627             : 
    3628             : const std::vector<dof_id_type> &
    3629         229 : MooseMesh::getNodeList(boundary_id_type nodeset_id) const
    3630             : {
    3631             :   std::map<boundary_id_type, std::vector<dof_id_type>>::const_iterator it =
    3632         229 :       _node_set_nodes.find(nodeset_id);
    3633             : 
    3634         229 :   if (it == _node_set_nodes.end())
    3635             :   {
    3636             :     // On a distributed mesh we might not know about a remote nodeset,
    3637             :     // so we'll return an empty vector and hope the nodeset exists
    3638             :     // elsewhere.
    3639           0 :     if (!getMesh().is_serial())
    3640             :     {
    3641           0 :       static const std::vector<dof_id_type> empty_vec;
    3642           0 :       return empty_vec;
    3643             :     }
    3644             :     // On a replicated mesh we should know about every nodeset and if
    3645             :     // we're asked for one that doesn't exist then it must be a bug.
    3646             :     else
    3647             :     {
    3648           0 :       mooseError("Unable to nodeset ID: ", nodeset_id, '.');
    3649             :     }
    3650             :   }
    3651             : 
    3652         229 :   return it->second;
    3653             : }
    3654             : 
    3655             : const std::set<BoundaryID> &
    3656     5068444 : MooseMesh::getSubdomainBoundaryIds(const SubdomainID subdomain_id) const
    3657             : {
    3658     5068444 :   const auto it = _sub_to_data.find(subdomain_id);
    3659             : 
    3660     5068444 :   if (it == _sub_to_data.end())
    3661           0 :     mooseError("Unable to find subdomain ID: ", subdomain_id, '.');
    3662             : 
    3663    10136888 :   return it->second.boundary_ids;
    3664             : }
    3665             : 
    3666             : std::set<BoundaryID>
    3667          22 : MooseMesh::getSubdomainInterfaceBoundaryIds(const SubdomainID subdomain_id) const
    3668             : {
    3669          22 :   const auto & bnd_ids = getSubdomainBoundaryIds(subdomain_id);
    3670          22 :   std::set<BoundaryID> boundary_ids(bnd_ids.begin(), bnd_ids.end());
    3671             :   std::unordered_map<SubdomainID, std::set<BoundaryID>>::const_iterator it =
    3672          22 :       _neighbor_subdomain_boundary_ids.find(subdomain_id);
    3673             : 
    3674          22 :   boundary_ids.insert(it->second.begin(), it->second.end());
    3675             : 
    3676          44 :   return boundary_ids;
    3677           0 : }
    3678             : 
    3679             : std::set<SubdomainID>
    3680         203 : MooseMesh::getBoundaryConnectedBlocks(const BoundaryID bid) const
    3681             : {
    3682         203 :   std::set<SubdomainID> subdomain_ids;
    3683         763 :   for (const auto & [sub_id, data] : _sub_to_data)
    3684         560 :     if (data.boundary_ids.find(bid) != data.boundary_ids.end())
    3685         203 :       subdomain_ids.insert(sub_id);
    3686             : 
    3687         203 :   return subdomain_ids;
    3688           0 : }
    3689             : 
    3690             : std::set<SubdomainID>
    3691         169 : MooseMesh::getBoundaryConnectedSecondaryBlocks(const BoundaryID bid) const
    3692             : {
    3693         169 :   std::set<SubdomainID> subdomain_ids;
    3694         507 :   for (const auto & it : _neighbor_subdomain_boundary_ids)
    3695         338 :     if (it.second.find(bid) != it.second.end())
    3696         169 :       subdomain_ids.insert(it.first);
    3697             : 
    3698         169 :   return subdomain_ids;
    3699           0 : }
    3700             : 
    3701             : std::set<SubdomainID>
    3702          11 : MooseMesh::getInterfaceConnectedBlocks(const BoundaryID bid) const
    3703             : {
    3704          11 :   std::set<SubdomainID> subdomain_ids = getBoundaryConnectedBlocks(bid);
    3705         110 :   for (const auto & it : _neighbor_subdomain_boundary_ids)
    3706          99 :     if (it.second.find(bid) != it.second.end())
    3707          44 :       subdomain_ids.insert(it.first);
    3708             : 
    3709          11 :   return subdomain_ids;
    3710           0 : }
    3711             : 
    3712             : const std::set<SubdomainID> &
    3713           0 : MooseMesh::getBlockConnectedBlocks(const SubdomainID subdomain_id) const
    3714             : {
    3715           0 :   const auto it = _sub_to_data.find(subdomain_id);
    3716             : 
    3717           0 :   if (it == _sub_to_data.end())
    3718           0 :     mooseError("Unable to find subdomain ID: ", subdomain_id, '.');
    3719             : 
    3720           0 :   return it->second.neighbor_subs;
    3721             : }
    3722             : 
    3723             : bool
    3724     1216264 : MooseMesh::isBoundaryNode(dof_id_type node_id) const
    3725             : {
    3726     1216264 :   bool found_node = false;
    3727     4992112 :   for (const auto & it : _bnd_node_ids)
    3728             :   {
    3729     4053776 :     if (it.second.find(node_id) != it.second.end())
    3730             :     {
    3731      277928 :       found_node = true;
    3732      277928 :       break;
    3733             :     }
    3734             :   }
    3735     1216264 :   return found_node;
    3736             : }
    3737             : 
    3738             : bool
    3739      995742 : MooseMesh::isBoundaryNode(dof_id_type node_id, BoundaryID bnd_id) const
    3740             : {
    3741      995742 :   bool found_node = false;
    3742      995742 :   std::map<boundary_id_type, std::set<dof_id_type>>::const_iterator it = _bnd_node_ids.find(bnd_id);
    3743      995742 :   if (it != _bnd_node_ids.end())
    3744      935442 :     if (it->second.find(node_id) != it->second.end())
    3745       11620 :       found_node = true;
    3746      995742 :   return found_node;
    3747             : }
    3748             : 
    3749             : bool
    3750           0 : MooseMesh::isBoundaryElem(dof_id_type elem_id) const
    3751             : {
    3752           0 :   bool found_elem = false;
    3753           0 :   for (const auto & it : _bnd_elem_ids)
    3754             :   {
    3755           0 :     if (it.second.find(elem_id) != it.second.end())
    3756             :     {
    3757           0 :       found_elem = true;
    3758           0 :       break;
    3759             :     }
    3760             :   }
    3761           0 :   return found_elem;
    3762             : }
    3763             : 
    3764             : bool
    3765      425114 : MooseMesh::isBoundaryElem(dof_id_type elem_id, BoundaryID bnd_id) const
    3766             : {
    3767      425114 :   bool found_elem = false;
    3768      425114 :   auto it = _bnd_elem_ids.find(bnd_id);
    3769      425114 :   if (it != _bnd_elem_ids.end())
    3770      393181 :     if (it->second.find(elem_id) != it->second.end())
    3771       22342 :       found_elem = true;
    3772      425114 :   return found_elem;
    3773             : }
    3774             : 
    3775             : void
    3776        1276 : MooseMesh::errorIfDistributedMesh(std::string name) const
    3777             : {
    3778        1276 :   if (_use_distributed_mesh)
    3779           0 :     mooseError("Cannot use ",
    3780             :                name,
    3781             :                " with DistributedMesh!\n",
    3782             :                "Consider specifying parallel_type = 'replicated' in your input file\n",
    3783             :                "to prevent it from being run with DistributedMesh.");
    3784        1276 : }
    3785             : 
    3786             : void
    3787       68453 : MooseMesh::setPartitionerHelper(MeshBase * const mesh)
    3788             : {
    3789       68453 :   if (_use_distributed_mesh && (_partitioner_name != "default" && _partitioner_name != "parmetis"))
    3790             :   {
    3791          16 :     _partitioner_name = "parmetis";
    3792          16 :     _partitioner_overridden = true;
    3793             :   }
    3794             : 
    3795       68453 :   setPartitioner(mesh ? *mesh : getMesh(), _partitioner_name, _use_distributed_mesh, _pars, *this);
    3796       68453 : }
    3797             : 
    3798             : void
    3799       68453 : MooseMesh::setPartitioner(MeshBase & mesh_base,
    3800             :                           MooseEnum & partitioner,
    3801             :                           bool use_distributed_mesh,
    3802             :                           const InputParameters & params,
    3803             :                           MooseObject & context_obj)
    3804             : {
    3805             :   // Set the partitioner based on partitioner name
    3806       68453 :   switch (partitioner)
    3807             :   {
    3808       63542 :     case -3: // default
    3809             :       // We'll use the default partitioner, but notify the user of which one is being used...
    3810       63542 :       if (use_distributed_mesh)
    3811       20870 :         partitioner = "parmetis";
    3812             :       else
    3813      106214 :         partitioner = "metis";
    3814       63542 :       break;
    3815             : 
    3816             :     // No need to explicitily create the metis or parmetis partitioners,
    3817             :     // They are the default for serial and parallel mesh respectively
    3818        4799 :     case -2: // metis
    3819             :     case -1: // parmetis
    3820        4799 :       break;
    3821             : 
    3822          60 :     case 0: // linear
    3823          60 :       mesh_base.partitioner().reset(new libMesh::LinearPartitioner);
    3824          60 :       break;
    3825          52 :     case 1: // centroid
    3826             :     {
    3827         104 :       if (!params.isParamValid("centroid_partitioner_direction"))
    3828           0 :         context_obj.paramError(
    3829             :             "centroid_partitioner_direction",
    3830             :             "If using the centroid partitioner you _must_ specify centroid_partitioner_direction!");
    3831             : 
    3832          52 :       MooseEnum direction = params.get<MooseEnum>("centroid_partitioner_direction");
    3833             : 
    3834          52 :       if (direction == "x")
    3835          32 :         mesh_base.partitioner().reset(
    3836          16 :             new libMesh::CentroidPartitioner(libMesh::CentroidPartitioner::X));
    3837          36 :       else if (direction == "y")
    3838          72 :         mesh_base.partitioner().reset(
    3839          36 :             new libMesh::CentroidPartitioner(libMesh::CentroidPartitioner::Y));
    3840           0 :       else if (direction == "z")
    3841           0 :         mesh_base.partitioner().reset(
    3842           0 :             new libMesh::CentroidPartitioner(libMesh::CentroidPartitioner::Z));
    3843           0 :       else if (direction == "radial")
    3844           0 :         mesh_base.partitioner().reset(
    3845           0 :             new libMesh::CentroidPartitioner(libMesh::CentroidPartitioner::RADIAL));
    3846          52 :       break;
    3847          52 :     }
    3848           0 :     case 2: // hilbert_sfc
    3849           0 :       mesh_base.partitioner().reset(new libMesh::HilbertSFCPartitioner);
    3850           0 :       break;
    3851           0 :     case 3: // morton_sfc
    3852           0 :       mesh_base.partitioner().reset(new libMesh::MortonSFCPartitioner);
    3853           0 :       break;
    3854             :   }
    3855       68453 : }
    3856             : 
    3857             : void
    3858        1495 : MooseMesh::setCustomPartitioner(Partitioner * partitioner)
    3859             : {
    3860        1495 :   _custom_partitioner = partitioner->clone();
    3861        1495 :   setIsCustomPartitionerRequested(true);
    3862        1495 :   if (_mesh)
    3863          12 :     _mesh->partitioner() = _custom_partitioner->clone();
    3864        1495 :   _partitioner_name = "custom";
    3865        1495 : }
    3866             : 
    3867             : bool
    3868           0 : MooseMesh::isCustomPartitionerRequested() const
    3869             : {
    3870           0 :   return _custom_partitioner_requested;
    3871             : }
    3872             : 
    3873             : bool
    3874      142731 : MooseMesh::hasSecondOrderElements()
    3875             : {
    3876      142731 :   bool mesh_has_second_order_elements = false;
    3877    46072273 :   for (auto it = activeLocalElementsBegin(), end = activeLocalElementsEnd(); it != end; ++it)
    3878    22977415 :     if ((*it)->default_order() == SECOND)
    3879             :     {
    3880       12644 :       mesh_has_second_order_elements = true;
    3881       12644 :       break;
    3882      142731 :     }
    3883             : 
    3884             :   // We checked our local elements, so take the max over all processors.
    3885      142731 :   comm().max(mesh_has_second_order_elements);
    3886      142731 :   return mesh_has_second_order_elements;
    3887             : }
    3888             : 
    3889             : void
    3890        2997 : MooseMesh::setIsCustomPartitionerRequested(bool cpr)
    3891             : {
    3892        2997 :   _custom_partitioner_requested = cpr;
    3893        2997 : }
    3894             : 
    3895             : std::unique_ptr<libMesh::PointLocatorBase>
    3896        6995 : MooseMesh::getPointLocator() const
    3897             : {
    3898        6995 :   return getMesh().sub_point_locator();
    3899             : }
    3900             : 
    3901             : void
    3902        4528 : MooseMesh::buildFiniteVolumeInfo() const
    3903             : {
    3904             :   mooseAssert(!Threads::in_threads,
    3905             :               "This routine has not been implemented for threads. Please query this routine before "
    3906             :               "a threaded region or contact a MOOSE developer to discuss.");
    3907        4528 :   _finite_volume_info_dirty = false;
    3908             : 
    3909             :   using Keytype = std::pair<const Elem *, unsigned short int>;
    3910             : 
    3911             :   // create a map from elem/side --> boundary ids
    3912             :   std::vector<std::tuple<dof_id_type, unsigned short int, boundary_id_type>> side_list =
    3913        4528 :       buildActiveSideList();
    3914        4528 :   std::map<Keytype, std::set<boundary_id_type>> side_map;
    3915      160910 :   for (auto & [elem_id, side, bc_id] : side_list)
    3916             :   {
    3917      156382 :     const Elem * elem = _mesh->elem_ptr(elem_id);
    3918      156382 :     Keytype key(elem, side);
    3919      156382 :     auto & bc_set = side_map[key];
    3920      156382 :     bc_set.insert(bc_id);
    3921             :   }
    3922             : 
    3923        4528 :   _face_info.clear();
    3924        4528 :   _all_face_info.clear();
    3925        4528 :   _elem_side_to_face_info.clear();
    3926             : 
    3927        4528 :   _elem_to_elem_info.clear();
    3928        4528 :   _elem_info.clear();
    3929             : 
    3930             :   // by performing the element ID comparison check in the below loop, we are ensuring that we never
    3931             :   // double count face contributions. If a face lies along a process boundary, the only process that
    3932             :   // will contribute to both sides of the face residuals/Jacobians will be the process that owns the
    3933             :   // element with the lower ID.
    3934        4528 :   auto begin = getMesh().active_elements_begin();
    3935        4528 :   auto end = getMesh().active_elements_end();
    3936             : 
    3937             :   // We prepare a map connecting the Elem* and the corresponding ElemInfo
    3938             :   // for the active elements.
    3939        4528 :   _elem_to_elem_info.reserve(nActiveLocalElem());
    3940        4528 :   unsigned int num_sides = 0;
    3941     1146389 :   for (const Elem * elem : as_range(begin, end))
    3942             :   {
    3943     1141861 :     _elem_to_elem_info.emplace(elem->id(), elem);
    3944     1141861 :     num_sides += elem->n_sides();
    3945        4528 :   }
    3946             : 
    3947             :   // Used to speed up FaceInfo creation:
    3948             :   // - element side builder that caches per type of element
    3949        4528 :   libMesh::ElemSideBuilder side_builder;
    3950             : 
    3951        4528 :   _all_face_info.reserve(num_sides / 2);
    3952        4528 :   dof_id_type face_index = 0;
    3953     2288250 :   for (const Elem * elem : as_range(begin, end))
    3954             :   {
    3955     5086275 :     for (unsigned int side = 0; side < elem->n_sides(); ++side)
    3956             :     {
    3957             :       // get the neighbor element
    3958     3944414 :       const Elem * neighbor = elem->neighbor_ptr(side);
    3959             : 
    3960             :       // Check if the FaceInfo shall belong to the element. If yes,
    3961             :       // create and initialize the FaceInfo. We need this to ensure that
    3962             :       // we do not duplicate FaceInfo-s.
    3963     3944414 :       if (Moose::FV::elemHasFaceInfo(*elem, neighbor))
    3964             :       {
    3965             :         mooseAssert(!neighbor || (neighbor->level() < elem->level() ? neighbor->active() : true),
    3966             :                     "If the neighbor is coarser than the element, we expect that the neighbor must "
    3967             :                     "be active.");
    3968             : 
    3969             :         // We construct the faceInfo using the elementinfo and side index
    3970             :         mooseAssert(elem->default_order() < 4, "Did not expect such high element orders in FV");
    3971     4097254 :         _all_face_info.emplace_back(
    3972     2048627 :             &_elem_to_elem_info[elem->id()], side, face_index++, side_builder);
    3973             : 
    3974     2048627 :         auto & fi = _all_face_info.back();
    3975             : 
    3976             :         // get all the sidesets that this face is contained in and cache them
    3977             :         // in the face info.
    3978     2048627 :         std::set<boundary_id_type> & boundary_ids = fi.boundaryIDs();
    3979     2048627 :         boundary_ids.clear();
    3980             : 
    3981             :         // We initialize the weights/other information in faceInfo. If the neighbor does not exist
    3982             :         // or is remote (so when we are on some sort of mesh boundary), we initialize the ghost
    3983             :         // cell and use it to compute the weights corresponding to the faceInfo.
    3984     2048627 :         if (!neighbor || neighbor == libMesh::remote_elem)
    3985      150017 :           fi.computeBoundaryCoefficients();
    3986             :         else
    3987     1898610 :           fi.computeInternalCoefficients(&_elem_to_elem_info[neighbor->id()]);
    3988             : 
    3989     2048627 :         auto lit = side_map.find(Keytype(&fi.elem(), fi.elemSideID()));
    3990     2048627 :         if (lit != side_map.end())
    3991      149793 :           boundary_ids.insert(lit->second.begin(), lit->second.end());
    3992             : 
    3993     2048627 :         if (fi.neighborPtr())
    3994             :         {
    3995     1898610 :           auto rit = side_map.find(Keytype(fi.neighborPtr(), fi.neighborSideID()));
    3996     1898610 :           if (rit != side_map.end())
    3997        3972 :             boundary_ids.insert(rit->second.begin(), rit->second.end());
    3998             :         }
    3999             :       }
    4000             :     }
    4001        4528 :   }
    4002             : 
    4003             :   // Build the local face info and elem_side to face info maps. We need to do this after
    4004             :   // _all_face_info is finished being constructed because emplace_back invalidates all iterators and
    4005             :   // references if ever the new size exceeds capacity
    4006        4528 :   _elem_side_to_face_info.reserve(_all_face_info.size());
    4007             :   // heuristic to avoid resizing too much
    4008        4528 :   _face_info.reserve(_all_face_info.size());
    4009     2053155 :   for (auto & fi : _all_face_info)
    4010             :   {
    4011     2048627 :     const Elem * const elem = &fi.elem();
    4012     2048627 :     const auto side = fi.elemSideID();
    4013             : 
    4014             : #ifndef NDEBUG
    4015             :     auto pair_it =
    4016             : #endif
    4017     2048627 :         _elem_side_to_face_info.emplace(std::make_pair(elem, side), &fi);
    4018             :     mooseAssert(pair_it.second, "We should be adding unique FaceInfo objects.");
    4019             : 
    4020             :     // We will add the faces on processor boundaries to the list of face infos on each
    4021             :     // associated processor.
    4022     2629614 :     if (fi.elem().processor_id() == this->processor_id() ||
    4023      580987 :         (fi.neighborPtr() && (fi.neighborPtr()->processor_id() == this->processor_id())))
    4024     1751577 :       _face_info.push_back(&fi);
    4025             :   }
    4026             : 
    4027        4528 :   _elem_info.reserve(nActiveLocalElem());
    4028     1146389 :   for (auto & ei : _elem_to_elem_info)
    4029     1141861 :     if (ei.second.elem()->processor_id() == this->processor_id())
    4030      980987 :       _elem_info.push_back(&ei.second);
    4031        4528 : }
    4032             : 
    4033             : const FaceInfo *
    4034   123101892 : MooseMesh::faceInfo(const Elem * elem, unsigned int side) const
    4035             : {
    4036   123101892 :   auto it = _elem_side_to_face_info.find(std::make_pair(elem, side));
    4037             : 
    4038   123101892 :   if (it == _elem_side_to_face_info.end())
    4039         792 :     return nullptr;
    4040             :   else
    4041             :   {
    4042             :     mooseAssert(it->second,
    4043             :                 "For some reason, the FaceInfo object is NULL! Try calling "
    4044             :                 "`buildFiniteVolumeInfo()` before using this accessor!");
    4045   123101100 :     return it->second;
    4046             :   }
    4047             : }
    4048             : 
    4049             : const ElemInfo &
    4050   109109456 : MooseMesh::elemInfo(const dof_id_type id) const
    4051             : {
    4052   109109456 :   return libmesh_map_find(_elem_to_elem_info, id);
    4053             : }
    4054             : 
    4055             : void
    4056        4508 : MooseMesh::computeFiniteVolumeCoords() const
    4057             : {
    4058        4508 :   if (_finite_volume_info_dirty)
    4059           0 :     mooseError("Trying to compute face- and elem-info coords when the information is dirty");
    4060             : 
    4061     2052415 :   for (auto & fi : _all_face_info)
    4062             :   {
    4063             :     // get elem & neighbor elements, and set subdomain ids
    4064     2047907 :     const SubdomainID elem_subdomain_id = fi.elemSubdomainID();
    4065     2047907 :     const SubdomainID neighbor_subdomain_id = fi.neighborSubdomainID();
    4066             : 
    4067     2047907 :     coordTransformFactor(
    4068     2047907 :         *this, elem_subdomain_id, fi.faceCentroid(), fi.faceCoord(), neighbor_subdomain_id);
    4069             :   }
    4070             : 
    4071     1146209 :   for (auto & ei : _elem_to_elem_info)
    4072     1141701 :     coordTransformFactor(
    4073     2283402 :         *this, ei.second.subdomain_id(), ei.second.centroid(), ei.second.coordFactor());
    4074        4508 : }
    4075             : 
    4076             : MooseEnum
    4077      199638 : MooseMesh::partitioning()
    4078             : {
    4079             :   MooseEnum partitioning(
    4080      598914 :       "default=-3 metis=-2 parmetis=-1 linear=0 centroid hilbert_sfc morton_sfc custom", "default");
    4081      199638 :   return partitioning;
    4082             : }
    4083             : 
    4084             : MooseEnum
    4085           0 : MooseMesh::elemTypes()
    4086             : {
    4087             :   MooseEnum elemTypes(
    4088             :       "EDGE EDGE2 EDGE3 EDGE4 QUAD QUAD4 QUAD8 QUAD9 TRI3 TRI6 HEX HEX8 HEX20 HEX27 TET4 TET10 "
    4089           0 :       "PRISM6 PRISM15 PRISM18 PYRAMID5 PYRAMID13 PYRAMID14");
    4090           0 :   return elemTypes;
    4091             : }
    4092             : 
    4093             : void
    4094       34640 : MooseMesh::allowRemoteElementRemoval(const bool allow_remote_element_removal)
    4095             : {
    4096       34640 :   _allow_remote_element_removal = allow_remote_element_removal;
    4097       34640 :   if (_mesh)
    4098       15830 :     _mesh->allow_remote_element_removal(allow_remote_element_removal);
    4099             : 
    4100       34640 :   if (!allow_remote_element_removal)
    4101             :     // If we're not allowing remote element removal now, then we will need deletion later after
    4102             :     // late geoemetric ghosting functors have been added (late geometric ghosting functor addition
    4103             :     // happens when algebraic ghosting functors are added)
    4104       34640 :     _need_delete = true;
    4105       34640 : }
    4106             : 
    4107             : void
    4108       17299 : MooseMesh::deleteRemoteElements()
    4109             : {
    4110       17299 :   _allow_remote_element_removal = true;
    4111       17299 :   if (!_mesh)
    4112           0 :     mooseError("Cannot delete remote elements because we have not yet attached a MeshBase");
    4113             : 
    4114       17299 :   _mesh->allow_remote_element_removal(true);
    4115             : 
    4116       17299 :   _mesh->delete_remote_elements();
    4117       17299 : }
    4118             : 
    4119             : void
    4120        4504 : MooseMesh::cacheFaceInfoVariableOwnership() const
    4121             : {
    4122             :   mooseAssert(
    4123             :       !Threads::in_threads,
    4124             :       "Performing writes to faceInfo variable association maps. This must be done unthreaded!");
    4125             : 
    4126        4504 :   const unsigned int num_eqs = _app.feProblem().es().n_systems();
    4127             : 
    4128     4099611 :   auto face_lambda = [this](const SubdomainID elem_subdomain_id,
    4129             :                             const SubdomainID neighbor_subdomain_id,
    4130             :                             SystemBase & sys,
    4131             :                             std::vector<std::vector<FaceInfo::VarFaceNeighbors>> & face_type_vector)
    4132             :   {
    4133     4099611 :     face_type_vector[sys.number()].resize(sys.nVariables(), FaceInfo::VarFaceNeighbors::NEITHER);
    4134     4099611 :     const auto & variables = sys.getVariables(0);
    4135             : 
    4136     8768127 :     for (const auto & var : variables)
    4137             :     {
    4138     4668516 :       const unsigned int var_num = var->number();
    4139     4668516 :       const unsigned int sys_num = var->sys().number();
    4140     4668516 :       std::set<SubdomainID> var_subdomains = var->blockIDs();
    4141             :       /**
    4142             :        * The following paragraph of code assigns the VarFaceNeighbors
    4143             :        * 1. The face is an internal face of this variable if it is defined on
    4144             :        *    the elem and neighbor subdomains
    4145             :        * 2. The face is an invalid face of this variable if it is neither defined
    4146             :        *    on the elem nor the neighbor subdomains
    4147             :        * 3. If not 1. or 2. then this is a boundary for this variable and the else clause
    4148             :        *    applies
    4149             :        */
    4150     4668516 :       bool var_defined_elem = var_subdomains.find(elem_subdomain_id) != var_subdomains.end();
    4151             :       bool var_defined_neighbor =
    4152     4668516 :           var_subdomains.find(neighbor_subdomain_id) != var_subdomains.end();
    4153     4668516 :       if (var_defined_elem && var_defined_neighbor)
    4154     3906932 :         face_type_vector[sys_num][var_num] = FaceInfo::VarFaceNeighbors::BOTH;
    4155      761584 :       else if (!var_defined_elem && !var_defined_neighbor)
    4156      327057 :         face_type_vector[sys_num][var_num] = FaceInfo::VarFaceNeighbors::NEITHER;
    4157             :       else
    4158             :       {
    4159             :         // this is a boundary face for this variable, set elem or neighbor
    4160      434527 :         if (var_defined_elem)
    4161      429343 :           face_type_vector[sys_num][var_num] = FaceInfo::VarFaceNeighbors::ELEM;
    4162        5184 :         else if (var_defined_neighbor)
    4163        5184 :           face_type_vector[sys_num][var_num] = FaceInfo::VarFaceNeighbors::NEIGHBOR;
    4164             :         else
    4165           0 :           mooseError("Should never get here");
    4166             :       }
    4167     4668516 :     }
    4168     4099611 :   };
    4169             : 
    4170             :   // We loop through the faces and check if they are internal, boundary or external to
    4171             :   // the variables in the problem
    4172     2052363 :   for (FaceInfo & face : _all_face_info)
    4173             :   {
    4174     2047859 :     const SubdomainID elem_subdomain_id = face.elemSubdomainID();
    4175     2047859 :     const SubdomainID neighbor_subdomain_id = face.neighborSubdomainID();
    4176             : 
    4177     2047859 :     auto & face_type_vector = face.faceType();
    4178             : 
    4179     2047859 :     face_type_vector.clear();
    4180     2047859 :     face_type_vector.resize(num_eqs);
    4181             : 
    4182             :     // First, we check the variables in the solver systems (linear/nonlinear)
    4183     4099611 :     for (const auto i : make_range(_app.feProblem().numSolverSystems()))
    4184     2051752 :       face_lambda(elem_subdomain_id,
    4185             :                   neighbor_subdomain_id,
    4186     2051752 :                   _app.feProblem().getSolverSystem(i),
    4187             :                   face_type_vector);
    4188             : 
    4189             :     // Then we check the variables in the auxiliary system
    4190     2047859 :     face_lambda(elem_subdomain_id,
    4191             :                 neighbor_subdomain_id,
    4192     2047859 :                 _app.feProblem().getAuxiliarySystem(),
    4193             :                 face_type_vector);
    4194             :   }
    4195        4504 : }
    4196             : 
    4197             : void
    4198        4504 : MooseMesh::cacheFVElementalDoFs() const
    4199             : {
    4200             :   mooseAssert(!Threads::in_threads,
    4201             :               "Performing writes to elemInfo dof indices. This must be done unthreaded!");
    4202             : 
    4203     2285944 :   auto elem_lambda = [](const ElemInfo & elem_info,
    4204             :                         SystemBase & sys,
    4205             :                         std::vector<std::vector<dof_id_type>> & dof_vector)
    4206             :   {
    4207     2285944 :     if (sys.nFVVariables())
    4208             :     {
    4209     1208331 :       dof_vector[sys.number()].resize(sys.nVariables(), libMesh::DofObject::invalid_id);
    4210     1208331 :       const auto & variables = sys.getVariables(0);
    4211             : 
    4212     3692668 :       for (const auto & var : variables)
    4213     2484337 :         if (var->isFV())
    4214             :         {
    4215     1438666 :           const auto & var_subdomains = var->blockIDs();
    4216             : 
    4217             :           // We will only cache for FV variables and if they live on the current subdomain
    4218     1438666 :           if (var_subdomains.find(elem_info.subdomain_id()) != var_subdomains.end())
    4219             :           {
    4220     1342399 :             std::vector<dof_id_type> indices;
    4221     1342399 :             var->dofMap().dof_indices(elem_info.elem(), indices, var->number());
    4222             :             mooseAssert(indices.size() == 1, "We expect to have only one dof per element!");
    4223     1342399 :             dof_vector[sys.number()][var->number()] = indices[0];
    4224     1342399 :           }
    4225             :         }
    4226             :     }
    4227     2285944 :   };
    4228             : 
    4229        4504 :   const unsigned int num_eqs = _app.feProblem().es().n_systems();
    4230             : 
    4231             :   // We loop through the elements in the mesh and cache the dof indices
    4232             :   // for the corresponding variables.
    4233     1146189 :   for (auto & ei_pair : _elem_to_elem_info)
    4234             :   {
    4235     1141685 :     auto & elem_info = ei_pair.second;
    4236     1141685 :     auto & dof_vector = elem_info.dofIndices();
    4237             : 
    4238     1141685 :     dof_vector.clear();
    4239     1141685 :     dof_vector.resize(num_eqs);
    4240             : 
    4241             :     // First, we cache the dof indices for the variables in the solver systems (linear, nonlinear)
    4242     2285944 :     for (const auto i : make_range(_app.feProblem().numSolverSystems()))
    4243     1144259 :       elem_lambda(elem_info, _app.feProblem().getSolverSystem(i), dof_vector);
    4244             : 
    4245             :     // Then we cache the dof indices for the auxvariables
    4246     1141685 :     elem_lambda(elem_info, _app.feProblem().getAuxiliarySystem(), dof_vector);
    4247             :   }
    4248        4504 : }
    4249             : 
    4250             : void
    4251        4504 : MooseMesh::setupFiniteVolumeMeshData() const
    4252             : {
    4253        4504 :   buildFiniteVolumeInfo();
    4254        4504 :   computeFiniteVolumeCoords();
    4255        4504 :   cacheFaceInfoVariableOwnership();
    4256        4504 :   cacheFVElementalDoFs();
    4257        4504 : }
    4258             : 
    4259             : void
    4260       64301 : MooseMesh::setCoordSystem(const std::vector<SubdomainName> & blocks,
    4261             :                           const MultiMooseEnum & coord_sys)
    4262             : {
    4263      321505 :   TIME_SECTION("setCoordSystem", 5, "Setting Coordinate System");
    4264       64301 :   if (!_provided_coord_blocks.empty() && (_provided_coord_blocks != blocks))
    4265             :   {
    4266           0 :     const std::string param_name = isParamValid("coord_block") ? "coord_block" : "block";
    4267           0 :     mooseWarning("Supplied blocks in the 'setCoordSystem' method do not match the value of the "
    4268             :                  "'Mesh/",
    4269             :                  param_name,
    4270             :                  "' parameter. Did you provide different parameter values for 'Mesh/",
    4271             :                  param_name,
    4272             :                  "' and 'Problem/block'?. We will honor the parameter value from 'Mesh/",
    4273             :                  param_name,
    4274             :                  "'");
    4275             :     mooseAssert(_coord_system_set,
    4276             :                 "If we are arriving here due to a bad specification in the Problem block, then we "
    4277             :                 "should have already set our coordinate system subdomains from the Mesh block");
    4278           0 :     return;
    4279           0 :   }
    4280      193965 :   if (_pars.isParamSetByUser("coord_type") && getParam<MultiMooseEnum>("coord_type") != coord_sys)
    4281           0 :     mooseError("Supplied coordinate systems in the 'setCoordSystem' method do not match the value "
    4282             :                "of the 'Mesh/coord_type' parameter. Did you provide different parameter values for "
    4283             :                "'coord_type' to 'Mesh' and 'Problem'?");
    4284             : 
    4285             :   // If blocks contain ANY_BLOCK_ID, it should be the only block specified, and coord_sys should
    4286             :   // have one and only one entry. In that case, the same coordinate system will be set for all
    4287             :   // subdomains.
    4288       64301 :   if (blocks.size() == 1 && blocks[0] == "ANY_BLOCK_ID")
    4289             :   {
    4290           0 :     if (coord_sys.size() > 1)
    4291           0 :       mooseError("If you specify ANY_BLOCK_ID as the only block, you must also specify a single "
    4292             :                  "coordinate system for it.");
    4293           0 :     if (!_mesh->is_prepared())
    4294           0 :       mooseError(
    4295             :           "You cannot set the coordinate system for ANY_BLOCK_ID before the mesh is prepared. "
    4296             :           "Please call this method after the mesh is prepared.");
    4297           0 :     const auto coord_type = coord_sys.size() == 0
    4298           0 :                                 ? Moose::COORD_XYZ
    4299           0 :                                 : Moose::stringToEnum<Moose::CoordinateSystemType>(coord_sys[0]);
    4300           0 :     for (const auto sid : meshSubdomains())
    4301           0 :       _coord_sys[sid] = coord_type;
    4302           0 :     return;
    4303             :   }
    4304             : 
    4305             :   // If multiple blocks are specified, but one of them is ANY_BLOCK_ID, let's emit a helpful error
    4306       64301 :   if (std::find(blocks.begin(), blocks.end(), "ANY_BLOCK_ID") != blocks.end())
    4307           0 :     mooseError("You cannot specify ANY_BLOCK_ID together with other blocks in the "
    4308             :                "setCoordSystem() method. If you want to set the same coordinate system for all "
    4309             :                "blocks, use ANY_BLOCK_ID as the only block.");
    4310             : 
    4311       64301 :   auto subdomains = meshSubdomains();
    4312             :   // It's possible that a user has called this API before the mesh is prepared and consequently we
    4313             :   // don't yet have the subdomains in meshSubdomains()
    4314       64700 :   for (const auto & sub_name : blocks)
    4315             :   {
    4316         399 :     const auto sub_id = getSubdomainID(sub_name);
    4317         399 :     subdomains.insert(sub_id);
    4318             :   }
    4319             : 
    4320       64301 :   if (coord_sys.size() <= 1)
    4321             :   {
    4322             :     // We will specify the same coordinate system for all blocks
    4323       64277 :     const auto coord_type = coord_sys.size() == 0
    4324       64277 :                                 ? Moose::COORD_XYZ
    4325       64277 :                                 : Moose::stringToEnum<Moose::CoordinateSystemType>(coord_sys[0]);
    4326      154491 :     for (const auto sid : subdomains)
    4327       90214 :       _coord_sys[sid] = coord_type;
    4328             :   }
    4329             :   else
    4330             :   {
    4331          24 :     if (blocks.size() != coord_sys.size())
    4332           0 :       mooseError("Number of blocks and coordinate systems does not match.");
    4333             : 
    4334          96 :     for (const auto i : index_range(blocks))
    4335             :     {
    4336          72 :       SubdomainID sid = getSubdomainID(blocks[i]);
    4337             :       Moose::CoordinateSystemType coord_type =
    4338          72 :           Moose::stringToEnum<Moose::CoordinateSystemType>(coord_sys[i]);
    4339          72 :       _coord_sys[sid] = coord_type;
    4340             :     }
    4341             : 
    4342          96 :     for (const auto & sid : subdomains)
    4343          72 :       if (_coord_sys.find(sid) == _coord_sys.end())
    4344           0 :         mooseError("Subdomain '" + Moose::stringify(sid) +
    4345             :                    "' does not have a coordinate system specified.");
    4346             :   }
    4347             : 
    4348       64301 :   _coord_system_set = true;
    4349             : 
    4350       64301 :   updateCoordTransform();
    4351       64301 : }
    4352             : 
    4353             : Moose::CoordinateSystemType
    4354  2280506188 : MooseMesh::getCoordSystem(SubdomainID sid) const
    4355             : {
    4356  2280506188 :   auto it = _coord_sys.find(sid);
    4357  2280506188 :   if (it != _coord_sys.end())
    4358  4561012376 :     return (*it).second;
    4359             :   else
    4360           0 :     mooseError("Requested subdomain ", sid, " does not exist.");
    4361             : }
    4362             : 
    4363             : Moose::CoordinateSystemType
    4364       55126 : MooseMesh::getUniqueCoordSystem() const
    4365             : {
    4366       55126 :   const auto unique_system = _coord_sys.find(*meshSubdomains().begin())->second;
    4367             :   // Check that it is actually unique
    4368       55126 :   bool result = std::all_of(
    4369       55126 :       std::next(_coord_sys.begin()),
    4370       55126 :       _coord_sys.end(),
    4371        4346 :       [unique_system](
    4372             :           typename std::unordered_map<SubdomainID, Moose::CoordinateSystemType>::const_reference
    4373        4346 :               item) { return (item.second == unique_system); });
    4374       55126 :   if (!result)
    4375           0 :     mooseError("The unique coordinate system of the mesh was requested by the mesh contains "
    4376             :                "multiple blocks with different coordinate systems");
    4377             : 
    4378       55126 :   if (usingGeneralAxisymmetricCoordAxes())
    4379           0 :     mooseError("General axisymmetric coordinate axes are being used, and it is currently "
    4380             :                "conservatively assumed that in this case there is no unique coordinate system.");
    4381             : 
    4382       55126 :   return unique_system;
    4383             : }
    4384             : 
    4385             : const std::map<SubdomainID, Moose::CoordinateSystemType> &
    4386       67305 : MooseMesh::getCoordSystem() const
    4387             : {
    4388       67305 :   return _coord_sys;
    4389             : }
    4390             : 
    4391             : void
    4392           0 : MooseMesh::setAxisymmetricCoordAxis(const MooseEnum & rz_coord_axis)
    4393             : {
    4394           0 :   _rz_coord_axis = rz_coord_axis;
    4395             : 
    4396           0 :   updateCoordTransform();
    4397           0 : }
    4398             : 
    4399             : void
    4400          17 : MooseMesh::setGeneralAxisymmetricCoordAxes(
    4401             :     const std::vector<SubdomainName> & blocks,
    4402             :     const std::vector<std::pair<Point, RealVectorValue>> & axes)
    4403             : {
    4404             :   // Set the axes for the given blocks
    4405             :   mooseAssert(blocks.size() == axes.size(), "Blocks and axes vectors must be the same length.");
    4406          58 :   for (const auto i : index_range(blocks))
    4407             :   {
    4408          41 :     const auto subdomain_id = getSubdomainID(blocks[i]);
    4409          41 :     const auto it = _coord_sys.find(subdomain_id);
    4410          41 :     if (it == _coord_sys.end())
    4411           0 :       mooseError("The block '",
    4412           0 :                  blocks[i],
    4413             :                  "' has not set a coordinate system. Make sure to call setCoordSystem() before "
    4414             :                  "setGeneralAxisymmetricCoordAxes().");
    4415             :     else
    4416             :     {
    4417          41 :       if (it->second == Moose::COORD_RZ)
    4418             :       {
    4419          41 :         const auto direction = axes[i].second;
    4420          41 :         if (direction.is_zero())
    4421           0 :           mooseError("Only nonzero vectors may be supplied for RZ directions.");
    4422             : 
    4423          41 :         _subdomain_id_to_rz_coord_axis[subdomain_id] =
    4424          82 :             std::make_pair(axes[i].first, direction.unit());
    4425             :       }
    4426             :       else
    4427           0 :         mooseError("The block '",
    4428           0 :                    blocks[i],
    4429             :                    "' was provided in setGeneralAxisymmetricCoordAxes(), but the coordinate system "
    4430             :                    "for this block is not 'RZ'.");
    4431             :     }
    4432             :   }
    4433             : 
    4434             :   // Make sure there are no RZ blocks that still do not have axes
    4435          17 :   const auto all_subdomain_ids = meshSubdomains();
    4436          70 :   for (const auto subdomain_id : all_subdomain_ids)
    4437          94 :     if (getCoordSystem(subdomain_id) == Moose::COORD_RZ &&
    4438          41 :         !_subdomain_id_to_rz_coord_axis.count(subdomain_id))
    4439           0 :       mooseError("The block '",
    4440           0 :                  getSubdomainName(subdomain_id),
    4441             :                  "' was specified to use the 'RZ' coordinate system but was not given in "
    4442             :                  "setGeneralAxisymmetricCoordAxes().");
    4443             : 
    4444          17 :   updateCoordTransform();
    4445          17 : }
    4446             : 
    4447             : const std::pair<Point, RealVectorValue> &
    4448     1063075 : MooseMesh::getGeneralAxisymmetricCoordAxis(SubdomainID subdomain_id) const
    4449             : {
    4450     1063075 :   auto it = _subdomain_id_to_rz_coord_axis.find(subdomain_id);
    4451     1063075 :   if (it != _subdomain_id_to_rz_coord_axis.end())
    4452     2126150 :     return (*it).second;
    4453             :   else
    4454           0 :     mooseError("Requested subdomain ", subdomain_id, " does not exist.");
    4455             : }
    4456             : 
    4457             : bool
    4458    24320584 : MooseMesh::usingGeneralAxisymmetricCoordAxes() const
    4459             : {
    4460    24320584 :   return _subdomain_id_to_rz_coord_axis.size() > 0;
    4461             : }
    4462             : 
    4463             : void
    4464       67305 : MooseMesh::updateCoordTransform()
    4465             : {
    4466       67305 :   if (!_coord_transform)
    4467       67284 :     _coord_transform = std::make_unique<MooseAppCoordTransform>(*this);
    4468             :   else
    4469          21 :     _coord_transform->setCoordinateSystem(*this);
    4470       67305 : }
    4471             : 
    4472             : unsigned int
    4473    20691312 : MooseMesh::getAxisymmetricRadialCoord() const
    4474             : {
    4475    20691312 :   if (usingGeneralAxisymmetricCoordAxes())
    4476           0 :     mooseError("getAxisymmetricRadialCoord() should not be called if "
    4477             :                "setGeneralAxisymmetricCoordAxes() has been called.");
    4478             : 
    4479    20691312 :   if (_rz_coord_axis == 0)
    4480      133200 :     return 1; // if the rotation axis is x (0), then the radial direction is y (1)
    4481             :   else
    4482    20558112 :     return 0; // otherwise the radial direction is assumed to be x, i.e., the rotation axis is y
    4483             : }
    4484             : 
    4485             : void
    4486       59757 : MooseMesh::checkCoordinateSystems()
    4487             : {
    4488    27656481 :   for (const auto & elem : getMesh().element_ptr_range())
    4489             :   {
    4490    13798365 :     SubdomainID sid = elem->subdomain_id();
    4491    13798365 :     if (_coord_sys[sid] == Moose::COORD_RZ && elem->dim() == 3)
    4492           3 :       mooseError("An RZ coordinate system was requested for subdomain " + Moose::stringify(sid) +
    4493             :                  " which contains 3D elements.");
    4494    13798362 :     if (_coord_sys[sid] == Moose::COORD_RSPHERICAL && elem->dim() > 1)
    4495           0 :       mooseError("An RSPHERICAL coordinate system was requested for subdomain " +
    4496           0 :                  Moose::stringify(sid) + " which contains 2D or 3D elements.");
    4497       59754 :   }
    4498       59754 : }
    4499             : 
    4500             : void
    4501        2022 : MooseMesh::setCoordData(const MooseMesh & other_mesh)
    4502             : {
    4503        2022 :   _coord_sys = other_mesh._coord_sys;
    4504        2022 :   _rz_coord_axis = other_mesh._rz_coord_axis;
    4505        2022 :   _subdomain_id_to_rz_coord_axis = other_mesh._subdomain_id_to_rz_coord_axis;
    4506        2022 : }
    4507             : 
    4508             : const MooseUnits &
    4509           2 : MooseMesh::lengthUnit() const
    4510             : {
    4511             :   mooseAssert(_coord_transform, "This must be non-null");
    4512           2 :   return _coord_transform->lengthUnit();
    4513             : }
    4514             : 
    4515             : void
    4516       67184 : MooseMesh::checkDuplicateSubdomainNames()
    4517             : {
    4518       67184 :   std::map<SubdomainName, SubdomainID> subdomain;
    4519      161536 :   for (const auto & sbd_id : _mesh_subdomains)
    4520             :   {
    4521       94355 :     std::string sub_name = getSubdomainName(sbd_id);
    4522       94355 :     if (!sub_name.empty() && subdomain.count(sub_name) > 0)
    4523           6 :       mooseError("The subdomain name ",
    4524             :                  sub_name,
    4525             :                  " is used for both subdomain with ID=",
    4526           3 :                  subdomain[sub_name],
    4527             :                  " and ID=",
    4528             :                  sbd_id,
    4529             :                  ", Please rename one of them!");
    4530             :     else
    4531       94352 :       subdomain[sub_name] = sbd_id;
    4532       94352 :   }
    4533       67181 : }
    4534             : 
    4535             : const std::vector<QpMap> &
    4536         800 : MooseMesh::getPRefinementMapHelper(
    4537             :     const Elem & elem,
    4538             :     const std::map<std::pair<ElemType, unsigned int>, std::vector<QpMap>> & map) const
    4539             : {
    4540             :   // We are actually seeking the map stored with the p_level - 1 key, e.g. the refinement map that
    4541             :   // maps from the previous p_level to this element's p_level
    4542         800 :   return libmesh_map_find(map,
    4543             :                           std::make_pair(elem.type(), cast_int<unsigned int>(elem.p_level() - 1)));
    4544             : }
    4545             : 
    4546             : const std::vector<QpMap> &
    4547           0 : MooseMesh::getPCoarseningMapHelper(
    4548             :     const Elem & elem,
    4549             :     const std::map<std::pair<ElemType, unsigned int>, std::vector<QpMap>> & map) const
    4550             : {
    4551             :   mooseAssert(elem.active() && elem.p_refinement_flag() == Elem::JUST_COARSENED,
    4552             :               "These are the conditions that should be met for requesting a coarsening map");
    4553           0 :   return libmesh_map_find(map, std::make_pair(elem.type(), elem.p_level()));
    4554             : }
    4555             : 
    4556             : const std::vector<QpMap> &
    4557         800 : MooseMesh::getPRefinementMap(const Elem & elem) const
    4558             : {
    4559         800 :   return getPRefinementMapHelper(elem, _elem_type_to_p_refinement_map);
    4560             : }
    4561             : 
    4562             : const std::vector<QpMap> &
    4563           0 : MooseMesh::getPRefinementSideMap(const Elem & elem) const
    4564             : {
    4565           0 :   return getPRefinementMapHelper(elem, _elem_type_to_p_refinement_side_map);
    4566             : }
    4567             : 
    4568             : const std::vector<QpMap> &
    4569           0 : MooseMesh::getPCoarseningMap(const Elem & elem) const
    4570             : {
    4571           0 :   return getPCoarseningMapHelper(elem, _elem_type_to_p_coarsening_map);
    4572             : }
    4573             : 
    4574             : const std::vector<QpMap> &
    4575           0 : MooseMesh::getPCoarseningSideMap(const Elem & elem) const
    4576             : {
    4577           0 :   return getPCoarseningMapHelper(elem, _elem_type_to_p_coarsening_side_map);
    4578             : }
    4579             : 
    4580             : bool
    4581       26451 : MooseMesh::skipNoncriticalPartitioning() const
    4582             : {
    4583       26451 :   return _mesh->skip_noncritical_partitioning();
    4584             : }

Generated by: LCOV version 1.14