21 #include "libmesh/distributed_mesh.h" 24 #include "libmesh/boundary_info.h" 25 #include "libmesh/elem.h" 26 #include "libmesh/libmesh_logging.h" 27 #include "libmesh/mesh_communication.h" 28 #include "libmesh/mesh_tools.h" 29 #include "libmesh/partitioner.h" 30 #include "libmesh/string_to_enum.h" 45 _is_serial_on_proc_0(true),
46 _deleted_coarse_elements(false),
47 _n_nodes(0), _n_elem(0), _max_node_id(0), _max_elem_id(0),
48 _next_free_local_node_id(this->processor_id()),
49 _next_free_local_elem_id(this->processor_id()),
50 _next_free_unpartitioned_node_id(this->n_processors()),
51 _next_free_unpartitioned_elem_id(this->n_processors())
52 #ifdef LIBMESH_ENABLE_UNIQUE_ID
53 , _next_unpartitioned_unique_id(this->n_processors())
56 #ifdef LIBMESH_ENABLE_UNIQUE_ID 60 const std::string default_partitioner =
"parmetis";
61 const std::string my_partitioner =
65 (Utility::string_to_enum<PartitionerType>(my_partitioner));
70 LOG_SCOPE(
"operator=(&&)",
"DistributedMesh");
93 *
this = std::move(cast_ref<DistributedMesh&>(other_mesh));
124 !this->nodes_and_elements_equal(other_mesh))
170 #ifdef LIBMESH_ENABLE_UNIQUE_ID 186 _is_serial_on_proc_0(other_mesh.is_serial()),
187 _deleted_coarse_elements(true),
188 _n_nodes(0), _n_elem(0), _max_node_id(0), _max_elem_id(0),
189 _next_free_local_node_id(this->processor_id()),
190 _next_free_local_elem_id(this->processor_id()),
191 _next_free_unpartitioned_node_id(this->n_processors()),
192 _next_free_unpartitioned_elem_id(this->n_processors())
210 this_boundary_info = other_boundary_info;
214 #ifdef LIBMESH_ENABLE_UNIQUE_ID 225 DistributedMesh & other_mesh = cast_ref<DistributedMesh&>(other_meshbase);
245 #ifdef LIBMESH_ENABLE_UNIQUE_ID 257 parallel_object_only();
282 #ifdef LIBMESH_ENABLE_UNIQUE_ID 301 parallel_object_only();
314 parallel_object_only();
327 for (; rit != rend; ++rit)
333 max_local = d->
id() + 1;
344 #ifdef LIBMESH_ENABLE_UNIQUE_ID 348 parallel_object_only();
375 parallel_object_only();
388 parallel_object_only();
401 for (; rit != rend; ++rit)
407 max_local = d->
id() + 1;
428 libmesh_assert_equal_to (
_nodes[i]->
id(), i);
439 libmesh_assert_equal_to (
_nodes[i]->
id(), i);
449 if (
const auto it =
_nodes.find(i);
452 const Node * n = *it;
465 if (
auto it =
_nodes.find(i);
482 libmesh_assert_equal_to (
_elements[i]->
id(), i);
493 libmesh_assert_equal_to (
_elements[i]->
id(), i);
506 const Elem * e = *it;
565 static_cast<dof_id_type>(e->
id()+1));
600 #ifdef LIBMESH_ENABLE_UNIQUE_ID 665 #ifdef LIBMESH_ENABLE_UNIQUE_ID 766 if (old_id == new_id)
771 libmesh_assert_equal_to (el->
id(), old_id);
855 static_cast<dof_id_type>(n->
id()+1));
885 #ifdef LIBMESH_ENABLE_UNIQUE_ID 966 if (old_id == new_id)
971 libmesh_assert_equal_to (nd->
id(), old_id);
981 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS 1012 for (
auto & node :
_nodes)
1098 template <
typename T>
1102 parallel_object_only();
1106 const dof_id_type pmax_id = std::max(pmax_node_id, pmax_elem_id);
1110 T * obj = objects[i];
1117 const dof_id_type dofid = obj && obj->valid_id() ?
1123 const dof_id_type procid = obj && obj->valid_processor_id() ?
1145 #if defined(LIBMESH_ENABLE_UNIQUE_ID) && !defined(NDEBUG) 1167 parallel_object_only();
1188 #if defined(LIBMESH_ENABLE_AMR) && !defined(NDEBUG) 1190 parallel_object_only();
1198 unsigned int refinement_flag = el ?
1200 unsigned int p_refinement_flag = el ?
1209 #endif // LIBMESH_ENABLE_AMR 1214 template <
typename T>
1219 parallel_object_only();
1227 std::unordered_map<processor_id_type, dof_id_type>
1228 ghost_objects_from_proc;
1230 object_iterator it = objects.
begin();
1231 object_iterator end = objects.
end();
1239 it = objects.
erase(it);
1244 unpartitioned_objects++;
1246 ghost_objects_from_proc[obj_procid]++;
1253 std::vector<dof_id_type> objects_on_proc(this->
n_processors(), 0);
1254 auto this_it = ghost_objects_from_proc.find(this->
processor_id());
1256 ((this_it == ghost_objects_from_proc.end()) ?
1257 dof_id_type(0) : this_it->second, objects_on_proc);
1262 if (ghost_objects_from_proc.count(p))
1263 libmesh_assert_less_equal (ghost_objects_from_proc[p], objects_on_proc[p]);
1265 libmesh_assert_less_equal (0, objects_on_proc[p]);
1269 std::vector<dof_id_type> first_object_on_proc(this->
n_processors());
1271 first_object_on_proc[i] = first_object_on_proc[i-1] +
1272 objects_on_proc[i-1];
1277 unpartitioned_objects;
1283 std::map<processor_id_type, std::vector<dof_id_type>>
1288 auto ghost_end = ghost_objects_from_proc.end();
1292 if (
const auto p_it = ghost_objects_from_proc.find(p);
1294 requested_ids[p].reserve(p_it->second);
1297 end = objects.
end();
1298 for (it = objects.
begin(); it != end; ++it)
1304 obj->set_id(next_id++);
1306 requested_ids[obj->processor_id()].push_back(obj->id());
1311 auto gather_functor =
1315 &first_object_on_proc,
1320 std::vector<dof_id_type> & new_ids)
1322 std::size_t ids_size = ids.size();
1323 new_ids.resize(ids_size);
1325 for (std::size_t i=0; i != ids_size; ++i)
1327 T * obj = objects[ids[i]];
1329 libmesh_assert_equal_to (obj->processor_id(), this->
processor_id());
1330 new_ids[i] = obj->id();
1332 libmesh_assert_greater_equal (new_ids[i],
1334 libmesh_assert_less (new_ids[i],
1340 auto action_functor =
1343 &first_object_on_proc,
1348 const std::vector<dof_id_type> & ids,
1349 const std::vector<dof_id_type> & data)
1354 T * obj = objects[ids[i]];
1356 libmesh_assert_equal_to (obj->processor_id(),
pid);
1357 libmesh_assert_greater_equal (data[i],
1358 first_object_on_proc[
pid]);
1359 libmesh_assert_less (data[i],
1360 first_object_on_proc[
pid] +
1361 objects_on_proc[
pid]);
1362 obj->set_id(data[i]);
1367 Parallel::pull_parallel_vector_data
1368 (this->
comm(), requested_ids, gather_functor, action_functor, ex);
1370 #ifdef LIBMESH_ENABLE_UNIQUE_ID 1371 auto unique_gather_functor =
1378 std::vector<unique_id_type> & data)
1380 std::size_t ids_size = ids.size();
1381 data.resize(ids_size);
1383 for (std::size_t i=0; i != ids_size; ++i)
1385 T * obj = objects[ids[i]];
1387 libmesh_assert_equal_to (obj->processor_id(), this->
processor_id());
1392 auto unique_action_functor =
1395 const std::vector<dof_id_type> & ids,
1396 const std::vector<unique_id_type> & data)
1400 T * obj = objects[ids[i]];
1402 libmesh_assert_equal_to (obj->processor_id(),
pid);
1404 obj->set_unique_id(data[i]);
1409 Parallel::pull_parallel_vector_data
1410 (this->
comm(), requested_ids, unique_gather_functor,
1411 unique_action_functor, unique_ex);
1417 next_id += objects_on_proc[i];
1418 for (it = objects.
begin(); it != end; ++it)
1424 obj->set_id(next_id++);
1429 it = objects.
begin();
1430 end = objects.
end();
1436 T *
next = objects[obj->id()];
1450 libmesh_assert_not_equal_to (
next->id(), obj->id());
1451 objects[obj->id()] = obj;
1453 next = objects[obj->id()];
1455 objects[obj->id()] = obj;
1461 it = objects.
erase(it);
1466 return first_free_id;
1472 parallel_object_only();
1481 LOG_SCOPE(
"renumber_nodes_and_elements()",
"DistributedMesh");
1492 std::set<dof_id_type> used_nodes;
1496 std::map<processor_id_type, std::map<dof_id_type, bool>>
1500 for (
auto & elem : this->element_ptr_range())
1501 for (
const Node & node : elem->node_ref_range())
1504 used_nodes.insert(n);
1510 auto & used_nodes_on_p = used_nodes_on_proc[p];
1512 used_nodes_on_p[n] =
true;
1514 if (!used_nodes_on_p.count(n))
1515 used_nodes_on_p[n] =
false;
1523 typedef unsigned char boolish;
1524 std::map<processor_id_type, std::vector<std::pair<dof_id_type, boolish>>>
1525 used_nodes_on_proc_vecs;
1526 for (
auto & [
pid, nodemap] : used_nodes_on_proc)
1527 used_nodes_on_proc_vecs[
pid].assign(nodemap.begin(), nodemap.end());
1529 std::map<dof_id_type,processor_id_type> repartitioned_node_pids;
1530 std::map<processor_id_type, std::set<dof_id_type>>
1531 repartitioned_node_sets_to_push;
1533 auto ids_action_functor =
1534 [&used_nodes, &repartitioned_node_pids,
1535 &repartitioned_node_sets_to_push]
1537 const std::vector<std::pair<dof_id_type, boolish>> & ids_and_bools)
1539 for (
auto [n, sender_could_become_owner] : ids_and_bools)
1543 if (!used_nodes.count(n))
1545 if (
auto it = repartitioned_node_pids.find(n);
1546 sender_could_become_owner)
1548 if (it != repartitioned_node_pids.end() &&
1552 repartitioned_node_pids[n] =
pid;
1555 if (it == repartitioned_node_pids.end())
1556 repartitioned_node_pids[n] =
1559 repartitioned_node_sets_to_push[
pid].insert(n);
1567 Parallel::push_parallel_vector_data
1568 (this->
comm(), used_nodes_on_proc_vecs, ids_action_functor);
1571 for (
auto & [n, p] : repartitioned_node_pids)
1581 std::map<processor_id_type, std::vector<std::pair<dof_id_type,processor_id_type>>>
1582 repartitioned_node_vecs;
1584 for (
auto & [p, nodeset] : repartitioned_node_sets_to_push)
1586 auto & rn_vec = repartitioned_node_vecs[p];
1587 for (
auto n : nodeset)
1588 rn_vec.emplace_back(n, repartitioned_node_pids[n]);
1591 auto repartition_node_functor =
1594 const std::vector<std::pair<dof_id_type, processor_id_type>> & ids_and_pids)
1596 for (
auto [n, p] : ids_and_pids)
1605 Parallel::push_parallel_vector_data
1606 (this->
comm(), repartitioned_node_vecs, repartition_node_functor);
1622 else if (!used_nodes.count(nd->
id()))
1664 libmesh_assert_equal_to (this->
max_node_id(), pmax_node_id);
1665 libmesh_assert_equal_to (this->
max_elem_id(), pmax_elem_id);
1686 for (
auto pr = this->_nodes.begin(),
1687 end = this->_nodes.end(); pr != end; ++pr)
1694 libmesh_assert_equal_to(this->
node_ptr(
id), n);
1700 end = this->
_elements.end(); pr != end; ++pr)
1707 libmesh_assert_equal_to(this->
elem_ptr(
id), e);
1716 parallel_object_only();
1721 this->active_local_elements_end()));
1722 this->
comm().
sum(active_elements);
1730 return active_elements;
1752 libmesh_assert_equal_to (this->
max_node_id(), pmax_node_id);
1753 libmesh_assert_equal_to (this->
max_elem_id(), pmax_elem_id);
1767 while (e_it != e_end)
1775 while (n_it != n_end)
1777 n_it =
_nodes.erase(n_it);
1824 std::set<Elem *> tmp;
1827 std::inserter(tmp, tmp.begin()));
1846 libmesh_assert_equal_to (this->
max_node_id(), pmax_node_id);
1847 libmesh_assert_equal_to (this->
max_elem_id(), pmax_elem_id);
UnstructuredMesh & operator=(const UnstructuredMesh &)=delete
Copy assignment is not allowed.
This mapvector templated class is intended to provide the performance characteristics of a std::map w...
virtual void update_post_partitioning()
Recalculate any cached data (or invalidate any caches that are computed on the fly) after elements an...
RefinementState refinement_flag() const
virtual Node * add_point(const Point &p, const dof_id_type id=DofObject::invalid_id, const processor_id_type proc_id=DofObject::invalid_processor_id) override final
functions for adding /deleting nodes elements.
virtual void update_parallel_id_counts() override
Updates parallel caches so that methods like n_elem() accurately reflect changes on other processors...
void allgather(const T &send_data, std::vector< T, A > &recv_data) const
virtual dof_id_type parallel_n_nodes() const override
const Elem * parent() const
bool _skip_renumber_nodes_and_elements
If this is true then renumbering will be kept to a minimum.
virtual Elem * add_elem(Elem *e) override final
Add elem e to the end of the element array.
static constexpr processor_id_type invalid_processor_id
An invalid processor_id to distinguish DoFs that have not been assigned to a processor.
bool has_removed_remote_elements
A Node is like a Point, but with more information.
virtual unique_id_type parallel_max_unique_id() const =0
dof_id_type n_unpartitioned_nodes() const
const unsigned int invalid_uint
A number which is used quite often to represent an invalid or uninitialized value for an unsigned int...
std::vector< std::string > _elem_integer_names
The array of names for integer data associated with each element in the mesh.
virtual void libmesh_assert_valid_parallel_ids() const override
Verify id and processor_id consistency of our elements and nodes containers.
virtual void fix_broken_node_and_element_numbering() override
There is no reason for a user to ever call this function.
static constexpr unique_id_type invalid_unique_id
An invalid unique_id to distinguish an uninitialized DofObject.
void set_mapping_type(const ElemMappingType type)
Sets the value of the mapping type for the element.
void allow_renumbering(bool allow)
If false is passed in then this mesh will no longer be renumbered when being prepared for use...
virtual void renumber_elem(dof_id_type old_id, dof_id_type new_id) override final
Changes the id of element old_id, both by changing elem(old_id)->id() and by moving elem(old_id) in t...
void skip_partitioning(bool skip)
If true is passed in then nothing on this mesh will be (re)partitioned.
bool _is_serial_on_proc_0
A boolean remembering whether we're serialized to proc 0 or not.
dof_id_type _n_nodes
Cached data from the last renumber_nodes_and_elements call.
bool allow_detect_interior_parents() const
virtual void clear() override
Clear all internal data.
virtual void renumber_nodes_and_elements() override
Remove nullptr elements from arrays.
RefinementState p_refinement_flag() const
virtual dof_id_type n_nodes() const override final
dof_id_type _next_free_local_node_id
Guaranteed globally unused IDs for use when adding new nodes or elements.
void remove(const Node *node)
Removes the boundary conditions associated with node node, if any exist.
virtual void allgather() override
Gathers all elements and nodes of the mesh onto every processor.
This is the base class from which all geometric element types are derived.
dof_id_type n_local_nodes() const
constraint_rows_type _constraint_rows
unique_id_type unique_id() const
void copy_constraint_rows(const MeshBase &other_mesh)
Copy the constraints from the other mesh to this mesh.
const Parallel::Communicator & comm() const
bool _is_serial
A boolean remembering whether we're serialized or not.
unsigned int p_level() const
void libmesh_assert_valid_parallel_flags() const
Verify refinement_flag and p_refinement_flag consistency of our elements containers.
The libMesh namespace provides an interface to certain functionality in the library.
virtual void delete_elem(Elem *e) override final
Removes element e from the mesh.
void allow_detect_interior_parents(bool allow)
If false is passed then this mesh will no longer work to detect interior parents when being prepared ...
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
virtual void add_extra_ghost_elem(Elem *e)
Inserts the element and adds it to a list of elements that should not get deleted or have their desce...
dof_id_type n_unpartitioned_elem() const
std::vector< std::string > _node_integer_names
The array of names for integer data associated with each node in the mesh.
virtual Node * add_node(Node *n) override final
Add Node n to the end of the vertex array.
Real distance(const Point &p)
Preparation preparation() const
virtual const Point & point(const dof_id_type i) const override final
void clear_stored_ranges()
Clears stored ranges, to indicate that the mesh has changed and they should be regenerated when next ...
virtual unique_id_type parallel_max_unique_id() const override
dof_id_type n_local_elem() const
virtual void delete_remote_elements() override
Deletes all nonlocal elements of the mesh except for "ghosts" which touch a local element...
void libmesh_assert_valid_parallel_object_ids(const dofobject_container< T > &) const
Verify id, processor_id, and if applicable unique_id consistency of a parallel objects container...
uint8_t processor_id_type
This is the MeshBase class.
void libmesh_assert_valid_parallel_p_levels() const
Verify p_level consistency of our elements containers.
ElemMappingType default_mapping_type() const
Returns the default master space to physical space mapping basis functions to be used on newly added ...
unique_id_type _next_unique_id
The next available unique id for assigning ids to DOF objects.
virtual void clear() override
Free all new memory associated with the object, but restore its original state, with the mesh pointer...
processor_id_type pid unsigned int level std::set< subdomain_id_type > ss const DofMap &dof_map LIBMESH_COMMA unsigned int dof_map LIBMESH_COMMA var_num unsigned char rflag processor_id_type pid const DofMap &dof_map LIBMESH_COMMA unsigned int dof_map LIBMESH_COMMA var_num DECLARE_NODE_ITERATORS(multi_evaluable_, std::vector< const DofMap * > dof_maps, dof_maps) protected dofobject_container< Node > _nodes
Move node and elements from a DistributedMesh.
void allow_remote_element_removal(bool allow)
If false is passed in then this mesh will no longer have remote elements deleted when being prepared ...
processor_id_type n_processors() const
virtual const Elem * elem_ptr(const dof_id_type i) const override final
friend class MeshCommunication
Make the MeshCommunication class a friend so that it can directly broadcast *_integer_names.
virtual dof_id_type parallel_n_elem() const override
T command_line_value(const std::string &, T)
This is the MeshCommunication class.
static std::unique_ptr< Partitioner > build(const PartitionerType solver_package)
Builds a Partitioner of the type specified by partitioner_type.
void min(const T &r, T &o, Request &req) const
const std::map< subdomain_id_type, std::string > & get_subdomain_name_map() const
static constexpr dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
bool has_removed_orphaned_nodes
virtual void move_nodes_and_elements(MeshBase &&other_mesh)=0
Move node and elements from other_mesh to this mesh.
std::set< Elem * > _extra_ghost_elems
These are extra ghost elements that we want to make sure not to delete when we call delete_remote_ele...
The UnstructuredMesh class is derived from the MeshBase class.
unsigned char default_mapping_data() const
Returns any default data value used by the master space to physical space mapping.
virtual bool is_serial_on_zero() const override final
std::unique_ptr< Partitioner > _partitioner
A partitioner to use at each prepare_for_use().
void set_mapping_data(const unsigned char data)
Sets the value of the mapping data for the element.
void clear_point_locator()
Releases the current PointLocator object.
virtual void set_next_unique_id(unique_id_type id) override
Sets the next available unique id to be used.
virtual void clear_elems() override
Clear internal Elem data.
bool allow_find_neighbors() const
virtual void own_node(Node &n) override final
Takes ownership of node n on this partition of a distributed mesh, by setting n.processor_id() to thi...
virtual void clear_extra_ghost_elems()
Clears extra ghost elements.
const std::set< Elem * > & extra_ghost_elems() const
Const accessor to the ghosted elements.
bool valid_unique_id() const
virtual const Node * query_node_ptr(const dof_id_type i) const override final
void allow_find_neighbors(bool allow)
If false is passed then this mesh will no longer work to find element neighbors when being prepared f...
virtual bool subclass_locally_equals(const MeshBase &other_mesh) const override
Shim to allow operator == (&) to behave like a virtual function without having to be one...
dof_id_type parallel_max_node_id() const
std::map< subdomain_id_type, std::string > & set_subdomain_name_map()
bool allow_remote_element_removal() const
The DistributedMesh class is derived from the MeshBase class, and is intended to provide identical fu...
void regenerate_id_sets()
Clears and regenerates the cached sets of ids.
virtual void clear()
Deletes all the element and node data that is currently stored.
dof_id_type _next_free_unpartitioned_node_id
dofobject_container< Elem > _elements
The elements in the mesh.
virtual void delete_node(Node *n) override final
Removes the Node n from the mesh.
bool skip_partitioning() const
virtual void renumber_node(dof_id_type old_id, dof_id_type new_id) override final
Changes the id of node old_id, both by changing node(old_id)->id() and by moving node(old_id) in the ...
std::vector< dof_id_type > _node_integer_default_values
The array of default initialization values for integer data associated with each node in the mesh...
static std::unique_ptr< Node > build(const Node &n)
Preparation _preparation
Flags indicating in what ways this mesh has been prepared.
virtual void redistribute() override
Redistribute elements between processors.
std::vector< dof_id_type > _elem_integer_default_values
The array of default initialization values for integer data associated with each element in the mesh...
DistributedMesh & operator=(const DistributedMesh &)=delete
Copy assignment is not allowed.
void set_unique_id(unique_id_type new_id)
Sets the unique_id for this DofObject.
virtual dof_id_type n_active_elem() const override final
dof_id_type _next_free_unpartitioned_elem_id
void max(const T &r, T &o, Request &req) const
DistributedMesh(const Parallel::Communicator &comm_in, unsigned char dim=1)
Constructor.
void post_dofobject_moves(MeshBase &&other_mesh)
Moves any superclass data (e.g.
virtual void gather_to_zero() override
Gathers all elements and nodes of the mesh onto processor zero.
virtual Elem * insert_elem(Elem *e) override final
Insert elem e to the element array, preserving its id and replacing/deleting any existing element wit...
virtual dof_id_type max_node_id() const override final
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
The DofObject defines an abstract base class for objects that have degrees of freedom associated with...
void add_extra_integers(const unsigned int n_integers)
Assigns a set of extra integers to this DofObject.
virtual void copy_nodes_and_elements(const MeshBase &other_mesh, const bool skip_find_neighbors=false, dof_id_type element_id_offset=0, dof_id_type node_id_offset=0, unique_id_type unique_id_offset=0, std::unordered_map< subdomain_id_type, subdomain_id_type > *id_remapping=nullptr, const bool skip_preparation=false)
Deep copy of nodes and elements from another mesh object (used by subclass copy constructors and by m...
virtual const Node & node_ref(const dof_id_type i) const
bool has_synched_id_counts
virtual void update_post_partitioning() override
Recalculate cached data after elements and nodes have been repartitioned.
bool allow_renumbering() const
unique_id_type _next_unpartitioned_unique_id
The next available unique id for assigning ids to unpartitioned DOF objects.
virtual MeshBase & assign(MeshBase &&other_mesh) override
Shim to call the move assignment operator for this class.
virtual dof_id_type n_elem() const override final
virtual ~DistributedMesh()
Destructor.
processor_id_type processor_id() const
virtual bool is_serial() const override final
virtual dof_id_type max_elem_id() const override final
dof_id_type _next_free_local_elem_id
virtual void redistribute()
Redistribute elements between processors.
processor_id_type processor_id() const
bool _deleted_coarse_elements
A boolean remembering whether we've recently deleted top-level elements or not.
A Point defines a location in LIBMESH_DIM dimensional Real space.
dof_id_type renumber_dof_objects(dofobject_container< T > &)
Renumber a parallel objects container.
void redistribute(DistributedMesh &mesh, bool newly_coarsened_only=false) const
This method takes a parallel distributed mesh and redistributes the elements.
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
dof_id_type parallel_max_elem_id() const
virtual const Node * node_ptr(const dof_id_type i) const override final
virtual const Elem * query_elem_ptr(const dof_id_type i) const override final