Skip to content

Commit 904e8c2

Browse files
authored
Merge pull request #3324 from roystgnr/distmesh_fixes
Poly2TriTriangulator fixes for DistributedMesh
2 parents 21b4b6e + 1e7ffca commit 904e8c2

14 files changed

Lines changed: 187 additions & 66 deletions

examples/subdomains/subdomains_ex3/subdomains_ex3.C

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ int main (int argc, char ** argv)
9090

9191
if (argc == 3 && std::atoi(argv[2]) == 3)
9292
{
93-
libmesh_here();
93+
std::cout << "Running in 3D" << std::endl;
9494
dim=3;
9595
}
9696

include/mesh/mesh_serializer.h

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,26 @@ namespace libMesh
3030
class MeshBase;
3131

3232
/**
33-
* Temporarily serialize a DistributedMesh for output; a distributed
34-
* mesh is allgathered by the MeshSerializer constructor if
35-
* need_serial is true, then remote elements are deleted again by the
36-
* destructor.
33+
* Temporarily serialize a DistributedMesh for non-distributed-mesh
34+
* capable code paths. A distributed mesh is allgathered by the
35+
* MeshSerializer constructor if need_serial is true, in which case
36+
* remote elements are deleted again by the destructor.
37+
*
38+
* Serialization to processor 0 alone (e.g. for serial output from
39+
* that processor) can be selected in the constructor.
40+
*
41+
* If allow_remote_element_removal() is set to true, that will also be
42+
* temporarily disabled by the serializer, to be reenabled after
43+
* serializer distruction if so; this allows prepare_for_use() to be
44+
* called safely from within serialized code.
45+
*
46+
* If a mesh is explicitly distributed by a `delete_remote_elements()`
47+
* call within serialized code, or if allow_remote_element_removal()
48+
* is explicitly set to true within serialized code, the behavior is
49+
* undefined.
3750
*
3851
* \author Roy Stogner
39-
* \date 2011
52+
* \date 2011-2022
4053
* \brief Temporarily serializes a DistributedMesh for output.
4154
*/
4255
class MeshSerializer
@@ -47,8 +60,24 @@ class MeshSerializer
4760
~MeshSerializer();
4861

4962
private:
63+
/*
64+
* The mesh which should remain serialized while this serializer
65+
* exists
66+
*/
5067
MeshBase & _mesh;
68+
69+
/*
70+
* Whether to delete remote elements on the mesh (returning it to a
71+
* distributed state) when the serializer is destroyed
72+
*/
5173
bool reparallelize;
74+
75+
/*
76+
* Whether to again allow `prepare_for_use` to delete remote
77+
* elements on the mesh (returning it to a distributed state) after
78+
* the serializer is destroyed
79+
*/
80+
bool resume_allow_remote_element_removal;
5281
};
5382

5483
} // namespace libMesh

include/mesh/poly2tri_triangulator.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727

2828
// Local Includes
2929
#include "libmesh/dof_object.h"
30-
#include "libmesh/mesh_serializer.h"
3130
#include "libmesh/triangulator_interface.h"
3231

3332
namespace libMesh
@@ -139,11 +138,6 @@ class Poly2TriTriangulator : public TriangulatorInterface
139138
*/
140139
std::map<const Hole *, std::unique_ptr<ArbitraryHole>> replaced_holes;
141140

142-
/**
143-
* We only operate on serialized meshes.
144-
*/
145-
MeshSerializer _serializer;
146-
147141
/**
148142
* Keep track of how many mesh nodes are boundary nodes.
149143
*/

include/mesh/triangulator_interface.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,11 +234,17 @@ class TriangulatorInterface
234234
protected:
235235
/**
236236
* Helper function to create PSLG segments from our other
237-
* boundary-defining options (nodal ordering, 1D mesh edges, 2D mesh
237+
* boundary-defining options (1D mesh edges, 2D mesh
238238
* boundary sides), if no segments already exist.
239239
*/
240240
void elems_to_segments();
241241

