https://mooseframework.inl.gov
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 
21 using 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.
29 {
30 public:
32 };
33 
34 TEST(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 
69 TEST(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 
106 TEST(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 
145 TEST(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 
167 TEST(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 
214 TEST(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 
242 TEST(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 
284 TEST(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 
314 TEST(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 }
KOKKOS_INLINE_FUNCTION const T * find(const T &target, const T *const begin, const T *const end)
The following methods are specializations for using the Parallel::packed_range_* routines for a vecto...
Base class for SBM boundary elements.
SBMBndElementBase(const Elem *elem, const Point &normal)
Constructor takes a pointer to a boundary element and its precomputed unit normal.
Derived class for 3-node triangular elements (Tri3) Triangle is listed first so it is initialized bef...
Definition: SBMBndTri3.h:19
Ball computeBoundingBall() const override
Derived class for 2-node edge elements LineSegment is listed first so it is initialized before SBMBnd...
Definition: SBMBndEdge2.h:19
TEST(SBMBndElementTest, Edge2Normal)