libMesh
simplex_refiner.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2025 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 // Local includes
20 #include "libmesh/simplex_refiner.h"
21 
22 #include "libmesh/boundary_info.h"
23 #include "libmesh/face_tri3.h"
24 #include "libmesh/int_range.h"
25 #include "libmesh/libmesh_logging.h"
26 #include "libmesh/mesh_base.h"
27 #include "libmesh/mesh_communication.h"
28 #include "libmesh/mesh_tools.h"
29 #include "libmesh/node.h"
30 #include "libmesh/parallel_algebra.h"
31 #include "libmesh/remote_elem.h"
32 #include "libmesh/unstructured_mesh.h"
33 #include "libmesh/utility.h"
34 
35 // TIMPI includes
37 #include "timpi/parallel_sync.h"
38 
39 
40 namespace libMesh
41 {
42 
43 //-----------------------------------------------------------------
44 // Mesh refinement methods
46  _mesh(mesh),
47  _desired_volume(0.1)
48 {}
49 
50 
51 
53 {
54  if (!_mesh.is_prepared())
56 
57  // We should add more options later: to refine via circumcenter
58  // insertion instead of edge center insertion, to do Delaunay swaps,
59  // etc.
60  return this->refine_via_edges();
61 }
62 
63 
64 
66 {
67  const Real min_volume_target = this->desired_volume();
68  FunctionBase<Real> *volume_func = this->get_desired_volume_function();
69 
70  // If this isn't a question, why are we here?
71  libmesh_assert(min_volume_target > 0 ||
72  volume_func != nullptr);
73 
74  // If we don't have position-dependent volume targets we can make a
75  // decision quickly
76  const Real volume = elem.volume();
77 
79  libmesh_warning
80  ("Warning: trying to refine sliver element with volume " <<
81  volume << "!\n");
82 
83  if (!volume_func)
84  return (volume > min_volume_target);
85 
86  // If we do?
87  //
88  // See if we're meeting the local volume target at all the elem
89  // vertices first
90  for (auto v : make_range(elem.n_vertices()))
91  {
92  // If we have an auto volume function, we'll use it and override other volume options
93  const Real local_volume_target = (*volume_func)(elem.point(v));
94  libmesh_error_msg_if
95  (local_volume_target <= 0,
96  "Non-positive desired element volumes are unachievable");
97  if (volume > local_volume_target)
98  return true;
99  }
100 
101  // If our vertices are happy, it's still possible that our interior
102  // isn't. Are we allowed not to bother checking it?
103  if (!min_volume_target)
104  return false;
105 
106  libmesh_not_implemented_msg
107  ("Combining a minimum desired_volume with an volume function isn't yet supported.");
108 }
109 
110 
112  dof_id_type coarse_id)
113 {
114  // Do we need to be refined for our own sake, or only if our
115  // neighbors were refined?
116  const bool should_refine = this->should_refine_elem(elem);
117 
118  // We only currently handle coarse conforming meshes here.
119  // Uniformly refined meshes should be flattened before using this.
120  libmesh_assert_equal_to(elem.level(), 0u);
121 
122  // Adding Tets or Tri6/Tri7 wouldn't be too much harder. Quads or
123  // other elements will get tricky.
124  libmesh_assert_equal_to(elem.type(), TRI3);
125 
126  const int n_sides = elem.n_sides();
127  int side_to_refine = 0;
128  Real length_to_refine = 0;
129  std::pair<Node *, Node *> vertices_to_refine;
130 
131  // Find the longest side and refine it first. We might change that
132  // heuristic at some point, to enable anisotropic refinement.
133  for (int side : make_range(n_sides))
134  {
135  std::pair<Node *, Node *> vertices
136  {elem.node_ptr((side+1)%n_sides),
137  elem.node_ptr(side)};
138 
139  if ((Point&)(*vertices.first) >
140  (Point&)(*vertices.second))
141  std::swap(vertices.first, vertices.second);
142 
143  const auto sidevec = *(Point*)vertices.first -
144  *(Point*)vertices.second;
145  Real length = sidevec.norm();
146 
147  // We might need to ask the owner of a coarse ghost element
148  // about whether it saw any refinement forced by a coarse
149  // neighbor with a smaller desired area.
150  if (const processor_id_type pid = elem.processor_id();
151  pid != _mesh.processor_id() && !_mesh.is_serial() &&
152  !_desired_volume_func.get() &&
153  elem.id() == coarse_id)
154  edge_queries[pid].emplace_back(vertices.first->id(),
155  vertices.second->id());
156 
157  // Test should_refine, but also test for any edges that were
158  // already refined by neighbors, which might happen even if
159  // !should_refine in cases where we're refining non-uniformly.
160  if (length > length_to_refine &&
161  (should_refine ||
162  new_nodes.find(vertices) != new_nodes.end()))
163  {
164  side_to_refine = side;
165  length_to_refine = length;
166  vertices_to_refine = vertices;
167  }
168  }
169 
170  // We might not refine anything.
171  if (!length_to_refine)
172  return 0;
173 
174  // But if we must, then do it.
175  //
176  // Find all the edge neighbors we can. For a triangle there'll just
177  // be the one, but we want to extend to tets later.
178  std::vector<Elem *> neighbors;
179  if (Elem * neigh = elem.neighbor_ptr(side_to_refine); neigh)
180  neighbors.push_back(neigh);
181 
182  Node * midedge_node;
183  if (auto it = new_nodes.find(vertices_to_refine);
184  it != new_nodes.end())
185  {
186  midedge_node = it->second;
187  }
188  else
189  {
190  // Add with our own processor id first, so we can request a temporary
191  // id that won't conflict with anyone else's canonical ids later
192  midedge_node =
193  _mesh.add_point((*(Point*)vertices_to_refine.first +
194  *(Point*)vertices_to_refine.second)/2,
196  _mesh.processor_id());
197 
198  // Then give the new node the best pid we can.
199  midedge_node->processor_id() = elem.processor_id();
200  for (const Elem * neigh : neighbors)
201  if (neigh != remote_elem)
202  midedge_node->processor_id() = std::min(midedge_node->processor_id(),
203  neigh->processor_id());
204 
205  new_nodes[vertices_to_refine] = midedge_node;
206  }
207 
208  // Good for Tris and Tets; we'll need more for Quads.
209  constexpr int max_subelems = 2;
210  std::unique_ptr<Tri3> subelem[max_subelems];
211 
212  // We'll switch(elem->type()) here eventually
213  subelem[0] = std::make_unique<Tri3>();
214  subelem[0]->set_node(0, elem.node_ptr(side_to_refine));
215  subelem[0]->set_node(1, midedge_node);
216  subelem[0]->set_node(2, elem.node_ptr((side_to_refine+2)%3));
217 
218  subelem[1] = std::make_unique<Tri3>();
219  subelem[1]->set_node(0, elem.node_ptr((side_to_refine+2)%3));
220  subelem[1]->set_node(1, midedge_node);
221  subelem[1]->set_node(2, elem.node_ptr((side_to_refine+1)%3));
222 
223  // Preserve boundary and sort-of-preserve neighbor information. We
224  // need remote_elem neighbors to avoid breaking distributed meshes
225  // (which can't reconstruct them from scratch), and we need at least
226  // placeholder neighbors to make sure we'll have good processor_id()
227  // options for new nodes in future splits of the same edge.
228  subelem[0]->set_neighbor(1, proxy_elements[elem.processor_id()].get());
229  subelem[1]->set_neighbor(0, proxy_elements[elem.processor_id()].get());
230 
231  BoundaryInfo & boundary_info = _mesh.get_boundary_info();
232  std::vector<boundary_id_type> bc_ids;
233  boundary_info.boundary_ids(&elem, side_to_refine, bc_ids);
234  boundary_info.add_side(subelem[0].get(), 0, bc_ids);
235  boundary_info.add_side(subelem[1].get(), 1, bc_ids);
236  subelem[0]->set_neighbor(0, elem.neighbor_ptr(side_to_refine));
237  subelem[1]->set_neighbor(1, elem.neighbor_ptr(side_to_refine));
238 
239  boundary_info.boundary_ids(&elem, (side_to_refine+1)%3, bc_ids);
240  boundary_info.add_side(subelem[1].get(), 2, bc_ids);
241  subelem[1]->set_neighbor(2, elem.neighbor_ptr((side_to_refine+1)%3));
242 
243  boundary_info.boundary_ids(&elem, (side_to_refine+2)%3, bc_ids);
244  boundary_info.add_side(subelem[0].get(), 2, bc_ids);
245  subelem[0]->set_neighbor(2, elem.neighbor_ptr((side_to_refine+2)%3));
246 
247  // Be sure the correct data is set for all subelems.
248  const unsigned int nei = elem.n_extra_integers();
249  for (unsigned int i=0; i != max_subelems; ++i)
250  if (subelem[i])
251  {
252  // We'd love to assert that we don't have a sliver here,
253  // but maybe our input had a sliver and we can't fix it.
254  libmesh_assert_greater_equal(subelem[i]->volume() + TOLERANCE,
255  elem.volume() / 2);
256 
257  // Copy any extra element data. Since the subelements
258  // haven't been added to the mesh yet any allocation has
259  // to be done manually.
260  subelem[i]->add_extra_integers(nei);
261  for (unsigned int ei=0; ei != nei; ++ei)
262  subelem[ei]->set_extra_integer(ei, elem.get_extra_integer(ei));
263 
264  subelem[i]->inherit_data_from(elem);
265  }
266 
267  boundary_info.remove(&elem);
268 
269  if (auto it = this->added_elements.find(coarse_id);
270  it != this->added_elements.end())
271  {
272  auto & added = it->second;
273  if (auto sub_it = added.find(elem.vertex_average());
274  sub_it != added.end())
275  {
276  if (&elem == sub_it->second)
277  added.erase(sub_it);
278  }
279  }
280 
281  // We only add an element to new_elements *right* before
282  // checking it for further refinement, so we only need to check the
283  // back() to see where the elem here came from.
284  if (!this->new_elements.empty() &&
285  &elem == this->new_elements.back().get())
286  {
287  this->new_elements.pop_back();
288  this->coarse_parent.erase(&elem);
289  }
290  else
291  _mesh.delete_elem(&elem);
292 
293  // We refined at least ourselves
294  std::size_t n_refined_elements = 1;
295 
296  for (unsigned int i=0; i != max_subelems; ++i)
297  {
298  Elem * add_elem = subelem[i].get();
299  added_elements[coarse_id].emplace(add_elem->vertex_average(),
300  add_elem);
301  this->new_elements.push_back(std::move(subelem[i]));
302  this->coarse_parent[add_elem] = coarse_id;
303  n_refined_elements +=
304  this->refine_via_edges(*add_elem, coarse_id);
305  }
306 
307  return n_refined_elements;
308 }
309 
310 
311 
313 {
314  this->new_nodes.clear();
315  this->new_elements.clear();
316 
317  // We need to look at neighbors' pids to determine new node pids,
318  // but we might be deleting elements and leaving dangling neighbor
319  // pointers. Put proxy neighbors in those pointers places instead.
320  for (auto & elem : _mesh.element_ptr_range())
321  for (auto n : make_range(elem->n_neighbors()))
322  {
323  Elem * neigh = elem->neighbor_ptr(n);
324  if (!neigh || neigh == remote_elem)
325  continue;
326 
327  const processor_id_type p = neigh->processor_id();
328  std::unique_ptr<Elem> & proxy_neighbor = proxy_elements[p];
329  if (!proxy_neighbor.get())
330  proxy_neighbor = Elem::build(NODEELEM);
331 
332  elem->set_neighbor(n, proxy_neighbor.get());
333  }
334 
335  // If we have a _desired_volume_func then we might still have
336  // some unsplit edges that could have been split by their
337  // remote_elem neighbors, and we'll need to query those.
338  auto edge_gather_functor =
339  [this]
341  const std::vector<std::pair<dof_id_type, dof_id_type>> & edges,
342  std::vector<refinement_datum> & edge_refinements)
343  {
344  // Fill those requests
345  const std::size_t query_size = edges.size();
346  edge_refinements.resize(query_size);
347  for (std::size_t i=0; i != query_size; ++i)
348  {
349  auto vertex_ids = edges[i];
350  std::pair<Node *, Node *> vertices
351  {_mesh.node_ptr(vertex_ids.first),
352  _mesh.node_ptr(vertex_ids.second)};
353  fill_refinement_datum(vertices, edge_refinements[i]);
354  }
355  };
356 
357  auto edge_action_functor =
358  [this]
360  const std::vector<std::pair<dof_id_type, dof_id_type>> &,
361  const std::vector<refinement_datum> & edge_refinements
362  )
363  {
364  for (const auto & one_edges_refinements : edge_refinements)
365  for (const auto & refinement : one_edges_refinements)
366  {
367  std::pair<Node *, Node *> vertices
368  {_mesh.node_ptr(std::get<0>(refinement)),
369  _mesh.node_ptr(std::get<1>(refinement))};
370 
371  if ((Point&)(*vertices.first) >
372  (Point&)(*vertices.second))
373  std::swap(vertices.first, vertices.second);
374 
375  if (auto it = new_nodes.find(vertices);
376  it != new_nodes.end())
377  {
378  _mesh.renumber_node(it->second->id(),
379  std::get<2>(refinement));
380  it->second->processor_id() = std::get<3>(refinement);
381  }
382  else
383  {
384  Node * new_node =
385  _mesh.add_point((*(Point*)vertices.first +
386  *(Point*)vertices.second)/2,
387  std::get<2>(refinement),
388  std::get<3>(refinement));
389  new_nodes[vertices] = new_node;
390  }
391  }
392  };
393 
394  std::size_t refined_elements = 0;
395  std::size_t newly_refined_elements = 0;
396  do
397  {
398  newly_refined_elements = 0;
399 
400  // Refinement results should agree on ghost elements, except for
401  // id and unique_id assignment
402  for (auto & elem : _mesh.element_ptr_range())
403  {
404  auto it = coarse_parent.find(elem);
405  const dof_id_type coarse_id =
406  (it == coarse_parent.end()) ?
407  elem->id() : it->second;
408 
409  newly_refined_elements +=
410  this->refine_via_edges(*elem, coarse_id);
411  }
412  refined_elements += newly_refined_elements;
413  for (auto & elem : this->new_elements)
414  _mesh.add_elem(std::move(elem));
415  this->new_elements.clear();
416 
417  // Yeah, this is just a lower bound in parallel
418  _mesh.comm().max(newly_refined_elements);
419 
420  if (newly_refined_elements && !_mesh.is_replicated())
421  {
422  bool have_edge_queries = !edge_queries.empty();
423  _mesh.comm().max(have_edge_queries);
424  refinement_datum * refinement_data_ex = nullptr;
425  if (have_edge_queries)
426  Parallel::pull_parallel_vector_data
427  (_mesh.comm(), edge_queries, edge_gather_functor,
428  edge_action_functor, refinement_data_ex);
429  }
430  }
431  while (newly_refined_elements);
432 
433  // If we're replicated then we should be done here.
434  if (!_mesh.is_replicated())
435  {
436  // If we're not replicated then we might have a bunch of nodes
437  // and elements that still have temporary id and unique_id
438  // values. Give them permanent ones.
439  std::unordered_map<processor_id_type, std::vector<dof_id_type>> elems_to_query;
440  for (const auto & [coarse_id, added_elem_map] : added_elements)
441  {
442  if (added_elem_map.empty())
443  continue;
444 
445  const processor_id_type pid =
446  added_elem_map.begin()->second->processor_id();
447  if (pid == _mesh.processor_id())
448  continue;
449 
450  elems_to_query[pid].push_back(coarse_id);
451  }
452 
453  // Return fine element data based on vertex_average()
454  typedef
455 #ifdef LIBMESH_ENABLE_UNIQUE_ID
456  std::vector<std::tuple<Point, dof_id_type, unique_id_type>>
457 #else
458  std::vector<std::tuple<Point, dof_id_type>>
459 #endif
460  elem_refinement_datum;
461 
462  auto added_gather_functor =
463  [this]
465  const std::vector<dof_id_type> & coarse_elems,
466  std::vector<elem_refinement_datum> & coarse_refinements)
467  {
468  // Fill those requests
469  const std::size_t query_size = coarse_elems.size();
470  coarse_refinements.resize(query_size);
471  for (auto i : make_range(query_size))
472  {
473  const dof_id_type coarse_id = coarse_elems[i];
474  const auto & added =
475  libmesh_map_find(added_elements, coarse_id);
476 
477  for (auto [vertex_avg, elem] : added)
478  {
479  coarse_refinements[i].emplace_back
480  (vertex_avg,
481  elem->id()
482 #ifdef LIBMESH_ENABLE_UNIQUE_ID
483  , elem->unique_id()
484 #endif
485  );
486  }
487  }
488  };
489 
490  auto added_action_functor =
491  [this]
493  const std::vector<dof_id_type> & coarse_elems,
494  const std::vector<elem_refinement_datum> & coarse_refinements)
495  {
496  const std::size_t query_size = coarse_elems.size();
497  for (auto i : make_range(query_size))
498  {
499  const dof_id_type coarse_id = coarse_elems[i];
500  const auto & refinement_data = coarse_refinements[i];
501  const auto & our_added =
502  libmesh_map_find(added_elements, coarse_id);
503  for (auto [vertex_avg, id
504 #ifdef LIBMESH_ENABLE_UNIQUE_ID
505  , unique_id
506 #endif
507  ] : refinement_data)
508  {
509  Elem & our_elem = *libmesh_map_find(our_added,
510  vertex_avg);
511  libmesh_assert_equal_to(our_elem.vertex_average(),
512  vertex_avg);
513 
514 #ifdef LIBMESH_ENABLE_UNIQUE_ID
515  our_elem.set_unique_id(unique_id);
516 #endif
517  _mesh.renumber_elem(our_elem.id(), id);
518  }
519  }
520  };
521 
522  elem_refinement_datum * refinement_data_ex = nullptr;
523  Parallel::pull_parallel_vector_data
524  (_mesh.comm(), elems_to_query, added_gather_functor,
525  added_action_functor, refinement_data_ex);
526 
527  // That took care of our element ids; now get node ids.
530  mc.make_node_ids_parallel_consistent (_mesh);
531  mc.make_node_unique_ids_parallel_consistent (_mesh);
532  }
533 
534  // In theory the operations on ghost elements are, after remote edge
535  // splittings are known, embarrassingly parallel. In practice ...
536  // let's verify that we haven't just desynchronized our mesh.
537 #ifdef DEBUG
540 # ifdef LIBMESH_ENABLE_UNIQUE_ID
542 # endif
543 #endif
544 
545  return refined_elements;
546 }
547 
548 
549 
551  (FunctionBase<Real> * desired)
552 {
553  if (desired)
554  _desired_volume_func = desired->clone();
555  else
556  _desired_volume_func.reset();
557 }
558 
559 
560 
562 {
563  return _desired_volume_func.get();
564 }
565 
566 
567 void SimplexRefiner::fill_refinement_datum(std::pair<Node *, Node *> vertices,
568  refinement_datum & vec)
569 {
570  auto it = new_nodes.find(vertices);
571  if (it == new_nodes.end())
572  return;
573 
574  vec.emplace_back(vertices.first->id(),
575  vertices.second->id(),
576  it->second->id(),
577  it->second->processor_id());
578 
579  std::pair<Node *, Node *> subedge {vertices.first, it->second};
580  if ((Point&)(*subedge.first) >
581  (Point&)(*subedge.second))
582  std::swap(subedge.first, subedge.second);
583  fill_refinement_datum(subedge, vec);
584 
585  subedge = std::make_pair(it->second, vertices.second);
586  if ((Point&)(*subedge.first) >
587  (Point&)(*subedge.second))
588  std::swap(subedge.first, subedge.second);
589  fill_refinement_datum(subedge, vec);
590 }
591 
592 
593 } // namespace libMesh
594 
void libmesh_assert_equal_points(const MeshBase &mesh)
A function for testing that node locations match across processors.
Definition: mesh_tools.C:1605
std::unordered_map< dof_id_type, std::unordered_map< Point, Elem * > > added_elements
Keep track of elements added within each original element, so we can answer queries about them from o...
virtual void set_desired_volume_function(FunctionBase< Real > *desired)
Set a function giving desired element volume as a function of position.
unique_id_type & set_unique_id()
Definition: dof_object.h:858
bool is_prepared() const
Definition: mesh_base.h:198
std::unique_ptr< FunctionBase< Real > > _desired_volume_func
Location-dependent volume requirements.
A Node is like a Point, but with more information.
Definition: node.h:52
virtual void renumber_node(dof_id_type old_id, dof_id_type new_id)=0
Changes the id of node old_id, both by changing node(old_id)->id() and by moving node(old_id) in the ...
SimplexRefiner(UnstructuredMesh &mesh)
The constructor.
virtual FunctionBase< Real > * get_desired_volume_function()
Get the function giving desired element volume as a function of position, or nullptr if no such funct...
std::vector< std::tuple< dof_id_type, dof_id_type, dof_id_type, processor_id_type > > refinement_datum
static constexpr Real TOLERANCE
std::map< std::pair< Node *, Node * >, Node * > new_nodes
Keep track of new nodes on edges.
void prepare_for_use(const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
Prepare a newly ecreated (or read) mesh for use.
Definition: mesh_base.C:759
void remove(const Node *node)
Removes the boundary conditions associated with node node, if any exist.
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
MeshBase & mesh
const Parallel::Communicator & comm() const
Real & desired_volume()
Sets and/or gets the desired element volume.
void boundary_ids(const Node *node, std::vector< boundary_id_type > &vec_to_fill) const
Fills a user-provided std::vector with the boundary ids associated with Node node.
The libMesh namespace provides an interface to certain functionality in the library.
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition: mesh_base.h:165
virtual Node * add_point(const Point &p, const dof_id_type id=DofObject::invalid_id, const processor_id_type proc_id=DofObject::invalid_processor_id)=0
Add a new Node at Point p to the end of the vertex array, with processor_id procid.
uint8_t processor_id_type
Definition: id_types.h:104
virtual bool is_serial() const
Definition: mesh_base.h:211
virtual void delete_elem(Elem *e)=0
Removes element e from the mesh.
This is the MeshCommunication class.
dof_id_type id() const
Definition: dof_object.h:828
UnstructuredMesh & _mesh
Reference to the mesh which is to be refined.
std::vector< std::unique_ptr< Elem > > new_elements
Keep track of elements to add so we don&#39;t invalidate iterators during an iteration over elements...
The UnstructuredMesh class is derived from the MeshBase class.
virtual Elem * add_elem(Elem *e)=0
Add elem e to the end of the element array.
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition: elem.C:444
bool refine_elements()
Finds elements which exceed the requested metric and refines them via subdivision into new simplices ...
The BoundaryInfo class contains information relevant to boundary conditions including storing faces...
Definition: boundary_info.h:57
bool should_refine_elem(Elem &elem)
Checks if an element exceeds the requested metric.
libmesh_assert(ctx)
static const dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
Definition: dof_object.h:482
void fill_refinement_datum(std::pair< Node *, Node *> vertices, refinement_datum &vec)
Helper for responding to edge queries.
Real volume(const MeshBase &mesh, unsigned int dim=libMesh::invalid_uint)
Find the total volume of a mesh (interpreting that as area for dim = 2, or total arc length for dim =...
Definition: mesh_tools.C:985
virtual unsigned int n_sides() const =0
std::unordered_map< Elem *, dof_id_type > coarse_parent
Keep track of added elements&#39; original coarse ids, so we get subsequent splits assigned to the correc...
const Elem * neighbor_ptr(unsigned int i) const
Definition: elem.h:2598
void libmesh_assert_equal_connectivity(const MeshBase &mesh)
A function for testing that element nodal connectivities match across processors. ...
Definition: mesh_tools.C:1621
unsigned int level() const
Definition: elem.h:3074
virtual unsigned int n_vertices() const =0
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual void renumber_elem(dof_id_type old_id, dof_id_type new_id)=0
Changes the id of element old_id, both by changing elem(old_id)->id() and by moving elem(old_id) in t...
void max(const T &r, T &o, Request &req) const
const Node * node_ptr(const unsigned int i) const
Definition: elem.h:2507
virtual bool is_replicated() const
Definition: mesh_base.h:233
void make_node_proc_ids_parallel_consistent(MeshBase &)
Assuming all processor ids on nodes touching local elements are parallel consistent, this function makes all other processor ids parallel consistent as well.
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
Add side side of element number elem with boundary id id to the boundary information data structure...
virtual Real volume() const
Definition: elem.C:3429
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition: int_range.h:140
virtual std::unique_ptr< FunctionBase< Output > > clone() const =0
unsigned int n_extra_integers() const
Returns how many extra integers are associated to the DofObject.
Definition: dof_object.h:1170
std::unordered_map< processor_id_type, std::unique_ptr< Elem > > proxy_elements
Keep track of what processor an element&#39;s neighbors came from.
std::size_t refine_via_edges()
Finds elements which exceed the requested metric and refines them via inserting new midedge nodes and...
std::unordered_map< processor_id_type, std::vector< std::pair< dof_id_type, dof_id_type > > > edge_queries
Keep track of coarse remote edges for which we should query about additional point splits...
virtual const Node * node_ptr(const dof_id_type i) const =0
void libmesh_assert_valid_unique_ids(const MeshBase &mesh)
A function for verifying that unique ids match across processors.
Definition: mesh_tools.C:1906
processor_id_type processor_id() const
processor_id_type processor_id() const
Definition: dof_object.h:905
virtual ElemType type() const =0
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
const Point & point(const unsigned int i) const
Definition: elem.h:2453
Point vertex_average() const
Definition: elem.C:688
dof_id_type get_extra_integer(const unsigned int index) const
Gets the value on this object of the extra integer associated with index, which should have been obta...
Definition: dof_object.h:1102
uint8_t dof_id_type
Definition: id_types.h:67
const RemoteElem * remote_elem
Definition: remote_elem.C:57