Skip to content

Commit 334a30d

Browse files
authored
Merge pull request #4394 from roystgnr/distributed_inffe_fixes
Distributed infinite element mesh building fixes
2 parents 8f848de + 483b23d commit 334a30d

5 files changed

Lines changed: 137 additions & 42 deletions

File tree

examples/miscellaneous/miscellaneous_ex15/miscellaneous_ex15.C

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050
#include "libmesh/error_vector.h"
5151
#include "libmesh/mesh_refinement.h"
5252
#include "libmesh/kelly_error_estimator.h"
53+
// for distributed mesh editing
54+
#include "libmesh/parallel_ghost_sync.h"
5355

5456
// Bring in everything from the libMesh namespace
5557
using namespace libMesh;
@@ -65,7 +67,7 @@ void assemble_func(EquationSystems & es, const std::string & system_name)
6567
std::unique_ptr<FEBase> fe (FEBase::build(dim, fe_type));
6668
std::unique_ptr<FEBase> inf_fe (FEBase::build_InfFE(dim, fe_type));
6769

68-
const std::vector<dof_id_type > charged_objects = es.parameters.get<std::vector<dof_id_type> >("charged_elem_id");
70+
const std::set<dof_id_type > charged_objects = es.parameters.get<std::set<dof_id_type> >("charged_elem_id");
6971
const auto qrule=fe_type.default_quadrature_rule(/*dim = */ 3, /*extra order = */ 3);
7072

7173
fe->attach_quadrature_rule (&*qrule);
@@ -115,7 +117,7 @@ void assemble_func(EquationSystems & es, const std::string & system_name)
115117

116118
Real rho;
117119
// check if charged_objects contains elem->id(), we add the corresponding charge to the rhs vector
118-
if (std::find(charged_objects.begin(), charged_objects.end(), elem->id()) != charged_objects.end())
120+
if (charged_objects.count(elem->id()))
119121
rho=1./elem->volume();
120122
else
121123
rho = 0;
@@ -178,6 +180,10 @@ int main (int argc, char** argv)
178180
// creation of an empty mesh-object
179181
Mesh mesh(init.comm(), dim);
180182

183+
// We're going to keep track of elements by id, so we don't want
184+
// even a DistributedMesh to renumber them.
185+
mesh.allow_renumbering(false);
186+
181187
// fill the meshes with a spherical grid of type HEX27 with radius r
182188
MeshTools::Generation::build_cube (mesh, /*nx= */5, /*ny=*/5, /*nz=*/3,
183189
/*xmin=*/ -5.2, /*xmax=*/5.2,
@@ -205,31 +211,44 @@ int main (int argc, char** argv)
205211
elem->neighbor_ptr(0)->subdomain_id()=2;
206212
}
207213

214+
// If we're on a distributed mesh then we might have missed some
215+
// ghosted base elements with remote infinite elements.
216+
if (!mesh.is_serial())
217+
{
218+
SyncSubdomainIds sync_obj(mesh);
219+
Parallel::sync_dofobject_data_by_id
220+
(mesh.comm(), mesh.elements_begin(), mesh.elements_end(),
221+
sync_obj);
222+
}
208223

