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/mesh_refinement.h"
55 #include "libmesh/quadrature.h"
56 #include "libmesh/boundary_info.h"
57 #include "libmesh/periodic_boundaries.h"
58 #include "libmesh/quadrature_gauss.h"
59 #include "libmesh/point_locator_base.h"
60 #include "libmesh/default_coupling.h"
61 #include "libmesh/ghost_point_neighbors.h"
62 #include "libmesh/fe_type.h"
63 #include "libmesh/enum_to_string.h"
64 
65 static const int GRAIN_SIZE =
66  1; // the grain_size does not have much influence on our execution speed
67 
68 using namespace libMesh;
69 
70 // Make newer nanoflann API compatible with older nanoflann versions
71 #if NANOFLANN_VERSION < 0x150
72 namespace nanoflann
73 {
74 typedef SearchParams SearchParameters;
75 
76 template <typename T, typename U>
77 using ResultItem = std::pair<T, U>;
78 }
79 #endif
80 
83 {
85 
86  MooseEnum parallel_type("DEFAULT REPLICATED DISTRIBUTED", "DEFAULT");
87  params.addParam<MooseEnum>("parallel_type",
88  parallel_type,
89  "DEFAULT: Use libMesh::ReplicatedMesh unless --distributed-mesh is "
90  "specified on the command line "
91  "REPLICATED: Always use libMesh::ReplicatedMesh "
92  "DISTRIBUTED: Always use libMesh::DistributedMesh");
93 
94  params.addParam<bool>(
95  "allow_renumbering",
96  true,
97  "If allow_renumbering=false, node and element numbers are kept fixed until deletion");
98 
99  // TODO: this parameter does not belong here, it's only for FileMesh
100  params.addParam<bool>("nemesis",
101  false,
102  "If nemesis=true and file=foo.e, actually reads "
103  "foo.e.N.0, foo.e.N.1, ... foo.e.N.N-1, "
104  "where N = # CPUs, with NemesisIO.");
105 
106  params.addParam<MooseEnum>(
107  "partitioner",
108  partitioning(),
109  "Specifies a mesh partitioner to use when splitting the mesh for a parallel computation.");
110  MooseEnum direction("x y z radial");
111  params.addParam<MooseEnum>("centroid_partitioner_direction",
112  direction,
113  "Specifies the sort direction if using the centroid partitioner. "
114  "Available options: x, y, z, radial");
115 
116  MooseEnum patch_update_strategy("never always auto iteration", "never");
117  params.addParam<MooseEnum>(
118  "patch_update_strategy",
119  patch_update_strategy,
120  "How often to update the geometric search 'patch'. The default is to "
121  "never update it (which is the most efficient but could be a problem "
122  "with lots of relative motion). 'always' will update the patch for all "
123  "secondary nodes at the beginning of every timestep which might be time "
124  "consuming. 'auto' will attempt to determine at the start of which "
125  "timesteps the patch for all secondary nodes needs to be updated automatically."
126  "'iteration' updates the patch at every nonlinear iteration for a "
127  "subset of secondary nodes for which penetration is not detected. If there "
128  "can be substantial relative motion between the primary and secondary surfaces "
129  "during the nonlinear iterations within a timestep, it is advisable to use "
130  "'iteration' option to ensure accurate contact detection.");
131 
132  // Note: This parameter is named to match 'construct_side_list_from_node_list' in SetupMeshAction
133  params.addParam<bool>(
134  "construct_node_list_from_side_list",
135  true,
136  "Whether or not to generate nodesets from the sidesets (currently often required).");
137  params.addParam<bool>(
138  "displace_node_list_by_side_list",
139  false,
140  "Whether to renumber existing nodesets with ids matching sidesets that "
141  "lack names matching sidesets, when constructing nodesets from sidesets via the default "
142  "'construct_node_list_from_side_list' option, rather than to merge them with the sideset.");
143  params.addParam<unsigned int>(
144  "patch_size", 40, "The number of nodes to consider in the NearestNode neighborhood.");
145  params.addParam<unsigned int>("ghosting_patch_size",
146  "The number of nearest neighbors considered "
147  "for ghosting purposes when 'iteration' "
148  "patch update strategy is used. Default is "
149  "5 * patch_size.");
150  params.addParam<unsigned int>("max_leaf_size",
151  10,
152  "The maximum number of points in each leaf of the KDTree used in "
153  "the nearest neighbor search. As the leaf size becomes larger,"
154  "KDTree construction becomes faster but the nearest neighbor search"
155  "becomes slower.");
156 
157  params.addParam<bool>("build_all_side_lowerd_mesh",
158  false,
159  "True to build the lower-dimensional mesh for all sides.");
160 
161  params.addParam<bool>("skip_refine_when_use_split",
162  true,
163  "True to skip uniform refinements when using a pre-split mesh.");
164 
165  params.addParam<std::vector<SubdomainID>>(
166  "add_subdomain_ids",
167  "The listed subdomain ids will be assumed valid for the mesh. This permits setting up "
168  "subdomain restrictions for subdomains initially containing no elements, which can occur, "
169  "for example, in additive manufacturing simulations which dynamically add and remove "
170  "elements. Names for this subdomains may be provided using add_subdomain_names. In this case "
171  "this list and add_subdomain_names must contain the same number of items.");
172  params.addParam<std::vector<SubdomainName>>(
173  "add_subdomain_names",
174  "The listed subdomain names will be assumed valid for the mesh. This permits setting up "
175  "subdomain restrictions for subdomains initially containing no elements, which can occur, "
176  "for example, in additive manufacturing simulations which dynamically add and remove "
177  "elements. IDs for this subdomains may be provided using add_subdomain_ids. Otherwise IDs "
178  "are automatically assigned. In case add_subdomain_ids is set too, both lists must contain "
179  "the same number of items.");
180 
181  params.addParam<std::vector<BoundaryID>>(
182  "add_sideset_ids",
183  "The listed sideset ids will be assumed valid for the mesh. This permits setting up boundary "
184  "restrictions for sidesets initially containing no sides. Names for this sidesets may be "
185  "provided using add_sideset_names. In this case this list and add_sideset_names must contain "
186  "the same number of items.");
187  params.addParam<std::vector<BoundaryName>>(
188  "add_sideset_names",
189  "The listed sideset names will be assumed valid for the mesh. This permits setting up "
190  "boundary restrictions for sidesets initially containing no sides. Ids for this sidesets may "
191  "be provided using add_sideset_ids. In this case this list and add_sideset_ids must contain "
192  "the same number of items.");
193 
194  params.addParam<std::vector<BoundaryID>>(
195  "add_nodeset_ids",
196  "The listed nodeset ids will be assumed valid for the mesh. This permits setting up boundary "
197  "restrictions for node initially containing no sides. Names for this nodesets may be "
198  "provided using add_nodeset_names. In this case this list and add_nodeset_names must contain "
199  "the same number of items.");
200  params.addParam<std::vector<BoundaryName>>(
201  "add_nodeset_names",
202  "The listed nodeset names will be assumed valid for the mesh. This permits setting up "
203  "boundary restrictions for nodesets initially containing no sides. Ids for this nodesets may "
204  "be provided using add_nodesets_ids. In this case this list and add_nodesets_ids must "
205  "contain the same number of items.");
206 
208 
209  // This indicates that the derived mesh type accepts a MeshGenerator, and should be set to true in
210  // derived types that do so.
211  params.addPrivateParam<bool>("_mesh_generator_mesh", false);
212 
213  // Whether or not the mesh is pre split
214  params.addPrivateParam<bool>("_is_split", false);
215 
216  params.registerBase("MooseMesh");
217 
218  // groups
219  params.addParamNamesToGroup("patch_update_strategy patch_size max_leaf_size", "Geometric search");
220  params.addParamNamesToGroup("nemesis", "Advanced");
221  params.addParamNamesToGroup("add_subdomain_ids add_subdomain_names add_sideset_ids "
222  "add_sideset_names add_nodeset_ids add_nodeset_names",
223  "Pre-declaration of future mesh sub-entities");
224  params.addParamNamesToGroup("construct_node_list_from_side_list build_all_side_lowerd_mesh "
225  "displace_node_list_by_side_list",
226  "Automatic definition of mesh element sides entities");
227  params.addParamNamesToGroup("partitioner centroid_partitioner_direction", "Partitioning");
228 
229  return params;
230 }
231 
233  : MooseObject(parameters),
234  Restartable(this, "Mesh"),
235  PerfGraphInterface(this),
236  _parallel_type(getParam<MooseEnum>("parallel_type").getEnum<MooseMesh::ParallelType>()),
237  _use_distributed_mesh(false),
238  _distribution_overridden(false),
239  _parallel_type_overridden(false),
240  _mesh(nullptr),
241  _partitioner_name(getParam<MooseEnum>("partitioner")),
242  _partitioner_overridden(false),
243  _custom_partitioner_requested(false),
244  _uniform_refine_level(0),
245  _skip_refine_when_use_split(getParam<bool>("skip_refine_when_use_split")),
246  _skip_deletion_repartition_after_refine(false),
247  _is_nemesis(getParam<bool>("nemesis")),
248  _node_to_elem_map_built(false),
249  _node_to_active_semilocal_elem_map_built(false),
250  _patch_size(getParam<unsigned int>("patch_size")),
251  _ghosting_patch_size(isParamValid("ghosting_patch_size")
252  ? getParam<unsigned int>("ghosting_patch_size")
253  : 5 * _patch_size),
254  _max_leaf_size(getParam<unsigned int>("max_leaf_size")),
255  _patch_update_strategy(
256  getParam<MooseEnum>("patch_update_strategy").getEnum<Moose::PatchUpdateType>()),
257  _regular_orthogonal_mesh(false),
258  _is_split(getParam<bool>("_is_split")),
259  _has_lower_d(false),
260  _allow_recovery(true),
261  _construct_node_list_from_side_list(getParam<bool>("construct_node_list_from_side_list")),
262  _displace_node_list_by_side_list(getParam<bool>("displace_node_list_by_side_list")),
263  _need_delete(false),
264  _allow_remote_element_removal(true),
265  _need_ghost_ghosted_boundaries(true),
266  _is_displaced(false),
267  _coord_sys(
268  declareRestartableData<std::map<SubdomainID, Moose::CoordinateSystemType>>("coord_sys")),
269  _rz_coord_axis(getParam<MooseEnum>("rz_coord_axis")),
270  _coord_system_set(false),
271  _doing_p_refinement(false)
272 {
273  if (isParamValid("ghosting_patch_size") && (_patch_update_strategy != Moose::Iteration))
274  mooseError("Ghosting patch size parameter has to be set in the mesh block "
275  "only when 'iteration' patch update strategy is used.");
276 
278  paramError("displace_node_list_by_side_list",
279  "'Mesh/displace_node_list_by_side_list' is true, but unused when "
280  "'Mesh/construct_node_list_from_side_list' is false");
281 
282  if (isParamValid("coord_block"))
283  {
284  if (isParamValid("block"))
285  paramWarning("block",
286  "You set both 'Mesh/block' and 'Mesh/coord_block'. The value of "
287  "'Mesh/coord_block' will be used.");
288 
289  _provided_coord_blocks = getParam<std::vector<SubdomainName>>("coord_block");
290  }
291  else if (isParamValid("block"))
292  _provided_coord_blocks = getParam<std::vector<SubdomainName>>("block");
293 
294  if (getParam<bool>("build_all_side_lowerd_mesh"))
295  // Do not initially allow removal of remote elements
297 
299 
300 #ifdef MOOSE_KOKKOS_ENABLED
301  if (_app.isKokkosAvailable())
302  _kokkos_mesh = std::make_unique<Moose::Kokkos::Mesh>(*this);
303 #endif
304 }
305 
306 MooseMesh::MooseMesh(const MooseMesh & other_mesh)
307  : MooseObject(other_mesh._pars),
308  Restartable(this, "Mesh"),
309  PerfGraphInterface(this, "CopiedMesh"),
310  _built_from_other_mesh(true),
311  _parallel_type(other_mesh._parallel_type),
312  _use_distributed_mesh(other_mesh._use_distributed_mesh),
313  _distribution_overridden(other_mesh._distribution_overridden),
314  _parallel_type_overridden(other_mesh._parallel_type_overridden),
315  _mesh(other_mesh.getMesh().clone()),
316  _partitioner_name(other_mesh._partitioner_name),
317  _partitioner_overridden(other_mesh._partitioner_overridden),
318  _custom_partitioner_requested(other_mesh._custom_partitioner_requested),
319  _uniform_refine_level(other_mesh.uniformRefineLevel()),
320  _skip_refine_when_use_split(other_mesh._skip_refine_when_use_split),
321  _skip_deletion_repartition_after_refine(other_mesh._skip_deletion_repartition_after_refine),
322  _is_nemesis(false),
323  _node_to_elem_map_built(false),
324  _node_to_active_semilocal_elem_map_built(false),
325  _patch_size(other_mesh._patch_size),
326  _ghosting_patch_size(other_mesh._ghosting_patch_size),
327  _max_leaf_size(other_mesh._max_leaf_size),
328  _patch_update_strategy(other_mesh._patch_update_strategy),
329  _regular_orthogonal_mesh(false),
330  _is_split(other_mesh._is_split),
331  _lower_d_interior_blocks(other_mesh._lower_d_interior_blocks),
332  _lower_d_boundary_blocks(other_mesh._lower_d_boundary_blocks),
333  _has_lower_d(other_mesh._has_lower_d),
334  _allow_recovery(other_mesh._allow_recovery),
335  _construct_node_list_from_side_list(other_mesh._construct_node_list_from_side_list),
336  _displace_node_list_by_side_list(other_mesh._displace_node_list_by_side_list),
337  _need_delete(other_mesh._need_delete),
338  _allow_remote_element_removal(other_mesh._allow_remote_element_removal),
339  _need_ghost_ghosted_boundaries(other_mesh._need_ghost_ghosted_boundaries),
340  _coord_sys(other_mesh._coord_sys),
341  _rz_coord_axis(other_mesh._rz_coord_axis),
342  _subdomain_id_to_rz_coord_axis(other_mesh._subdomain_id_to_rz_coord_axis),
343  _coord_system_set(other_mesh._coord_system_set),
344  _provided_coord_blocks(other_mesh._provided_coord_blocks),
345  _doing_p_refinement(other_mesh._doing_p_refinement)
346 {
347  // Note: this calls BoundaryInfo::operator= without changing the
348  // ownership semantics of either Mesh's BoundaryInfo object.
349  getMesh().get_boundary_info() = other_mesh.getMesh().get_boundary_info();
350 
351  const std::set<SubdomainID> & subdomains = other_mesh.meshSubdomains();
352  for (const auto & sbd_id : subdomains)
353  setSubdomainName(sbd_id, other_mesh.getMesh().subdomain_name(sbd_id));
354 
355  // Get references to BoundaryInfo objects to make the code below cleaner...
356  const BoundaryInfo & other_boundary_info = other_mesh.getMesh().get_boundary_info();
357  BoundaryInfo & boundary_info = getMesh().get_boundary_info();
358 
359  // Use the other BoundaryInfo object to build the list of side boundary ids
360  std::vector<BoundaryID> side_boundaries;
361  other_boundary_info.build_side_boundary_ids(side_boundaries);
362 
363  // Assign those boundary ids in our BoundaryInfo object
364  for (const auto & side_bnd_id : side_boundaries)
365  boundary_info.sideset_name(side_bnd_id) = other_boundary_info.get_sideset_name(side_bnd_id);
366 
367  // Do the same thing for node boundary ids
368  std::vector<BoundaryID> node_boundaries;
369  other_boundary_info.build_node_boundary_ids(node_boundaries);
370 
371  for (const auto & node_bnd_id : node_boundaries)
372  boundary_info.nodeset_name(node_bnd_id) = other_boundary_info.get_nodeset_name(node_bnd_id);
373 
374  _bounds.resize(other_mesh._bounds.size());
375  for (std::size_t i = 0; i < _bounds.size(); ++i)
376  {
377  _bounds[i].resize(other_mesh._bounds[i].size());
378  for (std::size_t j = 0; j < _bounds[i].size(); ++j)
379  _bounds[i][j] = other_mesh._bounds[i][j];
380  }
381 
383 
384 #ifdef MOOSE_KOKKOS_ENABLED
385  if (_app.isKokkosAvailable())
386  _kokkos_mesh = std::make_unique<Moose::Kokkos::Mesh>(*this);
387 #endif
388 }
389 
391 {
392  freeBndNodes();
393  freeBndElems();
395 }
396 
397 void
399 {
400  // free memory
401  for (auto & bnode : _bnd_nodes)
402  delete bnode;
403 
404  for (auto & it : _node_set_nodes)
405  it.second.clear();
406 
407  _node_set_nodes.clear();
408 
409  for (auto & it : _bnd_node_ids)
410  it.second.clear();
411 
412  _bnd_node_ids.clear();
413  _bnd_node_range.reset();
414 }
415 
416 void
418 {
419  // free memory
420  for (auto & belem : _bnd_elems)
421  delete belem;
422 
423  for (auto & it : _bnd_elem_ids)
424  it.second.clear();
425 
426  _bnd_elem_ids.clear();
427  _bnd_elem_range.reset();
428 }
429 
430 bool
431 MooseMesh::prepare(const MeshBase * const mesh_to_clone)
432 {
433  TIME_SECTION("prepare", 2, "Preparing Mesh", true);
434 
435  bool called_prepare_for_use = false;
436 
437  mooseAssert(_mesh, "The MeshBase has not been constructed");
438 
439  if (!dynamic_cast<DistributedMesh *>(&getMesh()) || _is_nemesis)
440  // For whatever reason we do not want to allow renumbering here nor ever in the future?
441  getMesh().allow_renumbering(false);
442 
443  if (mesh_to_clone)
444  {
445  mooseAssert(mesh_to_clone->is_prepared(),
446  "The mesh we wish to clone from must already be prepared");
447  _mesh = mesh_to_clone->clone();
448  _moose_mesh_prepared = false;
449  }
450  else if (!_mesh->is_prepared())
451  {
452  _mesh->prepare_for_use();
453  _moose_mesh_prepared = false;
454  called_prepare_for_use = true;
455  }
456 
458  return called_prepare_for_use;
459 
460  // Collect (local) subdomain IDs
461  _mesh_subdomains.clear();
462  for (const auto & elem : getMesh().element_ptr_range())
464 
465  // add explicitly requested subdomains
466  if (isParamValid("add_subdomain_ids") && !isParamValid("add_subdomain_names"))
467  {
468  // only subdomain ids are explicitly given
469  const auto & add_subdomain_id = getParam<std::vector<SubdomainID>>("add_subdomain_ids");
470  _mesh_subdomains.insert(add_subdomain_id.begin(), add_subdomain_id.end());
471  }
472  else if (isParamValid("add_subdomain_ids") && isParamValid("add_subdomain_names"))
473  {
474  const auto add_subdomain =
475  getParam<SubdomainID, SubdomainName>("add_subdomain_ids", "add_subdomain_names");
476  for (const auto & [sub_id, sub_name] : add_subdomain)
477  {
478  // add subdomain id
479  _mesh_subdomains.insert(sub_id);
480  // set name of the subdomain just added
481  setSubdomainName(sub_id, sub_name);
482  }
483  }
484  else if (isParamValid("add_subdomain_names"))
485  {
486  // the user has defined add_subdomain_names, but not add_subdomain_ids
487  const auto & add_subdomain_names = getParam<std::vector<SubdomainName>>("add_subdomain_names");
488 
489  // to define subdomain ids, we need the largest subdomain id defined yet.
490  subdomain_id_type offset = 0;
491  if (!_mesh_subdomains.empty())
492  offset = *_mesh_subdomains.rbegin();
493 
494  // add all subdomains (and auto-assign ids)
495  for (const SubdomainName & sub_name : add_subdomain_names)
496  {
497  // to avoid two subdomains with the same ID (notably on recover)
499  continue;
500  const auto sub_id = ++offset;
501  // add subdomain id
502  _mesh_subdomains.insert(sub_id);
503  // set name of the subdomain just added
504  setSubdomainName(sub_id, sub_name);
505  }
506  }
507 
508  // Make sure nodesets have been generated
510 
511  // Collect (local) boundary IDs
512  const std::set<BoundaryID> & local_bids = getMesh().get_boundary_info().get_boundary_ids();
513  _mesh_boundary_ids.insert(local_bids.begin(), local_bids.end());
514 
515  const std::set<BoundaryID> & local_node_bids =
517  _mesh_nodeset_ids.insert(local_node_bids.begin(), local_node_bids.end());
518 
519  const std::set<BoundaryID> & local_side_bids =
521  _mesh_sideset_ids.insert(local_side_bids.begin(), local_side_bids.end());
522 
523  // Add explicitly requested sidesets/nodesets
524  // This is done *after* the side boundaries (e.g. "right", ...) have been generated.
525  auto add_sets = [this](const bool sidesets, auto & set_ids)
526  {
527  const std::string type = sidesets ? "sideset" : "nodeset";
528  const std::string id_param = "add_" + type + "_ids";
529  const std::string name_param = "add_" + type + "_names";
530 
531  if (isParamValid(id_param))
532  {
533  const auto & add_ids = getParam<std::vector<BoundaryID>>(id_param);
534  _mesh_boundary_ids.insert(add_ids.begin(), add_ids.end());
535  set_ids.insert(add_ids.begin(), add_ids.end());
537  {
538  const auto & add_names = getParam<std::vector<BoundaryName>>(name_param);
539  mooseAssert(add_names.size() == add_ids.size(),
540  "Id and name sets must be the same size when adding.");
541  for (const auto i : index_range(add_ids))
542  setBoundaryName(add_ids[i], add_names[i]);
543  }
544  }
545  else if (isParamValid(name_param))
546  {
547  // the user has defined names, but not ids
548  const auto & add_names = getParam<std::vector<BoundaryName>>(name_param);
549 
550  auto & mesh_ids = sidesets ? _mesh_sideset_ids : _mesh_nodeset_ids;
551 
552  // to define ids, we need the largest id defined yet.
553  boundary_id_type offset = 0;
554  if (!mesh_ids.empty())
555  offset = *mesh_ids.rbegin();
556  if (!_mesh_boundary_ids.empty())
557  offset = std::max(offset, *_mesh_boundary_ids.rbegin());
558 
559  // add all sidesets/nodesets (and auto-assign ids)
560  for (const auto & name : add_names)
561  {
562  // to avoid two sets with the same ID (notably on recover)
564  continue;
565  const auto id = ++offset;
566  // add sideset id
567  _mesh_boundary_ids.insert(id);
568  set_ids.insert(id);
569  // set name of the sideset just added
570  setBoundaryName(id, name);
571  }
572  }
573  };
574 
575  add_sets(true, _mesh_sideset_ids);
576  add_sets(false, _mesh_nodeset_ids);
577 
578  // Communicate subdomain and boundary IDs if this is a parallel mesh
579  if (!getMesh().is_serial())
580  {
585  }
586 
588  {
589  if (!_coord_system_set)
590  setCoordSystem(_provided_coord_blocks, getParam<MultiMooseEnum>("coord_type"));
591  else if (_pars.isParamSetByUser("coord_type"))
592  mooseError(
593  "Trying to set coordinate system type information based on the user input file, but "
594  "the coordinate system type information has already been set programmatically! "
595  "Either remove your coordinate system type information from the input file, or contact "
596  "your application developer");
597  }
598 
599  // Set general axisymmetric axes if provided
600  if (isParamValid("rz_coord_blocks") && isParamValid("rz_coord_origins") &&
601  isParamValid("rz_coord_directions"))
602  {
603  const auto rz_coord_blocks = getParam<std::vector<SubdomainName>>("rz_coord_blocks");
604  const auto rz_coord_origins = getParam<std::vector<Point>>("rz_coord_origins");
605  const auto rz_coord_directions = getParam<std::vector<RealVectorValue>>("rz_coord_directions");
606  if (rz_coord_origins.size() == rz_coord_blocks.size() &&
607  rz_coord_directions.size() == rz_coord_blocks.size())
608  {
609  std::vector<std::pair<Point, RealVectorValue>> rz_coord_axes;
610  for (unsigned int i = 0; i < rz_coord_origins.size(); ++i)
611  rz_coord_axes.push_back(std::make_pair(rz_coord_origins[i], rz_coord_directions[i]));
612 
613  setGeneralAxisymmetricCoordAxes(rz_coord_blocks, rz_coord_axes);
614 
615  if (isParamSetByUser("rz_coord_axis"))
616  mooseError("The parameter 'rz_coord_axis' may not be provided if 'rz_coord_blocks', "
617  "'rz_coord_origins', and 'rz_coord_directions' are provided.");
618  }
619  else
620  mooseError("The parameters 'rz_coord_blocks', 'rz_coord_origins', and "
621  "'rz_coord_directions' must all have the same size.");
622  }
623  else if (isParamValid("rz_coord_blocks") || isParamValid("rz_coord_origins") ||
624  isParamValid("rz_coord_directions"))
625  mooseError("If any of the parameters 'rz_coord_blocks', 'rz_coord_origins', and "
626  "'rz_coord_directions' are provided, then all must be provided.");
627 
629 
630  update();
631 
632  // Check if there is subdomain name duplication for the same subdomain ID
634 
635  _moose_mesh_prepared = true;
636 
637  return called_prepare_for_use;
638 }
639 
640 void
642 {
643  TIME_SECTION("update", 3, "Updating Mesh", true);
644 
645  // Rebuild the boundary conditions
647 
648  // Update the node to elem map
649  _node_to_elem_map.clear();
650  _node_to_elem_map_built = false;
653 
654  buildNodeList();
656  cacheInfo();
657  buildElemIDInfo();
658 
659  // this will make moose mesh aware of p-refinement added by mesh generators including
660  // a file mesh generator loading a restart checkpoint file
661  _max_p_level = 0;
662  _max_h_level = 0;
663  for (const auto & elem : getMesh().active_local_element_ptr_range())
664  {
665  if (elem->p_level() > _max_p_level)
667  if (elem->level() > _max_h_level)
668  _max_h_level = elem->level();
669  }
670  comm().max(_max_p_level);
671  comm().max(_max_h_level);
672 
673  // the flag might have been set by calling doingPRefinement(true)
675 
676 #ifdef MOOSE_KOKKOS_ENABLED
678  _kokkos_mesh->update();
679 #endif
680 
682 }
683 
684 void
686 {
687  auto & mesh = getMesh();
688 
689  if (!mesh.is_serial())
690  mooseError(
691  "Hybrid finite element method must use replicated mesh.\nCurrently lower-dimensional mesh "
692  "does not support mesh re-partitioning and a debug assertion being hit related with "
693  "neighbors of lower-dimensional element, with distributed mesh.");
694 
695  // Lower-D element build requires neighboring element information
696  if (!mesh.is_prepared())
698 
699  // maximum number of sides of all elements
700  unsigned int max_n_sides = 0;
701 
702  // remove existing lower-d element first
703  std::set<Elem *> deleteable_elems;
704  for (auto & elem : mesh.element_ptr_range())
707  deleteable_elems.insert(elem);
708  else if (elem->n_sides() > max_n_sides)
709  max_n_sides = elem->n_sides();
710 
711  for (auto & elem : deleteable_elems)
713  for (const auto & id : _lower_d_interior_blocks)
714  _mesh_subdomains.erase(id);
715  for (const auto & id : _lower_d_boundary_blocks)
716  _mesh_subdomains.erase(id);
717  _lower_d_interior_blocks.clear();
718  _lower_d_boundary_blocks.clear();
719 
720  mesh.comm().max(max_n_sides);
721 
722  deleteable_elems.clear();
723 
724  // get all side types
725  std::set<int> interior_side_types;
726  std::set<int> boundary_side_types;
727  for (const auto & elem : mesh.active_element_ptr_range())
728  for (const auto side : elem->side_index_range())
729  {
730  Elem * neig = elem->neighbor_ptr(side);
731  std::unique_ptr<Elem> side_elem(elem->build_side_ptr(side));
732  if (neig)
733  interior_side_types.insert(side_elem->type());
734  else
735  boundary_side_types.insert(side_elem->type());
736  }
737  mesh.comm().set_union(interior_side_types);
738  mesh.comm().set_union(boundary_side_types);
739 
740  // assign block ids for different side types
741  std::map<ElemType, SubdomainID> interior_block_ids;
742  std::map<ElemType, SubdomainID> boundary_block_ids;
743  // we assume this id is not used by the mesh
745  for (const auto & tpid : interior_side_types)
746  {
747  const auto type = ElemType(tpid);
748  mesh.subdomain_name(id) = "INTERNAL_SIDE_LOWERD_SUBDOMAIN_" + Utility::enum_to_string(type);
749  interior_block_ids[type] = id;
750  _lower_d_interior_blocks.insert(id);
751  if (_mesh_subdomains.count(id) > 0)
752  mooseError("Trying to add a mesh block with id ", id, " that has existed in the mesh");
753  _mesh_subdomains.insert(id);
754  --id;
755  }
756  for (const auto & tpid : boundary_side_types)
757  {
758  const auto type = ElemType(tpid);
759  mesh.subdomain_name(id) = "BOUNDARY_SIDE_LOWERD_SUBDOMAIN_" + Utility::enum_to_string(type);
760  boundary_block_ids[type] = id;
761  _lower_d_boundary_blocks.insert(id);
762  if (_mesh_subdomains.count(id) > 0)
763  mooseError("Trying to add a mesh block with id ", id, " that has existed in the mesh");
764  _mesh_subdomains.insert(id);
765  --id;
766  }
767 
768  dof_id_type max_elem_id = mesh.max_elem_id();
769  unique_id_type max_unique_id = mesh.parallel_max_unique_id();
770 
771  std::vector<Elem *> side_elems;
773  for (const auto & elem : mesh.active_element_ptr_range())
774  {
775  // skip existing lower-d elements
776  if (elem->interior_parent())
777  continue;
778 
779  for (const auto side : elem->side_index_range())
780  {
781  Elem * neig = elem->neighbor_ptr(side);
782 
783  bool build_side = false;
784  if (!neig)
785  build_side = true;
786  else
787  {
788  mooseAssert(!neig->is_remote(), "We error if the mesh is not serial");
789  if (!neig->active())
790  build_side = true;
791  else if (neig->level() == elem->level() && elem->id() < neig->id())
792  build_side = true;
793  }
794 
795  if (build_side)
796  {
797  std::unique_ptr<Elem> side_elem(elem->build_side_ptr(side));
798 
799  // The side will be added with the same processor id as the parent.
800  side_elem->processor_id() = elem->processor_id();
801 
802  // Add subdomain ID
803  if (neig)
804  side_elem->subdomain_id() = interior_block_ids.at(side_elem->type());
805  else
806  side_elem->subdomain_id() = boundary_block_ids.at(side_elem->type());
807 
808  // set ids consistently across processors (these ids will be temporary)
809  side_elem->set_id(max_elem_id + elem->id() * max_n_sides + side);
810  side_elem->set_unique_id(max_unique_id + elem->id() * max_n_sides + side);
811 
812  // Also assign the side's interior parent, so it is always
813  // easy to figure out the Elem we came from.
814  // Note: the interior parent could be a ghost element.
815  side_elem->set_interior_parent(elem);
816 
817  side_elems.push_back(side_elem.release());
818 
819  // add link between higher d element to lower d element
820  auto pair = std::make_pair(elem, side);
821  auto link = std::make_pair(pair, side_elems.back());
822  auto ilink = std::make_pair(side_elems.back(), side);
825  }
826  }
827  }
828 
829  // finally, add the lower-dimensional element to the mesh
830  // Note: lower-d interior element will exist on a processor if its associated interior
831  // parent exists on a processor whether or not being a ghost. Lower-d elements will
832  // get its interior parent's processor id.
833  for (auto & elem : side_elems)
834  mesh.add_elem(elem);
835 
836  // we do all the stuff in prepare_for_use such as renumber_nodes_and_elements(),
837  // update_parallel_id_counts(), cache_elem_dims(), etc. except partitioning here.
838  const bool skip_partitioning_old = mesh.skip_partitioning();
839  mesh.skip_partitioning(true);
840  // Finding neighbors is ambiguous for lower-dimensional elements on interior faces
841  mesh.allow_find_neighbors(false);
843  mesh.skip_partitioning(skip_partitioning_old);
844 }
845 
846 const Node &
848 {
849  mooseDeprecated("MooseMesh::node() is deprecated, please use MooseMesh::nodeRef() instead");
850  return nodeRef(i);
851 }
852 
853 Node &
855 {
856  mooseDeprecated("MooseMesh::node() is deprecated, please use MooseMesh::nodeRef() instead");
857  return nodeRef(i);
858 }
859 
860 const Node &
862 {
863  const auto node_ptr = queryNodePtr(i);
864  mooseAssert(node_ptr, "Missing node");
865  return *node_ptr;
866 }
867 
868 Node &
870 {
871  return const_cast<Node &>(const_cast<const MooseMesh *>(this)->nodeRef(i));
872 }
873 
874 const Node *
876 {
877  return &nodeRef(i);
878 }
879 
880 Node *
882 {
883  return &nodeRef(i);
884 }
885 
886 const Node *
888 {
889  if (i > getMesh().max_node_id())
890  {
891  auto it = _quadrature_nodes.find(i);
892  if (it == _quadrature_nodes.end())
893  return nullptr;
894  auto & node_ptr = it->second;
895  mooseAssert(node_ptr, "Uninitialized quadrature node");
896  return node_ptr;
897  }
898 
899  return getMesh().query_node_ptr(i);
900 }
901 
902 Node *
904 {
905  return const_cast<Node *>(const_cast<const MooseMesh *>(this)->queryNodePtr(i));
906 }
907 
908 void
910 {
911  TIME_SECTION("meshChanged", 3, "Updating Because Mesh Changed");
912 
913  update();
914 
915  // Delete all of the cached ranges
916  _active_local_elem_range.reset();
917  _active_node_range.reset();
919  _local_node_range.reset();
920  _bnd_node_range.reset();
921  _bnd_elem_range.reset();
922 
923  // Rebuild the ranges
929 
930  // Call the callback function onMeshChanged
931  onMeshChanged();
932 }
933 
934 void
936 {
937 }
938 
939 void
941 {
942  TIME_SECTION("cacheChangedLists", 5, "Caching Changed Lists");
943 
944  ConstElemRange elem_range(getMesh().local_elements_begin(), getMesh().local_elements_end(), 1);
945  CacheChangedListsThread cclt(*this);
946  Threads::parallel_reduce(elem_range, cclt);
947 
949 
950  _refined_elements = std::make_unique<ConstElemPointerRange>(cclt._refined_elements.begin(),
951  cclt._refined_elements.end());
952  _coarsened_elements = std::make_unique<ConstElemPointerRange>(cclt._coarsened_elements.begin(),
953  cclt._coarsened_elements.end());
955 }
956 
959 {
960  return _refined_elements.get();
961 }
962 
965 {
966  return _coarsened_elements.get();
967 }
968 
969 const std::vector<const Elem *> &
971 {
972  auto elem_to_child_pair = _coarsened_element_children.find(elem);
973  mooseAssert(elem_to_child_pair != _coarsened_element_children.end(), "Missing element in map");
974  return elem_to_child_pair->second;
975 }
976 
977 void
978 MooseMesh::updateActiveSemiLocalNodeRange(std::set<dof_id_type> & ghosted_elems)
979 {
980  TIME_SECTION("updateActiveSemiLocalNodeRange", 5, "Updating ActiveSemiLocalNode Range");
981 
982  _semilocal_node_list.clear();
983 
984  // First add the nodes connected to local elems
985  ConstElemRange * active_local_elems = getActiveLocalElementRange();
986  for (const auto & elem : *active_local_elems)
987  {
988  for (unsigned int n = 0; n < elem->n_nodes(); ++n)
989  {
990  // Since elem is const here but we require a non-const Node * to
991  // store in the _semilocal_node_list (otherwise things like
992  // UpdateDisplacedMeshThread don't work), we are using a
993  // const_cast. A more long-term fix would be to have
994  // getActiveLocalElementRange return a non-const ElemRange.
995  Node * node = const_cast<Node *>(elem->node_ptr(n));
996 
997  _semilocal_node_list.insert(node);
998  }
999  }
1000 
1001  // Now add the nodes connected to ghosted_elems
1002  for (const auto & ghost_elem_id : ghosted_elems)
1003  {
1004  Elem * elem = getMesh().elem_ptr(ghost_elem_id);
1005  for (unsigned int n = 0; n < elem->n_nodes(); n++)
1006  {
1007  Node * node = elem->node_ptr(n);
1008 
1009  _semilocal_node_list.insert(node);
1010  }
1011  }
1012 
1013  // Now create the actual range
1014  _active_semilocal_node_range = std::make_unique<SemiLocalNodeRange>(_semilocal_node_list.begin(),
1015  _semilocal_node_list.end());
1016 }
1017 
1018 bool
1019 MooseMesh::isSemiLocal(Node * const node) const
1020 {
1021  return _semilocal_node_list.find(node) != _semilocal_node_list.end();
1022 }
1023 
1029 {
1030 public:
1032 
1033  bool operator()(const BndNode * const & lhs, const BndNode * const & rhs)
1034  {
1035  if (lhs->_bnd_id < rhs->_bnd_id)
1036  return true;
1037 
1038  if (lhs->_bnd_id > rhs->_bnd_id)
1039  return false;
1040 
1041  if (lhs->_node->id() < rhs->_node->id())
1042  return true;
1043 
1044  if (lhs->_node->id() > rhs->_node->id())
1045  return false;
1046 
1047  return false;
1048  }
1049 };
1050 
1051 void
1053 {
1054  TIME_SECTION("buildNodeList", 5, "Building Node List");
1055 
1056  freeBndNodes();
1057 
1058  auto bc_tuples = getMesh().get_boundary_info().build_node_list();
1059 
1060  int n = bc_tuples.size();
1061  _bnd_nodes.clear();
1062  _bnd_nodes.reserve(n);
1063  for (const auto & t : bc_tuples)
1064  {
1065  auto node_id = std::get<0>(t);
1066  auto bc_id = std::get<1>(t);
1067 
1068  _bnd_nodes.push_back(new BndNode(getMesh().node_ptr(node_id), bc_id));
1069  _node_set_nodes[bc_id].push_back(node_id);
1070  _bnd_node_ids[bc_id].insert(node_id);
1071  }
1072 
1073  _bnd_nodes.reserve(_bnd_nodes.size() + _extra_bnd_nodes.size());
1074  for (unsigned int i = 0; i < _extra_bnd_nodes.size(); i++)
1075  {
1076  BndNode * bnode = new BndNode(_extra_bnd_nodes[i]._node, _extra_bnd_nodes[i]._bnd_id);
1077  _bnd_nodes.push_back(bnode);
1078  _bnd_node_ids[std::get<1>(bc_tuples[i])].insert(_extra_bnd_nodes[i]._node->id());
1079  }
1080 
1081  // This sort is here so that boundary conditions are always applied in the same order
1082  std::sort(_bnd_nodes.begin(), _bnd_nodes.end(), BndNodeCompare());
1083 }
1084 
1085 void
1087 {
1088  auto & mesh = getMesh();
1089 
1090  _max_sides_per_elem = 0;
1091  _max_nodes_per_elem = 0;
1092  _max_nodes_per_side = 0;
1093 
1094  for (auto & elem : as_range(mesh.local_elements_begin(), mesh.local_elements_end()))
1095  {
1098 
1099  for (unsigned int side = 0; side < elem->n_sides(); ++side)
1101  }
1102 
1106 }
1107 
1108 void
1110 {
1111  unsigned int n = getMesh().n_elem_integers() + 1;
1112 
1113  _block_id_mapping.clear();
1114  _max_ids.clear();
1115  _min_ids.clear();
1116  _id_identical_flag.clear();
1117 
1118  _block_id_mapping.resize(n);
1121  _id_identical_flag.resize(n, std::vector<bool>(n, true));
1122  for (const auto & elem : getMesh().active_local_element_ptr_range())
1123  for (unsigned int i = 0; i < n; ++i)
1124  {
1125  auto id = (i == n - 1 ? elem->subdomain_id() : elem->get_extra_integer(i));
1126  _block_id_mapping[i][elem->subdomain_id()].insert(id);
1127  if (id > _max_ids[i])
1128  _max_ids[i] = id;
1129  if (id < _min_ids[i])
1130  _min_ids[i] = id;
1131  for (unsigned int j = 0; j < n; ++j)
1132  {
1133  auto idj = (j == n - 1 ? elem->subdomain_id() : elem->get_extra_integer(j));
1134  if (i != j && _id_identical_flag[i][j] && id != idj)
1135  _id_identical_flag[i][j] = false;
1136  }
1137  }
1138 
1139  for (unsigned int i = 0; i < n; ++i)
1140  {
1141  for (auto & blk : meshSubdomains())
1142  comm().set_union(_block_id_mapping[i][blk]);
1143  comm().min(_id_identical_flag[i]);
1144  }
1145  comm().max(_max_ids);
1146  comm().min(_min_ids);
1147 }
1148 
1149 std::unordered_map<dof_id_type, std::set<dof_id_type>>
1150 MooseMesh::getElemIDMapping(const std::string & from_id_name, const std::string & to_id_name) const
1151 {
1152  auto & mesh_base = getMesh();
1153 
1154  if (!mesh_base.has_elem_integer(from_id_name))
1155  mooseError("Mesh does not have the element integer name '", from_id_name, "'");
1156  if (!mesh_base.has_elem_integer(to_id_name))
1157  mooseError("Mesh does not have the element integer name '", to_id_name, "'");
1158 
1159  const auto id1 = mesh_base.get_elem_integer_index(from_id_name);
1160  const auto id2 = mesh_base.get_elem_integer_index(to_id_name);
1161 
1162  std::unordered_map<dof_id_type, std::set<dof_id_type>> id_map;
1163  for (const auto id : getAllElemIDs(id1))
1164  id_map[id] = std::set<dof_id_type>();
1165 
1166  for (const auto & elem : mesh_base.active_local_element_ptr_range())
1167  id_map[elem->get_extra_integer(id1)].insert(elem->get_extra_integer(id2));
1168 
1169  for (auto & [id, ids] : id_map)
1170  {
1171  libmesh_ignore(id); // avoid overzealous gcc 9.4 unused var warning
1172  comm().set_union(ids);
1173  }
1174 
1175  return id_map;
1176 }
1177 
1178 std::set<dof_id_type>
1179 MooseMesh::getAllElemIDs(unsigned int elem_id_index) const
1180 {
1181  std::set<dof_id_type> unique_ids;
1182  for (auto & pair : _block_id_mapping[elem_id_index])
1183  for (auto & id : pair.second)
1184  unique_ids.insert(id);
1185  return unique_ids;
1186 }
1187 
1188 std::set<dof_id_type>
1189 MooseMesh::getElemIDsOnBlocks(unsigned int elem_id_index, const std::set<SubdomainID> & blks) const
1190 {
1191  std::set<dof_id_type> unique_ids;
1192  for (auto & blk : blks)
1193  {
1194  auto it = _block_id_mapping[elem_id_index].find(blk);
1195  if (it == _block_id_mapping[elem_id_index].end())
1196  mooseError("Block ", blk, " is not available on the mesh");
1197 
1198  for (auto & mid : it->second)
1199  unique_ids.insert(mid);
1200  }
1201  return unique_ids;
1202 }
1203 
1204 void
1206 {
1207  TIME_SECTION("buildBndElemList", 5, "Building Boundary Elements List");
1208 
1209  freeBndElems();
1210 
1211  auto bc_tuples = getMesh().get_boundary_info().build_active_side_list();
1212 
1213  int n = bc_tuples.size();
1214  _bnd_elems.clear();
1215  _bnd_elems.reserve(n);
1216  for (const auto & t : bc_tuples)
1217  {
1218  auto elem_id = std::get<0>(t);
1219  auto side_id = std::get<1>(t);
1220  auto bc_id = std::get<2>(t);
1221 
1222  _bnd_elems.push_back(new BndElement(getMesh().elem_ptr(elem_id), side_id, bc_id));
1223  _bnd_elem_ids[bc_id].insert(elem_id);
1224  }
1225 }
1226 
1227 const std::map<dof_id_type, std::vector<dof_id_type>> &
1229 {
1230  if (!_node_to_elem_map_built) // Guard the creation with a double checked lock
1231  {
1232  Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
1233 
1235  {
1236  // This is allowing the timing to be run even with threads
1237  // This is safe because all threads will be waiting on this section when it runs
1238  // NOTE: Do not copy this construction to other places without thinking REALLY hard about it
1239  // The PerfGraph is NOT threadsafe and will cause all kinds of havok if care isn't taken
1240  auto in_threads = Threads::in_threads;
1241  Threads::in_threads = false;
1242  TIME_SECTION("nodeToElemMap", 5, "Building Node To Elem Map");
1243  Threads::in_threads = in_threads;
1244 
1245  for (const auto & elem : getMesh().active_element_ptr_range())
1246  for (unsigned int n = 0; n < elem->n_nodes(); n++)
1247  _node_to_elem_map[elem->node_id(n)].push_back(elem->id());
1248 
1249  _node_to_elem_map_built = true; // MUST be set at the end for double-checked locking to work!
1250  }
1251  }
1252  return _node_to_elem_map;
1253 }
1254 
1255 const std::map<dof_id_type, std::vector<dof_id_type>> &
1257 {
1258  if (!_node_to_active_semilocal_elem_map_built) // Guard the creation with a double checked lock
1259  {
1260  Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
1261 
1262  // This is allowing the timing to be run even with threads
1263  // This is safe because all threads will be waiting on this section when it runs
1264  // NOTE: Do not copy this construction to other places without thinking REALLY hard about it
1265  // The PerfGraph is NOT threadsafe and will cause all kinds of havok if care isn't taken
1266  auto in_threads = Threads::in_threads;
1267  Threads::in_threads = false;
1268  TIME_SECTION("nodeToActiveSemilocalElemMap", 5, "Building SemiLocalElemMap");
1269  Threads::in_threads = in_threads;
1270 
1272  {
1273  for (const auto & elem :
1274  as_range(getMesh().semilocal_elements_begin(), getMesh().semilocal_elements_end()))
1275  if (elem->active())
1276  for (unsigned int n = 0; n < elem->n_nodes(); n++)
1278 
1280  true; // MUST be set at the end for double-checked locking to work!
1281  }
1282  }
1283 
1285 }
1286 
1289 {
1291  {
1292  TIME_SECTION("getActiveLocalElementRange", 5);
1293 
1294  _active_local_elem_range = std::make_unique<ConstElemRange>(
1295  getMesh().active_local_elements_begin(), getMesh().active_local_elements_end(), GRAIN_SIZE);
1296  }
1297 
1298  return _active_local_elem_range.get();
1299 }
1300 
1301 NodeRange *
1303 {
1304  if (!_active_node_range)
1305  {
1306  TIME_SECTION("getActiveNodeRange", 5);
1307 
1308  _active_node_range = std::make_unique<NodeRange>(
1309  getMesh().active_nodes_begin(), getMesh().active_nodes_end(), GRAIN_SIZE);
1310  }
1311 
1312  return _active_node_range.get();
1313 }
1314 
1317 {
1318  mooseAssert(_active_semilocal_node_range,
1319  "_active_semilocal_node_range has not been created yet!");
1320 
1321  return _active_semilocal_node_range.get();
1322 }
1323 
1326 {
1327  if (!_local_node_range)
1328  {
1329  TIME_SECTION("getLocalNodeRange", 5);
1330 
1331  _local_node_range = std::make_unique<ConstNodeRange>(
1332  getMesh().local_nodes_begin(), getMesh().local_nodes_end(), GRAIN_SIZE);
1333  }
1334 
1335  return _local_node_range.get();
1336 }
1337 
1340 {
1341  if (!_bnd_node_range)
1342  {
1343  TIME_SECTION("getBoundaryNodeRange", 5);
1344 
1345  _bnd_node_range =
1346  std::make_unique<ConstBndNodeRange>(bndNodesBegin(), bndNodesEnd(), GRAIN_SIZE);
1347  }
1348 
1349  return _bnd_node_range.get();
1350 }
1351 
1354 {
1355  if (!_bnd_elem_range)
1356  {
1357  TIME_SECTION("getBoundaryElementRange", 5);
1358 
1359  _bnd_elem_range =
1360  std::make_unique<ConstBndElemRange>(bndElemsBegin(), bndElemsEnd(), GRAIN_SIZE);
1361  }
1362 
1363  return _bnd_elem_range.get();
1364 }
1365 
1366 const std::unordered_map<boundary_id_type, std::unordered_set<dof_id_type>> &
1368 {
1369  mooseDeprecated("MooseMesh::getBoundariesToElems is deprecated, "
1370  "use MooseMesh::getBoundariesToActiveSemiLocalElemIds");
1372 }
1373 
1374 const std::unordered_map<boundary_id_type, std::unordered_set<dof_id_type>> &
1376 {
1377  return _bnd_elem_ids;
1378 }
1379 
1380 std::unordered_set<dof_id_type>
1382 {
1383  // The boundary to element map is computed on every mesh update
1384  const auto it = _bnd_elem_ids.find(bid);
1385  if (it == _bnd_elem_ids.end())
1386  // Boundary is not local to this domain, return an empty set
1387  return std::unordered_set<dof_id_type>{};
1388  return it->second;
1389 }
1390 
1391 std::unordered_set<dof_id_type>
1393 {
1394  // Vector of boundary elems is updated every mesh update
1395  std::unordered_set<dof_id_type> neighbor_elems;
1396  for (const auto & bnd_elem : _bnd_elems)
1397  {
1398  const auto & [elem_ptr, elem_side, elem_bid] = *bnd_elem;
1399  if (elem_bid == bid)
1400  {
1401  const auto * neighbor = elem_ptr->neighbor_ptr(elem_side);
1402  // Dont add fully remote elements, ghosted is fine
1403  if (neighbor && neighbor != libMesh::remote_elem)
1404  {
1405  // handle mesh refinement, only return active elements near the boundary
1406  if (neighbor->active())
1407  neighbor_elems.insert(neighbor->id());
1408  else
1409  {
1410  std::vector<const Elem *> family;
1411  neighbor->active_family_tree_by_neighbor(family, elem_ptr);
1412  for (const auto & child_neighbor : family)
1413  neighbor_elems.insert(child_neighbor->id());
1414  }
1415  }
1416  }
1417  }
1418 
1419  return neighbor_elems;
1420 }
1421 
1422 bool
1424  const std::set<SubdomainID> & blk_group) const
1425 {
1426  mooseAssert(_bnd_elem_range, "Boundary element range is not initialized");
1427 
1428  // Loop over all side elements of the mesh, select those on the boundary
1429  for (const auto & bnd_elem : *_bnd_elem_range)
1430  {
1431  const auto & [elem_ptr, elem_side, elem_bid] = *bnd_elem;
1432  if (elem_bid == bid)
1433  {
1434  // If an element is internal to the group of subdomain, check the neighbor
1435  if (blk_group.find(elem_ptr->subdomain_id()) != blk_group.end())
1436  {
1437  const auto * const neighbor = elem_ptr->neighbor_ptr(elem_side);
1438 
1439  // If we did not ghost the neighbor, we cannot decide
1440  if (neighbor == libMesh::remote_elem)
1441  mooseError("Insufficient level of geometrical ghosting to determine "
1442  "if a boundary is internal to the mesh");
1443  // If the neighbor does not exist, then we are on the edge of the mesh
1444  if (!neighbor)
1445  continue;
1446  // If the neighbor is also in the group of subdomain,
1447  // then the boundary cuts the subdomains
1448  if (blk_group.find(neighbor->subdomain_id()) != blk_group.end())
1449  return false;
1450  }
1451  }
1452  }
1453  return true;
1454 }
1455 
1456 void
1458 {
1459  TIME_SECTION("cacheInfo", 3);
1460 
1461  _has_lower_d = false;
1462  _sub_to_data.clear();
1464  _block_node_list.clear();
1467  _lower_d_interior_blocks.clear();
1468  _lower_d_boundary_blocks.clear();
1469 
1470  auto & mesh = getMesh();
1471 
1472  // TODO: Thread this!
1473  for (const auto & elem : mesh.element_ptr_range())
1474  {
1475  const Elem * ip_elem = elem->interior_parent();
1476 
1477  if (ip_elem)
1478  {
1479  if (elem->active())
1480  _sub_to_data[elem->subdomain_id()].is_lower_d = true;
1481  unsigned int ip_side = ip_elem->which_side_am_i(elem);
1482 
1483  // For some grid sequencing tests: ip_side == libMesh::invalid_uint
1484  if (ip_side != libMesh::invalid_uint)
1485  {
1486  auto pair = std::make_pair(ip_elem, ip_side);
1488  std::pair<std::pair<const Elem *, unsigned short int>, const Elem *>(pair, elem));
1490  std::pair<const Elem *, unsigned short int>(elem, ip_side));
1491 
1492  auto id = elem->subdomain_id();
1493  if (ip_elem->neighbor_ptr(ip_side))
1494  {
1495  if (mesh.subdomain_name(id).find("INTERNAL_SIDE_LOWERD_SUBDOMAIN_") != std::string::npos)
1496  _lower_d_interior_blocks.insert(id);
1497  }
1498  else
1499  {
1500  if (mesh.subdomain_name(id).find("BOUNDARY_SIDE_LOWERD_SUBDOMAIN_") != std::string::npos)
1501  _lower_d_boundary_blocks.insert(id);
1502  }
1503  }
1504  }
1505 
1506  for (unsigned int nd = 0; nd < elem->n_nodes(); ++nd)
1507  {
1508  Node & node = *elem->node_ptr(nd);
1509  _block_node_list[node.id()].insert(elem->subdomain_id());
1510  }
1511  }
1514 
1515  for (const auto & elem : mesh.active_local_element_ptr_range())
1516  {
1517  SubdomainID subdomain_id = elem->subdomain_id();
1518  auto & sub_data = _sub_to_data[subdomain_id];
1519  for (unsigned int side = 0; side < elem->n_sides(); side++)
1520  {
1521  std::vector<BoundaryID> boundary_ids = getBoundaryIDs(elem, side);
1522  sub_data.boundary_ids.insert(boundary_ids.begin(), boundary_ids.end());
1523 
1524  Elem * neig = elem->neighbor_ptr(side);
1525  if (neig)
1526  {
1527  _neighbor_subdomain_boundary_ids[neig->subdomain_id()].insert(boundary_ids.begin(),
1528  boundary_ids.end());
1529  SubdomainID neighbor_subdomain_id = neig->subdomain_id();
1530  if (neighbor_subdomain_id != subdomain_id)
1531  sub_data.neighbor_subs.insert(neighbor_subdomain_id);
1532  }
1533  }
1534  }
1535 
1536  for (const auto blk_id : _mesh_subdomains)
1537  {
1538  auto & sub_data = _sub_to_data[blk_id];
1539  _communicator.set_union(sub_data.neighbor_subs);
1540  _communicator.set_union(sub_data.boundary_ids);
1541  _communicator.max(sub_data.is_lower_d);
1542  if (sub_data.is_lower_d)
1543  _has_lower_d = true;
1545  }
1546 }
1547 
1548 const std::set<SubdomainID> &
1550 {
1551  auto it = _block_node_list.find(node.id());
1552 
1553  if (it == _block_node_list.end())
1554  mooseError("Unable to find node: ", node.id(), " in any block list.");
1555 
1556  return it->second;
1557 }
1558 
1561 {
1562  return face_info_iterator(
1563  _face_info.begin(),
1564  _face_info.end(),
1566 }
1567 
1570 {
1571  return face_info_iterator(
1572  _face_info.end(),
1573  _face_info.end(),
1575 }
1576 
1579 {
1580  return elem_info_iterator(_elem_info.begin(),
1581  _elem_info.end(),
1583 }
1584 
1587 {
1588  return elem_info_iterator(_elem_info.end(),
1589  _elem_info.end(),
1591 }
1592 
1593 // default begin() accessor
1596 {
1598  return bnd_node_iterator(_bnd_nodes.begin(), _bnd_nodes.end(), p);
1599 }
1600 
1601 // default end() accessor
1604 {
1606  return bnd_node_iterator(_bnd_nodes.end(), _bnd_nodes.end(), p);
1607 }
1608 
1609 // default begin() accessor
1612 {
1614  return bnd_elem_iterator(_bnd_elems.begin(), _bnd_elems.end(), p);
1615 }
1616 
1617 // default end() accessor
1620 {
1622  return bnd_elem_iterator(_bnd_elems.end(), _bnd_elems.end(), p);
1623 }
1624 
1625 const Node *
1626 MooseMesh::addUniqueNode(const Point & p, Real tol)
1627 {
1632  if (getMesh().n_nodes() != _node_map.size())
1633  {
1634  _node_map.clear();
1635  _node_map.reserve(getMesh().n_nodes());
1636  for (const auto & node : getMesh().node_ptr_range())
1637  _node_map.push_back(node);
1638  }
1639 
1640  Node * node = nullptr;
1641  for (unsigned int i = 0; i < _node_map.size(); ++i)
1642  {
1643  if (p.relative_fuzzy_equals(*_node_map[i], tol))
1644  {
1645  node = _node_map[i];
1646  break;
1647  }
1648  }
1649  if (node == nullptr)
1650  {
1651  node = getMesh().add_node(new Node(p));
1652  _node_map.push_back(node);
1653  }
1654 
1655  mooseAssert(node != nullptr, "Node is NULL");
1656  return node;
1657 }
1658 
1659 Node *
1661  const unsigned short int side,
1662  const unsigned int qp,
1663  BoundaryID bid,
1664  const Point & point)
1665 {
1666  Node * qnode;
1667 
1668  if (_elem_to_side_to_qp_to_quadrature_nodes[elem->id()][side].find(qp) ==
1670  {
1671  // Create a new node id starting from the max node id and counting down. This will be the least
1672  // likely to collide with an existing node id.
1673  // Note that we are using numeric_limits<unsigned>::max even
1674  // though max_id is stored as a dof_id_type. I tried this with
1675  // numeric_limits<dof_id_type>::max and it broke several tests in
1676  // MOOSE. So, this is some kind of a magic number that we will
1677  // just continue to use...
1679  dof_id_type new_id = max_id - _quadrature_nodes.size();
1680 
1681  if (new_id <= getMesh().max_node_id())
1682  mooseError("Quadrature node id collides with existing node id!");
1683 
1684  qnode = new Node(point, new_id);
1685 
1686  // Keep track of this new node in two different ways for easy lookup
1687  _quadrature_nodes[new_id] = qnode;
1688  _elem_to_side_to_qp_to_quadrature_nodes[elem->id()][side][qp] = qnode;
1689 
1690  if (elem->active())
1691  {
1692  _node_to_elem_map[new_id].push_back(elem->id());
1693  _node_to_active_semilocal_elem_map[new_id].push_back(elem->id());
1694  }
1695  }
1696  else
1697  qnode = _elem_to_side_to_qp_to_quadrature_nodes[elem->id()][side][qp];
1698 
1699  BndNode * bnode = new BndNode(qnode, bid);
1700  _bnd_nodes.push_back(bnode);
1701  _bnd_node_ids[bid].insert(qnode->id());
1702 
1703  _extra_bnd_nodes.push_back(*bnode);
1704 
1705  // Do this so the range will be regenerated next time it is accessed
1706  _bnd_node_range.reset();
1707 
1708  return qnode;
1709 }
1710 
1711 Node *
1713  const unsigned short int side,
1714  const unsigned int qp)
1715 {
1716  mooseAssert(_elem_to_side_to_qp_to_quadrature_nodes.find(elem->id()) !=
1718  "Elem has no quadrature nodes!");
1719  mooseAssert(_elem_to_side_to_qp_to_quadrature_nodes[elem->id()].find(side) !=
1721  "Side has no quadrature nodes!");
1722  mooseAssert(_elem_to_side_to_qp_to_quadrature_nodes[elem->id()][side].find(qp) !=
1724  "qp not found on side!");
1725 
1726  return _elem_to_side_to_qp_to_quadrature_nodes[elem->id()][side][qp];
1727 }
1728 
1729 void
1731 {
1732  // Delete all the quadrature nodes
1733  for (auto & it : _quadrature_nodes)
1734  delete it.second;
1735 
1736  _quadrature_nodes.clear();
1738  _extra_bnd_nodes.clear();
1739 }
1740 
1741 BoundaryID
1742 MooseMesh::getBoundaryID(const BoundaryName & boundary_name) const
1743 {
1744  if (boundary_name == "ANY_BOUNDARY_ID")
1745  mooseError("Please use getBoundaryIDs() when passing \"ANY_BOUNDARY_ID\"");
1746 
1747  return MooseMeshUtils::getBoundaryID(boundary_name, getMesh());
1748 }
1749 
1750 const Elem *
1751 MooseMesh::getLowerDElem(const Elem * elem, unsigned short int side) const
1752 {
1753  auto it = _higher_d_elem_side_to_lower_d_elem.find(std::make_pair(elem, side));
1754 
1755  if (it != _higher_d_elem_side_to_lower_d_elem.end())
1756  return it->second;
1757  else
1758  return nullptr;
1759 }
1760 
1761 unsigned int
1762 MooseMesh::getHigherDSide(const Elem * elem) const
1763 {
1764  auto it = _lower_d_elem_to_higher_d_elem_side.find(elem);
1765 
1766  if (it != _lower_d_elem_to_higher_d_elem_side.end())
1767  return it->second;
1768  else
1769  return libMesh::invalid_uint;
1770 }
1771 
1772 std::vector<BoundaryID>
1773 MooseMesh::getBoundaryIDs(const std::vector<BoundaryName> & boundary_name,
1774  bool generate_unknown) const
1775 {
1777  getMesh(), boundary_name, generate_unknown, _mesh_boundary_ids);
1778 }
1779 
1781 MooseMesh::getSubdomainID(const SubdomainName & subdomain_name) const
1782 {
1783  return MooseMeshUtils::getSubdomainID(subdomain_name, getMesh());
1784 }
1785 
1786 std::vector<SubdomainID>
1787 MooseMesh::getSubdomainIDs(const std::vector<SubdomainName> & subdomain_name) const
1788 {
1789  return MooseMeshUtils::getSubdomainIDs(getMesh(), subdomain_name);
1790 }
1791 
1792 std::set<SubdomainID>
1793 MooseMesh::getSubdomainIDs(const std::set<SubdomainName> & subdomain_name) const
1794 {
1795  return MooseMeshUtils::getSubdomainIDs(getMesh(), subdomain_name);
1796 }
1797 
1798 void
1799 MooseMesh::setSubdomainName(SubdomainID subdomain_id, const SubdomainName & name)
1800 {
1801  mooseAssert(name != "ANY_BLOCK_ID", "Cannot set subdomain name to 'ANY_BLOCK_ID'");
1802  getMesh().subdomain_name(subdomain_id) = name;
1803 }
1804 
1805 void
1806 MooseMesh::setSubdomainName(MeshBase & mesh, SubdomainID subdomain_id, const SubdomainName & name)
1807 {
1808  mooseAssert(name != "ANY_BLOCK_ID", "Cannot set subdomain name to 'ANY_BLOCK_ID'");
1809  mesh.subdomain_name(subdomain_id) = name;
1810 }
1811 
1812 const std::string &
1814 {
1815  return getMesh().subdomain_name(subdomain_id);
1816 }
1817 
1818 std::vector<SubdomainName>
1819 MooseMesh::getSubdomainNames(const std::vector<SubdomainID> & subdomain_ids) const
1820 {
1821  std::vector<SubdomainName> names(subdomain_ids.size());
1822 
1823  for (unsigned int i = 0; i < subdomain_ids.size(); i++)
1824  names[i] = getSubdomainName(subdomain_ids[i]);
1825 
1826  return names;
1827 }
1828 
1829 void
1830 MooseMesh::setBoundaryName(BoundaryID boundary_id, BoundaryName name)
1831 {
1832  BoundaryInfo & boundary_info = getMesh().get_boundary_info();
1833 
1834  // We need to figure out if this boundary is a sideset or nodeset
1835  if (boundary_info.get_side_boundary_ids().count(boundary_id))
1836  boundary_info.sideset_name(boundary_id) = name;
1837  else
1838  boundary_info.nodeset_name(boundary_id) = name;
1839 }
1840 
1841 const std::string &
1843 {
1844  const BoundaryInfo & boundary_info = getMesh().get_boundary_info();
1845 
1846  // We need to figure out if this boundary is a sideset or nodeset
1847  if (boundary_info.get_side_boundary_ids().count(boundary_id))
1848  return boundary_info.get_sideset_name(boundary_id);
1849  else
1850  return boundary_info.get_nodeset_name(boundary_id);
1851 }
1852 
1853 // specialization for PointListAdaptor<MooseMesh::PeriodicNodeInfo>
1854 template <>
1855 inline const Point &
1857  const MooseMesh::PeriodicNodeInfo & item) const
1858 {
1859  return *(item.first);
1860 }
1861 
1862 void
1863 MooseMesh::buildPeriodicNodeMap(std::multimap<dof_id_type, dof_id_type> & periodic_node_map,
1864  unsigned int var_number,
1865  libMesh::PeriodicBoundaries * pbs) const
1866 {
1867  TIME_SECTION("buildPeriodicNodeMap", 5);
1868 
1869  // clear existing map
1870  periodic_node_map.clear();
1871 
1872  // get periodic nodes
1873  std::vector<PeriodicNodeInfo> periodic_nodes;
1874  for (const auto & t : getMesh().get_boundary_info().build_node_list())
1875  {
1876  // unfortunately libMesh does not give us a pointer, so we have to look it up ourselves
1877  auto node = _mesh->node_ptr(std::get<0>(t));
1878  mooseAssert(node != nullptr,
1879  "libMesh::BoundaryInfo::build_node_list() returned an ID for a non-existing node");
1880  auto bc_id = std::get<1>(t);
1881  periodic_nodes.emplace_back(node, bc_id);
1882  }
1883 
1884  // sort by boundary id
1885  std::sort(periodic_nodes.begin(),
1886  periodic_nodes.end(),
1887  [](const PeriodicNodeInfo & a, const PeriodicNodeInfo & b) -> bool
1888  { return a.second > b.second; });
1889 
1890  // build kd-tree
1891  using KDTreeType = nanoflann::KDTreeSingleIndexAdaptor<
1892  nanoflann::L2_Simple_Adaptor<Real, PointListAdaptor<PeriodicNodeInfo>, Real, std::size_t>,
1894  LIBMESH_DIM,
1895  std::size_t>;
1896  const unsigned int max_leaf_size = 20; // slightly affects runtime
1897  auto point_list =
1898  PointListAdaptor<PeriodicNodeInfo>(periodic_nodes.begin(), periodic_nodes.end());
1899  auto kd_tree = std::make_unique<KDTreeType>(
1900  LIBMESH_DIM, point_list, nanoflann::KDTreeSingleIndexAdaptorParams(max_leaf_size));
1901  mooseAssert(kd_tree != nullptr, "KDTree was not properly initialized.");
1902  kd_tree->buildIndex();
1903 
1904  // data structures for kd-tree search
1905  nanoflann::SearchParameters search_params;
1906  std::vector<nanoflann::ResultItem<std::size_t, Real>> ret_matches;
1907 
1908  // iterate over periodic nodes (boundary ids are in contiguous blocks)
1909  libMesh::PeriodicBoundaryBase * periodic = nullptr;
1910  BoundaryID current_bc_id = BoundaryInfo::invalid_id;
1911  for (auto & pair : periodic_nodes)
1912  {
1913  // entering a new block of boundary IDs
1914  if (pair.second != current_bc_id)
1915  {
1916  current_bc_id = pair.second;
1917  periodic = pbs->boundary(current_bc_id);
1918  if (periodic && !periodic->is_my_variable(var_number))
1919  periodic = nullptr;
1920  }
1921 
1922  // variable is not periodic at this node, skip
1923  if (!periodic)
1924  continue;
1925 
1926  // clear result buffer
1927  ret_matches.clear();
1928 
1929  // id of the current node
1930  const auto id = pair.first->id();
1931 
1932  // position where we expect a periodic partner for the current node and boundary
1933  Point search_point = periodic->get_corresponding_pos(*pair.first);
1934 
1935  // search at the expected point
1936  kd_tree->radiusSearch(&(search_point)(0), libMesh::TOLERANCE, ret_matches, search_params);
1937  for (auto & match_pair : ret_matches)
1938  {
1939  const auto & match = periodic_nodes[match_pair.first];
1940  // add matched node if the boundary id is the corresponding id in the periodic pair
1941  if (match.second == periodic->pairedboundary)
1942  periodic_node_map.emplace(id, match.first->id());
1943  }
1944  }
1945 }
1946 
1947 void
1948 MooseMesh::buildPeriodicNodeSets(std::map<BoundaryID, std::set<dof_id_type>> & periodic_node_sets,
1949  unsigned int var_number,
1950  libMesh::PeriodicBoundaries * pbs) const
1951 {
1952  TIME_SECTION("buildPeriodicNodeSets", 5);
1953 
1954  periodic_node_sets.clear();
1955 
1956  // Loop over all the boundary nodes adding the periodic nodes to the appropriate set
1957  for (const auto & t : getMesh().get_boundary_info().build_node_list())
1958  {
1959  auto node_id = std::get<0>(t);
1960  auto bc_id = std::get<1>(t);
1961 
1962  // Is this current node on a known periodic boundary?
1963  if (periodic_node_sets.find(bc_id) != periodic_node_sets.end())
1964  periodic_node_sets[bc_id].insert(node_id);
1965  else // This still might be a periodic node but we just haven't seen this boundary_id yet
1966  {
1967  const libMesh::PeriodicBoundaryBase * periodic = pbs->boundary(bc_id);
1968  if (periodic && periodic->is_my_variable(var_number))
1969  periodic_node_sets[bc_id].insert(node_id);
1970  }
1971  }
1972 }
1973 
1974 bool
1976 {
1977  TIME_SECTION("detectOrthogonalDimRanges", 5);
1978 
1980  return true;
1981 
1982  std::vector<Real> min(3, std::numeric_limits<Real>::max());
1983  std::vector<Real> max(3, std::numeric_limits<Real>::min());
1984  unsigned int dim = getMesh().mesh_dimension();
1985 
1986  // Find the bounding box of our mesh
1987  for (const auto & node : getMesh().node_ptr_range())
1988  // Check all coordinates, we don't know if this mesh might be lying in a higher dim even if the
1989  // mesh dimension is lower.
1990  for (const auto i : make_range(Moose::dim))
1991  {
1992  if ((*node)(i) < min[i])
1993  min[i] = (*node)(i);
1994  if ((*node)(i) > max[i])
1995  max[i] = (*node)(i);
1996  }
1997 
1998  this->comm().max(max);
1999  this->comm().min(min);
2000 
2001  _extreme_nodes.resize(8); // 2^LIBMESH_DIM
2002  // Now make sure that there are actual nodes at all of the extremes
2003  std::vector<bool> extreme_matches(8, false);
2004  std::vector<unsigned int> comp_map(3);
2005  for (const auto & node : getMesh().node_ptr_range())
2006  {
2007  // See if the current node is located at one of the extremes
2008  unsigned int coord_match = 0;
2009 
2010  for (const auto i : make_range(Moose::dim))
2011  {
2012  if (std::abs((*node)(i)-min[i]) < tol)
2013  {
2014  comp_map[i] = MIN;
2015  ++coord_match;
2016  }
2017  else if (std::abs((*node)(i)-max[i]) < tol)
2018  {
2019  comp_map[i] = MAX;
2020  ++coord_match;
2021  }
2022  }
2023 
2024  if (coord_match == LIBMESH_DIM) // Found a coordinate at one of the extremes
2025  {
2026  _extreme_nodes[comp_map[X] * 4 + comp_map[Y] * 2 + comp_map[Z]] = node;
2027  extreme_matches[comp_map[X] * 4 + comp_map[Y] * 2 + comp_map[Z]] = true;
2028  }
2029  }
2030 
2031  // See if we matched all of the extremes for the mesh dimension
2032  this->comm().max(extreme_matches);
2033  if (std::count(extreme_matches.begin(), extreme_matches.end(), true) == (1 << dim))
2034  _regular_orthogonal_mesh = true;
2035 
2036  // Set the bounds
2037  _bounds.resize(LIBMESH_DIM);
2038  for (const auto i : make_range(Moose::dim))
2039  {
2040  _bounds[i].resize(2);
2041  _bounds[i][MIN] = min[i];
2042  _bounds[i][MAX] = max[i];
2043  }
2044 
2045  return _regular_orthogonal_mesh;
2046 }
2047 
2048 void
2050 {
2051  TIME_SECTION("detectPairedSidesets", 5);
2052 
2053  // Loop over level-0 elements (since boundary condition information
2054  // is only directly stored for them) and find sidesets with normals
2055  // that point in the -x, +x, -y, +y, and -z, +z direction. If there
2056  // is a unique sideset id for each direction, then the paired
2057  // sidesets consist of (-x,+x), (-y,+y), (-z,+z). If there are
2058  // multiple sideset ids for a given direction, then we can't pick a
2059  // single pair for that direction. In that case, we'll just return
2060  // as was done in the original algorithm.
2061 
2062  // Points used for direction comparison
2063  const Point minus_x(-1, 0, 0), plus_x(1, 0, 0), minus_y(0, -1, 0), plus_y(0, 1, 0),
2064  minus_z(0, 0, -1), plus_z(0, 0, 1);
2065 
2066  // we need to test all element dimensions from dim down to 1
2067  const unsigned int dim = getMesh().mesh_dimension();
2068 
2069  // boundary id sets for elements of different dimensions
2070  std::vector<std::set<BoundaryID>> minus_x_ids(dim), plus_x_ids(dim), minus_y_ids(dim),
2071  plus_y_ids(dim), minus_z_ids(dim), plus_z_ids(dim);
2072 
2073  std::vector<std::unique_ptr<FEBase>> fe_faces(dim);
2074  std::vector<std::unique_ptr<libMesh::QGauss>> qfaces(dim);
2075  for (unsigned side_dim = 0; side_dim < dim; ++side_dim)
2076  {
2077  // Face is assumed to be flat, therefore normal is assumed to be
2078  // constant over the face, therefore only compute it at 1 qp.
2079  qfaces[side_dim] = std::unique_ptr<libMesh::QGauss>(new libMesh::QGauss(side_dim, CONSTANT));
2080 
2081  // A first-order Lagrange FE for the face.
2082  fe_faces[side_dim] = FEBase::build(side_dim + 1, FEType(FIRST, libMesh::LAGRANGE));
2083  fe_faces[side_dim]->attach_quadrature_rule(qfaces[side_dim].get());
2084  }
2085 
2086  // We need this to get boundary ids for each boundary face we encounter.
2087  BoundaryInfo & boundary_info = getMesh().get_boundary_info();
2088  std::vector<boundary_id_type> face_ids;
2089 
2090  for (auto & elem : as_range(getMesh().level_elements_begin(0), getMesh().level_elements_end(0)))
2091  {
2092  // dimension of the current element and its normals
2093  unsigned int side_dim = elem->dim() - 1;
2094  const std::vector<Point> & normals = fe_faces[side_dim]->get_normals();
2095 
2096  // loop over element sides
2097  for (unsigned int s = 0; s < elem->n_sides(); s++)
2098  {
2099  // If side is on the boundary
2100  if (elem->neighbor_ptr(s) == nullptr)
2101  {
2102  std::unique_ptr<Elem> side = elem->build_side_ptr(s);
2103 
2104  fe_faces[side_dim]->reinit(elem, s);
2105 
2106  // Get the boundary ID(s) for this side. If there is more
2107  // than 1 boundary id, then we already can't determine a
2108  // unique pairing of sides in this direction, but we'll just
2109  // keep going to keep the logic simple.
2110  boundary_info.boundary_ids(elem, s, face_ids);
2111 
2112  // x-direction faces
2113  if (normals[0].absolute_fuzzy_equals(minus_x))
2114  minus_x_ids[side_dim].insert(face_ids.begin(), face_ids.end());
2115  else if (normals[0].absolute_fuzzy_equals(plus_x))
2116  plus_x_ids[side_dim].insert(face_ids.begin(), face_ids.end());
2117 
2118  // y-direction faces
2119  else if (normals[0].absolute_fuzzy_equals(minus_y))
2120  minus_y_ids[side_dim].insert(face_ids.begin(), face_ids.end());
2121  else if (normals[0].absolute_fuzzy_equals(plus_y))
2122  plus_y_ids[side_dim].insert(face_ids.begin(), face_ids.end());
2123 
2124  // z-direction faces
2125  else if (normals[0].absolute_fuzzy_equals(minus_z))
2126  minus_z_ids[side_dim].insert(face_ids.begin(), face_ids.end());
2127  else if (normals[0].absolute_fuzzy_equals(plus_z))
2128  plus_z_ids[side_dim].insert(face_ids.begin(), face_ids.end());
2129  }
2130  }
2131  }
2132 
2133  // For a distributed mesh, boundaries may be distributed as well. We therefore collect information
2134  // from everyone. If the mesh is already serial, then there is no need to do an allgather. Note
2135  // that this is just going to gather information about what the periodic bc ids are. We are not
2136  // gathering any remote elements or anything like that. It's just that the GhostPointNeighbors
2137  // ghosting functor currently relies on the fact that every process agrees on whether we have
2138  // periodic boundaries; every process that thinks there are periodic boundaries will call
2139  // MeshBase::sub_point_locator which makes a parallel_object_only() assertion (right or wrong). So
2140  // we all need to go there (or not go there)
2141  if (_use_distributed_mesh && !_mesh->is_serial())
2142  {
2143  // Pack all data together so that we send them via one communication
2144  // pair: boundary side --> boundary ids.
2145  std::vector<std::pair<boundary_id_type, boundary_id_type>> vecdata;
2146  // We check boundaries on all dimensions
2147  for (unsigned side_dim = 0; side_dim < dim; ++side_dim)
2148  {
2149  // "6" means: we have at most 6 boundaries. It is true for generated simple mesh
2150  // "detectPairedSidesets" is designed for only simple meshes
2151  for (auto bd = minus_x_ids[side_dim].begin(); bd != minus_x_ids[side_dim].end(); bd++)
2152  vecdata.emplace_back(side_dim * 6 + 0, *bd);
2153 
2154  for (auto bd = plus_x_ids[side_dim].begin(); bd != plus_x_ids[side_dim].end(); bd++)
2155  vecdata.emplace_back(side_dim * 6 + 1, *bd);
2156 
2157  for (auto bd = minus_y_ids[side_dim].begin(); bd != minus_y_ids[side_dim].end(); bd++)
2158  vecdata.emplace_back(side_dim * 6 + 2, *bd);
2159 
2160  for (auto bd = plus_y_ids[side_dim].begin(); bd != plus_y_ids[side_dim].end(); bd++)
2161  vecdata.emplace_back(side_dim * 6 + 3, *bd);
2162 
2163  for (auto bd = minus_z_ids[side_dim].begin(); bd != minus_z_ids[side_dim].end(); bd++)
2164  vecdata.emplace_back(side_dim * 6 + 4, *bd);
2165 
2166  for (auto bd = plus_z_ids[side_dim].begin(); bd != plus_z_ids[side_dim].end(); bd++)
2167  vecdata.emplace_back(side_dim * 6 + 5, *bd);
2168  }
2169 
2170  _communicator.allgather(vecdata, false);
2171 
2172  // Unpack data, and add them into minus/plus_x/y_ids
2173  for (auto pair = vecdata.begin(); pair != vecdata.end(); pair++)
2174  {
2175  // Convert data from the long vector, and add data to separated sets
2176  auto side_dim = pair->first / 6;
2177  auto side = pair->first % 6;
2178 
2179  switch (side)
2180  {
2181  case 0:
2182  minus_x_ids[side_dim].insert(pair->second);
2183  break;
2184  case 1:
2185  plus_x_ids[side_dim].insert(pair->second);
2186  break;
2187  case 2:
2188  minus_y_ids[side_dim].insert(pair->second);
2189  break;
2190  case 3:
2191  plus_y_ids[side_dim].insert(pair->second);
2192  break;
2193  case 4:
2194  minus_z_ids[side_dim].insert(pair->second);
2195  break;
2196  case 5:
2197  plus_z_ids[side_dim].insert(pair->second);
2198  break;
2199  default:
2200  mooseError("Unknown boundary side ", side);
2201  }
2202  }
2203 
2204  } // end if (_use_distributed_mesh && !_need_ghost_ghosted_boundaries)
2205 
2206  for (unsigned side_dim = 0; side_dim < dim; ++side_dim)
2207  {
2208  // If unique pairings were found, fill up the _paired_boundary data
2209  // structure with that information.
2210  if (minus_x_ids[side_dim].size() == 1 && plus_x_ids[side_dim].size() == 1)
2211  _paired_boundary.emplace_back(
2212  std::make_pair(*(minus_x_ids[side_dim].begin()), *(plus_x_ids[side_dim].begin())));
2213  else
2215  "For side dimension " + std::to_string(side_dim) +
2216  " we did not find paired boundaries (sidesets) in X due to the presence of " +
2217  std::to_string(minus_x_ids[side_dim].size()) + " -X normal and " +
2218  std::to_string(plus_x_ids[side_dim].size()) + " +X normal boundaries.");
2219 
2220  if (minus_y_ids[side_dim].size() == 1 && plus_y_ids[side_dim].size() == 1)
2221  _paired_boundary.emplace_back(
2222  std::make_pair(*(minus_y_ids[side_dim].begin()), *(plus_y_ids[side_dim].begin())));
2223  else
2225  "For side dimension " + std::to_string(side_dim) +
2226  " we did not find paired boundaries (sidesets) in Y due to the presence of " +
2227  std::to_string(minus_y_ids[side_dim].size()) + " -Y normal and " +
2228  std::to_string(plus_y_ids[side_dim].size()) + " +Y normal boundaries.");
2229 
2230  if (minus_z_ids[side_dim].size() == 1 && plus_z_ids[side_dim].size() == 1)
2231  _paired_boundary.emplace_back(
2232  std::make_pair(*(minus_z_ids[side_dim].begin()), *(plus_z_ids[side_dim].begin())));
2233  else
2235  "For side dimension " + std::to_string(side_dim) +
2236  " we did not find paired boundaries (sidesets) in Z due to the presence of " +
2237  std::to_string(minus_z_ids[side_dim].size()) + " -Z normal and " +
2238  std::to_string(plus_z_ids[side_dim].size()) + " +Z normal boundaries.");
2239  }
2240 }
2241 
2242 Real
2243 MooseMesh::dimensionWidth(unsigned int component) const
2244 {
2245  return getMaxInDimension(component) - getMinInDimension(component);
2246 }
2247 
2248 Real
2249 MooseMesh::getMinInDimension(unsigned int component) const
2250 {
2251  mooseAssert(_mesh, "The MeshBase has not been constructed");
2252  mooseAssert(component < _bounds.size(), "Requested dimension out of bounds");
2253 
2254  return _bounds[component][MIN];
2255 }
2256 
2257 Real
2258 MooseMesh::getMaxInDimension(unsigned int component) const
2259 {
2260  mooseAssert(_mesh, "The MeshBase has not been constructed");
2261  mooseAssert(component < _bounds.size(), "Requested dimension out of bounds");
2262 
2263  return _bounds[component][MAX];
2264 }
2265 
2266 void
2267 MooseMesh::addPeriodicVariable(unsigned int var_num, BoundaryID primary, BoundaryID secondary)
2268 {
2270  return;
2271 
2272  _periodic_dim[var_num].resize(dimension());
2273 
2274  _half_range = Point(dimensionWidth(0) / 2.0, dimensionWidth(1) / 2.0, dimensionWidth(2) / 2.0);
2275 
2276  bool component_found = false;
2277  for (unsigned int component = 0; component < dimension(); ++component)
2278  {
2279  const std::pair<BoundaryID, BoundaryID> * boundary_ids = getPairedBoundaryMapping(component);
2280 
2281  if (boundary_ids != nullptr &&
2282  ((boundary_ids->first == primary && boundary_ids->second == secondary) ||
2283  (boundary_ids->first == secondary && boundary_ids->second == primary)))
2284  {
2285  _periodic_dim[var_num][component] = true;
2286  component_found = true;
2287  }
2288  }
2289  if (!component_found)
2290  mooseWarning("Could not find a match between boundary '",
2291  getBoundaryName(primary),
2292  "' and '",
2293  getBoundaryName(secondary),
2294  "' to set periodic boundary conditions for variable (index:",
2295  var_num,
2296  ") in either the X, Y or Z direction. The periodic dimension of the mesh for this "
2297  "variable will not be stored.");
2298 }
2299 
2300 bool
2301 MooseMesh::isTranslatedPeriodic(unsigned int nonlinear_var_num, unsigned int component) const
2302 {
2303  mooseAssert(component < dimension(), "Requested dimension out of bounds");
2304 
2305  if (_periodic_dim.find(nonlinear_var_num) != _periodic_dim.end())
2306  return _periodic_dim.at(nonlinear_var_num)[component];
2307  else
2308  return false;
2309 }
2310 
2312 MooseMesh::minPeriodicVector(unsigned int nonlinear_var_num, Point p, Point q) const
2313 {
2314  for (unsigned int i = 0; i < dimension(); ++i)
2315  {
2316  // check to see if we're closer in real or periodic space in x, y, and z
2317  if (isTranslatedPeriodic(nonlinear_var_num, i))
2318  {
2319  // Need to test order before differencing
2320  if (p(i) > q(i))
2321  {
2322  if (p(i) - q(i) > _half_range(i))
2323  p(i) -= _half_range(i) * 2;
2324  }
2325  else
2326  {
2327  if (q(i) - p(i) > _half_range(i))
2328  p(i) += _half_range(i) * 2;
2329  }
2330  }
2331  }
2332 
2333  return q - p;
2334 }
2335 
2336 Real
2337 MooseMesh::minPeriodicDistance(unsigned int nonlinear_var_num, Point p, Point q) const
2338 {
2339  return minPeriodicVector(nonlinear_var_num, p, q).norm();
2340 }
2341 
2342 const std::pair<BoundaryID, BoundaryID> *
2344 {
2346  mooseError("Trying to retrieve automatic paired mapping for a mesh that is not regular and "
2347  "orthogonal");
2348 
2349  mooseAssert(component < dimension(), "Requested dimension out of bounds");
2350 
2351  if (_paired_boundary.empty())
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 
2861 const RealVectorValue &
2863 {
2864  mooseAssert(_boundary_to_normal_map.get() != nullptr, "Boundary To Normal Map not built!");
2865 
2866  // Note: Boundaries that are not in the map (existing boundaries) will default
2867  // construct a new RealVectorValue - (x,y,z)=(0, 0, 0)
2868  return (*_boundary_to_normal_map)[id];
2869 }
2870 
2871 MooseMesh &
2873 {
2874  mooseError("MooseMesh::clone() is no longer supported, use MooseMesh::safeClone() instead.");
2875 }
2876 
2877 void
2879 {
2880  switch (_parallel_type)
2881  {
2882  case ParallelType::DEFAULT:
2883  // The user did not specify 'parallel_type = XYZ' in the input file,
2884  // so we allow the --distributed-mesh command line arg to possibly turn
2885  // on DistributedMesh. If the command line arg is not present, we pick ReplicatedMesh.
2887  _use_distributed_mesh = true;
2888  break;
2892  _use_distributed_mesh = false;
2893  break;
2895  _use_distributed_mesh = true;
2896  break;
2897  }
2898 
2899  // If the user specifies 'nemesis = true' in the Mesh block, or they are using --use-split,
2900  // we must use DistributedMesh.
2901  if (_is_nemesis || _is_split)
2902  _use_distributed_mesh = true;
2903 }
2904 
2905 std::unique_ptr<MeshBase>
2907 {
2908  std::unique_ptr<MeshBase> mesh;
2910  mesh = buildTypedMesh<DistributedMesh>(dim);
2911  else
2912  mesh = buildTypedMesh<ReplicatedMesh>(dim);
2913 
2914  return mesh;
2915 }
2916 
2917 void
2918 MooseMesh::setMeshBase(std::unique_ptr<MeshBase> mesh_base)
2919 {
2920  _mesh = std::move(mesh_base);
2921  _mesh->allow_remote_element_removal(_allow_remote_element_removal);
2922 }
2923 
2924 void
2926 {
2933  if (!_mesh)
2935 
2937  mooseError("You cannot use the mesh splitter capability with DistributedMesh!");
2938 
2939  TIME_SECTION("init", 2);
2940 
2942  {
2943  // Some partitioners are not idempotent. Some recovery data
2944  // files require partitioning to match mesh partitioning. This
2945  // means that, when recovering, we can't safely repartition.
2946  const bool skip_partitioning_later = getMesh().skip_partitioning();
2947  getMesh().skip_partitioning(true);
2948  const bool allow_renumbering_later = getMesh().allow_renumbering();
2949  getMesh().allow_renumbering(false);
2950 
2951  // For now, only read the recovery mesh on the Ultimate Master..
2952  // sub-apps need to just build their mesh like normal
2953  {
2954  TIME_SECTION("readRecoveredMesh", 2);
2956  }
2957 
2958  getMesh().allow_renumbering(allow_renumbering_later);
2959  getMesh().skip_partitioning(skip_partitioning_later);
2960  }
2961  else // Normally just build the mesh
2962  {
2963  // Don't allow partitioning during building
2964  if (_app.isSplitMesh())
2965  getMesh().skip_partitioning(true);
2966  buildMesh();
2967 
2968  // Re-enable partitioning so the splitter can partition!
2969  if (_app.isSplitMesh())
2970  getMesh().skip_partitioning(false);
2971 
2972  if (getParam<bool>("build_all_side_lowerd_mesh"))
2973  buildLowerDMesh();
2974  }
2975 
2977 }
2978 
2979 unsigned int
2981 {
2982  return getMesh().mesh_dimension();
2983 }
2984 
2985 unsigned int
2987 {
2988  const Real abs_zero = 1e-12;
2989 
2990  // See if the mesh is completely containd in the z and y planes to calculate effective spatial
2991  // dim
2992  for (unsigned int dim = LIBMESH_DIM; dim >= 1; --dim)
2993  if (dimensionWidth(dim - 1) >= abs_zero)
2994  return dim;
2995 
2996  // If we get here, we have a 1D mesh on the x-axis.
2997  return 1;
2998 }
2999 
3000 unsigned int
3001 MooseMesh::getBlocksMaxDimension(const std::vector<SubdomainName> & blocks) const
3002 {
3003  const auto & mesh = getMesh();
3004 
3005  // Take a shortcut if possible
3006  if (const auto & elem_dims = mesh.elem_dimensions(); mesh.is_prepared() && elem_dims.size() == 1)
3007  return *elem_dims.begin();
3008 
3009  unsigned short dim = 0;
3010  const auto subdomain_ids = getSubdomainIDs(blocks);
3011  const std::set<SubdomainID> subdomain_ids_set(subdomain_ids.begin(), subdomain_ids.end());
3012  for (const auto & elem : mesh.active_subdomain_set_elements_ptr_range(subdomain_ids_set))
3013  dim = std::max(dim, elem->dim());
3014 
3015  // Get the maximumal globally
3017  return dim;
3018 }
3019 
3020 std::vector<BoundaryID>
3021 MooseMesh::getBoundaryIDs(const Elem * const elem, const unsigned short int side) const
3022 {
3023  std::vector<BoundaryID> ids;
3024  getMesh().get_boundary_info().boundary_ids(elem, side, ids);
3025  return ids;
3026 }
3027 
3028 const std::set<BoundaryID> &
3030 {
3032 }
3033 
3034 void
3036 {
3037  auto & boundary_info = getMesh().get_boundary_info();
3038 
3040  {
3041  const std::set<boundary_id_type> & side_bcids = boundary_info.get_side_boundary_ids();
3042 
3044  {
3045  // Don't want to use auto here - the rbegin trick relies on a
3046  // sorted set and we want the compiler to scream if libMesh ever
3047  // switches type
3048  const std::set<boundary_id_type> & node_bcids = boundary_info.get_node_boundary_ids();
3049 
3050  // If we've got a reasonable largest BC id, we can just use the
3051  // subsequent unused ones
3052  boundary_id_type next_bcid = 0;
3053  if (!node_bcids.empty())
3054  next_bcid = std::max(next_bcid, cast_int<boundary_id_type>(*node_bcids.rbegin() + 1));
3055  if (!side_bcids.empty())
3056  next_bcid = std::max(next_bcid, cast_int<boundary_id_type>(*side_bcids.rbegin() + 1));
3057 
3058  // If we've got an unreasonable largest BC id, we should
3059  // probably just search for unused ones with moderate values, so we
3060  // don't risk wrapping.
3061  if (next_bcid > 1000 || next_bcid <= 0)
3062  next_bcid = 1000;
3063 
3064  // If any side bcid is already a node bcid with a different name,
3065  // that's a different boundary condition that we need to reassign
3066  // rather than overwrite or merge to.
3067  for (auto bcid : side_bcids)
3068  if (node_bcids.count(bcid) &&
3069  (boundary_info.get_sideset_name(bcid) != boundary_info.get_nodeset_name(bcid)))
3070  {
3071  boundary_info.renumber_node_id(bcid, next_bcid);
3072  do
3073  {
3074  ++next_bcid;
3075  } while (node_bcids.count(next_bcid) || side_bcids.count(next_bcid));
3076  }
3077  }
3078 
3079  // If any side bcid isn't already a node bcid, we should make
3080  // sure that our new node bcid is given the same name.
3081  for (auto bcid : side_bcids)
3082  boundary_info.nodeset_name(bcid) = boundary_info.get_sideset_name(bcid);
3083 
3084  boundary_info.build_node_list_from_side_list();
3085  }
3086 }
3087 
3088 std::vector<std::tuple<dof_id_type, unsigned short int, boundary_id_type>>
3090 {
3092 }
3093 
3094 std::vector<std::tuple<dof_id_type, unsigned short int, boundary_id_type>>
3096 {
3098 }
3099 
3100 unsigned int
3101 MooseMesh::sideWithBoundaryID(const Elem * const elem, const BoundaryID boundary_id) const
3102 {
3103  return getMesh().get_boundary_info().side_with_boundary_id(elem, boundary_id);
3104 }
3105 
3108 {
3109  return getMesh().local_nodes_begin();
3110 }
3111 
3114 {
3115  return getMesh().local_nodes_end();
3116 }
3117 
3118 MeshBase::const_node_iterator
3120 {
3121  return getMesh().local_nodes_begin();
3122 }
3123 
3124 MeshBase::const_node_iterator
3126 {
3127  return getMesh().local_nodes_end();
3128 }
3129 
3130 MeshBase::element_iterator
3132 {
3133  return getMesh().active_local_elements_begin();
3134 }
3135 
3136 const MeshBase::element_iterator
3138 {
3139  return getMesh().active_local_elements_end();
3140 }
3141 
3142 MeshBase::const_element_iterator
3144 {
3145  return getMesh().active_local_elements_begin();
3146 }
3147 
3148 const MeshBase::const_element_iterator
3150 {
3151  return getMesh().active_local_elements_end();
3152 }
3153 
3156 {
3157  return getMesh().n_nodes();
3158 }
3159 
3162 {
3163  return getMesh().n_elem();
3164 }
3165 
3168 {
3169  return getMesh().max_node_id();
3170 }
3171 
3174 {
3175  return getMesh().max_elem_id();
3176 }
3177 
3178 Elem *
3180 {
3181  mooseDeprecated("MooseMesh::elem() is deprecated, please use MooseMesh::elemPtr() instead");
3182  return elemPtr(i);
3183 }
3184 
3185 const Elem *
3187 {
3188  mooseDeprecated("MooseMesh::elem() is deprecated, please use MooseMesh::elemPtr() instead");
3189  return elemPtr(i);
3190 }
3191 
3192 Elem *
3194 {
3195  return getMesh().elem_ptr(i);
3196 }
3197 
3198 const Elem *
3200 {
3201  return getMesh().elem_ptr(i);
3202 }
3203 
3204 Elem *
3206 {
3207  return getMesh().query_elem_ptr(i);
3208 }
3209 
3210 const Elem *
3212 {
3213  return getMesh().query_elem_ptr(i);
3214 }
3215 
3216 bool
3218 {
3219  return _mesh->is_prepared() && _moose_mesh_prepared;
3220 }
3221 
3222 void
3224 {
3225  if (state)
3226  mooseError("We don't have any right to tell the libmesh mesh that it *is* prepared. Only a "
3227  "call to prepare_for_use should tell us that");
3228 
3229  // Some people may call this even before we have a MeshBase object. This isn't dangerous really
3230  // because when the MeshBase object is born, it knows it's in an unprepared state
3231  if (_mesh)
3232  _mesh->set_isnt_prepared();
3233 
3234  // If the libMesh mesh isn't preparead, then our MooseMesh wrapper is also no longer prepared
3235  _moose_mesh_prepared = false;
3236 
3241  _regular_orthogonal_mesh = false;
3242 }
3243 
3244 void
3246 {
3247  prepared(false);
3248 }
3249 
3250 const std::set<SubdomainID> &
3252 {
3253  return _mesh_subdomains;
3254 }
3255 
3256 const std::set<BoundaryID> &
3258 {
3259  return _mesh_boundary_ids;
3260 }
3261 
3262 const std::set<BoundaryID> &
3264 {
3265  return _mesh_sideset_ids;
3266 }
3267 
3268 const std::set<BoundaryID> &
3270 {
3271  return _mesh_nodeset_ids;
3272 }
3273 
3274 void
3275 MooseMesh::setMeshBoundaryIDs(std::set<BoundaryID> boundary_IDs)
3276 {
3277  _mesh_boundary_ids = boundary_IDs;
3278 }
3279 
3280 void
3282  std::unique_ptr<std::map<BoundaryID, RealVectorValue>> boundary_map)
3283 {
3284  _boundary_to_normal_map = std::move(boundary_map);
3285 }
3286 
3287 void
3288 MooseMesh::setBoundaryToNormalMap(std::map<BoundaryID, RealVectorValue> * boundary_map)
3289 {
3290  mooseDeprecated("setBoundaryToNormalMap(std::map<BoundaryID, RealVectorValue> * boundary_map) is "
3291  "deprecated, use the unique_ptr version instead");
3292  _boundary_to_normal_map.reset(boundary_map);
3293 }
3294 
3295 unsigned int
3297 {
3298  return _uniform_refine_level;
3299 }
3300 
3301 void
3302 MooseMesh::setUniformRefineLevel(unsigned int level, bool deletion)
3303 {
3304  _uniform_refine_level = level;
3306 }
3307 
3308 void
3310 {
3311  _ghosted_boundaries.insert(boundary_id);
3312 }
3313 
3314 void
3315 MooseMesh::setGhostedBoundaryInflation(const std::vector<Real> & inflation)
3316 {
3317  _ghosted_boundaries_inflation = inflation;
3318 }
3319 
3320 const std::set<unsigned int> &
3322 {
3323  return _ghosted_boundaries;
3324 }
3325 
3326 const std::vector<Real> &
3328 {
3330 }
3331 
3332 namespace // Anonymous namespace for helpers
3333 {
3334 // A class for templated methods that expect output iterator
3335 // arguments, which adds objects to the Mesh.
3336 // Although extra_ghost_elem_inserter can add any object, we
3337 // template it around object type so that type inference and
3338 // iterator_traits will work.
3339 // This object specifically is used to insert extra ghost elems into the mesh
3340 template <typename T>
3341 struct extra_ghost_elem_inserter
3342 {
3343  using iterator_category = std::output_iterator_tag;
3344  using value_type = T;
3345 
3346  extra_ghost_elem_inserter(DistributedMesh & m) : mesh(m) {}
3347 
3348  void operator=(const Elem * e) { mesh.add_extra_ghost_elem(const_cast<Elem *>(e)); }
3349 
3350  void operator=(Node * n) { mesh.add_node(n); }
3351 
3352  void operator=(Point * p) { mesh.add_point(*p); }
3353 
3354  extra_ghost_elem_inserter & operator++() { return *this; }
3355 
3356  extra_ghost_elem_inserter operator++(int) { return extra_ghost_elem_inserter(*this); }
3357 
3358  // We don't return a reference-to-T here because we don't want to
3359  // construct one or have any of its methods called. We just want
3360  // to allow the returned object to be able to do mesh insertions
3361  // with operator=().
3362  extra_ghost_elem_inserter & operator*() { return *this; }
3363 
3364 private:
3366 };
3367 
3378 struct CompareElemsByLevel
3379 {
3380  bool operator()(const Elem * a, const Elem * b) const
3381  {
3382  libmesh_assert(a);
3383  libmesh_assert(b);
3384  const unsigned int al = a->level(), bl = b->level();
3385  const dof_id_type aid = a->id(), bid = b->id();
3386 
3387  return (al == bl) ? aid < bid : al < bl;
3388  }
3389 };
3390 
3391 } // anonymous namespace
3392 
3393 void
3395 {
3396  // No need to do this if using a serial mesh
3397  // We do not need to ghost boundary elements when _need_ghost_ghosted_boundaries
3398  // is not true. _need_ghost_ghosted_boundaries can be set by a mesh generator
3399  // where boundaries are already ghosted accordingly
3401  return;
3402 
3403  TIME_SECTION("GhostGhostedBoundaries", 3);
3404 
3405  parallel_object_only();
3406 
3407  DistributedMesh & mesh = dynamic_cast<DistributedMesh &>(getMesh());
3408 
3409  // We clear ghosted elements that were added by previous invocations of this
3410  // method but leave ghosted elements that were added by other code, e.g.
3411  // OversampleOutput, untouched
3412  mesh.clear_extra_ghost_elems(_ghost_elems_from_ghost_boundaries);
3414 
3415  std::set<const Elem *, CompareElemsByLevel> boundary_elems_to_ghost;
3416  std::set<Node *> connected_nodes_to_ghost;
3417 
3418  std::vector<const Elem *> family_tree;
3419 
3420  for (const auto & t : mesh.get_boundary_info().build_side_list())
3421  {
3422  auto elem_id = std::get<0>(t);
3423  auto bc_id = std::get<2>(t);
3424 
3425  if (_ghosted_boundaries.find(bc_id) != _ghosted_boundaries.end())
3426  {
3427  Elem * elem = mesh.elem_ptr(elem_id);
3428 
3429 #ifdef LIBMESH_ENABLE_AMR
3431  Elem * parent = elem->parent();
3432  while (parent)
3433  {
3434  family_tree.push_back(parent);
3435  parent = parent->parent();
3436  }
3437 #else
3438  family_tree.clear();
3439  family_tree.push_back(elem);
3440 #endif
3441  for (const auto & felem : family_tree)
3442  {
3443  boundary_elems_to_ghost.insert(felem);
3444 
3445  // The entries of connected_nodes_to_ghost need to be
3446  // non-constant, so that they will work in things like
3447  // UpdateDisplacedMeshThread. The container returned by
3448  // family_tree contains const Elems even when the Elem
3449  // it is called on is non-const, so once that interface
3450  // gets fixed we can remove this const_cast.
3451  for (unsigned int n = 0; n < felem->n_nodes(); ++n)
3452  connected_nodes_to_ghost.insert(const_cast<Node *>(felem->node_ptr(n)));
3453  }
3454  }
3455  }
3456 
3457  // We really do want to store this by value instead of by reference
3458  const auto prior_ghost_elems = mesh.extra_ghost_elems();
3459 
3461  connected_nodes_to_ghost.begin(),
3462  connected_nodes_to_ghost.end(),
3463  extra_ghost_elem_inserter<Node>(mesh));
3464 
3466  boundary_elems_to_ghost.begin(),
3467  boundary_elems_to_ghost.end(),
3468  extra_ghost_elem_inserter<Elem>(mesh));
3469 
3470  const auto & current_ghost_elems = mesh.extra_ghost_elems();
3471 
3472  std::set_difference(current_ghost_elems.begin(),
3473  current_ghost_elems.end(),
3474  prior_ghost_elems.begin(),
3475  prior_ghost_elems.end(),
3476  std::inserter(_ghost_elems_from_ghost_boundaries,
3478 }
3479 
3480 unsigned int
3482 {
3483  return _patch_size;
3484 }
3485 
3486 void
3488 {
3489  _patch_update_strategy = patch_update_strategy;
3490 }
3491 
3492 const Moose::PatchUpdateType &
3494 {
3495  return _patch_update_strategy;
3496 }
3497 
3499 MooseMesh::getInflatedProcessorBoundingBox(Real inflation_multiplier) const
3500 {
3501  // Grab a bounding box to speed things up. Note that
3502  // local_bounding_box is *not* equivalent to processor_bounding_box
3503  // with processor_id() except in serial.
3504  BoundingBox bbox = MeshTools::create_local_bounding_box(getMesh());
3505 
3506  // Inflate the bbox just a bit to deal with roundoff
3507  // Adding 1% of the diagonal size in each direction on each end
3508  Real inflation_amount = inflation_multiplier * (bbox.max() - bbox.min()).norm();
3509  Point inflation(inflation_amount, inflation_amount, inflation_amount);
3510 
3511  bbox.first -= inflation; // min
3512  bbox.second += inflation; // max
3513 
3514  return bbox;
3515 }
3516 
3517 MooseMesh::operator libMesh::MeshBase &() { return getMesh(); }
3518 
3519 MooseMesh::operator const libMesh::MeshBase &() const { return getMesh(); }
3520 
3521 const MeshBase *
3523 {
3524  return _mesh.get();
3525 }
3526 
3527 MeshBase &
3529 {
3530  mooseAssert(_mesh, "Mesh hasn't been created");
3531  return *_mesh;
3532 }
3533 
3534 const MeshBase &
3536 {
3537  mooseAssert(_mesh, "Mesh hasn't been created");
3538  return *_mesh;
3539 }
3540 
3541 void
3542 MooseMesh::printInfo(std::ostream & os, const unsigned int verbosity /* = 0 */) const
3543 {
3544  os << '\n';
3545  getMesh().print_info(os, verbosity);
3546  os << std::flush;
3547 }
3548 
3549 const std::vector<dof_id_type> &
3551 {
3552  std::map<boundary_id_type, std::vector<dof_id_type>>::const_iterator it =
3553  _node_set_nodes.find(nodeset_id);
3554 
3555  if (it == _node_set_nodes.end())
3556  {
3557  // On a distributed mesh we might not know about a remote nodeset,
3558  // so we'll return an empty vector and hope the nodeset exists
3559  // elsewhere.
3560  if (!getMesh().is_serial())
3561  {
3562  static const std::vector<dof_id_type> empty_vec;
3563  return empty_vec;
3564  }
3565  // On a replicated mesh we should know about every nodeset and if
3566  // we're asked for one that doesn't exist then it must be a bug.
3567  else
3568  {
3569  mooseError("Unable to nodeset ID: ", nodeset_id, '.');
3570  }
3571  }
3572 
3573  return it->second;
3574 }
3575 
3576 const std::set<BoundaryID> &
3578 {
3579  const auto it = _sub_to_data.find(subdomain_id);
3580 
3581  if (it == _sub_to_data.end())
3582  mooseError("Unable to find subdomain ID: ", subdomain_id, '.');
3583 
3584  return it->second.boundary_ids;
3585 }
3586 
3587 std::set<BoundaryID>
3589 {
3590  const auto & bnd_ids = getSubdomainBoundaryIds(subdomain_id);
3591  std::set<BoundaryID> boundary_ids(bnd_ids.begin(), bnd_ids.end());
3592  std::unordered_map<SubdomainID, std::set<BoundaryID>>::const_iterator it =
3593  _neighbor_subdomain_boundary_ids.find(subdomain_id);
3594 
3595  boundary_ids.insert(it->second.begin(), it->second.end());
3596 
3597  return boundary_ids;
3598 }
3599 
3600 std::set<SubdomainID>
3602 {
3603  std::set<SubdomainID> subdomain_ids;
3604  for (const auto & [sub_id, data] : _sub_to_data)
3605  if (data.boundary_ids.find(bid) != data.boundary_ids.end())
3606  subdomain_ids.insert(sub_id);
3607 
3608  return subdomain_ids;
3609 }
3610 
3611 std::set<SubdomainID>
3613 {
3614  std::set<SubdomainID> subdomain_ids;
3615  for (const auto & it : _neighbor_subdomain_boundary_ids)
3616  if (it.second.find(bid) != it.second.end())
3617  subdomain_ids.insert(it.first);
3618 
3619  return subdomain_ids;
3620 }
3621 
3622 std::set<SubdomainID>
3624 {
3625  std::set<SubdomainID> subdomain_ids = getBoundaryConnectedBlocks(bid);
3626  for (const auto & it : _neighbor_subdomain_boundary_ids)
3627  if (it.second.find(bid) != it.second.end())
3628  subdomain_ids.insert(it.first);
3629 
3630  return subdomain_ids;
3631 }
3632 
3633 const std::set<SubdomainID> &
3635 {
3636  const auto it = _sub_to_data.find(subdomain_id);
3637 
3638  if (it == _sub_to_data.end())
3639  mooseError("Unable to find subdomain ID: ", subdomain_id, '.');
3640 
3641  return it->second.neighbor_subs;
3642 }
3643 
3644 bool
3646 {
3647  bool found_node = false;
3648  for (const auto & it : _bnd_node_ids)
3649  {
3650  if (it.second.find(node_id) != it.second.end())
3651  {
3652  found_node = true;
3653  break;
3654  }
3655  }
3656  return found_node;
3657 }
3658 
3659 bool
3661 {
3662  bool found_node = false;
3663  std::map<boundary_id_type, std::set<dof_id_type>>::const_iterator it = _bnd_node_ids.find(bnd_id);
3664  if (it != _bnd_node_ids.end())
3665  if (it->second.find(node_id) != it->second.end())
3666  found_node = true;
3667  return found_node;
3668 }
3669 
3670 bool
3672 {
3673  bool found_elem = false;
3674  for (const auto & it : _bnd_elem_ids)
3675  {
3676  if (it.second.find(elem_id) != it.second.end())
3677  {
3678  found_elem = true;
3679  break;
3680  }
3681  }
3682  return found_elem;
3683 }
3684 
3685 bool
3687 {
3688  bool found_elem = false;
3689  auto it = _bnd_elem_ids.find(bnd_id);
3690  if (it != _bnd_elem_ids.end())
3691  if (it->second.find(elem_id) != it->second.end())
3692  found_elem = true;
3693  return found_elem;
3694 }
3695 
3696 void
3697 MooseMesh::errorIfDistributedMesh(std::string name) const
3698 {
3700  mooseError("Cannot use ",
3701  name,
3702  " with DistributedMesh!\n",
3703  "Consider specifying parallel_type = 'replicated' in your input file\n",
3704  "to prevent it from being run with DistributedMesh.");
3705 }
3706 
3707 void
3709 {
3710  if (_use_distributed_mesh && (_partitioner_name != "default" && _partitioner_name != "parmetis"))
3711  {
3712  _partitioner_name = "parmetis";
3713  _partitioner_overridden = true;
3714  }
3715 
3717 }
3718 
3719 void
3721  MooseEnum & partitioner,
3722  bool use_distributed_mesh,
3723  const InputParameters & params,
3724  MooseObject & context_obj)
3725 {
3726  // Set the partitioner based on partitioner name
3727  switch (partitioner)
3728  {
3729  case -3: // default
3730  // We'll use the default partitioner, but notify the user of which one is being used...
3731  if (use_distributed_mesh)
3732  partitioner = "parmetis";
3733  else
3734  partitioner = "metis";
3735  break;
3736 
3737  // No need to explicitily create the metis or parmetis partitioners,
3738  // They are the default for serial and parallel mesh respectively
3739  case -2: // metis
3740  case -1: // parmetis
3741  break;
3742 
3743  case 0: // linear
3744  mesh_base.partitioner().reset(new libMesh::LinearPartitioner);
3745  break;
3746  case 1: // centroid
3747  {
3748  if (!params.isParamValid("centroid_partitioner_direction"))
3749  context_obj.paramError(
3750  "centroid_partitioner_direction",
3751  "If using the centroid partitioner you _must_ specify centroid_partitioner_direction!");
3752 
3753  MooseEnum direction = params.get<MooseEnum>("centroid_partitioner_direction");
3754 
3755  if (direction == "x")
3756  mesh_base.partitioner().reset(
3758  else if (direction == "y")
3759  mesh_base.partitioner().reset(
3761  else if (direction == "z")
3762  mesh_base.partitioner().reset(
3764  else if (direction == "radial")
3765  mesh_base.partitioner().reset(
3767  break;
3768  }
3769  case 2: // hilbert_sfc
3770  mesh_base.partitioner().reset(new libMesh::HilbertSFCPartitioner);
3771  break;
3772  case 3: // morton_sfc
3773  mesh_base.partitioner().reset(new libMesh::MortonSFCPartitioner);
3774  break;
3775  }
3776 }
3777 
3778 void
3780 {
3781  _custom_partitioner = partitioner->clone();
3782 }
3783 
3784 bool
3786 {
3788 }
3789 
3790 bool
3792 {
3793  bool mesh_has_second_order_elements = false;
3794  for (auto it = activeLocalElementsBegin(), end = activeLocalElementsEnd(); it != end; ++it)
3795  if ((*it)->default_order() == SECOND)
3796  {
3797  mesh_has_second_order_elements = true;
3798  break;
3799  }
3800 
3801  // We checked our local elements, so take the max over all processors.
3802  comm().max(mesh_has_second_order_elements);
3803  return mesh_has_second_order_elements;
3804 }
3805 
3806 void
3808 {
3810 }
3811 
3812 std::unique_ptr<libMesh::PointLocatorBase>
3814 {
3815  return getMesh().sub_point_locator();
3816 }
3817 
3818 void
3820 {
3821  mooseAssert(!Threads::in_threads,
3822  "This routine has not been implemented for threads. Please query this routine before "
3823  "a threaded region or contact a MOOSE developer to discuss.");
3824  _finite_volume_info_dirty = false;
3825 
3826  using Keytype = std::pair<const Elem *, unsigned short int>;
3827 
3828  // create a map from elem/side --> boundary ids
3829  std::vector<std::tuple<dof_id_type, unsigned short int, boundary_id_type>> side_list =
3831  std::map<Keytype, std::set<boundary_id_type>> side_map;
3832  for (auto & [elem_id, side, bc_id] : side_list)
3833  {
3834  const Elem * elem = _mesh->elem_ptr(elem_id);
3835  Keytype key(elem, side);
3836  auto & bc_set = side_map[key];
3837  bc_set.insert(bc_id);
3838  }
3839 
3840  _face_info.clear();
3841  _all_face_info.clear();
3842  _elem_side_to_face_info.clear();
3843 
3844  _elem_to_elem_info.clear();
3845  _elem_info.clear();
3846 
3847  // by performing the element ID comparison check in the below loop, we are ensuring that we never
3848  // double count face contributions. If a face lies along a process boundary, the only process that
3849  // will contribute to both sides of the face residuals/Jacobians will be the process that owns the
3850  // element with the lower ID.
3851  auto begin = getMesh().active_elements_begin();
3852  auto end = getMesh().active_elements_end();
3853 
3854  // We prepare a map connecting the Elem* and the corresponding ElemInfo
3855  // for the active elements.
3856  for (const Elem * elem : as_range(begin, end))
3857  _elem_to_elem_info.emplace(elem->id(), elem);
3858 
3859  dof_id_type face_index = 0;
3860  for (const Elem * elem : as_range(begin, end))
3861  {
3862  for (unsigned int side = 0; side < elem->n_sides(); ++side)
3863  {
3864  // get the neighbor element
3865  const Elem * neighbor = elem->neighbor_ptr(side);
3866 
3867  // Check if the FaceInfo shall belong to the element. If yes,
3868  // create and initialize the FaceInfo. We need this to ensure that
3869  // we do not duplicate FaceInfo-s.
3870  if (Moose::FV::elemHasFaceInfo(*elem, neighbor))
3871  {
3872  mooseAssert(!neighbor || (neighbor->level() < elem->level() ? neighbor->active() : true),
3873  "If the neighbor is coarser than the element, we expect that the neighbor must "
3874  "be active.");
3875 
3876  // We construct the faceInfo using the elementinfo and side index
3877  _all_face_info.emplace_back(&_elem_to_elem_info[elem->id()], side, face_index++);
3878 
3879  auto & fi = _all_face_info.back();
3880 
3881  // get all the sidesets that this face is contained in and cache them
3882  // in the face info.
3883  std::set<boundary_id_type> & boundary_ids = fi.boundaryIDs();
3884  boundary_ids.clear();
3885 
3886  // We initialize the weights/other information in faceInfo. If the neighbor does not exist
3887  // or is remote (so when we are on some sort of mesh boundary), we initialize the ghost
3888  // cell and use it to compute the weights corresponding to the faceInfo.
3889  if (!neighbor || neighbor == libMesh::remote_elem)
3890  fi.computeBoundaryCoefficients();
3891  else
3892  fi.computeInternalCoefficients(&_elem_to_elem_info[neighbor->id()]);
3893 
3894  auto lit = side_map.find(Keytype(&fi.elem(), fi.elemSideID()));
3895  if (lit != side_map.end())
3896  boundary_ids.insert(lit->second.begin(), lit->second.end());
3897 
3898  if (fi.neighborPtr())
3899  {
3900  auto rit = side_map.find(Keytype(fi.neighborPtr(), fi.neighborSideID()));
3901  if (rit != side_map.end())
3902  boundary_ids.insert(rit->second.begin(), rit->second.end());
3903  }
3904  }
3905  }
3906  }
3907 
3908  // Build the local face info and elem_side to face info maps. We need to do this after
3909  // _all_face_info is finished being constructed because emplace_back invalidates all iterators and
3910  // references if ever the new size exceeds capacity
3911  for (auto & fi : _all_face_info)
3912  {
3913  const Elem * const elem = &fi.elem();
3914  const auto side = fi.elemSideID();
3915 
3916 #ifndef NDEBUG
3917  auto pair_it =
3918 #endif
3919  _elem_side_to_face_info.emplace(std::make_pair(elem, side), &fi);
3920  mooseAssert(pair_it.second, "We should be adding unique FaceInfo objects.");
3921 
3922  // We will add the faces on processor boundaries to the list of face infos on each
3923  // associated processor.
3924  if (fi.elem().processor_id() == this->processor_id() ||
3925  (fi.neighborPtr() && (fi.neighborPtr()->processor_id() == this->processor_id())))
3926  _face_info.push_back(&fi);
3927  }
3928 
3929  for (auto & ei : _elem_to_elem_info)
3930  if (ei.second.elem()->processor_id() == this->processor_id())
3931  _elem_info.push_back(&ei.second);
3932 }
3933 
3934 const FaceInfo *
3935 MooseMesh::faceInfo(const Elem * elem, unsigned int side) const
3936 {
3937  auto it = _elem_side_to_face_info.find(std::make_pair(elem, side));
3938 
3939  if (it == _elem_side_to_face_info.end())
3940  return nullptr;
3941  else
3942  {
3943  mooseAssert(it->second,
3944  "For some reason, the FaceInfo object is NULL! Try calling "
3945  "`buildFiniteVolumeInfo()` before using this accessor!");
3946  return it->second;
3947  }
3948 }
3949 
3950 const ElemInfo &
3952 {
3953  return libmesh_map_find(_elem_to_elem_info, id);
3954 }
3955 
3956 void
3958 {
3960  mooseError("Trying to compute face- and elem-info coords when the information is dirty");
3961 
3962  for (auto & fi : _all_face_info)
3963  {
3964  // get elem & neighbor elements, and set subdomain ids
3965  const SubdomainID elem_subdomain_id = fi.elemSubdomainID();
3966  const SubdomainID neighbor_subdomain_id = fi.neighborSubdomainID();
3967 
3969  *this, elem_subdomain_id, fi.faceCentroid(), fi.faceCoord(), neighbor_subdomain_id);
3970  }
3971 
3972  for (auto & ei : _elem_to_elem_info)
3974  *this, ei.second.subdomain_id(), ei.second.centroid(), ei.second.coordFactor());
3975 }
3976 
3977 MooseEnum
3979 {
3980  MooseEnum partitioning("default=-3 metis=-2 parmetis=-1 linear=0 centroid hilbert_sfc morton_sfc",
3981  "default");
3982  return partitioning;
3983 }
3984 
3985 MooseEnum
3987 {
3989  "EDGE EDGE2 EDGE3 EDGE4 QUAD QUAD4 QUAD8 QUAD9 TRI3 TRI6 HEX HEX8 HEX20 HEX27 TET4 TET10 "
3990  "PRISM6 PRISM15 PRISM18 PYRAMID5 PYRAMID13 PYRAMID14");
3991  return elemTypes;
3992 }
3993 
3994 void
3995 MooseMesh::allowRemoteElementRemoval(const bool allow_remote_element_removal)
3996 {
3997  _allow_remote_element_removal = allow_remote_element_removal;
3998  if (_mesh)
3999  _mesh->allow_remote_element_removal(allow_remote_element_removal);
4000 
4001  if (!allow_remote_element_removal)
4002  // If we're not allowing remote element removal now, then we will need deletion later after
4003  // late geoemetric ghosting functors have been added (late geometric ghosting functor addition
4004  // happens when algebraic ghosting functors are added)
4005  _need_delete = true;
4006 }
4007 
4008 void
4010 {
4012  if (!_mesh)
4013  mooseError("Cannot delete remote elements because we have not yet attached a MeshBase");
4014 
4015  _mesh->allow_remote_element_removal(true);
4016 
4017  _mesh->delete_remote_elements();
4018 }
4019 
4020 void
4022 {
4023  mooseAssert(
4024  !Threads::in_threads,
4025  "Performing writes to faceInfo variable association maps. This must be done unthreaded!");
4026 
4027  const unsigned int num_eqs = _app.feProblem().es().n_systems();
4028 
4029  auto face_lambda = [this](const SubdomainID elem_subdomain_id,
4030  const SubdomainID neighbor_subdomain_id,
4031  SystemBase & sys,
4032  std::vector<std::vector<FaceInfo::VarFaceNeighbors>> & face_type_vector)
4033  {
4034  face_type_vector[sys.number()].resize(sys.nVariables(), FaceInfo::VarFaceNeighbors::NEITHER);
4035  const auto & variables = sys.getVariables(0);
4036 
4037  for (const auto & var : variables)
4038  {
4039  const unsigned int var_num = var->number();
4040  const unsigned int sys_num = var->sys().number();
4041  std::set<SubdomainID> var_subdomains = var->blockIDs();
4051  bool var_defined_elem = var_subdomains.find(elem_subdomain_id) != var_subdomains.end();
4052  bool var_defined_neighbor =
4053  var_subdomains.find(neighbor_subdomain_id) != var_subdomains.end();
4054  if (var_defined_elem && var_defined_neighbor)
4055  face_type_vector[sys_num][var_num] = FaceInfo::VarFaceNeighbors::BOTH;
4056  else if (!var_defined_elem && !var_defined_neighbor)
4057  face_type_vector[sys_num][var_num] = FaceInfo::VarFaceNeighbors::NEITHER;
4058  else
4059  {
4060  // this is a boundary face for this variable, set elem or neighbor
4061  if (var_defined_elem)
4062  face_type_vector[sys_num][var_num] = FaceInfo::VarFaceNeighbors::ELEM;
4063  else if (var_defined_neighbor)
4064  face_type_vector[sys_num][var_num] = FaceInfo::VarFaceNeighbors::NEIGHBOR;
4065  else
4066  mooseError("Should never get here");
4067  }
4068  }
4069  };
4070 
4071  // We loop through the faces and check if they are internal, boundary or external to
4072  // the variables in the problem
4073  for (FaceInfo & face : _all_face_info)
4074  {
4075  const SubdomainID elem_subdomain_id = face.elemSubdomainID();
4076  const SubdomainID neighbor_subdomain_id = face.neighborSubdomainID();
4077 
4078  auto & face_type_vector = face.faceType();
4079 
4080  face_type_vector.clear();
4081  face_type_vector.resize(num_eqs);
4082 
4083  // First, we check the variables in the solver systems (linear/nonlinear)
4084  for (const auto i : make_range(_app.feProblem().numSolverSystems()))
4085  face_lambda(elem_subdomain_id,
4086  neighbor_subdomain_id,
4088  face_type_vector);
4089 
4090  // Then we check the variables in the auxiliary system
4091  face_lambda(elem_subdomain_id,
4092  neighbor_subdomain_id,
4094  face_type_vector);
4095  }
4096 }
4097 
4098 void
4100 {
4101  mooseAssert(!Threads::in_threads,
4102  "Performing writes to elemInfo dof indices. This must be done unthreaded!");
4103 
4104  auto elem_lambda = [](const ElemInfo & elem_info,
4105  SystemBase & sys,
4106  std::vector<std::vector<dof_id_type>> & dof_vector)
4107  {
4108  if (sys.nFVVariables())
4109  {
4110  dof_vector[sys.number()].resize(sys.nVariables(), libMesh::DofObject::invalid_id);
4111  const auto & variables = sys.getVariables(0);
4112 
4113  for (const auto & var : variables)
4114  if (var->isFV())
4115  {
4116  const auto & var_subdomains = var->blockIDs();
4117 
4118  // We will only cache for FV variables and if they live on the current subdomain
4119  if (var_subdomains.find(elem_info.subdomain_id()) != var_subdomains.end())
4120  {
4121  std::vector<dof_id_type> indices;
4122  var->dofMap().dof_indices(elem_info.elem(), indices, var->number());
4123  mooseAssert(indices.size() == 1, "We expect to have only one dof per element!");
4124  dof_vector[sys.number()][var->number()] = indices[0];
4125  }
4126  }
4127  }
4128  };
4129 
4130  const unsigned int num_eqs = _app.feProblem().es().n_systems();
4131 
4132  // We loop through the elements in the mesh and cache the dof indices
4133  // for the corresponding variables.
4134  for (auto & ei_pair : _elem_to_elem_info)
4135  {
4136  auto & elem_info = ei_pair.second;
4137  auto & dof_vector = elem_info.dofIndices();
4138 
4139  dof_vector.clear();
4140  dof_vector.resize(num_eqs);
4141 
4142  // First, we cache the dof indices for the variables in the solver systems (linear, nonlinear)
4143  for (const auto i : make_range(_app.feProblem().numSolverSystems()))
4144  elem_lambda(elem_info, _app.feProblem().getSolverSystem(i), dof_vector);
4145 
4146  // Then we cache the dof indices for the auxvariables
4147  elem_lambda(elem_info, _app.feProblem().getAuxiliarySystem(), dof_vector);
4148  }
4149 }
4150 
4151 void
4153 {
4158 }
4159 
4160 void
4161 MooseMesh::setCoordSystem(const std::vector<SubdomainName> & blocks,
4162  const MultiMooseEnum & coord_sys)
4163 {
4164  TIME_SECTION("setCoordSystem", 5, "Setting Coordinate System");
4166  {
4167  const std::string param_name = isParamValid("coord_block") ? "coord_block" : "block";
4168  mooseWarning("Supplied blocks in the 'setCoordSystem' method do not match the value of the "
4169  "'Mesh/",
4170  param_name,
4171  "' parameter. Did you provide different parameter values for 'Mesh/",
4172  param_name,
4173  "' and 'Problem/block'?. We will honor the parameter value from 'Mesh/",
4174  param_name,
4175  "'");
4176  mooseAssert(_coord_system_set,
4177  "If we are arriving here due to a bad specification in the Problem block, then we "
4178  "should have already set our coordinate system subdomains from the Mesh block");
4179  return;
4180  }
4181  if (_pars.isParamSetByUser("coord_type") && getParam<MultiMooseEnum>("coord_type") != coord_sys)
4182  mooseError("Supplied coordinate systems in the 'setCoordSystem' method do not match the value "
4183  "of the 'Mesh/coord_type' parameter. Did you provide different parameter values for "
4184  "'coord_type' to 'Mesh' and 'Problem'?");
4185 
4186  // If blocks contain ANY_BLOCK_ID, it should be the only block specified, and coord_sys should
4187  // have one and only one entry. In that case, the same coordinate system will be set for all
4188  // subdomains.
4189  if (blocks.size() == 1 && blocks[0] == "ANY_BLOCK_ID")
4190  {
4191  if (coord_sys.size() > 1)
4192  mooseError("If you specify ANY_BLOCK_ID as the only block, you must also specify a single "
4193  "coordinate system for it.");
4194  if (!_mesh->is_prepared())
4195  mooseError(
4196  "You cannot set the coordinate system for ANY_BLOCK_ID before the mesh is prepared. "
4197  "Please call this method after the mesh is prepared.");
4198  const auto coord_type = coord_sys.size() == 0
4200  : Moose::stringToEnum<Moose::CoordinateSystemType>(coord_sys[0]);
4201  for (const auto sid : meshSubdomains())
4202  _coord_sys[sid] = coord_type;
4203  return;
4204  }
4205 
4206  // If multiple blocks are specified, but one of them is ANY_BLOCK_ID, let's emit a helpful error
4207  if (std::find(blocks.begin(), blocks.end(), "ANY_BLOCK_ID") != blocks.end())
4208  mooseError("You cannot specify ANY_BLOCK_ID together with other blocks in the "
4209  "setCoordSystem() method. If you want to set the same coordinate system for all "
4210  "blocks, use ANY_BLOCK_ID as the only block.");
4211 
4212  auto subdomains = meshSubdomains();
4213  // It's possible that a user has called this API before the mesh is prepared and consequently we
4214  // don't yet have the subdomains in meshSubdomains()
4215  for (const auto & sub_name : blocks)
4216  {
4217  const auto sub_id = getSubdomainID(sub_name);
4218  subdomains.insert(sub_id);
4219  }
4220 
4221  if (coord_sys.size() <= 1)
4222  {
4223  // We will specify the same coordinate system for all blocks
4224  const auto coord_type = coord_sys.size() == 0
4226  : Moose::stringToEnum<Moose::CoordinateSystemType>(coord_sys[0]);
4227  for (const auto sid : subdomains)
4228  _coord_sys[sid] = coord_type;
4229  }
4230  else
4231  {
4232  if (blocks.size() != coord_sys.size())
4233  mooseError("Number of blocks and coordinate systems does not match.");
4234 
4235  for (const auto i : index_range(blocks))
4236  {
4237  SubdomainID sid = getSubdomainID(blocks[i]);
4238  Moose::CoordinateSystemType coord_type =
4239  Moose::stringToEnum<Moose::CoordinateSystemType>(coord_sys[i]);
4240  _coord_sys[sid] = coord_type;
4241  }
4242 
4243  for (const auto & sid : subdomains)
4244  if (_coord_sys.find(sid) == _coord_sys.end())
4245  mooseError("Subdomain '" + Moose::stringify(sid) +
4246  "' does not have a coordinate system specified.");
4247  }
4248 
4249  _coord_system_set = true;
4250 
4252 }
4253 
4256 {
4257  auto it = _coord_sys.find(sid);
4258  if (it != _coord_sys.end())
4259  return (*it).second;
4260  else
4261  mooseError("Requested subdomain ", sid, " does not exist.");
4262 }
4263 
4266 {
4267  const auto unique_system = _coord_sys.find(*meshSubdomains().begin())->second;
4268  // Check that it is actually unique
4269  bool result = std::all_of(
4270  std::next(_coord_sys.begin()),
4271  _coord_sys.end(),
4272  [unique_system](
4273  typename std::unordered_map<SubdomainID, Moose::CoordinateSystemType>::const_reference
4274  item) { return (item.second == unique_system); });
4275  if (!result)
4276  mooseError("The unique coordinate system of the mesh was requested by the mesh contains "
4277  "multiple blocks with different coordinate systems");
4278 
4280  mooseError("General axisymmetric coordinate axes are being used, and it is currently "
4281  "conservatively assumed that in this case there is no unique coordinate system.");
4282 
4283  return unique_system;
4284 }
4285 
4286 const std::map<SubdomainID, Moose::CoordinateSystemType> &
4288 {
4289  return _coord_sys;
4290 }
4291 
4292 void
4294 {
4295  _rz_coord_axis = rz_coord_axis;
4296 
4298 }
4299 
4300 void
4302  const std::vector<SubdomainName> & blocks,
4303  const std::vector<std::pair<Point, RealVectorValue>> & axes)
4304 {
4305  // Set the axes for the given blocks
4306  mooseAssert(blocks.size() == axes.size(), "Blocks and axes vectors must be the same length.");
4307  for (const auto i : index_range(blocks))
4308  {
4309  const auto subdomain_id = getSubdomainID(blocks[i]);
4310  const auto it = _coord_sys.find(subdomain_id);
4311  if (it == _coord_sys.end())
4312  mooseError("The block '",
4313  blocks[i],
4314  "' has not set a coordinate system. Make sure to call setCoordSystem() before "
4315  "setGeneralAxisymmetricCoordAxes().");
4316  else
4317  {
4318  if (it->second == Moose::COORD_RZ)
4319  {
4320  const auto direction = axes[i].second;
4321  if (direction.is_zero())
4322  mooseError("Only nonzero vectors may be supplied for RZ directions.");
4323 
4324  _subdomain_id_to_rz_coord_axis[subdomain_id] =
4325  std::make_pair(axes[i].first, direction.unit());
4326  }
4327  else
4328  mooseError("The block '",
4329  blocks[i],
4330  "' was provided in setGeneralAxisymmetricCoordAxes(), but the coordinate system "
4331  "for this block is not 'RZ'.");
4332  }
4333  }
4334 
4335  // Make sure there are no RZ blocks that still do not have axes
4336  const auto all_subdomain_ids = meshSubdomains();
4337  for (const auto subdomain_id : all_subdomain_ids)
4338  if (getCoordSystem(subdomain_id) == Moose::COORD_RZ &&
4339  !_subdomain_id_to_rz_coord_axis.count(subdomain_id))
4340  mooseError("The block '",
4341  getSubdomainName(subdomain_id),
4342  "' was specified to use the 'RZ' coordinate system but was not given in "
4343  "setGeneralAxisymmetricCoordAxes().");
4344 
4346 }
4347 
4348 const std::pair<Point, RealVectorValue> &
4350 {
4351  auto it = _subdomain_id_to_rz_coord_axis.find(subdomain_id);
4352  if (it != _subdomain_id_to_rz_coord_axis.end())
4353  return (*it).second;
4354  else
4355  mooseError("Requested subdomain ", subdomain_id, " does not exist.");
4356 }
4357 
4358 bool
4360 {
4361  return _subdomain_id_to_rz_coord_axis.size() > 0;
4362 }
4363 
4364 void
4366 {
4367  if (!_coord_transform)
4368  _coord_transform = std::make_unique<MooseAppCoordTransform>(*this);
4369  else
4370  _coord_transform->setCoordinateSystem(*this);
4371 }
4372 
4373 unsigned int
4375 {
4377  mooseError("getAxisymmetricRadialCoord() should not be called if "
4378  "setGeneralAxisymmetricCoordAxes() has been called.");
4379 
4380  if (_rz_coord_axis == 0)
4381  return 1; // if the rotation axis is x (0), then the radial direction is y (1)
4382  else
4383  return 0; // otherwise the radial direction is assumed to be x, i.e., the rotation axis is y
4384 }
4385 
4386 void
4388 {
4389  for (const auto & elem : getMesh().element_ptr_range())
4390  {
4391  SubdomainID sid = elem->subdomain_id();
4392  if (_coord_sys[sid] == Moose::COORD_RZ && elem->dim() == 3)
4393  mooseError("An RZ coordinate system was requested for subdomain " + Moose::stringify(sid) +
4394  " which contains 3D elements.");
4395  if (_coord_sys[sid] == Moose::COORD_RSPHERICAL && elem->dim() > 1)
4396  mooseError("An RSPHERICAL coordinate system was requested for subdomain " +
4397  Moose::stringify(sid) + " which contains 2D or 3D elements.");
4398  }
4399 }
4400 
4401 void
4403 {
4404  _coord_sys = other_mesh._coord_sys;
4405  _rz_coord_axis = other_mesh._rz_coord_axis;
4407 }
4408 
4409 const MooseUnits &
4411 {
4412  mooseAssert(_coord_transform, "This must be non-null");
4413  return _coord_transform->lengthUnit();
4414 }
4415 
4416 void
4418 {
4419  std::map<SubdomainName, SubdomainID> subdomain;
4420  for (const auto & sbd_id : _mesh_subdomains)
4421  {
4422  std::string sub_name = getSubdomainName(sbd_id);
4423  if (!sub_name.empty() && subdomain.count(sub_name) > 0)
4424  mooseError("The subdomain name ",
4425  sub_name,
4426  " is used for both subdomain with ID=",
4427  subdomain[sub_name],
4428  " and ID=",
4429  sbd_id,
4430  ", Please rename one of them!");
4431  else
4432  subdomain[sub_name] = sbd_id;
4433  }
4434 }
4435 
4436 const std::vector<QpMap> &
4438  const Elem & elem,
4439  const std::map<std::pair<ElemType, unsigned int>, std::vector<QpMap>> & map) const
4440 {
4441  // We are actually seeking the map stored with the p_level - 1 key, e.g. the refinement map that
4442  // maps from the previous p_level to this element's p_level
4443  return libmesh_map_find(map,
4444  std::make_pair(elem.type(), cast_int<unsigned int>(elem.p_level() - 1)));
4445 }
4446 
4447 const std::vector<QpMap> &
4449  const Elem & elem,
4450  const std::map<std::pair<ElemType, unsigned int>, std::vector<QpMap>> & map) const
4451 {
4452  mooseAssert(elem.active() && elem.p_refinement_flag() == Elem::JUST_COARSENED,
4453  "These are the conditions that should be met for requesting a coarsening map");
4454  return libmesh_map_find(map, std::make_pair(elem.type(), elem.p_level()));
4455 }
4456 
4457 const std::vector<QpMap> &
4459 {
4461 }
4462 
4463 const std::vector<QpMap> &
4465 {
4467 }
4468 
4469 const std::vector<QpMap> &
4471 {
4473 }
4474 
4475 const std::vector<QpMap> &
4477 {
4479 }
4480 
4481 bool
4483 {
4484  return _mesh->skip_noncritical_partitioning();
4485 }
ParallelType _parallel_type
Can be set to DISTRIBUTED, REPLICATED, or DEFAULT.
Definition: MooseMesh.h:1445
static InputParameters validParams()
Typical "Moose-style" constructor and copy constructor.
Definition: MooseMesh.C:82
virtual bnd_node_iterator bndNodesEnd()
Definition: MooseMesh.C:1603
virtual bnd_elem_iterator bndElemsEnd()
Definition: MooseMesh.C:1619
std::vector< std::vector< Real > > _bounds
The bounds in each dimension of the mesh for regular orthogonal meshes.
Definition: MooseMesh.h:1607
std::set< Node * > _semilocal_node_list
Used for generating the semilocal node range.
Definition: MooseMesh.h:1515
void remove_id(boundary_id_type id, bool global=false)
KOKKOS_INLINE_FUNCTION Real3 operator*(const Real left, const Real3 right)
Definition: KokkosTypes.h:249
std::map< dof_id_type, Node * > _quadrature_nodes
Definition: MooseMesh.h:1574
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:4476
virtual Real getMaxInDimension(unsigned int component) const
Definition: MooseMesh.C:2258
static const std::string & checkpointSuffix()
The file suffix for the checkpoint mesh.
Definition: MooseApp.C:3155
bool _node_to_elem_map_built
Definition: MooseMesh.h:1533
std::unique_ptr< libMesh::NodeRange > _active_node_range
Definition: MooseMesh.h:1524
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:1661
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:1660
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:3089
void computeMaxPerElemAndSide()
Compute the maximum numbers per element and side.
Definition: MooseMesh.C:1086
const std::set< BoundaryID > & meshNodesetIds() const
Returns a read-only reference to the set of nodesets currently present in the Mesh.
Definition: MooseMesh.C:3269
void buildElemIDInfo()
Build extra data for faster access to the information of extra element integers.
Definition: MooseMesh.C:1109
std::vector< FaceInfo > _all_face_info
FaceInfo object storing information for face based loops.
Definition: MooseMesh.h:1631
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:1635
bool allowRemoteElementRemoval() const
Whether we are allow remote element removal.
Definition: MooseMesh.h:1097
virtual Real getMinInDimension(unsigned int component) const
Returns the min or max of the requested dimension respectively.
Definition: MooseMesh.C:2249
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
libMesh::ConstElemRange * getActiveLocalElementRange()
Return pointers to range objects for various types of ranges (local nodes, boundary elems...
Definition: MooseMesh.C:1288
virtual void read(const std::string &name, void *mesh_data=nullptr, bool skip_renumber_nodes_and_elements=false, bool skip_find_neighbors=false)=0
virtual void onMeshChanged()
Declares a callback function that is executed at the conclusion of meshChanged(). ...
Definition: MooseMesh.C:935
bool prepared() const
Setter/getter for whether the mesh is prepared.
Definition: MooseMesh.C:3217
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:3245
const std::set< boundary_id_type > & get_side_boundary_ids() const
const std::set< BoundaryID > & getBoundaryIDs() const
Returns a const reference to a set of all user-specified boundary IDs.
Definition: MooseMesh.C:3029
bool _is_nemesis
True if a Nemesis Mesh was read in.
Definition: MooseMesh.h:1496
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:1917
virtual MooseMesh & clone() const
Clone method.
Definition: MooseMesh.C:2872
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:42
const Elem * parent() const
const std::string & getBoundaryName(BoundaryID boundary_id) const
Return the name of the boundary given the id.
Definition: MooseMesh.C:1842
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:4448
bool isCustomPartitionerRequested() const
Setter and getter for _custom_partitioner_requested.
Definition: MooseMesh.C:3785
bool _need_ghost_ghosted_boundaries
A parallel mesh generator such as DistributedRectilinearMeshGenerator already make everything ready...
Definition: MooseMesh.h:1868
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:820
unsigned int _uniform_refine_level
The level of uniform refinement requested (set to zero if AMR is disabled)
Definition: MooseMesh.h:1484
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:4437
unsigned int n_systems() const
std::vector< dof_id_type > _min_ids
Minimum integer ID for each extra element integer.
Definition: MooseMesh.h:1875
Helper class for sorting Boundary Nodes so that we always get the same order of application for bound...
Definition: MooseMesh.C:1028
const InputParameters & _pars
The object&#39;s parameters.
Definition: MooseBase.h:366
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:1381
auto norm() const -> decltype(std::norm(Real()))
std::string & nodeset_name(boundary_id_type id)
const MooseUnits & lengthUnit() const
Definition: MooseMesh.C:4410
void checkDuplicateSubdomainNames()
Loop through all subdomain IDs and check if there is name duplication used for the subdomains with sa...
Definition: MooseMesh.C:4417
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:3577
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:1658
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:1789
void setCoordData(const MooseMesh &other_mesh)
Set the coordinate system data to that of other_mesh.
Definition: MooseMesh.C:4402
static InputParameters validParams()
Describes the parameters this object can take to setup transformations.
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:439
virtual ~MooseMesh()
Definition: MooseMesh.C:390
void freeBndElems()
Definition: MooseMesh.C:417
const T & getParam(const std::string &name) const
Retrieve a parameter for the object.
Definition: MooseBase.h:388
bool _finite_volume_info_dirty
Definition: MooseMesh.h:1642
virtual Elem * elemPtr(const dof_id_type i)
Definition: MooseMesh.C:3193
void allow_renumbering(bool allow)
char ** blocks
The definition of the bnd_elem_iterator struct.
Definition: MooseMesh.h:2083
std::map< SubdomainID, Moose::CoordinateSystemType > & _coord_sys
Type of coordinate system per subdomain.
Definition: MooseMesh.h:1901
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:3645
face_info_iterator ownedFaceInfoBegin()
Iterators to owned faceInfo objects.
Definition: MooseMesh.C:1560
const std::map< dof_id_type, std::vector< dof_id_type > > & nodeToActiveSemilocalElemMap()
If not already created, creates a map from every node to all active semilocal elements to which they ...
Definition: MooseMesh.C:1256
std::vector< std::tuple< dof_id_type, unsigned short int, boundary_id_type > > buildActiveSideList() const
Calls BoundaryInfo::build_active_side_list.
Definition: MooseMesh.C:3095
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:3720
void skip_partitioning(bool skip)
void buildLowerDMesh()
Build lower-d mesh for all sides.
Definition: MooseMesh.C:685
const std::set< BoundaryID > & meshSidesetIds() const
Returns a read-only reference to the set of sidesets currently present in the Mesh.
Definition: MooseMesh.C:3263
virtual std::unique_ptr< Elem > build_side_ptr(const unsigned int i)=0
std::unordered_map< const Elem *, unsigned short int > _lower_d_elem_to_higher_d_elem_side
Definition: MooseMesh.h:1839
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:4099
static constexpr Real TOLERANCE
void cacheFaceInfoVariableOwnership() const
Cache if variables live on the elements connected by the FaceInfo objects.
Definition: MooseMesh.C:4021
bool _custom_partitioner_requested
Definition: MooseMesh.h:1468
unsigned int _max_nodes_per_side
The maximum number of nodes per side.
Definition: MooseMesh.h:1886
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:1367
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:4265
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)
std::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:1532
MooseMesh()=delete
unsigned int _max_sides_per_elem
The maximum number of sides per element.
Definition: MooseMesh.h:1880
bool isSemiLocal(Node *const node) const
Returns true if the node is semi-local.
Definition: MooseMesh.C:1019
unsigned int which_side_am_i(const Elem *e) const
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:1751
std::unique_ptr< libMesh::StoredRange< MooseMesh::const_bnd_elem_iterator, const BndElement * > > _bnd_elem_range
Definition: MooseMesh.h:1529
RealVectorValue minPeriodicVector(unsigned int nonlinear_var_num, Point p, Point q) const
This function returns the minimum vector between two points on the mesh taking into account periodici...
Definition: MooseMesh.C:2312
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:1572
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 (as opposed to h-refinement)
Definition: MooseMesh.h:1920
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:2878
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:940
bool isTranslatedPeriodic(unsigned int nonlinear_var_num, unsigned int component) const
Returns whether this generated mesh is periodic in the given dimension for the given variable...
Definition: MooseMesh.C:2301
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:41
const Point & getPoint(const PointObject &item) const
get a Point reference from the PointData object at index idx in the list
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:2040
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:1627
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:4359
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:1780
const std::set< SubdomainID > & getBlockConnectedBlocks(const SubdomainID subdomain_id) const
Get the list of subdomains neighboring a given subdomain.
Definition: MooseMesh.C:3634
virtual const Node * queryNodePtr(const dof_id_type i) const
Definition: MooseMesh.C:887
virtual std::unique_ptr< Partitioner > & partitioner()
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:1838
Helper object for holding qp mapping info.
Definition: MooseMesh.h:73
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:398
bool _has_lower_d
Whether there are any lower-dimensional blocks that are manifolds of higher-dimensional block faces...
Definition: MooseMesh.h:1843
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:3327
std::unique_ptr< ConstElemPointerRange > _refined_elements
The elements that were just refined.
Definition: MooseMesh.h:1502
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:1877
virtual dof_id_type maxElemId() const
Definition: MooseMesh.C:3173
static constexpr std::size_t dim
This is the dimension of all vector and tensor datastructures used in MOOSE.
Definition: Moose.h:162
const Parallel::Communicator & comm() const
virtual bnd_elem_iterator bndElemsBegin()
Return iterators to the beginning/end of the boundary elements list.
Definition: MooseMesh.C:1611
void setUniformRefineLevel(unsigned int, bool deletion=true)
Set uniform refinement level.
Definition: MooseMesh.C:3302
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:1463
std::unique_ptr< libMesh::ConstElemRange > _active_local_elem_range
A range for use with threading.
Definition: MooseMesh.h:1521
std::map< dof_id_type, std::set< SubdomainID > > _block_node_list
list of nodes that belongs to a specified block (domain)
Definition: MooseMesh.h:1580
bool _node_to_active_semilocal_elem_map_built
Definition: MooseMesh.h:1537
void build_side_boundary_ids(std::vector< boundary_id_type > &b_ids) const
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:1564
virtual void init()
Initialize the Mesh object.
Definition: MooseMesh.C:2925
ConstElemPointerRange * refinedElementRange() const
Return a range that is suitable for threaded execution over elements that were just refined...
Definition: MooseMesh.C:958
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:1762
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:1639
void detectPairedSidesets()
This routine detects paired sidesets of a regular orthogonal mesh (.i.e.
Definition: MooseMesh.C:2049
const std::set< SubdomainID > & getNodeBlockIds(const Node &node) const
Return list of blocks to which the given node belongs.
Definition: MooseMesh.C:1549
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:1811
void setMeshBoundaryIDs(std::set< BoundaryID > boundary_IDs)
Sets the set of BoundaryIDs Is called by AddAllSideSetsByNormals.
Definition: MooseMesh.C:3275
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:1583
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:1835
void cacheInfo()
Definition: MooseMesh.C:1457
std::basic_ostream< charT, traits > * os
Definition: InfixIterator.h:33
void build_active_side_list(std::vector< dof_id_type > &element_id_list, std::vector< unsigned short int > &side_list, std::vector< boundary_id_type > &bc_id_list) const
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:84
const BoundaryInfo & get_boundary_info() const
std::set< Elem * > _ghost_elems_from_ghost_boundaries
Set of elements ghosted by ghostGhostedBoundaries.
Definition: MooseMesh.h:1862
SECOND
virtual void buildMesh()=0
Must be overridden by child classes.
void setPartitionerHelper(MeshBase *mesh=nullptr)
Definition: MooseMesh.C:3708
void deleteRemoteElements()
Delete remote elements.
Definition: MooseMesh.C:4009
bool isSplitMesh() const
Whether or not this is a split mesh operation.
Definition: MooseApp.C:1896
unsigned int _to
The qp to map to.
Definition: MooseMesh.h:82
bool in_threads
BoundaryID _bnd_id
boundary id for the node
Definition: BndNode.h:26
libMesh::ConstNodeRange * getLocalNodeRange()
Definition: MooseMesh.C:1325
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:861
bool _allow_recovery
Whether or not this Mesh is allowed to read a recovery file.
Definition: MooseMesh.h:1846
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:1787
bool operator()(const BndNode *const &lhs, const BndNode *const &rhs)
Definition: MooseMesh.C:1033
void buildNodeListFromSideList()
Calls BoundaryInfo::build_node_list_from_side_list().
Definition: MooseMesh.C:3035
FEProblemBase & feProblem() const
Definition: MooseApp.C:2043
const std::string & getSubdomainName(SubdomainID subdomain_id) const
Return the name of a block given an id.
Definition: MooseMesh.C:1813
void setPatchUpdateStrategy(Moose::PatchUpdateType patch_update_strategy)
Set the patch size update strategy.
Definition: MooseMesh.C:3487
void buildFiniteVolumeInfo() const
Builds the face and elem info vectors that store meta-data needed for looping over and doing calculat...
Definition: MooseMesh.C:3819
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:1830
const std::pair< Point, RealVectorValue > & getGeneralAxisymmetricCoordAxis(SubdomainID subdomain_id) const
Gets the general axisymmetric coordinate axis for a block.
Definition: MooseMesh.C:4349
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:3257
std::set< SubdomainID > _lower_d_interior_blocks
Mesh blocks for interior lower-d elements in different types.
Definition: MooseMesh.h:1833
virtual Elem * queryElemPtr(const dof_id_type i)
Definition: MooseMesh.C:3205
elem_info_iterator ownedElemInfoBegin()
Iterators to owned faceInfo objects.
Definition: MooseMesh.C:1578
void build_side_list(std::vector< dof_id_type > &element_id_list, std::vector< unsigned short int > &side_list, std::vector< boundary_id_type > &bc_id_list) const
void setIsCustomPartitionerRequested(bool cpr)
Definition: MooseMesh.C:3807
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:1924
virtual bnd_node_iterator bndNodesBegin()
Return iterators to the beginning/end of the boundary nodes list.
Definition: MooseMesh.C:1595
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:3697
const MeshBase * getMeshPtr() const
Definition: MooseMesh.C:3522
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:460
This data structure is used to store geometric and variable related metadata about each cell face in ...
Definition: FaceInfo.h:36
const std::vector< const FaceInfo * > & faceInfo() const
Accessor for local FaceInfo objects.
Definition: MooseMesh.h:2216
void updateCoordTransform()
Update the coordinate transformation object based on our coordinate system data.
Definition: MooseMesh.C:4365
static const int GRAIN_SIZE
Definition: MooseMesh.C:65
bool _use_distributed_mesh
False by default.
Definition: MooseMesh.h:1450
virtual bool is_serial() const
void libmesh_ignore(const Args &...)
void build_node_list(std::vector< dof_id_type > &node_id_list, std::vector< boundary_id_type > &bc_id_list) const
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:1557
const dof_id_type n_nodes
const std::string & name() const
Get the name of the class.
Definition: MooseBase.h:103
virtual void find_neighbors(const bool reset_remote_elements=false, const bool reset_current_list=true)=0
bool _built_from_other_mesh
Whether or not this mesh was built from another mesh.
Definition: MooseMesh.h:1441
bool hasKokkosObjects() const
Get whether there is any Kokkos object added by actions.
Definition: MooseApp.h:1119
bool _allow_remote_element_removal
Whether to allow removal of remote elements.
Definition: MooseMesh.h:1859
std::unique_ptr< Moose::Kokkos::Mesh > _kokkos_mesh
Pointer to Kokkos mesh object.
Definition: MooseMesh.h:1459
virtual bool skipNoncriticalPartitioning() const
Definition: MooseMesh.C:4482
SemiLocalNodeRange * getActiveSemiLocalNodeRange() const
Definition: MooseMesh.C:1316
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:1392
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:1799
virtual void delete_elem(Elem *e)=0
void clearQuadratureNodes()
Clear out any existing quadrature nodes.
Definition: MooseMesh.C:1730
dof_id_type id() const
MeshBase & getMesh()
Accessor for the underlying libMesh Mesh object.
Definition: MooseMesh.C:3528
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:4287
static constexpr dof_id_type invalid_id
std::vector< BndNode * > _bnd_nodes
array of boundary nodes
Definition: MooseMesh.h:1560
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:2980
unsigned int _from
The qp to map from.
Definition: MooseMesh.h:79
std::pair< const Node *, BoundaryID > PeriodicNodeInfo
Helper type for building periodic node maps.
Definition: MooseMesh.h:1077
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:2906
unsigned int getAxisymmetricRadialCoord() const
Returns the desired radial direction for RZ coordinate transformation.
Definition: MooseMesh.C:4374
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:3321
bool _construct_node_list_from_side_list
Whether or not to allow generation of nodesets from sidesets.
Definition: MooseMesh.h:1849
boundary_id_type BoundaryID
void print_info(std::ostream &os=libMesh::out, const unsigned int verbosity=0, const bool global=true) const
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:3101
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:3550
virtual const Node * nodePtr(const dof_id_type i) const
Definition: MooseMesh.C:875
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:1873
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:92
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:641
void mooseDeprecated(Args &&... args) const
const std::string & type() const
Get the type of this class.
Definition: MooseBase.h:93
const std::pair< BoundaryID, BoundaryID > * getPairedBoundaryMapping(unsigned int component)
This function attempts to return the paired boundary ids for the given component. ...
Definition: MooseMesh.C:2343
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition: MooseEnum.h:33
bool isKokkosAvailable() const
Get whether Kokkos is available.
Definition: MooseApp.h:1101
std::set< BoundaryID > _mesh_nodeset_ids
Definition: MooseMesh.h:1553
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:1911
std::set< SubdomainID > getBoundaryConnectedBlocks(const BoundaryID bid) const
Get the list of subdomains associated with the given boundary.
Definition: MooseMesh.C:3601
void checkCoordinateSystems()
Performs a sanity check for every element in the mesh.
Definition: MooseMesh.C:4387
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:1785
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:1809
const bool _is_split
Whether or not we are using a (pre-)split mesh (automatically DistributedMesh)
Definition: MooseMesh.h:1613
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:4161
std::unique_ptr< ConstElemPointerRange > _coarsened_elements
The elements that were just coarsened.
Definition: MooseMesh.h:1505
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:1179
std::set< BoundaryID > _mesh_boundary_ids
A set of boundary IDs currently present in the mesh.
Definition: MooseMesh.h:1551
const Node * addUniqueNode(const Point &p, Real tol=1e-6)
Add a new node to the mesh.
Definition: MooseMesh.C:1626
std::vector< BndNode > _extra_bnd_nodes
Definition: MooseMesh.h:1577
MooseApp & _app
The MOOSE application this is associated with.
Definition: MooseBase.h:357
bool _moose_mesh_prepared
True if prepare has been called on the mesh.
Definition: MooseMesh.h:1499
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
auto norm(const T &a) -> decltype(std::abs(a))
The definition of the face_info_iterator struct.
Definition: MooseMesh.h:1952
unsigned int uniformRefineLevel() const
Returns the level of uniform refinement requested (zero if AMR is disabled).
Definition: MooseMesh.C:3296
std::vector< BndElement * > _bnd_elems
array of boundary elems
Definition: MooseMesh.h:1567
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:1914
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:1819
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:4458
AuxiliarySystem & getAuxiliarySystem()
Real minPeriodicDistance(unsigned int nonlinear_var_num, Point p, Point q) const
This function returns the distance between two points on the mesh taking into account periodicity for...
Definition: MooseMesh.C:2337
bool hasSecondOrderElements()
check if the mesh has SECOND order elements
Definition: MooseMesh.C:3791
unsigned int getPatchSize() const
Getter for the patch_size parameter.
Definition: MooseMesh.C:3481
void buildRefinementAndCoarseningMaps(Assembly *assembly)
Create the refinement and coarsening maps necessary for projection of stateful material properties wh...
Definition: MooseMesh.C:2512
bool _parallel_type_overridden
Definition: MooseMesh.h:1452
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:3131
std::vector< Node * > _node_map
Vector of all the Nodes in the mesh for determining when to add a new point.
Definition: MooseMesh.h:1601
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:1576
Executioner * getExecutioner() const
Retrieve the Executioner for this App.
Definition: MooseApp.C:2206
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:4464
bool _displace_node_list_by_side_list
Whether or not to displace unrelated nodesets by nodesets constructed from sidesets.
Definition: MooseMesh.h:1853
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:1623
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:1712
void printInfo(std::ostream &os=libMesh::out, const unsigned int verbosity=0) const
Calls print_info() on the underlying Mesh.
Definition: MooseMesh.C:3542
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:3155
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:3978
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:970
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:1150
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:1806
void setBoundaryName(BoundaryID boundary_id, BoundaryName name)
This method sets the boundary name of the boundary based on the id parameter.
Definition: MooseMesh.C:1830
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:1598
infix_ostream_iterator< T, charT, traits > & operator=(T const &item)
Definition: InfixIterator.h:46
Physical unit management class with runtime unit string parsing, unit checking, unit conversion...
Definition: Units.h:32
void addPeriodicVariable(unsigned int var_num, BoundaryID primary, BoundaryID secondary)
For "regular orthogonal" meshes, determine if variable var_num is periodic with respect to the primar...
Definition: MooseMesh.C:2267
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:1552
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:4301
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:3281
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
bool _partitioner_overridden
Definition: MooseMesh.h:1464
bool detectOrthogonalDimRanges(Real tol=1e-6)
This routine determines whether the Mesh is a regular orthogonal mesh (i.e.
Definition: MooseMesh.C:1975
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:978
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.
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:1189
unsigned int getBlocksMaxDimension(const std::vector< SubdomainName > &blocks) const
Returns the maximum element dimension on the given blocks.
Definition: MooseMesh.C:3001
std::unique_ptr< libMesh::MeshBase > _mesh
Pointer to underlying libMesh mesh object.
Definition: MooseMesh.h:1455
void max(const T &r, T &o, Request &req) const
libMesh::NodeRange * getActiveNodeRange()
Definition: MooseMesh.C:1302
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:3315
void freeBndNodes()
Definition: MooseMesh.C:398
The definition of the elem_info_iterator struct.
Definition: MooseMesh.h:1996
Real dimensionWidth(unsigned int component) const
Returns the width of the requested dimension.
Definition: MooseMesh.C:2243
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:1490
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:3612
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:1883
bool _need_delete
Whether we need to delete remote elements after init&#39;ing the EquationSystems.
Definition: MooseMesh.h:1856
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:1783
void setAxisymmetricCoordAxis(const MooseEnum &rz_coord_axis)
For axisymmetric simulations, set the symmetry coordinate axis.
Definition: MooseMesh.C:4293
std::set< BoundaryID > getSubdomainInterfaceBoundaryIds(const SubdomainID subdomain_id) const
Get the list of boundaries that contact the given subdomain.
Definition: MooseMesh.C:3588
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:847
infix_ostream_iterator< T, charT, traits > & operator++()
Definition: InfixIterator.h:57
libMesh::BoundingBox getInflatedProcessorBoundingBox(Real inflation_multiplier=0.01) const
Get a (slightly inflated) processor bounding box.
Definition: MooseMesh.C:3499
std::map< unsigned int, std::vector< bool > > _periodic_dim
A map of vectors indicating which dimensions are periodic in a regular orthogonal mesh for the specif...
Definition: MooseMesh.h:1653
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:2918
void buildBndElemList()
Definition: MooseMesh.C:1205
std::set< SubdomainID > getInterfaceConnectedBlocks(const BoundaryID bid) const
Get the list of subdomains contacting the given boundary.
Definition: MooseMesh.C:3623
SolverSystem & getSolverSystem(unsigned int sys_num)
Get non-constant reference to a solver system.
std::vector< Real > _ghosted_boundaries_inflation
Definition: MooseMesh.h:1586
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:1871
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:1589
elem_info_iterator ownedElemInfoEnd()
Definition: MooseMesh.C:1586
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:1948
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:3813
void addGhostedBoundary(BoundaryID boundary_id)
This will add the boundary ids to be ghosted to this processor.
Definition: MooseMesh.C:3309
virtual dof_id_type maxNodeId() const
Calls max_node/elem_id() on the underlying libMesh mesh object.
Definition: MooseMesh.C:3167
virtual Elem * elem(const dof_id_type i)
Various accessors (pointers/references) for Elem "i".
Definition: MooseMesh.C:3179
libMesh::StoredRange< MooseMesh::const_bnd_elem_iterator, const BndElement * > * getBoundaryElementRange()
Definition: MooseMesh.C:1353
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:1569
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:1907
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:3113
const RealVectorValue & getNormalByBoundaryID(BoundaryID id) const
Returns the normal vector associated with a given BoundaryID.
Definition: MooseMesh.C:2862
std::set< SubdomainID > _mesh_subdomains
A set of subdomain IDs currently present in the mesh.
Definition: MooseMesh.h:1543
ConstElemPointerRange * coarsenedElementRange() const
Return a range that is suitable for threaded execution over elements that were just coarsened...
Definition: MooseMesh.C:964
static InputParameters validParams()
Definition: MooseObject.C:25
const Moose::PatchUpdateType & getPatchUpdateStrategy() const
Get the current patch update strategy.
Definition: MooseMesh.C:3493
bool doingPRefinement() const
Query whether we have p-refinement.
Definition: MooseMesh.h:1370
std::vector< std::pair< BoundaryID, BoundaryID > > _paired_boundary
A vector holding the paired boundaries for a regular orthogonal mesh.
Definition: MooseMesh.h:1610
const Point & max() const
void ghostGhostedBoundaries()
Actually do the ghosting of boundaries that need to be ghosted to this processor. ...
Definition: MooseMesh.C:3394
std::set< unsigned int > _ghosted_boundaries
Definition: MooseMesh.h:1585
MeshBase::node_iterator localNodesBegin()
Calls local_nodes_begin/end() on the underlying libMesh mesh object.
Definition: MooseMesh.C:3107
unsigned int _rz_coord_axis
Storage for RZ axis selection.
Definition: MooseMesh.h:1904
void computeFiniteVolumeCoords() const
Compute the face coordinate value for all FaceInfo and ElemInfo objects.
Definition: MooseMesh.C:3957
void buildNodeList()
Calls BoundaryInfo::build_node_list()/build_side_list() and makes separate copies of Nodes/Elems in t...
Definition: MooseMesh.C:1052
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:1827
std::unique_ptr< libMesh::Partitioner > _custom_partitioner
The custom partitioner.
Definition: MooseMesh.h:1467
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:3671
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:1512
processor_id_type processor_id() const
static constexpr subdomain_id_type invalid_subdomain_id
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:4470
bool isRecovering() const
Whether or not this is a "recover" calculation.
Definition: MooseApp.C:1884
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:1863
processor_id_type processor_id() const
std::string getRestartRecoverFileBase() const
The file_base for the recovery file.
Definition: MooseApp.h:494
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:1375
const MeshBase::element_iterator activeLocalElementsEnd()
Definition: MooseMesh.C:3137
bool relative_fuzzy_equals(const TypeVector< Real > &rhs, Real tol=TOLERANCE) const
std::unique_ptr< libMesh::ConstNodeRange > _local_node_range
Definition: MooseMesh.h:1525
libMesh::StoredRange< MooseMesh::const_bnd_node_iterator, const BndNode * > * getBoundaryNodeRange()
Definition: MooseMesh.C:1339
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:1604
std::map< dof_id_type, std::vector< dof_id_type > > _node_to_active_semilocal_elem_map
A map of all of the current nodes to the active elements that they are connected to.
Definition: MooseMesh.h:1536
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:431
const ElemInfo & elemInfo(const dof_id_type id) const
Accessor for the elemInfo object for a given element ID.
Definition: MooseMesh.C:3951
void setCustomPartitioner(libMesh::Partitioner *partitioner)
Setter for custom partitioner.
Definition: MooseMesh.C:3779
Real _distance
The distance between them.
Definition: MooseMesh.h:85
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:3986
void meshChanged()
Declares that the MooseMesh has changed, invalidates cached data and rebuilds caches.
Definition: MooseMesh.C:909
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:1742
std::unique_ptr< libMesh::StoredRange< MooseMesh::const_bnd_node_iterator, const BndNode * > > _bnd_node_range
Definition: MooseMesh.h:1527
uint8_t dof_id_type
virtual dof_id_type nElem() const
Definition: MooseMesh.C:3161
const std::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:1228
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:1423
std::unique_ptr< SemiLocalNodeRange > _active_semilocal_node_range
Definition: MooseMesh.h:1523
const std::set< SubdomainID > & meshSubdomains() const
Returns a read-only reference to the set of subdomains currently present in the Mesh.
Definition: MooseMesh.C:3251
SubdomainID getSubdomainID(const SubdomainName &subdomain_name) const
Get the associated subdomain ID for the subdomain name.
Definition: MooseMesh.C:1781
unsigned int _max_p_level
Maximum p-refinement level of all elements.
Definition: MooseMesh.h:1922
void setupFiniteVolumeMeshData() const
Sets up the additional data needed for finite volume computations.
Definition: MooseMesh.C:4152
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:2986