Skip to content

Commit 76ac32d

Browse files
committed
Add IntersectionTools::within_edge_on_side
1 parent 8f7d2f7 commit 76ac32d

File tree

3 files changed

+244
-0
lines changed

3 files changed

+244
-0
lines changed

include/geom/intersection_tools.h

+27
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
namespace libMesh
2626
{
2727

28+
class Elem;
29+
class ElemExtrema;
2830
class Point;
2931

3032
namespace IntersectionTools
@@ -82,6 +84,31 @@ bool collinear(const Point & p1,
8284
const Point & p3,
8385
const Real tol = TOLERANCE);
8486

87+
/**
88+
* \returns True if the given point is contained within an edge
89+
* on the given side of an element
90+
* @param elem The element
91+
* @param p The point
92+
* @param s The side
93+
* @param extrema "Extrema" to be filled with the edge that the point
94+
* is within, if any (must be initially invalid)
95+
* @param linearize Whether or not to "linearize" the check, if this
96+
* is set to false and edges are found to not be collinear, an error
97+
* is thrown
98+
*
99+
* \p extrema will be set to an "at vertex" state if the point is
100+
* both within the edge _and_ at a vertex.
101+
*
102+
* This method is only implemented for three-dimensional, finite
103+
* elements.
104+
*/
105+
bool within_edge_on_side(const Elem & elem,
106+
const Point & p,
107+
const unsigned short s,
108+
ElemExtrema & extrema,
109+
const bool linearize = false,
110+
const Real tol = TOLERANCE);
111+
85112
} // namespace IntersectionTools
86113
} // namespace libMesh
87114

src/geom/intersection_tools.C

+67
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
#include "libmesh/point.h"
2323
#include "libmesh/int_range.h"
24+
#include "libmesh/elem.h"
25+
#include "libmesh/elem_extrema.h"
2426

2527
namespace libMesh::IntersectionTools
2628
{
@@ -76,6 +78,71 @@ bool collinear(const Point & p1,
7678
return true;
7779
}
7880

81+
bool within_edge_on_side(const Elem & elem,
82+
const Point & p,
83+
const unsigned short s,
84+
ElemExtrema & extrema,
85+
const bool linearize,
86+
const Real tol)
87+
{
88+
libmesh_assert_less(s, elem.n_sides());
89+
libmesh_assert(extrema.is_invalid());
90+
libmesh_assert_equal_to(elem.dim(), 3);
91+
92+
// For higher order than linear without linearization, make sure
93+
// that our edges are collinear
94+
if (elem.default_order() > 1 && !linearize)
95+
for (const auto e : elem.edge_index_range())
96+
{
97+
// we should expect 3 edges for our higher order elems
98+
libmesh_assert_equal_to(elem.n_nodes_on_edge(e), 3);
99+
100+
const unsigned int * edge_nodes_map = elem.nodes_on_edge_ptr(e);
101+
if (!collinear(elem.point(edge_nodes_map[0]),
102+
elem.point(edge_nodes_map[1]),
103+
elem.point(edge_nodes_map[2])))
104+
libmesh_error_msg("Failed to use Cell::without_edge_on_side without linearization "
105+
"because an edge was found that is not collinear.");
106+
}
107+
108+
const auto vs = elem.n_vertices_on_side(s);
109+
const unsigned int * side_nodes_map = elem.nodes_on_side_ptr(s);
110+
111+
// side_nodes_map will point to something like [v0, v1, v2, v3]
112+
// With the loop below, we're going to check (in this order):
113+
// [v3 -> v0], [v0 -> v1], [v1 -> v2], [v2 -> v3]
114+
auto last_v = side_nodes_map[vs - 1];
115+
for (const auto side_v : make_range(vs))
116+
{
117+
const auto other_v = side_nodes_map[side_v];
118+
const auto within_result = within_segment(elem.point(last_v), elem.point(other_v), p, tol);
119+
if (within_result == NOT_WITHIN)
120+
{
121+
last_v = side_nodes_map[side_v];
122+
continue;
123+
}
124+
125+
if (within_result == BETWEEN)
126+
{
127+
extrema.set_edge(last_v, other_v);
128+
libmesh_assert(extrema.build_edge(elem)->close_to_point(p, tol));
129+
}
130+
else if (within_result == AT_BEGINNING)
131+
{
132+
extrema.set_vertex(last_v);
133+
libmesh_assert(elem.point(last_v).absolute_fuzzy_equals(p, tol));
134+
}
135+
else
136+
{
137+
extrema.set_vertex(other_v);
138+
libmesh_assert(elem.point(other_v).absolute_fuzzy_equals(p, tol));
139+
}
140+
return true;
141+
}
142+
143+
return false;
144+
}
145+
79146
} // namespace libMesh::IntersectionTools
80147