209224
// Now we set the sources of the field: prism-shaped objects that are
210225
// determined here by containing certain points:
211-
PointLocatorTree pt_lctr(mesh);
212-
std::vector<dof_id_type> charged_elem_ids(3);
226+
auto p_pt_lctr = mesh.sub_point_locator();
227+
auto & pt_lctr = *p_pt_lctr;
228+
pt_lctr.enable_out_of_mesh_mode();
229+
230+
std::set<dof_id_type> charged_elem_ids;
213231
{
214232
Point pt_0(-3.,-3.0,-1.5);
215233
Point pt_1(2.,-2.6,-1.5);
216234
Point pt_2(2., 3.1, 1.7);
217235
const Elem * elem_0=pt_lctr(pt_0);
218236
if (elem_0)
219-
charged_elem_ids[0]=elem_0->id();
220-
else
221-
// this indicates some error which I don't know how to handle.
222-
libmesh_not_implemented();
237+
charged_elem_ids.insert(elem_0->id());
223238
const Elem * elem_1=pt_lctr(pt_1);
224239
if (elem_1)
225-
charged_elem_ids[1]=elem_1->id();
226-
else
227-
libmesh_not_implemented();
240+
charged_elem_ids.insert(elem_1->id());
228241
const Elem * elem_2=pt_lctr(pt_2);
229242
if (elem_2)
230-
charged_elem_ids[2]=elem_2->id();
231-
else
232-
libmesh_not_implemented();
243+
charged_elem_ids.insert(elem_2->id());
244+
245+
// On a distributed mesh we might not have every point in a
246+
// semilocal element on every processor
247+
if (!mesh.is_serial())
248+
mesh.comm().set_union(charged_elem_ids);
249+
250+
// But we should have every point on *some* processor
251+
libmesh_assert_equal_to(charged_elem_ids.size(), 3);
233252
}
234253

235254
// Create an equation systems object
@@ -238,7 +257,7 @@ int main (int argc, char** argv)
238257
// This is the only system added here.
239258
LinearImplicitSystem & eig_sys = eq_sys.add_system<LinearImplicitSystem> ("Poisson");
240259

241-
eq_sys.parameters.set<std::vector<dof_id_type> >("charged_elem_id")=charged_elem_ids;
260+
eq_sys.parameters.set<std::set<dof_id_type> >("charged_elem_id")=charged_elem_ids;
242261

243262
//set the complete type of the variable
244263
FEType fe_type(SECOND, LAGRANGE, FOURTH, JACOBI_20_00, CARTESIAN);
@@ -272,21 +291,31 @@ int main (int argc, char** argv)
272291
// in the refined mesh, find the elements that describe the
273292
// sources of gravitational field: Due to refinement, there are
274293
// successively more elements to account for the same object.
275-
std::vector<dof_id_type> charged_elem_child(0);
276-
for(unsigned int j=0; j< charged_elem_ids.size(); ++j)
294+
std::set<dof_id_type> charged_elem_children;
295+
for(auto id : charged_elem_ids)
277296
{
278-
Elem * charged_elem = mesh.elem_ptr(charged_elem_ids[j]);
297+
Elem * charged_elem = mesh.query_elem_ptr(id);
298+
if (!charged_elem)
299+
{
300+
libmesh_assert(!mesh.is_serial());
301+
continue;
302+
}
303+
279304
if (charged_elem->has_children())
280305
{
281306
for(auto & child : charged_elem->child_ref_range())
282-
charged_elem_child.push_back(child.id());
307+
if (!child.is_remote())
308+
charged_elem_children.insert(child.id());
283309
}
284310
else
285-
charged_elem_child.push_back(charged_elem->id());
311+
charged_elem_children.insert(charged_elem->id());
286312
}
287313

288-
charged_elem_ids=charged_elem_child;
289-
eq_sys.parameters.set<std::vector<dof_id_type> >("charged_elem_id")=charged_elem_ids;
314+
charged_elem_ids=charged_elem_children;
315+
if (!mesh.is_serial())
316+
mesh.comm().set_union(charged_elem_ids);
317+
318+
eq_sys.parameters.set<std::set<dof_id_type> >("charged_elem_id")=charged_elem_ids;
290319

291320
// re-assemble and than solve again.
292321
eig_sys.solve();

