Skip to content

Commit 769d7ad

Browse files
committed
Integrate disconnected neighbor detection via PeriodicBoundaries
- Introduced PeriodicBoundaries::disconnected_neighbor() to locate geometrically coincident elements across disconnected boundaries using PointLocator. - Replaced legacy geometry-based neighbor search in UnstructuredMesh::find_neighbors() with unified PeriodicBoundaries-based logic.
1 parent 28f4c7e commit 769d7ad

File tree

6 files changed

+74
-150
lines changed

6 files changed

+74
-150
lines changed

.DS_Store

8 KB
Binary file not shown.

include/base/periodic_boundaries.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ class PeriodicBoundaries : public std::map<boundary_id_type, std::unique_ptr<Per
6868
const PointLocatorBase & point_locator,
6969
const Elem * e,
7070
unsigned int side,
71-
unsigned int * neigh_side = nullptr) const;
71+
unsigned int * neigh_side = nullptr,
72+
bool skip_finding_check = false) const;
7273
};
7374

7475
} // namespace libMesh

include/mesh/mesh_base.h

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@
3636
#include <string>
3737
#include <memory>
3838

39+
// periodic boundary condition support
40+
#include "libmesh/periodic_boundaries.h"
41+
#include "libmesh/periodic_boundary.h"
42+
3943
namespace libMesh
4044
{
4145

@@ -1820,27 +1824,49 @@ class MeshBase : public ParallelObject
18201824
const std::set<subdomain_id_type> & get_mesh_subdomains() const
18211825
{ libmesh_assert(this->is_prepared()); return _mesh_subdomains; }
18221826

1827+
#ifdef LIBMESH_ENABLE_PERIODIC
18231828
/**
18241829
* Register a pair of boundaries as disconnected boundaries.
18251830
*/
1826-
void add_disconnected_boundaries (const boundary_id_type b1,
1827-
const boundary_id_type b2)
1828-
{
1829-
if (b1 == b2)
1830-
_boundary_id_pairs.emplace(b1, b2);
1831-
else
1831+
void add_disconnected_boundaries(const boundary_id_type b1,
1832+
const boundary_id_type b2)
18321833
{
1833-
_boundary_id_pairs.emplace(b1, b2);
1834-
_boundary_id_pairs.emplace(b2, b1);
1834+
// Lazily allocate the container the first time it’s needed
1835+
if (!_disconnected_boundary_pairs)
1836+
_disconnected_boundary_pairs = std::make_unique<PeriodicBoundaries>();
1837+
1838+
// Create forward and inverse boundary mappings
1839+
PeriodicBoundary forward(RealVectorValue(0., 0., 0.));
1840+
PeriodicBoundary inverse(RealVectorValue(0., 0., 0.));
1841+
1842+
forward.myboundary = b1;
1843+
forward.pairedboundary = b2;
1844+
inverse.myboundary = b2;
1845+
inverse.pairedboundary = b1;
1846+
1847+
// Add both directions into the container
1848+
_disconnected_boundary_pairs->emplace(b1, forward.clone());
1849+
_disconnected_boundary_pairs->emplace(b2, inverse.clone());
1850+
}
1851+
1852+
PeriodicBoundaries * get_disconnected_boundaries()
1853+
{
1854+
return _disconnected_boundary_pairs.get();
18351855
}
1836-
}
18371856

1857+
const PeriodicBoundaries * get_disconnected_boundaries() const
1858+
{
1859+
return _disconnected_boundary_pairs.get();
1860+
}
1861+
#endif
18381862

18391863

18401864
protected:
18411865

1842-
/// @brief a map of pairs of boundary ids between which new boundary sides are created
1843-
std::unordered_map<boundary_id_type, boundary_id_type> _boundary_id_pairs;
1866+
#ifdef LIBMESH_ENABLE_PERIODIC
1867+
/// @brief The disconnected boundary id pairs.
1868+
std::unique_ptr<PeriodicBoundaries> _disconnected_boundary_pairs;
1869+
#endif
18441870

18451871
/**
18461872
* This class holds the boundary information. It can store nodes, edges,

src/base/periodic_boundaries.C

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ const Elem * PeriodicBoundaries::neighbor(boundary_id_type boundary_id,
6060
const PointLocatorBase & point_locator,
6161
const Elem * e,
6262
unsigned int side,
63-
unsigned int * neigh_side) const
63+
unsigned int * neigh_side,
64+
bool skip_finding_check) const
6465
{
6566
std::unique_ptr<const Elem> neigh_side_proxy;
6667

@@ -80,6 +81,10 @@ const Elem * PeriodicBoundaries::neighbor(boundary_id_type boundary_id,
8081
const MeshBase & mesh = point_locator.get_mesh();
8182
for(const Elem * elem_it : candidate_elements)
8283
{
84+
85+
if (elem_it == e) // skip self
86+
continue;
87+
8388
std::vector<unsigned int> neigh_sides =
8489
mesh.get_boundary_info().sides_with_boundary_id(elem_it, b->pairedboundary);
8590

@@ -101,18 +106,18 @@ const Elem * PeriodicBoundaries::neighbor(boundary_id_type boundary_id,
101106
}
102107
}
103108

104-
// If we should have found a periodic neighbor but didn't then
105-
// either we're on a ghosted element with a remote periodic neighbor
106-
// or we're on a mesh with an inconsistent periodic boundary.
107-
libmesh_error_msg_if(mesh.is_serial() ||
108-
(e->processor_id() == mesh.processor_id()),
109-
"Periodic boundary neighbor not found");
109+
if (!skip_finding_check)
110+
// If we should have found a periodic neighbor but didn't then
111+
// either we're on a ghosted element with a remote periodic neighbor
112+
// or we're on a mesh with an inconsistent periodic boundary.
113+
libmesh_error_msg_if(mesh.is_serial() ||
114+
(e->processor_id() == mesh.processor_id()),
115+
"Periodic boundary neighbor not found");
110116

111117
if (neigh_side)
112118
*neigh_side = libMesh::invalid_uint;
113119
return remote_elem;
114120
}
115-
116121
} // namespace libMesh
117122

118123

src/geom/elem.C

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -833,41 +833,6 @@ bool Elem::topologically_equal (const Elem & rhs) const
833833

834834

835835

836-
bool Elem::geometrically_equal(const Elem &rhs, const Real tol_in) const
837-
{
838-
// 1. Basic checks: dimension & node count
839-
if (this->dim() != rhs.dim())
840-
return false;
841-
if (this->n_nodes() != rhs.n_nodes())
842-
return false;
843-
844-
// 2. Compare centroids within tolerance
845-
const Real tol = tol_in > 0 ? tol_in : 1e-12;
846-
const Point c1 = this->vertex_average();
847-
const Point c2 = rhs.vertex_average();
848-
if ((c1 - c2).norm() > tol)
849-
return false;
850-
851-
// 3. Unordered comparison within tolerance
852-
const unsigned int n = this->n_nodes();
853-
std::vector<Point> pts1(n), pts2(n);
854-
for (unsigned int i = 0; i < n; ++i)
855-
pts1[i] = this->point(i);
856-
for (unsigned int i = 0; i < n; ++i)
857-
pts2[i] = rhs.point(i);
858-
859-
for (const auto &p1 : pts1)
860-
{
861-
auto found = std::any_of(pts2.begin(), pts2.end(),
862-
[&](const Point &p2)
863-
{ return (p1 - p2).norm() <= tol; });
864-
if (!found)
865-
return false;
866-
}
867-
868-
return true;
869-
}
870-
871836
bool Elem::is_semilocal(const processor_id_type my_pid) const
872837
{
873838
std::set<const Elem *> point_neighbors;

src/mesh/unstructured_mesh.C

Lines changed: 22 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,6 @@
4747
#include <sstream>
4848
#include <unordered_map>
4949

50-
// TIMPI includes
51-
#include "timpi/parallel_implementation.h"
52-
#include "timpi/parallel_sync.h"
5350
namespace {
5451

5552
using namespace libMesh;
@@ -996,111 +993,40 @@ void UnstructuredMesh::find_neighbors (const bool reset_remote_elements,
996993
}
997994
}
998995

999-
{
1000-
// Build or obtain a point locator (sub-locator) and initialize it
1001-
std::unique_ptr<PointLocatorBase> point_locator = this->sub_point_locator();
1002-
1003-
// Get a reference to the boundary info object for convenience
1004-
auto & bi = this->get_boundary_info();
996+
#ifdef LIBMESH_ENABLE_PERIODIC
997+
auto * db = this->get_disconnected_boundaries();
1005998

1006-
// Check whether the element side belongs to a paired boundary
1007-
auto pick_paired_id = [this, &bi](const Elem * e, unsigned int side,
1008-
boundary_id_type &current_id,
1009-
boundary_id_type &paired_id) -> bool
1010-
{
1011-
std::vector<boundary_id_type> ids;
1012-
bi.boundary_ids(e, side, ids);
1013-
for (auto id : ids)
999+
if (db)
10141000
{
1015-
auto it = _boundary_id_pairs.find(id);
1016-
if (it != _boundary_id_pairs.end())
1017-
{
1018-
current_id = id;
1019-
paired_id = it->second;
1020-
return true;
1021-
}
1022-
}
1023-
return false;
1024-
};
1025-
1026-
// Main loop: iterate over all elements and their sides to find disconnected neighbors
1027-
for (const auto & element : this->element_ptr_range())
1028-
{
1029-
for (auto ms : element->side_index_range())
1030-
{
1031-
// Skip if this side already has a valid neighbor (including remote neighbors)
1032-
if (element->neighbor_ptr(ms) != nullptr &&
1033-
element->neighbor_ptr(ms) != remote_elem)
1034-
continue;
1035-
1036-
// Determine whether this side belongs to a paired (disconnected) boundary
1037-
boundary_id_type current_id;
1038-
boundary_id_type paired_id;
1039-
if (!pick_paired_id(element, ms, current_id, paired_id))
1040-
continue; // No valid pair found, skip this side
1041-
1042-
// Build the geometry of this side
1043-
std::unique_ptr<const Elem> this_side;
1044-
element->build_side_ptr(this_side, ms);
1045-
1046-
// Compute centroid and approximate element size
1047-
const Point c_mid = this_side->vertex_average();
1048-
1049-
// Use the point locator to find candidate elements near the side centroid
1050-
std::set<const Elem *> const_candidate_elements;
1051-
point_locator->operator()(c_mid, const_candidate_elements);
1001+
// Obtain a point locator
1002+
std::unique_ptr<PointLocatorBase> point_locator = this->sub_point_locator();
10521003

1053-
std::set<Elem *> candidate_elements;
1054-
for (auto ce : const_candidate_elements)
1055-
candidate_elements.insert(this->elem_ptr(ce->id()));
1004+
// Get the disconnected boundaries object (from periodic BCs)
1005+
auto * db = this->get_disconnected_boundaries();
10561006

1057-
bool paired_found = false; // Flag to mark if a valid match was found
1058-
1059-
// Loop through candidate elements to look for potential paired neighbors
1060-
for (auto * cand_elem : candidate_elements)
1007+
for (const auto & element : this->element_ptr_range())
10611008
{
1062-
if (!cand_elem || cand_elem == element)
1063-
continue;
1064-
1065-
// If paired_id == current_id, it means that there is no paired boundary on the other side element.
1066-
// In this case, we should consider all sides of the candidate element.
1067-
std::vector<unsigned int> cand_neigh_sides =
1068-
(paired_id == current_id) ?
1069-
std::vector<unsigned int>(cand_elem->side_index_range().begin(),
1070-
cand_elem->side_index_range().end()) :
1071-
bi.sides_with_boundary_id(cand_elem, paired_id);
1072-
1073-
if (cand_neigh_sides.empty())
1074-
continue;
1075-
1076-
// Compare each candidate side to the current one
1077-
for (auto ns : cand_neigh_sides)
1009+
for (auto ms : element->side_index_range())
10781010
{
1079-
// Skip if this side already has a valid (non-remote) neighbor
1080-
if (cand_elem->neighbor_ptr(ns) != nullptr &&
1081-
cand_elem->neighbor_ptr(ns) != remote_elem)
1011+
// Skip if this side already has a valid neighbor (including remote neighbors)
1012+
if (element->neighbor_ptr(ms) != nullptr &&
1013+
element->neighbor_ptr(ms) != remote_elem)
10821014
continue;
10831015

1084-
// Build the geometry of the candidate side
1085-
std::unique_ptr<const Elem> cand_side;
1086-
cand_elem->build_side_ptr(cand_side, ns);
1087-
1088-
// Check geometric equality
1089-
if (this_side->geometrically_equal(*cand_side))
1016+
for (const auto & [id, boundary_ptr] : *db)
10901017
{
1091-
element->set_neighbor(ms, cand_elem);
1092-
cand_elem->set_neighbor(ns, element);
1093-
paired_found = true;
1018+
unsigned int neigh_side;
1019+
const Elem * neigh = db->neighbor(id, *point_locator, element, ms, &neigh_side, true /*skip_finding_check*/);
1020+
if (neigh && neigh != remote_elem && neigh != element)
1021+
{
1022+
element->set_neighbor(ms, this->elem_ptr(neigh->id()));
1023+
this->elem_ptr(neigh->id())->set_neighbor(neigh_side, element);
1024+
}
10941025
}
10951026
}
1096-
1097-
if (paired_found)
1098-
break; // Stop searching through other candidates
10991027
}
11001028
}
1101-
}
1102-
}
1103-
1029+
#endif // LIBMESH_ENABLE_PERIODIC
11041030

11051031
#ifdef LIBMESH_ENABLE_AMR
11061032

@@ -1325,6 +1251,7 @@ void UnstructuredMesh::find_neighbors (const bool reset_remote_elements,
13251251

13261252
#endif // AMR
13271253

1254+
13281255
#ifdef DEBUG
13291256
MeshTools::libmesh_assert_valid_neighbors(*this,
13301257
!reset_remote_elements);

0 commit comments

Comments
 (0)