libMesh
Public Member Functions | Protected Member Functions | Private Types | Private Member Functions | Private Attributes | List of all members
libMesh::SimplexRefiner Class Reference

A C++ class to refine a simplicial mesh via splitting edges that exceed a given metric. More...

#include <simplex_refiner.h>

Public Member Functions

 SimplexRefiner (UnstructuredMesh &mesh)
 The constructor. More...
 
bool refine_elements ()
 Finds elements which exceed the requested metric and refines them via subdivision into new simplices as necessary. More...
 
virtual void set_desired_volume_function (FunctionBase< Real > *desired)
 Set a function giving desired element volume as a function of position. More...
 
virtual FunctionBase< Real > * get_desired_volume_function ()
 Get the function giving desired element volume as a function of position, or nullptr if no such function has been set. More...
 
Realdesired_volume ()
 Sets and/or gets the desired element volume. More...
 

Protected Member Functions

std::size_t refine_via_edges ()
 Finds elements which exceed the requested metric and refines them via inserting new midedge nodes and bisecting as necessary. More...
 
bool should_refine_elem (Elem &elem)
 Checks if an element exceeds the requested metric. More...
 
std::size_t refine_via_edges (Elem &elem, dof_id_type coarse_id)
 Checks if an element exceeds the requested metric or if it has an edge which was split by a neighboring metric and refines it (bisecting it, removing it from the mesh to be replaced by the two subelements, and recursing into those) if necessary. More...
 

Private Types

typedef std::vector< std::tuple< dof_id_type, dof_id_type, dof_id_type, processor_id_type > > refinement_datum
 

Private Member Functions

void fill_refinement_datum (std::pair< Node *, Node *> vertices, refinement_datum &vec)
 Helper for responding to edge queries. More...
 

Private Attributes

UnstructuredMesh_mesh
 Reference to the mesh which is to be refined. More...
 
Real _desired_volume
 The desired volume for the elements in the resulting mesh. More...
 
std::unique_ptr< FunctionBase< Real > > _desired_volume_func
 Location-dependent volume requirements. More...
 
std::map< std::pair< Node *, Node * >, Node * > new_nodes
 Keep track of new nodes on edges. More...
 
std::vector< std::unique_ptr< Elem > > new_elements
 Keep track of elements to add so we don't invalidate iterators during an iteration over elements. More...
 
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. More...
 
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 other processors. More...
 
std::unordered_map< Elem *, dof_id_typecoarse_parent
 Keep track of added elements' original coarse ids, so we get subsequent splits assigned to the correct coarse id. More...
 
std::unordered_map< processor_id_type, std::unique_ptr< Elem > > proxy_elements
 Keep track of what processor an element's neighbors came from. More...
 

Detailed Description

A C++ class to refine a simplicial mesh via splitting edges that exceed a given metric.

Author
Roy H. Stogner
Date
2024

Definition at line 49 of file simplex_refiner.h.

Member Typedef Documentation

◆ refinement_datum

Definition at line 171 of file simplex_refiner.h.

Constructor & Destructor Documentation

◆ SimplexRefiner()

libMesh::SimplexRefiner::SimplexRefiner ( UnstructuredMesh mesh)
explicit

The constructor.

A reference to the mesh containing the elements which are to be split by edge subdivision must be provided.

At the time of refining this mesh should be conforming.

Definition at line 45 of file simplex_refiner.C.

45  :
46  _mesh(mesh),
47  _desired_volume(0.1)
48 {}
MeshBase & mesh
UnstructuredMesh & _mesh
Reference to the mesh which is to be refined.
Real _desired_volume
The desired volume for the elements in the resulting mesh.

Member Function Documentation

◆ desired_volume()

Real& libMesh::SimplexRefiner::desired_volume ( )
inline

Sets and/or gets the desired element volume.

Set to zero to disable volume constraint.