include/mesh/mesh_base.h

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -973,14 +973,20 @@ class MeshBase : public ParallelObject
973973
* After this routine is called all the elements with a \p nullptr neighbor
974974
* pointer are guaranteed to be on the boundary. Thus this routine is
975975
* useful for automatically determining the boundaries of the domain.
976-
* If reset_remote_elements is left to false, remote neighbor links are not
977-
* reset and searched for in the local mesh. If reset_current_list is
978-
* left as true, then any existing links will be reset before initiating
979-
* the algorithm, while honoring the value of the reset_remote_elements
980-
* flag.
976+
*
977+
* If \p reset_remote_elements is left to false, remote neighbor
978+
* links are not reset and searched for in the local mesh.
979+
*
980+
* If \p reset_current_list is left as true, then any existing links
981+
* will be reset before initiating the algorithm, while honoring the
982+
* value of the \p reset_remote_elements flag.
983+
*
984+
* If \p assert_valid is left as true, then in dbg mode extensive
985+
* consistency checking is performed before returning.
981986
*/
982987
virtual void find_neighbors (const bool reset_remote_elements = false,
983-
const bool reset_current_list = true) = 0;
988+
const bool reset_current_list = true,
989+
const bool assert_valid = true) = 0;
984990

985991
/**
986992
* Removes any orphaned nodes, nodes not connected to any elements.

include/mesh/unstructured_mesh.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,8 @@ class UnstructuredMesh : public MeshBase
293293
* Other functions from MeshBase requiring re-definition.
294294
*/
295295
virtual void find_neighbors (const bool reset_remote_elements = false,
296-
const bool reset_current_list = true) override;
296+
const bool reset_current_list = true,
297+
const bool assert_valid = true) override;
297298

298299
#ifdef LIBMESH_ENABLE_AMR
299300
/**

src/mesh/inf_elem_builder.C

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
#include "libmesh/mesh_base.h"
3636
#include "libmesh/remote_elem.h"
3737

38+
#include "timpi/parallel_implementation.h"
39+
3840
namespace libMesh
3941
{
4042

@@ -387,10 +389,14 @@ void InfElemBuilder::build_inf_elem(const Point & origin,
387389

388390
} // neighbor(s) == nullptr
389391

390-
391-
392-
393-
392+
// On a distributed mesh it might be some other processor who sees
393+
// the farthest node.
394+
if (!this->_mesh.is_serial())
395+
{
396+
unsigned int rank;
397+
this->_mesh.comm().maxloc(max_r, rank);
398+
this->_mesh.comm().broadcast(max_r_node, rank);
399+
}
394400

395401
// If a boundary side has one node on the outer boundary,
396402
// all points of this side are on the outer boundary.
@@ -405,8 +411,14 @@ void InfElemBuilder::build_inf_elem(const Point & origin,
405411
// Only the case of no outer boundary is to be excluded.
406412
onodes.insert(max_r_node);
407413

414+
// If we're not on a serial mesh, we'll need to synchronize that
415+
// onodes list too.
416+
bool did_parallel_update;
408417

418+
do
409419
{
420+
did_parallel_update = false;
421+
410422
auto face_it = faces.begin();
411423
auto face_end = faces.end();
412424
unsigned int facesfound=0;
@@ -448,7 +460,16 @@ void InfElemBuilder::build_inf_elem(const Point & origin,
448460
face_it = faces.begin();
449461
}
450462
}
463+
464+
if (!this->_mesh.is_serial())
465+
{
466+
auto my_onodes_size = onodes.size();
467+
this->_mesh.comm().set_union(onodes);
468+
did_parallel_update = (onodes.size() > my_onodes_size);
469+
this->_mesh.comm().max(did_parallel_update);
470+
}
451471
}
472+
while (did_parallel_update);
452473

453474

454475
#ifdef DEBUG
@@ -486,26 +507,32 @@ void InfElemBuilder::build_inf_elem(const Point & origin,
486507

487508
// for each boundary node, add an outer_node with
488509
// double distance from origin.
489-
for (const auto & dof : onodes)
510+
for (const auto & n : onodes)
490511
{
491-
Point p = (Point(this->_mesh.point(dof)) * 2) - origin;
512+
if (!this->_mesh.query_node_ptr(n))
513+
{
514+
libmesh_assert(!_mesh.is_serial());
515+
continue;
516+
}
517+
518+
Point p = (Point(this->_mesh.point(n)) * 2) - origin;
492519
if (_mesh.is_serial())
493520
{
494521
// Add with a default id in serial
495-
outer_nodes[dof]=this->_mesh.add_point(p);
522+
outer_nodes[n]=this->_mesh.add_point(p);
496523
}
497524
else
498525
{
499526
// Pick a unique id in parallel
500-
Node & bnode = _mesh.node_ref(dof);
527+
Node & bnode = _mesh.node_ref(n);
501528
dof_id_type new_id = bnode.id() + old_max_node_id;
502529
std::unique_ptr<Node> new_node = Node::build(p, new_id);
503530
new_node->processor_id() = bnode.processor_id();
504531
#ifdef LIBMESH_ENABLE_UNIQUE_ID
505532
new_node->set_unique_id(old_max_unique_id + bnode.id());
506533
#endif
507534

508-
outer_nodes[dof] =
535+
outer_nodes[n] =
509536
this->_mesh.add_node(std::move(new_node));
510537
}
511538
}
@@ -665,6 +692,32 @@ void InfElemBuilder::build_inf_elem(const Point & origin,
665692
this->_mesh.add_elem(std::move(el));
666693
} // for
667694

695+
// If we're not in serial, we might not have all our remote_elem
696+
// neighbors set up properly on the new elements: two new infinite
697+
// elements which should be neighbors may not be rooted in finite
698+
// elmeents which are neighbors, and we were only looking for
699+
// remote_elem links on each root finite element. We'll fix the
700+
// problem by first finding all the neighbors we can and then
701+
// recognizing that any missing neighbors on infinite elements must
702+
// be remote.
703+
if (!_mesh.is_serial())
704+
{
705+
_mesh.find_neighbors(/*reset_remote_elements=*/ false,
706+
/*reset_current_list=*/ true,
707+
/*assert_valid=*/ false);
708+
709+
for (auto & p : ofaces)
710+
{
711+
Elem & belem = this->_mesh.elem_ref(p.first);
712+
Elem * inf_elem = belem.neighbor_ptr(p.second);
713+
libmesh_assert(inf_elem);
714+
715+
for (auto s : make_range(inf_elem->n_sides()))
716+
if (!inf_elem->neighbor_ptr(s))
717+
inf_elem->set_neighbor
718+
(s, const_cast<RemoteElem *>(remote_elem));
719+
}
720+
}
668721

