libMesh
Loading...
Searching...
No Matches
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.
 
bool refine_elements ()
 Finds elements which exceed the requested metric and refines them via subdivision into new simplices as necessary.
 
virtual void set_desired_volume_function (FunctionBase< Real > *desired)
 Set a function giving desired element volume as a function of position.
 
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.
 
Realdesired_volume ()
 Sets and/or gets the desired element volume.
 

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.
 
bool should_refine_elem (Elem &elem)
 Checks if an element exceeds the requested metric.
 
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.
 

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.
 

Private Attributes

UnstructuredMesh_mesh
 Reference to the mesh which is to be refined.
 
Real _desired_volume
 The desired volume for the elements in the resulting mesh.
 
std::unique_ptr< FunctionBase< Real > > _desired_volume_func
 Location-dependent volume requirements.
 
std::map< std::pair< Node *, Node * >, Node * > new_nodes
 Keep track of new nodes on edges.
 
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.
 
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.
 
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.
 
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.
 
std::unordered_map< processor_id_type, std::unique_ptr< Elem > > proxy_elements
 Keep track of what processor an element's neighbors came from.
 

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),
48{}
Real _desired_volume
The desired volume for the elements in the resulting mesh.
UnstructuredMesh & _mesh
Reference to the mesh which is to be refined.
MeshBase & 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.

93{return _desired_volume;}

References _desired_volume.

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

◆ 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.

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.

References fill_refinement_datum(), and new_nodes.

Referenced by fill_refinement_datum(), and refine_via_edges().

◆ 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.

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

References _desired_volume_func.

Referenced by should_refine_elem().

◆ 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.

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.C:1064
void prepare_for_use(const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
Prepare a newly created (or read) mesh for use.
Definition mesh_base.C:824
std::size_t refine_via_edges()
Finds elements which exceed the requested metric and refines them via inserting new midedge nodes and...

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

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

◆ 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.

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)
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;
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 max(const T &r, T &o, Request &req) const
dof_id_type id() const
Definition dof_object.h:819
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition elem.C:442
virtual const Node * node_ptr(const dof_id_type i) const =0
virtual bool is_replicated() const
Definition mesh_base.h:379
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...
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.
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 ...
virtual void clear()
Deletes all the element and node data that is currently stored.
Definition mesh_base.C:1036
processor_id_type processor_id() const
const Parallel::Communicator & comm() const
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.
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::vector< std::tuple< dof_id_type, dof_id_type, dof_id_type, processor_id_type > > refinement_datum
std::unordered_map< Elem *, dof_id_type > coarse_parent
Keep track of added elements' original coarse ids, so we get subsequent splits assigned to the correc...
std::unordered_map< processor_id_type, std::unique_ptr< Elem > > proxy_elements
Keep track of what processor an element's neighbors came from.
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.
void pull_parallel_vector_data(const Communicator &comm, const MapToVectors &queries, GatherFunctor &gather_data, const ActionFunctor &act_on_data, const datum *example)
void libmesh_assert_equal_connectivity(const MeshBase &mesh)
A function for testing that element nodal connectivities match across processors.
void libmesh_assert_valid_unique_ids(const MeshBase &mesh)
A function for verifying that unique ids match across processors.
void libmesh_assert_equal_points(const MeshBase &mesh)
A function for testing that node locations match across processors.
const RemoteElem * remote_elem
Definition remote_elem.C:57
uint8_t dof_id_type
Definition id_types.h:67
uint8_t processor_id_type
Definition id_types.h:104
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:176

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_ids_parallel_consistent(), libMesh::MeshCommunication::make_node_proc_ids_parallel_consistent(), libMesh::MeshCommunication::make_node_unique_ids_parallel_consistent(), libMesh::make_range(), libMesh::Parallel::Communicator::max(), libMesh::Elem::neighbor_ptr(), new_elements, new_nodes, libMesh::MeshBase::node_ptr(), libMesh::NODEELEM, libMesh::DofObject::processor_id(), libMesh::ParallelObject::processor_id(), proxy_elements, TIMPI::pull_parallel_vector_data(), refine_via_edges(), 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(), refine_via_edges(), and refine_via_edges().

◆ 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.

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,
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}
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.
processor_id_type processor_id() const
Definition dof_object.h:881
static constexpr dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
Definition dof_object.h:473
virtual bool is_serial() const
Definition mesh_base.h:357
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition mesh_base.h:170
virtual void delete_elem(Elem *e)=0
Removes element e from the mesh.
bool should_refine_elem(Elem &elem)
Checks if an element exceeds the requested metric.
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 =...
const Elem & get(const ElemType type_in)
static constexpr Real TOLERANCE
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real

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::DofObject::processor_id(), libMesh::ParallelObject::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(), and libMesh::Elem::volume().

◆ 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 550 of file simplex_refiner.C.

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

References _desired_volume_func, and libMesh::FunctionBase< Output >::clone().

◆ 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.

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
78 if (volume < TOLERANCE*TOLERANCE)
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...
Real & desired_volume()
Sets and/or gets the desired element volume.
libmesh_assert(ctx)

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

Referenced by refine_via_edges().

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(), refine_via_edges(), and set_desired_volume_function().

◆ _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(), refine_via_edges(), 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(), and 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(), and 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(), and 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(), and 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(), refine_via_edges(), 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(), and refine_via_edges().


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