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