If a desired_volume_function is set, then desired_volume() should be used to set a minimum desired volume; this will reduce "false negatives" by suggesting how finely to sample desired_volume_function inside large elements, where ideally the desired_volume_function will be satisfied in the element interior and not just at the element vertices.

Definition at line 93 of file simplex_refiner.h.

References _desired_volume.

Referenced by main(), should_refine_elem(), and SimplexRefinementTest::testRefinement().

93 {return _desired_volume;}
Real _desired_volume
The desired volume for the elements in the resulting mesh.

◆ fill_refinement_datum()

void libMesh::SimplexRefiner::fill_refinement_datum ( std::pair< Node *, Node *>  vertices,
refinement_datum vec 
)
private

Helper for responding to edge queries.

Definition at line 567 of file simplex_refiner.C.

References new_nodes.

Referenced by refine_via_edges().

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 }
std::map< std::pair< Node *, Node * >, Node * > new_nodes
Keep track of new nodes on edges.
void fill_refinement_datum(std::pair< Node *, Node *> vertices, refinement_datum &vec)
Helper for responding to edge queries.

◆ get_desired_volume_function()

FunctionBase< Real > * libMesh::SimplexRefiner::get_desired_volume_function ( )
virtual

Get the function giving desired element volume as a function of position, or nullptr if no such function has been set.

Definition at line 561 of file simplex_refiner.C.

References _desired_volume_func.

Referenced by should_refine_elem().

562 {
563  return _desired_volume_func.get();
564 }
std::unique_ptr< FunctionBase< Real > > _desired_volume_func
Location-dependent volume requirements.

◆ refine_elements()

bool libMesh::SimplexRefiner::refine_elements ( )

Finds elements which exceed the requested metric and refines them via subdivision into new simplices as necessary.

Returns
true iff the mesh actually changed.

Definition at line 52 of file simplex_refiner.C.

References _mesh, libMesh::MeshBase::is_prepared(), libMesh::MeshBase::prepare_for_use(), and refine_via_edges().

Referenced by main(), and SimplexRefinementTest::testRefinement().

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 }
bool is_prepared() const
Definition: mesh_base.h:198
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
UnstructuredMesh & _mesh
Reference to the mesh which is to be refined.
std::size_t refine_via_edges()
Finds elements which exceed the requested metric and refines them via inserting new midedge nodes and...

◆ refine_via_edges() [1/2]

std::size_t libMesh::SimplexRefiner::refine_via_edges ( )
protected

Finds elements which exceed the requested metric and refines them via inserting new midedge nodes and bisecting as necessary.

Returns
the number of refined elements

Definition at line 312 of file simplex_refiner.C.

References _mesh, libMesh::MeshBase::add_elem(), libMesh::MeshBase::add_point(), added_elements, libMesh::Elem::build(), coarse_parent, libMesh::ParallelObject::comm(), edge_queries, fill_refinement_datum(), libMesh::DofObject::id(), libMesh::MeshBase::is_replicated(), libMesh::MeshTools::libmesh_assert_equal_connectivity(), libMesh::MeshTools::libmesh_assert_equal_points(), libMesh::MeshTools::libmesh_assert_valid_unique_ids(), libMesh::MeshCommunication::make_node_proc_ids_parallel_consistent(), libMesh::make_range(), TIMPI::Communicator::max(), libMesh::Elem::neighbor_ptr(), new_elements, new_nodes, libMesh::MeshBase::node_ptr(), libMesh::NODEELEM, libMesh::ParallelObject::processor_id(), libMesh::DofObject::processor_id(), proxy_elements, libMesh::remote_elem, libMesh::MeshBase::renumber_elem(), libMesh::MeshBase::renumber_node(), libMesh::DofObject::set_unique_id(), and libMesh::Elem::vertex_average().