242+
/**
243+
* Helper function to create PSLG segments from our node ordering,
244+
* up to the maximum node id, if no segments already exist.
245+
*/
246+
void nodes_to_segments(dof_id_type max_node_id);
247+
242248
/**
243249
* Helper function to add extra points (midpoints of initial
244250
* segments) to a PSLG triangulation

src/error_estimation/exact_solution.C

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "libmesh/function_base.h"
2525
#include "libmesh/mesh_base.h"
2626
#include "libmesh/mesh_function.h"
27+
#include "libmesh/mesh_serializer.h"
2728
#include "libmesh/numeric_vector.h"
2829
#include "libmesh/parallel.h"
2930
#include "libmesh/quadrature.h"
@@ -475,9 +476,14 @@ void ExactSolution::_compute_error(std::string_view sys_name,
475476
const unsigned int var_component =
476477
computed_system.variable_scalar_number(var, 0);
477478

478-
// Prepare a global solution and a MeshFunction of the coarse system if we need one
479+
// Prepare a global solution, a serialized mesh, and a MeshFunction
480+
// of the coarse system, if we need them for fine-system integration
479481
std::unique_ptr<MeshFunction> coarse_values;
480-
std::unique_ptr<NumericVector<Number>> comparison_soln = NumericVector<Number>::build(_equation_systems.comm());
482+
std::unique_ptr<NumericVector<Number>> comparison_soln =
483+
NumericVector<Number>::build(_equation_systems.comm());
484+
MeshSerializer
485+
serial(const_cast<MeshBase&>(_equation_systems.get_mesh()),
486+
_equation_systems_fine);
481487
if (_equation_systems_fine)
482488
{
483489
const System & comparison_system

src/mesh/mesh_serializer.C

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ namespace libMesh
2626

2727
MeshSerializer::MeshSerializer(MeshBase & mesh, bool need_serial, bool serial_only_needed_on_proc_0) :
2828
_mesh(mesh),
29-
reparallelize(false)
29+
reparallelize(false),
30+
resume_allow_remote_element_removal(false)
3031
{
3132
libmesh_parallel_only(mesh.comm());
3233
if (need_serial && !_mesh.is_serial() && !serial_only_needed_on_proc_0) {
@@ -38,6 +39,12 @@ MeshSerializer::MeshSerializer(MeshBase & mesh, bool need_serial, bool serial_on
3839
// Just waste a bit of space on processor 0 to speed things up
3940
_mesh.gather_to_zero();
4041
}
42+
43+
if (_mesh.allow_remote_element_removal())
44+
{
45+
resume_allow_remote_element_removal = true;
46+
_mesh.allow_remote_element_removal(false);
47+
}
4148
}
4249

4350

@@ -46,6 +53,9 @@ MeshSerializer::~MeshSerializer()
4653
{
4754
if (reparallelize)
4855
_mesh.delete_remote_elements();
56+
57+
if (resume_allow_remote_element_removal)
58+
_mesh.allow_remote_element_removal(true);
4959
}
5060

5161
} // namespace libMesh

src/mesh/mesh_triangle_holes.C

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "libmesh/elem.h"
2626
#include "libmesh/int_range.h"
2727
#include "libmesh/mesh_base.h"
28+
#include "libmesh/mesh_serializer.h"
2829
#include "libmesh/node.h"
2930
#include "libmesh/parallel_algebra.h" // Packing<Point>
3031
#include "libmesh/simple_range.h"
@@ -262,6 +263,9 @@ TriangulatorInterface::MeshedHole::MeshedHole(const MeshBase & mesh,
262263
// pointers as keys.
263264
libmesh_parallel_only(mesh.comm());
264265

266+
MeshSerializer serial(const_cast<MeshBase &>(mesh),
267+
/* serial */ true, /* only proc 0 */ true);
268+
265269
if (mesh.processor_id() != 0)
266270
{
267271
// Receive what proc 0 will send later

src/mesh/mesh_triangle_interface.C

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,16 @@ void TriangleInterface::triangulate()
6060
// Will the triangulation have holes?
6161
const bool have_holes = ((_holes != nullptr) && (!_holes->empty()));
6262

63+
unsigned int n_hole_points = this->total_hole_points();
64+
65+
// If we're doing PSLG without segments, construct them from all our
66+
// mesh nodes
67+
this->nodes_to_segments(_mesh.max_node_id());
68+
6369
// Insert additional new points in between existing boundary points,
6470
// if that is requested and reasonable
6571
this->insert_any_extra_boundary_points();
6672

67-
unsigned int n_hole_points = this->total_hole_points();
68-
6973
// Regardless of whether we added additional points, the set of points to
7074
// triangulate is now sitting in the mesh.
7175

@@ -339,6 +343,8 @@ void TriangleInterface::triangulate()
339343
}
340344

341345

346+
_mesh.set_mesh_dimension(2);
347+
342348

343349
// To the naked eye, a few smoothing iterations usually looks better,
344350
// so we do this by default unless the user says not to.

