Skip to content

Commit 28ff000

Browse files
authored
Merge pull request #4182 from roystgnr/interior_parent_parallel
Track a MeshBase::interior_mesh() to enable BoundaryMesh serialization
2 parents cb15e0b + 9c92a9b commit 28ff000

File tree

5 files changed

+424
-98
lines changed

5 files changed

+424
-98
lines changed

include/mesh/mesh_base.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1787,6 +1787,25 @@ class MeshBase : public ParallelObject
17871787
*/
17881788
void detect_interior_parents();
17891789

1790+
/**
1791+
* \return A mesh that may own interior parents of elements in this
1792+
* mesh. In most cases this mesh includes its own interior parents,
1793+
* but in cases where a separate "interior" mesh was used to create
1794+
* this mesh as a distinct lower-dimensional boundary (or boundary
1795+
* subset) mesh, the original mesh will be returned here.
1796+
*/
1797+
const MeshBase & interior_mesh() const { return *_interior_mesh; }
1798+
1799+
/**
1800+
* \return A writeable reference to the interior mesh.
1801+
*/
1802+
MeshBase & interior_mesh() { return *_interior_mesh; }
1803+
1804+
/**
1805+
* Sets the interior mesh. For advanced use only.
1806+
*/
1807+
void set_interior_mesh(MeshBase & int_mesh) { _interior_mesh = &int_mesh; }
1808+
17901809
/**
17911810
* \return The cached mesh subdomains. As long as the mesh is prepared, this
17921811
* should contain all the subdomain ids across processors. Relies on the mesh
@@ -1906,6 +1925,12 @@ class MeshBase : public ParallelObject
19061925
unique_id_type _next_unique_id;
19071926
#endif
19081927

1928+
/**
1929+
* Defaulting to \p this, a pointer to the mesh used to generate
1930+
* boundary elements on \p this.
1931+
*/
1932+
MeshBase *_interior_mesh;
1933+
19091934
/**
19101935
* If this is true then no partitioning should be done with the
19111936
* possible exception of orphaned nodes.

src/mesh/boundary_info.C

Lines changed: 60 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -478,12 +478,18 @@ void BoundaryInfo::sync (const std::set<boundary_id_type> & requested_boundary_i
478478
* into dangling pointers, and it won't be easy to tell which. So
479479
* we'll keep around a distributed copy for that case, and query it
480480
* to fix up interior_parent() links as necessary.
481+
*
482+
* We'll also need to make sure to unserialize the mesh *before* we
483+
* prepare the boundary mesh for use, or the prepare_for_use() call
484+
* on a refined boundary mesh will happily notice that it can find
485+
* and restore some refined elements' interior_parent pointers, not
486+
* knowing that those interior parents are about to go remote.
481487
*/
482488
std::unique_ptr<MeshBase> mesh_copy;
483489
if (boundary_mesh.is_serial() && !_mesh->is_serial())
484490
mesh_copy = _mesh->clone();
485491

486-
MeshSerializer serializer
492+
auto serializer = std::make_unique<MeshSerializer>
487493
(const_cast<MeshBase &>(*_mesh), boundary_mesh.is_serial());
488494

