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