Referenced by refine_elements(), and refine_via_edges().

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.
528  MeshCommunication mc;
529  mc.make_node_proc_ids_parallel_consistent (_mesh);
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 }
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 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 ...
std::vector< std::tuple< dof_id_type, dof_id_type, dof_id_type, processor_id_type > > refinement_datum
std::map< std::pair< Node *, Node * >, Node * > new_nodes
Keep track of new nodes on edges.
const Parallel::Communicator & comm() 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)=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
uint8_t processor_id_type
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...
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
void fill_refinement_datum(std::pair< Node *, Node *> vertices, refinement_datum &vec)
Helper for responding to edge queries.
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...
void libmesh_assert_equal_connectivity(const MeshBase &mesh)
A function for testing that element nodal connectivities match across processors. ...
Definition: mesh_tools.C:1621
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
virtual bool is_replicated() const
Definition: mesh_base.h:233
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
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
uint8_t dof_id_type
Definition: id_types.h:67
const RemoteElem * remote_elem
Definition: remote_elem.C:57

◆ refine_via_edges() [2/2]

std::size_t libMesh::SimplexRefiner::refine_via_edges ( Elem elem,
dof_id_type  coarse_id 
)
protected

Checks if an element exceeds the requested metric or if it has an edge which was split by a neighboring metric and refines it (bisecting it, removing it from the mesh to be replaced by the two subelements, and recursing into those) if necessary.

The coarse_id specifies which coarse element this one is (or which this one was originally refined from), to make global communication easier after local refinement is done.

Returns
the number of refinements done; this may be greater than 1 if subelements were themselves refined.

Definition at line 111 of file simplex_refiner.C.

References _desired_volume_func, _mesh, libMesh::MeshBase::add_point(), libMesh::BoundaryInfo::add_side(), added_elements, libMesh::BoundaryInfo::boundary_ids(), coarse_parent, libMesh::MeshBase::delete_elem(), edge_queries, libMesh::MeshBase::get_boundary_info(), libMesh::DofObject::get_extra_integer(), libMesh::DofObject::id(), libMesh::DofObject::invalid_id, libMesh::MeshBase::is_serial(), libMesh::Elem::level(), libMesh::make_range(), libMesh::DofObject::n_extra_integers(), libMesh::Elem::n_sides(), libMesh::Elem::neighbor_ptr(), new_elements, new_nodes, libMesh::Elem::node_ptr(), libMesh::ParallelObject::processor_id(), libMesh::DofObject::processor_id(), proxy_elements, libMesh::Real, refine_via_edges(), libMesh::remote_elem, libMesh::BoundaryInfo::remove(), should_refine_elem(), libMesh::TOLERANCE, libMesh::TRI3, libMesh::Elem::type(), libMesh::Elem::vertex_average(), libMesh::MeshTools::volume(), and libMesh::Elem::volume().

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 }
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...
std::unique_ptr< FunctionBase< Real > > _desired_volume_func
Location-dependent volume requirements.
static constexpr Real TOLERANCE
std::map< std::pair< Node *, Node * >, Node * > new_nodes
Keep track of new nodes on edges.
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.
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
virtual bool is_serial() const
Definition: mesh_base.h:211
virtual void delete_elem(Elem *e)=0
Removes element e from the mesh.
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...
bool should_refine_elem(Elem &elem)
Checks if an element exceeds the requested metric.
static const dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
Definition: dof_object.h:482
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
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...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
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
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...
processor_id_type processor_id() const
processor_id_type processor_id() const
Definition: dof_object.h:905
const RemoteElem * remote_elem
Definition: remote_elem.C:57

◆ set_desired_volume_function()

void libMesh::SimplexRefiner::set_desired_volume_function ( FunctionBase< Real > *  desired)
virtual

Set a function giving desired element volume as a function of position.

Set this to nullptr to disable position-dependent volume constraint (falling back on desired_volume()).

Definition at line 551 of file simplex_refiner.C.

References libMesh::FunctionBase< Output >::clone().

