chombo-discharge
Loading...
Searching...
No Matches
CD_PolyhedralEBUtilsImplem.H
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2021-2026 SINTEF Energy Research
3 *
4 * SPDX-License-Identifier: GPL-3.0-or-later
5 */
6
13#ifndef CD_POLYHEDRALEBUTILSIMPLEM_H
14#define CD_POLYHEDRALEBUTILSIMPLEM_H
15
16// Std includes
17#include <cmath>
18
19// Chombo includes
20#include <CH_assert.H>
21
22// Our includes
24#include <CD_NamespaceHeader.H>
25
26namespace PolyhedralEB {
27
28inline bool
29isFluid(const Real a_value) noexcept
30{
31 return a_value < 0.0;
32}
33
34namespace detail {
35
36inline int
37edgeDirection(const int a_edge) noexcept
38{
39 CH_assert(a_edge >= 0 && a_edge < CutCellSurface::s_numEdges);
40
41 return a_edge / (CutCellSurface::s_numEdges / SpaceDim);
42}
43
44inline void
45edgeOrigin(const int a_edge, int a_offset[SpaceDim]) noexcept
46{
47 CH_assert(a_edge >= 0 && a_edge < CutCellSurface::s_numEdges);
48
49 const int dir = edgeDirection(a_edge);
50 const int local = a_edge % (CutCellSurface::s_numEdges / SpaceDim);
51
52 for (int d = 0; d < SpaceDim; d++) {
53 a_offset[d] = 0;
54 }
55
56#if CH_SPACEDIM == 3
57 a_offset[s_transverse[dir][0]] = local & 1;
58 a_offset[s_transverse[dir][1]] = (local >> 1) & 1;
59#else
60 a_offset[1 - dir] = local & 1;
61#endif
62
63 a_offset[dir] = 0;
64}
65
66inline void
67edgeCorners(const int a_edge, int& a_lo, int& a_hi) noexcept
68{
69 CH_assert(a_edge >= 0 && a_edge < CutCellSurface::s_numEdges);
70
71 const int dir = edgeDirection(a_edge);
72
73 int offset[SpaceDim];
74 edgeOrigin(a_edge, offset);
75
76 a_lo = 0;
77
78 for (int d = 0; d < SpaceDim; d++) {
79 a_lo |= offset[d] << d;
80 }
81
82 a_hi = a_lo | (1 << dir);
83}
84
85inline RealVect
86cornerPosition(const int a_corner) noexcept
87{
88 CH_assert(a_corner >= 0 && a_corner < CutCellSurface::s_numCorners);
89
90 RealVect x = RealVect::Zero;
91
92 for (int d = 0; d < SpaceDim; d++) {
93 x[d] = -0.5 + static_cast<Real>((a_corner >> d) & 1);
94 }
95
96 return x;
97}
98
99inline void
100faceCorners(const int a_dir, const int a_side, int a_corner[1 << (SpaceDim - 1)]) noexcept
101{
102 CH_assert(a_dir >= 0 && a_dir < SpaceDim);
103 CH_assert(a_side == 0 || a_side == 1);
104
105#if CH_SPACEDIM == 3
106 const int t0 = s_transverse[a_dir][0];
107 const int t1 = s_transverse[a_dir][1];
108
109 const int ring[4][2] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
110
111 for (int i = 0; i < 4; i++) {
112 a_corner[i] = (a_side << a_dir) | (ring[i][0] << t0) | (ring[i][1] << t1);
113 }
114#else
115 const int t = 1 - a_dir;
116
117 for (int i = 0; i < 2; i++) {
118 a_corner[i] = (a_side << a_dir) | (i << t);
119 }
120#endif
121}
122
123inline int
124edgeIndex(const int a_dir, const int a_offset[SpaceDim]) noexcept
125{
126 CH_assert(a_dir >= 0 && a_dir < SpaceDim);
127
128 for (int d = 0; d < SpaceDim; d++) {
129 CH_assert(a_offset[d] == 0 || a_offset[d] == 1);
130 }
131
132#if CH_SPACEDIM == 3
133 return 4 * a_dir + ((a_offset[s_transverse[a_dir][0]] & 1) | ((a_offset[s_transverse[a_dir][1]] & 1) << 1));
134#else
135 return 2 * a_dir + (a_offset[1 - a_dir] & 1);
136#endif
137}
138
139inline RealVect
140crossingPosition(const CutCellSurface& a_surface, const int a_edge, const Real a_tolerance) noexcept
141{
142 CH_assert(a_edge >= 0 && a_edge < CutCellSurface::s_numEdges);
143 CH_assert(a_surface.hasCrossing(a_edge));
144 CH_assert(a_tolerance >= 0.0 && a_tolerance < 0.5);
145
146 const int dir = edgeDirection(a_edge);
147
148 int offset[SpaceDim];
149 edgeOrigin(a_edge, offset);
150
151 Real t = a_surface.m_crossing[a_edge];
152
153 // A crossing recorded exactly at an endpoint sits on a corner the interface passes through,
154 // so it is already where it belongs and displacing it would open a sliver of the
155 // displacement's own width. Every other crossing is held off the endpoints, which is what
156 // keeps the combinatorics generic.
157 if (t != 0.0 && t != 1.0) {
158 t = std::max(t, a_tolerance);
159 t = std::min(t, 1.0 - a_tolerance);
160 }
161
162 RealVect x = RealVect::Zero;
163
164 for (int d = 0; d < SpaceDim; d++) {
165 x[d] = -0.5 + static_cast<Real>(offset[d]);
166 }
167
168 x[dir] = -0.5 + t;
169
170 return x;
171}
172
173#if CH_SPACEDIM == 3
174inline void
175faceEdges(const int a_dir, const int a_side, int a_edge[4]) noexcept
176{
177 CH_assert(a_dir >= 0 && a_dir < SpaceDim);
178 CH_assert(a_side == 0 || a_side == 1);
179
180 const int t0 = s_transverse[a_dir][0];
181 const int t1 = s_transverse[a_dir][1];
182
183 // each entry is the t0 and t1 offset of the edge's low corner, and the direction it runs
184 const int spec[4][3] = {{0, 0, t0}, {1, 0, t1}, {1, 1, t0}, {0, 1, t1}};
185
186 for (int i = 0; i < 4; i++) {
187 int offset[SpaceDim];
188
189 for (int d = 0; d < SpaceDim; d++) {
190 offset[d] = 0;
191 }
192
193 offset[a_dir] = a_side;
194 offset[t0] = spec[i][0];
195 offset[t1] = spec[i][1];
196
197 const int run = spec[i][2];
198
199 offset[run] = 0;
200
201 a_edge[i] = edgeIndex(run, offset);
202 }
203}
204
205inline void
206polygonMoments(const RealVect* a_vertex,
207 const int a_num,
208 Real& a_area,
209 RealVect& a_vector,
210 RealVect& a_centroid) noexcept
211{
212 CH_assert(a_num >= 0);
213 CH_assert(a_num == 0 || a_vertex != nullptr);
214
215 a_area = 0.0;
216 a_vector = RealVect::Zero;
217 a_centroid = RealVect::Zero;
218
219 if (a_num < 3) {
220 return;
221 }
222
223 for (int i = 1; i < a_num - 1; i++) {
224 const RealVect u = a_vertex[i] - a_vertex[0];
225 const RealVect v = a_vertex[i + 1] - a_vertex[0];
226
227 a_vector += 0.5 * RealVect(u[1] * v[2] - u[2] * v[1], u[2] * v[0] - u[0] * v[2], u[0] * v[1] - u[1] * v[0]);
228 }
229
230 a_area = a_vector.vectorLength();
231
232 if (a_area <= 0.0) {
233 return;
234 }
235
236 const RealVect unit = a_vector / a_area;
237
238 for (int i = 1; i < a_num - 1; i++) {
239 const RealVect u = a_vertex[i] - a_vertex[0];
240 const RealVect v = a_vertex[i + 1] - a_vertex[0];
241
242 const RealVect n = RealVect(u[1] * v[2] - u[2] * v[1], u[2] * v[0] - u[0] * v[2], u[0] * v[1] - u[1] * v[0]);
243
244 const Real w = 0.5 * n.dotProduct(unit);
245
246 a_centroid += w * (a_vertex[0] + a_vertex[i] + a_vertex[i + 1]) / 3.0;
247 }
248
249 a_centroid /= a_area;
250}
251
252inline int
253facePairs(const int a_dir, const int a_side, const CutCellSurface& a_surface, int a_pair[2][2]) noexcept
254{
255 CH_assert(a_dir >= 0 && a_dir < SpaceDim);
256 CH_assert(a_side == 0 || a_side == 1);
257
258 int faceEdge[4];
259 int faceCorner[4];
260
261 faceEdges(a_dir, a_side, faceEdge);
262 faceCorners(a_dir, a_side, faceCorner);
263
264 int hit[4];
265 int numHit = 0;
266
267 for (int i = 0; i < 4; i++) {
268 if (a_surface.hasCrossing(faceEdge[i])) {
269 hit[numHit++] = i;
270 }
271 }
272
273 if (numHit == 0) {
274 return 0;
275 }
276
277 if (numHit == 2) {
278 a_pair[0][0] = faceEdge[hit[0]];
279 a_pair[0][1] = faceEdge[hit[1]];
280
281 return 1;
282 }
283
284 if (numHit != 4) {
285 return -1;
286 }
287
288 const Real f00 = a_surface.m_corner[faceCorner[0]];
289 const Real f10 = a_surface.m_corner[faceCorner[1]];
290 const Real f11 = a_surface.m_corner[faceCorner[2]];
291 const Real f01 = a_surface.m_corner[faceCorner[3]];
292
293 const Real den = f00 + f11 - f10 - f01;
294 const Real saddle = (std::abs(den) > 0.0) ? (f00 * f11 - f10 * f01) / den : (f00 + f11);
295
296 if (isFluid(saddle) == isFluid(f00)) {
297 a_pair[0][0] = faceEdge[0]; // chords cut off face corners 1 and 3
298 a_pair[0][1] = faceEdge[1];
299 a_pair[1][0] = faceEdge[2];
300 a_pair[1][1] = faceEdge[3];
301 }
302 else {
303 a_pair[0][0] = faceEdge[3]; // chords cut off face corners 0 and 2
304 a_pair[0][1] = faceEdge[0];
305 a_pair[1][0] = faceEdge[1];
306 a_pair[1][1] = faceEdge[2];
307 }
308
309 return 2;
310}
311
312inline int
314 int a_loop[CutCellSurface::s_numEdges],
315 int a_start[CutCellSurface::s_numEdges + 1]) noexcept
316{
317 constexpr int numEdges = CutCellSurface::s_numEdges;
318
319 int adjacent[numEdges][2];
320 int degree[numEdges] = {0};
321
322 for (int d = 0; d < SpaceDim; d++) {
323 for (int side = 0; side < 2; side++) {
324 int pair[2][2];
325 const int numPairs = facePairs(d, side, a_surface, pair);
326
327 if (numPairs < 0) {
328 return -1;
329 }
330
331 for (int k = 0; k < numPairs; k++) {
332 const int x = pair[k][0];
333 const int y = pair[k][1];
334
335 if (degree[x] > 1 || degree[y] > 1) {
336 return -1;
337 }
338
339 adjacent[x][degree[x]++] = y;
340 adjacent[y][degree[y]++] = x;
341 }
342 }
343 }
344
345 int numCrossings = 0;
346
347 for (int e = 0; e < numEdges; e++) {
348 if (a_surface.hasCrossing(e)) {
349 numCrossings++;
350
351 if (degree[e] != 2) {
352 return -1;
353 }
354 }
355 }
356
357 if (numCrossings < 3) {
358 return -1;
359 }
360
361 bool used[numEdges] = {false};
362 int numLoops = 0;
363 int put = 0;
364
365 a_start[0] = 0;
366
367 for (int e = 0; e < numEdges; e++) {
368 if (!a_surface.hasCrossing(e) || used[e]) {
369 continue;
370 }
371
372 const int begin = put;
373
374 int previous = -1;
375 int current = e;
376
377 while (true) {
378 CH_assert(put < numEdges);
379
380 used[current] = true;
381 a_loop[put++] = current;
382
383 const int next = (adjacent[current][0] != previous) ? adjacent[current][0] : adjacent[current][1];
384
385 if (next == e) {
386 break;
387 }
388
389 if (used[next] || put > numEdges) {
390 return -1;
391 }
392
393 previous = current;
394 current = next;
395 }
396
397 if (put - begin < 3) {
398 return -1;
399 }
400
401 CH_assert(numLoops < numEdges);
402
403 a_start[++numLoops] = put;
404 }
405
406 return numLoops;
407}
408#endif
409
410inline bool
411sameVertex(const RealVect& a_a, const RealVect& a_b) noexcept
412{
413 return (a_a - a_b).vectorLength() <= s_weldTolerance;
414}
415
416} // namespace detail
417} // namespace PolyhedralEB
418
419#include <CD_NamespaceFooter.H>
420
421#endif
Declaration of the cell topology and polygon geometry the polyhedral cut cells are built from.
bool isFluid(const Real a_value) noexcept
Which side of the interface a value lies on.
Definition CD_PolyhedralEBUtilsImplem.H:29
The values a cut cell's embedded boundary is reconstructed from.
Definition CD_CutCellSurface.H:41
bool hasCrossing(const int a_edge) const noexcept
Whether the edge carries a crossing.
Definition CD_CutCellSurfaceImplem.H:37
static constexpr int s_numEdges
Number of cell edges a crossing can sit on.
Definition CD_CutCellSurface.H:46
static constexpr int s_numCorners
Number of cell corners.
Definition CD_CutCellSurface.H:51
void edgeOrigin(const int a_edge, int a_offset[SpaceDim]) noexcept
Corner offsets of a cell edge's low end.
Definition CD_PolyhedralEBUtilsImplem.H:45
void polygonMoments(const RealVect *a_vertex, const int a_num, Real &a_area, RealVect &a_vector, RealVect &a_centroid) noexcept
Area vector, area and centroid of a planar polygon given in circuit order.
Definition CD_PolyhedralEBUtilsImplem.H:206
constexpr int s_transverse[3][2]
The two directions transverse to each coordinate direction, in increasing order.
Definition CD_PolyhedralEBUtils.H:59
void faceEdges(const int a_dir, const int a_side, int a_edge[4]) noexcept
The edges of a cell face, edge i joining face corners i and i+1.
Definition CD_PolyhedralEBUtilsImplem.H:175
int facePairs(const int a_dir, const int a_side, const CutCellSurface &a_surface, int a_pair[2][2]) noexcept
The chords on a cell face, each an unordered pair of edge indices.
Definition CD_PolyhedralEBUtilsImplem.H:253
bool sameVertex(const RealVect &a_a, const RealVect &a_b) noexcept
Whether two positions are the same vertex.
Definition CD_PolyhedralEBUtilsImplem.H:411
RealVect cornerPosition(const int a_corner) noexcept
Position of a cell corner in the cell's own frame.
Definition CD_PolyhedralEBUtilsImplem.H:86
constexpr Real s_weldTolerance
Distance within which two vertices are taken to be the same one.
Definition CD_PolyhedralEBUtils.H:210
int edgeDirection(const int a_edge) noexcept
Direction a cell edge runs along.
Definition CD_PolyhedralEBUtilsImplem.H:37
void edgeCorners(const int a_edge, int &a_lo, int &a_hi) noexcept
The corners a cell edge joins, low end first.
Definition CD_PolyhedralEBUtilsImplem.H:67
void faceCorners(const int a_dir, const int a_side, int a_corner[1<<(SpaceDim - 1)]) noexcept
The corners of a cell face, in circuit order around the face.
Definition CD_PolyhedralEBUtilsImplem.H:100
int edgeIndex(const int a_dir, const int a_offset[SpaceDim]) noexcept
Index of the edge running along a_dir whose low corner has the given offsets.
Definition CD_PolyhedralEBUtilsImplem.H:124
RealVect crossingPosition(const CutCellSurface &a_surface, const int a_edge, const Real a_tolerance) noexcept
Position of an edge crossing in the cell's own frame.
Definition CD_PolyhedralEBUtilsImplem.H:140
int crossingLoops(const CutCellSurface &a_surface, int a_loop[CutCellSurface::s_numEdges], int a_start[CutCellSurface::s_numEdges+1]) noexcept
Order the crossings into closed loops, or fail if they do not form clean cycles.
Definition CD_PolyhedralEBUtilsImplem.H:313