489495
/**
@@ -566,6 +572,11 @@ void BoundaryInfo::sync (const std::set<boundary_id_type> & requested_boundary_i
566572
// interior partitioning.
567573
boundary_mesh.partitioner().reset(nullptr);
568574

575+
// Deserialize the interior mesh before the boundary mesh
576+
// prepare_for_use() can come to erroneous conclusions about which
577+
// of its elements are semilocal
578+
serializer.reset();
579+
569580
// Make boundary_mesh nodes and elements contiguous
570581
boundary_mesh.prepare_for_use();
571582

@@ -650,10 +661,22 @@ void BoundaryInfo::add_elements(const std::set<boundary_id_type> & requested_bou
650661
LOG_SCOPE("add_elements()", "BoundaryInfo");
651662

652663
// We're not prepared to mix serial and distributed meshes in this
653-
// method, so make sure they match from the start.
664+
// method, so make sure their statuses match from the start.
665+
//
666+
// Specifically test *is_serial* here - we can handle a mix of
667+
// ReplicatedMesh and serialized DistributedMesh.
654668
libmesh_assert_equal_to(_mesh->is_serial(),
655669
boundary_mesh.is_serial());
656670

671+
// If the boundary mesh already has interior pointers pointing at
672+
// elements in a third mesh then we're in trouble
673+
libmesh_assert(&boundary_mesh.interior_mesh() == &boundary_mesh ||
674+
&boundary_mesh.interior_mesh() == _mesh);
675+
676+
// And now we're going to add interior pointers to elements from
677+
// this mesh
678+
boundary_mesh.set_interior_mesh(*_mesh);
679+
657680
std::map<std::pair<dof_id_type, unsigned char>, dof_id_type> side_id_map;
658681
this->_find_id_maps(requested_boundary_ids,
659682
0,
@@ -682,25 +705,18 @@ void BoundaryInfo::add_elements(const std::set<boundary_id_type> & requested_bou
682705
!subdomains_relative_to.count(elem->subdomain_id()))
683706
continue;
684707

685-
// Get the top-level parent for this element
686-
const Elem * top_parent = elem->top_parent();
687-
688-
// Find all the boundary side ids for this Elem.
689-
auto bounds = _boundary_side_id.equal_range(top_parent);
690-
691708
for (auto s : elem->side_index_range())
692709
{
693710
bool add_this_side = false;
694-
boundary_id_type this_bcid = invalid_id;
695711

696-
for (const auto & pr : as_range(bounds))
697-
{
698-
this_bcid = pr.second.second;
712+
// Find all the boundary side ids for this Elem side.
713+
std::vector<boundary_id_type> bcids;
714+
this->boundary_ids(elem, s, bcids);
699715

700-
// if this side is flagged with a boundary condition
701-
// and the user wants this id
702-
if ((pr.second.first == s) &&
703-
(requested_boundary_ids.count(this_bcid)))
716+
for (const boundary_id_type bcid : bcids)
717+
{
718+
// if the user wants this id, we want this side
719+
if (requested_boundary_ids.count(bcid))
704720
{
705721
add_this_side = true;
706722
break;
@@ -713,8 +729,7 @@ void BoundaryInfo::add_elements(const std::set<boundary_id_type> & requested_bou
713729
// boundary was copied to the BoundaryMesh, and handles the
714730
// case where elements on the geometric boundary are not in
715731
// any sidesets.
716-
if (bounds.first == bounds.second &&
717-
requested_boundary_ids.count(invalid_id) &&
732+
if (requested_boundary_ids.count(invalid_id) &&
718733
elem->neighbor_ptr(s) == nullptr)
719734
add_this_side = true;
720735

@@ -765,6 +780,9 @@ void BoundaryInfo::add_elements(const std::set<boundary_id_type> & requested_bou
765780
new_elem->set_extra_integer(parent_side_index_tag, s);
766781

767782
#ifdef LIBMESH_ENABLE_AMR
783+
new_elem->set_refinement_flag(elem->refinement_flag());
784+
new_elem->set_p_refinement_flag(elem->p_refinement_flag());
785+
768786
// Set parent links
769787
if (elem->parent())
770788
{
@@ -778,8 +796,6 @@ void BoundaryInfo::add_elements(const std::set<boundary_id_type> & requested_bou
778796

779797
new_elem->set_parent(side_parent);
780798

781-
side_parent->set_refinement_flag(Elem::INACTIVE);
782-
783799
// Figuring out which child we are of our parent
784800
// is a trick. Due to libMesh child numbering
785801
// conventions, if we are an element on a vertex,
@@ -802,6 +818,22 @@ void BoundaryInfo::add_elements(const std::set<boundary_id_type> & requested_bou
802818
side_parent->add_child(new_elem, 3);
803819
}
804820
}
821+
822+
// Set remote_elem child links if necessary. Rather than
823+
// worrying about which interior child corresponds to which side
824+
// child we'll just set all null links to be remote and we'll
825+
// rely on our detection of actual semilocal children to
826+
// overwrite the links that shouldn't be remote.
827+
if (elem->has_children())
828+
for (auto c : make_range(elem->n_children()))
829+
if (elem->child_ptr(c) == remote_elem &&
830+
elem->is_child_on_side(c, s))
831+
{
832+
for (auto sc : make_range(new_elem->n_children()))
833+
if (!new_elem->raw_child_ptr(sc))
834+
new_elem->add_child
835+
(const_cast<RemoteElem*>(remote_elem), sc);
836+
}
805837
#endif
806838

807839
new_elem->set_interior_parent (elem);
@@ -3136,26 +3168,18 @@ void BoundaryInfo::_find_id_maps(const std::set<boundary_id_type> & requested_bo
31363168
!subdomains_relative_to.count(elem->subdomain_id()))
31373169
continue;
31383170

3139-
// Get the top-level parent for this element. This is used for
3140-
// searching for boundary sides on this element.
3141-
const Elem * top_parent = elem->top_parent();
3142-
3143-
// Find all the boundary side ids for this Elem.
3144-
auto bounds = _boundary_side_id.equal_range(top_parent);
3145-
31463171
for (auto s : elem->side_index_range())
31473172
{
31483173
bool add_this_side = false;
3149-
boundary_id_type this_bcid = invalid_id;
31503174

3151-
for (const auto & pr : as_range(bounds))
3152-
{
3153-
this_bcid = pr.second.second;
3175+
// Find all the boundary side ids for this Elem side.
3176+
std::vector<boundary_id_type> bcids;
3177+
this->boundary_ids(elem, s, bcids);
31543178

3155-
// if this side is flagged with a boundary condition
3156-
// and the user wants this id
3157-
if ((pr.second.first == s) &&
3158-
(requested_boundary_ids.count(this_bcid)))
3179+
for (const boundary_id_type bcid : bcids)
3180+
{
3181+
// if the user wants this id, we want this side
3182+
if (requested_boundary_ids.count(bcid))
31593183
{
31603184
add_this_side = true;
31613185
break;
@@ -3168,8 +3192,7 @@ void BoundaryInfo::_find_id_maps(const std::set<boundary_id_type> & requested_bo
31683192
// boundary was copied to the BoundaryMesh, and handles the
31693193
// case where elements on the geometric boundary are not in
31703194
// any sidesets.
3171-
if (bounds.first == bounds.second &&
3172-
requested_boundary_ids.count(invalid_id) &&
3195+
if (requested_boundary_ids.count(invalid_id) &&
31733196
elem->neighbor_ptr(s) == nullptr)
31743197
add_this_side = true;
31753198

src/mesh/mesh_base.C

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ MeshBase::MeshBase (const Parallel::Communicator & comm_in,
7070
#ifdef LIBMESH_ENABLE_UNIQUE_ID
7171
_next_unique_id(DofObject::invalid_unique_id),
7272
#endif
73+
_interior_mesh(this),
7374
_skip_noncritical_partitioning(false),
7475
_skip_all_partitioning(libMesh::on_command_line("--skip-partitioning")),
7576
_skip_renumber_nodes_and_elements(false),
@@ -101,6 +102,10 @@ MeshBase::MeshBase (const MeshBase & other_mesh) :
101102
#ifdef LIBMESH_ENABLE_UNIQUE_ID
102103
_next_unique_id(other_mesh._next_unique_id),
103104
#endif
105+
// If the other mesh interior_parent pointers just go back to
106+
// itself, so should we
107+
_interior_mesh((other_mesh._interior_mesh == &other_mesh) ?
108+
this : other_mesh._interior_mesh),
104109
_skip_noncritical_partitioning(other_mesh._skip_noncritical_partitioning),
105110
_skip_all_partitioning(other_mesh._skip_all_partitioning),
106111
_skip_renumber_nodes_and_elements(other_mesh._skip_renumber_nodes_and_elements),
@@ -168,9 +173,13 @@ MeshBase& MeshBase::operator= (MeshBase && other_mesh)
168173
_is_prepared = other_mesh.is_prepared();
169174
_point_locator = std::move(other_mesh._point_locator);
170175
_count_lower_dim_elems_in_point_locator = other_mesh.get_count_lower_dim_elems_in_point_locator();
171-
#ifdef LIBMESH_ENABLE_UNIQUE_ID
172-
_next_unique_id = other_mesh.next_unique_id();
173-
#endif
176+
#ifdef LIBMESH_ENABLE_UNIQUE_ID
177+
_next_unique_id = other_mesh.next_unique_id();
178+
#endif
179+
// If the other mesh interior_parent pointers just go back to
180+
// itself, so should we
181+
_interior_mesh = (other_mesh._interior_mesh == &other_mesh) ?
182+
this : other_mesh._interior_mesh;
174183
_skip_noncritical_partitioning = other_mesh.skip_noncritical_partitioning();
175184
_skip_all_partitioning = other_mesh.skip_partitioning();
176185
_skip_renumber_nodes_and_elements = !(other_mesh.allow_renumbering());
@@ -247,6 +256,15 @@ bool MeshBase::locally_equals (const MeshBase & other_mesh) const
247256
if (_count_lower_dim_elems_in_point_locator !=
248257
other_mesh._count_lower_dim_elems_in_point_locator)
249258
return false;
259+
260+
// We should either both have our own interior parents or both not;
261+
// but if we both don't then we can't really assert anything else
262+
// because pointing at the same interior mesh is fair but so is
263+
// pointing at two different copies of "the same" interior mesh.
264+
if ((_interior_mesh == this) !=
265+
(other_mesh._interior_mesh == &other_mesh))
266+
return false;
267+
250268
if (_skip_noncritical_partitioning != other_mesh._skip_noncritical_partitioning)
251269
return false;
252270
if (_skip_all_partitioning != other_mesh._skip_all_partitioning)
@@ -1794,11 +1812,17 @@ void MeshBase::detect_interior_parents()
17941812
// This requires an inspection on every processor
17951813
parallel_object_only();
17961814

1797-
// Check if the mesh contains mixed dimensions. If so, then set interior parents, otherwise return.
1815+
// Check if the mesh contains mixed dimensions. If so, then we may
1816+
// have interior parents to set. Otherwise return.
17981817
if (this->elem_dimensions().size() == 1)
17991818
return;
18001819

1801-
//This map will be used to set interior parents
1820+
// Do we have interior parent pointers going to a different mesh?
1821+
// If so then we'll still check to make sure that's the only place
1822+
// they go, so we can libmesh_not_implemented() if not.
1823+
const bool separate_interior_mesh = (&(this->interior_mesh()) != this);
1824+
1825+
// This map will be used to set interior parents
18021826
std::unordered_map<dof_id_type, std::vector<dof_id_type>> node_to_elem;
18031827

18041828
for (const auto & elem : this->element_ptr_range())
@@ -1877,6 +1901,14 @@ void MeshBase::detect_interior_parents()
18771901
break;
18781902
}
18791903
}
1904+
1905+
// Do we have a mixed dimensional mesh that contains some of
1906+
// its own interior parents, but we already expect to have
1907+
// interior parents on a different mesh? That's going to
1908+
// take some work to support if anyone needs it.
1909+
if (separate_interior_mesh)
1910+
libmesh_not_implemented_msg
1911+
("interior_parent() values in multiple meshes are unsupported.");
18801912
}
18811913
}
18821914
}

src/parallel/parallel_elem.C

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,8 @@ Packing<Elem *>::unpack (std::vector<largest_id_type>::const_iterator in,
579579
}
580580
else
581581
{
582-
Elem * ip = mesh->query_elem_ptr(interior_parent_id);
582+
Elem * ip =
583+
mesh->interior_mesh().query_elem_ptr(interior_parent_id);
583584

584585
// The sending processor sees an interior parent here, so
585586
// if we don't have that interior element, then we'd
@@ -794,7 +795,8 @@ Packing<Elem *>::unpack (std::vector<largest_id_type>::const_iterator in,
794795
{
795796
// If we don't have the interior parent element, then it's
796797
// a remote_elem until we get it.
797-
Elem * ip = mesh->query_elem_ptr(interior_parent_id);
798+
Elem * ip =
799+
mesh->interior_mesh().query_elem_ptr(interior_parent_id);
798800
if (!ip )
799801
elem->set_interior_parent
800802
(const_cast<RemoteElem *>(remote_elem));

0 commit comments

Comments
 (0)