552 {
553  if (desired)
554  _desired_volume_func = desired->clone();
555  else
556  _desired_volume_func.reset();
557 }
std::unique_ptr< FunctionBase< Real > > _desired_volume_func
Location-dependent volume requirements.
virtual std::unique_ptr< FunctionBase< Output > > clone() const =0

◆ should_refine_elem()

bool libMesh::SimplexRefiner::should_refine_elem ( Elem elem)
protected

Checks if an element exceeds the requested metric.

Definition at line 65 of file simplex_refiner.C.

References desired_volume(), get_desired_volume_function(), libMesh::libmesh_assert(), libMesh::make_range(), libMesh::Elem::n_vertices(), libMesh::Elem::point(), libMesh::Real, libMesh::TOLERANCE, libMesh::MeshTools::volume(), and libMesh::Elem::volume().

Referenced by refine_via_edges().

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 }
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...
static constexpr Real TOLERANCE
Real & desired_volume()
Sets and/or gets the desired element volume.
libmesh_assert(ctx)
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
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
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

Member Data Documentation

◆ _desired_volume

Real libMesh::SimplexRefiner::_desired_volume
private

The desired volume for the elements in the resulting mesh.

Definition at line 137 of file simplex_refiner.h.

Referenced by desired_volume().

◆ _desired_volume_func

std::unique_ptr<FunctionBase<Real> > libMesh::SimplexRefiner::_desired_volume_func
private

Location-dependent volume requirements.

Definition at line 142 of file simplex_refiner.h.

Referenced by get_desired_volume_function(), and refine_via_edges().

◆ _mesh

UnstructuredMesh& libMesh::SimplexRefiner::_mesh
private

Reference to the mesh which is to be refined.

Definition at line 132 of file simplex_refiner.h.

Referenced by refine_elements(), and refine_via_edges().

◆ added_elements

std::unordered_map<dof_id_type, std::unordered_map<Point, Elem *> > libMesh::SimplexRefiner::added_elements
private

Keep track of elements added within each original element, so we can answer queries about them from other processors.

If the element originally with id i is split, its replacements e1 and e2 will be in added_elements[i][ei.vertex_average()]

Definition at line 185 of file simplex_refiner.h.

Referenced by refine_via_edges().

◆ coarse_parent

std::unordered_map<Elem *, dof_id_type> libMesh::SimplexRefiner::coarse_parent
private

Keep track of added elements' original coarse ids, so we get subsequent splits assigned to the correct coarse id.

Definition at line 191 of file simplex_refiner.h.

Referenced by refine_via_edges().

◆ edge_queries

std::unordered_map<processor_id_type, std::vector<std::pair<dof_id_type, dof_id_type> > > libMesh::SimplexRefiner::edge_queries
private

Keep track of coarse remote edges for which we should query about additional point splits.

Definition at line 163 of file simplex_refiner.h.

Referenced by refine_via_edges().

◆ new_elements

std::vector<std::unique_ptr<Elem> > libMesh::SimplexRefiner::new_elements
private

Keep track of elements to add so we don't invalidate iterators during an iteration over elements.

Definition at line 154 of file simplex_refiner.h.

Referenced by refine_via_edges().

◆ new_nodes

std::map<std::pair<Node *, Node *>, Node *> libMesh::SimplexRefiner::new_nodes
private

Keep track of new nodes on edges.

A new node x in between node y and node z (with y<z) will be reflected by new_nodes[(y,z)]=x

Definition at line 148 of file simplex_refiner.h.

Referenced by fill_refinement_datum(), and refine_via_edges().

◆ proxy_elements

std::unordered_map<processor_id_type, std::unique_ptr<Elem> > libMesh::SimplexRefiner::proxy_elements
private

Keep track of what processor an element's neighbors came from.

Definition at line 196 of file simplex_refiner.h.

Referenced by refine_via_edges().


The documentation for this class was generated from the following files: