tetOverlapVolume.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2012-2025 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "tetOverlapVolume.H"
27 #include "polyMesh.H"
28 #include "OFstream.H"
29 #include "treeBoundBox.H"
30 #include "cutTriTet.H"
31 
32 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
33 
34 namespace Foam
35 {
37 }
38 
39 
40 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
41 
43 {}
44 
45 
46 // * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * * //
47 
48 Foam::scalar Foam::tetOverlapVolume::tetTetOverlapVol
49 (
50  const tetPointRef& tetA,
51  const tetPointRef& tetB
52 ) const
53 {
54  typedef cutTriTet::noOp noOp;
55 
56  // A maximum of three cuts are made (the tets that result from the final cut
57  // are not stored), and each cut can create at most three tets. The
58  // temporary storage must therefore extend to 3^3 = 27 tets.
59  typedef cutTetList<27> tetListType;
60  static tetListType cutTetList1, cutTetList2;
61 
62  // face 0
63  const plane pl0(tetB.b(), tetB.d(), tetB.c());
64  if (!pl0.valid())
65  {
66  return 0;
67  }
68  const FixedList<point, 4> t({tetA.a(), tetA.b(), tetA.c(), tetA.d()});
69  cutTetList1.clear();
70  tetCut(t, pl0, cutTriTet::appendOp<tetListType>(cutTetList1), noOp());
71  if (cutTetList1.size() == 0)
72  {
73  return 0;
74  }
75 
76  // face 1
77  const plane pl1(tetB.a(), tetB.c(), tetB.d());
78  if (!pl1.valid())
79  {
80  return 0;
81  }
82  cutTetList2.clear();
83  for (label i = 0; i < cutTetList1.size(); i++)
84  {
85  const FixedList<point, 4>& t = cutTetList1[i];
86  tetCut(t, pl1, cutTriTet::appendOp<tetListType>(cutTetList2), noOp());
87  }
88  if (cutTetList2.size() == 0)
89  {
90  return 0;
91  }
92 
93  // face 2
94  const plane pl2(tetB.a(), tetB.d(), tetB.b());
95  if (!pl2.valid())
96  {
97  return 0;
98  }
99  cutTetList1.clear();
100  for (label i = 0; i < cutTetList2.size(); i++)
101  {
102  const FixedList<point, 4>& t = cutTetList2[i];
103  tetCut(t, pl2, cutTriTet::appendOp<tetListType>(cutTetList1), noOp());
104  }
105  if (cutTetList1.size() == 0)
106  {
107  return 0;
108  }
109 
110  // face 3
111  const plane pl3(tetB.a(), tetB.b(), tetB.c());
112  if (!pl3.valid())
113  {
114  return 0;
115  }
116  scalar v = 0;
117  for (label i = 0; i < cutTetList1.size(); i++)
118  {
119  const FixedList<point, 4>& t = cutTetList1[i];
120  v += tetCut(t, pl3, cutTriTet::volumeOp(), noOp());
121  }
122 
123  return v;
124 }
125 
126 
127 Foam::treeBoundBox Foam::tetOverlapVolume::pyrBb
128 (
129  const pointField& points,
130  const face& f,
131  const point& fc
132 ) const
133 {
134  treeBoundBox bb(fc, fc);
135  forAll(f, fp)
136  {
137  const point& pt = points[f[fp]];
138  bb.min() = min(bb.min(), pt);
139  bb.max() = max(bb.max(), pt);
140  }
141  return bb;
142 }
143 
144 
145 // * * * * * * * * * * * Public Member Functions * * * * * * * * * * * * * //
146 
148 (
149  const primitiveMesh& mesh,
150  const label cellI
151 ) const
152 {
153  const cell& cFaces = mesh.cells()[cellI];
154  const point& cc = mesh.cellCentres()[cellI];
155 
156  scalar vol = 0.0;
157 
158  forAll(cFaces, cF)
159  {
160  label faceI = cFaces[cF];
161 
162  const face& f = mesh.faces()[faceI];
163 
164  bool own = (mesh.faceOwner()[faceI] == cellI);
165 
166  label tetBasePtI = 0;
167 
168  const point& tetBasePt = mesh.points()[f[tetBasePtI]];
169 
170  for (label tetPtI = 1; tetPtI < f.size() - 1; tetPtI++)
171  {
172  label facePtI = (tetPtI + tetBasePtI) % f.size();
173  label otherFacePtI = f.fcIndex(facePtI);
174 
175  label pt0I = -1;
176  label pt1I = -1;
177 
178  if (own)
179  {
180  pt0I = f[facePtI];
181  pt1I = f[otherFacePtI];
182  }
183  else
184  {
185  pt0I = f[otherFacePtI];
186  pt1I = f[facePtI];
187  }
188 
189  const tetPointRef tet
190  (
191  cc,
192  tetBasePt,
193  mesh.points()[pt0I],
194  mesh.points()[pt1I]
195  );
196 
197  vol += tet.mag();
198  }
199  }
200 
201  return vol;
202 }
203 
204 
206 (
207  const primitiveMesh& meshA,
208  const label cellAI,
209  const primitiveMesh& meshB,
210  const label cellBI,
211  const treeBoundBox& cellBbB,
212  const scalar threshold
213 ) const
214 {
215  const cell& cFacesA = meshA.cells()[cellAI];
216  const point& ccA = meshA.cellCentres()[cellAI];
217 
218  const cell& cFacesB = meshB.cells()[cellBI];
219  const point& ccB = meshB.cellCentres()[cellBI];
220 
221  scalar vol = 0.0;
222 
223  forAll(cFacesA, cFA)
224  {
225  label faceAI = cFacesA[cFA];
226 
227  const face& fA = meshA.faces()[faceAI];
228  const treeBoundBox pyrA = pyrBb(meshA.points(), fA, ccA);
229  if (!pyrA.overlaps(cellBbB))
230  {
231  continue;
232  }
233 
234  bool ownA = (meshA.faceOwner()[faceAI] == cellAI);
235 
236  label tetBasePtAI = 0;
237 
238  const point& tetBasePtA = meshA.points()[fA[tetBasePtAI]];
239 
240  for (label tetPtI = 1; tetPtI < fA.size() - 1; tetPtI++)
241  {
242  label facePtAI = (tetPtI + tetBasePtAI) % fA.size();
243  label otherFacePtAI = fA.fcIndex(facePtAI);
244 
245  label pt0I = -1;
246  label pt1I = -1;
247 
248  if (ownA)
249  {
250  pt0I = fA[facePtAI];
251  pt1I = fA[otherFacePtAI];
252  }
253  else
254  {
255  pt0I = fA[otherFacePtAI];
256  pt1I = fA[facePtAI];
257  }
258 
259  const tetPointRef tetA
260  (
261  ccA,
262  tetBasePtA,
263  meshA.points()[pt0I],
264  meshA.points()[pt1I]
265  );
266  const treeBoundBox tetABb(tetA.bounds());
267 
268 
269  // Loop over tets of cellB
270  forAll(cFacesB, cFB)
271  {
272  label faceBI = cFacesB[cFB];
273 
274  const face& fB = meshB.faces()[faceBI];
275  const treeBoundBox pyrB = pyrBb(meshB.points(), fB, ccB);
276  if (!pyrB.overlaps(pyrA))
277  {
278  continue;
279  }
280 
281  bool ownB = (meshB.faceOwner()[faceBI] == cellBI);
282 
283  label tetBasePtBI = 0;
284 
285  const point& tetBasePtB = meshB.points()[fB[tetBasePtBI]];
286 
287  for (label tetPtI = 1; tetPtI < fB.size() - 1; tetPtI++)
288  {
289  label facePtBI = (tetPtI + tetBasePtBI) % fB.size();
290  label otherFacePtBI = fB.fcIndex(facePtBI);
291 
292  label pt0I = -1;
293  label pt1I = -1;
294 
295  if (ownB)
296  {
297  pt0I = fB[facePtBI];
298  pt1I = fB[otherFacePtBI];
299  }
300  else
301  {
302  pt0I = fB[otherFacePtBI];
303  pt1I = fB[facePtBI];
304  }
305 
306  const tetPointRef tetB
307  (
308  ccB,
309  tetBasePtB,
310  meshB.points()[pt0I],
311  meshB.points()[pt1I]
312  );
313 
314  if (!tetB.bounds().overlaps(tetABb))
315  {
316  continue;
317  }
318 
319  vol += tetTetOverlapVol(tetA, tetB);
320 
321  if (vol > threshold)
322  {
323  return true;
324  }
325  }
326  }
327  }
328  }
329 
330  return false;
331 }
332 
333 
335 (
336  const primitiveMesh& meshA,
337  const label cellAI,
338  const primitiveMesh& meshB,
339  const label cellBI,
340  const treeBoundBox& cellBbB
341 ) const
342 {
343  const cell& cFacesA = meshA.cells()[cellAI];
344  const point& ccA = meshA.cellCentres()[cellAI];
345 
346  const cell& cFacesB = meshB.cells()[cellBI];
347  const point& ccB = meshB.cellCentres()[cellBI];
348 
349  scalar vol = 0.0;
350 
351  forAll(cFacesA, cFA)
352  {
353  label faceAI = cFacesA[cFA];
354 
355  const face& fA = meshA.faces()[faceAI];
356  const treeBoundBox pyrA = pyrBb(meshA.points(), fA, ccA);
357  if (!pyrA.overlaps(cellBbB))
358  {
359  continue;
360  }
361 
362  bool ownA = (meshA.faceOwner()[faceAI] == cellAI);
363 
364  label tetBasePtAI = 0;
365 
366  const point& tetBasePtA = meshA.points()[fA[tetBasePtAI]];
367 
368  for (label tetPtI = 1; tetPtI < fA.size() - 1; tetPtI++)
369  {
370  label facePtAI = (tetPtI + tetBasePtAI) % fA.size();
371  label otherFacePtAI = fA.fcIndex(facePtAI);
372 
373  label pt0I = -1;
374  label pt1I = -1;
375 
376  if (ownA)
377  {
378  pt0I = fA[facePtAI];
379  pt1I = fA[otherFacePtAI];
380  }
381  else
382  {
383  pt0I = fA[otherFacePtAI];
384  pt1I = fA[facePtAI];
385  }
386 
387  const tetPointRef tetA
388  (
389  ccA,
390  tetBasePtA,
391  meshA.points()[pt0I],
392  meshA.points()[pt1I]
393  );
394  const treeBoundBox tetABb(tetA.bounds());
395 
396 
397  // Loop over tets of cellB
398  forAll(cFacesB, cFB)
399  {
400  label faceBI = cFacesB[cFB];
401 
402  const face& fB = meshB.faces()[faceBI];
403  const treeBoundBox pyrB = pyrBb(meshB.points(), fB, ccB);
404  if (!pyrB.overlaps(pyrA))
405  {
406  continue;
407  }
408 
409  bool ownB = (meshB.faceOwner()[faceBI] == cellBI);
410 
411  label tetBasePtBI = 0;
412 
413  const point& tetBasePtB = meshB.points()[fB[tetBasePtBI]];
414 
415  for (label tetPtI = 1; tetPtI < fB.size() - 1; tetPtI++)
416  {
417  label facePtBI = (tetPtI + tetBasePtBI) % fB.size();
418  label otherFacePtBI = fB.fcIndex(facePtBI);
419 
420  label pt0I = -1;
421  label pt1I = -1;
422 
423  if (ownB)
424  {
425  pt0I = fB[facePtBI];
426  pt1I = fB[otherFacePtBI];
427  }
428  else
429  {
430  pt0I = fB[otherFacePtBI];
431  pt1I = fB[facePtBI];
432  }
433 
434  const tetPointRef tetB
435  (
436  ccB,
437  tetBasePtB,
438  meshB.points()[pt0I],
439  meshB.points()[pt1I]
440  );
441  if (!tetB.bounds().overlaps(tetABb))
442  {
443  continue;
444  }
445 
446  vol += tetTetOverlapVol(tetA, tetB);
447  }
448  }
449  }
450  }
451 
452  return vol;
453 }
454 
455 
456 // ************************************************************************* //
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:449
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
label fcIndex(const label i) const
Return the forward circular index, i.e. the next index.
Definition: UListI.H:58
bool overlaps(const boundBox &) const
Overlaps/touches boundingBox?
Definition: boundBoxI.H:154
A cell is defined as a list of faces with extra functionality.
Definition: cell.H:60
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:76
Geometric class that creates a 2D plane and can return the intersection point between a line and the ...
Definition: plane.H:62
virtual const faceList & faces() const
Return raw faces.
Definition: polyMesh.C:1308
virtual const labelList & faceOwner() const
Return face owner.
Definition: polyMesh.C:1321
virtual const pointField & points() const
Return raw points.
Definition: polyMesh.C:1295
Cell-face mesh analysis engine.
Definition: primitiveMesh.H:75
virtual const faceList & faces() const =0
Return faces.
const vectorField & cellCentres() const
virtual const labelList & faceOwner() const =0
Face face-owner addressing.
virtual const pointField & points() const =0
Return mesh points.
const cellList & cells() const
Calculates the overlap volume of two cells using tetrahedral decomposition.
scalar cellCellOverlapVolumeMinDecomp(const primitiveMesh &meshA, const label cellAI, const primitiveMesh &meshB, const label cellBI, const treeBoundBox &cellBbB) const
Calculates the overlap volume.
scalar cellVolumeMinDecomp(const primitiveMesh &mesh, const label celli) const
Calculates the cell volume.
bool cellCellOverlapMinDecomp(const primitiveMesh &meshA, const label cellAI, const primitiveMesh &meshB, const label cellBI, const treeBoundBox &cellBbB, const scalar threshold=0.0) const
Return true if olverlap volume is greater than threshold.
tetOverlapVolume()
Null constructor.
A tetrahedron primitive.
Definition: tetrahedron.H:82
boundBox bounds() const
Calculate the bounding box.
Definition: tetrahedronI.H:480
const Point & a() const
Return vertices.
Definition: tetrahedronI.H:72
const Point & c() const
Definition: tetrahedronI.H:86
scalar mag() const
Return volume.
Definition: tetrahedronI.H:170
const Point & b() const
Definition: tetrahedronI.H:79
const Point & d() const
Definition: tetrahedronI.H:93
Standard boundBox + extra functionality for use in octree.
Definition: treeBoundBox.H:90
bool overlaps(const boundBox &) const
Overlaps other bounding box?
Definition: boundBoxI.H:154
Foam::fvMesh mesh(Foam::IOobject(regionName, runTime.name(), runTime, Foam::IOobject::MUST_READ), false)
const pointField & points
Namespace for OpenFOAM.
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:42
vector point
Point is a vector.
Definition: point.H:41
dimensioned< Type > min(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
cutTriTet::opAddResult< AboveOp, BelowOp >::type tetCut(const FixedList< Point, 4 > &tet, const FixedList< scalar, 4 > &level, const AboveOp &aboveOp, const BelowOp &belowOp)
As triCut, but for a tetrahedron.
defineTypeNameAndDebug(atmosphericBoundaryLayer, 0)
dimensioned< Type > max(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
labelList f(nPoints)