unstructured_mesh.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2018 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it 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 // C++ includes
21 #include <fstream>
22 #include <sstream>
23 #include <iomanip>
24 #include <unordered_map>
25 
26 // C includes
27 #include <sys/types.h> // for pid_t
28 #include <unistd.h> // for getpid(), unlink()
29 
30 // Local includes
31 #include "libmesh/boundary_info.h"
35 #include "libmesh/elem.h"
36 #include "libmesh/mesh_tools.h" // For n_levels
37 #include "libmesh/parallel.h"
38 #include "libmesh/remote_elem.h"
39 #include "libmesh/namebased_io.h"
40 #include "libmesh/partitioner.h"
41 #include "libmesh/enum_order.h"
43 
44 
45 
46 namespace libMesh
47 {
48 
49 
50 // ------------------------------------------------------------
51 // UnstructuredMesh class member functions
53  unsigned char d) :
54  MeshBase (comm_in,d)
55 {
56  libmesh_assert (libMesh::initialized());
57 }
58 
59 
60 
62  const bool skip_find_neighbors,
63  dof_id_type element_id_offset,
64  dof_id_type node_id_offset,
66 #ifdef LIBMESH_ENABLE_UNIQUE_ID
67  unique_id_offset
68 #endif
69  )
70 {
71  LOG_SCOPE("copy_nodes_and_elements()", "UnstructuredMesh");
72 
73  // If we are partitioned into fewer parts than the incoming mesh,
74  // then we need to "wrap" the other Mesh's processor ids to fit
75  // within our range. This can happen, for example, while stitching
76  // ReplicatedMeshes with small numbers of elements in parallel...
77  bool wrap_proc_ids = (_n_parts < other_mesh._n_parts);
78 
79  // We're assuming our subclass data needs no copy
80  libmesh_assert_equal_to (_is_prepared, other_mesh._is_prepared);
81 
82  // We're assuming the other mesh has proper element number ordering,
83  // so that we add parents before their children, and that the other
84  // mesh is consistently partitioned.
85 #ifdef DEBUG
87  MeshTools::libmesh_assert_valid_procids<Node>(other_mesh);
88 #endif
89 
90  //Copy in Nodes
91  {
92  //Preallocate Memory if necessary
93  this->reserve_nodes(other_mesh.n_nodes());
94 
95  for (const auto & oldn : other_mesh.node_ptr_range())
96  {
97  processor_id_type added_pid = cast_int<processor_id_type>
98  (wrap_proc_ids ? oldn->processor_id() % _n_parts : oldn->processor_id());
99 
100  // Add new nodes in old node Point locations
101 #ifdef LIBMESH_ENABLE_UNIQUE_ID
102  Node * newn =
103 #endif
104  this->add_point(*oldn,
105  oldn->id() + node_id_offset,
106  added_pid);
107 
108 #ifdef LIBMESH_ENABLE_UNIQUE_ID
109  newn->set_unique_id() =
110  oldn->unique_id() + unique_id_offset;
111 #endif
112  }
113  }
114 
115  //Copy in Elements
116  {
117  //Preallocate Memory if necessary
118  this->reserve_elem(other_mesh.n_elem());
119 
120  // Declare a map linking old and new elements, needed to copy the neighbor lists
121  typedef std::unordered_map<const Elem *, Elem *> map_type;
122  map_type old_elems_to_new_elems;
123 
124  // Loop over the elements
125  for (const auto & old : other_mesh.element_ptr_range())
126  {
127  // Build a new element
128  Elem * newparent = old->parent() ?
129  this->elem_ptr(old->parent()->id() + element_id_offset) :
130  nullptr;
131  std::unique_ptr<Elem> ap = Elem::build(old->type(), newparent);
132  Elem * el = ap.release();
133 
134  el->subdomain_id() = old->subdomain_id();
135 
136  for (auto s : old->side_index_range())
137  if (old->neighbor_ptr(s) == remote_elem)
138  el->set_neighbor(s, const_cast<RemoteElem *>(remote_elem));
139 
140 #ifdef LIBMESH_ENABLE_AMR
141  if (old->has_children())
142  for (unsigned int c = 0, nc = old->n_children(); c != nc; ++c)
143  if (old->child_ptr(c) == remote_elem)
144  el->add_child(const_cast<RemoteElem *>(remote_elem), c);
145 
146  //Create the parent's child pointers if necessary
147  if (newparent)
148  {
149  unsigned int oldc = old->parent()->which_child_am_i(old);
150  newparent->add_child(el, oldc);
151  }
152 
153  // Copy the refinement flags
154  el->set_refinement_flag(old->refinement_flag());
155 
156  // Use hack_p_level since we may not have sibling elements
157  // added yet
158  el->hack_p_level(old->p_level());
159 
160  el->set_p_refinement_flag(old->p_refinement_flag());
161 #endif // #ifdef LIBMESH_ENABLE_AMR
162 
163  //Assign all the nodes
164  for (auto i : el->node_index_range())
165  el->set_node(i) =
166  this->node_ptr(old->node_id(i) + node_id_offset);
167 
168  // And start it off with the same processor id (mod _n_parts).
169  el->processor_id() = cast_int<processor_id_type>
170  (wrap_proc_ids ? old->processor_id() % _n_parts : old->processor_id());
171 
172  // Give it the same element and unique ids
173  el->set_id(old->id() + element_id_offset);
174 
175 #ifdef LIBMESH_ENABLE_UNIQUE_ID
176  el->set_unique_id() =
177  old->unique_id() + unique_id_offset;
178 #endif
179 
180  //Hold onto it
181  if (!skip_find_neighbors)
182  {
183  this->add_elem(el);
184  }
185  else
186  {
187  Elem * new_el = this->add_elem(el);
188  old_elems_to_new_elems[old] = new_el;
189  }
190 
191  // Add the link between the original element and this copy to the map
192  if (skip_find_neighbors)
193  old_elems_to_new_elems[old] = el;
194  }
195 
196  // Loop (again) over the elements to fill in the neighbors
197  if (skip_find_neighbors)
198  {
199  for (const auto & old_elem : other_mesh.element_ptr_range())
200  {
201  Elem * new_elem = old_elems_to_new_elems[old_elem];
202  for (auto s : old_elem->side_index_range())
203  {
204  const Elem * old_neighbor = old_elem->neighbor_ptr(s);
205  Elem * new_neighbor = old_elems_to_new_elems[old_neighbor];
206  new_elem->set_neighbor(s, new_neighbor);
207  }
208  }
209  }
210  }
211 
212  //Finally prepare the new Mesh for use. Keep the same numbering and
213  //partitioning for now.
214  this->allow_renumbering(false);
215  this->allow_remote_element_removal(false);
216  this->skip_partitioning(true);
217 
218  this->prepare_for_use(false, skip_find_neighbors);
219 
220  //But in the long term, use the same renumbering and partitioning
221  //policies as our source mesh.
222  this->allow_renumbering(other_mesh.allow_renumbering());
224  this->skip_partitioning(other_mesh.skip_partitioning());
225 }
226 
227 
228 
230 {
231  // this->clear (); // Nothing to clear at this level
232 
233  libmesh_exceptionless_assert (!libMesh::closed());
234 }
235 
236 
237 
238 
239 
240 void UnstructuredMesh::find_neighbors (const bool reset_remote_elements,
241  const bool reset_current_list)
242 {
243  // We might actually want to run this on an empty mesh
244  // (e.g. the boundary mesh for a nonexistent bcid!)
245  // libmesh_assert_not_equal_to (this->n_nodes(), 0);
246  // libmesh_assert_not_equal_to (this->n_elem(), 0);
247 
248  // This function must be run on all processors at once
249  parallel_object_only();
250 
251  LOG_SCOPE("find_neighbors()", "Mesh");
252 
253  //TODO:[BSK] This should be removed later?!
254  if (reset_current_list)
255  for (const auto & e : this->element_ptr_range())
256  for (auto s : e->side_index_range())
257  if (e->neighbor_ptr(s) != remote_elem || reset_remote_elements)
258  e->set_neighbor(s, nullptr);
259 
260  // Find neighboring elements by first finding elements
261  // with identical side keys and then check to see if they
262  // are neighbors
263  {
264  // data structures -- Use the hash_multimap if available
265  typedef unsigned int key_type;
266  typedef std::pair<Elem *, unsigned char> val_type;
267  typedef std::pair<key_type, val_type> key_val_pair;
268 
269  typedef std::unordered_multimap<key_type, val_type> map_type;
270 
271  // A map from side keys to corresponding elements & side numbers
272  map_type side_to_elem_map;
273 
274  // Pull objects out of the loop to reduce heap operations
275  std::unique_ptr<Elem> my_side, their_side;
276 
277  for (const auto & element : this->element_ptr_range())
278  {
279  for (auto ms : element->side_index_range())
280  {
281  next_side:
282  // If we haven't yet found a neighbor on this side, try.
283  // Even if we think our neighbor is remote, that
284  // information may be out of date.
285  if (element->neighbor_ptr(ms) == nullptr ||
286  element->neighbor_ptr(ms) == remote_elem)
287  {
288  // Get the key for the side of this element
289  const unsigned int key = element->key(ms);
290 
291  // Look for elements that have an identical side key
292  auto bounds = side_to_elem_map.equal_range(key);
293 
294  // May be multiple keys, check all the possible
295  // elements which _might_ be neighbors.
296  if (bounds.first != bounds.second)
297  {
298  // Get the side for this element
299  element->side_ptr(my_side, ms);
300 
301  // Look at all the entries with an equivalent key
302  while (bounds.first != bounds.second)
303  {
304  // Get the potential element
305  Elem * neighbor = bounds.first->second.first;
306 
307  // Get the side for the neighboring element
308  const unsigned int ns = bounds.first->second.second;
309  neighbor->side_ptr(their_side, ns);
310  //libmesh_assert(my_side.get());
311  //libmesh_assert(their_side.get());
312 
313  // If found a match with my side
314  //
315  // We need special tests here for 1D:
316  // since parents and children have an equal
317  // side (i.e. a node), we need to check
318  // ns != ms, and we also check level() to
319  // avoid setting our neighbor pointer to
320  // any of our neighbor's descendants
321  if ((*my_side == *their_side) &&
322  (element->level() == neighbor->level()) &&
323  ((element->dim() != 1) || (ns != ms)))
324  {
325  // So share a side. Is this a mixed pair
326  // of subactive and active/ancestor
327  // elements?
328  // If not, then we're neighbors.
329  // If so, then the subactive's neighbor is
330 
331  if (element->subactive() ==
332  neighbor->subactive())
333  {
334  // an element is only subactive if it has
335  // been coarsened but not deleted
336  element->set_neighbor (ms,neighbor);
337  neighbor->set_neighbor(ns,element);
338  }
339  else if (element->subactive())
340  {
341  element->set_neighbor(ms,neighbor);
342  }
343  else if (neighbor->subactive())
344  {
345  neighbor->set_neighbor(ns,element);
346  }
347  side_to_elem_map.erase (bounds.first);
348 
349  // get out of this nested crap
350  goto next_side;
351  }
352 
353  ++bounds.first;
354  }
355  }
356 
357  // didn't find a match...
358  // Build the map entry for this element
359  key_val_pair kvp;
360 
361  kvp.first = key;
362  kvp.second.first = element;
363  kvp.second.second = cast_int<unsigned char>(ms);
364  side_to_elem_map.insert (kvp);
365  }
366  }
367  }
368  }
369 
370 #ifdef LIBMESH_ENABLE_AMR
371 
399  const unsigned int n_levels = MeshTools::n_levels(*this);
400  for (unsigned int level = 1; level < n_levels; ++level)
401  {
402  for (auto & current_elem : as_range(level_elements_begin(level),
403  level_elements_end(level)))
404  {
405  libmesh_assert(current_elem);
406  Elem * parent = current_elem->parent();
407  libmesh_assert(parent);
408  const unsigned int my_child_num = parent->which_child_am_i(current_elem);
409 
410  for (auto s : current_elem->side_index_range())
411  {
412  if (current_elem->neighbor_ptr(s) == nullptr ||
413  (current_elem->neighbor_ptr(s) == remote_elem &&
414  parent->is_child_on_side(my_child_num, s)))
415  {
416  Elem * neigh = parent->neighbor_ptr(s);
417 
418  // If neigh was refined and had non-subactive children
419  // made remote earlier, then our current elem should
420  // actually have one of those remote children as a
421  // neighbor
422  if (neigh &&
423  (neigh->ancestor() ||
424  // If neigh has subactive children which should have
425  // matched as neighbors of the current element but
426  // did not, then those likewise must be remote
427  // children.
428  (current_elem->subactive() && neigh->has_children() &&
429  (neigh->level()+1) == current_elem->level())))
430  {
431 #ifdef DEBUG
432  // Let's make sure that "had children made remote"
433  // situation is actually the case
434  libmesh_assert(neigh->has_children());
435  bool neigh_has_remote_children = false;
436  for (auto & child : neigh->child_ref_range())
437  if (&child == remote_elem)
438  neigh_has_remote_children = true;
439  libmesh_assert(neigh_has_remote_children);
440 
441  // And let's double-check that we don't have
442  // a remote_elem neighboring an active local element
443  if (current_elem->active())
444  libmesh_assert_not_equal_to (current_elem->processor_id(),
445  this->processor_id());
446 #endif // DEBUG
447  neigh = const_cast<RemoteElem *>(remote_elem);
448  }
449  // If neigh and current_elem are more than one level
450  // apart, figuring out whether we have a remote
451  // neighbor here becomes much harder.
452  else if (neigh && (current_elem->subactive() &&
453  neigh->has_children()))
454  {
455  // Find the deepest descendant of neigh which
456  // we could consider for a neighbor. If we run
457  // out of neigh children, then that's our
458  // neighbor. If we find a potential neighbor
459  // with remote_children and we don't find any
460  // potential neighbors among its non-remote
461  // children, then our neighbor must be remote.
462  while (neigh != remote_elem &&
463  neigh->has_children())
464  {
465  bool found_neigh = false;
466  for (unsigned int c = 0, nc = neigh->n_children();
467  !found_neigh && c != nc; ++c)
468  {
469  Elem * child = neigh->child_ptr(c);
470  if (child == remote_elem)
471  continue;
472  for (auto ncn : child->neighbor_ptr_range())
473  {
474  if (ncn != remote_elem &&
475  ncn->is_ancestor_of(current_elem))
476  {
477  neigh = ncn;
478  found_neigh = true;
479  break;
480  }
481  }
482  }
483  if (!found_neigh)
484  neigh = const_cast<RemoteElem *>(remote_elem);
485  }
486  }
487  current_elem->set_neighbor(s, neigh);
488 #ifdef DEBUG
489  if (neigh != nullptr && neigh != remote_elem)
490  // We ignore subactive elements here because
491  // we don't care about neighbors of subactive element.
492  if ((!neigh->active()) && (!current_elem->subactive()))
493  {
494  libMesh::err << "On processor " << this->processor_id()
495  << std::endl;
496  libMesh::err << "Bad element ID = " << current_elem->id()
497  << ", Side " << s << ", Bad neighbor ID = " << neigh->id() << std::endl;
498  libMesh::err << "Bad element proc_ID = " << current_elem->processor_id()
499  << ", Bad neighbor proc_ID = " << neigh->processor_id() << std::endl;
500  libMesh::err << "Bad element size = " << current_elem->hmin()
501  << ", Bad neighbor size = " << neigh->hmin() << std::endl;
502  libMesh::err << "Bad element center = " << current_elem->centroid()
503  << ", Bad neighbor center = " << neigh->centroid() << std::endl;
504  libMesh::err << "ERROR: "
505  << (current_elem->active()?"Active":"Ancestor")
506  << " Element at level "
507  << current_elem->level() << std::endl;
508  libMesh::err << "with "
509  << (parent->active()?"active":
510  (parent->subactive()?"subactive":"ancestor"))
511  << " parent share "
512  << (neigh->subactive()?"subactive":"ancestor")
513  << " neighbor at level " << neigh->level()
514  << std::endl;
515  NameBasedIO(*this).write ("bad_mesh.gmv");
516  libmesh_error_msg("Problematic mesh written to bad_mesh.gmv.");
517  }
518 #endif // DEBUG
519  }
520  }
521 
522  // We can skip to the next element if we're full-dimension
523  // and therefore don't have any interior parents
524  if (current_elem->dim() >= LIBMESH_DIM)
525  continue;
526 
527  // We have no interior parents unless we can find one later
528  current_elem->set_interior_parent(nullptr);
529 
530  Elem * pip = parent->interior_parent();
531 
532  if (!pip)
533  continue;
534 
535  // If there's no interior_parent children, whether due to a
536  // remote element or a non-conformity, then there's no
537  // children to search.
538  if (pip == remote_elem || pip->active())
539  {
540  current_elem->set_interior_parent(pip);
541  continue;
542  }
543 
544  // For node comparisons we'll need a sensible tolerance
545  Real node_tolerance = current_elem->hmin() * TOLERANCE;
546 
547  // Otherwise our interior_parent should be a child of our
548  // parent's interior_parent.
549  for (auto & child : pip->child_ref_range())
550  {
551  // If we have a remote_elem, that might be our
552  // interior_parent. We'll set it provisionally now and
553  // keep trying to find something better.
554  if (&child == remote_elem)
555  {
556  current_elem->set_interior_parent
557  (const_cast<RemoteElem *>(remote_elem));
558  continue;
559  }
560 
561  bool child_contains_our_nodes = true;
562  for (auto & n : current_elem->node_ref_range())
563  {
564  bool child_contains_this_node = false;
565  for (auto & cn : child.node_ref_range())
566  if (cn.absolute_fuzzy_equals
567  (n, node_tolerance))
568  {
569  child_contains_this_node = true;
570  break;
571  }
572  if (!child_contains_this_node)
573  {
574  child_contains_our_nodes = false;
575  break;
576  }
577  }
578  if (child_contains_our_nodes)
579  {
580  current_elem->set_interior_parent(&child);
581  break;
582  }
583  }
584 
585  // We should have found *some* interior_parent at this
586  // point, whether semilocal or remote.
587  libmesh_assert(current_elem->interior_parent());
588  }
589  }
590 
591 #endif // AMR
592 
593 
594 #ifdef DEBUG
596  !reset_remote_elements);
598 #endif
599 }
600 
601 
602 
603 void UnstructuredMesh::read (const std::string & name,
604  void *,
605  bool skip_renumber_nodes_and_elements,
606  bool skip_find_neighbors)
607 {
608  // Set the skip_renumber_nodes_and_elements flag on all processors
609  // if necessary.
610  // This ensures that renumber_nodes_and_elements is *not* called
611  // during prepare_for_use() for certain types of mesh files.
612  // This is required in cases where there is an associated solution
613  // file which expects a certain ordering of the nodes.
614  if (name.rfind(".gmv") + 4 == name.size())
615  this->allow_renumbering(false);
616 
617  NameBasedIO(*this).read(name);
618 
619  if (skip_renumber_nodes_and_elements)
620  {
621  // Use MeshBase::allow_renumbering() yourself instead.
622  libmesh_deprecated();
623  this->allow_renumbering(false);
624  }
625 
626  // Done reading the mesh. Now prepare it for use.
627  this->prepare_for_use(/*skip_renumber (deprecated)*/ false,
628  skip_find_neighbors);
629 }
630 
631 
632 
633 void UnstructuredMesh::write (const std::string & name)
634 {
635  LOG_SCOPE("write()", "Mesh");
636 
637  NameBasedIO(*this).write(name);
638 }
639 
640 
641 
642 void UnstructuredMesh::write (const std::string & name,
643  const std::vector<Number> & v,
644  const std::vector<std::string> & vn)
645 {
646  LOG_SCOPE("write()", "Mesh");
647 
648  NameBasedIO(*this).write_nodal_data(name, v, vn);
649 }
650 
651 
652 
653 
654 
656  const processor_id_type pid) const
657 {
658 
659  // Issue a warning if the number the number of processors
660  // currently available is less that that requested for
661  // partitioning. This is not necessarily an error since
662  // you may run on one processor and still partition the
663  // mesh into several partitions.
664 #ifdef DEBUG
665  if (this->n_processors() < pid)
666  {
667  libMesh::out << "WARNING: You are creating a "
668  << "mesh for a processor id (="
669  << pid
670  << ") greater than "
671  << "the number of processors available for "
672  << "the calculation. (="
673  << this->n_processors()
674  << ")."
675  << std::endl;
676  }
677 #endif
678 
679  this->create_submesh (pid_mesh,
680  this->active_pid_elements_begin(pid),
681  this->active_pid_elements_end(pid));
682 }
683 
684 
685 
686 
687 
688 
689 
691  const const_element_iterator & it,
692  const const_element_iterator & it_end) const
693 {
694  // Just in case the subdomain_mesh already has some information
695  // in it, get rid of it.
696  new_mesh.clear();
697 
698  // If we're not serial, our submesh isn't either.
699  // There are no remote elements to delete on an empty mesh, but
700  // calling the method to do so marks the mesh as parallel.
701  if (!this->is_serial())
702  new_mesh.delete_remote_elements();
703 
704  // Fail if (*this == new_mesh), we cannot create a submesh inside ourself!
705  // This may happen if the user accidentally passes the original mesh into
706  // this function! We will check this by making sure we did not just
707  // clear ourself.
708  libmesh_assert_not_equal_to (this->n_nodes(), 0);
709  libmesh_assert_not_equal_to (this->n_elem(), 0);
710 
711  // Container to catch boundary IDs handed back by BoundaryInfo
712  std::vector<boundary_id_type> bc_ids;
713 
714  for (const auto & old_elem : as_range(it, it_end))
715  {
716  // Add an equivalent element type to the new_mesh.
717  // Copy ids for this element.
718  Elem * new_elem = Elem::build(old_elem->type()).release();
719  new_elem->set_id() = old_elem->id();
720 #ifdef LIBMESH_ENABLE_UNIQUE_ID
721  new_elem->set_unique_id() = old_elem->unique_id();
722 #endif
723  new_elem->subdomain_id() = old_elem->subdomain_id();
724  new_elem->processor_id() = old_elem->processor_id();
725 
726  new_mesh.add_elem (new_elem);
727 
728  libmesh_assert(new_elem);
729 
730  // Loop over the nodes on this element.
731  for (auto n : old_elem->node_index_range())
732  {
733  const dof_id_type this_node_id = old_elem->node_id(n);
734 
735  // Add this node to the new mesh if it's not there already
736  if (!new_mesh.query_node_ptr(this_node_id))
737  {
738 #ifdef LIBMESH_ENABLE_UNIQUE_ID
739  Node * newn =
740 #endif
741  new_mesh.add_point (old_elem->point(n),
742  this_node_id,
743  old_elem->node_ptr(n)->processor_id());
744 
745 #ifdef LIBMESH_ENABLE_UNIQUE_ID
746  newn->set_unique_id() = old_elem->node_ptr(n)->unique_id();
747 #endif
748  }
749 
750  // Define this element's connectivity on the new mesh
751  new_elem->set_node(n) = new_mesh.node_ptr(this_node_id);
752  }
753 
754  // Maybe add boundary conditions for this element
755  for (auto s : old_elem->side_index_range())
756  {
757  this->get_boundary_info().boundary_ids(old_elem, s, bc_ids);
758  new_mesh.get_boundary_info().add_side (new_elem, s, bc_ids);
759  }
760  } // end loop over elements
761 
762  // Prepare the new_mesh for use
763  new_mesh.prepare_for_use(/*skip_renumber =*/false);
764 }
765 
766 
767 
768 #ifdef LIBMESH_ENABLE_AMR
770 {
771  LOG_SCOPE ("contract()", "Mesh");
772 
773  // Flag indicating if this call actually changes the mesh
774  bool mesh_changed = false;
775 
776 #ifdef DEBUG
777  for (const auto & elem : this->element_ptr_range())
778  libmesh_assert(elem->active() || elem->subactive() || elem->ancestor());
779 #endif
780 
781  // Loop over the elements.
782  for (auto & elem : this->element_ptr_range())
783  {
784  // Delete all the subactive ones
785  if (elem->subactive())
786  {
787  // No level-0 element should be subactive.
788  // Note that we CAN'T test elem->level(), as that
789  // touches elem->parent()->dim(), and elem->parent()
790  // might have already been deleted!
791  libmesh_assert(elem->parent());
792 
793  // Delete the element
794  // This just sets a pointer to nullptr, and doesn't
795  // invalidate any iterators
796  this->delete_elem(elem);
797 
798  // the mesh has certainly changed
799  mesh_changed = true;
800  }
801  else
802  {
803  // Compress all the active ones
804  if (elem->active())
805  elem->contract();
806  else
807  libmesh_assert (elem->ancestor());
808  }
809  }
810 
811  // Strip any newly-created nullptr voids out of the element array
813 
814  // FIXME: Need to understand why deleting subactive children
815  // invalidates the point locator. For now we will clear it explicitly
816  this->clear_point_locator();
817 
818  // Allow our GhostingFunctor objects to reinit if necessary.
819  for (auto & gf : as_range(this->ghosting_functors_begin(),
820  this->ghosting_functors_end()))
821  {
822  libmesh_assert(gf);
823  gf->mesh_reinit();
824  }
825 
826  return mesh_changed;
827 }
828 #endif // #ifdef LIBMESH_ENABLE_AMR
829 
830 
831 
833 {
834  /*
835  * when the mesh is not prepared,
836  * at least renumber the nodes and
837  * elements, so that the node ids
838  * are correct
839  */
840  if (!this->_is_prepared)
842 
843  START_LOG("all_first_order()", "Mesh");
844 
848  std::vector<bool> node_touched_by_me(this->max_node_id(), false);
849 
850  // Loop over the high-ordered elements.
851  // First make sure they _are_ indeed high-order, and then replace
852  // them with an equivalent first-order element.
853  for (auto & so_elem : element_ptr_range())
854  {
855  libmesh_assert(so_elem);
856 
857  /*
858  * build the first-order equivalent, add to
859  * the new_elements list.
860  */
861  Elem * lo_elem = Elem::build
863  (so_elem->type()), so_elem->parent()).release();
864 
865  const unsigned short n_sides = so_elem->n_sides();
866 
867  for (unsigned short s=0; s != n_sides; ++s)
868  if (so_elem->neighbor_ptr(s) == remote_elem)
869  lo_elem->set_neighbor(s, const_cast<RemoteElem *>(remote_elem));
870 
871 #ifdef LIBMESH_ENABLE_AMR
872  /*
873  * Reset the parent links of any child elements
874  */
875  if (so_elem->has_children())
876  for (unsigned int c = 0, nc = so_elem->n_children(); c != nc; ++c)
877  {
878  Elem * child = so_elem->child_ptr(c);
879  child->set_parent(lo_elem);
880  lo_elem->add_child(child, c);
881  }
882 
883  /*
884  * Reset the child link of any parent element
885  */
886  if (so_elem->parent())
887  {
888  unsigned int c =
889  so_elem->parent()->which_child_am_i(so_elem);
890  lo_elem->parent()->replace_child(lo_elem, c);
891  }
892 
893  /*
894  * Copy as much data to the new element as makes sense
895  */
896  lo_elem->set_p_level(so_elem->p_level());
897  lo_elem->set_refinement_flag(so_elem->refinement_flag());
898  lo_elem->set_p_refinement_flag(so_elem->p_refinement_flag());
899 #endif
900 
901  libmesh_assert_equal_to (lo_elem->n_vertices(), so_elem->n_vertices());
902 
903  /*
904  * By definition the vertices of the linear and
905  * second order element are identically numbered.
906  * transfer these.
907  */
908  for (unsigned int v=0; v < so_elem->n_vertices(); v++)
909  {
910  lo_elem->set_node(v) = so_elem->node_ptr(v);
911  node_touched_by_me[lo_elem->node_id(v)] = true;
912  }
913 
914  /*
915  * find_neighbors relies on remote_elem neighbor links being
916  * properly maintained.
917  */
918  for (unsigned short s=0; s != n_sides; s++)
919  {
920  if (so_elem->neighbor_ptr(s) == remote_elem)
921  lo_elem->set_neighbor(s, const_cast<RemoteElem*>(remote_elem));
922  }
923 
931  (this->get_boundary_info(), so_elem, lo_elem);
932 
933  /*
934  * The new first-order element is ready.
935  * Inserting it into the mesh will replace and delete
936  * the second-order element.
937  */
938  lo_elem->set_id(so_elem->id());
939 #ifdef LIBMESH_ENABLE_UNIQUE_ID
940  lo_elem->set_unique_id() = so_elem->unique_id();
941 #endif
942  lo_elem->processor_id() = so_elem->processor_id();
943  lo_elem->subdomain_id() = so_elem->subdomain_id();
944  this->insert_elem(lo_elem);
945  }
946 
947  // Deleting nodes does not invalidate iterators, so this is safe.
948  for (const auto & node : this->node_ptr_range())
949  if (!node_touched_by_me[node->id()])
950  this->delete_node(node);
951 
952  // If crazy people applied boundary info to non-vertices and then
953  // deleted those non-vertices, we should make sure their boundary id
954  // caches are correct.
956 
957  STOP_LOG("all_first_order()", "Mesh");
958 
959  // On hanging nodes that used to also be second order nodes, we
960  // might now have an invalid nodal processor_id()
962 
963  // delete or renumber nodes if desired
964  this->prepare_for_use();
965 }
966 
967 
968 
969 void UnstructuredMesh::all_second_order (const bool full_ordered)
970 {
971  // This function must be run on all processors at once
972  parallel_object_only();
973 
974  /*
975  * when the mesh is not prepared,
976  * at least renumber the nodes and
977  * elements, so that the node ids
978  * are correct
979  */
980  if (!this->_is_prepared)
982 
983  /*
984  * If the mesh is empty
985  * then we have nothing to do
986  */
987  if (!this->n_elem())
988  return;
989 
990  /*
991  * If the mesh is already second order
992  * then we have nothing to do.
993  * We have to test for this in a round-about way to avoid
994  * a bug on distributed parallel meshes with more processors
995  * than elements.
996  */
997  bool already_second_order = false;
998  if (this->elements_begin() != this->elements_end() &&
999  (*(this->elements_begin()))->default_order() != FIRST)
1000  already_second_order = true;
1001  this->comm().max(already_second_order);
1002  if (already_second_order)
1003  return;
1004 
1005  START_LOG("all_second_order()", "Mesh");
1006 
1007  /*
1008  * this map helps in identifying second order
1009  * nodes. Namely, a second-order node:
1010  * - edge node
1011  * - face node
1012  * - bubble node
1013  * is uniquely defined through a set of adjacent
1014  * vertices. This set of adjacent vertices is
1015  * used to identify already added higher-order
1016  * nodes. We are safe to use node id's since we
1017  * make sure that these are correctly numbered.
1018  */
1019  std::map<std::vector<dof_id_type>, Node *> adj_vertices_to_so_nodes;
1020 
1021  /*
1022  * for speed-up of the \p add_point() method, we
1023  * can reserve memory. Guess the number of additional
1024  * nodes for different dimensions
1025  */
1026  switch (this->mesh_dimension())
1027  {
1028  case 1:
1029  /*
1030  * in 1D, there can only be order-increase from Edge2
1031  * to Edge3. Something like 1/2 of n_nodes() have
1032  * to be added
1033  */
1034  this->reserve_nodes(static_cast<unsigned int>
1035  (1.5*static_cast<double>(this->n_nodes())));
1036  break;
1037 
1038  case 2:
1039  /*
1040  * in 2D, either refine from Tri3 to Tri6 (double the nodes)
1041  * or from Quad4 to Quad8 (again, double) or Quad9 (2.25 that much)
1042  */
1043  this->reserve_nodes(static_cast<unsigned int>
1044  (2*static_cast<double>(this->n_nodes())));
1045  break;
1046 
1047 
1048  case 3:
1049  /*
1050  * in 3D, either refine from Tet4 to Tet10 (factor = 2.5) up to
1051  * Hex8 to Hex27 (something > 3). Since in 3D there _are_ already
1052  * quite some nodes, and since we do not want to overburden the memory by
1053  * a too conservative guess, use the lower bound
1054  */
1055  this->reserve_nodes(static_cast<unsigned int>
1056  (2.5*static_cast<double>(this->n_nodes())));
1057  break;
1058 
1059  default:
1060  // Hm?
1061  libmesh_error_msg("Unknown mesh dimension " << this->mesh_dimension());
1062  }
1063 
1064 
1065 
1066  /*
1067  * form a vector that will hold the node id's of
1068  * the vertices that are adjacent to the son-th
1069  * second-order node. Pull this outside of the
1070  * loop so that silly compilers don't repeatedly
1071  * create and destroy the vector.
1072  */
1073  std::vector<dof_id_type> adjacent_vertices_ids;
1074 
1082  it = elements_begin(),
1083  endit = elements_end();
1084 
1085  for (; it != endit; ++it)
1086  {
1087  // the linear-order element
1088  Elem * lo_elem = *it;
1089 
1090  libmesh_assert(lo_elem);
1091 
1092  // make sure it is linear order
1093  if (lo_elem->default_order() != FIRST)
1094  libmesh_error_msg("ERROR: This is not a linear element: type=" << lo_elem->type());
1095 
1096  // this does _not_ work for refined elements
1097  libmesh_assert_equal_to (lo_elem->level (), 0);
1098 
1099  /*
1100  * build the second-order equivalent, add to
1101  * the new_elements list. Note that this here
1102  * is the only point where \p full_ordered
1103  * is necessary. The remaining code works well
1104  * for either type of second-order equivalent, e.g.
1105  * Hex20 or Hex27, as equivalents for Hex8
1106  */
1107  Elem * so_elem =
1109  full_ordered) ).release();
1110 
1111  libmesh_assert_equal_to (lo_elem->n_vertices(), so_elem->n_vertices());
1112 
1113 
1114  /*
1115  * By definition the vertices of the linear and
1116  * second order element are identically numbered.
1117  * transfer these.
1118  */
1119  for (unsigned int v=0; v < lo_elem->n_vertices(); v++)
1120  so_elem->set_node(v) = lo_elem->node_ptr(v);
1121 
1122  /*
1123  * Now handle the additional mid-side nodes. This
1124  * is simply handled through a map that remembers
1125  * the already-added nodes. This map maps the global
1126  * ids of the vertices (that uniquely define this
1127  * higher-order node) to the new node.
1128  * Notation: son = second-order node
1129  */
1130  const unsigned int son_begin = so_elem->n_vertices();
1131  const unsigned int son_end = so_elem->n_nodes();
1132 
1133 
1134  for (unsigned int son=son_begin; son<son_end; son++)
1135  {
1136  const unsigned int n_adjacent_vertices =
1137  so_elem->n_second_order_adjacent_vertices(son);
1138 
1139  adjacent_vertices_ids.resize(n_adjacent_vertices);
1140 
1141  for (unsigned int v=0; v<n_adjacent_vertices; v++)
1142  adjacent_vertices_ids[v] =
1143  so_elem->node_id( so_elem->second_order_adjacent_vertex(son,v) );
1144 
1145  /*
1146  * \p adjacent_vertices_ids is now in order of the current
1147  * side. sort it, so that comparisons with the
1148  * \p adjacent_vertices_ids created through other elements'
1149  * sides can match
1150  */
1151  std::sort(adjacent_vertices_ids.begin(),
1152  adjacent_vertices_ids.end());
1153 
1154 
1155  // does this set of vertices already have a mid-node added?
1156  auto pos = adj_vertices_to_so_nodes.equal_range (adjacent_vertices_ids);
1157 
1158  // no, not added yet
1159  if (pos.first == pos.second)
1160  {
1161  /*
1162  * for this set of vertices, there is no
1163  * second_order node yet. Add it.
1164  *
1165  * compute the location of the new node as
1166  * the average over the adjacent vertices.
1167  */
1168  Point new_location = this->point(adjacent_vertices_ids[0]);
1169  for (unsigned int v=1; v<n_adjacent_vertices; v++)
1170  new_location += this->point(adjacent_vertices_ids[v]);
1171 
1172  new_location /= static_cast<Real>(n_adjacent_vertices);
1173 
1174  /* Add the new point to the mesh.
1175  * If we are on a serialized mesh, then we're doing this
1176  * all in sync, and the node processor_id will be
1177  * consistent between processors.
1178  * If we are on a distributed mesh, we can fix
1179  * inconsistent processor ids later, but only if every
1180  * processor gives new nodes a *locally* consistent
1181  * processor id, so we'll give the new node the
1182  * processor id of an adjacent element for now and then
1183  * we'll update that later if appropriate.
1184  */
1185  Node * so_node = this->add_point
1186  (new_location, DofObject::invalid_id,
1187  lo_elem->processor_id());
1188 
1189  /*
1190  * insert the new node with its defining vertex
1191  * set into the map, and relocate pos to this
1192  * new entry, so that the so_elem can use
1193  * \p pos for inserting the node
1194  */
1195  adj_vertices_to_so_nodes.insert(pos.first,
1196  std::make_pair(adjacent_vertices_ids,
1197  so_node));
1198 
1199  so_elem->set_node(son) = so_node;
1200  }
1201  // yes, already added.
1202  else
1203  {
1204  Node * so_node = pos.first->second;
1205  libmesh_assert(so_node);
1206 
1207  so_elem->set_node(son) = so_node;
1208 
1209  // We need to ensure that the processor who should own a
1210  // node *knows* they own the node. And because
1211  // Node::choose_processor_id() may depend on Node id,
1212  // which may not yet be authoritative, we still have to
1213  // use a dumb-but-id-independent partitioning heuristic.
1214  processor_id_type chosen_pid =
1215  std::min (so_node->processor_id(),
1216  lo_elem->processor_id());
1217 
1218  // Plus, if we just discovered that we own this node,
1219  // then on a distributed mesh we need to make sure to
1220  // give it a valid id, not just a placeholder id!
1221  if (!this->is_replicated() &&
1222  so_node->processor_id() != this->processor_id() &&
1223  chosen_pid == this->processor_id())
1224  this->own_node(*so_node);
1225 
1226  so_node->processor_id() = chosen_pid;
1227  }
1228  }
1229 
1230  /*
1231  * find_neighbors relies on remote_elem neighbor links being
1232  * properly maintained.
1233  */
1234  for (auto s : lo_elem->side_index_range())
1235  {
1236  if (lo_elem->neighbor_ptr(s) == remote_elem)
1237  so_elem->set_neighbor(s, const_cast<RemoteElem*>(remote_elem));
1238  }
1239 
1252  (this->get_boundary_info(), lo_elem, so_elem);
1253 
1254  /*
1255  * The new second-order element is ready.
1256  * Inserting it into the mesh will replace and delete
1257  * the first-order element.
1258  */
1259  so_elem->set_id(lo_elem->id());
1260 #ifdef LIBMESH_ENABLE_UNIQUE_ID
1261  so_elem->set_unique_id() = lo_elem->unique_id();
1262 #endif
1263  so_elem->processor_id() = lo_elem->processor_id();
1264  so_elem->subdomain_id() = lo_elem->subdomain_id();
1265  this->insert_elem(so_elem);
1266  }
1267 
1268  // we can clear the map
1269  adj_vertices_to_so_nodes.clear();
1270 
1271 
1272  STOP_LOG("all_second_order()", "Mesh");
1273 
1274  // On a DistributedMesh our ghost node processor ids may be bad,
1275  // the ids of nodes touching remote elements may be inconsistent,
1276  // unique_ids of newly added non-local nodes remain unset, and our
1277  // partitioning of new nodes may not be well balanced.
1278  //
1279  // make_nodes_parallel_consistent() will fix all this.
1280  if (!this->is_serial())
1282 
1283  // renumber nodes, elements etc
1284  this->prepare_for_use(/*skip_renumber =*/ false);
1285 }
1286 
1287 } // namespace libMesh
std::string name(const ElemQuality q)
Definition: elem_quality.C:42
void set_p_level(const unsigned int p)
Definition: elem.h:2692
virtual void copy_nodes_and_elements(const UnstructuredMesh &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)
bool closed()
Definition: libmesh.C:265
unique_id_type & set_unique_id()
Definition: dof_object.h:685
virtual void reserve_nodes(const dof_id_type nn)=0
const Elem * parent() const
Definition: elem.h:2479
virtual const Elem * elem(const dof_id_type i) const
Definition: mesh_base.h:537
Used by ParallelMesh to represent an Elem owned by another processor.
Definition: remote_elem.h:44
virtual Node *& set_node(const unsigned int i)
Definition: elem.h:2024
A geometric point in (x,y,z) space associated with a DOF.
Definition: node.h:52
bool is_ancestor_of(const Elem *descendant) const
Definition: elem.h:2458
void libmesh_assert_valid_amr_interior_parents(const MeshBase &mesh)
Definition: mesh_tools.C:1340
void set_parent(Elem *p)
Definition: elem.h:2495
virtual element_iterator level_elements_begin(unsigned int level)=0
const Elem * interior_parent() const
Definition: elem.C:804
virtual void read(const std::string &mesh_file) override
Definition: namebased_io.C:59
void allow_renumbering(bool allow)
Definition: mesh_base.h:782
IntRange< unsigned short > side_index_range() const
Definition: elem.h:2166
void skip_partitioning(bool skip)
Definition: mesh_base.h:811
static void set_node_processor_ids(MeshBase &mesh)
Definition: partitioner.C:679
virtual bool is_child_on_side(const unsigned int c, const unsigned int s) const =0
The base class for all geometric element types.
Definition: elem.h:100
uint8_t processor_id_type
Definition: id_types.h:99
void add_child(Elem *elem)
Definition: elem.C:1461
void set_refinement_flag(const RefinementState rflag)
Definition: elem.h:2646
unique_id_type unique_id() const
Definition: dof_object.h:672
const Parallel::Communicator & comm() const
virtual unsigned int n_children() const =0
static const Real TOLERANCE
virtual void own_node(Node &)
Definition: mesh_base.h:639
virtual void all_first_order() override
virtual void find_neighbors(const bool reset_remote_elements=false, const bool reset_current_list=true) override
void create_pid_mesh(UnstructuredMesh &pid_mesh, const processor_id_type pid) const
const BoundaryInfo & get_boundary_info() const
Definition: mesh_base.h:131
virtual Node * add_point(const Point &p, const dof_id_type id=DofObject::invalid_id, const processor_id_type proc_id=DofObject::invalid_processor_id)=0
void set_interior_parent(Elem *p)
Definition: elem.C:856
Base class for Mesh.
Definition: mesh_base.h:77
SimpleRange< ChildRefIter > child_ref_range()
Definition: elem.h:1779
dof_id_type & set_id()
Definition: dof_object.h:664
bool ancestor() const
Definition: elem.C:1427
std::vector< boundary_id_type > boundary_ids(const Node *node) const
virtual bool contract() override
virtual element_iterator elements_begin()=0
virtual element_iterator level_elements_end(unsigned int level)=0
unsigned int _n_parts
Definition: mesh_base.h:1384
void replace_child(Elem *elem, unsigned int c)
Definition: elem.C:1507
void allow_remote_element_removal(bool allow)
Definition: mesh_base.h:791
processor_id_type n_processors() const
virtual bool is_serial() const
Definition: mesh_base.h:154
virtual void delete_elem(Elem *e)=0
virtual SimpleRange< element_iterator > element_ptr_range()=0
dof_id_type id() const
Definition: dof_object.h:655
virtual Real hmin() const
Definition: elem.C:356
virtual element_iterator active_pid_elements_begin(processor_id_type proc_id)=0
Base class for Replicated and Distributed meshes.
virtual Elem * add_elem(Elem *e)=0
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition: elem.C:245
virtual element_iterator elements_end()=0
unsigned int n_levels(const MeshBase &mesh)
Definition: mesh_tools.C:653
virtual const Node * query_node_ptr(const dof_id_type i) const =0
virtual SimpleRange< node_iterator > node_ptr_range()=0
void clear_point_locator()
Definition: mesh_base.C:517
virtual void write(const std::string &mesh_file) override
Definition: namebased_io.C:276
SimpleRange< I > as_range(const std::pair< I, I > &p)
Definition: simple_range.h:57
static const dof_id_type invalid_id
Definition: dof_object.h:347
virtual void delete_node(Node *n)=0
void libmesh_assert_valid_amr_elem_ids(const MeshBase &mesh)
Definition: mesh_tools.C:1320
void prepare_for_use(const bool skip_renumber_nodes_and_elements=false, const bool skip_find_neighbors=false)
Definition: mesh_base.C:152
OStreamProxy err(std::cerr)
bool allow_remote_element_removal() const
Definition: mesh_base.h:792
void set_neighbor(const unsigned int i, Elem *n)
Definition: elem.h:2083
virtual void write_nodal_data(const std::string &, const std::vector< Number > &, const std::vector< std::string > &) override
Definition: namebased_io.C:422
virtual void clear()
Definition: mesh_base.C:260
static ElemType second_order_equivalent_type(const ElemType et, const bool full_ordered=true)
Definition: elem.C:2643
unsigned int which_child_am_i(const Elem *e) const
Definition: elem.h:2620
bool skip_partitioning() const
Definition: mesh_base.h:812
virtual const Node & node(const dof_id_type i) const
Definition: mesh_base.h:454
virtual const Elem * elem_ptr(const dof_id_type i) const =0
UnstructuredMesh(const Parallel::Communicator &comm_in, unsigned char dim=1)
virtual Elem * insert_elem(Elem *e)=0
const Elem * neighbor_ptr(unsigned int i) const
Definition: elem.h:2050
std::set< GhostingFunctor * >::const_iterator ghosting_functors_begin() const
Definition: mesh_base.h:835
unsigned int level() const
Definition: elem.h:2521
virtual unsigned int n_vertices() const =0
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual element_iterator active_pid_elements_end(processor_id_type proc_id)=0
virtual std::unique_ptr< Elem > side_ptr(unsigned int i)=0
subdomain_id_type subdomain_id() const
Definition: elem.h:2034
void copy_boundary_ids(const BoundaryInfo &old_boundary_info, const Elem *const old_elem, const Elem *const new_elem)
const Node * node_ptr(const unsigned int i) const
Definition: elem.h:1957
void make_nodes_parallel_consistent(MeshBase &)
virtual bool is_replicated() const
Definition: mesh_base.h:176
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
bool subactive() const
Definition: elem.h:2408
unsigned int mesh_dimension() const
Definition: mesh_base.C:126
bool initialized()
Definition: libmesh.C:258
virtual const Point & point(const dof_id_type i) const =0
SimpleRange< NeighborPtrIter > neighbor_ptr_range()
Definition: elem.h:2988
void set_p_refinement_flag(const RefinementState pflag)
Definition: elem.h:2662
bool allow_renumbering() const
Definition: mesh_base.h:783
void libmesh_assert_valid_neighbors(const MeshBase &mesh, bool assert_valid_remote_elems=true)
Definition: mesh_tools.C:2068
virtual void delete_remote_elements()
Definition: mesh_base.h:196
virtual void read(const std::string &name, void *mesh_data=nullptr, bool skip_renumber_nodes_and_elements=false, bool skip_find_neighbors=false) override
virtual dof_id_type max_node_id() const =0
virtual dof_id_type n_elem() const =0
virtual const Node * node_ptr(const dof_id_type i) const =0
processor_id_type processor_id() const
virtual Order default_order() const =0
OStreamProxy out(std::cout)
virtual void all_second_order(const bool full_ordered=true) override
bool active() const
Definition: elem.h:2390
static ElemType first_order_equivalent_type(const ElemType et)
Definition: elem.C:2584
processor_id_type processor_id() const
Definition: dof_object.h:717
virtual ElemType type() const =0
long double min(long double a, double b)
A geometric point in (x,y,z) space.
Definition: point.h:38
dof_id_type node_id(const unsigned int i) const
Definition: elem.h:1914
virtual void reserve_elem(const dof_id_type ne)=0
uint8_t unique_id_type
Definition: id_types.h:79
virtual Point centroid() const
Definition: elem.C:344
bool has_children() const
Definition: elem.h:2428
virtual void renumber_nodes_and_elements()=0
virtual dof_id_type n_nodes() const =0
void create_submesh(UnstructuredMesh &new_mesh, const const_element_iterator &it, const const_element_iterator &it_end) const
virtual void write(const std::string &name) override
const Elem * child_ptr(unsigned int i) const
Definition: elem.h:2578
std::set< GhostingFunctor * >::const_iterator ghosting_functors_end() const
Definition: mesh_base.h:841
uint8_t dof_id_type
Definition: id_types.h:64
const RemoteElem * remote_elem
Definition: remote_elem.C:57