669722
#ifdef DEBUG
670723
_mesh.libmesh_assert_valid_parallel_ids();

src/mesh/unstructured_mesh.C

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -941,7 +941,8 @@ UnstructuredMesh::~UnstructuredMesh ()
941941

942942

943943
void UnstructuredMesh::find_neighbors (const bool reset_remote_elements,
944-
const bool reset_current_list)
944+
const bool reset_current_list,
945+
const bool assert_valid)
945946
{
946947
// We might actually want to run this on an empty mesh
947948
// (e.g. the boundary mesh for a nonexistent bcid!)
@@ -1326,9 +1327,14 @@ void UnstructuredMesh::find_neighbors (const bool reset_remote_elements,
13261327
#endif // AMR
13271328

13281329
#ifdef DEBUG
1329-
MeshTools::libmesh_assert_valid_neighbors(*this,
1330-
!reset_remote_elements);
1331-
MeshTools::libmesh_assert_valid_amr_interior_parents(*this);
1330+
if (assert_valid)
1331+
{
1332+
MeshTools::libmesh_assert_valid_neighbors(*this,
1333+
!reset_remote_elements);
1334+
MeshTools::libmesh_assert_valid_amr_interior_parents(*this);
1335+
}
1336+
#else
1337+
libmesh_ignore(assert_valid);
13321338
#endif
13331339

13341340
this->_preparation.has_neighbor_ptrs = true;

0 commit comments

Comments
 (0)