libMesh
Loading...
Searching...
No Matches
simplex_refiner.C
Go to the documentation of this file.
1// The libMesh Finite Element Library.
2// Copyright (C) 2002-2026 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
40namespace 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();
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}
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,
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)
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.
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
565
566
567void 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 max(const T &r, T &o, Request &req) const
The BoundaryInfo class contains information relevant to boundary conditions including storing faces,...
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.
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.
void remove(const Node *node)
Removes the boundary conditions associated with node node, if any exist.
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...
unsigned int n_extra_integers() const
Returns how many extra integers are associated to the DofObject.
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
dof_id_type id() const
Definition dof_object.h:819
void set_unique_id(unique_id_type new_id)
Sets the unique_id for this DofObject.
Definition dof_object.h:848
This is the base class from which all geometric element types are derived.
Definition elem.h:96
virtual unsigned int n_vertices() const =0
const Point & point(const unsigned int i) const
Definition elem.h:2462
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition elem.C:442
unsigned int level() const
Definition elem.h:3091
const Node * node_ptr(const unsigned int i) const
Definition elem.h:2516
virtual Real volume() const
Definition elem.C:3462
virtual ElemType type() const =0
virtual unsigned int n_sides() const =0
const Elem * neighbor_ptr(unsigned int i) const
Definition elem.h:2615
Point vertex_average() const
Definition elem.C:669
Base class for functors that can be evaluated at a point and (optionally) time.
virtual std::unique_ptr< FunctionBase< Output > > clone() const =0
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
bool is_prepared() const
Definition mesh_base.C:1064
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...
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
virtual void delete_elem(Elem *e)=0
Removes element e from the mesh.
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 Elem * add_elem(Elem *e)=0
Add elem e to the end of the element array.
This is the MeshCommunication class.
void make_node_unique_ids_parallel_consistent(MeshBase &)
Assuming all unique_ids on local nodes are globally unique, and assuming all processor ids are parall...
void make_node_ids_parallel_consistent(MeshBase &)
Assuming all ids on local nodes are globally unique, and assuming all processor ids are parallel cons...
void make_node_proc_ids_parallel_consistent(MeshBase &)
Assuming all processor ids on nodes touching local elements are parallel consistent,...
A Node is like a Point, but with more information.
Definition node.h:55
processor_id_type processor_id() const
const Parallel::Communicator & comm() const
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
std::size_t refine_via_edges()
Finds elements which exceed the requested metric and refines them via inserting new midedge nodes and...
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.
SimplexRefiner(UnstructuredMesh &mesh)
The constructor.
virtual void set_desired_volume_function(FunctionBase< Real > *desired)
Set a function giving desired element volume as a function of position.
bool refine_elements()
Finds elements which exceed the requested metric and refines them via subdivision into new simplices ...
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...
bool should_refine_elem(Elem &elem)
Checks if an element exceeds the requested metric.
std::map< std::pair< Node *, Node * >, Node * > new_nodes
Keep track of new nodes on edges.
std::vector< std::tuple< dof_id_type, dof_id_type, dof_id_type, processor_id_type > > refinement_datum
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::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::unique_ptr< FunctionBase< Real > > _desired_volume_func
Location-dependent volume requirements.
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 fill_refinement_datum(std::pair< Node *, Node * > vertices, refinement_datum &vec)
Helper for responding to edge queries.
Real & desired_volume()
Sets and/or gets the desired element volume.
UnstructuredMesh & _mesh
Reference to the mesh which is to be refined.
The UnstructuredMesh class is derived from the MeshBase class.
MeshBase & mesh
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.
The libMesh namespace provides an interface to certain functionality in the library.
libmesh_assert(ctx)
const RemoteElem * remote_elem
Definition remote_elem.C:57
static constexpr Real TOLERANCE
uint8_t dof_id_type
Definition id_types.h:67
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
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