libMesh
mesh_base.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 
19 
20 // library configuration
21 #include "libmesh/libmesh_config.h"
22 
23 // Local includes
24 #include "libmesh/boundary_info.h"
25 #include "libmesh/libmesh_logging.h"
26 #include "libmesh/elem.h"
27 #include "libmesh/ghost_point_neighbors.h"
28 #include "libmesh/mesh_base.h"
29 #include "libmesh/mesh_communication.h"
30 #include "libmesh/mesh_serializer.h"
31 #include "libmesh/mesh_tools.h"
32 #include "libmesh/parallel.h"
33 #include "libmesh/parallel_algebra.h"
34 #include "libmesh/parallel_fe_type.h"
35 #include "libmesh/partitioner.h"
36 #include "libmesh/point_locator_base.h"
37 #include "libmesh/sparse_matrix.h"
38 #include "libmesh/threads.h"
39 #include "libmesh/enum_elem_type.h"
40 #include "libmesh/enum_point_locator_type.h"
41 #include "libmesh/enum_to_string.h"
42 #include "libmesh/point_locator_nanoflann.h"
43 #include "libmesh/elem_side_builder.h"
44 #include "libmesh/elem_range.h"
45 #include "libmesh/node_range.h"
46 
47 // C++ includes
48 #include <algorithm> // for std::min
49 #include <map> // for std::multimap
50 #include <memory>
51 #include <sstream> // for std::ostringstream
52 #include <unordered_map>
53 
54 #include "libmesh/periodic_boundaries.h"
55 #include "libmesh/periodic_boundary.h"
56 
57 namespace libMesh
58 {
59 
60 
61 
62 // ------------------------------------------------------------
63 // MeshBase class member functions
65  unsigned char d) :
66  ParallelObject (comm_in),
67  boundary_info (new BoundaryInfo(*this)), // BoundaryInfo has protected ctor, can't use std::make_unique
68  _n_parts (1),
69  _default_mapping_type(LAGRANGE_MAP),
70  _default_mapping_data(0),
71  _preparation (),
72  _element_stored_range (),
73  _const_active_local_element_stored_range (),
74  _point_locator (),
75  _count_lower_dim_elems_in_point_locator(true),
76  _partitioner (),
77 #ifdef LIBMESH_ENABLE_UNIQUE_ID
78  _next_unique_id(DofObject::invalid_unique_id),
79 #endif
80  _interior_mesh(this),
81  _skip_noncritical_partitioning(false),
82  _skip_all_partitioning(libMesh::on_command_line("--skip-partitioning")),
83  _skip_renumber_nodes_and_elements(false),
84  _skip_find_neighbors(false),
85  _skip_detect_interior_parents(false),
86  _allow_remote_element_removal(true),
87  _allow_node_and_elem_unique_id_overlap(false),
88  _spatial_dimension(d),
89  _default_ghosting(std::make_unique<GhostPointNeighbors>(*this)),
90  _point_locator_close_to_point_tol(0.)
91 {
92  _elem_dims.insert(d);
93  _ghosting_functors.push_back(_default_ghosting.get());
94  libmesh_assert_less_equal (LIBMESH_DIM, 3);
95  libmesh_assert_greater_equal (LIBMESH_DIM, d);
97 }
98 
99 
100 
101 MeshBase::MeshBase (const MeshBase & other_mesh) :
102  ParallelObject (other_mesh),
103  boundary_info (new BoundaryInfo(*this)), // BoundaryInfo has protected ctor, can't use std::make_unique
104  _n_parts (other_mesh._n_parts),
105  _default_mapping_type(other_mesh._default_mapping_type),
106  _default_mapping_data(other_mesh._default_mapping_data),
107  _preparation (other_mesh._preparation),
108  _element_stored_range (),
109  _const_active_local_element_stored_range (),
110  _point_locator (),
111  _count_lower_dim_elems_in_point_locator(other_mesh._count_lower_dim_elems_in_point_locator),
112  _partitioner (),
113 #ifdef LIBMESH_ENABLE_UNIQUE_ID
114  _next_unique_id(other_mesh._next_unique_id),
115 #endif
116  // If the other mesh interior_parent pointers just go back to
117  // itself, so should we
118  _interior_mesh((other_mesh._interior_mesh == &other_mesh) ?
119  this : other_mesh._interior_mesh),
120  _skip_noncritical_partitioning(other_mesh._skip_noncritical_partitioning),
121  _skip_all_partitioning(other_mesh._skip_all_partitioning),
122  _skip_renumber_nodes_and_elements(other_mesh._skip_renumber_nodes_and_elements),
123  _skip_find_neighbors(other_mesh._skip_find_neighbors),
124  _skip_detect_interior_parents(other_mesh._skip_detect_interior_parents),
125  _allow_remote_element_removal(other_mesh._allow_remote_element_removal),
126  _allow_node_and_elem_unique_id_overlap(other_mesh._allow_node_and_elem_unique_id_overlap),
127  _elem_dims(other_mesh._elem_dims),
128  _elem_default_orders(other_mesh._elem_default_orders),
129  _supported_nodal_order(other_mesh._supported_nodal_order),
130  _mesh_subdomains(other_mesh._mesh_subdomains),
131  _elemset_codes_inverse_map(other_mesh._elemset_codes_inverse_map),
132  _all_elemset_ids(other_mesh._all_elemset_ids),
133  _spatial_dimension(other_mesh._spatial_dimension),
134  _default_ghosting(std::make_unique<GhostPointNeighbors>(*this)),
135  _point_locator_close_to_point_tol(other_mesh._point_locator_close_to_point_tol)
136 {
137  const GhostingFunctor * const other_default_ghosting = other_mesh._default_ghosting.get();
138 
139  for (GhostingFunctor * const gf : other_mesh._ghosting_functors)
140  {
141  // If the other mesh is using default ghosting, then we will use our own
142  // default ghosting
143  if (gf == other_default_ghosting)
144  {
145  _ghosting_functors.push_back(_default_ghosting.get());
146  continue;
147  }
148 
149  std::shared_ptr<GhostingFunctor> clone_gf = gf->clone();
150  // Some subclasses of GhostingFunctor might not override the
151  // clone function yet. If this is the case, GhostingFunctor will
152  // return nullptr by default. The clone function should be overridden
153  // in all derived classes. This following code ("else") is written
154  // for API upgrade. That will allow users gradually to update their code.
155  // Once the API upgrade is done, we will come back and delete "else."
156  if (clone_gf)
157  {
158  clone_gf->set_mesh(this);
159  add_ghosting_functor(clone_gf);
160  }
161  else
162  {
163  libmesh_deprecated();
165  }
166  }
167 
168  if (other_mesh._partitioner.get())
169  _partitioner = other_mesh._partitioner->clone();
170 
171 #ifdef LIBMESH_ENABLE_PERIODIC
172  // Deep copy of all periodic boundaries
173  if (other_mesh._disjoint_neighbor_boundary_pairs)
174  {
175  _disjoint_neighbor_boundary_pairs = std::make_unique<PeriodicBoundaries>();
176 
177  for (const auto & [id, pb] : *other_mesh._disjoint_neighbor_boundary_pairs)
178  if (pb)
179  (*_disjoint_neighbor_boundary_pairs)[id] = pb->clone();
180  }
181 #endif
182 
183  // _elemset_codes stores pointers to entries in _elemset_codes_inverse_map,
184  // so it is not possible to simply copy it directly from other_mesh
185  for (const auto & [set, code] : _elemset_codes_inverse_map)
186  _elemset_codes.emplace(code, &set);
187 }
188 
190 {
191  LOG_SCOPE("operator=(&&)", "MeshBase");
192 
193  // Move assign as a ParallelObject.
194  this->ParallelObject::operator=(other_mesh);
195 
196  _n_parts = other_mesh.n_partitions();
197  _default_mapping_type = other_mesh.default_mapping_type();
198  _default_mapping_data = other_mesh.default_mapping_data();
199  _preparation = other_mesh._preparation;
200  _element_stored_range = std::move(other_mesh._element_stored_range);
201  _const_active_local_element_stored_range = std::move(other_mesh._const_active_local_element_stored_range);
202  _point_locator = std::move(other_mesh._point_locator);
203  _count_lower_dim_elems_in_point_locator = other_mesh.get_count_lower_dim_elems_in_point_locator();
204 #ifdef LIBMESH_ENABLE_UNIQUE_ID
205  _next_unique_id = other_mesh.next_unique_id();
206 #endif
207  // If the other mesh interior_parent pointers just go back to
208  // itself, so should we
209  _interior_mesh = (other_mesh._interior_mesh == &other_mesh) ?
210  this : other_mesh._interior_mesh;
211  _skip_noncritical_partitioning = other_mesh.skip_noncritical_partitioning();
212  _skip_all_partitioning = other_mesh.skip_partitioning();
213  _skip_renumber_nodes_and_elements = !(other_mesh.allow_renumbering());
214  _skip_find_neighbors = !(other_mesh.allow_find_neighbors());
215  _skip_detect_interior_parents = !(other_mesh.allow_detect_interior_parents());
216  _allow_remote_element_removal = other_mesh.allow_remote_element_removal();
217  _allow_node_and_elem_unique_id_overlap = other_mesh.allow_node_and_elem_unique_id_overlap();
218  _block_id_to_name = std::move(other_mesh._block_id_to_name);
219  _elem_dims = std::move(other_mesh.elem_dimensions());
220  _elem_default_orders = std::move(other_mesh.elem_default_orders());
221  _supported_nodal_order = other_mesh.supported_nodal_order();
222  _mesh_subdomains = other_mesh._mesh_subdomains;
223  _elemset_codes = std::move(other_mesh._elemset_codes);
224  _elemset_codes_inverse_map = std::move(other_mesh._elemset_codes_inverse_map);
225  _all_elemset_ids = std::move(other_mesh._all_elemset_ids);
226  _spatial_dimension = other_mesh.spatial_dimension();
227  _elem_integer_names = std::move(other_mesh._elem_integer_names);
228  _elem_integer_default_values = std::move(other_mesh._elem_integer_default_values);
229  _node_integer_names = std::move(other_mesh._node_integer_names);
230  _node_integer_default_values = std::move(other_mesh._node_integer_default_values);
231  _point_locator_close_to_point_tol = other_mesh.get_point_locator_close_to_point_tol();
232 
233 #ifdef LIBMESH_ENABLE_PERIODIC
234  // Deep copy of all periodic boundaries:
235  // We must clone each PeriodicBoundaryBase in the source map,
236  // since unique_ptr cannot be copied and we need independent instances
237  if (other_mesh._disjoint_neighbor_boundary_pairs)
238  {
239  _disjoint_neighbor_boundary_pairs = std::make_unique<PeriodicBoundaries>();
240 
241  for (const auto & [id, pb] : *other_mesh._disjoint_neighbor_boundary_pairs)
242  if (pb)
243  (*_disjoint_neighbor_boundary_pairs)[id] = pb->clone();
244  }
245 #endif
246 
247  // This relies on our subclasses *not* invalidating pointers when we
248  // do their portion of the move assignment later!
249  boundary_info = std::move(other_mesh.boundary_info);
250  boundary_info->set_mesh(*this);
251 
252 #ifdef DEBUG
253  // Make sure that move assignment worked for pointers
254  for (const auto & [set, code] : _elemset_codes_inverse_map)
255  {
256  auto it = _elemset_codes.find(code);
257  libmesh_assert_msg(it != _elemset_codes.end(),
258  "Elemset code " << code << " not found in _elmset_codes container.");
259  libmesh_assert_equal_to(it->second, &set);
260  }
261 #endif
262 
263  // We're *not* really done at this point, but we have the problem
264  // that some of our data movement might be expecting subclasses data
265  // movement to happen first. We'll let subclasses handle that by
266  // calling our post_dofobject_moves()
267  return *this;
268 }
269 
270 
271 bool MeshBase::operator== (const MeshBase & other_mesh) const
272 {
273  LOG_SCOPE("operator==()", "MeshBase");
274 
275  bool is_equal = this->locally_equals(other_mesh);
276  this->comm().min(is_equal);
277  return is_equal;
278 }
279 
280 
281 bool MeshBase::locally_equals (const MeshBase & other_mesh) const
282 {
283  // Check whether (almost) everything in the base is equal
284  //
285  // We don't check _next_unique_id here, because it's expected to
286  // change in a DistributedMesh prepare_for_use(); it's conceptually
287  // "mutable".
288  //
289  // We use separate if statements instead of logical operators here,
290  // to make it easy to see the failing condition when using a
291  // debugger to figure out why a MeshTools::valid_is_prepared(mesh)
292  // is failing.
293  if (_n_parts != other_mesh._n_parts)
294  return false;
296  return false;
298  return false;
299  if (_preparation != other_mesh._preparation)
300  return false;
303  return false;
304 
305  // We should either both have our own interior parents or both not;
306  // but if we both don't then we can't really assert anything else
307  // because pointing at the same interior mesh is fair but so is
308  // pointing at two different copies of "the same" interior mesh.
309  if ((_interior_mesh == this) !=
310  (other_mesh._interior_mesh == &other_mesh))
311  return false;
312 
314  return false;
316  return false;
318  return false;
319  if (_skip_find_neighbors != other_mesh._skip_find_neighbors)
320  return false;
322  return false;
324  return false;
326  return false;
327  if (_spatial_dimension != other_mesh._spatial_dimension)
328  return false;
330  return false;
331  if (_block_id_to_name != other_mesh._block_id_to_name)
332  return false;
333  if (_elem_dims != other_mesh._elem_dims)
334  return false;
335  if (_elem_default_orders != other_mesh._elem_default_orders)
336  return false;
338  return false;
339  if (_mesh_subdomains != other_mesh._mesh_subdomains)
340  return false;
341  if (_all_elemset_ids != other_mesh._all_elemset_ids)
342  return false;
343  if (_elem_integer_names != other_mesh._elem_integer_names)
344  return false;
346  return false;
347  if (_node_integer_names != other_mesh._node_integer_names)
348  return false;
350  return false;
351  if (static_cast<bool>(_default_ghosting) != static_cast<bool>(other_mesh._default_ghosting))
352  return false;
353  if (static_cast<bool>(_partitioner) != static_cast<bool>(other_mesh._partitioner))
354  return false;
355  if (*boundary_info != *other_mesh.boundary_info)
356  return false;
357 
358  // First check whether the "existence" of the two pointers differs (one present, one absent)
359  if (static_cast<bool>(_disjoint_neighbor_boundary_pairs) !=
360  static_cast<bool>(other_mesh._disjoint_neighbor_boundary_pairs))
361  return false;
362  // If both exist, compare the contents (Weak Test: just compare sizes like `_ghosting_functors`)
365  return false;
366 
367  const constraint_rows_type & other_rows =
368  other_mesh.get_constraint_rows();
369  for (const auto & [node, row] : this->_constraint_rows)
370  {
371  const dof_id_type node_id = node->id();
372  const Node * other_node = other_mesh.query_node_ptr(node_id);
373  if (!other_node)
374  return false;
375 
376  auto it = other_rows.find(other_node);
377  if (it == other_rows.end())
378  return false;
379 
380  const auto & other_row = it->second;
381  if (row.size() != other_row.size())
382  return false;
383 
384  for (auto i : index_range(row))
385  {
386  const auto & [elem_pair, coef] = row[i];
387  const auto & [other_elem_pair, other_coef] = other_row[i];
388  libmesh_assert(elem_pair.first);
389  libmesh_assert(other_elem_pair.first);
390  if (elem_pair.first->id() !=
391  other_elem_pair.first->id() ||
392  elem_pair.second !=
393  other_elem_pair.second ||
394  coef != other_coef)
395  return false;
396  }
397  }
398 
399  for (const auto & [elemset_code, elemset_ptr] : this->_elemset_codes)
400  if (const auto it = other_mesh._elemset_codes.find(elemset_code);
401  it == other_mesh._elemset_codes.end() || *elemset_ptr != *it->second)
402  return false;
403 
404  // FIXME: we have no good way to compare ghosting functors, since
405  // they're in a vector of pointers, and we have no way *at all*
406  // to compare ghosting functors, since they don't have operator==
407  // defined and we encourage users to subclass them. We can check if
408  // we have the same number, is all.
409  if (_ghosting_functors.size() !=
410  other_mesh._ghosting_functors.size())
411  return false;
412 
413  // Same deal for partitioners. We tested that we both have one or
414  // both don't, but are they equivalent? Let's guess "yes".
415 
416  // Now let the subclasses decide whether everything else is equal
417  return this->subclass_locally_equals(other_mesh);
418 }
419 
420 
422 {
423  this->MeshBase::clear();
424 
425  libmesh_exceptionless_assert (!libMesh::closed());
426 }
427 
428 
429 
430 unsigned int MeshBase::mesh_dimension() const
431 {
432  if (!_elem_dims.empty())
433  return cast_int<unsigned int>(*_elem_dims.rbegin());
434  return 0;
435 }
436 
437 
438 
439 void MeshBase::set_elem_dimensions(std::set<unsigned char> elem_dims)
440 {
441 #ifdef DEBUG
442  // In debug mode, we call cache_elem_data() and then make sure
443  // the result actually agrees with what the user specified.
444  parallel_object_only();
445 
446  this->cache_elem_data();
447  libmesh_assert_msg(_elem_dims == elem_dims, \
448  "Specified element dimensions does not match true element dimensions!");
449 #endif
450 
451  _elem_dims = std::move(elem_dims);
452 }
453 
454 
455 
457 {
458  // Populate inverse map, stealing id_set's resources
459  auto [it1, inserted1] = _elemset_codes_inverse_map.emplace(std::move(id_set), code);
460 
461  // Reference to the newly inserted (or previously existing) id_set
462  const auto & inserted_id_set = it1->first;
463 
464  // Keep track of all elemset ids ever added for O(1) n_elemsets()
465  // performance. Only need to do this if we didn't know about this
466  // id_set before...
467  if (inserted1)
468  _all_elemset_ids.insert(inserted_id_set.begin(), inserted_id_set.end());
469 
470  // Take the address of the newly emplaced set to use in
471  // _elemset_codes, avoid duplicating std::set storage
472  auto [it2, inserted2] = _elemset_codes.emplace(code, &inserted_id_set);
473 
474  // Throw an error if this code already exists with a pointer to a
475  // different set of ids.
476  libmesh_error_msg_if(!inserted2 && it2->second != &inserted_id_set,
477  "The elemset code " << code << " already exists with a different id_set.");
478 }
479 
480 
481 
482 unsigned int MeshBase::n_elemsets() const
483 {
484  return _all_elemset_ids.size();
485 }
486 
487 void MeshBase::get_elemsets(dof_id_type elemset_code, MeshBase::elemset_type & id_set_to_fill) const
488 {
489  // If we don't recognize this elemset_code, hand back an empty set
490  id_set_to_fill.clear();
491 
492  if (const auto it = _elemset_codes.find(elemset_code);
493  it != _elemset_codes.end())
494  id_set_to_fill.insert(it->second->begin(), it->second->end());
495 }
496 
498 {
499  auto it = _elemset_codes_inverse_map.find(id_set);
500  return (it == _elemset_codes_inverse_map.end()) ? DofObject::invalid_id : it->second;
501 }
502 
503 std::vector<dof_id_type> MeshBase::get_elemset_codes() const
504 {
505  std::vector<dof_id_type> ret;
506  ret.reserve(_elemset_codes.size());
507  for (const auto & pr : _elemset_codes)
508  ret.push_back(pr.first);
509  return ret;
510 }
511 
513 {
514  // Look up elemset ids for old_code
515  auto it = _elemset_codes.find(old_code);
516 
517  // If we don't have the old_code, then do nothing. Alternatively, we
518  // could throw an error since trying to change an elemset code you
519  // don't have could indicate there's a problem...
520  if (it == _elemset_codes.end())
521  return;
522 
523  // Make copy of the set of elemset ids. We are not changing these,
524  // only updating the elemset code it corresponds to.
525  elemset_type id_set_copy = *(it->second);
526 
527  // Look up the corresponding entry in the inverse map. Note: we want
528  // the iterator because we are going to remove it.
529  auto inverse_it = _elemset_codes_inverse_map.find(id_set_copy);
530  libmesh_error_msg_if(inverse_it == _elemset_codes_inverse_map.end(),
531  "Expected _elemset_codes_inverse_map entry for elemset code " << old_code);
532 
533  // Erase entry from inverse map
534  _elemset_codes_inverse_map.erase(inverse_it);
535 
536  // Erase entry from forward map
537  _elemset_codes.erase(it);
538 
539  // Add new code with original set of ids.
540  this->add_elemset_code(new_code, id_set_copy);
541 
542  // We can't update any actual elemset codes if there is no extra integer defined for it.
543  if (!this->has_elem_integer("elemset_code"))
544  return;
545 
546  // Get index of elemset_code extra integer
547  unsigned int elemset_index = this->get_elem_integer_index("elemset_code");
548 
549  // Loop over all elems and update code
551  (this->element_stored_range(),
552  [elemset_index, old_code, new_code](const ElemRange & range)
553  {
554  for (Elem * elem : range)
555  {
556  dof_id_type elemset_code =
557  elem->get_extra_integer(elemset_index);
558 
559  if (elemset_code == old_code)
560  elem->set_extra_integer(elemset_index, new_code);
561  }
562  });
563 }
564 
566 {
567  // Early return if we don't have old_id
568  if (!_all_elemset_ids.count(old_id))
569  return;
570 
571  // Throw an error if the new_id is already used
572  libmesh_error_msg_if(_all_elemset_ids.count(new_id),
573  "Cannot change elemset id " << old_id <<
574  " to " << new_id << ", " << new_id << " already exists.");
575 
576  // We will build up a new version of the inverse map so we can iterate over
577  // the current one without invalidating anything.
578  std::map<MeshBase::elemset_type, dof_id_type> new_elemset_codes_inverse_map;
579  for (const auto & [id_set, elemset_code] : _elemset_codes_inverse_map)
580  {
581  auto id_set_copy = id_set;
582  if (id_set_copy.count(old_id))
583  {
584  // Remove old_id, insert new_id
585  id_set_copy.erase(old_id);
586  id_set_copy.insert(new_id);
587  }
588 
589  // Store in new version of map
590  new_elemset_codes_inverse_map.emplace(id_set_copy, elemset_code);
591  }
592 
593  // Swap existing map with newly-built one
594  _elemset_codes_inverse_map.swap(new_elemset_codes_inverse_map);
595 
596  // Reconstruct _elemset_codes map
597  _elemset_codes.clear();
598  for (const auto & [id_set, elemset_code] : _elemset_codes_inverse_map)
599  _elemset_codes.emplace(elemset_code, &id_set);
600 
601  // Update _all_elemset_ids
602  _all_elemset_ids.erase(old_id);
603  _all_elemset_ids.insert(new_id);
604 }
605 
606 unsigned int MeshBase::spatial_dimension () const
607 {
608  return cast_int<unsigned int>(_spatial_dimension);
609 }
610 
611 
612 
613 void MeshBase::set_spatial_dimension(unsigned char d)
614 {
615  // The user can set the _spatial_dimension however they wish,
616  // libMesh will only *increase* the spatial dimension, however,
617  // never decrease it.
618  _spatial_dimension = d;
619 }
620 
621 
622 
623 unsigned int MeshBase::add_elem_integer(std::string name,
624  bool allocate_data,
625  dof_id_type default_value)
626 {
627  for (auto i : index_range(_elem_integer_names))
628  if (_elem_integer_names[i] == name)
629  {
630  libmesh_assert_less(i, _elem_integer_default_values.size());
631  _elem_integer_default_values[i] = default_value;
632  return i;
633  }
634 
635  libmesh_assert_equal_to(_elem_integer_names.size(),
637  _elem_integer_names.push_back(std::move(name));
638  _elem_integer_default_values.push_back(default_value);
639  if (allocate_data)
640  this->size_elem_extra_integers();
641  return _elem_integer_names.size()-1;
642 }
643 
644 
645 
646 std::vector<unsigned int> MeshBase::add_elem_integers(const std::vector<std::string> & names,
647  bool allocate_data,
648  const std::vector<dof_id_type> * default_values)
649 {
650  libmesh_assert(!default_values || default_values->size() == names.size());
651  libmesh_assert_equal_to(_elem_integer_names.size(), _elem_integer_default_values.size());
652 
653  std::unordered_map<std::string, std::size_t> name_indices;
654  for (auto i : index_range(_elem_integer_names))
655  name_indices[_elem_integer_names[i]] = i;
656 
657  std::vector<unsigned int> returnval(names.size());
658 
659  bool added_an_integer = false;
660  for (auto i : index_range(names))
661  {
662  const std::string & name = names[i];
663  if (const auto it = name_indices.find(name);
664  it != name_indices.end())
665  {
666  returnval[i] = it->second;
667  _elem_integer_default_values[it->second] =
668  default_values ? (*default_values)[i] : DofObject::invalid_id;
669  }
670  else
671  {
672  returnval[i] = _elem_integer_names.size();
673  name_indices[name] = returnval[i];
674  _elem_integer_names.push_back(name);
676  (default_values ? (*default_values)[i] : DofObject::invalid_id);
677  added_an_integer = true;
678  }
679  }
680 
681  if (allocate_data && added_an_integer)
682  this->size_elem_extra_integers();
683 
684  return returnval;
685 }
686 
687 
688 
689 unsigned int MeshBase::get_elem_integer_index(std::string_view name) const
690 {
691  for (auto i : index_range(_elem_integer_names))
692  if (_elem_integer_names[i] == name)
693  return i;
694 
695  libmesh_error_msg("Unknown elem integer " << name);
696  return libMesh::invalid_uint;
697 }
698 
699 
700 
701 bool MeshBase::has_elem_integer(std::string_view name) const
702 {
703  for (auto & entry : _elem_integer_names)
704  if (entry == name)
705  return true;
706 
707  return false;
708 }
709 
710 
711 
712 unsigned int MeshBase::add_node_integer(std::string name,
713  bool allocate_data,
714  dof_id_type default_value)
715 {
716  for (auto i : index_range(_node_integer_names))
717  if (_node_integer_names[i] == name)
718  {
719  libmesh_assert_less(i, _node_integer_default_values.size());
720  _node_integer_default_values[i] = default_value;
721  return i;
722  }
723 
724  libmesh_assert_equal_to(_node_integer_names.size(),
726  _node_integer_names.push_back(std::move(name));
727  _node_integer_default_values.push_back(default_value);
728  if (allocate_data)
729  this->size_node_extra_integers();
730  return _node_integer_names.size()-1;
731 }
732 
733 
734 
735 std::vector<unsigned int> MeshBase::add_node_integers(const std::vector<std::string> & names,
736  bool allocate_data,
737  const std::vector<dof_id_type> * default_values)
738 {
739  libmesh_assert(!default_values || default_values->size() == names.size());
740  libmesh_assert_equal_to(_node_integer_names.size(), _node_integer_default_values.size());
741 
742  std::unordered_map<std::string, std::size_t> name_indices;
743  for (auto i : index_range(_node_integer_names))
744  name_indices[_node_integer_names[i]] = i;
745 
746  std::vector<unsigned int> returnval(names.size());
747 
748  bool added_an_integer = false;
749  for (auto i : index_range(names))
750  {
751  const std::string & name = names[i];
752  if (const auto it = name_indices.find(name);
753  it != name_indices.end())
754  {
755  returnval[i] = it->second;
756  _node_integer_default_values[it->second] =
757  default_values ? (*default_values)[i] : DofObject::invalid_id;
758  }
759  else
760  {
761  returnval[i] = _node_integer_names.size();
762  name_indices[name] = returnval[i];
763  _node_integer_names.push_back(name);
765  (default_values ? (*default_values)[i] : DofObject::invalid_id);
766  added_an_integer = true;
767  }
768  }
769 
770  if (allocate_data && added_an_integer)
771  this->size_node_extra_integers();
772 
773  return returnval;
774 }
775 
776 
777 
778 unsigned int MeshBase::get_node_integer_index(std::string_view name) const
779 {
780  for (auto i : index_range(_node_integer_names))
781  if (_node_integer_names[i] == name)
782  return i;
783 
784  libmesh_error_msg("Unknown node integer " << name);
785  return libMesh::invalid_uint;
786 }
787 
788 
789 
790 bool MeshBase::has_node_integer(std::string_view name) const
791 {
792  for (auto & entry : _node_integer_names)
793  if (entry == name)
794  return true;
795 
796  return false;
797 }
798 
799 
800 
802 {
803  LOG_SCOPE("remove_orphaned_nodes()", "MeshBase");
804 
805  // Will hold the set of nodes that are currently connected to elements
806  std::unordered_set<Node *> connected_nodes;
807 
808  // Loop over the elements. Find which nodes are connected to at
809  // least one of them.
810  for (const auto & element : this->element_ptr_range())
811  for (auto & n : element->node_ref_range())
812  connected_nodes.insert(&n);
813 
814  for (const auto & node : this->node_ptr_range())
815  if (!connected_nodes.count(node))
816  this->delete_node(node);
817 
819 }
820 
821 
822 
823 #ifdef LIBMESH_ENABLE_DEPRECATED
824 void MeshBase::prepare_for_use (const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
825 {
826  libmesh_deprecated();
827 
828  // We only respect the users wish if they tell us to skip renumbering. If they tell us not to
829  // skip renumbering but someone previously called allow_renumbering(false), then the latter takes
830  // precedence
831  if (skip_renumber_nodes_and_elements)
832  this->allow_renumbering(false);
833 
834  // We always accept the user's value for skip_find_neighbors, in contrast to skip_renumber
835  const bool old_allow_find_neighbors = this->allow_find_neighbors();
836  this->allow_find_neighbors(!skip_find_neighbors);
837 
838  this->prepare_for_use();
839 
840  this->allow_find_neighbors(old_allow_find_neighbors);
841 }
842 
843 void MeshBase::prepare_for_use (const bool skip_renumber_nodes_and_elements)
844 {
845  libmesh_deprecated();
846 
847  // We only respect the users wish if they tell us to skip renumbering. If they tell us not to
848  // skip renumbering but someone previously called allow_renumbering(false), then the latter takes
849  // precedence
850  if (skip_renumber_nodes_and_elements)
851  this->allow_renumbering(false);
852 
853  this->prepare_for_use();
854 }
855 #endif // LIBMESH_ENABLE_DEPRECATED
856 
857 
858 
859 
861 {
862  // Mark everything as unprepared, except for those things we've been
863  // told we don't need to prepare, for backwards compatibility
864  this->clear_point_locator();
865  this->clear_stored_ranges();
866  _preparation = false;
869 
870  this->complete_preparation();
871 }
872 
873 
875 {
876  LOG_SCOPE("complete_preparation()", "MeshBase");
877 
878  parallel_object_only();
879 
880  libmesh_assert(this->comm().verify(this->is_serial()));
881 
882  // If we don't go into this method with valid constraint rows, we're
883  // only going to be able to make that worse.
884 #ifdef DEBUG
886 #endif
887 
888  // A distributed mesh may have processors with no elements (or
889  // processors with no elements of higher dimension, if we ever
890  // support mixed-dimension meshes), but we want consistent
891  // mesh_dimension anyways.
892  //
893  // cache_elem_data() should get the elem_dimensions() and
894  // mesh_dimension() correct later, and we don't need it earlier.
895 
896 
897  // Renumber the nodes and elements so that they in contiguous
898  // blocks. By default, _skip_renumber_nodes_and_elements is false.
899  //
900  // Instances where you if prepare_for_use() should not renumber the nodes
901  // and elements include reading in e.g. an xda/r or gmv file. In
902  // this case, the ordering of the nodes may depend on an accompanying
903  // solution, and the node ordering cannot be changed.
904 
905 
906  // Mesh modification operations might not leave us with consistent
907  // id counts, or might leave us with orphaned nodes we're no longer
908  // using, but our partitioner might need that consistency and/or
909  // might be confused by orphaned nodes.
911  {
915  }
916  else
917  {
919  this->remove_orphaned_nodes();
922  }
923 
924  // Let all the elements find their neighbors
926  this->find_neighbors();
927 
928  // The user may have set boundary conditions. We require that the
929  // boundary conditions were set consistently. Because we examine
930  // neighbors when evaluating non-raw boundary condition IDs, this
931  // assert is only valid when our neighbor links are in place.
932 #ifdef DEBUG
934 #endif
935 
936  // Search the mesh for all the dimensions of the elements
937  // and cache them.
939  this->cache_elem_data();
940 
941  // Search the mesh for elements that have a neighboring element
942  // of dim+1 and set that element as the interior parent
944  {
946  this->detect_interior_parents();
947  else
948  {
949  // We must set the flag that says "interior parent pointers have been set up"
950  // even though we skip detect_interior_parents().
952  }
953  }
954 
955  // Fix up node unique ids in case mesh generation code didn't take
956  // exceptional care to do so.
957  // MeshCommunication().make_node_unique_ids_parallel_consistent(*this);
958 
959  // We're going to still require that mesh generation code gets
960  // element unique ids consistent.
961 #if defined(DEBUG) && defined(LIBMESH_ENABLE_UNIQUE_ID)
963 #endif
964 
965  // Allow our GhostingFunctor objects to reinit if necessary.
966  // Do this before partitioning and redistributing, and before
967  // deleting remote elements.
969  this->reinit_ghosting_functors();
970 
971  // Partition the mesh unless *all* partitioning is to be skipped.
972  // If only noncritical partitioning is to be skipped, the
973  // partition() call will still check for orphaned nodes.
975  this->partition();
976  else if (!this->n_unpartitioned_elem() &&
977  !this->n_unpartitioned_nodes())
979 
980  // If we're using DistributedMesh, we'll probably want it
981  // parallelized.
982  if (this->_allow_remote_element_removal &&
984  this->delete_remote_elements();
985  else
987 
988  // Much of our boundary info may have been for now-remote parts of the mesh,
989  // in which case we don't want to keep local copies of data meant to be
990  // local. On the other hand we may have deleted, or the user may have added in
991  // a distributed fashion, boundary data that is meant to be global. So we
992  // handle both of those scenarios here
995 
998 
999  // The mesh is now prepared for use, with the possible exception of
1000  // partitioning that was supposed to be skipped, and it should know
1001  // it.
1002 #ifndef NDEBUG
1003  Preparation completed_preparation = _preparation;
1004  if (skip_partitioning())
1005  completed_preparation.is_partitioned = true;
1006  libmesh_assert(completed_preparation);
1007 #endif
1008 
1009 #ifdef DEBUG
1011 #ifdef LIBMESH_ENABLE_UNIQUE_ID
1013 #endif
1014 #endif
1015 }
1016 
1017 void
1019 {
1020  for (auto & gf : _ghosting_functors)
1021  {
1022  libmesh_assert(gf);
1023  gf->mesh_reinit();
1024  }
1025 
1027 }
1028 
1030 {
1031  // Reset the number of partitions
1032  _n_parts = 1;
1033 
1034  // Reset the preparation flags
1035  _preparation = false;
1036 
1037  // Clear boundary information
1038  if (boundary_info)
1039  boundary_info->clear();
1040 
1041  // Clear cached element data
1042  _elem_dims.clear();
1043  _elem_default_orders.clear();
1045 
1046  _elemset_codes.clear();
1048 
1049  _constraint_rows.clear();
1050 
1051  // Clear our point locator.
1052  this->clear_point_locator();
1053  this->clear_stored_ranges();
1054 }
1055 
1056 
1058 {
1059  return static_cast<bool>(_preparation);
1060 }
1061 
1062 
1064 {
1065  _preparation = false;
1066  this->clear_point_locator();
1067  this->clear_stored_ranges();
1068 }
1069 
1070 
1072 {
1073  // We used to implicitly support duplicate inserts to std::set
1074 #ifdef LIBMESH_ENABLE_DEPRECATED
1075  _ghosting_functors.erase
1076  (std::remove(_ghosting_functors.begin(),
1077  _ghosting_functors.end(),
1078  &ghosting_functor),
1079  _ghosting_functors.end());
1080 #endif
1081 
1082  // We shouldn't have two copies of the same functor
1083  libmesh_assert(std::find(_ghosting_functors.begin(),
1084  _ghosting_functors.end(),
1085  &ghosting_functor) ==
1086  _ghosting_functors.end());
1087 
1088  _ghosting_functors.push_back(&ghosting_functor);
1089 }
1090 
1091 
1092 
1094 {
1095  auto raw_it = std::find(_ghosting_functors.begin(),
1096  _ghosting_functors.end(), &ghosting_functor);
1097 
1098  // The DofMap has a "to_mesh" parameter that tells it to avoid
1099  // registering a new functor with the mesh, but it doesn't keep
1100  // track of which functors weren't added, so we'll support "remove a
1101  // functor that isn't there" just like we did with set::erase
1102  // before.
1103  if (raw_it != _ghosting_functors.end())
1104  _ghosting_functors.erase(raw_it);
1105 
1106  // We shouldn't have had two copies of the same functor
1107  libmesh_assert(std::find(_ghosting_functors.begin(),
1108  _ghosting_functors.end(),
1109  &ghosting_functor) ==
1110  _ghosting_functors.end());
1111 
1112  if (const auto it = _shared_functors.find(&ghosting_functor);
1113  it != _shared_functors.end())
1114  _shared_functors.erase(it);
1115 }
1116 
1117 
1118 
1119 void MeshBase::subdomain_ids (std::set<subdomain_id_type> & ids, const bool global /* = true */) const
1120 {
1121  // This requires an inspection on every processor
1122  if (global)
1123  parallel_object_only();
1124 
1125  struct SBDInserter {
1126  std::set<subdomain_id_type> my_ids;
1127 
1128  SBDInserter () {}
1129  SBDInserter (SBDInserter &, Threads::split) {}
1130 
1131  void operator()(const ConstElemRange & range) {
1132  for (const Elem * elem : range)
1133  my_ids.insert(elem->subdomain_id());
1134  }
1135 
1136  void join(SBDInserter & other) {
1137  my_ids.merge(other.my_ids);
1138  }
1139  };
1140 
1141  SBDInserter inserter;
1143 
1144  ids.swap(inserter.my_ids);
1145 
1146  if (global)
1147  {
1148  // Only include the unpartitioned elements if the user requests the global IDs.
1149  // In the case of the local subdomain IDs, it doesn't make sense to include the
1150  // unpartitioned elements because said elements do not have a sense of locality.
1151  for (const auto & elem : this->active_unpartitioned_element_ptr_range())
1152  ids.insert(elem->subdomain_id());
1153 
1154  // Some subdomains may only live on other processors
1155  this->comm().set_union(ids);
1156  }
1157 }
1158 
1159 
1160 
1162 {
1163  // We now have all elements and nodes redistributed; our ghosting
1164  // functors should be ready to redistribute and/or recompute any
1165  // cached data they use too.
1166  for (auto & gf : as_range(this->ghosting_functors_begin(),
1167  this->ghosting_functors_end()))
1168  gf->redistribute();
1169 }
1170 
1171 
1172 
1174 {
1175  // A range over all elements might still be fine here, but any range
1176  // over local elements is obsolete if our partitioner changed the
1177  // definition of "local".
1179 }
1180 
1181 
1182 
1184 {
1185  // This requires an inspection on every processor
1186  parallel_object_only();
1187 
1188  std::set<subdomain_id_type> ids;
1189 
1190  this->subdomain_ids (ids);
1191 
1192  return cast_int<subdomain_id_type>(ids.size());
1193 }
1194 
1195 
1196 
1198 {
1199  std::set<subdomain_id_type> ids;
1200 
1201  this->subdomain_ids (ids, /* global = */ false);
1202 
1203  return cast_int<subdomain_id_type>(ids.size());
1204 }
1205 
1206 
1207 
1208 
1210 {
1211  // We're either counting a processor's nodes or unpartitioned
1212  // nodes
1213  libmesh_assert (proc_id < this->n_processors() ||
1214  proc_id == DofObject::invalid_processor_id);
1215 
1216  return static_cast<dof_id_type>(std::distance (this->pid_nodes_begin(proc_id),
1217  this->pid_nodes_end (proc_id)));
1218 }
1219 
1220 
1221 
1223 {
1224  // We're either counting a processor's elements or unpartitioned
1225  // elements
1226  libmesh_assert (proc_id < this->n_processors() ||
1227  proc_id == DofObject::invalid_processor_id);
1228 
1229  return static_cast<dof_id_type>(std::distance (this->pid_elements_begin(proc_id),
1230  this->pid_elements_end (proc_id)));
1231 }
1232 
1233 
1234 
1236 {
1237  libmesh_assert_less (proc_id, this->n_processors());
1238  return static_cast<dof_id_type>(std::distance (this->active_pid_elements_begin(proc_id),
1239  this->active_pid_elements_end (proc_id)));
1240 }
1241 
1242 
1243 
1245 {
1246  dof_id_type ne=0;
1247 
1248  for (const auto & elem : this->element_ptr_range())
1249  ne += elem->n_sub_elem();
1250 
1251  return ne;
1252 }
1253 
1254 
1255 
1257 {
1258  dof_id_type ne=0;
1259 
1260  for (const auto & elem : this->active_element_ptr_range())
1261  ne += elem->n_sub_elem();
1262 
1263  return ne;
1264 }
1265 
1266 
1267 
1268 std::string MeshBase::get_info(const unsigned int verbosity /* = 0 */, const bool global /* = true */) const
1269 {
1270  std::ostringstream oss;
1271 
1272  oss << " Mesh Information:" << '\n';
1273 
1274  if (!_elem_dims.empty())
1275  {
1276  oss << " elem_dimensions()={";
1277  std::copy(_elem_dims.begin(),
1278  --_elem_dims.end(), // --end() is valid if the set is non-empty
1279  std::ostream_iterator<unsigned int>(oss, ", "));
1280  oss << cast_int<unsigned int>(*_elem_dims.rbegin());
1281  oss << "}\n";
1282  }
1283 
1284  if (!_elem_default_orders.empty())
1285  {
1286  oss << " elem_default_orders()={";
1287  std::transform(_elem_default_orders.begin(),
1288  --_elem_default_orders.end(),
1289  std::ostream_iterator<std::string>(oss, ", "),
1290  [](Order o)
1291  { return Utility::enum_to_string<Order>(o); });
1292  oss << Utility::enum_to_string<Order>(*_elem_default_orders.rbegin());
1293  oss << "}\n";
1294  }
1295 
1296  oss << " supported_nodal_order()=" << this->supported_nodal_order() << '\n'
1297  << " spatial_dimension()=" << this->spatial_dimension() << '\n'
1298  << " n_nodes()=" << this->n_nodes() << '\n'
1299  << " n_local_nodes()=" << this->n_local_nodes() << '\n'
1300  << " n_elem()=" << this->n_elem() << '\n'
1301  << " n_local_elem()=" << this->n_local_elem() << '\n';
1302 #ifdef LIBMESH_ENABLE_AMR
1303  oss << " n_active_elem()=" << this->n_active_elem() << '\n';
1304 #endif
1305  if (global)
1306  oss << " n_subdomains()=" << static_cast<std::size_t>(this->n_subdomains()) << '\n';
1307  else
1308  oss << " n_local_subdomains()= " << static_cast<std::size_t>(this->n_local_subdomains()) << '\n';
1309  oss << " n_elemsets()=" << static_cast<std::size_t>(this->n_elemsets()) << '\n';
1310  if (!_elemset_codes.empty())
1311  oss << " n_elemset_codes=" << _elemset_codes.size() << '\n';
1312  oss << " n_partitions()=" << static_cast<std::size_t>(this->n_partitions()) << '\n'
1313  << " n_processors()=" << static_cast<std::size_t>(this->n_processors()) << '\n'
1314  << " n_threads()=" << static_cast<std::size_t>(libMesh::n_threads()) << '\n'
1315  << " processor_id()=" << static_cast<std::size_t>(this->processor_id()) << '\n'
1316  << " is_prepared()=" << (this->is_prepared() ? "true" : "false") << '\n'
1317  << " is_replicated()=" << (this->is_replicated() ? "true" : "false") << '\n';
1318 
1319  if (verbosity > 0)
1320  {
1321  if (global)
1322  {
1323  libmesh_parallel_only(this->comm());
1324  if (this->processor_id() != 0)
1325  oss << "\n Detailed global get_info() (verbosity > 0) is reduced and output to only rank 0.";
1326  }
1327 
1328  // Helper for printing element types
1329  const auto elem_type_helper = [](const std::set<int> & elem_types) {
1330  std::stringstream ss;
1331  for (auto it = elem_types.begin(); it != elem_types.end();)
1332  {
1333  ss << Utility::enum_to_string((ElemType)*it);
1334  if (++it != elem_types.end())
1335  ss << ", ";
1336  }
1337  return ss.str();
1338  };
1339 
1340  // Helper for whether or not the given DofObject is to be included. If we're doing
1341  // a global reduction, we also count unpartitioned objects on rank 0.
1342  const auto include_object = [this, &global](const DofObject & dof_object) {
1343  return this->processor_id() == dof_object.processor_id() ||
1344  (global &&
1345  this->processor_id() == 0 &&
1346  dof_object.processor_id() == DofObject::invalid_processor_id);
1347  };
1348 
1349  Real volume = 0;
1350 
1351  // Add bounding box information
1352  const auto bbox = global ? MeshTools::create_bounding_box(*this) : MeshTools::create_local_bounding_box(*this);
1353  if (!global || this->processor_id() == 0)
1354  oss << "\n " << (global ? "" : "Local ") << "Mesh Bounding Box:\n"
1355  << " Minimum: " << bbox.min() << "\n"
1356  << " Maximum: " << bbox.max() << "\n"
1357  << " Delta: " << (bbox.max() - bbox.min()) << "\n";
1358 
1359  // Obtain the global or local element types
1360  std::set<int> elem_types;
1361  for (const Elem * elem : this->active_local_element_ptr_range())
1362  elem_types.insert(elem->type());
1363  if (global)
1364  {
1365  // Pick up unpartitioned elems on rank 0
1366  if (this->processor_id() == 0)
1367  for (const Elem * elem : this->active_unpartitioned_element_ptr_range())
1368  elem_types.insert(elem->type());
1369 
1370  this->comm().set_union(elem_types);
1371  }
1372 
1373  // Add element types
1374  if (!global || this->processor_id() == 0)
1375  oss << "\n " << (global ? "" : "Local ") << "Mesh Element Type(s):\n "
1376  << elem_type_helper(elem_types) << "\n";
1377 
1378  // Reduce the nodeset ids
1379  auto nodeset_ids = this->get_boundary_info().get_node_boundary_ids();
1380  if (global)
1381  this->comm().set_union(nodeset_ids);
1382 
1383  // Accumulate local information for each nodeset
1384  struct NodesetInfo
1385  {
1386  std::size_t num_nodes = 0;
1387  BoundingBox bbox;
1388  };
1389  std::map<boundary_id_type, NodesetInfo> nodeset_info_map;
1390  for (const auto & [node, id] : this->get_boundary_info().get_nodeset_map())
1391  {
1392  if (!include_object(*node))
1393  continue;
1394 
1395  NodesetInfo & info = nodeset_info_map[id];
1396 
1397  ++info.num_nodes;
1398 
1399  if (verbosity > 1)
1400  info.bbox.union_with(*node);
1401  }
1402 
1403  // Add nodeset info
1404  if (!global || this->processor_id() == 0)
1405  {
1406  oss << "\n " << (global ? "" : "Local ") << "Mesh Nodesets:\n";
1407  if (nodeset_ids.empty())
1408  oss << " None\n";
1409  }
1410 
1411  const auto & nodeset_name_map = this->get_boundary_info().get_nodeset_name_map();
1412  for (const auto id : nodeset_ids)
1413  {
1414  NodesetInfo & info = nodeset_info_map[id];
1415 
1416  // Reduce the local information for this nodeset if required
1417  if (global)
1418  {
1419  this->comm().sum(info.num_nodes);
1420  if (verbosity > 1)
1421  {
1422  this->comm().min(info.bbox.min());
1423  this->comm().max(info.bbox.max());
1424  }
1425  }
1426 
1427  const bool has_name = nodeset_name_map.count(id) && nodeset_name_map.at(id).size();
1428  const std::string name = has_name ? nodeset_name_map.at(id) : "";
1429  if (global)
1430  libmesh_assert(this->comm().verify(name));
1431 
1432  if (global ? this->processor_id() == 0 : info.num_nodes > 0)
1433  {
1434  oss << " Nodeset " << id;
1435  if (has_name)
1436  oss << " (" << name << ")";
1437  oss << ", " << info.num_nodes << " " << (global ? "" : "local ") << "nodes\n";
1438 
1439  if (verbosity > 1)
1440  {
1441  oss << " " << (global ? "Bounding" : "Local bounding") << " box minimum: "
1442  << info.bbox.min() << "\n"
1443  << " " << (global ? "Bounding" : "Local bounding") << " box maximum: "
1444  << info.bbox.max() << "\n"
1445  << " " << (global ? "Bounding" : "Local bounding") << " box delta: "
1446  << (info.bbox.max() - info.bbox.min()) << "\n";
1447  }
1448  }
1449  }
1450 
1451  // Reduce the sideset ids
1452  auto sideset_ids = this->get_boundary_info().get_side_boundary_ids();
1453  if (global)
1454  this->comm().set_union(sideset_ids);
1455 
1456  // Accumulate local information for each sideset
1457  struct SidesetInfo
1458  {
1459  std::size_t num_sides = 0;
1460  Real volume = 0;
1461  std::set<int> side_elem_types;
1462  std::set<int> elem_types;
1463  std::set<dof_id_type> elem_ids;
1464  std::set<dof_id_type> node_ids;
1465  BoundingBox bbox;
1466  };
1467  ElemSideBuilder side_builder;
1468  std::map<boundary_id_type, SidesetInfo> sideset_info_map;
1469  for (const auto & pair : this->get_boundary_info().get_sideset_map())
1470  {
1471  const Elem * elem = pair.first;
1472  if (!include_object(*elem))
1473  continue;
1474 
1475  const auto id = pair.second.second;
1476  SidesetInfo & info = sideset_info_map[id];
1477 
1478  const auto s = pair.second.first;
1479  const Elem & side = side_builder(*elem, s);
1480 
1481  ++info.num_sides;
1482  info.side_elem_types.insert(side.type());
1483  info.elem_types.insert(elem->type());
1484  info.elem_ids.insert(elem->id());
1485 
1486  for (const Node & node : side.node_ref_range())
1487  if (include_object(node))
1488  info.node_ids.insert(node.id());
1489 
1490  if (verbosity > 1)
1491  {
1492  info.volume += side.volume();
1493  info.bbox.union_with(side.loose_bounding_box());
1494  }
1495  }
1496 
1497  // Add sideset info
1498  if (!global || this->processor_id() == 0)
1499  {
1500  oss << "\n " << (global ? "" : "Local ") << "Mesh Sidesets:\n";
1501  if (sideset_ids.empty())
1502  oss << " None\n";
1503  }
1504  const auto & sideset_name_map = this->get_boundary_info().get_sideset_name_map();
1505  for (const auto id : sideset_ids)
1506  {
1507  SidesetInfo & info = sideset_info_map[id];
1508 
1509  auto num_elems = info.elem_ids.size();
1510  auto num_nodes = info.node_ids.size();
1511 
1512  // Reduce the local information for this sideset if required
1513  if (global)
1514  {
1515  this->comm().sum(info.num_sides);
1516  this->comm().set_union(info.side_elem_types, 0);
1517  this->comm().sum(num_elems);
1518  this->comm().set_union(info.elem_types, 0);
1519  this->comm().sum(num_nodes);
1520  if (verbosity > 1)
1521  {
1522  this->comm().sum(info.volume);
1523  this->comm().min(info.bbox.min());
1524  this->comm().max(info.bbox.max());
1525  }
1526  }
1527 
1528  const bool has_name = sideset_name_map.count(id) && sideset_name_map.at(id).size();
1529  const std::string name = has_name ? sideset_name_map.at(id) : "";
1530  if (global)
1531  libmesh_assert(this->comm().verify(name));
1532 
1533  if (global ? this->processor_id() == 0 : info.num_sides > 0)
1534  {
1535  oss << " Sideset " << id;
1536  if (has_name)
1537  oss << " (" << name << ")";
1538  oss << ", " << info.num_sides << " sides (" << elem_type_helper(info.side_elem_types) << ")"
1539  << ", " << num_elems << " " << (global ? "" : "local ") << "elems (" << elem_type_helper(info.elem_types) << ")"
1540  << ", " << num_nodes << " " << (global ? "" : "local ") << "nodes\n";
1541 
1542  if (verbosity > 1)
1543  {
1544  oss << " " << (global ? "Side" : "Local side") << " volume: " << info.volume << "\n"
1545  << " " << (global ? "Bounding" : "Local bounding") << " box minimum: "
1546  << info.bbox.min() << "\n"
1547  << " " << (global ? "Bounding" : "Local bounding") << " box maximum: "
1548  << info.bbox.max() << "\n"
1549  << " " << (global ? "Bounding" : "Local bounding") << " box delta: "
1550  << (info.bbox.max() - info.bbox.min()) << "\n";
1551  }
1552  }
1553  }
1554 
1555  // Reduce the edgeset ids
1556  auto edgeset_ids = this->get_boundary_info().get_edge_boundary_ids();
1557  if (global)
1558  this->comm().set_union(edgeset_ids);
1559 
1560  // Accumulate local information for each edgeset
1561  struct EdgesetInfo
1562  {
1563  std::size_t num_edges = 0;
1564  std::set<int> edge_elem_types;
1565  BoundingBox bbox;
1566  };
1567  std::map<boundary_id_type, EdgesetInfo> edgeset_info_map;
1568  std::unique_ptr<const Elem> edge;
1569 
1570  for (const auto & pair : this->get_boundary_info().get_edgeset_map())
1571  {
1572  const Elem * elem = pair.first;
1573  if (!include_object(*elem))
1574  continue;
1575 
1576  const auto id = pair.second.second;
1577  EdgesetInfo & info = edgeset_info_map[id];
1578 
1579  elem->build_edge_ptr(edge, pair.second.first);
1580 
1581  ++info.num_edges;
1582  info.edge_elem_types.insert(edge->type());
1583 
1584  if (verbosity > 1)
1585  info.bbox.union_with(edge->loose_bounding_box());
1586  }
1587 
1588  // Add edgeset info
1589  if (!global || this->processor_id() == 0)
1590  {
1591  oss << "\n " << (global ? "" : "Local ") << "Mesh Edgesets:\n";
1592  if (edgeset_ids.empty())
1593  oss << " None\n";
1594  }
1595 
1596  const auto & edgeset_name_map = this->get_boundary_info().get_edgeset_name_map();
1597  for (const auto id : edgeset_ids)
1598  {
1599  EdgesetInfo & info = edgeset_info_map[id];
1600 
1601  // Reduce the local information for this edgeset if required
1602  if (global)
1603  {
1604  this->comm().sum(info.num_edges);
1605  this->comm().set_union(info.edge_elem_types, 0);
1606  if (verbosity > 1)
1607  {
1608  this->comm().min(info.bbox.min());
1609  this->comm().min(info.bbox.max());
1610  }
1611  }
1612 
1613  const bool has_name = edgeset_name_map.count(id) && edgeset_name_map.at(id).size();
1614  const std::string name = has_name ? edgeset_name_map.at(id) : "";
1615  if (global)
1616  libmesh_assert(this->comm().verify(name));
1617 
1618  if (global ? this->processor_id() == 0 : info.num_edges > 0)
1619  {
1620  oss << " Edgeset " << id;
1621  if (has_name)
1622  oss << " (" << name << ")";
1623  oss << ", " << info.num_edges << " " << (global ? "" : "local ") << "edges ("
1624  << elem_type_helper(info.edge_elem_types) << ")\n";
1625 
1626  if (verbosity > 1)
1627  {
1628  oss << " " << (global ? "Bounding" : "Local bounding") << " box minimum: "
1629  << info.bbox.min() << "\n"
1630  << " " << (global ? "Bounding" : "Local bounding") << " box maximum: "
1631  << info.bbox.max() << "\n"
1632  << " " << (global ? "Bounding" : "Local bounding") << " box delta: "
1633  << (info.bbox.max() - info.bbox.min()) << "\n";
1634  }
1635  }
1636  }
1637 
1638  // Reduce the block IDs and block names
1639  std::set<subdomain_id_type> subdomains;
1640  for (const Elem * elem : this->active_element_ptr_range())
1641  if (include_object(*elem))
1642  subdomains.insert(elem->subdomain_id());
1643  if (global)
1644  this->comm().set_union(subdomains);
1645 
1646  // Accumulate local information for each subdomain
1647  struct SubdomainInfo
1648  {
1649  std::size_t num_elems = 0;
1650  Real volume = 0;
1651  std::set<int> elem_types;
1652  std::set<dof_id_type> active_node_ids;
1653 #ifdef LIBMESH_ENABLE_AMR
1654  std::size_t num_active_elems = 0;
1655 #endif
1656  BoundingBox bbox;
1657  };
1658  std::map<subdomain_id_type, SubdomainInfo> subdomain_info_map;
1659  for (const Elem * elem : this->element_ptr_range())
1660  if (include_object(*elem))
1661  {
1662  SubdomainInfo & info = subdomain_info_map[elem->subdomain_id()];
1663 
1664  ++info.num_elems;
1665  info.elem_types.insert(elem->type());
1666 
1667 #ifdef LIBMESH_ENABLE_AMR
1668  if (elem->active())
1669  ++info.num_active_elems;
1670 #endif
1671 
1672  for (const Node & node : elem->node_ref_range())
1673  if (include_object(node) && node.active())
1674  info.active_node_ids.insert(node.id());
1675 
1676  if (verbosity > 1 && elem->active())
1677  {
1678  info.volume += elem->volume();
1679  info.bbox.union_with(elem->loose_bounding_box());
1680  }
1681  }
1682 
1683  // Add subdomain info
1684  oss << "\n " << (global ? "" : "Local ") << "Mesh Subdomains:\n";
1685  const auto & subdomain_name_map = this->get_subdomain_name_map();
1686  for (const auto id : subdomains)
1687  {
1688  SubdomainInfo & info = subdomain_info_map[id];
1689 
1690  auto num_active_nodes = info.active_node_ids.size();
1691 
1692  // Reduce the information for this subdomain if needed
1693  if (global)
1694  {
1695  this->comm().sum(info.num_elems);
1696 #ifdef LIBMESH_ENABLE_AMR
1697  this->comm().sum(info.num_active_elems);
1698 #endif
1699  this->comm().sum(num_active_nodes);
1700  this->comm().set_union(info.elem_types, 0);
1701  if (verbosity > 1)
1702  {
1703  this->comm().min(info.bbox.min());
1704  this->comm().max(info.bbox.max());
1705  this->comm().sum(info.volume);
1706  }
1707  }
1708  if (verbosity > 1)
1709  volume += info.volume;
1710 
1711  const bool has_name = subdomain_name_map.count(id);
1712  const std::string name = has_name ? subdomain_name_map.at(id) : "";
1713  if (global)
1714  libmesh_assert(this->comm().verify(name));
1715 
1716  if (!global || this->processor_id() == 0)
1717  {
1718  oss << " Subdomain " << id;
1719  if (has_name)
1720  oss << " (" << name << ")";
1721  oss << ": " << info.num_elems << " " << (global ? "" : "local ") << "elems "
1722  << "(" << elem_type_helper(info.elem_types);
1723 #ifdef LIBMESH_ENABLE_AMR
1724  oss << ", " << info.num_active_elems << " active";
1725 #endif
1726  oss << "), " << num_active_nodes << " " << (global ? "" : "local ") << "active nodes\n";
1727  if (verbosity > 1)
1728  {
1729  oss << " " << (global ? "Volume" : "Local volume") << ": " << info.volume << "\n";
1730  oss << " " << (global ? "Bounding" : "Local bounding") << " box minimum: "
1731  << info.bbox.min() << "\n"
1732  << " " << (global ? "Bounding" : "Local bounding") << " box maximum: "
1733  << info.bbox.max() << "\n"
1734  << " " << (global ? "Bounding" : "Local bounding") << " box delta: "
1735  << (info.bbox.max() - info.bbox.min()) << "\n";
1736  }
1737  }
1738  }
1739 
1740  oss << " " << (global ? "Global" : "Local") << " mesh volume = " << volume << "\n";
1741 
1742  }
1743 
1744  return oss.str();
1745 }
1746 
1747 
1748 void MeshBase::print_info(std::ostream & os, const unsigned int verbosity /* = 0 */, const bool global /* = true */) const
1749 {
1750  os << this->get_info(verbosity, global)
1751  << std::endl;
1752 }
1753 
1754 
1755 std::ostream & operator << (std::ostream & os, const MeshBase & m)
1756 {
1757  m.print_info(os);
1758  return os;
1759 }
1760 
1761 
1762 void MeshBase::partition (const unsigned int n_parts)
1763 {
1764  // If we get here and we have unpartitioned elements, we need that
1765  // fixed.
1766  if (this->n_unpartitioned_elem() > 0)
1767  {
1768  libmesh_assert (partitioner().get());
1769  libmesh_assert (this->is_serial());
1770  partitioner()->partition (*this, n_parts);
1771  }
1772  // A nullptr partitioner or a skip_partitioning(true) call or a
1773  // skip_noncritical_partitioning(true) call means don't repartition;
1774  // skip_noncritical_partitioning() checks all these.
1775  else if (!skip_noncritical_partitioning())
1776  {
1777  partitioner()->partition (*this, n_parts);
1778  }
1779  else
1780  {
1781  // Adaptive coarsening may have "orphaned" nodes on processors
1782  // whose elements no longer share them. We need to check for
1783  // and possibly fix that.
1785 
1786  // Make sure locally cached partition count is correct
1787  this->recalculate_n_partitions();
1788 
1789  // Make sure any other locally cached data is correct
1790  this->update_post_partitioning();
1791  }
1792 
1794 }
1795 
1796 void MeshBase::all_second_order (const bool full_ordered)
1797 {
1798  this->all_second_order_range(this->element_ptr_range(), full_ordered);
1799 }
1800 
1802 {
1803  this->all_complete_order_range(this->element_ptr_range());
1804 }
1805 
1807 {
1808  // This requires an inspection on every processor
1809  parallel_object_only();
1810 
1811  unsigned int max_proc_id=0;
1812 
1813  for (const auto & elem : this->active_local_element_ptr_range())
1814  max_proc_id = std::max(max_proc_id, static_cast<unsigned int>(elem->processor_id()));
1815 
1816  // The number of partitions is one more than the max processor ID.
1817  _n_parts = max_proc_id+1;
1818 
1819  this->comm().max(_n_parts);
1820 
1821  return _n_parts;
1822 }
1823 
1824 
1825 
1826 std::unique_ptr<PointLocatorBase> MeshBase::sub_point_locator () const
1827 {
1828  // If there's no master point locator, then we need one.
1829  if (_point_locator.get() == nullptr)
1830  {
1831  // PointLocator construction may not be safe within threads
1833 
1834  // And it may require parallel communication
1835  parallel_object_only();
1836 
1837 #ifdef LIBMESH_ENABLE_NANOFLANN_POINTLOCATOR
1839 #else
1841 #endif
1842 
1844  _point_locator->set_close_to_point_tol(_point_locator_close_to_point_tol);
1845  }
1846 
1847  // Otherwise there was a master point locator, and we can grab a
1848  // sub-locator easily.
1849  return
1850 #ifdef LIBMESH_ENABLE_NANOFLANN_POINTLOCATOR
1852 #else
1854 #endif
1855 }
1856 
1857 
1858 
1860 {
1861  _point_locator.reset(nullptr);
1862 }
1863 
1864 
1865 
1867 {
1868  _count_lower_dim_elems_in_point_locator = count_lower_dim_elems;
1869 }
1870 
1871 
1872 
1874 {
1876 }
1877 
1878 
1879 
1881 {
1882  return _block_id_to_name[id];
1883 }
1884 
1885 const std::string & MeshBase::subdomain_name(subdomain_id_type id) const
1886 {
1887  // An empty string to return when no matching subdomain name is found
1888  static const std::string empty;
1889 
1890  if (const auto iter = _block_id_to_name.find(id);
1891  iter == _block_id_to_name.end())
1892  return empty;
1893  else
1894  return iter->second;
1895 }
1896 
1897 
1898 
1899 
1901 {
1902  // Linear search over the map values.
1903  for (const auto & [sbd_id, sbd_name] : _block_id_to_name)
1904  if (sbd_name == name)
1905  return sbd_id;
1906 
1907  // If we made it here without returning, we don't have a subdomain
1908  // with the requested name, so return Elem::invalid_subdomain_id.
1910 }
1911 
1912 
1914 {
1915  if (!_element_stored_range)
1916  {
1917  // Range construction may not be safe within threads
1919 
1921  std::make_unique<ElemRange>(this->elements_begin(),
1922  this->elements_end());
1923  }
1924 
1925  return *_element_stored_range;
1926 }
1927 
1929 {
1931  {
1932  // Range construction may not be safe within threads
1934 
1936  std::make_unique<ConstElemRange>(this->active_local_elements_begin(),
1937  this->active_local_elements_end());
1938  }
1939 
1941 }
1942 
1944 {
1945  _element_stored_range.reset(nullptr);
1947 }
1948 
1949 
1950 #ifdef LIBMESH_ENABLE_DEPRECATED
1952 {
1953  libmesh_deprecated();
1954 
1955  this->cache_elem_data();
1956 }
1957 #endif // LIBMESH_ENABLE_DEPRECATED
1958 
1960 {
1961  // This requires an inspection on every processor
1962  parallel_object_only();
1963 
1964  // Need to clear containers first in case all elements of a
1965  // particular dimension/order/subdomain have been deleted.
1966  _elem_dims.clear();
1967  _elem_default_orders.clear();
1968  _mesh_subdomains.clear();
1970 
1971  for (const auto & elem : this->active_element_ptr_range())
1972  {
1973  _elem_dims.insert(cast_int<unsigned char>(elem->dim()));
1974  _elem_default_orders.insert(elem->default_order());
1975  _mesh_subdomains.insert(elem->subdomain_id());
1977  static_cast<Order>
1978  (std::min(static_cast<int>(_supported_nodal_order),
1979  static_cast<int>(elem->supported_nodal_order())));
1980  }
1981 
1982  if (!this->is_serial())
1983  {
1984  // Some different dimension/order/subdomain elements may only live
1985  // on other processors
1986  this->comm().set_union(_elem_dims);
1988  this->comm().min(_supported_nodal_order);
1989  this->comm().set_union(_mesh_subdomains);
1990  }
1991 
1992  // If the largest element dimension found is larger than the current
1993  // _spatial_dimension, increase _spatial_dimension.
1994  unsigned int max_dim = this->mesh_dimension();
1995  if (max_dim > _spatial_dimension)
1996  _spatial_dimension = cast_int<unsigned char>(max_dim);
1997 
1998  // _spatial_dimension may need to increase from 1->2 or 2->3 if the
1999  // mesh is full of 1D elements but they are not x-aligned, or the
2000  // mesh is full of 2D elements but they are not in the x-y plane.
2001  // If the mesh is x-aligned or x-y planar, we will end up checking
2002  // every node's coordinates and not breaking out of the loop
2003  // early...
2004  if (_spatial_dimension < LIBMESH_DIM)
2005  {
2006  for (const auto & node : this->node_ptr_range())
2007  {
2008  // Note: the exact floating point comparison is intentional,
2009  // we don't want to get tripped up by tolerances.
2010  if ((*node)(0) != 0. && _spatial_dimension < 1)
2011  _spatial_dimension = 1;
2012 
2013  if ((*node)(1) != 0. && _spatial_dimension < 2)
2014  {
2015  _spatial_dimension = 2;
2016 #if LIBMESH_DIM == 2
2017  // If libmesh is compiled in 2D mode, this is the
2018  // largest spatial dimension possible so we can break
2019  // out.
2020  break;
2021 #endif
2022  }
2023 
2024 #if LIBMESH_DIM > 2
2025  if ((*node)(2) != 0.)
2026  {
2027  // Spatial dimension can't get any higher than this, so
2028  // we can break out.
2029  _spatial_dimension = 3;
2030  break;
2031  }
2032 #endif
2033  }
2034  }
2035 
2037 }
2038 
2039 
2041 {
2042  // This requires every processor
2043  parallel_object_only();
2044 
2045  this->comm().set_union(_block_id_to_name);
2046 }
2047 
2048 
2050 {
2051  LOG_SCOPE("detect_interior_parents()", "MeshBase");
2052 
2053  // This requires an inspection on every processor
2054  parallel_object_only();
2055 
2056  // This requires up-to-date mesh dimensions in cache
2058 
2059  // Early return if the mesh is empty or has elements of a single spatial dimension.
2060  if (this->elem_dimensions().size() <= 1)
2061  {
2063  return;
2064  }
2065 
2066  // Convenient elem_dimensions iterators
2067  const auto dim_start = this->elem_dimensions().begin();
2068  const auto dim_end = this->elem_dimensions().end();
2069 
2070  // In this function we find only +1 dimensional interior parents,
2071  // (so, for a given element el, the interior parent p must satisfy p.dim() == el.dim() + 1).
2072  // Therefore, we can avoid checking the existence of interior parents
2073  // for all those elements el such there there is no p with p.dim() == el.dim() + 1.
2074  // We store whether to skip any given dimension in the construction of interior parents
2075  // inside the vector in dimensions_to_skip_for_interior_parents.
2076  std::vector<bool> skip_dimension_for_interior_parents(/*count=*/LIBMESH_DIM+1, /*value=*/false);
2077  skip_dimension_for_interior_parents.back() = true;
2078 
2079  // Moreover, in the following, we will build a node-to-elem map.
2080  // It is among the elems of this map that we will look for interior parents.
2081  // Therefore, we can skip all elems p such that there is no el with el.dim() == p.dim() - 1.
2082  // We store whether to skip any given dimension in the construction of the node-to-elem map
2083  // in the vector skip_dimensions_for_node_to_el_map.
2084  std::vector<bool> skip_dimensions_for_node_to_el_map(/*count=*/LIBMESH_DIM+1, /*value=*/false);
2085  skip_dimensions_for_node_to_el_map[*dim_start] = true;
2086 
2087  // We also create a flag to know if all dimensions should be skipped,
2088  // and if we should therefore return early.
2089  bool skip_all_dimensions = true;
2090 
2091  // Fill dimensions_to_skip_for_interior_parents and dimensions_to_skip_for_node_to_el_map.
2092  for (auto [it, next] = std::make_tuple(dim_start, std::next(dim_start));
2093  next != dim_end; ++it, ++next)
2094  {
2095  if (*it + 1 != *next) // if sequential dimensions differ by exactly 1
2096  {
2097  skip_dimension_for_interior_parents[*it] = true;
2098  skip_dimensions_for_node_to_el_map[*next] = true;
2099  }
2100  else if (!skip_dimension_for_interior_parents[*it])
2101  skip_all_dimensions = false;
2102  }
2103 
2104  // There is nothing to do if all dimensions should be
2105  // skipped. Before returning, we must also set the flag that says
2106  // "interior parent pointers have been set up" even though we
2107  // determined there was no work to be done.
2108  if (skip_all_dimensions)
2109  {
2111  return;
2112  }
2113 
2114  // Do we have interior parent pointers going to a different mesh?
2115  // If so then we'll still check to make sure that's the only place
2116  // they go, so we can libmesh_not_implemented() if not.
2117  const bool separate_interior_mesh = (&(this->interior_mesh()) != this);
2118 
2119  // This map will be used to set interior parents
2120  std::unordered_map<dof_id_type, std::vector<dof_id_type>> node_to_elem;
2121 
2122  for (const auto & elem : this->element_ptr_range())
2123  {
2124  // Ignore element if it cannot be interior parent of any other elem.
2125  if (skip_dimensions_for_node_to_el_map[elem->dim()])
2126  continue;
2127 
2128  // Populating the node_to_elem map, same as MeshTools::build_nodes_to_elem_map
2129  for (auto n : make_range(elem->n_vertices()))
2130  {
2131  libmesh_assert_less (elem->id(), this->max_elem_id());
2132 
2133  node_to_elem[elem->node_id(n)].push_back(elem->id());
2134  }
2135  }
2136 
2137  // Automatically set interior parents
2138  for (const auto & element : this->element_ptr_range())
2139  {
2140  // Ignore elements with dimensions to skip
2141  // or elements that already have an interior parent.
2142  if (skip_dimension_for_interior_parents[element->dim()] || element->interior_parent())
2143  continue;
2144 
2145  // Start by generating sets of dim+1 dimensional elements that
2146  // touch each vertex of the current element. If we encounter a
2147  // vertex not connected to _any_ dim+1 dimensional elements,
2148  // then we can exit the loop without checking the remaining
2149  // vertices since an interior parent (if it exists) will be
2150  // connected to all vertices of the current element.
2151  std::vector<std::set<dof_id_type>> neighbors( element->n_vertices() );
2152 
2153  bool found_interior_parents = true;
2154 
2155  for (auto n : make_range(element->n_vertices()))
2156  {
2157  auto it = node_to_elem.find(element->node_id(n));
2158 
2159  // Check at first that this node is not isolated.
2160  if (it == node_to_elem.end())
2161  {
2162  found_interior_parents = false;
2163  break; // out of n-loop
2164  }
2165 
2166  for (const auto & vertex_neighbor_id : it->second)
2167  if (this->elem_ref(vertex_neighbor_id).dim() == element->dim()+1)
2168  neighbors[n].insert(vertex_neighbor_id);
2169 
2170  if (neighbors[n].empty())
2171  {
2172  // We have found an empty set for one vertex, no reason
2173  // to continue.
2174  found_interior_parents = false;
2175  break; // out of n-loop
2176  }
2177  }
2178 
2179  // If we have generated a non-empty set of elements for each
2180  // vertex, we will now look for a vertex_neighbor_id that
2181  // appears in _all_ of those sets. If found, this is our interior
2182  // parent id. If multiple such common ids are found, we will
2183  // take the lowest such id to be the interior parent id.
2184  if (found_interior_parents)
2185  {
2186  std::set<dof_id_type> & neighbors_0 = neighbors[0];
2187  for (const auto & interior_parent_id : neighbors_0)
2188  {
2189  found_interior_parents = false;
2190  for (auto n : make_range(1u, element->n_vertices()))
2191  {
2192  if (neighbors[n].count(interior_parent_id))
2193  {
2194  found_interior_parents = true;
2195  }
2196  else
2197  {
2198  found_interior_parents = false;
2199  break;
2200  }
2201  }
2202 
2203  if (found_interior_parents)
2204  {
2205  element->set_interior_parent(this->elem_ptr(interior_parent_id));
2206  break;
2207  }
2208  }
2209 
2210  // Do we have a mixed dimensional mesh that contains some of
2211  // its own interior parents, but we already expect to have
2212  // interior parents on a different mesh? That's going to
2213  // take some work to support if anyone needs it.
2214  if (separate_interior_mesh)
2215  libmesh_not_implemented_msg
2216  ("interior_parent() values in multiple meshes are unsupported.");
2217  }
2218  }
2219 
2220  // This flag doesn't necessarily mean any Elems actually have
2221  // interior parent pointers, just that we did all the work to
2222  // determine whether or not they do.
2224 }
2225 
2226 
2227 
2228 #ifdef LIBMESH_ENABLE_PERIODIC
2229 
2233  const boundary_id_type b2,
2234  const RealVectorValue & translation)
2235  {
2236  // Lazily allocate the container the first time it’s needed
2238  _disjoint_neighbor_boundary_pairs = std::make_unique<PeriodicBoundaries>();
2239 
2241 
2242  // Create forward and inverse boundary mappings
2243  PeriodicBoundary forward(translation);
2244  PeriodicBoundary inverse(translation * -1.0);
2245 
2246  forward.myboundary = b1;
2247  forward.pairedboundary = b2;
2248  inverse.myboundary = b2;
2249  inverse.pairedboundary = b1;
2250 
2251  // Add both directions into the container
2252  db.emplace(b1, forward.clone());
2253  db.emplace(b2, inverse.clone());
2254  }
2255 
2257  {
2258  return _disjoint_neighbor_boundary_pairs.get();
2259  }
2260 
2262  {
2263  return _disjoint_neighbor_boundary_pairs.get();
2264  }
2265 
2267  const boundary_id_type b2)
2268  {
2269  // Nothing to remove if not allocated or empty
2271  return;
2272 
2273  auto & pairs = *_disjoint_neighbor_boundary_pairs;
2274 
2275  // Helper to check and erase both directions
2276  auto erase_if_match = [](boundary_id_type key,
2277  boundary_id_type pair,
2278  PeriodicBoundaries & pb_map)
2279  {
2280  auto it = pb_map.find(key);
2281  if (it != pb_map.end())
2282  {
2283  const auto & pb = *(it->second);
2284  // Check both directions
2285  if ((pb.myboundary == key && pb.pairedboundary == pair) ||
2286  (pb.pairedboundary == key && pb.myboundary == pair))
2287  pb_map.erase(it);
2288  }
2289  };
2290 
2291  erase_if_match(b1, b2, pairs);
2292  erase_if_match(b2, b1, pairs);
2293  }
2294 
2295 
2296 #endif
2297 
2298 
2299 
2301 {
2303  if (_point_locator)
2304  {
2305  if (val > 0.)
2306  _point_locator->set_close_to_point_tol(val);
2307  else
2308  _point_locator->unset_close_to_point_tol();
2309  }
2310 }
2311 
2312 
2313 
2315 {
2317 }
2318 
2319 
2320 
2322 {
2323  const std::size_t new_size = _elem_integer_names.size();
2324 
2326  (this->element_stored_range(),
2327  [new_size, this](const ElemRange & range)
2328  {
2329  for (Elem * elem : range)
2330  elem->add_extra_integers(new_size, this->_elem_integer_default_values);
2331  });
2332 }
2333 
2334 
2335 
2337 {
2338  const std::size_t new_size = _node_integer_names.size();
2339  for (auto node : this->node_ptr_range())
2340  node->add_extra_integers(new_size, _node_integer_default_values);
2341 }
2342 
2343 
2344 std::pair<std::vector<unsigned int>, std::vector<unsigned int>>
2346 {
2347  std::pair<std::vector<unsigned int>, std::vector<unsigned int>> returnval;
2348  returnval.first = this->add_elem_integers(other._elem_integer_names, true, &other._elem_integer_default_values);
2349  returnval.second = this->add_node_integers(other._node_integer_names, true, &other._node_integer_default_values);
2350  return returnval;
2351 }
2352 
2353 
2354 
2355 void
2357 {
2358  // Now that all the DofObject moving is done, we can move the GhostingFunctor objects
2359  // which include the _default_ghosting,_ghosting_functors and _shared_functors. We also need
2360  // to set the mesh object associated with these functors to the assignee mesh.
2361 
2362  // _default_ghosting
2363  _default_ghosting = std::move(other_mesh._default_ghosting);
2364  _default_ghosting->set_mesh(this);
2365 
2366  // _ghosting_functors
2367  _ghosting_functors = std::move(other_mesh._ghosting_functors);
2368 
2369  for (const auto gf : _ghosting_functors )
2370  {
2371  gf->set_mesh(this);
2372  }
2373 
2374  // _shared_functors
2375  _shared_functors = std::move(other_mesh._shared_functors);
2376 
2377  for (const auto & sf : _shared_functors )
2378  {
2379  (sf.second)->set_mesh(this);
2380  }
2381 
2382  // _constraint_rows
2383  _constraint_rows = std::move(other_mesh._constraint_rows);
2384 
2385  if (other_mesh.partitioner())
2386  _partitioner = std::move(other_mesh.partitioner());
2387 }
2388 
2389 
2390 void
2392 {
2393  this->_spatial_dimension = other_mesh._spatial_dimension;
2394  this->_elem_dims = other_mesh._elem_dims;
2395  this->_elem_default_orders = other_mesh._elem_default_orders;
2397  this->_mesh_subdomains = other_mesh._mesh_subdomains;
2398 }
2399 
2400 
2401 bool MeshBase::nodes_and_elements_equal(const MeshBase & other_mesh) const
2402 {
2403  for (const auto & other_node : other_mesh.node_ptr_range())
2404  {
2405  const Node * node = this->query_node_ptr(other_node->id());
2406  if (!node)
2407  return false;
2408  if (*other_node != *node)
2409  return false;
2410  }
2411  for (const auto & node : this->node_ptr_range())
2412  if (!other_mesh.query_node_ptr(node->id()))
2413  return false;
2414 
2415  for (const auto & other_elem : other_mesh.element_ptr_range())
2416  {
2417  const Elem * elem = this->query_elem_ptr(other_elem->id());
2418  if (!elem)
2419  return false;
2420  if (!other_elem->topologically_equal(*elem))
2421  return false;
2422  }
2423  for (const auto & elem : this->element_ptr_range())
2424  if (!other_mesh.query_elem_ptr(elem->id()))
2425  return false;
2426 
2427  return true;
2428 }
2429 
2430 
2432 {
2433  dof_id_type n_local_rows=0, n_unpartitioned_rows=0;
2434  for (const auto & [node, node_constraints] : _constraint_rows)
2435  {
2436  // Unpartitioned nodes
2437  if (node->processor_id() == DofObject::invalid_processor_id)
2438  n_unpartitioned_rows++;
2439  else if (node->processor_id() == this->processor_id())
2440  n_local_rows++;
2441  }
2442 
2443  this->comm().sum(n_local_rows);
2444 
2445  return n_unpartitioned_rows + n_local_rows;
2446 }
2447 
2448 
2449 void
2451 {
2452  LOG_SCOPE("copy_constraint_rows(mesh)", "MeshBase");
2453 
2454  _constraint_rows.clear();
2455 
2456  const auto & other_constraint_rows = other_mesh.get_constraint_rows();
2457  for (const auto & [other_node, other_node_constraints] : other_constraint_rows)
2458  {
2459  const Node * const our_node = this->node_ptr(other_node->id());
2460  constraint_rows_mapped_type our_node_constraints;
2461  for (const auto & [other_inner_key_pair, constraint_value] : other_node_constraints)
2462  {
2463  const auto & [other_elem, local_node_id] = other_inner_key_pair;
2464  const Elem * const our_elem = this->elem_ptr(other_elem->id());
2465  our_node_constraints.emplace_back(std::make_pair(our_elem, local_node_id), constraint_value);
2466  }
2467  _constraint_rows[our_node] = std::move(our_node_constraints);
2468  }
2469 }
2470 
2471 
2472 template <typename T>
2473 void
2475  bool precondition_constraint_operator)
2476 {
2477  LOG_SCOPE("copy_constraint_rows(mat)", "MeshBase");
2478 
2479  this->_constraint_rows.clear();
2480 
2481  // We're not going to support doing this distributed yet; it'd be
2482  // pointless unless we temporarily had a linear partitioning to
2483  // better match the constraint operator.
2484  MeshSerializer serialize(*this);
2485 
2486  // Our current mesh should already reflect the desired assembly space
2487  libmesh_error_msg_if(this->n_nodes() != constraint_operator.m(),
2488  "Constraint operator matrix with " <<
2489  constraint_operator.m() <<
2490  "rows does not match this mesh with " <<
2491  this->n_nodes() << " nodes");
2492 
2493  // First, find what new unconstrained DoFs we need to add. We can't
2494  // iterate over columns in a SparseMatrix, so we'll iterate over
2495  // rows and keep track of columns.
2496 
2497  // If we have nodes that will work unconstrained, keep track of
2498  // their node ids and corresponding column indices.
2499  // existing_unconstrained_nodes[column_id] = node_id
2500  std::map<dof_id_type, dof_id_type> existing_unconstrained_columns;
2501  std::set<dof_id_type> existing_unconstrained_nodes;
2502 
2503  // In case we need new nodes, keep track of their columns.
2504  // columns[j][k] will be the kth row index and value of column j
2505  typedef
2506  std::unordered_map<dof_id_type,
2507  std::vector<std::pair<dof_id_type, Real>>>
2508  columns_type;
2509  columns_type columns(constraint_operator.n());
2510 
2511  // If we need to precondition the constraint operator (e.g. it's an
2512  // unpreconditioned extraction operator for a Flex IGA matrix),
2513  // we'll want to keep track of the sum of each column, because we'll
2514  // be dividing each column by that sum (Jacobi preconditioning on
2515  // the right, which then leads to symmetric preconditioning on a
2516  // physics Jacobian).
2517  std::unordered_map<dof_id_type, Real> column_sums;
2518 
2519  // Work in parallel, though we'll have to sync shortly
2520  for (auto i : make_range(constraint_operator.row_start(),
2521  constraint_operator.row_stop()))
2522  {
2523  std::vector<numeric_index_type> indices;
2524  std::vector<T> values;
2525 
2526  constraint_operator.get_row(i, indices, values);
2527  libmesh_assert_equal_to(indices.size(), values.size());
2528 
2529  if (indices.size() == 1 &&
2530  values[0] == T(1))
2531  {
2532  // If we have multiple simple Ui=Uj constraints, let the
2533  // first one be our "unconstrained" node and let the others
2534  // be constrained to it.
2535  if (existing_unconstrained_columns.find(indices[0]) !=
2536  existing_unconstrained_columns.end())
2537  {
2538  const auto j = indices[0];
2539  columns[j].emplace_back(i, 1);
2540  }
2541  else
2542  {
2543  existing_unconstrained_nodes.insert(i);
2544  existing_unconstrained_columns.emplace(indices[0],i);
2545  }
2546  }
2547  else
2548  for (auto jj : index_range(indices))
2549  {
2550  const auto j = indices[jj];
2551  const Real coef = libmesh_real(values[jj]);
2552  libmesh_assert_equal_to(coef, values[jj]);
2553  columns[j].emplace_back(i, coef);
2554  }
2555  }
2556 
2557  // Merge data from different processors' slabs of the matrix
2558  this->comm().set_union(existing_unconstrained_nodes);
2559  this->comm().set_union(existing_unconstrained_columns);
2560 
2561  std::vector<columns_type> all_columns;
2562  this->comm().allgather(columns, all_columns);
2563 
2564  columns.clear();
2565  for (auto p : index_range(all_columns))
2566  for (auto & [j, subcol] : all_columns[p])
2567  for (auto [i, v] : subcol)
2568  columns[j].emplace_back(i,v);
2569 
2570  // Keep track of elements on which unconstrained nodes exist, and
2571  // their local node indices.
2572  // node_to_elem_ptrs[node] = [elem_id, local_node_num]
2573  std::unordered_map<const Node *, std::pair<dof_id_type, unsigned int>> node_to_elem_ptrs;
2574 
2575  // Find elements attached to any existing nodes that will stay
2576  // unconstrained. We'll also build a subdomain set here so we don't
2577  // have to assert that the mesh is already prepared before we pick a
2578  // new subdomain for any NodeElems we need to add.
2579  std::set<subdomain_id_type> subdomain_ids;
2580  for (const Elem * elem : this->element_ptr_range())
2581  {
2582  subdomain_ids.insert(elem->subdomain_id());
2583  for (auto n : make_range(elem->n_nodes()))
2584  {
2585  const Node * node = elem->node_ptr(n);
2586  if (existing_unconstrained_nodes.count(node->id()))
2587  node_to_elem_ptrs.emplace(node, std::make_pair(elem->id(), n));
2588  }
2589  }
2590 
2591  const subdomain_id_type new_sbd_id = *subdomain_ids.rbegin() + 1;
2592 
2593  for (auto j : make_range(constraint_operator.n()))
2594  {
2595  // If we already have a good node for this then we're done
2596  if (existing_unconstrained_columns.count(j))
2597  continue;
2598 
2599  // Get a half-decent spot to place a new NodeElem for
2600  // unconstrained DoF(s) here. Getting a *fully*-decent spot
2601  // would require finding a Moore-Penrose pseudoinverse, and I'm
2602  // not going to do that, but scaling a transpose will at least
2603  // get us a little uniqueness to make visualization reasonable.
2604  Point newpt;
2605  Real total_scaling = 0;
2606  unsigned int total_entries = 0;
2607 
2608  // We'll get a decent initial pid choice here too, if only to
2609  // aid in later repartitioning.
2610  std::map<processor_id_type, int> pids;
2611 
2612  auto & column = columns[j];
2613  for (auto [i, r] : column)
2614  {
2615  Node & constrained_node = this->node_ref(i);
2616  const Point constrained_pt = constrained_node;
2617  newpt += r*constrained_pt;
2618  total_scaling += r;
2619  ++total_entries;
2620  ++pids[constrained_node.processor_id()];
2621  }
2622 
2623  if (precondition_constraint_operator)
2624  column_sums[j] = total_scaling;
2625 
2626  libmesh_error_msg_if
2627  (!total_entries,
2628  "Empty column " << j <<
2629  " found in constraint operator matrix");
2630 
2631  // If we have *cancellation* here then we can end up dividing by
2632  // zero; try just evenly scaling across all constrained node
2633  // points instead.
2634  if (total_scaling > TOLERANCE)
2635  newpt /= total_scaling;
2636  else
2637  newpt /= total_entries;
2638 
2639  Node *n = this->add_point(newpt);
2640  std::unique_ptr<Elem> elem = Elem::build(NODEELEM);
2641  elem->set_node(0, n);
2642  elem->subdomain_id() = new_sbd_id;
2643 
2644  Elem * added_elem = this->add_elem(std::move(elem));
2645  this->_elem_dims.insert(0);
2646  this->_elem_default_orders.insert(added_elem->default_order());
2647  this->_supported_nodal_order =
2648  static_cast<Order>
2649  (std::min(static_cast<int>(this->_supported_nodal_order),
2650  static_cast<int>(added_elem->supported_nodal_order())));
2651  this->_mesh_subdomains.insert(new_sbd_id);
2652  node_to_elem_ptrs.emplace(n, std::make_pair(added_elem->id(), 0));
2653  existing_unconstrained_columns.emplace(j,n->id());
2654 
2655  // Repartition the new objects *after* adding them, so a
2656  // DistributedMesh doesn't get confused and think you're not
2657  // adding them on all processors at once.
2658  int n_pids = 0;
2659  for (auto [pid, count] : pids)
2660  if (count >= n_pids)
2661  {
2662  n_pids = count;
2663  added_elem->processor_id() = pid;
2664  n->processor_id() = pid;
2665  }
2666  }
2667 
2668  // Calculate constraint rows in an indexed form that's easy for us
2669  // to allgather
2670  std::unordered_map<dof_id_type,
2671  std::vector<std::pair<std::pair<dof_id_type, unsigned int>,Real>>>
2672  indexed_constraint_rows;
2673 
2674  for (auto i : make_range(constraint_operator.row_start(),
2675  constraint_operator.row_stop()))
2676  {
2677  if (existing_unconstrained_nodes.count(i))
2678  continue;
2679 
2680  std::vector<numeric_index_type> indices;
2681  std::vector<T> values;
2682 
2683  constraint_operator.get_row(i, indices, values);
2684 
2685  std::vector<std::pair<std::pair<dof_id_type, unsigned int>, Real>> constraint_row;
2686 
2687  for (auto jj : index_range(indices))
2688  {
2689  const dof_id_type node_id =
2690  existing_unconstrained_columns[indices[jj]];
2691 
2692  Node & constraining_node = this->node_ref(node_id);
2693 
2694  libmesh_assert(node_to_elem_ptrs.count(&constraining_node));
2695 
2696  auto p = node_to_elem_ptrs[&constraining_node];
2697 
2698  Real coef = libmesh_real(values[jj]);
2699  libmesh_assert_equal_to(coef, values[jj]);
2700 
2701  // If we're preconditioning and we created a nodeelem then
2702  // we can scale the meaning of that nodeelem's value to give
2703  // us a better-conditioned matrix after the constraints are
2704  // applied.
2705  if (precondition_constraint_operator)
2706  if (auto sum_it = column_sums.find(indices[jj]);
2707  sum_it != column_sums.end())
2708  {
2709  const Real scaling = sum_it->second;
2710 
2711  if (scaling > TOLERANCE)
2712  coef /= scaling;
2713  }
2714 
2715  constraint_row.emplace_back(std::make_pair(p, coef));
2716  }
2717 
2718  indexed_constraint_rows.emplace(i, std::move(constraint_row));
2719  }
2720 
2721  this->comm().set_union(indexed_constraint_rows);
2722 
2723  // Add constraint rows as mesh constraint rows
2724  for (auto & [node_id, indexed_row] : indexed_constraint_rows)
2725  {
2726  Node * constrained_node = this->node_ptr(node_id);
2727 
2728  constraint_rows_mapped_type constraint_row;
2729 
2730  for (auto [p, coef] : indexed_row)
2731  {
2732  const Elem * elem = this->elem_ptr(p.first);
2733  constraint_row.emplace_back
2734  (std::make_pair(std::make_pair(elem, p.second), coef));
2735  }
2736 
2737  this->_constraint_rows.emplace(constrained_node,
2738  std::move(constraint_row));
2739  }
2740 }
2741 
2742 
2743 void MeshBase::print_constraint_rows(std::ostream & os,
2744  bool print_nonlocal) const
2745 {
2746  parallel_object_only();
2747 
2748  std::string local_constraints =
2749  this->get_local_constraints(print_nonlocal);
2750 
2751  if (this->processor_id())
2752  {
2753  this->comm().send(0, local_constraints);
2754  }
2755  else
2756  {
2757  os << "Processor 0:\n";
2758  os << local_constraints;
2759 
2760  for (auto p : IntRange<processor_id_type>(1, this->n_processors()))
2761  {
2762  this->comm().receive(p, local_constraints);
2763  os << "Processor " << p << ":\n";
2764  os << local_constraints;
2765  }
2766  }
2767 }
2768 
2769 
2770 
2771 std::string MeshBase::get_local_constraints(bool print_nonlocal) const
2772 {
2773  std::ostringstream os;
2774 
2775  if (print_nonlocal)
2776  os << "All ";
2777  else
2778  os << "Local ";
2779 
2780  os << "Mesh Constraint Rows:"
2781  << std::endl;
2782 
2783  for (const auto & [node, row] : _constraint_rows)
2784  {
2785  const bool local = (node->processor_id() == this->processor_id());
2786 
2787  // Skip non-local dofs if requested
2788  if (!print_nonlocal && !local)
2789  continue;
2790 
2791  os << "Constraints for " << (local ? "Local" : "Ghost") << " Node " << node->id()
2792  << ": \t";
2793 
2794  for (const auto & [elem_and_node, coef] : row)
2795  os << " ((" << elem_and_node.first->id() << ',' << elem_and_node.second << "), " << coef << ")\t";
2796 
2797  os << std::endl;
2798  }
2799 
2800  return os.str();
2801 }
2802 
2804  is_partitioned(false),
2805  has_synched_id_counts(false),
2806  has_neighbor_ptrs(false),
2807  has_cached_elem_data(false),
2808  has_interior_parent_ptrs(false),
2809  has_removed_remote_elements(false),
2810  has_removed_orphaned_nodes(false),
2811  has_boundary_id_sets(false),
2812  has_reinit_ghosting_functors(false)
2813 {}
2814 
2815 MeshBase::Preparation::operator bool() const
2816 {
2817  return is_partitioned &&
2818  has_synched_id_counts &&
2819  has_neighbor_ptrs &&
2820  has_cached_elem_data &&
2821  has_interior_parent_ptrs &&
2822  has_removed_remote_elements &&
2823  has_removed_orphaned_nodes &&
2824  has_reinit_ghosting_functors &&
2825  has_boundary_id_sets;
2826 }
2827 
2830 {
2831  is_partitioned = set_all;
2832  has_synched_id_counts = set_all;
2833  has_neighbor_ptrs = set_all;
2834  has_cached_elem_data = set_all;
2835  has_interior_parent_ptrs = set_all;
2836  has_removed_remote_elements = set_all;
2837  has_removed_orphaned_nodes = set_all;
2838  has_reinit_ghosting_functors = set_all;
2839  has_boundary_id_sets = set_all;
2840 
2841  return *this;
2842 }
2843 
2844 bool
2846 {
2847  if (is_partitioned != other.is_partitioned)
2848  return false;
2849  if (has_synched_id_counts != other.has_synched_id_counts)
2850  return false;
2851  if (has_neighbor_ptrs != other.has_neighbor_ptrs)
2852  return false;
2853  if (has_cached_elem_data != other.has_cached_elem_data)
2854  return false;
2855  if (has_interior_parent_ptrs != other.has_interior_parent_ptrs)
2856  return false;
2857  if (has_removed_remote_elements != other.has_removed_remote_elements)
2858  return false;
2859  if (has_removed_orphaned_nodes != other.has_removed_orphaned_nodes)
2860  return false;
2861  if (has_reinit_ghosting_functors != other.has_reinit_ghosting_functors)
2862  return false;
2863  if (has_boundary_id_sets != other.has_boundary_id_sets)
2864  return false;
2865 
2866  return true;
2867 }
2868 
2869 bool
2871 {
2872  return !(*this == other);
2873 }
2874 
2875 
2876 // Explicit instantiations for our template function
2877 template LIBMESH_EXPORT void
2878 MeshBase::copy_constraint_rows(const SparseMatrix<Real> & constraint_operator,
2879  bool precondition_constraint_operator);
2880 
2881 #ifdef LIBMESH_USE_COMPLEX_NUMBERS
2882 template LIBMESH_EXPORT void
2883 MeshBase::copy_constraint_rows(const SparseMatrix<Complex> & constraint_operator,
2884  bool precondition_constraint_operator);
2885 #endif
2886 
2887 
2888 } // namespace libMesh
T libmesh_real(T a)
std::string name(const ElemQuality q)
This function returns a string containing some name for q.
Definition: elem_quality.C:42
GhostingFunctorIterator ghosting_functors_begin() const
Beginning of range of ghosting functors.
Definition: mesh_base.h:1462
bool operator==(const MeshBase &other_mesh) const
This tests for exactly-equal data in all the senses that a mathematician would care about (element co...
Definition: mesh_base.C:271
void parallel_for(const Range &range, const Body &body, unsigned int n_threads=libMesh::n_threads())
Execute the provided function object in parallel on the specified range.
Definition: threads_none.h:73
std::set< subdomain_id_type > _mesh_subdomains
We cache the subdomain ids of the elements present in the mesh.
Definition: mesh_base.h:2300
bool has_node_integer(std::string_view name) const
Definition: mesh_base.C:790
virtual void update_post_partitioning()
Recalculate any cached data (or invalidate any caches that are computed on the fly) after elements an...
Definition: mesh_base.C:1173
std::vector< unsigned int > add_elem_integers(const std::vector< std::string > &names, bool allocate_data=true, const std::vector< dof_id_type > *default_values=nullptr)
Register integer data (of type dof_id_type) to be added to each element in the mesh, one string name for each new integer.
Definition: mesh_base.C:646
bool closed()
Checks that the library has been closed.
Definition: libmesh.C:331
ElemType
Defines an enum for geometric element types.
void allgather(const T &send_data, std::vector< T, A > &recv_data) const
std::unique_ptr< ElemRange > _element_stored_range
A cached ElemRange for threaded mutation of all semilocal elements of this mesh.
Definition: mesh_base.h:2172
void elem_types(const MeshBase &mesh, std::vector< ElemType > &et)
Fills in a vector of all element types in the mesh.
Definition: mesh_tools.C:723
const std::set< boundary_id_type > & get_side_boundary_ids() const
virtual bool subclass_locally_equals(const MeshBase &other_mesh) const =0
Shim to allow operator == (&) to behave like a virtual function without having to be one...
bool _allow_node_and_elem_unique_id_overlap
The Exodus reader (and potentially other readers in the future?) now supports setting Node and Elem u...
Definition: mesh_base.h:2268
bool _skip_renumber_nodes_and_elements
If this is true then renumbering will be kept to a minimum.
Definition: mesh_base.h:2237
bool is_prepared() const
Definition: mesh_base.C:1057
Order
defines an enum for polynomial orders.
Definition: enum_order.h:40
static constexpr processor_id_type invalid_processor_id
An invalid processor_id to distinguish DoFs that have not been assigned to a processor.
Definition: dof_object.h:484
constraint_rows_type & get_constraint_rows()
Constraint rows accessors.
Definition: mesh_base.h:1905
virtual dof_id_type n_active_elem() const =0
A Node is like a Point, but with more information.
Definition: node.h:52
This abstract base class defines the interface by which library code and user code can report associa...
const MeshBase & interior_mesh() const
Definition: mesh_base.h:2008
unsigned int n_threads()
Definition: libmesh_base.h:109
Real get_point_locator_close_to_point_tol() const
Definition: mesh_base.C:2314
dof_id_type n_elem_on_proc(const processor_id_type proc) const
Definition: mesh_base.C:1222
dof_id_type n_unpartitioned_nodes() const
Definition: mesh_base.h:587
const unsigned int invalid_uint
A number which is used quite often to represent an invalid or uninitialized value for an unsigned int...
Definition: libmesh.h:303
std::vector< std::string > _elem_integer_names
The array of names for integer data associated with each element in the mesh.
Definition: mesh_base.h:2338
std::unique_ptr< PointLocatorBase > sub_point_locator() const
Definition: mesh_base.C:1826
void remove_orphaned_nodes()
Removes any orphaned nodes, nodes not connected to any elements.
Definition: mesh_base.C:801
std::vector< dof_id_type > get_elemset_codes() const
Return a vector of all elemset codes defined on the mesh.
Definition: mesh_base.C:503
void correct_node_proc_ids(MeshBase &)
Changes the processor ids on each node so be the same as the id of the lowest element touching that n...
Definition: mesh_tools.C:2508
virtual void all_second_order_range(const SimpleRange< element_iterator > &range, const bool full_ordered=true)=0
Converts a set of this Mesh&#39;s elements defined by range from FIRST order to SECOND order...
MPI_Info info
The IntRange templated class is intended to make it easy to loop over integers which are indices of a...
Definition: int_range.h:53
void detect_interior_parents()
Search the mesh for elements that have a neighboring element of dim+1 and set that element as the int...
Definition: mesh_base.C:2049
dof_id_type n_active_elem_on_proc(const processor_id_type proc) const
Definition: mesh_base.C:1235
libMesh::BoundingBox create_bounding_box(const MeshBase &mesh)
Definition: mesh_tools.C:566
static constexpr Real TOLERANCE
MeshBase * _interior_mesh
Defaulting to this, a pointer to the mesh used to generate boundary elements on this.
Definition: mesh_base.h:2219
std::vector< std::pair< std::pair< const Elem *, unsigned int >, Real > > constraint_rows_mapped_type
Definition: mesh_base.h:1899
We&#39;re using a class instead of a typedef to allow forward declarations and future flexibility...
subdomain_id_type n_local_subdomains() const
Definition: mesh_base.C:1197
void copy_cached_data(const MeshBase &other_mesh)
Helper class to copy cached data, to synchronize with a possibly unprepared other_mesh.
Definition: mesh_base.C:2391
virtual std::unique_ptr< GhostingFunctor > clone() const =0
A clone() is needed because GhostingFunctor can not be shared between different meshes.
Dummy "splitting object" used to distinguish splitting constructors from copy constructors.
Definition: threads_none.h:63
void set_spatial_dimension(unsigned char d)
Sets the "spatial dimension" of the Mesh.
Definition: mesh_base.C:613
std::map< dof_id_type, const MeshBase::elemset_type * > _elemset_codes
Map from "element set code" to list of set ids to which that element belongs (and vice-versa)...
Definition: mesh_base.h:2324
bool operator==(const Preparation &other) const
Two Preparation objects are equivalent iff all the flags match, regardless of the true/false status o...
Definition: mesh_base.C:2845
Preparation & operator=(bool set_all)
Set all flags to the "set_all" value.
Definition: mesh_base.C:2829
virtual void find_neighbors(const bool reset_remote_elements=false, const bool reset_current_list=true, const bool assert_valid=true)=0
Locate element face (edge in 2D) neighbors.
std::vector< GhostingFunctor * > _ghosting_functors
The list of all GhostingFunctor objects to be used when distributing a DistributedMesh.
Definition: mesh_base.h:2391
bool skip_noncritical_partitioning() const
Definition: mesh_base.h:1404
void sum(T &r) const
unsigned int add_elem_integer(std::string name, bool allocate_data=true, dof_id_type default_value=DofObject::invalid_id)
Register an integer datum (of type dof_id_type) to be added to each element in the mesh...
Definition: mesh_base.C:623
void add_elemset_code(dof_id_type code, MeshBase::elemset_type id_set)
Tabulate a user-defined "code" for elements which belong to the element sets specified in id_set...
Definition: mesh_base.C:456
const std::map< boundary_id_type, std::string > & get_sideset_name_map() const
bool has_elem_integer(std::string_view name) const
Definition: mesh_base.C:701
bool get_count_lower_dim_elems_in_point_locator() const
Get the current value of _count_lower_dim_elems_in_point_locator.
Definition: mesh_base.C:1873
void remove_ghosting_functor(GhostingFunctor &ghosting_functor)
Removes a functor which was previously added to the set of ghosting functors.
Definition: mesh_base.C:1093
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
virtual BoundingBox loose_bounding_box() const
Definition: elem.C:3498
dof_id_type n_local_nodes() const
Definition: mesh_base.h:581
virtual std::unique_ptr< Partitioner > & partitioner()
A partitioner to use at each partitioning.
Definition: mesh_base.h:165
constraint_rows_type _constraint_rows
Definition: mesh_base.h:2409
void libmesh_assert_valid_constraint_rows(const MeshBase &mesh)
A function for verifying that all mesh constraint rows express relations between nodes and elements t...
Definition: mesh_tools.C:1745
std::ostream & operator<<(std::ostream &os, const OrderWrapper &order)
Overload stream operators.
Definition: fe_type.h:182
void copy_constraint_rows(const MeshBase &other_mesh)
Copy the constraints from the other mesh to this mesh.
Definition: mesh_base.C:2450
const Parallel::Communicator & comm() const
std::vector< unsigned int > add_node_integers(const std::vector< std::string > &names, bool allocate_data=true, const std::vector< dof_id_type > *default_values=nullptr)
Register integer data (of type dof_id_type) to be added to each node in the mesh. ...
Definition: mesh_base.C:735
bool _skip_noncritical_partitioning
If this is true then no partitioning should be done with the possible exception of orphaned nodes...
Definition: mesh_base.h:2225
unsigned char _spatial_dimension
The "spatial dimension" of the Mesh.
Definition: mesh_base.h:2332
std::unique_ptr< BoundaryInfo > boundary_info
This class holds the boundary information.
Definition: mesh_base.h:2098
The StoredRange class defines a contiguous, divisible set of objects.
Definition: stored_range.h:54
GhostingFunctorIterator ghosting_functors_end() const
End of range of ghosting functors.
Definition: mesh_base.h:1468
bool _allow_remote_element_removal
If this is false then even on DistributedMesh remote elements will not be deleted during mesh prepara...
Definition: mesh_base.h:2255
The libMesh namespace provides an interface to certain functionality in the library.
std::map< MeshBase::elemset_type, dof_id_type > _elemset_codes_inverse_map
Definition: mesh_base.h:2325
dof_id_type get_elemset_code(const MeshBase::elemset_type &id_set) const
Definition: mesh_base.C:497
The definition of a periodic boundary.
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition: mesh_base.h:170
dof_id_type n_unpartitioned_elem() const
Definition: mesh_base.h:693
std::vector< std::string > _node_integer_names
The array of names for integer data associated with each node in the mesh.
Definition: mesh_base.h:2350
bool in_threads
A boolean which is true iff we are in a Threads:: function It may be useful to assert(!Threadsin_thre...
Definition: threads.C:33
Real distance(const Point &p)
void clear_stored_ranges()
Clears stored ranges, to indicate that the mesh has changed and they should be regenerated when next ...
Definition: mesh_base.C:1943
virtual Node * add_point(const Point &p, const dof_id_type id=DofObject::invalid_id, const processor_id_type proc_id=DofObject::invalid_processor_id)=0
Add a new Node at Point p to the end of the vertex array, with processor_id procid.
dof_id_type n_local_elem() const
Definition: mesh_base.h:687
MeshBase::elemset_type _all_elemset_ids
Definition: mesh_base.h:2326
dof_id_type n_constraint_rows() const
Definition: mesh_base.C:2431
bool nodes_and_elements_equal(const MeshBase &other_mesh) const
Tests for equality of all elements and nodes in the mesh.
Definition: mesh_base.C:2401
libMesh::BoundingBox create_local_bounding_box(const MeshBase &mesh)
Definition: mesh_tools.C:631
uint8_t processor_id_type
Definition: id_types.h:104
This is the MeshBase class.
Definition: mesh_base.h:80
virtual numeric_index_type row_stop() const =0
subdomain_id_type get_id_by_name(std::string_view name) const
Definition: mesh_base.C:1900
void get_elemsets(dof_id_type elemset_code, MeshBase::elemset_type &id_set_to_fill) const
Look up the element sets for a given elemset code and vice-versa.
Definition: mesh_base.C:487
void change_elemset_code(dof_id_type old_code, dof_id_type new_code)
Replace elemset code "old_code" with "new_code".
Definition: mesh_base.C:512
virtual void all_complete_order_range(const SimpleRange< element_iterator > &range)=0
Converts a set of elements in this (conforming, non-refined) mesh into "complete" order elements...
const std::set< boundary_id_type > & get_node_boundary_ids() const
Generic sparse matrix.
Definition: vector_fe_ex5.C:46
ParallelObject & operator=(const ParallelObject &libmesh_dbg_var(other))
"Assignment" operator.
Order supported_nodal_order() const
Definition: mesh_base.h:435
unique_id_type _next_unique_id
The next available unique id for assigning ids to DOF objects.
Definition: mesh_base.h:2212
unsigned int _n_parts
The number of partitions the mesh has.
Definition: mesh_base.h:2144
unsigned int get_elem_integer_index(std::string_view name) const
Definition: mesh_base.C:689
processor_id_type n_processors() const
virtual bool is_serial() const
Definition: mesh_base.h:347
boundary_id_type myboundary
The boundary ID of this boundary and its counterpart.
void add_disjoint_neighbor_boundary_pairs(const boundary_id_type b1, const boundary_id_type b2, const RealVectorValue &translation)
Register a pair of boundaries as disjoint neighbor boundary pairs.
Definition: mesh_base.C:2232
Status receive(const unsigned int dest_processor_id, T &buf, const MessageTag &tag=any_tag) const
const std::map< boundary_id_type, std::string > & get_nodeset_name_map() const
unsigned char _default_mapping_data
The default mapping data (unused with Lagrange, used for nodal weight lookup index with rational base...
Definition: mesh_base.h:2157
virtual Order supported_nodal_order() const
Definition: elem.h:1004
int8_t boundary_id_type
Definition: id_types.h:51
dof_id_type id() const
Definition: dof_object.h:819
void min(const T &r, T &o, Request &req) const
const std::map< subdomain_id_type, std::string > & get_subdomain_name_map() const
Definition: mesh_base.h:1896
static constexpr dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
Definition: dof_object.h:473
const ElemRange & element_stored_range()
Definition: mesh_base.C:1913
dof_id_type n_sub_elem() const
Definition: mesh_base.C:1244
virtual Elem * add_elem(Elem *e)=0
Add elem e to the end of the element array.
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition: elem.C:442
ElemMappingType _default_mapping_type
The default mapping type (typically Lagrange) between master and physical space to assign to newly ad...
Definition: mesh_base.h:2150
virtual void update_parallel_id_counts()=0
Updates parallel caches so that methods like n_elem() accurately reflect changes on other processors...
PeriodicBoundaries * get_disjoint_neighbor_boundary_pairs()
Definition: mesh_base.C:2256
void print_info(std::ostream &os=libMesh::out, const unsigned int verbosity=0, const bool global=true) const
Prints relevant information about the mesh.
Definition: mesh_base.C:1748
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
Helper function that allows us to treat a homogenous pair as a range.
Definition: simple_range.h:57
virtual numeric_index_type m() const =0
void cache_elem_data()
Definition: mesh_base.C:1959
std::unique_ptr< Partitioner > _partitioner
A partitioner to use at each prepare_for_use().
Definition: mesh_base.h:2206
virtual const Node * query_node_ptr(const dof_id_type i) const =0
const std::map< boundary_id_type, std::string > & get_edgeset_name_map() const
virtual dof_id_type max_elem_id() const =0
void clear_point_locator()
Releases the current PointLocator object.
Definition: mesh_base.C:1859
void subdomain_ids(std::set< subdomain_id_type > &ids, const bool global=true) const
Constructs a list of all subdomain identifiers in the local mesh if global == false, and in the global mesh if global == true (default).
Definition: mesh_base.C:1119
std::set< Order > _elem_default_orders
We cache the (default) order of the geometric elements present in the mesh.
Definition: mesh_base.h:2289
bool allow_find_neighbors() const
Definition: mesh_base.h:1353
The BoundaryInfo class contains information relevant to boundary conditions including storing faces...
Definition: boundary_info.h:57
libmesh_assert(ctx)
void libmesh_assert_valid_boundary_ids(const MeshBase &mesh)
A function for verifying that boundary condition ids match across processors.
Definition: mesh_tools.C:1788
bool _skip_find_neighbors
If this is true then we will skip find_neighbors in prepare_for_use.
Definition: mesh_base.h:2242
virtual void delete_node(Node *n)=0
Removes the Node n from the mesh.
std::pair< std::vector< unsigned int >, std::vector< unsigned int > > merge_extra_integer_names(const MeshBase &other)
Merge extra-integer arrays from an other mesh.
Definition: mesh_base.C:2345
const ConstElemRange & active_local_element_stored_range() const
Definition: mesh_base.C:1928
unsigned int n_elemsets() const
Returns the number of unique elemset ids which have been added via add_elemset_code(), which is the size of the _all_elemset_ids set.
Definition: mesh_base.C:482
Order _supported_nodal_order
We cache the maximum nodal order supported by all the mesh&#39;s elements (the minimum supported_nodal_or...
Definition: mesh_base.h:2295
std::set< unsigned char > _elem_dims
We cache the dimension of the elements present in the mesh.
Definition: mesh_base.h:2282
unsigned int get_node_integer_index(std::string_view name) const
Definition: mesh_base.C:778
std::string & subdomain_name(subdomain_id_type id)
Definition: mesh_base.C:1880
const std::set< unsigned char > & elem_dimensions() const
Definition: mesh_base.h:420
Helper for building element sides that minimizes the construction of new elements.
dof_id_type n_nodes_on_proc(const processor_id_type proc) const
Definition: mesh_base.C:1209
std::string get_local_constraints(bool print_nonlocal=false) const
Gets a string reporting all mesh constraint rows local to this processor.
Definition: mesh_base.C:2771
void size_node_extra_integers()
Size extra-integer arrays of all nodes in the mesh.
Definition: mesh_base.C:2336
An object whose state is distributed along a set of processors.
Real volume(const MeshBase &mesh, unsigned int dim=libMesh::invalid_uint)
Find the total volume of a mesh (interpreting that as area for dim = 2, or total arc length for dim =...
Definition: mesh_tools.C:1020
void regenerate_id_sets()
Clears and regenerates the cached sets of ids.
void prepare_for_use()
Definition: mesh_base.C:860
virtual void clear()
Deletes all the element and node data that is currently stored.
Definition: mesh_base.C:1029
Flags indicating in what ways a mesh has been prepared for use.
Definition: mesh_base.h:2047
std::unique_ptr< PeriodicBoundaries > _disjoint_neighbor_boundary_pairs
The disjoint neighbor boundary id pairs.
Definition: mesh_base.h:2086
void parallel_reduce(const Range &range, Body &body, unsigned int n_threads=libMesh::n_threads())
Execute the provided reduction operation in parallel on the specified range.
Definition: threads_none.h:109
std::string enum_to_string(const T e)
bool skip_partitioning() const
Definition: mesh_base.h:1421
std::map< GhostingFunctor *, std::shared_ptr< GhostingFunctor > > _shared_functors
Hang on to references to any GhostingFunctor objects we were passed in shared_ptr form...
Definition: mesh_base.h:2397
SimpleRange< NodeRefIter > node_ref_range()
Returns a range with all nodes of an element, usable in range-based for loops.
Definition: elem.h:2679
void complete_preparation()
Definition: mesh_base.C:874
unsigned int n_partitions() const
Definition: mesh_base.h:1516
Defines a Cartesian bounding box by the two corner extremum.
Definition: bounding_box.h:40
virtual const Elem * elem_ptr(const dof_id_type i) const =0
std::vector< dof_id_type > _node_integer_default_values
The array of default initialization values for integer data associated with each node in the mesh...
Definition: mesh_base.h:2356
Preparation _preparation
Flags indicating in what ways this mesh has been prepared.
Definition: mesh_base.h:2162
static const unsigned int next[3]
A lookup table for the increment modulo 3 operation, for iterating through the three nodes per elemen...
unsigned int recalculate_n_partitions()
In a few (very rare) cases, the user may have manually tagged the elements with specific processor ID...
Definition: mesh_base.C:1806
virtual void get_row(numeric_index_type i, std::vector< numeric_index_type > &indices, std::vector< T > &values) const =0
Get a row from the matrix.
std::set< elemset_id_type > elemset_type
Typedef for the "set" container used to store elemset ids.
Definition: mesh_base.h:456
std::vector< dof_id_type > _elem_integer_default_values
The array of default initialization values for integer data associated with each element in the mesh...
Definition: mesh_base.h:2344
subdomain_id_type n_subdomains() const
Definition: mesh_base.C:1183
std::string get_info(const unsigned int verbosity=0, const bool global=true) const
Definition: mesh_base.C:1268
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual const Elem * query_elem_ptr(const dof_id_type i) const =0
bool _skip_detect_interior_parents
If this is true then we will skip detect_interior_parents in prepare_for_use.
Definition: mesh_base.h:2247
void max(const T &r, T &o, Request &req) const
virtual unsigned short dim() const =0
void post_dofobject_moves(MeshBase &&other_mesh)
Moves any superclass data (e.g.
Definition: mesh_base.C:2356
Temporarily serialize a DistributedMesh for non-distributed-mesh capable code paths.
virtual void all_complete_order()
Calls the range-based version of this function with a range consisting of all elements in the mesh...
Definition: mesh_base.C:1801
virtual numeric_index_type row_start() const =0
unsigned int spatial_dimension() const
Definition: mesh_base.C:606
virtual std::unique_ptr< PeriodicBoundaryBase > clone(TransformationType t=FORWARD) const override
If we want the DofMap to be able to make copies of references and store them in the underlying map...
void send(const unsigned int dest_processor_id, const T &buf, const MessageTag &tag=no_tag) const
std::map< const Node *, constraint_rows_mapped_type > constraint_rows_type
Definition: mesh_base.h:1900
void print_constraint_rows(std::ostream &os=libMesh::out, bool print_nonlocal=false) const
Prints (from processor 0) all mesh constraint rows.
Definition: mesh_base.C:2743
const std::set< boundary_id_type > & get_edge_boundary_ids() const
virtual bool is_replicated() const
Definition: mesh_base.h:369
virtual const Elem & elem_ref(const dof_id_type i) const
Definition: mesh_base.h:778
void set_count_lower_dim_elems_in_point_locator(bool count_lower_dim_elems)
In the point locator, do we count lower dimensional elements when we refine point locator regions...
Definition: mesh_base.C:1866
virtual Real volume() const
Definition: elem.C:3462
MeshBase(const Parallel::Communicator &comm_in, unsigned char dim=1)
Constructor.
Definition: mesh_base.C:64
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition: int_range.h:176
void all_second_order(const bool full_ordered=true)
Calls the range-based version of this function with a range consisting of all elements in the mesh...
Definition: mesh_base.C:1796
dof_id_type n_active_sub_elem() const
Same as n_sub_elem(), but only counts active elements.
Definition: mesh_base.C:1256
The DofObject defines an abstract base class for objects that have degrees of freedom associated with...
Definition: dof_object.h:54
virtual ~MeshBase()
Destructor.
Definition: mesh_base.C:421
void set_elem_dimensions(std::set< unsigned char > elem_dims)
Most of the time you should not need to call this, as the element dimensions will be set automaticall...
Definition: mesh_base.C:439
unsigned int mesh_dimension() const
Definition: mesh_base.C:430
std::unique_ptr< GhostingFunctor > _default_ghosting
The default geometric GhostingFunctor, used to implement standard libMesh element ghosting behavior...
Definition: mesh_base.h:2382
void change_elemset_id(elemset_id_type old_id, elemset_id_type new_id)
Replace elemset id "old_id" with "new_id".
Definition: mesh_base.C:565
bool initialized()
Checks that library initialization has been done.
Definition: libmesh.C:324
unsigned int add_node_integer(std::string name, bool allocate_data=true, dof_id_type default_value=DofObject::invalid_id)
Register an integer datum (of type dof_id_type) to be added to each node in the mesh.
Definition: mesh_base.C:712
void sync_subdomain_name_map()
libMesh often expects all processors to know about names of all subdomain ids, but distributed mesh g...
Definition: mesh_base.C:2040
std::unique_ptr< ConstElemRange > _const_active_local_element_stored_range
A cached ConstElemRange for threaded calculation on all local elements of this mesh.
Definition: mesh_base.h:2183
virtual const Node & node_ref(const dof_id_type i) const
Definition: mesh_base.h:735
bool _skip_all_partitioning
If this is true then no partitioning should be done.
Definition: mesh_base.h:2230
bool on_command_line(std::string arg)
Definition: libmesh.C:934
bool allow_renumbering() const
Definition: mesh_base.h:1346
void cache_elem_dims()
Definition: mesh_base.C:1951
virtual void delete_remote_elements()
When supported, deletes all nonlocal elements of the mesh except for "ghosts" which touch a local ele...
Definition: mesh_base.h:389
void union_with(const Point &p)
Enlarges this bounding box to include the given point.
Definition: bounding_box.h:286
void remove_disjoint_boundary_pair(const boundary_id_type b1, const boundary_id_type b2)
Definition: mesh_base.C:2266
void reinit_ghosting_functors()
Loops over ghosting functors and calls mesh_reinit()
Definition: mesh_base.C:1018
virtual dof_id_type n_elem() const =0
virtual std::unique_ptr< Elem > build_edge_ptr(const unsigned int i)=0
virtual const Node * node_ptr(const dof_id_type i) const =0
void libmesh_assert_valid_unique_ids(const MeshBase &mesh)
A function for verifying that unique ids match across processors.
Definition: mesh_tools.C:1986
processor_id_type processor_id() const
static constexpr subdomain_id_type invalid_subdomain_id
A static integral constant representing an invalid subdomain id.
Definition: elem.h:246
virtual Order default_order() const =0
std::unique_ptr< PointLocatorBase > _point_locator
A PointLocator class for this mesh.
Definition: mesh_base.h:2192
bool operator!=(const Preparation &other) const
Definition: mesh_base.C:2870
virtual void redistribute()
Redistribute elements between processors.
Definition: mesh_base.C:1161
processor_id_type processor_id() const
Definition: dof_object.h:881
Real _point_locator_close_to_point_tol
If nonzero, we will call PointLocatorBase::set_close_to_point_tol() on any PointLocators that we crea...
Definition: mesh_base.h:2415
virtual ElemType type() const =0
static std::unique_ptr< PointLocatorBase > build(PointLocatorType t, const MeshBase &mesh, const PointLocatorBase *master=nullptr)
Builds an PointLocator for the mesh mesh.
bool _count_lower_dim_elems_in_point_locator
Do we count lower dimensional elements in point locator refinement? This is relevant in tree-based po...
Definition: mesh_base.h:2198
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
bool locally_equals(const MeshBase &other_mesh) const
This behaves the same as operator==, but only for the local and ghosted aspects of the mesh; i...
Definition: mesh_base.C:281
std::map< subdomain_id_type, std::string > _block_id_to_name
This structure maintains the mapping of named blocks for file formats that support named blocks...
Definition: mesh_base.h:2275
void unset_is_prepared()
Tells this we have done some operation where we should no longer consider ourself prepared...
Definition: mesh_base.C:1063
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition: int_range.h:153
virtual dof_id_type n_nodes() const =0
virtual void renumber_nodes_and_elements()=0
After partitioning a mesh it is useful to renumber the nodes and elements so that they lie in contigu...
This class implements the original default geometry ghosting requirements in libMesh: point neighbors...
void size_elem_extra_integers()
Size extra-integer arrays of all elements in the mesh.
Definition: mesh_base.C:2321
virtual numeric_index_type n() const =0
void add_ghosting_functor(GhostingFunctor &ghosting_functor)
Adds a functor which can specify ghosting requirements for use on distributed meshes.
Definition: mesh_base.C:1071
uint8_t dof_id_type
Definition: id_types.h:67
MeshBase & operator=(const MeshBase &)=delete
Copy and move assignment are not allowed because MeshBase subclasses manually manage memory (Elems an...
void set_point_locator_close_to_point_tol(Real val)
Set value used by PointLocatorBase::close_to_point_tol().
Definition: mesh_base.C:2300
void set_union(T &data, const unsigned int root_id) const