81148

tests/geom/intersection_tools_test.C

+150
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
#include "libmesh_cppunit.h"
22

3+
#include "test_comm.h"
4+
35
#include <libmesh/intersection_tools.h>
46
#include <libmesh/point.h>
57
#include <libmesh/int_range.h>
8+
#include <libmesh/enum_elem_type.h>
9+
#include <libmesh/mesh.h>
10+
#include <libmesh/mesh_generation.h>
11+
#include <libmesh/elem.h>
12+
#include <libmesh/elem_extrema.h>
613

714
using namespace libMesh;
815

@@ -80,3 +87,146 @@ public:
8087
};
8188

8289
CPPUNIT_TEST_SUITE_REGISTRATION( IntersectionToolsTest );
90+
91+
template <ElemType elem_type>
92+
class MeshedIntersectionToolsTest : public CppUnit::TestCase {
93+
94+
private:
95+
std::unique_ptr<Mesh> _mesh;
96+
97+
protected:
98+
std::string libmesh_suite_name;
99+
100+
public:
101+
void setUp()
102+
{
103+
const Real minpos = 1.5, maxpos = 4.86;
104+
const unsigned int N = 3;
105+
106+
_mesh = std::make_unique<Mesh>(*TestCommWorld);
107+
auto test_elem = Elem::build(elem_type);
108+
109+
const unsigned int dim = test_elem->dim();
110+
const unsigned int use_y = dim > 1;
111+
const unsigned int use_z = dim > 2;
112+
113+
MeshTools::Generation::build_cube (*_mesh,
114+
N, N*use_y, N*use_z,
115+
minpos, maxpos,
116+
minpos, use_y*maxpos,
117+
minpos, use_z*maxpos,
118+
elem_type);
119+
}
120+
121+
void test_within_edge_on_side()
122+
{
123+
LOG_UNIT_TEST;
124+
125+
if (_mesh->mesh_dimension() != 3)
126+
return;
127+
128+
// check locations at every node
129+
for (const auto & elem : _mesh->active_local_element_ptr_range())
130+
for (const auto s : elem->side_index_range())
131+
for (const auto e : elem->edge_index_range())
132+
for (const auto n : elem->nodes_on_edge(e))
133+
{
134+
ElemExtrema extrema;
135+
const auto within = IntersectionTools::within_edge_on_side(*elem,
136+
elem->point(n),
137+
s,
138+
extrema);
139+
140+
CPPUNIT_ASSERT_EQUAL(within, elem->is_node_on_side(n, s));
141+
if (elem->is_node_on_side(n, s))
142+
{
143+
CPPUNIT_ASSERT_EQUAL(elem->is_vertex(n), extrema.at_vertex(n));
144+
CPPUNIT_ASSERT_EQUAL(elem->is_vertex(n), !extrema.at_edge(*elem, e));
145+
}
146+
}
147+
148+
// cut edges into segments
149+
for (const auto & elem : _mesh->active_local_element_ptr_range())
150+
for (const auto e : elem->edge_index_range())
151+
for (const auto s : elem->side_index_range())
152+
if (elem->is_edge_on_side(e, s))
153+
{
154+
const auto nodes_on_edge = elem->nodes_on_edge(e);
155+
const auto & p1 = elem->point(nodes_on_edge[0]);
156+
const auto & p2 = elem->point(nodes_on_edge[1]);
157+
const auto length_vec = p2 - p1;
158+
const auto length = length_vec.norm();
159+
const auto p1_to_p2 = length_vec / length;
160+
161+
int segments = 5;
162+
Real dx = (Real)1 / segments * length;
163+
for (const auto i : make_range(-1, segments + 1))
164+
{
165+
const auto p = p1 + Real(i) * dx * p1_to_p2;
166+
ElemExtrema extrema;
167+
const auto within = IntersectionTools::within_edge_on_side(*elem,
168+
p,
169+
s,
170+
extrema);
171+
172+
CPPUNIT_ASSERT_EQUAL(within, i >= 0 && i <= segments);
173+
CPPUNIT_ASSERT_EQUAL(extrema.at_vertex(nodes_on_edge[0]), i == 0);
174+
CPPUNIT_ASSERT_EQUAL(extrema.at_vertex(nodes_on_edge[1]), i == segments);
175+
CPPUNIT_ASSERT_EQUAL(extrema.at_edge(*elem, e), i > 0 && i < segments);
176+
}
177+
}
178+
179+
// check elem centroids
180+
for (const auto & elem : _mesh->active_local_element_ptr_range())
181+
for (const auto s : elem->side_index_range())
182+
{
183+
ElemExtrema extrema;
184+
CPPUNIT_ASSERT(!IntersectionTools::within_edge_on_side(*elem,
185+
elem->vertex_average(),
186+
s,
187+
extrema));
188+
}
189+
}
190+
191+
};
192+
193+
#define MESHEDINTERSECTIONTOOLSTEST \
194+
CPPUNIT_TEST( test_within_edge_on_side );
195+
196+
#define INSTANTIATE_MESHEDINTERSECTIONTOOLSTEST(elemtype) \
197+
class MeshedIntersectionToolsTest_##elemtype : public MeshedIntersectionToolsTest<elemtype> { \
198+
public: \
199+
MeshedIntersectionToolsTest_##elemtype() : \
200+
MeshedIntersectionToolsTest<elemtype>() { \
201+
if (unitlog->summarized_logs_enabled()) \
202+
this->libmesh_suite_name = "MeshedIntersectionToolsTest"; \
203+
else \
204+
this->libmesh_suite_name = "MeshedIntersectionToolsTest_" #elemtype; \
205+
} \
206+
CPPUNIT_TEST_SUITE( MeshedIntersectionToolsTest_##elemtype ); \
207+
MESHEDINTERSECTIONTOOLSTEST; \
208+
CPPUNIT_TEST_SUITE_END(); \
209+
}; \
210+
\
211+
CPPUNIT_TEST_SUITE_REGISTRATION( MeshedIntersectionToolsTest_##elemtype )
212+
213+
214+
#if LIBMESH_DIM > 2
215+
INSTANTIATE_MESHEDINTERSECTIONTOOLSTEST(TET4);
216+
INSTANTIATE_MESHEDINTERSECTIONTOOLSTEST(TET10);
217+
INSTANTIATE_MESHEDINTERSECTIONTOOLSTEST(TET14);
218+
219+
INSTANTIATE_MESHEDINTERSECTIONTOOLSTEST(HEX8);
220+
INSTANTIATE_MESHEDINTERSECTIONTOOLSTEST(HEX20);
221+
INSTANTIATE_MESHEDINTERSECTIONTOOLSTEST(HEX27);
222+
223+
INSTANTIATE_MESHEDINTERSECTIONTOOLSTEST(PRISM6);
224+
INSTANTIATE_MESHEDINTERSECTIONTOOLSTEST(PRISM15);
225+
INSTANTIATE_MESHEDINTERSECTIONTOOLSTEST(PRISM18);
226+
INSTANTIATE_MESHEDINTERSECTIONTOOLSTEST(PRISM20);
227+
INSTANTIATE_MESHEDINTERSECTIONTOOLSTEST(PRISM21);
228+
229+
INSTANTIATE_MESHEDINTERSECTIONTOOLSTEST(PYRAMID5);
230+
INSTANTIATE_MESHEDINTERSECTIONTOOLSTEST(PYRAMID13);
231+
INSTANTIATE_MESHEDINTERSECTIONTOOLSTEST(PYRAMID14);
232+
#endif // LIBMESH_DIM > 2

0 commit comments

Comments
 (0)