Line data Source code
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
64 333137 : MeshBase::MeshBase (const Parallel::Communicator & comm_in,
65 333137 : unsigned char d) :
66 : ParallelObject (comm_in),
67 333137 : boundary_info (new BoundaryInfo(*this)), // BoundaryInfo has protected ctor, can't use std::make_unique
68 313637 : _n_parts (1),
69 313637 : _default_mapping_type(LAGRANGE_MAP),
70 313637 : _default_mapping_data(0),
71 313637 : _preparation (),
72 313637 : _element_stored_range (),
73 313637 : _const_active_local_element_stored_range (),
74 313637 : _point_locator (),
75 313637 : _count_lower_dim_elems_in_point_locator(true),
76 313637 : _partitioner (),
77 : #ifdef LIBMESH_ENABLE_UNIQUE_ID
78 313637 : _next_unique_id(DofObject::invalid_unique_id),
79 : #endif
80 313637 : _interior_mesh(this),
81 313637 : _skip_noncritical_partitioning(false),
82 342971 : _skip_all_partitioning(libMesh::on_command_line("--skip-partitioning")),
83 313637 : _skip_renumber_nodes_and_elements(false),
84 313637 : _skip_find_neighbors(false),
85 313637 : _skip_detect_interior_parents(false),
86 313637 : _allow_remote_element_removal(true),
87 313637 : _allow_node_and_elem_unique_id_overlap(false),
88 313637 : _spatial_dimension(d),
89 333137 : _default_ghosting(std::make_unique<GhostPointNeighbors>(*this)),
90 1048077 : _point_locator_close_to_point_tol(0.)
91 : {
92 323471 : _elem_dims.insert(d);
93 333137 : _ghosting_functors.push_back(_default_ghosting.get());
94 9834 : libmesh_assert_less_equal (LIBMESH_DIM, 3);
95 9834 : libmesh_assert_greater_equal (LIBMESH_DIM, d);
96 9834 : libmesh_assert (libMesh::initialized());
97 333137 : }
98 :
99 :
100 :
101 36742 : MeshBase::MeshBase (const MeshBase & other_mesh) :
102 : ParallelObject (other_mesh),
103 36742 : boundary_info (new BoundaryInfo(*this)), // BoundaryInfo has protected ctor, can't use std::make_unique
104 36742 : _n_parts (other_mesh._n_parts),
105 36742 : _default_mapping_type(other_mesh._default_mapping_type),
106 36742 : _default_mapping_data(other_mesh._default_mapping_data),
107 22800 : _preparation (other_mesh._preparation),
108 22800 : _element_stored_range (),
109 22800 : _const_active_local_element_stored_range (),
110 22800 : _point_locator (),
111 36742 : _count_lower_dim_elems_in_point_locator(other_mesh._count_lower_dim_elems_in_point_locator),
112 22800 : _partitioner (),
113 : #ifdef LIBMESH_ENABLE_UNIQUE_ID
114 36742 : _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 36742 : _interior_mesh((other_mesh._interior_mesh == &other_mesh) ?
119 : this : other_mesh._interior_mesh),
120 36742 : _skip_noncritical_partitioning(other_mesh._skip_noncritical_partitioning),
121 36742 : _skip_all_partitioning(other_mesh._skip_all_partitioning),
122 36742 : _skip_renumber_nodes_and_elements(other_mesh._skip_renumber_nodes_and_elements),
123 36742 : _skip_find_neighbors(other_mesh._skip_find_neighbors),
124 36742 : _skip_detect_interior_parents(other_mesh._skip_detect_interior_parents),
125 36742 : _allow_remote_element_removal(other_mesh._allow_remote_element_removal),
126 36742 : _allow_node_and_elem_unique_id_overlap(other_mesh._allow_node_and_elem_unique_id_overlap),
127 13154 : _elem_dims(other_mesh._elem_dims),
128 13154 : _elem_default_orders(other_mesh._elem_default_orders),
129 36742 : _supported_nodal_order(other_mesh._supported_nodal_order),
130 13154 : _mesh_subdomains(other_mesh._mesh_subdomains),
131 13154 : _elemset_codes_inverse_map(other_mesh._elemset_codes_inverse_map),
132 13154 : _all_elemset_ids(other_mesh._all_elemset_ids),
133 36742 : _spatial_dimension(other_mesh._spatial_dimension),
134 36742 : _default_ghosting(std::make_unique<GhostPointNeighbors>(*this)),
135 149332 : _point_locator_close_to_point_tol(other_mesh._point_locator_close_to_point_tol)
136 : {
137 13154 : const GhostingFunctor * const other_default_ghosting = other_mesh._default_ghosting.get();
138 :
139 107550 : 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 70808 : if (gf == other_default_ghosting)
144 : {
145 36742 : _ghosting_functors.push_back(_default_ghosting.get());
146 36742 : continue;
147 : }
148 :
149 67116 : 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 34066 : if (clone_gf)
157 : {
158 34066 : clone_gf->set_mesh(this);
159 63734 : add_ghosting_functor(clone_gf);
160 : }
161 : else
162 : {
163 : libmesh_deprecated();
164 0 : add_ghosting_functor(*gf);
165 : }
166 : }
167 :
168 36742 : if (other_mesh._partitioner.get())
169 60188 : _partitioner = other_mesh._partitioner->clone();
170 :
171 : #ifdef LIBMESH_ENABLE_PERIODIC
172 : // Deep copy of all periodic boundaries
173 36742 : if (other_mesh._disjoint_neighbor_boundary_pairs)
174 : {
175 156 : _disjoint_neighbor_boundary_pairs = std::make_unique<PeriodicBoundaries>();
176 :
177 281 : for (const auto & [id, pb] : *other_mesh._disjoint_neighbor_boundary_pairs)
178 190 : if (pb)
179 328 : (*_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 36754 : for (const auto & [set, code] : _elemset_codes_inverse_map)
186 12 : _elemset_codes.emplace(code, &set);
187 36742 : }
188 :
189 426 : MeshBase& MeshBase::operator= (MeshBase && other_mesh)
190 : {
191 12 : LOG_SCOPE("operator=(&&)", "MeshBase");
192 :
193 : // Move assign as a ParallelObject.
194 12 : this->ParallelObject::operator=(other_mesh);
195 :
196 426 : _n_parts = other_mesh.n_partitions();
197 426 : _default_mapping_type = other_mesh.default_mapping_type();
198 426 : _default_mapping_data = other_mesh.default_mapping_data();
199 426 : _preparation = other_mesh._preparation;
200 12 : _element_stored_range = std::move(other_mesh._element_stored_range);
201 12 : _const_active_local_element_stored_range = std::move(other_mesh._const_active_local_element_stored_range);
202 12 : _point_locator = std::move(other_mesh._point_locator);
203 426 : _count_lower_dim_elems_in_point_locator = other_mesh.get_count_lower_dim_elems_in_point_locator();
204 : #ifdef LIBMESH_ENABLE_UNIQUE_ID
205 426 : _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 426 : _interior_mesh = (other_mesh._interior_mesh == &other_mesh) ?
210 : this : other_mesh._interior_mesh;
211 426 : _skip_noncritical_partitioning = other_mesh.skip_noncritical_partitioning();
212 426 : _skip_all_partitioning = other_mesh.skip_partitioning();
213 426 : _skip_renumber_nodes_and_elements = !(other_mesh.allow_renumbering());
214 426 : _skip_find_neighbors = !(other_mesh.allow_find_neighbors());
215 426 : _skip_detect_interior_parents = !(other_mesh.allow_detect_interior_parents());
216 426 : _allow_remote_element_removal = other_mesh.allow_remote_element_removal();
217 426 : _allow_node_and_elem_unique_id_overlap = other_mesh.allow_node_and_elem_unique_id_overlap();
218 12 : _block_id_to_name = std::move(other_mesh._block_id_to_name);
219 12 : _elem_dims = std::move(other_mesh.elem_dimensions());
220 12 : _elem_default_orders = std::move(other_mesh.elem_default_orders());
221 426 : _supported_nodal_order = other_mesh.supported_nodal_order();
222 12 : _mesh_subdomains = other_mesh._mesh_subdomains;
223 12 : _elemset_codes = std::move(other_mesh._elemset_codes);
224 12 : _elemset_codes_inverse_map = std::move(other_mesh._elemset_codes_inverse_map);
225 12 : _all_elemset_ids = std::move(other_mesh._all_elemset_ids);
226 426 : _spatial_dimension = other_mesh.spatial_dimension();
227 426 : _elem_integer_names = std::move(other_mesh._elem_integer_names);
228 426 : _elem_integer_default_values = std::move(other_mesh._elem_integer_default_values);
229 426 : _node_integer_names = std::move(other_mesh._node_integer_names);
230 426 : _node_integer_default_values = std::move(other_mesh._node_integer_default_values);
231 426 : _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 426 : if (other_mesh._disjoint_neighbor_boundary_pairs)
238 : {
239 0 : _disjoint_neighbor_boundary_pairs = std::make_unique<PeriodicBoundaries>();
240 :
241 0 : for (const auto & [id, pb] : *other_mesh._disjoint_neighbor_boundary_pairs)
242 0 : if (pb)
243 0 : (*_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 12 : boundary_info = std::move(other_mesh.boundary_info);
250 12 : boundary_info->set_mesh(*this);
251 :
252 : #ifdef DEBUG
253 : // Make sure that move assignment worked for pointers
254 12 : for (const auto & [set, code] : _elemset_codes_inverse_map)
255 : {
256 0 : auto it = _elemset_codes.find(code);
257 0 : libmesh_assert_msg(it != _elemset_codes.end(),
258 : "Elemset code " << code << " not found in _elmset_codes container.");
259 0 : 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 438 : return *this;
268 : }
269 :
270 :
271 27485 : bool MeshBase::operator== (const MeshBase & other_mesh) const
272 : {
273 13396 : LOG_SCOPE("operator==()", "MeshBase");
274 :
275 27485 : bool is_equal = this->locally_equals(other_mesh);
276 27485 : this->comm().min(is_equal);
277 40881 : return is_equal;
278 : }
279 :
280 :
281 12366 : void MeshBase::assert_equal_to (const MeshBase & other_mesh,
282 : std::string_view failure_context) const
283 : {
284 : #ifndef NDEBUG
285 24732 : LOG_SCOPE("assert_equal_to()", "MeshBase");
286 :
287 12366 : std::string_view local_diff = first_difference_from(other_mesh);
288 :
289 12366 : bool diff_found = !local_diff.empty();
290 12366 : this->comm().max(diff_found);
291 :
292 12366 : if (diff_found)
293 : {
294 : // Construct a user-friendly message to throw on pid 0
295 0 : std::set<std::string> unique_diffs;
296 0 : if (!local_diff.empty())
297 0 : unique_diffs.insert(std::string(local_diff));
298 0 : this->comm().set_union(unique_diffs);
299 :
300 0 : if (!this->processor_id())
301 : {
302 0 : std::string error_msg {failure_context};
303 0 : error_msg += "\nMeshes failed asserted equality in at least these aspects:\n";
304 0 : for (auto & diff : unique_diffs)
305 : {
306 0 : error_msg += diff;
307 0 : error_msg += '\n';
308 : }
309 0 : libmesh_assert_msg(!diff_found, error_msg);
310 : }
311 :
312 : // We're not going to throw on other processors because we don't
313 : // want to accidentally preempt pid 0's error message. We're
314 : // not even going to exit on other processors because for all we
315 : // know user code is going to catch that error and sync up with
316 : // us later.
317 : }
318 : #else
319 : libmesh_ignore(other_mesh, failure_context);
320 : #endif // NDEBUG
321 12366 : }
322 :
323 :
324 27485 : bool MeshBase::locally_equals (const MeshBase & other_mesh) const
325 : {
326 27485 : const std::string_view diff = first_difference_from(other_mesh);
327 41289 : return diff.empty();
328 : }
329 :
330 :
331 39851 : std::string_view MeshBase::first_difference_from(const MeshBase & other_mesh) const
332 : {
333 : // Check whether (almost) everything in the base is equal
334 : //
335 : // We don't check _next_unique_id here, because it's expected to
336 : // change in a DistributedMesh prepare_for_use(); it's conceptually
337 : // "mutable".
338 : //
339 : // We use separate tests here and return strings for each test,
340 : // to make it easy to see the failing condition a
341 : // MeshTools::libmesh_valid_is_prepared(mesh) is failing.
342 :
343 : #define CHECK_MEMBER(member_name) \
344 : if (member_name != other_mesh.member_name) \
345 : return #member_name;
346 :
347 39851 : CHECK_MEMBER(_n_parts);
348 39851 : CHECK_MEMBER(_default_mapping_type);
349 39851 : CHECK_MEMBER(_default_mapping_data);
350 39851 : CHECK_MEMBER(_preparation);
351 39851 : CHECK_MEMBER(_count_lower_dim_elems_in_point_locator);
352 :
353 : // We should either both have our own interior parents or both not;
354 : // but if we both don't then we can't really assert anything else
355 : // because pointing at the same interior mesh is fair but so is
356 : // pointing at two different copies of "the same" interior mesh.
357 66021 : if ((_interior_mesh == this) !=
358 39851 : (other_mesh._interior_mesh == &other_mesh))
359 0 : return "_interior_mesh";
360 :
361 39851 : CHECK_MEMBER(_skip_noncritical_partitioning);
362 39851 : CHECK_MEMBER(_skip_all_partitioning);
363 39851 : CHECK_MEMBER(_skip_renumber_nodes_and_elements);
364 39851 : CHECK_MEMBER(_skip_find_neighbors);
365 39851 : CHECK_MEMBER(_skip_detect_interior_parents);
366 39851 : CHECK_MEMBER(_allow_remote_element_removal);
367 39851 : CHECK_MEMBER(_allow_node_and_elem_unique_id_overlap);
368 39851 : CHECK_MEMBER(_spatial_dimension);
369 39851 : CHECK_MEMBER(_point_locator_close_to_point_tol);
370 39851 : CHECK_MEMBER(_block_id_to_name);
371 39851 : CHECK_MEMBER(_elem_dims);
372 39851 : CHECK_MEMBER(_elem_default_orders);
373 39851 : CHECK_MEMBER(_supported_nodal_order);
374 39857 : CHECK_MEMBER(_mesh_subdomains);
375 39638 : CHECK_MEMBER(_all_elemset_ids);
376 39638 : CHECK_MEMBER(_elem_integer_names);
377 40040 : CHECK_MEMBER(_elem_integer_default_values);
378 39638 : CHECK_MEMBER(_node_integer_names);
379 40040 : CHECK_MEMBER(_node_integer_default_values);
380 :
381 39638 : if (static_cast<bool>(_default_ghosting) != static_cast<bool>(other_mesh._default_ghosting))
382 0 : return "_default_ghosting";
383 39638 : if (static_cast<bool>(_partitioner) != static_cast<bool>(other_mesh._partitioner))
384 0 : return "_partitioner";
385 39638 : if (*boundary_info != *other_mesh.boundary_info)
386 213 : return "boundary_info";
387 :
388 : // First check whether the "existence" of the two pointers differs (one present, one absent)
389 65175 : if (static_cast<bool>(_disjoint_neighbor_boundary_pairs) !=
390 25750 : static_cast<bool>(other_mesh._disjoint_neighbor_boundary_pairs))
391 0 : return "_disjoint_neighbor_boundary_pairs existence";
392 : // If both exist, compare the contents (Weak Test: just compare sizes like `_ghosting_functors`)
393 39469 : if (_disjoint_neighbor_boundary_pairs &&
394 25794 : (_disjoint_neighbor_boundary_pairs->size() != other_mesh._disjoint_neighbor_boundary_pairs->size()))
395 0 : return "_disjoint_neighbor_boundary_pairs size";
396 :
397 : const constraint_rows_type & other_rows =
398 25750 : other_mesh.get_constraint_rows();
399 115421 : for (const auto & [node, row] : this->_constraint_rows)
400 : {
401 75996 : const dof_id_type node_id = node->id();
402 75996 : const Node * other_node = other_mesh.query_node_ptr(node_id);
403 75996 : if (!other_node)
404 0 : return "_constraint_rows node presence";
405 :
406 30776 : auto it = other_rows.find(other_node);
407 75996 : if (it == other_rows.end())
408 0 : return "_constraint_rows row presence";
409 :
410 30776 : const auto & other_row = it->second;
411 82412 : if (row.size() != other_row.size())
412 0 : return "_constraint_rows row size";
413 :
414 312149 : for (auto i : index_range(row))
415 : {
416 73992 : const auto & [elem_pair, coef] = row[i];
417 73992 : const auto & [other_elem_pair, other_coef] = other_row[i];
418 73992 : libmesh_assert(elem_pair.first);
419 73992 : libmesh_assert(other_elem_pair.first);
420 236153 : if (elem_pair.first->id() !=
421 398314 : other_elem_pair.first->id() ||
422 236153 : elem_pair.second !=
423 310145 : other_elem_pair.second ||
424 236153 : coef != other_coef)
425 0 : return "_constraint_rows row entry";
426 : }
427 : }
428 :
429 39449 : for (const auto & [elemset_code, elemset_ptr] : this->_elemset_codes)
430 24 : if (const auto it = other_mesh._elemset_codes.find(elemset_code);
431 24 : it == other_mesh._elemset_codes.end() || *elemset_ptr != *it->second)
432 0 : return "_elemset_codes";
433 :
434 : // FIXME: we have no good way to compare ghosting functors, since
435 : // they're in a vector of pointers, and we have no way *at all*
436 : // to compare ghosting functors, since they don't have operator==
437 : // defined and we encourage users to subclass them. We can check if
438 : // we have the same number, is all.
439 65571 : if (_ghosting_functors.size() !=
440 26146 : other_mesh._ghosting_functors.size())
441 0 : return "_ghosting_functors size";
442 :
443 : // Same deal for partitioners. We tested that we both have one or
444 : // both don't, but are they equivalent? Let's guess "yes".
445 :
446 : // Now let the subclasses decide whether everything else is equal
447 39425 : return this->subclass_first_difference_from(other_mesh);
448 : }
449 :
450 :
451 380333 : MeshBase::~MeshBase()
452 : {
453 369879 : this->MeshBase::clear();
454 :
455 22988 : libmesh_exceptionless_assert (!libMesh::closed());
456 1379190 : }
457 :
458 :
459 :
460 11125593 : unsigned int MeshBase::mesh_dimension() const
461 : {
462 11125593 : if (!_elem_dims.empty())
463 11125593 : return cast_int<unsigned int>(*_elem_dims.rbegin());
464 0 : return 0;
465 : }
466 :
467 :
468 :
469 0 : void MeshBase::set_elem_dimensions(std::set<unsigned char> elem_dims)
470 : {
471 : #ifdef DEBUG
472 : // In debug mode, we call cache_elem_data() and then make sure
473 : // the result actually agrees with what the user specified.
474 0 : parallel_object_only();
475 :
476 0 : this->cache_elem_data();
477 0 : libmesh_assert_msg(_elem_dims == elem_dims, \
478 : "Specified element dimensions does not match true element dimensions!");
479 : #endif
480 :
481 0 : _elem_dims = std::move(elem_dims);
482 0 : }
483 :
484 :
485 :
486 1775 : void MeshBase::add_elemset_code(dof_id_type code, MeshBase::elemset_type id_set)
487 : {
488 : // Populate inverse map, stealing id_set's resources
489 1725 : auto [it1, inserted1] = _elemset_codes_inverse_map.emplace(std::move(id_set), code);
490 :
491 : // Reference to the newly inserted (or previously existing) id_set
492 1775 : const auto & inserted_id_set = it1->first;
493 :
494 : // Keep track of all elemset ids ever added for O(1) n_elemsets()
495 : // performance. Only need to do this if we didn't know about this
496 : // id_set before...
497 1775 : if (inserted1)
498 1775 : _all_elemset_ids.insert(inserted_id_set.begin(), inserted_id_set.end());
499 :
500 : // Take the address of the newly emplaced set to use in
501 : // _elemset_codes, avoid duplicating std::set storage
502 1775 : auto [it2, inserted2] = _elemset_codes.emplace(code, &inserted_id_set);
503 :
504 : // Throw an error if this code already exists with a pointer to a
505 : // different set of ids.
506 1775 : libmesh_error_msg_if(!inserted2 && it2->second != &inserted_id_set,
507 : "The elemset code " << code << " already exists with a different id_set.");
508 1775 : }
509 :
510 :
511 :
512 15010 : unsigned int MeshBase::n_elemsets() const
513 : {
514 15010 : return _all_elemset_ids.size();
515 : }
516 :
517 2963 : void MeshBase::get_elemsets(dof_id_type elemset_code, MeshBase::elemset_type & id_set_to_fill) const
518 : {
519 : // If we don't recognize this elemset_code, hand back an empty set
520 102 : id_set_to_fill.clear();
521 :
522 2963 : if (const auto it = _elemset_codes.find(elemset_code);
523 102 : it != _elemset_codes.end())
524 1386 : id_set_to_fill.insert(it->second->begin(), it->second->end());
525 2963 : }
526 :
527 852 : dof_id_type MeshBase::get_elemset_code(const MeshBase::elemset_type & id_set) const
528 : {
529 24 : auto it = _elemset_codes_inverse_map.find(id_set);
530 852 : return (it == _elemset_codes_inverse_map.end()) ? DofObject::invalid_id : it->second;
531 : }
532 :
533 8453 : std::vector<dof_id_type> MeshBase::get_elemset_codes() const
534 : {
535 240 : std::vector<dof_id_type> ret;
536 8453 : ret.reserve(_elemset_codes.size());
537 9234 : for (const auto & pr : _elemset_codes)
538 781 : ret.push_back(pr.first);
539 8453 : return ret;
540 : }
541 :
542 284 : void MeshBase::change_elemset_code(dof_id_type old_code, dof_id_type new_code)
543 : {
544 : // Look up elemset ids for old_code
545 8 : auto it = _elemset_codes.find(old_code);
546 :
547 : // If we don't have the old_code, then do nothing. Alternatively, we
548 : // could throw an error since trying to change an elemset code you
549 : // don't have could indicate there's a problem...
550 284 : if (it == _elemset_codes.end())
551 0 : return;
552 :
553 : // Make copy of the set of elemset ids. We are not changing these,
554 : // only updating the elemset code it corresponds to.
555 284 : elemset_type id_set_copy = *(it->second);
556 :
557 : // Look up the corresponding entry in the inverse map. Note: we want
558 : // the iterator because we are going to remove it.
559 8 : auto inverse_it = _elemset_codes_inverse_map.find(id_set_copy);
560 284 : libmesh_error_msg_if(inverse_it == _elemset_codes_inverse_map.end(),
561 : "Expected _elemset_codes_inverse_map entry for elemset code " << old_code);
562 :
563 : // Erase entry from inverse map
564 276 : _elemset_codes_inverse_map.erase(inverse_it);
565 :
566 : // Erase entry from forward map
567 276 : _elemset_codes.erase(it);
568 :
569 : // Add new code with original set of ids.
570 560 : this->add_elemset_code(new_code, id_set_copy);
571 :
572 : // We can't update any actual elemset codes if there is no extra integer defined for it.
573 284 : if (!this->has_elem_integer("elemset_code"))
574 0 : return;
575 :
576 : // Get index of elemset_code extra integer
577 284 : unsigned int elemset_index = this->get_elem_integer_index("elemset_code");
578 :
579 : // Loop over all elems and update code
580 : Threads::parallel_for
581 284 : (this->element_stored_range(),
582 1240 : [elemset_index, old_code, new_code](const ElemRange & range)
583 : {
584 5254 : for (Elem * elem : range)
585 : {
586 : dof_id_type elemset_code =
587 4970 : elem->get_extra_integer(elemset_index);
588 :
589 4970 : if (elemset_code == old_code)
590 2415 : elem->set_extra_integer(elemset_index, new_code);
591 : }
592 284 : });
593 : }
594 :
595 284 : void MeshBase::change_elemset_id(elemset_id_type old_id, elemset_id_type new_id)
596 : {
597 : // Early return if we don't have old_id
598 8 : if (!_all_elemset_ids.count(old_id))
599 0 : return;
600 :
601 : // Throw an error if the new_id is already used
602 8 : libmesh_error_msg_if(_all_elemset_ids.count(new_id),
603 : "Cannot change elemset id " << old_id <<
604 : " to " << new_id << ", " << new_id << " already exists.");
605 :
606 : // We will build up a new version of the inverse map so we can iterate over
607 : // the current one without invalidating anything.
608 16 : std::map<MeshBase::elemset_type, dof_id_type> new_elemset_codes_inverse_map;
609 852 : for (const auto & [id_set, elemset_code] : _elemset_codes_inverse_map)
610 : {
611 32 : auto id_set_copy = id_set;
612 16 : if (id_set_copy.count(old_id))
613 : {
614 : // Remove old_id, insert new_id
615 8 : id_set_copy.erase(old_id);
616 276 : id_set_copy.insert(new_id);
617 : }
618 :
619 : // Store in new version of map
620 552 : new_elemset_codes_inverse_map.emplace(id_set_copy, elemset_code);
621 : }
622 :
623 : // Swap existing map with newly-built one
624 8 : _elemset_codes_inverse_map.swap(new_elemset_codes_inverse_map);
625 :
626 : // Reconstruct _elemset_codes map
627 8 : _elemset_codes.clear();
628 852 : for (const auto & [id_set, elemset_code] : _elemset_codes_inverse_map)
629 568 : _elemset_codes.emplace(elemset_code, &id_set);
630 :
631 : // Update _all_elemset_ids
632 8 : _all_elemset_ids.erase(old_id);
633 276 : _all_elemset_ids.insert(new_id);
634 : }
635 :
636 :
637 :
638 279715 : void MeshBase::set_spatial_dimension(unsigned char d)
639 : {
640 : // The user can set the _spatial_dimension however they wish,
641 : // libMesh will only *increase* the spatial dimension, however,
642 : // never decrease it.
643 279715 : _spatial_dimension = d;
644 279715 : }
645 :
646 :
647 :
648 4716 : unsigned int MeshBase::add_elem_integer(std::string name,
649 : bool allocate_data,
650 : dof_id_type default_value)
651 : {
652 5912 : for (auto i : index_range(_elem_integer_names))
653 1416 : if (_elem_integer_names[i] == name)
654 : {
655 8 : libmesh_assert_less(i, _elem_integer_default_values.size());
656 284 : _elem_integer_default_values[i] = default_value;
657 284 : return i;
658 : }
659 :
660 136 : libmesh_assert_equal_to(_elem_integer_names.size(),
661 : _elem_integer_default_values.size());
662 4432 : _elem_integer_names.push_back(std::move(name));
663 4432 : _elem_integer_default_values.push_back(default_value);
664 4432 : if (allocate_data)
665 3844 : this->size_elem_extra_integers();
666 4568 : return _elem_integer_names.size()-1;
667 : }
668 :
669 :
670 :
671 50547 : std::vector<unsigned int> MeshBase::add_elem_integers(const std::vector<std::string> & names,
672 : bool allocate_data,
673 : const std::vector<dof_id_type> * default_values)
674 : {
675 13558 : libmesh_assert(!default_values || default_values->size() == names.size());
676 13558 : libmesh_assert_equal_to(_elem_integer_names.size(), _elem_integer_default_values.size());
677 :
678 27116 : std::unordered_map<std::string, std::size_t> name_indices;
679 51399 : for (auto i : index_range(_elem_integer_names))
680 852 : name_indices[_elem_integer_names[i]] = i;
681 :
682 51739 : std::vector<unsigned int> returnval(names.size());
683 :
684 13558 : bool added_an_integer = false;
685 53252 : for (auto i : index_range(names))
686 : {
687 356 : const std::string & name = names[i];
688 2705 : if (const auto it = name_indices.find(name);
689 278 : it != name_indices.end())
690 : {
691 426 : returnval[i] = it->second;
692 438 : _elem_integer_default_values[it->second] =
693 426 : default_values ? (*default_values)[i] : DofObject::invalid_id;
694 : }
695 : else
696 : {
697 2345 : returnval[i] = _elem_integer_names.size();
698 2279 : name_indices[name] = returnval[i];
699 2279 : _elem_integer_names.push_back(name);
700 : _elem_integer_default_values.push_back
701 3083 : (default_values ? (*default_values)[i] : DofObject::invalid_id);
702 266 : added_an_integer = true;
703 : }
704 : }
705 :
706 50547 : if (allocate_data && added_an_integer)
707 1201 : this->size_elem_extra_integers();
708 :
709 64105 : return returnval;
710 : }
711 :
712 :
713 :
714 3278 : unsigned int MeshBase::get_elem_integer_index(std::string_view name) const
715 : {
716 4130 : for (auto i : index_range(_elem_integer_names))
717 4037 : if (_elem_integer_names[i] == name)
718 3278 : return i;
719 :
720 0 : libmesh_error_msg("Unknown elem integer " << name);
721 : return libMesh::invalid_uint;
722 : }
723 :
724 :
725 :
726 8539 : bool MeshBase::has_elem_integer(std::string_view name) const
727 : {
728 9533 : for (auto & entry : _elem_integer_names)
729 2367 : if (entry == name)
730 40 : return true;
731 :
732 632 : return false;
733 : }
734 :
735 :
736 :
737 16916 : unsigned int MeshBase::add_node_integer(std::string name,
738 : bool allocate_data,
739 : dof_id_type default_value)
740 : {
741 27329 : for (auto i : index_range(_node_integer_names))
742 10208 : if (_node_integer_names[i] == name)
743 : {
744 16 : libmesh_assert_less(i, _node_integer_default_values.size());
745 568 : _node_integer_default_values[i] = default_value;
746 568 : return i;
747 : }
748 :
749 702 : libmesh_assert_equal_to(_node_integer_names.size(),
750 : _node_integer_default_values.size());
751 16348 : _node_integer_names.push_back(std::move(name));
752 16348 : _node_integer_default_values.push_back(default_value);
753 16348 : if (allocate_data)
754 7120 : this->size_node_extra_integers();
755 17050 : return _node_integer_names.size()-1;
756 : }
757 :
758 :
759 :
760 50405 : std::vector<unsigned int> MeshBase::add_node_integers(const std::vector<std::string> & names,
761 : bool allocate_data,
762 : const std::vector<dof_id_type> * default_values)
763 : {
764 13554 : libmesh_assert(!default_values || default_values->size() == names.size());
765 13554 : libmesh_assert_equal_to(_node_integer_names.size(), _node_integer_default_values.size());
766 :
767 27108 : std::unordered_map<std::string, std::size_t> name_indices;
768 50973 : for (auto i : index_range(_node_integer_names))
769 568 : name_indices[_node_integer_names[i]] = i;
770 :
771 51593 : std::vector<unsigned int> returnval(names.size());
772 :
773 13554 : bool added_an_integer = false;
774 53729 : for (auto i : index_range(names))
775 : {
776 884 : const std::string & name = names[i];
777 3324 : if (const auto it = name_indices.find(name);
778 788 : it != name_indices.end())
779 : {
780 0 : returnval[i] = it->second;
781 0 : _node_integer_default_values[it->second] =
782 0 : default_values ? (*default_values)[i] : DofObject::invalid_id;
783 : }
784 : else
785 : {
786 3420 : returnval[i] = _node_integer_names.size();
787 3324 : name_indices[name] = returnval[i];
788 3324 : _node_integer_names.push_back(name);
789 : _node_integer_default_values.push_back
790 4416 : (default_values ? (*default_values)[i] : DofObject::invalid_id);
791 788 : added_an_integer = true;
792 : }
793 : }
794 :
795 50405 : if (allocate_data && added_an_integer)
796 1218 : this->size_node_extra_integers();
797 :
798 63959 : return returnval;
799 : }
800 :
801 :
802 :
803 710 : unsigned int MeshBase::get_node_integer_index(std::string_view name) const
804 : {
805 1988 : for (auto i : index_range(_node_integer_names))
806 1968 : if (_node_integer_names[i] == name)
807 710 : return i;
808 :
809 0 : libmesh_error_msg("Unknown node integer " << name);
810 : return libMesh::invalid_uint;
811 : }
812 :
813 :
814 :
815 1136 : bool MeshBase::has_node_integer(std::string_view name) const
816 : {
817 3134 : for (auto & entry : _node_integer_names)
818 3134 : if (entry == name)
819 32 : return true;
820 :
821 0 : return false;
822 : }
823 :
824 :
825 :
826 54823 : void MeshBase::remove_orphaned_nodes ()
827 : {
828 27620 : LOG_SCOPE("remove_orphaned_nodes()", "MeshBase");
829 :
830 : // Will hold the set of nodes that are currently connected to elements
831 13810 : std::unordered_set<Node *> connected_nodes;
832 :
833 : // Loop over the elements. Find which nodes are connected to at
834 : // least one of them.
835 5243898 : for (const auto & element : this->element_ptr_range())
836 30102892 : for (auto & n : element->node_ref_range())
837 26624281 : connected_nodes.insert(&n);
838 :
839 13706810 : for (const auto & node : this->node_ptr_range())
840 3213420 : if (!connected_nodes.count(node))
841 122761 : this->delete_node(node);
842 :
843 54823 : _preparation.has_removed_orphaned_nodes = true;
844 54823 : }
845 :
846 :
847 :
848 : #ifdef LIBMESH_ENABLE_DEPRECATED
849 0 : void MeshBase::prepare_for_use (const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
850 : {
851 : libmesh_deprecated();
852 :
853 : // We only respect the users wish if they tell us to skip renumbering. If they tell us not to
854 : // skip renumbering but someone previously called allow_renumbering(false), then the latter takes
855 : // precedence
856 0 : if (skip_renumber_nodes_and_elements)
857 0 : this->allow_renumbering(false);
858 :
859 : // We always accept the user's value for skip_find_neighbors, in contrast to skip_renumber
860 0 : const bool old_allow_find_neighbors = this->allow_find_neighbors();
861 0 : this->allow_find_neighbors(!skip_find_neighbors);
862 :
863 0 : this->prepare_for_use();
864 :
865 0 : this->allow_find_neighbors(old_allow_find_neighbors);
866 0 : }
867 :
868 0 : void MeshBase::prepare_for_use (const bool skip_renumber_nodes_and_elements)
869 : {
870 : libmesh_deprecated();
871 :
872 : // We only respect the users wish if they tell us to skip renumbering. If they tell us not to
873 : // skip renumbering but someone previously called allow_renumbering(false), then the latter takes
874 : // precedence
875 0 : if (skip_renumber_nodes_and_elements)
876 0 : this->allow_renumbering(false);
877 :
878 0 : this->prepare_for_use();
879 0 : }
880 : #endif // LIBMESH_ENABLE_DEPRECATED
881 :
882 :
883 :
884 :
885 448386 : void MeshBase::prepare_for_use ()
886 : {
887 : // Mark everything as unprepared, except for those things we've been
888 : // told we don't need to prepare, for backwards compatibility
889 448386 : this->clear_point_locator();
890 448386 : this->clear_stored_ranges();
891 448386 : _preparation = false;
892 448386 : _preparation.has_neighbor_ptrs = _skip_find_neighbors;
893 448386 : _preparation.has_removed_remote_elements = !_allow_remote_element_removal;
894 :
895 448386 : this->complete_preparation();
896 448386 : }
897 :
898 :
899 645929 : void MeshBase::complete_preparation()
900 : {
901 62200 : LOG_SCOPE("complete_preparation()", "MeshBase");
902 :
903 31100 : parallel_object_only();
904 :
905 31100 : libmesh_assert(this->comm().verify(this->is_serial()));
906 :
907 645929 : _preparation.libmesh_assert_consistent(this->comm());
908 :
909 : #ifdef DEBUG
910 : // If we don't go into this method with valid constraint rows, we're
911 : // only going to be able to make that worse.
912 31100 : MeshTools::libmesh_assert_valid_constraint_rows(*this);
913 :
914 : // If this mesh thinks it's already partially prepared, then in
915 : // optimized builds we'll trust it, but in debug builds we'll check.
916 31100 : const bool was_partly_prepared = (_preparation == Preparation());
917 : #endif
918 :
919 : // A distributed mesh may have processors with no elements (or
920 : // processors with no elements of higher dimension, if we ever
921 : // support mixed-dimension meshes), but we want consistent
922 : // mesh_dimension anyways.
923 : //
924 : // cache_elem_data() should get the elem_dimensions() and
925 : // mesh_dimension() correct later, and we don't need it earlier.
926 :
927 :
928 : // Renumber the nodes and elements so that they in contiguous
929 : // blocks. By default, _skip_renumber_nodes_and_elements is false.
930 : //
931 : // Instances where you if prepare_for_use() should not renumber the nodes
932 : // and elements include reading in e.g. an xda/r or gmv file. In
933 : // this case, the ordering of the nodes may depend on an accompanying
934 : // solution, and the node ordering cannot be changed.
935 :
936 :
937 : // Mesh modification operations might not leave us with consistent
938 : // id counts, or might leave us with orphaned nodes we're no longer
939 : // using, but our partitioner might need that consistency and/or
940 : // might be confused by orphaned nodes.
941 645929 : if (!_skip_renumber_nodes_and_elements)
942 : {
943 588692 : if (!_preparation.has_removed_orphaned_nodes ||
944 194916 : !_preparation.has_synched_id_counts)
945 393776 : this->renumber_nodes_and_elements();
946 : }
947 : else
948 : {
949 57237 : if (!_preparation.has_removed_orphaned_nodes)
950 54823 : this->remove_orphaned_nodes();
951 57237 : if (!_preparation.has_synched_id_counts)
952 54823 : this->update_parallel_id_counts();
953 : }
954 :
955 : // Let all the elements find their neighbors
956 645929 : if (!_skip_find_neighbors && !_preparation.has_neighbor_ptrs)
957 436921 : this->find_neighbors();
958 :
959 : // The user may have set boundary conditions. We require that the
960 : // boundary conditions were set consistently. Because we examine
961 : // neighbors when evaluating non-raw boundary condition IDs, this
962 : // assert is only valid when our neighbor links are in place.
963 : #ifdef DEBUG
964 31100 : MeshTools::libmesh_assert_valid_boundary_ids(*this);
965 : #endif
966 :
967 : // Search the mesh for all the dimensions of the elements
968 : // and cache them.
969 645929 : if (!_preparation.has_cached_elem_data)
970 634782 : this->cache_elem_data();
971 :
972 : // libMesh expects every processor to know about every subdomain
973 : // name, but distributed mesh generators may only have set names for
974 : // the part of the mesh they know about, so make sure the map is
975 : // consistent everywhere.
976 645929 : if (!_preparation.has_synched_subdomain_name_map)
977 448386 : this->sync_subdomain_name_map();
978 :
979 : // Search the mesh for elements that have a neighboring element
980 : // of dim+1 and set that element as the interior parent
981 645929 : if (!_preparation.has_interior_parent_ptrs)
982 : {
983 448386 : if (!_skip_detect_interior_parents)
984 448386 : this->detect_interior_parents();
985 : else
986 : {
987 : // We must set the flag that says "interior parent pointers have been set up"
988 : // even though we skip detect_interior_parents().
989 0 : _preparation.has_interior_parent_ptrs = true;
990 : }
991 : }
992 :
993 : // Fix up node unique ids in case mesh generation code didn't take
994 : // exceptional care to do so.
995 : // MeshCommunication().make_node_unique_ids_parallel_consistent(*this);
996 :
997 : // We're going to still require that mesh generation code gets
998 : // element unique ids consistent.
999 : #if defined(DEBUG) && defined(LIBMESH_ENABLE_UNIQUE_ID)
1000 31100 : MeshTools::libmesh_assert_valid_unique_ids(*this);
1001 : #endif
1002 :
1003 : // Allow our GhostingFunctor objects to reinit if necessary.
1004 : // Do this before partitioning and redistributing, and before
1005 : // deleting remote elements.
1006 645929 : if (!_preparation.has_reinit_ghosting_functors)
1007 448599 : this->reinit_ghosting_functors();
1008 :
1009 : // Partition the mesh unless *all* partitioning is to be skipped.
1010 : // If only noncritical partitioning is to be skipped, the
1011 : // partition() call will still check for orphaned nodes.
1012 645929 : if (!skip_partitioning() && !_preparation.is_partitioned)
1013 13094 : this->partition();
1014 426486 : else if (!this->n_unpartitioned_elem() &&
1015 18006 : !this->n_unpartitioned_nodes())
1016 213243 : _preparation.is_partitioned = true;
1017 :
1018 : // If we're using DistributedMesh, we'll probably want it
1019 : // parallelized.
1020 645929 : if (this->_allow_remote_element_removal &&
1021 621282 : !_preparation.has_removed_remote_elements)
1022 381665 : this->delete_remote_elements();
1023 : else
1024 264264 : _preparation.has_removed_remote_elements = true;
1025 :
1026 : // Much of our boundary info may have been for now-remote parts of the mesh,
1027 : // in which case we don't want to keep local copies of data meant to be
1028 : // local. On the other hand we may have deleted, or the user may have added in
1029 : // a distributed fashion, boundary data that is meant to be global. So we
1030 : // handle both of those scenarios here
1031 645929 : if (!_preparation.has_boundary_id_sets)
1032 102256 : this->get_boundary_info().regenerate_id_sets();
1033 :
1034 645929 : if (!_skip_renumber_nodes_and_elements)
1035 588692 : this->renumber_nodes_and_elements();
1036 :
1037 : // The mesh is now prepared for use, with the possible exception of
1038 : // partitioning that was supposed to be skipped, and it should know
1039 : // it.
1040 : #ifndef NDEBUG
1041 31100 : Preparation completed_preparation = _preparation;
1042 31100 : if (skip_partitioning())
1043 12534 : completed_preparation.is_partitioned = true;
1044 31100 : libmesh_assert(completed_preparation);
1045 : #endif
1046 :
1047 : #ifdef DEBUG
1048 : // The if() here avoids both unnecessary work *and* stack overflow
1049 31100 : if (was_partly_prepared)
1050 12350 : MeshTools::libmesh_assert_valid_is_prepared(*this);
1051 :
1052 31100 : MeshTools::libmesh_assert_valid_boundary_ids(*this);
1053 : #ifdef LIBMESH_ENABLE_UNIQUE_ID
1054 31100 : MeshTools::libmesh_assert_valid_unique_ids(*this);
1055 : #endif
1056 : #endif
1057 645929 : }
1058 :
1059 : void
1060 448599 : MeshBase::reinit_ghosting_functors()
1061 : {
1062 1010170 : for (auto & gf : _ghosting_functors)
1063 : {
1064 32470 : libmesh_assert(gf);
1065 561571 : gf->mesh_reinit();
1066 : }
1067 :
1068 448599 : _preparation.has_reinit_ghosting_functors = true;
1069 448599 : }
1070 :
1071 1042736 : void MeshBase::clear ()
1072 : {
1073 : // Reset the number of partitions
1074 1042736 : _n_parts = 1;
1075 :
1076 : // Reset the preparation flags
1077 1042736 : _preparation = false;
1078 :
1079 : // Clear boundary information
1080 1042736 : if (boundary_info)
1081 1041458 : boundary_info->clear();
1082 :
1083 : // Clear cached element data
1084 55449 : _elem_dims.clear();
1085 55449 : _elem_default_orders.clear();
1086 1042736 : _supported_nodal_order = MAXIMUM;
1087 :
1088 55449 : _elemset_codes.clear();
1089 55449 : _elemset_codes_inverse_map.clear();
1090 :
1091 55449 : _constraint_rows.clear();
1092 :
1093 : // Clear our point locator.
1094 1042736 : this->clear_point_locator();
1095 1042736 : this->clear_stored_ranges();
1096 1042736 : }
1097 :
1098 :
1099 577006 : bool MeshBase::is_prepared() const
1100 : {
1101 577006 : return static_cast<bool>(_preparation);
1102 : }
1103 :
1104 :
1105 568 : void MeshBase::unset_is_prepared()
1106 : {
1107 568 : _preparation = false;
1108 568 : this->clear_point_locator();
1109 568 : this->clear_stored_ranges();
1110 568 : }
1111 :
1112 :
1113 1008223 : void MeshBase::add_ghosting_functor(GhostingFunctor & ghosting_functor)
1114 : {
1115 : // We used to implicitly support duplicate inserts to std::set
1116 : #ifdef LIBMESH_ENABLE_DEPRECATED
1117 : _ghosting_functors.erase
1118 947485 : (std::remove(_ghosting_functors.begin(),
1119 : _ghosting_functors.end(),
1120 1008223 : &ghosting_functor),
1121 92798 : _ghosting_functors.end());
1122 : #endif
1123 :
1124 : // We shouldn't have two copies of the same functor
1125 32060 : libmesh_assert(std::find(_ghosting_functors.begin(),
1126 : _ghosting_functors.end(),
1127 : &ghosting_functor) ==
1128 : _ghosting_functors.end());
1129 :
1130 1008223 : _ghosting_functors.push_back(&ghosting_functor);
1131 1008223 : }
1132 :
1133 :
1134 :
1135 974299 : void MeshBase::remove_ghosting_functor(GhostingFunctor & ghosting_functor)
1136 : {
1137 918967 : auto raw_it = std::find(_ghosting_functors.begin(),
1138 1001965 : _ghosting_functors.end(), &ghosting_functor);
1139 :
1140 : // The DofMap has a "to_mesh" parameter that tells it to avoid
1141 : // registering a new functor with the mesh, but it doesn't keep
1142 : // track of which functors weren't added, so we'll support "remove a
1143 : // functor that isn't there" just like we did with set::erase
1144 : // before.
1145 974299 : if (raw_it != _ghosting_functors.end())
1146 973376 : _ghosting_functors.erase(raw_it);
1147 :
1148 : // We shouldn't have had two copies of the same functor
1149 27666 : libmesh_assert(std::find(_ghosting_functors.begin(),
1150 : _ghosting_functors.end(),
1151 : &ghosting_functor) ==
1152 : _ghosting_functors.end());
1153 :
1154 974299 : if (const auto it = _shared_functors.find(&ghosting_functor);
1155 27666 : it != _shared_functors.end())
1156 69 : _shared_functors.erase(it);
1157 974299 : }
1158 :
1159 :
1160 :
1161 27995 : void MeshBase::subdomain_ids (std::set<subdomain_id_type> & ids, const bool global /* = true */) const
1162 : {
1163 : // This requires an inspection on every processor
1164 1298 : if (global)
1165 1298 : parallel_object_only();
1166 :
1167 34 : struct SBDInserter {
1168 : std::set<subdomain_id_type> my_ids;
1169 :
1170 1298 : SBDInserter () {}
1171 36 : SBDInserter (SBDInserter &, Threads::split) {}
1172 :
1173 28101 : void operator()(const ConstElemRange & range) {
1174 2218422 : for (const Elem * elem : range)
1175 2190321 : my_ids.insert(elem->subdomain_id());
1176 28101 : }
1177 :
1178 36 : void join(SBDInserter & other) {
1179 36 : my_ids.merge(other.my_ids);
1180 36 : }
1181 : };
1182 :
1183 2596 : SBDInserter inserter;
1184 27995 : Threads::parallel_reduce(this->active_local_element_stored_range(), inserter);
1185 :
1186 1298 : ids.swap(inserter.my_ids);
1187 :
1188 27995 : if (global)
1189 : {
1190 : // Only include the unpartitioned elements if the user requests the global IDs.
1191 : // In the case of the local subdomain IDs, it doesn't make sense to include the
1192 : // unpartitioned elements because said elements do not have a sense of locality.
1193 58166 : for (const auto & elem : this->active_unpartitioned_element_ptr_range())
1194 28873 : ids.insert(elem->subdomain_id());
1195 :
1196 : // Some subdomains may only live on other processors
1197 27995 : this->comm().set_union(ids);
1198 : }
1199 27995 : }
1200 :
1201 :
1202 :
1203 633683 : void MeshBase::redistribute()
1204 : {
1205 : // We now have all elements and nodes redistributed; our ghosting
1206 : // functors should be ready to redistribute and/or recompute any
1207 : // cached data they use too.
1208 637019 : for (auto & gf : as_range(this->ghosting_functors_begin(),
1209 1433395 : this->ghosting_functors_end()))
1210 770508 : gf->redistribute();
1211 633683 : }
1212 :
1213 :
1214 :
1215 535576 : void MeshBase::update_post_partitioning()
1216 : {
1217 : // A range over all elements might still be fine here, but any range
1218 : // over local elements is obsolete if our partitioner changed the
1219 : // definition of "local".
1220 9774 : _const_active_local_element_stored_range.reset(nullptr);
1221 535576 : }
1222 :
1223 :
1224 :
1225 21170 : subdomain_id_type MeshBase::n_subdomains() const
1226 : {
1227 : // This requires an inspection on every processor
1228 596 : parallel_object_only();
1229 :
1230 1192 : std::set<subdomain_id_type> ids;
1231 :
1232 21170 : this->subdomain_ids (ids);
1233 :
1234 21766 : return cast_int<subdomain_id_type>(ids.size());
1235 : }
1236 :
1237 :
1238 :
1239 0 : subdomain_id_type MeshBase::n_local_subdomains() const
1240 : {
1241 0 : std::set<subdomain_id_type> ids;
1242 :
1243 0 : this->subdomain_ids (ids, /* global = */ false);
1244 :
1245 0 : return cast_int<subdomain_id_type>(ids.size());
1246 : }
1247 :
1248 :
1249 :
1250 :
1251 4035651 : dof_id_type MeshBase::n_nodes_on_proc (const processor_id_type proc_id) const
1252 : {
1253 : // We're either counting a processor's nodes or unpartitioned
1254 : // nodes
1255 37338 : libmesh_assert (proc_id < this->n_processors() ||
1256 : proc_id == DofObject::invalid_processor_id);
1257 :
1258 8033964 : return static_cast<dof_id_type>(std::distance (this->pid_nodes_begin(proc_id),
1259 12069615 : this->pid_nodes_end (proc_id)));
1260 : }
1261 :
1262 :
1263 :
1264 4467809 : dof_id_type MeshBase::n_elem_on_proc (const processor_id_type proc_id) const
1265 : {
1266 : // We're either counting a processor's elements or unpartitioned
1267 : // elements
1268 50442 : libmesh_assert (proc_id < this->n_processors() ||
1269 : proc_id == DofObject::invalid_processor_id);
1270 :
1271 8885176 : return static_cast<dof_id_type>(std::distance (this->pid_elements_begin(proc_id),
1272 13352985 : this->pid_elements_end (proc_id)));
1273 : }
1274 :
1275 :
1276 :
1277 1298067 : dof_id_type MeshBase::n_active_elem_on_proc (const processor_id_type proc_id) const
1278 : {
1279 2164 : libmesh_assert_less (proc_id, this->n_processors());
1280 2593970 : return static_cast<dof_id_type>(std::distance (this->active_pid_elements_begin(proc_id),
1281 3892037 : this->active_pid_elements_end (proc_id)));
1282 : }
1283 :
1284 :
1285 :
1286 0 : dof_id_type MeshBase::n_sub_elem () const
1287 : {
1288 0 : dof_id_type ne=0;
1289 :
1290 0 : for (const auto & elem : this->element_ptr_range())
1291 0 : ne += elem->n_sub_elem();
1292 :
1293 0 : return ne;
1294 : }
1295 :
1296 :
1297 :
1298 32266 : dof_id_type MeshBase::n_active_sub_elem () const
1299 : {
1300 1575 : dof_id_type ne=0;
1301 :
1302 25301373 : for (const auto & elem : this->active_element_ptr_range())
1303 25267532 : ne += elem->n_sub_elem();
1304 :
1305 32266 : return ne;
1306 : }
1307 :
1308 :
1309 :
1310 14995 : std::string MeshBase::get_info(const unsigned int verbosity /* = 0 */, const bool global /* = true */) const
1311 : {
1312 15835 : std::ostringstream oss;
1313 :
1314 14995 : oss << " Mesh Information:" << '\n';
1315 :
1316 14995 : if (!_elem_dims.empty())
1317 : {
1318 14995 : oss << " elem_dimensions()={";
1319 14995 : std::copy(_elem_dims.begin(),
1320 420 : --_elem_dims.end(), // --end() is valid if the set is non-empty
1321 14575 : std::ostream_iterator<unsigned int>(oss, ", "));
1322 14995 : oss << cast_int<unsigned int>(*_elem_dims.rbegin());
1323 14995 : oss << "}";
1324 14995 : if (!this->preparation().has_cached_elem_data)
1325 0 : oss << " (may be out of date)";
1326 14995 : oss << '\n';
1327 : }
1328 :
1329 14995 : if (!_elem_default_orders.empty())
1330 : {
1331 14995 : oss << " elem_default_orders()={";
1332 14995 : std::transform(_elem_default_orders.begin(),
1333 420 : --_elem_default_orders.end(),
1334 14995 : std::ostream_iterator<std::string>(oss, ", "),
1335 8 : [](Order o)
1336 276 : { return Utility::enum_to_string<Order>(o); });
1337 14995 : oss << Utility::enum_to_string<Order>(*_elem_default_orders.rbegin());
1338 14995 : oss << "}";
1339 14995 : if (!this->preparation().has_cached_elem_data)
1340 0 : oss << " (may be out of date)";
1341 14995 : oss << '\n';
1342 : }
1343 :
1344 14995 : oss << " supported_nodal_order()=" << this->_supported_nodal_order;
1345 14995 : if (!this->preparation().has_cached_elem_data)
1346 0 : oss << " (may be out of date)";
1347 14995 : oss << '\n';
1348 :
1349 14995 : oss << " spatial_dimension()=" << int(this->_spatial_dimension);
1350 14995 : if (!this->preparation().has_cached_elem_data)
1351 0 : oss << " (may be out of date)";
1352 14995 : oss << '\n';
1353 :
1354 14995 : oss << " n_nodes()=" << this->n_nodes() << '\n'
1355 14995 : << " n_local_nodes()=" << this->n_local_nodes() << '\n'
1356 15415 : << " n_elem()=" << this->n_elem() << '\n'
1357 29570 : << " n_local_elem()=" << this->n_local_elem() << '\n';
1358 : #ifdef LIBMESH_ENABLE_AMR
1359 29150 : oss << " n_active_elem()=" << this->n_active_elem() << '\n';
1360 : #endif
1361 14995 : if (global)
1362 15415 : oss << " n_subdomains()=" << static_cast<std::size_t>(this->n_subdomains()) << '\n';
1363 : else
1364 0 : oss << " n_local_subdomains()= " << static_cast<std::size_t>(this->n_local_subdomains()) << '\n';
1365 15415 : oss << " n_elemsets()=" << static_cast<std::size_t>(this->n_elemsets()) << '\n';
1366 14995 : if (!_elemset_codes.empty())
1367 0 : oss << " n_elemset_codes=" << _elemset_codes.size() << '\n';
1368 14995 : oss << " n_partitions()=" << static_cast<std::size_t>(this->n_partitions()) << '\n'
1369 15835 : << " n_processors()=" << static_cast<std::size_t>(this->n_processors()) << '\n'
1370 15415 : << " n_threads()=" << static_cast<std::size_t>(libMesh::n_threads()) << '\n'
1371 15835 : << " processor_id()=" << static_cast<std::size_t>(this->processor_id()) << '\n'
1372 15415 : << " is_prepared()=" << (this->is_prepared() ? "true" : "false") << '\n'
1373 43089 : << " is_replicated()=" << (this->is_replicated() ? "true" : "false") << '\n';
1374 :
1375 14995 : if (verbosity > 0)
1376 : {
1377 284 : if (global)
1378 : {
1379 8 : libmesh_parallel_only(this->comm());
1380 292 : if (this->processor_id() != 0)
1381 236 : oss << "\n Detailed global get_info() (verbosity > 0) is reduced and output to only rank 0.";
1382 : }
1383 :
1384 : // Helper for printing element types
1385 672 : const auto elem_type_helper = [](const std::set<int> & elem_types) {
1386 784 : std::stringstream ss;
1387 1344 : for (auto it = elem_types.begin(); it != elem_types.end();)
1388 : {
1389 1288 : ss << Utility::enum_to_string((ElemType)*it);
1390 672 : if (++it != elem_types.end())
1391 0 : ss << ", ";
1392 : }
1393 728 : return ss.str();
1394 560 : };
1395 :
1396 : // Helper for whether or not the given DofObject is to be included. If we're doing
1397 : // a global reduction, we also count unpartitioned objects on rank 0.
1398 871156 : const auto include_object = [this, &global](const DofObject & dof_object) {
1399 978188 : return this->processor_id() == dof_object.processor_id() ||
1400 250756 : (global &&
1401 42324 : this->processor_id() == 0 &&
1402 846328 : dof_object.processor_id() == DofObject::invalid_processor_id);
1403 276 : };
1404 :
1405 8 : Real volume = 0;
1406 :
1407 : // Add bounding box information
1408 284 : const auto bbox = global ? MeshTools::create_bounding_box(*this) : MeshTools::create_local_bounding_box(*this);
1409 284 : if (!global || this->processor_id() == 0)
1410 : oss << "\n " << (global ? "" : "Local ") << "Mesh Bounding Box:\n"
1411 48 : << " Minimum: " << bbox.min() << "\n"
1412 44 : << " Maximum: " << bbox.max() << "\n"
1413 88 : << " Delta: " << (bbox.max() - bbox.min()) << "\n";
1414 :
1415 : // Obtain the global or local element types
1416 16 : std::set<int> elem_types;
1417 49712 : for (const Elem * elem : this->active_local_element_ptr_range())
1418 49420 : elem_types.insert(elem->type());
1419 284 : if (global)
1420 : {
1421 : // Pick up unpartitioned elems on rank 0
1422 292 : if (this->processor_id() == 0)
1423 92 : for (const Elem * elem : this->active_unpartitioned_element_ptr_range())
1424 40 : elem_types.insert(elem->type());
1425 :
1426 284 : this->comm().set_union(elem_types);
1427 : }
1428 :
1429 : // Add element types
1430 284 : if (!global || this->processor_id() == 0)
1431 : oss << "\n " << (global ? "" : "Local ") << "Mesh Element Type(s):\n "
1432 140 : << elem_type_helper(elem_types) << "\n";
1433 :
1434 : // Reduce the nodeset ids
1435 16 : auto nodeset_ids = this->get_boundary_info().get_node_boundary_ids();
1436 284 : if (global)
1437 284 : this->comm().set_union(nodeset_ids);
1438 :
1439 : // Accumulate local information for each nodeset
1440 1608 : struct NodesetInfo
1441 : {
1442 : std::size_t num_nodes = 0;
1443 : BoundingBox bbox;
1444 : };
1445 16 : std::map<boundary_id_type, NodesetInfo> nodeset_info_map;
1446 91376 : for (const auto & [node, id] : this->get_boundary_info().get_nodeset_map())
1447 : {
1448 91092 : if (!include_object(*node))
1449 38364 : continue;
1450 :
1451 48672 : NodesetInfo & info = nodeset_info_map[id];
1452 :
1453 48672 : ++info.num_nodes;
1454 :
1455 48672 : if (verbosity > 1)
1456 48672 : info.bbox.union_with(*node);
1457 : }
1458 :
1459 : // Add nodeset info
1460 284 : if (!global || this->processor_id() == 0)
1461 : {
1462 48 : oss << "\n " << (global ? "" : "Local ") << "Mesh Nodesets:\n";
1463 48 : if (nodeset_ids.empty())
1464 0 : oss << " None\n";
1465 : }
1466 :
1467 8 : const auto & nodeset_name_map = this->get_boundary_info().get_nodeset_name_map();
1468 1988 : for (const auto id : nodeset_ids)
1469 : {
1470 1704 : NodesetInfo & info = nodeset_info_map[id];
1471 :
1472 : // Reduce the local information for this nodeset if required
1473 1704 : if (global)
1474 : {
1475 1704 : this->comm().sum(info.num_nodes);
1476 1704 : if (verbosity > 1)
1477 : {
1478 1752 : this->comm().min(info.bbox.min());
1479 1752 : this->comm().max(info.bbox.max());
1480 : }
1481 : }
1482 :
1483 1704 : const bool has_name = nodeset_name_map.count(id) && nodeset_name_map.at(id).size();
1484 1800 : const std::string name = has_name ? nodeset_name_map.at(id) : "";
1485 96 : if (global)
1486 48 : libmesh_assert(this->comm().verify(name));
1487 :
1488 1704 : if (global ? this->processor_id() == 0 : info.num_nodes > 0)
1489 : {
1490 288 : oss << " Nodeset " << id;
1491 288 : if (has_name)
1492 528 : oss << " (" << name << ")";
1493 312 : oss << ", " << info.num_nodes << " " << (global ? "" : "local ") << "nodes\n";
1494 :
1495 288 : if (verbosity > 1)
1496 : {
1497 288 : oss << " " << (global ? "Bounding" : "Local bounding") << " box minimum: "
1498 288 : << info.bbox.min() << "\n"
1499 288 : << " " << (global ? "Bounding" : "Local bounding") << " box maximum: "
1500 288 : << info.bbox.max() << "\n"
1501 288 : << " " << (global ? "Bounding" : "Local bounding") << " box delta: "
1502 288 : << (info.bbox.max() - info.bbox.min()) << "\n";
1503 : }
1504 : }
1505 : }
1506 :
1507 : // Reduce the sideset ids
1508 16 : auto sideset_ids = this->get_boundary_info().get_side_boundary_ids();
1509 284 : if (global)
1510 284 : this->comm().set_union(sideset_ids);
1511 :
1512 : // Accumulate local information for each sideset
1513 : struct SidesetInfo
1514 : {
1515 : std::size_t num_sides = 0;
1516 : Real volume = 0;
1517 : std::set<int> side_elem_types;
1518 : std::set<int> elem_types;
1519 : std::set<dof_id_type> elem_ids;
1520 : std::set<dof_id_type> node_ids;
1521 : BoundingBox bbox;
1522 : };
1523 16 : ElemSideBuilder side_builder;
1524 16 : std::map<boundary_id_type, SidesetInfo> sideset_info_map;
1525 70396 : for (const auto & pair : this->get_boundary_info().get_sideset_map())
1526 : {
1527 70112 : const Elem * elem = pair.first;
1528 63456 : if (!include_object(*elem))
1529 30176 : continue;
1530 :
1531 39936 : const auto id = pair.second.second;
1532 39936 : SidesetInfo & info = sideset_info_map[id];
1533 :
1534 39936 : const auto s = pair.second.first;
1535 39936 : const Elem & side = side_builder(*elem, s);
1536 :
1537 39936 : ++info.num_sides;
1538 39936 : info.side_elem_types.insert(side.type());
1539 39936 : info.elem_types.insert(elem->type());
1540 39936 : info.elem_ids.insert(elem->id());
1541 :
1542 199680 : for (const Node & node : side.node_ref_range())
1543 146432 : if (include_object(node))
1544 147552 : info.node_ids.insert(node.id());
1545 :
1546 39936 : if (verbosity > 1)
1547 : {
1548 39936 : info.volume += side.volume();
1549 39936 : info.bbox.union_with(side.loose_bounding_box());
1550 : }
1551 : }
1552 :
1553 : // Add sideset info
1554 284 : if (!global || this->processor_id() == 0)
1555 : {
1556 48 : oss << "\n " << (global ? "" : "Local ") << "Mesh Sidesets:\n";
1557 48 : if (sideset_ids.empty())
1558 0 : oss << " None\n";
1559 : }
1560 8 : const auto & sideset_name_map = this->get_boundary_info().get_sideset_name_map();
1561 1988 : for (const auto id : sideset_ids)
1562 : {
1563 1704 : SidesetInfo & info = sideset_info_map[id];
1564 :
1565 1704 : auto num_elems = info.elem_ids.size();
1566 1704 : auto num_nodes = info.node_ids.size();
1567 :
1568 : // Reduce the local information for this sideset if required
1569 1704 : if (global)
1570 : {
1571 1704 : this->comm().sum(info.num_sides);
1572 1704 : this->comm().set_union(info.side_elem_types, 0);
1573 1704 : this->comm().sum(num_elems);
1574 1704 : this->comm().set_union(info.elem_types, 0);
1575 1704 : this->comm().sum(num_nodes);
1576 1704 : if (verbosity > 1)
1577 : {
1578 1704 : this->comm().sum(info.volume);
1579 1752 : this->comm().min(info.bbox.min());
1580 1752 : this->comm().max(info.bbox.max());
1581 : }
1582 : }
1583 :
1584 1704 : const bool has_name = sideset_name_map.count(id) && sideset_name_map.at(id).size();
1585 1800 : const std::string name = has_name ? sideset_name_map.at(id) : "";
1586 96 : if (global)
1587 48 : libmesh_assert(this->comm().verify(name));
1588 :
1589 1704 : if (global ? this->processor_id() == 0 : info.num_sides > 0)
1590 : {
1591 288 : oss << " Sideset " << id;
1592 288 : if (has_name)
1593 528 : oss << " (" << name << ")";
1594 552 : oss << ", " << info.num_sides << " sides (" << elem_type_helper(info.side_elem_types) << ")"
1595 1056 : << ", " << num_elems << " " << (global ? "" : "local ") << "elems (" << elem_type_helper(info.elem_types) << ")"
1596 600 : << ", " << num_nodes << " " << (global ? "" : "local ") << "nodes\n";
1597 :
1598 288 : if (verbosity > 1)
1599 : {
1600 312 : oss << " " << (global ? "Side" : "Local side") << " volume: " << info.volume << "\n"
1601 288 : << " " << (global ? "Bounding" : "Local bounding") << " box minimum: "
1602 288 : << info.bbox.min() << "\n"
1603 288 : << " " << (global ? "Bounding" : "Local bounding") << " box maximum: "
1604 288 : << info.bbox.max() << "\n"
1605 288 : << " " << (global ? "Bounding" : "Local bounding") << " box delta: "
1606 288 : << (info.bbox.max() - info.bbox.min()) << "\n";
1607 : }
1608 : }
1609 : }
1610 :
1611 : // Reduce the edgeset ids
1612 16 : auto edgeset_ids = this->get_boundary_info().get_edge_boundary_ids();
1613 284 : if (global)
1614 284 : this->comm().set_union(edgeset_ids);
1615 :
1616 : // Accumulate local information for each edgeset
1617 0 : struct EdgesetInfo
1618 : {
1619 : std::size_t num_edges = 0;
1620 : std::set<int> edge_elem_types;
1621 : BoundingBox bbox;
1622 : };
1623 16 : std::map<boundary_id_type, EdgesetInfo> edgeset_info_map;
1624 284 : std::unique_ptr<const Elem> edge;
1625 :
1626 284 : for (const auto & pair : this->get_boundary_info().get_edgeset_map())
1627 : {
1628 0 : const Elem * elem = pair.first;
1629 0 : if (!include_object(*elem))
1630 0 : continue;
1631 :
1632 0 : const auto id = pair.second.second;
1633 0 : EdgesetInfo & info = edgeset_info_map[id];
1634 :
1635 0 : elem->build_edge_ptr(edge, pair.second.first);
1636 :
1637 0 : ++info.num_edges;
1638 0 : info.edge_elem_types.insert(edge->type());
1639 :
1640 0 : if (verbosity > 1)
1641 0 : info.bbox.union_with(edge->loose_bounding_box());
1642 : }
1643 :
1644 : // Add edgeset info
1645 284 : if (!global || this->processor_id() == 0)
1646 : {
1647 48 : oss << "\n " << (global ? "" : "Local ") << "Mesh Edgesets:\n";
1648 48 : if (edgeset_ids.empty())
1649 48 : oss << " None\n";
1650 : }
1651 :
1652 8 : const auto & edgeset_name_map = this->get_boundary_info().get_edgeset_name_map();
1653 284 : for (const auto id : edgeset_ids)
1654 : {
1655 0 : EdgesetInfo & info = edgeset_info_map[id];
1656 :
1657 : // Reduce the local information for this edgeset if required
1658 0 : if (global)
1659 : {
1660 0 : this->comm().sum(info.num_edges);
1661 0 : this->comm().set_union(info.edge_elem_types, 0);
1662 0 : if (verbosity > 1)
1663 : {
1664 0 : this->comm().min(info.bbox.min());
1665 0 : this->comm().min(info.bbox.max());
1666 : }
1667 : }
1668 :
1669 0 : const bool has_name = edgeset_name_map.count(id) && edgeset_name_map.at(id).size();
1670 0 : const std::string name = has_name ? edgeset_name_map.at(id) : "";
1671 0 : if (global)
1672 0 : libmesh_assert(this->comm().verify(name));
1673 :
1674 0 : if (global ? this->processor_id() == 0 : info.num_edges > 0)
1675 : {
1676 0 : oss << " Edgeset " << id;
1677 0 : if (has_name)
1678 0 : oss << " (" << name << ")";
1679 0 : oss << ", " << info.num_edges << " " << (global ? "" : "local ") << "edges ("
1680 0 : << elem_type_helper(info.edge_elem_types) << ")\n";
1681 :
1682 0 : if (verbosity > 1)
1683 : {
1684 0 : oss << " " << (global ? "Bounding" : "Local bounding") << " box minimum: "
1685 0 : << info.bbox.min() << "\n"
1686 0 : << " " << (global ? "Bounding" : "Local bounding") << " box maximum: "
1687 0 : << info.bbox.max() << "\n"
1688 0 : << " " << (global ? "Bounding" : "Local bounding") << " box delta: "
1689 0 : << (info.bbox.max() - info.bbox.min()) << "\n";
1690 : }
1691 : }
1692 : }
1693 :
1694 : // Reduce the block IDs and block names
1695 16 : std::set<subdomain_id_type> subdomains;
1696 194032 : for (const Elem * elem : this->active_element_ptr_range())
1697 92640 : if (include_object(*elem))
1698 49420 : subdomains.insert(elem->subdomain_id());
1699 284 : if (global)
1700 284 : this->comm().set_union(subdomains);
1701 :
1702 : // Accumulate local information for each subdomain
1703 0 : struct SubdomainInfo
1704 : {
1705 : std::size_t num_elems = 0;
1706 : Real volume = 0;
1707 : std::set<int> elem_types;
1708 : std::set<dof_id_type> active_node_ids;
1709 : #ifdef LIBMESH_ENABLE_AMR
1710 : std::size_t num_active_elems = 0;
1711 : #endif
1712 : BoundingBox bbox;
1713 : };
1714 16 : std::map<subdomain_id_type, SubdomainInfo> subdomain_info_map;
1715 194032 : for (const Elem * elem : this->element_ptr_range())
1716 92640 : if (include_object(*elem))
1717 : {
1718 49152 : SubdomainInfo & info = subdomain_info_map[elem->subdomain_id()];
1719 :
1720 49152 : ++info.num_elems;
1721 49152 : info.elem_types.insert(elem->type());
1722 :
1723 : #ifdef LIBMESH_ENABLE_AMR
1724 4096 : if (elem->active())
1725 49152 : ++info.num_active_elems;
1726 : #endif
1727 :
1728 442368 : for (const Node & node : elem->node_ref_range())
1729 392704 : if (include_object(node) && node.active())
1730 330608 : info.active_node_ids.insert(node.id());
1731 :
1732 49152 : if (verbosity > 1 && elem->active())
1733 : {
1734 49152 : info.volume += elem->volume();
1735 49152 : info.bbox.union_with(elem->loose_bounding_box());
1736 : }
1737 268 : }
1738 :
1739 : // Add subdomain info
1740 284 : oss << "\n " << (global ? "" : "Local ") << "Mesh Subdomains:\n";
1741 8 : const auto & subdomain_name_map = this->get_subdomain_name_map();
1742 568 : for (const auto id : subdomains)
1743 : {
1744 284 : SubdomainInfo & info = subdomain_info_map[id];
1745 :
1746 284 : auto num_active_nodes = info.active_node_ids.size();
1747 :
1748 : // Reduce the information for this subdomain if needed
1749 284 : if (global)
1750 : {
1751 284 : this->comm().sum(info.num_elems);
1752 : #ifdef LIBMESH_ENABLE_AMR
1753 284 : this->comm().sum(info.num_active_elems);
1754 : #endif
1755 284 : this->comm().sum(num_active_nodes);
1756 284 : this->comm().set_union(info.elem_types, 0);
1757 284 : if (verbosity > 1)
1758 : {
1759 292 : this->comm().min(info.bbox.min());
1760 292 : this->comm().max(info.bbox.max());
1761 284 : this->comm().sum(info.volume);
1762 : }
1763 : }
1764 284 : if (verbosity > 1)
1765 284 : volume += info.volume;
1766 :
1767 8 : const bool has_name = subdomain_name_map.count(id);
1768 292 : const std::string name = has_name ? subdomain_name_map.at(id) : "";
1769 16 : if (global)
1770 8 : libmesh_assert(this->comm().verify(name));
1771 :
1772 284 : if (!global || this->processor_id() == 0)
1773 : {
1774 48 : oss << " Subdomain " << id;
1775 48 : if (has_name)
1776 0 : oss << " (" << name << ")";
1777 48 : oss << ": " << info.num_elems << " " << (global ? "" : "local ") << "elems "
1778 56 : << "(" << elem_type_helper(info.elem_types);
1779 : #ifdef LIBMESH_ENABLE_AMR
1780 48 : oss << ", " << info.num_active_elems << " active";
1781 : #endif
1782 52 : oss << "), " << num_active_nodes << " " << (global ? "" : "local ") << "active nodes\n";
1783 48 : if (verbosity > 1)
1784 : {
1785 52 : oss << " " << (global ? "Volume" : "Local volume") << ": " << info.volume << "\n";
1786 48 : oss << " " << (global ? "Bounding" : "Local bounding") << " box minimum: "
1787 48 : << info.bbox.min() << "\n"
1788 48 : << " " << (global ? "Bounding" : "Local bounding") << " box maximum: "
1789 48 : << info.bbox.max() << "\n"
1790 48 : << " " << (global ? "Bounding" : "Local bounding") << " box delta: "
1791 48 : << (info.bbox.max() - info.bbox.min()) << "\n";
1792 : }
1793 : }
1794 : }
1795 :
1796 560 : oss << " " << (global ? "Global" : "Local") << " mesh volume = " << volume << "\n";
1797 :
1798 268 : }
1799 :
1800 15415 : return oss.str();
1801 14155 : }
1802 :
1803 :
1804 14995 : void MeshBase::print_info(std::ostream & os, const unsigned int verbosity /* = 0 */, const bool global /* = true */) const
1805 : {
1806 15415 : os << this->get_info(verbosity, global)
1807 420 : << std::endl;
1808 14995 : }
1809 :
1810 :
1811 0 : std::ostream & operator << (std::ostream & os, const MeshBase & m)
1812 : {
1813 0 : m.print_info(os);
1814 0 : return os;
1815 : }
1816 :
1817 :
1818 433538 : void MeshBase::partition (const unsigned int n_parts)
1819 : {
1820 : // If we get here and we have unpartitioned elements, we need that
1821 : // fixed.
1822 433538 : if (this->n_unpartitioned_elem() > 0)
1823 : {
1824 9588 : libmesh_assert (partitioner().get());
1825 9588 : libmesh_assert (this->is_serial());
1826 316139 : partitioner()->partition (*this, n_parts);
1827 : }
1828 : // A nullptr partitioner or a skip_partitioning(true) call or a
1829 : // skip_noncritical_partitioning(true) call means don't repartition;
1830 : // skip_noncritical_partitioning() checks all these.
1831 3530 : else if (!skip_noncritical_partitioning())
1832 : {
1833 110670 : partitioner()->partition (*this, n_parts);
1834 : }
1835 : else
1836 : {
1837 : // Adaptive coarsening may have "orphaned" nodes on processors
1838 : // whose elements no longer share them. We need to check for
1839 : // and possibly fix that.
1840 6729 : MeshTools::correct_node_proc_ids(*this);
1841 :
1842 : // Make sure locally cached partition count is correct
1843 6729 : this->recalculate_n_partitions();
1844 :
1845 : // Make sure any other locally cached data is correct
1846 6729 : this->update_post_partitioning();
1847 : }
1848 :
1849 433538 : _preparation.is_partitioned = true;
1850 433538 : }
1851 :
1852 18720 : void MeshBase::all_second_order (const bool full_ordered)
1853 : {
1854 18720 : this->all_second_order_range(this->element_ptr_range(), full_ordered);
1855 18720 : }
1856 :
1857 15042 : void MeshBase::all_complete_order ()
1858 : {
1859 15042 : this->all_complete_order_range(this->element_ptr_range());
1860 15042 : }
1861 :
1862 6729 : unsigned int MeshBase::recalculate_n_partitions()
1863 : {
1864 : // This requires an inspection on every processor
1865 170 : parallel_object_only();
1866 :
1867 6729 : unsigned int max_proc_id=0;
1868 :
1869 225762 : for (const auto & elem : this->active_local_element_ptr_range())
1870 223045 : max_proc_id = std::max(max_proc_id, static_cast<unsigned int>(elem->processor_id()));
1871 :
1872 : // The number of partitions is one more than the max processor ID.
1873 6729 : _n_parts = max_proc_id+1;
1874 :
1875 6729 : this->comm().max(_n_parts);
1876 :
1877 6729 : return _n_parts;
1878 : }
1879 :
1880 :
1881 :
1882 568400 : std::unique_ptr<PointLocatorBase> MeshBase::sub_point_locator () const
1883 : {
1884 : // If there's no master point locator, then we need one.
1885 568400 : if (_point_locator.get() == nullptr)
1886 : {
1887 : // PointLocator construction may not be safe within threads
1888 902 : libmesh_assert(!Threads::in_threads);
1889 :
1890 : // And it may require parallel communication
1891 902 : parallel_object_only();
1892 :
1893 : #ifdef LIBMESH_ENABLE_NANOFLANN_POINTLOCATOR
1894 : _point_locator = PointLocatorBase::build(NANOFLANN, *this);
1895 : #else
1896 43664 : _point_locator = PointLocatorBase::build(TREE_ELEMENTS, *this);
1897 : #endif
1898 :
1899 22663 : if (_point_locator_close_to_point_tol > 0.)
1900 0 : _point_locator->set_close_to_point_tol(_point_locator_close_to_point_tol);
1901 : }
1902 :
1903 : // Otherwise there was a master point locator, and we can grab a
1904 : // sub-locator easily.
1905 : return
1906 : #ifdef LIBMESH_ENABLE_NANOFLANN_POINTLOCATOR
1907 : PointLocatorBase::build(NANOFLANN, *this, _point_locator.get());
1908 : #else
1909 568400 : PointLocatorBase::build(TREE_ELEMENTS, *this, _point_locator.get());
1910 : #endif
1911 : }
1912 :
1913 :
1914 :
1915 110041621 : void MeshBase::clear_point_locator ()
1916 : {
1917 2948333 : _point_locator.reset(nullptr);
1918 110041621 : }
1919 :
1920 :
1921 :
1922 0 : void MeshBase::set_count_lower_dim_elems_in_point_locator(bool count_lower_dim_elems)
1923 : {
1924 0 : _count_lower_dim_elems_in_point_locator = count_lower_dim_elems;
1925 0 : }
1926 :
1927 :
1928 :
1929 6883297 : bool MeshBase::get_count_lower_dim_elems_in_point_locator() const
1930 : {
1931 6883297 : return _count_lower_dim_elems_in_point_locator;
1932 : }
1933 :
1934 :
1935 :
1936 1063271 : std::string & MeshBase::subdomain_name(subdomain_id_type id)
1937 : {
1938 45266 : this->unset_has_synched_subdomain_name_map();
1939 1063271 : return _block_id_to_name[id];
1940 : }
1941 :
1942 44105 : const std::string & MeshBase::subdomain_name(subdomain_id_type id) const
1943 : {
1944 : // An empty string to return when no matching subdomain name is found
1945 44105 : static const std::string empty;
1946 :
1947 44105 : if (const auto iter = _block_id_to_name.find(id);
1948 1623 : iter == _block_id_to_name.end())
1949 1417 : return empty;
1950 : else
1951 2476 : return iter->second;
1952 : }
1953 :
1954 0 : void MeshBase::set_subdomain_name(const subdomain_id_type id,
1955 : const std::string & name,
1956 : const bool synchronous)
1957 : {
1958 0 : if (synchronous)
1959 0 : parallel_object_only();
1960 : else
1961 0 : this->unset_has_synched_subdomain_name_map();
1962 0 : _block_id_to_name[id] = name;
1963 0 : }
1964 :
1965 :
1966 0 : subdomain_id_type MeshBase::get_id_by_name(std::string_view name) const
1967 : {
1968 : // Linear search over the map values.
1969 0 : for (const auto & [sbd_id, sbd_name] : _block_id_to_name)
1970 0 : if (sbd_name == name)
1971 0 : return sbd_id;
1972 :
1973 : // If we made it here without returning, we don't have a subdomain
1974 : // with the requested name, so return Elem::invalid_subdomain_id.
1975 0 : return Elem::invalid_subdomain_id;
1976 : }
1977 :
1978 :
1979 1684272 : const ElemRange & MeshBase::element_stored_range()
1980 : {
1981 1684272 : if (!_element_stored_range)
1982 : {
1983 : // Range construction may not be safe within threads
1984 26786 : libmesh_assert(!Threads::in_threads);
1985 :
1986 : _element_stored_range =
1987 2067157 : std::make_unique<ElemRange>(this->elements_begin(),
1988 1432064 : this->elements_end());
1989 : }
1990 :
1991 1684272 : return *_element_stored_range;
1992 : }
1993 :
1994 570334 : const ConstElemRange & MeshBase::active_local_element_stored_range() const
1995 : {
1996 570334 : if (!_const_active_local_element_stored_range)
1997 : {
1998 : // Range construction may not be safe within threads
1999 1754 : libmesh_assert(!Threads::in_threads);
2000 :
2001 : _const_active_local_element_stored_range =
2002 906389 : std::make_unique<ConstElemRange>(this->active_local_elements_begin(),
2003 608352 : this->active_local_elements_end());
2004 : }
2005 :
2006 570334 : return *_const_active_local_element_stored_range;
2007 : }
2008 :
2009 112291490 : void MeshBase::clear_stored_ranges()
2010 : {
2011 2944717 : _element_stored_range.reset(nullptr);
2012 2944717 : _const_active_local_element_stored_range.reset(nullptr);
2013 112291490 : }
2014 :
2015 :
2016 : #ifdef LIBMESH_ENABLE_DEPRECATED
2017 0 : void MeshBase::cache_elem_dims()
2018 : {
2019 : libmesh_deprecated();
2020 :
2021 0 : this->cache_elem_data();
2022 0 : }
2023 : #endif // LIBMESH_ENABLE_DEPRECATED
2024 :
2025 702595 : void MeshBase::cache_elem_data()
2026 : {
2027 : // This requires an inspection on every processor
2028 32936 : parallel_object_only();
2029 :
2030 : // Need to clear containers first in case all elements of a
2031 : // particular dimension/order/subdomain have been deleted.
2032 53338 : _elem_dims.clear();
2033 53338 : _elem_default_orders.clear();
2034 53338 : _mesh_subdomains.clear();
2035 702595 : _supported_nodal_order = MAXIMUM;
2036 :
2037 37301691 : for (const auto & elem : this->active_element_ptr_range())
2038 : {
2039 35929437 : _elem_dims.insert(cast_int<unsigned char>(elem->dim()));
2040 35929437 : _elem_default_orders.insert(elem->default_order());
2041 35929437 : _mesh_subdomains.insert(elem->subdomain_id());
2042 35929437 : _supported_nodal_order =
2043 35929437 : static_cast<Order>
2044 71858874 : (std::min(static_cast<int>(_supported_nodal_order),
2045 36454708 : static_cast<int>(elem->supported_nodal_order())));
2046 649257 : }
2047 :
2048 702595 : if (!this->is_serial())
2049 : {
2050 : // Some different dimension/order/subdomain elements may only live
2051 : // on other processors
2052 294062 : this->comm().set_union(_elem_dims);
2053 294062 : this->comm().set_union(_elem_default_orders);
2054 294062 : this->comm().min(_supported_nodal_order);
2055 294062 : this->comm().set_union(_mesh_subdomains);
2056 : }
2057 :
2058 : // If the largest element dimension found is larger than the current
2059 : // _spatial_dimension, increase _spatial_dimension.
2060 702595 : unsigned int max_dim = this->mesh_dimension();
2061 702595 : if (max_dim > _spatial_dimension)
2062 43459 : _spatial_dimension = cast_int<unsigned char>(max_dim);
2063 :
2064 : // _spatial_dimension may need to increase from 1->2 or 2->3 if the
2065 : // mesh is full of 1D elements but they are not x-aligned, or the
2066 : // mesh is full of 2D elements but they are not in the x-y plane.
2067 : // If the mesh is x-aligned or x-y planar, we will end up checking
2068 : // every node's coordinates and not breaking out of the loop
2069 : // early...
2070 702595 : if (_spatial_dimension < LIBMESH_DIM)
2071 : {
2072 51836238 : for (const auto & node : this->node_ptr_range())
2073 : {
2074 : // Note: the exact floating point comparison is intentional,
2075 : // we don't want to get tripped up by tolerances.
2076 27230897 : if ((*node)(0) != 0. && _spatial_dimension < 1)
2077 0 : _spatial_dimension = 1;
2078 :
2079 27230897 : if ((*node)(1) != 0. && _spatial_dimension < 2)
2080 : {
2081 2643 : _spatial_dimension = 2;
2082 : #if LIBMESH_DIM == 2
2083 : // If libmesh is compiled in 2D mode, this is the
2084 : // largest spatial dimension possible so we can break
2085 : // out.
2086 : break;
2087 : #endif
2088 : }
2089 :
2090 : #if LIBMESH_DIM > 2
2091 27230897 : if ((*node)(2) != 0.)
2092 : {
2093 : // Spatial dimension can't get any higher than this, so
2094 : // we can break out.
2095 8355 : _spatial_dimension = 3;
2096 8355 : break;
2097 : }
2098 : #endif
2099 346712 : }
2100 : }
2101 :
2102 702595 : _preparation.has_cached_elem_data = true;
2103 702595 : }
2104 :
2105 :
2106 498326 : void MeshBase::sync_subdomain_name_map()
2107 : {
2108 : // This requires every processor
2109 27182 : parallel_object_only();
2110 :
2111 498326 : this->comm().set_union(_block_id_to_name);
2112 :
2113 498326 : _preparation.has_synched_subdomain_name_map = true;
2114 498326 : }
2115 :
2116 :
2117 448386 : void MeshBase::detect_interior_parents()
2118 : {
2119 25554 : LOG_SCOPE("detect_interior_parents()", "MeshBase");
2120 :
2121 : // This requires an inspection on every processor
2122 25554 : parallel_object_only();
2123 :
2124 : // This requires up-to-date mesh dimensions, but if we don't have
2125 : // them cached then we can't update them without changing the mesh
2126 : // in unexpected ways that interfere with our tests of
2127 : // partially-prepared meshes in MeshTools::*valid_is_prepared
2128 25554 : std::set<unsigned char> elem_dims_copy;
2129 448386 : if (_preparation.has_cached_elem_data)
2130 25554 : elem_dims_copy = this->elem_dimensions();
2131 : else
2132 : {
2133 0 : for (const auto & elem : this->active_element_ptr_range())
2134 0 : elem_dims_copy.insert(cast_int<unsigned char>(elem->dim()));
2135 0 : if (!this->is_serial())
2136 0 : this->comm().set_union(elem_dims_copy);
2137 : }
2138 :
2139 : // Early return if the mesh is empty or has elements of a single spatial dimension.
2140 448386 : if (elem_dims_copy.size() <= 1)
2141 : {
2142 441533 : _preparation.has_interior_parent_ptrs = true;
2143 441533 : return;
2144 : }
2145 :
2146 : // Convenient elem_dimensions iterators
2147 6665 : const auto dim_start = elem_dims_copy.begin();
2148 356 : const auto dim_end = elem_dims_copy.end();
2149 :
2150 : // In this function we find only +1 dimensional interior parents,
2151 : // (so, for a given element el, the interior parent p must satisfy p.dim() == el.dim() + 1).
2152 : // Therefore, we can avoid checking the existence of interior parents
2153 : // for all those elements el such there there is no p with p.dim() == el.dim() + 1.
2154 : // We store whether to skip any given dimension in the construction of interior parents
2155 : // inside the vector in dimensions_to_skip_for_interior_parents.
2156 6853 : std::vector<bool> skip_dimension_for_interior_parents(/*count=*/LIBMESH_DIM+1, /*value=*/false);
2157 6665 : skip_dimension_for_interior_parents.back() = true;
2158 :
2159 : // Moreover, in the following, we will build a node-to-elem map.
2160 : // It is among the elems of this map that we will look for interior parents.
2161 : // Therefore, we can skip all elems p such that there is no el with el.dim() == p.dim() - 1.
2162 : // We store whether to skip any given dimension in the construction of the node-to-elem map
2163 : // in the vector skip_dimensions_for_node_to_el_map.
2164 6853 : std::vector<bool> skip_dimensions_for_node_to_el_map(/*count=*/LIBMESH_DIM+1, /*value=*/false);
2165 6853 : skip_dimensions_for_node_to_el_map[*dim_start] = true;
2166 :
2167 : // We also create a flag to know if all dimensions should be skipped,
2168 : // and if we should therefore return early.
2169 356 : bool skip_all_dimensions = true;
2170 :
2171 : // Fill dimensions_to_skip_for_interior_parents and dimensions_to_skip_for_node_to_el_map.
2172 7213 : for (auto [it, next] = std::make_tuple(dim_start, std::next(dim_start));
2173 14208 : next != dim_end; ++it, ++next)
2174 : {
2175 6995 : if (*it + 1 != *next) // if sequential dimensions differ by exactly 1
2176 : {
2177 110 : skip_dimension_for_interior_parents[*it] = true;
2178 1461 : skip_dimensions_for_node_to_el_map[*next] = true;
2179 : }
2180 5686 : else if (!skip_dimension_for_interior_parents[*it])
2181 290 : skip_all_dimensions = false;
2182 : }
2183 :
2184 : // There is nothing to do if all dimensions should be
2185 : // skipped. Before returning, we must also set the flag that says
2186 : // "interior parent pointers have been set up" even though we
2187 : // determined there was no work to be done.
2188 6853 : if (skip_all_dimensions)
2189 : {
2190 1319 : _preparation.has_interior_parent_ptrs = true;
2191 66 : return;
2192 : }
2193 :
2194 : // Do we have interior parent pointers going to a different mesh?
2195 : // If so then we'll still check to make sure that's the only place
2196 : // they go, so we can libmesh_not_implemented() if not.
2197 442 : const bool separate_interior_mesh = (&(this->interior_mesh()) != this);
2198 :
2199 : // This map will be used to set interior parents
2200 290 : std::unordered_map<dof_id_type, std::vector<dof_id_type>> node_to_elem;
2201 :
2202 311322 : for (const auto & elem : this->element_ptr_range())
2203 : {
2204 : // Ignore element if it cannot be interior parent of any other elem.
2205 162285 : if (skip_dimensions_for_node_to_el_map[elem->dim()])
2206 66904 : continue;
2207 :
2208 : // Populating the node_to_elem map, same as MeshTools::build_nodes_to_elem_map
2209 526639 : for (auto n : make_range(elem->n_vertices()))
2210 : {
2211 49888 : libmesh_assert_less (elem->id(), this->max_elem_id());
2212 :
2213 458552 : node_to_elem[elem->node_id(n)].push_back(elem->id());
2214 : }
2215 5092 : }
2216 :
2217 : // Automatically set interior parents
2218 311322 : for (const auto & element : this->element_ptr_range())
2219 : {
2220 : // Ignore elements with dimensions to skip
2221 : // or elements that already have an interior parent.
2222 162285 : if (skip_dimension_for_interior_parents[element->dim()] || element->interior_parent())
2223 33922 : continue;
2224 :
2225 : // Start by generating sets of dim+1 dimensional elements that
2226 : // touch each vertex of the current element. If we encounter a
2227 : // vertex not connected to _any_ dim+1 dimensional elements,
2228 : // then we can exit the loop without checking the remaining
2229 : // vertices since an interior parent (if it exists) will be
2230 : // connected to all vertices of the current element.
2231 148593 : std::vector<std::set<dof_id_type>> neighbors( element->n_vertices() );
2232 :
2233 13410 : bool found_interior_parents = true;
2234 :
2235 131564 : for (auto n : make_range(element->n_vertices()))
2236 : {
2237 137446 : auto it = node_to_elem.find(element->node_id(n));
2238 :
2239 : // Check at first that this node is not isolated.
2240 130564 : if (it == node_to_elem.end())
2241 : {
2242 2764 : found_interior_parents = false;
2243 13376 : break; // out of n-loop
2244 : }
2245 :
2246 386703 : for (const auto & vertex_neighbor_id : it->second)
2247 309537 : if (this->elem_ref(vertex_neighbor_id).dim() == element->dim()+1)
2248 8739 : neighbors[n].insert(vertex_neighbor_id);
2249 :
2250 82576 : if (neighbors[n].empty())
2251 : {
2252 : // We have found an empty set for one vertex, no reason
2253 : // to continue.
2254 10612 : found_interior_parents = false;
2255 10612 : break; // out of n-loop
2256 : }
2257 : }
2258 :
2259 : // If we have generated a non-empty set of elements for each
2260 : // vertex, we will now look for a vertex_neighbor_id that
2261 : // appears in _all_ of those sets. If found, this is our interior
2262 : // parent id. If multiple such common ids are found, we will
2263 : // take the lowest such id to be the interior parent id.
2264 13410 : if (found_interior_parents)
2265 : {
2266 62 : std::set<dof_id_type> & neighbors_0 = neighbors[0];
2267 1645 : for (const auto & interior_parent_id : neighbors_0)
2268 : {
2269 46 : found_interior_parents = false;
2270 2633 : for (auto n : make_range(1u, element->n_vertices()))
2271 : {
2272 1633 : if (neighbors[n].count(interior_parent_id))
2273 : {
2274 34 : found_interior_parents = true;
2275 : }
2276 : else
2277 : {
2278 12 : found_interior_parents = false;
2279 12 : break;
2280 : }
2281 : }
2282 :
2283 1426 : if (found_interior_parents)
2284 : {
2285 781 : element->set_interior_parent(this->elem_ptr(interior_parent_id));
2286 22 : break;
2287 : }
2288 : }
2289 :
2290 : // Do we have a mixed dimensional mesh that contains some of
2291 : // its own interior parents, but we already expect to have
2292 : // interior parents on a different mesh? That's going to
2293 : // take some work to support if anyone needs it.
2294 1000 : if (separate_interior_mesh)
2295 0 : libmesh_not_implemented_msg
2296 : ("interior_parent() values in multiple meshes are unsupported.");
2297 : }
2298 113225 : }
2299 :
2300 : // This flag doesn't necessarily mean any Elems actually have
2301 : // interior parent pointers, just that we did all the work to
2302 : // determine whether or not they do.
2303 5534 : _preparation.has_interior_parent_ptrs = true;
2304 : }
2305 :
2306 :
2307 :
2308 : #ifdef LIBMESH_ENABLE_PERIODIC
2309 : /**
2310 : * Register a pair of boundaries as disjoint neighbor boundary pairs.
2311 : */
2312 994 : void MeshBase::add_disjoint_neighbor_boundary_pairs(const boundary_id_type b1,
2313 : const boundary_id_type b2,
2314 : const RealVectorValue & translation)
2315 : {
2316 : // Lazily allocate the container the first time it’s needed
2317 994 : if (!_disjoint_neighbor_boundary_pairs)
2318 1496 : _disjoint_neighbor_boundary_pairs = std::make_unique<PeriodicBoundaries>();
2319 :
2320 28 : PeriodicBoundaries & db = *_disjoint_neighbor_boundary_pairs;
2321 :
2322 : // Create forward and inverse boundary mappings
2323 1022 : PeriodicBoundary forward(translation);
2324 994 : PeriodicBoundary inverse(translation * -1.0);
2325 :
2326 994 : forward.myboundary = b1;
2327 994 : forward.pairedboundary = b2;
2328 994 : inverse.myboundary = b2;
2329 994 : inverse.pairedboundary = b1;
2330 :
2331 : // Add both directions into the container
2332 994 : db.emplace(b1, forward.clone());
2333 1022 : db.emplace(b2, inverse.clone());
2334 994 : }
2335 :
2336 646751 : PeriodicBoundaries * MeshBase::get_disjoint_neighbor_boundary_pairs()
2337 : {
2338 646751 : return _disjoint_neighbor_boundary_pairs.get();
2339 : }
2340 :
2341 2698295 : const PeriodicBoundaries * MeshBase::get_disjoint_neighbor_boundary_pairs() const
2342 : {
2343 2698295 : return _disjoint_neighbor_boundary_pairs.get();
2344 : }
2345 :
2346 213 : void MeshBase::remove_disjoint_boundary_pair(const boundary_id_type b1,
2347 : const boundary_id_type b2)
2348 : {
2349 : // Nothing to remove if not allocated or empty
2350 213 : if (!_disjoint_neighbor_boundary_pairs || _disjoint_neighbor_boundary_pairs->empty())
2351 0 : return;
2352 :
2353 6 : auto & pairs = *_disjoint_neighbor_boundary_pairs;
2354 :
2355 : // Helper to check and erase both directions
2356 402 : auto erase_if_match = [](boundary_id_type key,
2357 : boundary_id_type pair,
2358 24 : PeriodicBoundaries & pb_map)
2359 : {
2360 12 : auto it = pb_map.find(key);
2361 426 : if (it != pb_map.end())
2362 : {
2363 12 : const auto & pb = *(it->second);
2364 : // Check both directions
2365 426 : if ((pb.myboundary == key && pb.pairedboundary == pair) ||
2366 0 : (pb.pairedboundary == key && pb.myboundary == pair))
2367 414 : pb_map.erase(it);
2368 : }
2369 426 : };
2370 :
2371 213 : erase_if_match(b1, b2, pairs);
2372 213 : erase_if_match(b2, b1, pairs);
2373 : }
2374 :
2375 :
2376 : #endif
2377 :
2378 :
2379 :
2380 0 : void MeshBase::set_point_locator_close_to_point_tol(Real val)
2381 : {
2382 0 : _point_locator_close_to_point_tol = val;
2383 0 : if (_point_locator)
2384 : {
2385 0 : if (val > 0.)
2386 0 : _point_locator->set_close_to_point_tol(val);
2387 : else
2388 0 : _point_locator->unset_close_to_point_tol();
2389 : }
2390 0 : }
2391 :
2392 :
2393 :
2394 426 : Real MeshBase::get_point_locator_close_to_point_tol() const
2395 : {
2396 426 : return _point_locator_close_to_point_tol;
2397 : }
2398 :
2399 :
2400 :
2401 5471 : void MeshBase::size_elem_extra_integers()
2402 : {
2403 444 : const std::size_t new_size = _elem_integer_names.size();
2404 :
2405 : Threads::parallel_for
2406 5471 : (this->element_stored_range(),
2407 11108 : [new_size, this](const ElemRange & range)
2408 : {
2409 18776 : for (Elem * elem : range)
2410 13305 : elem->add_extra_integers(new_size, this->_elem_integer_default_values);
2411 5317 : });
2412 5471 : }
2413 :
2414 :
2415 :
2416 16810 : void MeshBase::size_node_extra_integers()
2417 : {
2418 1174 : const std::size_t new_size = _node_integer_names.size();
2419 417233 : for (auto node : this->node_ptr_range())
2420 216374 : node->add_extra_integers(new_size, _node_integer_default_values);
2421 16810 : }
2422 :
2423 :
2424 : std::pair<std::vector<unsigned int>, std::vector<unsigned int>>
2425 38517 : MeshBase::merge_extra_integer_names(const MeshBase & other)
2426 : {
2427 13204 : std::pair<std::vector<unsigned int>, std::vector<unsigned int>> returnval;
2428 38517 : returnval.first = this->add_elem_integers(other._elem_integer_names, true, &other._elem_integer_default_values);
2429 38517 : returnval.second = this->add_node_integers(other._node_integer_names, true, &other._node_integer_default_values);
2430 38517 : return returnval;
2431 : }
2432 :
2433 :
2434 :
2435 : void
2436 426 : MeshBase::post_dofobject_moves(MeshBase && other_mesh)
2437 : {
2438 : // Now that all the DofObject moving is done, we can move the GhostingFunctor objects
2439 : // which include the _default_ghosting,_ghosting_functors and _shared_functors. We also need
2440 : // to set the mesh object associated with these functors to the assignee mesh.
2441 :
2442 : // _default_ghosting
2443 12 : _default_ghosting = std::move(other_mesh._default_ghosting);
2444 426 : _default_ghosting->set_mesh(this);
2445 :
2446 : // _ghosting_functors
2447 426 : _ghosting_functors = std::move(other_mesh._ghosting_functors);
2448 :
2449 852 : for (const auto gf : _ghosting_functors )
2450 : {
2451 426 : gf->set_mesh(this);
2452 : }
2453 :
2454 : // _shared_functors
2455 12 : _shared_functors = std::move(other_mesh._shared_functors);
2456 :
2457 426 : for (const auto & sf : _shared_functors )
2458 : {
2459 0 : (sf.second)->set_mesh(this);
2460 : }
2461 :
2462 : // _constraint_rows
2463 12 : _constraint_rows = std::move(other_mesh._constraint_rows);
2464 :
2465 426 : if (other_mesh.partitioner())
2466 426 : _partitioner = std::move(other_mesh.partitioner());
2467 426 : }
2468 :
2469 :
2470 : void
2471 0 : MeshBase::copy_cached_data(const MeshBase & other_mesh)
2472 : {
2473 0 : this->_spatial_dimension = other_mesh._spatial_dimension;
2474 0 : this->_elem_dims = other_mesh._elem_dims;
2475 0 : this->_elem_default_orders = other_mesh._elem_default_orders;
2476 0 : this->_supported_nodal_order = other_mesh._supported_nodal_order;
2477 0 : this->_mesh_subdomains = other_mesh._mesh_subdomains;
2478 0 : }
2479 :
2480 :
2481 39425 : bool MeshBase::nodes_and_elements_equal(const MeshBase & other_mesh) const
2482 : {
2483 7827786 : for (const auto & other_node : other_mesh.node_ptr_range())
2484 : {
2485 6500417 : const Node * node = this->query_node_ptr(other_node->id());
2486 6500417 : if (!node)
2487 0 : return false;
2488 6500417 : if (*other_node != *node)
2489 0 : return false;
2490 13279 : }
2491 7827786 : for (const auto & node : this->node_ptr_range())
2492 6500417 : if (!other_mesh.query_node_ptr(node->id()))
2493 13279 : return false;
2494 :
2495 3958036 : for (const auto & other_elem : other_mesh.element_ptr_range())
2496 : {
2497 3469197 : const Elem * elem = this->query_elem_ptr(other_elem->id());
2498 3469197 : if (!elem)
2499 0 : return false;
2500 3469197 : if (!other_elem->topologically_equal(*elem))
2501 6 : return false;
2502 13279 : }
2503 3957760 : for (const auto & elem : this->element_ptr_range())
2504 3469056 : if (!other_mesh.query_elem_ptr(elem->id()))
2505 13150 : return false;
2506 :
2507 39284 : return true;
2508 : }
2509 :
2510 :
2511 764 : dof_id_type MeshBase::n_constraint_rows() const
2512 : {
2513 764 : dof_id_type n_local_rows=0, n_unpartitioned_rows=0;
2514 764 : for (const auto & [node, node_constraints] : _constraint_rows)
2515 : {
2516 : // Unpartitioned nodes
2517 0 : if (node->processor_id() == DofObject::invalid_processor_id)
2518 0 : n_unpartitioned_rows++;
2519 0 : else if (node->processor_id() == this->processor_id())
2520 0 : n_local_rows++;
2521 : }
2522 :
2523 764 : this->comm().sum(n_local_rows);
2524 :
2525 764 : return n_unpartitioned_rows + n_local_rows;
2526 : }
2527 :
2528 :
2529 : void
2530 36742 : MeshBase::copy_constraint_rows(const MeshBase & other_mesh)
2531 : {
2532 26308 : LOG_SCOPE("copy_constraint_rows(mesh)", "MeshBase");
2533 :
2534 13942 : _constraint_rows.clear();
2535 :
2536 13154 : const auto & other_constraint_rows = other_mesh.get_constraint_rows();
2537 97350 : for (const auto & [other_node, other_node_constraints] : other_constraint_rows)
2538 : {
2539 60608 : const Node * const our_node = this->node_ptr(other_node->id());
2540 30776 : constraint_rows_mapped_type our_node_constraints;
2541 259765 : for (const auto & [other_inner_key_pair, constraint_value] : other_node_constraints)
2542 : {
2543 36996 : const auto & [other_elem, local_node_id] = other_inner_key_pair;
2544 199157 : const Elem * const our_elem = this->elem_ptr(other_elem->id());
2545 199157 : our_node_constraints.emplace_back(std::make_pair(our_elem, local_node_id), constraint_value);
2546 : }
2547 60608 : _constraint_rows[our_node] = std::move(our_node_constraints);
2548 : }
2549 36742 : }
2550 :
2551 :
2552 : template <typename T>
2553 : void
2554 565 : MeshBase::copy_constraint_rows(const SparseMatrix<T> & constraint_operator,
2555 : bool precondition_constraint_operator)
2556 : {
2557 32 : LOG_SCOPE("copy_constraint_rows(mat)", "MeshBase");
2558 :
2559 16 : this->_constraint_rows.clear();
2560 :
2561 : // We're not going to support doing this distributed yet; it'd be
2562 : // pointless unless we temporarily had a linear partitioning to
2563 : // better match the constraint operator.
2564 597 : MeshSerializer serialize(*this);
2565 :
2566 : // Our current mesh should already reflect the desired assembly space
2567 565 : libmesh_error_msg_if(this->n_nodes() != constraint_operator.m(),
2568 : "Constraint operator matrix with " <<
2569 : constraint_operator.m() <<
2570 : "rows does not match this mesh with " <<
2571 : this->n_nodes() << " nodes");
2572 :
2573 : // First, find what new unconstrained DoFs we need to add. We can't
2574 : // iterate over columns in a SparseMatrix, so we'll iterate over
2575 : // rows and keep track of columns.
2576 :
2577 : // If we have nodes that will work unconstrained, keep track of
2578 : // their node ids and corresponding column indices.
2579 : // existing_unconstrained_nodes[column_id] = node_id
2580 32 : std::map<dof_id_type, dof_id_type> existing_unconstrained_columns;
2581 32 : std::set<dof_id_type> existing_unconstrained_nodes;
2582 :
2583 : // In case we need new nodes, keep track of their columns.
2584 : // columns[j][k] will be the kth row index and value of column j
2585 : typedef
2586 : std::unordered_map<dof_id_type,
2587 : std::vector<std::pair<dof_id_type, Real>>>
2588 : columns_type;
2589 1114 : columns_type columns(constraint_operator.n());
2590 :
2591 : // If we need to precondition the constraint operator (e.g. it's an
2592 : // unpreconditioned extraction operator for a Flex IGA matrix),
2593 : // we'll want to keep track of the sum of each column, because we'll
2594 : // be dividing each column by that sum (Jacobi preconditioning on
2595 : // the right, which then leads to symmetric preconditioning on a
2596 : // physics Jacobian).
2597 32 : std::unordered_map<dof_id_type, Real> column_sums;
2598 :
2599 : // Work in parallel, though we'll have to sync shortly
2600 4028 : for (auto i : make_range(constraint_operator.row_start(),
2601 533 : constraint_operator.row_stop()))
2602 : {
2603 618 : std::vector<numeric_index_type> indices;
2604 618 : std::vector<T> values;
2605 :
2606 3463 : constraint_operator.get_row(i, indices, values);
2607 309 : libmesh_assert_equal_to(indices.size(), values.size());
2608 :
2609 3847 : if (indices.size() == 1 &&
2610 825 : values[0] == T(1))
2611 : {
2612 : // If we have multiple simple Ui=Uj constraints, let the
2613 : // first one be our "unconstrained" node and let the others
2614 : // be constrained to it.
2615 810 : if (existing_unconstrained_columns.find(indices[0]) !=
2616 138 : existing_unconstrained_columns.end())
2617 : {
2618 36 : const auto j = indices[0];
2619 36 : columns[j].emplace_back(i, 1);
2620 : }
2621 : else
2622 : {
2623 708 : existing_unconstrained_nodes.insert(i);
2624 774 : existing_unconstrained_columns.emplace(indices[0],i);
2625 : }
2626 : }
2627 : else
2628 22028 : for (auto jj : index_range(indices))
2629 : {
2630 19375 : const auto j = indices[jj];
2631 21134 : const Real coef = libmesh_real(values[jj]);
2632 1759 : libmesh_assert_equal_to(coef, values[jj]);
2633 19375 : columns[j].emplace_back(i, coef);
2634 : }
2635 : }
2636 :
2637 : // Merge data from different processors' slabs of the matrix
2638 565 : this->comm().set_union(existing_unconstrained_nodes);
2639 565 : this->comm().set_union(existing_unconstrained_columns);
2640 :
2641 48 : std::vector<columns_type> all_columns;
2642 565 : this->comm().allgather(columns, all_columns);
2643 :
2644 16 : columns.clear();
2645 6106 : for (auto p : index_range(all_columns))
2646 65601 : for (auto & [j, subcol] : all_columns[p])
2647 183429 : for (auto [i, v] : subcol)
2648 123369 : columns[j].emplace_back(i,v);
2649 :
2650 : // Keep track of elements on which unconstrained nodes exist, and
2651 : // their local node indices.
2652 : // node_to_elem_ptrs[node] = [elem_id, local_node_num]
2653 32 : std::unordered_map<const Node *, std::pair<dof_id_type, unsigned int>> node_to_elem_ptrs;
2654 :
2655 : // Find elements attached to any existing nodes that will stay
2656 : // unconstrained. We'll also build a subdomain set here so we don't
2657 : // have to assert that the mesh is already prepared before we pick a
2658 : // new subdomain for any NodeElems we need to add.
2659 32 : std::set<subdomain_id_type> subdomain_ids;
2660 145181 : for (const Elem * elem : this->element_ptr_range())
2661 : {
2662 49738 : subdomain_ids.insert(elem->subdomain_id());
2663 244572 : for (auto n : make_range(elem->n_nodes()))
2664 : {
2665 194834 : const Node * node = elem->node_ptr(n);
2666 194834 : if (existing_unconstrained_nodes.count(node->id()))
2667 12175 : node_to_elem_ptrs.emplace(node, std::make_pair(elem->id(), n));
2668 : }
2669 : }
2670 :
2671 565 : const subdomain_id_type new_sbd_id = *subdomain_ids.rbegin() + 1;
2672 :
2673 23189 : for (auto j : make_range(constraint_operator.n()))
2674 : {
2675 : // If we already have a good node for this then we're done
2676 4800 : if (existing_unconstrained_columns.count(j))
2677 4668 : continue;
2678 :
2679 : // Get a half-decent spot to place a new NodeElem for
2680 : // unconstrained DoF(s) here. Getting a *fully*-decent spot
2681 : // would require finding a Moore-Penrose pseudoinverse, and I'm
2682 : // not going to do that, but scaling a transpose will at least
2683 : // get us a little uniqueness to make visualization reasonable.
2684 264 : Point newpt;
2685 264 : Real total_scaling = 0;
2686 264 : unsigned int total_entries = 0;
2687 :
2688 : // We'll get a decent initial pid choice here too, if only to
2689 : // aid in later repartitioning.
2690 528 : std::map<processor_id_type, int> pids;
2691 :
2692 264 : auto & column = columns[j];
2693 122230 : for (auto [i, r] : column)
2694 : {
2695 112988 : Node & constrained_node = this->node_ref(i);
2696 112988 : const Point constrained_pt = constrained_node;
2697 3228 : newpt += r*constrained_pt;
2698 112988 : total_scaling += r;
2699 112988 : ++total_entries;
2700 112988 : ++pids[constrained_node.processor_id()];
2701 : }
2702 :
2703 9242 : if (precondition_constraint_operator)
2704 0 : column_sums[j] = total_scaling;
2705 :
2706 9242 : libmesh_error_msg_if
2707 : (!total_entries,
2708 : "Empty column " << j <<
2709 : " found in constraint operator matrix");
2710 :
2711 : // If we have *cancellation* here then we can end up dividing by
2712 : // zero; try just evenly scaling across all constrained node
2713 : // points instead.
2714 9242 : if (total_scaling > TOLERANCE)
2715 264 : newpt /= total_scaling;
2716 : else
2717 0 : newpt /= total_entries;
2718 :
2719 9242 : Node *n = this->add_point(newpt);
2720 9506 : std::unique_ptr<Elem> elem = Elem::build(NODEELEM);
2721 9242 : elem->set_node(0, n);
2722 9242 : elem->subdomain_id() = new_sbd_id;
2723 :
2724 9506 : Elem * added_elem = this->add_elem(std::move(elem));
2725 9242 : this->_elem_dims.insert(0);
2726 9242 : this->_elem_default_orders.insert(added_elem->default_order());
2727 9242 : this->_supported_nodal_order =
2728 8714 : static_cast<Order>
2729 18484 : (std::min(static_cast<int>(this->_supported_nodal_order),
2730 9242 : static_cast<int>(added_elem->supported_nodal_order())));
2731 8978 : this->_mesh_subdomains.insert(new_sbd_id);
2732 9242 : node_to_elem_ptrs.emplace(n, std::make_pair(added_elem->id(), 0));
2733 9242 : existing_unconstrained_columns.emplace(j,n->id());
2734 :
2735 : // Repartition the new objects *after* adding them, so a
2736 : // DistributedMesh doesn't get confused and think you're not
2737 : // adding them on all processors at once.
2738 264 : int n_pids = 0;
2739 43912 : for (auto [pid, count] : pids)
2740 34670 : if (count >= n_pids)
2741 : {
2742 324 : n_pids = count;
2743 15469 : added_elem->processor_id() = pid;
2744 15469 : n->processor_id() = pid;
2745 : }
2746 : }
2747 :
2748 : // Calculate constraint rows in an indexed form that's easy for us
2749 : // to allgather
2750 : std::unordered_map<dof_id_type,
2751 : std::vector<std::pair<std::pair<dof_id_type, unsigned int>,Real>>>
2752 32 : indexed_constraint_rows;
2753 :
2754 4028 : for (auto i : make_range(constraint_operator.row_start(),
2755 533 : constraint_operator.row_stop()))
2756 : {
2757 951 : if (existing_unconstrained_nodes.count(i))
2758 774 : continue;
2759 :
2760 486 : std::vector<numeric_index_type> indices;
2761 486 : std::vector<T> values;
2762 :
2763 2689 : constraint_operator.get_row(i, indices, values);
2764 :
2765 486 : std::vector<std::pair<std::pair<dof_id_type, unsigned int>, Real>> constraint_row;
2766 :
2767 22100 : for (auto jj : index_range(indices))
2768 : {
2769 21173 : const dof_id_type node_id =
2770 : existing_unconstrained_columns[indices[jj]];
2771 :
2772 19411 : Node & constraining_node = this->node_ref(node_id);
2773 :
2774 1762 : libmesh_assert(node_to_elem_ptrs.count(&constraining_node));
2775 :
2776 19411 : auto p = node_to_elem_ptrs[&constraining_node];
2777 :
2778 19382 : Real coef = libmesh_real(values[jj]);
2779 1762 : libmesh_assert_equal_to(coef, values[jj]);
2780 :
2781 : // If we're preconditioning and we created a nodeelem then
2782 : // we can scale the meaning of that nodeelem's value to give
2783 : // us a better-conditioned matrix after the constraints are
2784 : // applied.
2785 19411 : if (precondition_constraint_operator)
2786 0 : if (auto sum_it = column_sums.find(indices[jj]);
2787 0 : sum_it != column_sums.end())
2788 : {
2789 0 : const Real scaling = sum_it->second;
2790 :
2791 0 : if (scaling > TOLERANCE)
2792 0 : coef /= scaling;
2793 : }
2794 :
2795 21173 : constraint_row.emplace_back(std::make_pair(p, coef));
2796 : }
2797 :
2798 243 : indexed_constraint_rows.emplace(i, std::move(constraint_row));
2799 : }
2800 :
2801 565 : this->comm().set_union(indexed_constraint_rows);
2802 :
2803 : // Add constraint rows as mesh constraint rows
2804 17591 : for (auto & [node_id, indexed_row] : indexed_constraint_rows)
2805 : {
2806 17026 : Node * constrained_node = this->node_ptr(node_id);
2807 :
2808 972 : constraint_rows_mapped_type constraint_row;
2809 :
2810 140395 : for (auto [p, coef] : indexed_row)
2811 : {
2812 123369 : const Elem * elem = this->elem_ptr(p.first);
2813 7048 : constraint_row.emplace_back
2814 126893 : (std::make_pair(std::make_pair(elem, p.second), coef));
2815 : }
2816 :
2817 16540 : this->_constraint_rows.emplace(constrained_node,
2818 486 : std::move(constraint_row));
2819 : }
2820 1098 : }
2821 :
2822 :
2823 0 : void MeshBase::print_constraint_rows(std::ostream & os,
2824 : bool print_nonlocal) const
2825 : {
2826 0 : parallel_object_only();
2827 :
2828 : std::string local_constraints =
2829 0 : this->get_local_constraints(print_nonlocal);
2830 :
2831 0 : if (this->processor_id())
2832 : {
2833 0 : this->comm().send(0, local_constraints);
2834 : }
2835 : else
2836 : {
2837 0 : os << "Processor 0:\n";
2838 0 : os << local_constraints;
2839 :
2840 0 : for (auto p : IntRange<processor_id_type>(1, this->n_processors()))
2841 : {
2842 0 : this->comm().receive(p, local_constraints);
2843 0 : os << "Processor " << p << ":\n";
2844 0 : os << local_constraints;
2845 : }
2846 : }
2847 0 : }
2848 :
2849 :
2850 :
2851 0 : std::string MeshBase::get_local_constraints(bool print_nonlocal) const
2852 : {
2853 0 : std::ostringstream os;
2854 :
2855 0 : if (print_nonlocal)
2856 0 : os << "All ";
2857 : else
2858 0 : os << "Local ";
2859 :
2860 0 : os << "Mesh Constraint Rows:"
2861 0 : << std::endl;
2862 :
2863 0 : for (const auto & [node, row] : _constraint_rows)
2864 : {
2865 0 : const bool local = (node->processor_id() == this->processor_id());
2866 :
2867 : // Skip non-local dofs if requested
2868 0 : if (!print_nonlocal && !local)
2869 0 : continue;
2870 :
2871 0 : os << "Constraints for " << (local ? "Local" : "Ghost") << " Node " << node->id()
2872 0 : << ": \t";
2873 :
2874 0 : for (const auto & [elem_and_node, coef] : row)
2875 0 : os << " ((" << elem_and_node.first->id() << ',' << elem_and_node.second << "), " << coef << ")\t";
2876 :
2877 0 : os << std::endl;
2878 : }
2879 :
2880 0 : return os.str();
2881 0 : }
2882 :
2883 378377 : MeshBase::Preparation::Preparation() :
2884 315309 : is_partitioned(false),
2885 315309 : has_synched_id_counts(false),
2886 315309 : has_neighbor_ptrs(false),
2887 315309 : has_cached_elem_data(false),
2888 315309 : has_interior_parent_ptrs(false),
2889 315309 : has_removed_remote_elements(false),
2890 315309 : has_removed_orphaned_nodes(false),
2891 315309 : has_boundary_id_sets(false),
2892 315309 : has_reinit_ghosting_functors(false),
2893 378377 : has_synched_subdomain_name_map(false)
2894 378377 : {}
2895 :
2896 622244 : MeshBase::Preparation::operator bool() const
2897 : {
2898 1215118 : return is_partitioned &&
2899 592874 : has_synched_id_counts &&
2900 592874 : has_neighbor_ptrs &&
2901 592874 : has_cached_elem_data &&
2902 592868 : has_interior_parent_ptrs &&
2903 592868 : has_removed_remote_elements &&
2904 592868 : has_removed_orphaned_nodes &&
2905 592868 : has_reinit_ghosting_functors &&
2906 1309690 : has_boundary_id_sets &&
2907 688322 : has_synched_subdomain_name_map;
2908 : }
2909 :
2910 : MeshBase::Preparation &
2911 1491690 : MeshBase::Preparation::operator= (bool set_all)
2912 : {
2913 1491690 : is_partitioned = set_all;
2914 1491690 : has_synched_id_counts = set_all;
2915 1491690 : has_neighbor_ptrs = set_all;
2916 1491690 : has_cached_elem_data = set_all;
2917 1491690 : has_interior_parent_ptrs = set_all;
2918 1491690 : has_removed_remote_elements = set_all;
2919 1491690 : has_removed_orphaned_nodes = set_all;
2920 1491690 : has_reinit_ghosting_functors = set_all;
2921 1491690 : has_boundary_id_sets = set_all;
2922 1491690 : has_synched_subdomain_name_map = set_all;
2923 :
2924 1491690 : return *this;
2925 : }
2926 :
2927 : bool
2928 85091 : MeshBase::Preparation::operator== (const Preparation & other) const
2929 : {
2930 85091 : if (is_partitioned != other.is_partitioned)
2931 17956 : return false;
2932 65413 : if (has_synched_id_counts != other.has_synched_id_counts)
2933 6 : return false;
2934 65407 : if (has_neighbor_ptrs != other.has_neighbor_ptrs)
2935 348 : return false;
2936 65059 : if (has_cached_elem_data != other.has_cached_elem_data)
2937 0 : return false;
2938 65059 : if (has_interior_parent_ptrs != other.has_interior_parent_ptrs)
2939 0 : return false;
2940 65059 : if (has_removed_remote_elements != other.has_removed_remote_elements)
2941 12856 : return false;
2942 52203 : if (has_removed_orphaned_nodes != other.has_removed_orphaned_nodes)
2943 0 : return false;
2944 52203 : if (has_reinit_ghosting_functors != other.has_reinit_ghosting_functors)
2945 0 : return false;
2946 52203 : if (has_boundary_id_sets != other.has_boundary_id_sets)
2947 0 : return false;
2948 52203 : if (has_synched_subdomain_name_map != other.has_synched_subdomain_name_map)
2949 0 : return false;
2950 :
2951 38114 : return true;
2952 : }
2953 :
2954 : bool
2955 39851 : MeshBase::Preparation::operator!= (const Preparation & other) const
2956 : {
2957 39851 : return !(*this == other);
2958 : }
2959 :
2960 :
2961 : void
2962 645929 : MeshBase::Preparation::libmesh_assert_consistent (const Parallel::Communicator & libmesh_dbg_var(comm))
2963 : {
2964 31100 : libmesh_assert(comm.verify(is_partitioned));
2965 31100 : libmesh_assert(comm.verify(has_synched_id_counts));
2966 31100 : libmesh_assert(comm.verify(has_neighbor_ptrs));
2967 31100 : libmesh_assert(comm.verify(has_cached_elem_data));
2968 31100 : libmesh_assert(comm.verify(has_interior_parent_ptrs));
2969 31100 : libmesh_assert(comm.verify(has_removed_remote_elements));
2970 31100 : libmesh_assert(comm.verify(has_removed_orphaned_nodes));
2971 31100 : libmesh_assert(comm.verify(has_reinit_ghosting_functors));
2972 31100 : libmesh_assert(comm.verify(has_boundary_id_sets));
2973 645929 : }
2974 :
2975 :
2976 : // Explicit instantiations for our template function
2977 : template LIBMESH_EXPORT void
2978 : MeshBase::copy_constraint_rows(const SparseMatrix<Real> & constraint_operator,
2979 : bool precondition_constraint_operator);
2980 :
2981 : #ifdef LIBMESH_USE_COMPLEX_NUMBERS
2982 : template LIBMESH_EXPORT void
2983 : MeshBase::copy_constraint_rows(const SparseMatrix<Complex> & constraint_operator,
2984 : bool precondition_constraint_operator);
2985 : #endif
2986 :
2987 :
2988 : } // namespace libMesh
|