src/mesh/poly2tri_triangulator.C

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "libmesh/enum_elem_type.h"
2929
#include "libmesh/function_base.h"
3030
#include "libmesh/hashing.h"
31+
#include "libmesh/mesh_serializer.h"
3132
#include "libmesh/mesh_smoother_laplace.h"
3233
#include "libmesh/mesh_triangle_holes.h"
3334
#include "libmesh/unstructured_mesh.h"
@@ -151,7 +152,6 @@ namespace libMesh
151152
Poly2TriTriangulator::Poly2TriTriangulator(UnstructuredMesh & mesh,
152153
dof_id_type n_boundary_nodes)
153154
: TriangulatorInterface(mesh),
154-
_serializer(_mesh),
155155
_n_boundary_nodes(n_boundary_nodes),
156156
_refine_bdy_allowed(true)
157157
{
@@ -164,6 +164,11 @@ Poly2TriTriangulator::~Poly2TriTriangulator() = default;
164164
// Primary function responsible for performing the triangulation
165165
void Poly2TriTriangulator::triangulate()
166166
{
167+
// We only operate on serialized meshes. And it's not safe to
168+
// serialize earlier, because it would then be possible for the user
169+
// to re-parallelize the mesh in between there and here.
170+
MeshSerializer serializer(_mesh);
171+
167172
// We don't yet support every set of Triangulator options in the
168173
// poly2tri implementation
169174

@@ -184,6 +189,14 @@ void Poly2TriTriangulator::triangulate()
184189
_elem_type != TRI7)
185190
libmesh_not_implemented();
186191

192+
// If we have no explicit segments defined, we may get them from
193+
// mesh elements
194+
this->elems_to_segments();
195+
196+
// If we *still* have no explicit segments defined, we get them from
197+
// the order of nodes.
198+
this->nodes_to_segments(_n_boundary_nodes);
199+
187200
// Insert additional new points in between existing boundary points,
188201
// if that is requested and reasonable
189202
this->insert_any_extra_boundary_points();
@@ -199,6 +212,8 @@ void Poly2TriTriangulator::triangulate()
199212
if (_markers)
200213
libmesh_not_implemented();
201214

215+
_mesh.set_mesh_dimension(2);
216+
202217
// To the naked eye, a few smoothing iterations usually looks better,
203218
// so we do this by default unless the user says not to.
204219
if (this->_smooth_after_generating)
@@ -273,9 +288,10 @@ void Poly2TriTriangulator::triangulate_current_points()
273288

274289
// Unless we're using an explicit segments list, we assume node ids
275290
// are contiguous here.
291+
dof_id_type nn = _mesh.max_node_id();
276292
if (this->segments.empty())
277293
libmesh_error_msg_if
278-
(_mesh.n_nodes() != _mesh.max_node_id(),
294+
(_mesh.n_nodes() != nn,
279295
"Poly2TriTriangulator needs contiguous node ids or explicit segments!");
280296

281297
// And if we have more nodes than outer boundary points, the rest
@@ -301,10 +317,6 @@ void Poly2TriTriangulator::triangulate_current_points()
301317
// Prepare poly2tri points for our nodes, sorted into outer boundary
302318
// points and interior Steiner points.
303319

304-
// If we have no explicit segments defined, we may get them from
305-
// mesh elements
306-
this->elems_to_segments();
307-
308320
if (this->segments.empty())
309321
{
310322
// If we have no segments even after taking elems into account,
@@ -455,7 +467,7 @@ void Poly2TriTriangulator::triangulate_current_points()
455467
}
456468
else
457469
{
458-
Node * node = _mesh.add_point(p);
470+
Node * node = _mesh.add_point(p, nn++);
459471
point_node_map[pt] = node;
460472
}
461473
}
@@ -577,6 +589,11 @@ bool Poly2TriTriangulator::insert_refinement_points()
577589

578590
BoundaryInfo & boundary_info = _mesh.get_boundary_info();
579591

592+
// In cases where we've been working with contiguous node id ranges;
593+
// let's keep it that way.
594+
dof_id_type nn = _mesh.max_node_id();
595+
dof_id_type ne = _mesh.max_elem_id();
596+
580597
for (auto & elem : mesh.element_ptr_range())
581598
{
582599
// element_ptr_range skips deleted elements ... right?
@@ -663,13 +680,13 @@ bool Poly2TriTriangulator::insert_refinement_points()
663680
side))
664681
{
665682
new_pt = ray_start;
666-
new_node = mesh.add_point(new_pt);
683+
new_node = mesh.add_point(new_pt, nn++);
667684
boundary_refine(side);
668685
}
669686
else
670687
{
671688
new_pt = cavity_elem->vertex_average();
672-
new_node = mesh.add_point(new_pt);
689+
new_node = mesh.add_point(new_pt, nn++);
673690
// This was going to be a side refinement but it's
674691
// now an internal refinement
675692
side = invalid_uint;
@@ -724,21 +741,21 @@ bool Poly2TriTriangulator::insert_refinement_points()
724741
// Let's just try bisecting for now
725742
new_pt = (cavity_elem->point(side) +
726743
cavity_elem->point((side+1)%3)) / 2;
727-
new_node = mesh.add_point(new_pt);
744+
new_node = mesh.add_point(new_pt, nn++);
728745
boundary_refine(side);
729746
}
730747
else // Do the best we can under these restrictions
731748
{
732749
new_pt = cavity_elem->vertex_average();
733-
new_node = mesh.add_point(new_pt);
750+
new_node = mesh.add_point(new_pt, nn++);
734751

735752
// This was going to be a side refinement but it's
736753
// now an internal refinement
737754
side = invalid_uint;
738755
}
739756
}
740757
else
741-
new_node = mesh.add_point(new_pt);
758+
new_node = mesh.add_point(new_pt, nn++);
742759
}
743760
else
744761
libmesh_assert(new_node);
@@ -845,7 +862,7 @@ bool Poly2TriTriangulator::insert_refinement_points()
845862
continue;
846863
}
847864

848-
auto new_elem = Elem::build(TRI3);
865+
auto new_elem = Elem::build_with_id(TRI3, ne++);
849866
new_elem->set_node(0) = new_node;
850867
new_elem->set_node(1) = node_CW;
851868
new_elem->set_node(2) = node_CCW;

0 commit comments

Comments
 (0)