https://mooseframework.inl.gov
Loading...
Searching...
No Matches
SBMBndElementTest.C
Go to the documentation of this file.
1//* This file is part of the MOOSE framework
2//* https://mooseframework.inl.gov
3//*
4//* All rights reserved, see COPYRIGHT for full restrictions
5//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6//*
7//* Licensed under LGPL 2.1, please see LICENSE for details
8//* https://www.gnu.org/licenses/lgpl-2.1.html
9
10#include "gtest/gtest.h"
11#include "SBMBndEdge2.h"
12#include "SBMBndTri3.h"
13#include "MooseMesh.h"
14#include "libmesh/face_tri3.h"
15#include "libmesh/edge_edge2.h"
16#include "libmesh/cell_tet4.h"
17#include "SBMUtils.h"
18#include "LineSegment.h"
19#include "Ball.h"
20
21using namespace libMesh;
22
23// Minimal SBMBndElementBase subclass that is neither LineSegment nor Triangle.
24// Used to drive the unsupported-geometry mooseError branches in
25// SBMBndElementBase::intersect and ::computeBoundingBall. The dispatchers
26// under test short-circuit before touching the normal, so the supplied
27// placeholder normal is never inspected.
33
34TEST(SBMBndElementTest, Edge2Normal)
35{
36 std::unique_ptr<Edge2> edge(new Edge2());
37
38 std::unique_ptr<Node> n0(new Node(Point(0.0, 0.0, 0.0), 0));
39 std::unique_ptr<Node> n1(new Node(Point(1.0, 0.0, 0.0), 1));
40
41 edge->set_node(0) = n0.get();
42 edge->set_node(1) = n1.get();
43
44 SBMBndEdge2 bnd_edge(edge.get());
45 Point n = bnd_edge.normal();
46
47 EXPECT_NEAR(n(0), 0.0, 1e-12);
48 EXPECT_NEAR(n(2), 0.0, 1e-12);
49 EXPECT_NEAR(std::abs(n(1)), 1.0, 1e-12);
50
51 // Line crossing through (0.5, -1) to (0.5, 1)
52 Point a(0.5, -1.0, 0.0);
53 Point b(0.5, 1.0, 0.0);
54 LineSegment line_segment_ab(a, b);
55 EXPECT_TRUE(bnd_edge.intersect(line_segment_ab));
56
57 // Line parallel, no intercept
58 Point c(0.0, 1.0, 0.0);
59 Point d(1.0, 1.0, 0.0);
60 LineSegment line_segment_cd(c, d);
61 EXPECT_FALSE(bnd_edge.intersect(line_segment_cd));
62
63 // Check distance vector
64 Point pt(0.5, 1.0, 0.0);
65 Point dist = bnd_edge.distanceFrom(pt);
66 EXPECT_NEAR(dist(1), -1.0, 1e-12);
67}
68
69TEST(SBMBndElementTest, Tri3Normal)
70{
71 std::unique_ptr<Tri3> tri(new Tri3());
72
73 std::unique_ptr<Node> n0(new Node(Point(0.0, 0.0, 0.0), 0));
74 std::unique_ptr<Node> n1(new Node(Point(1.0, 0.0, 0.0), 1));
75 std::unique_ptr<Node> n2(new Node(Point(0.0, 1.0, 0.0), 2));
76
77 tri->set_node(0) = n0.get();
78 tri->set_node(1) = n1.get();
79 tri->set_node(2) = n2.get();
80
81 SBMBndTri3 bnd_tri(tri.get());
82 Point n = bnd_tri.normal();
83
84 EXPECT_NEAR(n(0), 0.0, 1e-12);
85 EXPECT_NEAR(n(1), 0.0, 1e-12);
86 EXPECT_NEAR(std::abs(n(2)), 1.0, 1e-12);
87
88 // Line from below passing through triangle center
89 Point a(0.3, 0.3, -1.0);
90 Point b(0.3, 0.3, 1.0);
91 LineSegment line_segment_ab(a, b);
92 EXPECT_TRUE(bnd_tri.intersect(line_segment_ab));
93
94 // Line away from triangle
95 Point c(2.0, 2.0, -1.0);
96 Point d(2.0, 2.0, 1.0);
97 LineSegment line_segment_cd(c, d);
98 EXPECT_FALSE(bnd_tri.intersect(line_segment_cd));
99
100 // Check distance vector roughly in +Z
101 Point pt(0.3, 0.3, 1.0);
102 Point dist = bnd_tri.distanceFrom(pt);
103 EXPECT_NEAR(dist(2), -1.0, 1e-12);
104}
105
106TEST(SBMBndElementTest, Edge2NormalTilted)
107{
108 std::unique_ptr<Edge2> edge(new Edge2());
109
110 std::unique_ptr<Node> n0(new Node(Point(0.0, 0.0, 0.0), 0));
111 std::unique_ptr<Node> n1(new Node(Point(1.0, 1.0, 0.0), 1));
112
113 edge->set_node(0) = n0.get();
114 edge->set_node(1) = n1.get();
115
116 SBMBndEdge2 bnd_edge(edge.get());
117 Point n = bnd_edge.normal();
118
119 // Expected normal
120 const double inv_sqrt2 = 1.0 / std::sqrt(2.0);
121 EXPECT_NEAR(n(0), -inv_sqrt2, 1e-12);
122 EXPECT_NEAR(n(1), inv_sqrt2, 1e-12);
123 EXPECT_NEAR(n(2), 0.0, 1e-12);
124
125 // Line crossing
126 Point a(0.5, 0.0, 0.0);
127 Point b(0.5, 1.0, 0.0);
128 LineSegment line_segment_ab(a, b);
129 EXPECT_TRUE(bnd_edge.intersect(line_segment_ab));
130
131 // Line outside
132 Point c(1.5, 1.0, 0.0);
133 Point d(1.5, 2.0, 0.0);
134 LineSegment line_segment_cd(c, d);
135 EXPECT_FALSE(bnd_edge.intersect(line_segment_cd));
136
137 // Check distance vector direction
138 Point pt(0.5, 0.0, 0.0); // Point below edge
139 Point dist = bnd_edge.distanceFrom(pt);
140 // Should be mostly pointing along normal
141 double dot = dist(0) * n(0) + dist(1) * n(1);
142 EXPECT_GT(dot, 0.0); // Same direction
143}
144
145TEST(SBMBndElementTest, Edge2DistanceNodeFallback)
146{
147 // Edge from (0,0,0) to (1,0,0); query point lies far off the line axis so
148 // its projection onto the edge falls outside the segment, forcing the side
149 // loop in distanceFrom() to take the NODEELEM switch case.
150 std::unique_ptr<Edge2> edge(new Edge2());
151 std::unique_ptr<Node> n0(new Node(Point(0.0, 0.0, 0.0), 0));
152 std::unique_ptr<Node> n1(new Node(Point(1.0, 0.0, 0.0), 1));
153 edge->set_node(0) = n0.get();
154 edge->set_node(1) = n1.get();
155
156 SBMBndEdge2 bnd_edge(edge.get());
157
158 // Projection of (2, 1, 0) onto the line is (2, 0, 0) which is outside [0,1].
159 // Nearest entity is node (1, 0, 0).
160 Point pt(2.0, 1.0, 0.0);
161 Point dist = bnd_edge.distanceFrom(pt);
162 EXPECT_NEAR(dist(0), -1.0, 1e-12);
163 EXPECT_NEAR(dist(1), -1.0, 1e-12);
164 EXPECT_NEAR(dist(2), 0.0, 1e-12);
165}
166
167TEST(SBMBndElementTest, Tri3NormalTilted)
168{
169 std::unique_ptr<Tri3> tri(new Tri3());
170
171 std::unique_ptr<Node> n0(new Node(Point(0.0, 0.0, 0.0), 0));
172 std::unique_ptr<Node> n1(new Node(Point(1.0, 0.0, 1.0), 1));
173 std::unique_ptr<Node> n2(new Node(Point(0.0, 1.0, 1.0), 2));
174
175 tri->set_node(0) = n0.get();
176 tri->set_node(1) = n1.get();
177 tri->set_node(2) = n2.get();
178
179 SBMBndTri3 bnd_tri(tri.get());
180 Point n = bnd_tri.normal();
181
182 // Expected normal
183 const double inv_sqrt3 = 1.0 / std::sqrt(3.0);
184 EXPECT_NEAR(n(0), -inv_sqrt3, 1e-12);
185 EXPECT_NEAR(n(1), -inv_sqrt3, 1e-12);
186 EXPECT_NEAR(n(2), inv_sqrt3, 1e-12);
187
188 // Line from below passing through triangle center
189 Point a(0.3, 0.3, -1.0);
190 Point b(0.3, 0.3, 2.0);
191 LineSegment line_segment_ab(a, b);
192 EXPECT_TRUE(bnd_tri.intersect(line_segment_ab));
193
194 // Line away from triangle
195 Point c(2.0, 2.0, -1.0);
196 Point d(2.0, 2.0, 2.0);
197 LineSegment line_segment_cd(c, d);
198 EXPECT_FALSE(bnd_tri.intersect(line_segment_cd));
199
200 // Check distance vector
201 Point pt(2.0, 0.0, 2.0);
202 Point dist = bnd_tri.distanceFrom(pt);
203 EXPECT_NEAR(dist(0), -1.0, 1e-12);
204 EXPECT_NEAR(dist(1), 0.0, 1e-12);
205 EXPECT_NEAR(dist(2), -1.0, 1e-12);
206
207 Point pt2(0.0, 0.0, 0.0);
208 Point dist2 = bnd_tri.distanceFrom(pt2);
209 EXPECT_NEAR(dist2(0), 0.0, 1e-12);
210 EXPECT_NEAR(dist2(1), 0.0, 1e-12);
211 EXPECT_NEAR(dist2(2), 0.0, 1e-12);
212}
213
214TEST(SBMBndElementTest, ProjectedBoundingBoxDiagonal)
215{
216 // Tri3 spanning [0,1] x [0,1] in the z=0 plane. Bounding-box diagonal is
217 // (1,1,0); projected onto a plane orthogonal to z (i.e. removing the
218 // z-component, which is already zero) the tangential diagonal has norm
219 // sqrt(2). Projecting onto a plane orthogonal to the diagonal direction
220 // itself zeroes out the projection.
221 std::unique_ptr<Tri3> tri(new Tri3());
222 std::unique_ptr<Node> n0(new Node(Point(0.0, 0.0, 0.0), 0));
223 std::unique_ptr<Node> n1(new Node(Point(1.0, 0.0, 0.0), 1));
224 std::unique_ptr<Node> n2(new Node(Point(0.0, 1.0, 0.0), 2));
225 tri->set_node(0) = n0.get();
226 tri->set_node(1) = n1.get();
227 tri->set_node(2) = n2.get();
228
229 SBMBndTri3 bnd_tri(tri.get());
230
231 EXPECT_NEAR(bnd_tri.getProjectedBoundingBoxDiagonal(Point(0.0, 0.0, 1.0) /*normal_dir*/),
232 std::sqrt(2.0),
233 1e-12);
234
235 const double inv_sqrt2 = 1.0 / std::sqrt(2.0);
236 EXPECT_NEAR(
237 bnd_tri.getProjectedBoundingBoxDiagonal(Point(inv_sqrt2, inv_sqrt2, 0.0) /*normal_dir*/),
238 0.0,
239 1e-12);
240}
241
242TEST(SBMBndElementTest, BaseDynamicDispatcherIntersectAndBoundingBall)
243{
244 // Exercise SBMBndElementBase::intersect / computeBoundingBall through a
245 // base-class reference; the `using` declarations in the derived classes
246 // otherwise route direct calls to LineSegment/Triangle and bypass these
247 // dispatchers.
248 std::unique_ptr<Edge2> edge(new Edge2());
249 std::unique_ptr<Node> e0(new Node(Point(0.0, 0.0, 0.0), 0));
250 std::unique_ptr<Node> e1(new Node(Point(1.0, 0.0, 0.0), 1));
251 edge->set_node(0) = e0.get();
252 edge->set_node(1) = e1.get();
253 SBMBndEdge2 bnd_edge(edge.get());
254 const SBMBndElementBase & edge_base = bnd_edge;
255
256 LineSegment crossing(Point(0.5, -1.0, 0.0), Point(0.5, 1.0, 0.0));
257 LineSegment parallel(Point(0.0, 1.0, 0.0), Point(1.0, 1.0, 0.0));
258 EXPECT_TRUE(edge_base.intersect(crossing));
259 EXPECT_FALSE(edge_base.intersect(parallel));
260
261 const Ball edge_ball = edge_base.computeBoundingBall();
262 EXPECT_NEAR(edge_ball.center()(0), 0.5, 1e-12);
263 EXPECT_NEAR(edge_ball.radius(), 0.5, 1e-12);
264
265 std::unique_ptr<Tri3> tri(new Tri3());
266 std::unique_ptr<Node> t0(new Node(Point(0.0, 0.0, 0.0), 0));
267 std::unique_ptr<Node> t1(new Node(Point(1.0, 0.0, 0.0), 1));
268 std::unique_ptr<Node> t2(new Node(Point(0.0, 1.0, 0.0), 2));
269 tri->set_node(0) = t0.get();
270 tri->set_node(1) = t1.get();
271 tri->set_node(2) = t2.get();
272 SBMBndTri3 bnd_tri(tri.get());
273 const SBMBndElementBase & tri_base = bnd_tri;
274
275 LineSegment piercing(Point(0.3, 0.3, -1.0), Point(0.3, 0.3, 1.0));
276 LineSegment missing(Point(2.0, 2.0, -1.0), Point(2.0, 2.0, 1.0));
277 EXPECT_TRUE(tri_base.intersect(piercing));
278 EXPECT_FALSE(tri_base.intersect(missing));
279
280 const Ball tri_ball = tri_base.computeBoundingBall();
281 EXPECT_GT(tri_ball.radius(), 0.0);
282}
283
284TEST(SBMBndElementTest, ConstructionRejectsUnsupportedSideType)
285{
286 // SBMBndElementBase validates side(0)->type() in its ctor so misconfigured
287 // SBMBnd* subclasses fail to construct rather than producing wrong answers
288 // later. Tet4 has Tri3 sides (neither EDGE2 nor NODEELEM) and must throw.
289 std::unique_ptr<Tet4> tet(new Tet4());
290 std::unique_ptr<Node> n0(new Node(Point(0.0, 0.0, 0.0), 0));
291 std::unique_ptr<Node> n1(new Node(Point(1.0, 0.0, 0.0), 1));
292 std::unique_ptr<Node> n2(new Node(Point(0.0, 1.0, 0.0), 2));
293 std::unique_ptr<Node> n3(new Node(Point(0.0, 0.0, 1.0), 3));
294 tet->set_node(0) = n0.get();
295 tet->set_node(1) = n1.get();
296 tet->set_node(2) = n2.get();
297 tet->set_node(3) = n3.get();
298
299 EXPECT_THROW(
300 {
301 try
302 {
303 SBMBndUnsupportedForTest bnd(tet.get(), Point(0.0, 0.0, 1.0));
304 }
305 catch (const std::exception & e)
306 {
307 EXPECT_NE(std::string(e.what()).find("unsupported side type"), std::string::npos);
308 throw;
309 }
310 },
311 std::exception);
312}
313
314TEST(SBMBndElementTest, UnsupportedGeometryDispatchersThrow)
315{
316 // Drive a SBMBndElementBase subclass that is neither LineSegment nor
317 // Triangle through the base-class dispatchers; both intersect() and
318 // computeBoundingBall() must mooseError on the unsupported geometry type.
319 std::unique_ptr<Edge2> edge(new Edge2());
320 std::unique_ptr<Node> n0(new Node(Point(0.0, 0.0, 0.0), 0));
321 std::unique_ptr<Node> n1(new Node(Point(1.0, 0.0, 0.0), 1));
322 edge->set_node(0) = n0.get();
323 edge->set_node(1) = n1.get();
324
325 // Placeholder unit normal; the dispatchers under test never consult it.
326 SBMBndUnsupportedForTest bnd(edge.get(), Point(0.0, 0.0, 1.0));
327 const SBMBndElementBase & base = bnd;
328
329 LineSegment line(Point(0.5, -1.0, 0.0), Point(0.5, 1.0, 0.0));
330 EXPECT_THROW(
331 {
332 try
333 {
334 base.intersect(line);
335 }
336 catch (const std::exception & e)
337 {
338 EXPECT_NE(std::string(e.what()).find("unsupported geometry type"), std::string::npos);
339 throw;
340 }
341 },
342 std::exception);
343
344 EXPECT_THROW(
345 {
346 try
347 {
348 base.computeBoundingBall();
349 }
350 catch (const std::exception & e)
351 {
352 EXPECT_NE(std::string(e.what()).find("unsupported geometry type"), std::string::npos);
353 throw;
354 }
355 },
356 std::exception);
357}
TEST(SBMBndElementTest, Edge2Normal)
const libMesh::Point & center() const
libMesh::Real radius() const
Derived class for 2-node edge elements LineSegment is listed first so it is initialized before SBMBnd...
Definition SBMBndEdge2.h:20
bool intersect(const libMesh::Plane &pl, Point &intersect_p) const
const Point & normal() const
Getter for the normal vector.
Base class for SBM boundary elements.
Ball computeBoundingBall() const
Compute a bounding ball for this boundary element.
Real getProjectedBoundingBoxDiagonal(const Point &normal_dir) const
Compute the length of the element bounding-box diagonal projected onto a plane orthogonal to a given ...
virtual Point distanceFrom(const Point &pt) const
Compute the distance vector from an arbitrary point to this boundary element.
SBMBndElementBase(const Elem *elem, const Point &normal)
Constructor takes a pointer to a boundary element and its precomputed unit normal.
bool intersect(const LineSegment &line_segment) const
Check if the given line segment intersects this boundary element.
Derived class for 3-node triangular elements (Tri3) Triangle is listed first so it is initialized bef...
Definition SBMBndTri3.h:20
const Point & normal() const
Getter for the normal vector.
bool intersect(const LineSegment &l, libMesh::Point &intersect_p) const
The following methods are specializations for using the Parallel::packed_range_* routines for a vecto...