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