treeDataFace.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) 2011-2026 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 "treeDataFace.H"
27 #include "polyMesh.H"
28 #include "triangleFuncs.H"
29 
30 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
31 
32 namespace Foam
33 {
35 
36  scalar treeDataFace::tolSqr = sqr(1e-6);
37 }
38 
39 
40 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
41 
42 Foam::treeBoundBox Foam::treeDataFace::calcBb(const label facei) const
43 {
44  const pointField& points = mesh_.points();
45 
46  const face& f = mesh_.faces()[facei];
47 
48  treeBoundBox bb(points[f[0]], points[f[0]]);
49 
50  for (label fp = 1; fp < f.size(); fp++)
51  {
52  const point& p = points[f[fp]];
53 
54  bb.min() = min(bb.min(), p);
55  bb.max() = max(bb.max(), p);
56  }
57  return bb;
58 }
59 
60 
61 void Foam::treeDataFace::update()
62 {
63  forAll(faceLabels_, i)
64  {
65  isTreeFace_.set(faceLabels_[i], 1);
66  }
67 
68  if (cacheBb_)
69  {
70  bbs_.setSize(faceLabels_.size());
71 
72  forAll(faceLabels_, i)
73  {
74  bbs_[i] = calcBb(faceLabels_[i]);
75  }
76  }
77 }
78 
79 
80 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
81 
83 (
84  const bool cacheBb,
85  const primitiveMesh& mesh,
86  const labelUList& faceLabels
87 )
88 :
89  mesh_(mesh),
90  faceLabels_(faceLabels),
91  isTreeFace_(mesh.nFaces(), 0),
92  cacheBb_(cacheBb)
93 {
94  update();
95 }
96 
97 
99 (
100  const bool cacheBb,
101  const primitiveMesh& mesh,
102  labelList&& faceLabels
103 )
104 :
105  mesh_(mesh),
106  faceLabels_(faceLabels),
107  isTreeFace_(mesh.nFaces(), 0),
108  cacheBb_(cacheBb)
109 {
110  update();
111 }
112 
113 
115 (
116  const bool cacheBb,
117  const primitiveMesh& mesh
118 )
119 :
120  mesh_(mesh),
121  faceLabels_(identityMap(mesh_.nFaces())),
122  isTreeFace_(mesh.nFaces(), 0),
123  cacheBb_(cacheBb)
124 {
125  update();
126 }
127 
128 
130 (
131  const bool cacheBb,
132  const polyPatch& patch
133 )
134 :
135  mesh_(patch.mesh()),
136  faceLabels_(identityMap(patch.start(), patch.size())),
137  isTreeFace_(mesh_.nFaces(), 0),
138  cacheBb_(cacheBb)
139 {
140  update();
141 }
142 
143 
145 (
146  const indexedOctree<treeDataFace>& tree
147 )
148 :
149  tree_(tree)
150 {}
151 
152 
154 (
155  const indexedOctree<treeDataFace>& tree
156 )
157 :
158  tree_(tree)
159 {}
160 
161 
162 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
163 
165 {
166  pointField cc(faceLabels_.size());
167 
168  forAll(faceLabels_, i)
169  {
170  cc[i] = mesh_.faceCentres()[faceLabels_[i]];
171  }
172 
173  return cc;
174 }
175 
176 
178 (
179  const indexedOctree<treeDataFace>& oc,
180  const point& sample
181 ) const
182 {
183  // Need to determine whether sample is 'inside' or 'outside'
184  // Done by finding nearest face. This gives back a face which is
185  // guaranteed to contain nearest point. This point can be
186  // - in interior of face: compare to face normal
187  // - on edge of face: compare to edge normal
188  // - on point of face: compare to point normal
189  // Unfortunately the octree does not give us back the intersection point
190  // or where on the face it has hit so we have to recreate all that
191  // information.
192 
193 
194  // Find nearest face to sample
195  pointIndexHit info = oc.findNearest(sample, sqr(great));
196 
197  if (info.index() == -1)
198  {
200  << "Could not find " << sample << " in octree."
201  << abort(FatalError);
202  }
203 
204 
205  // Get actual intersection point on face
206  label facei = faceLabels_[info.index()];
207 
208  if (debug & 2)
209  {
210  Pout<< "getSampleType : sample:" << sample
211  << " nearest face:" << facei;
212  }
213 
214  const pointField& points = mesh_.points();
215 
216  // Retest to classify where on face info is. Note: could be improved. We
217  // already have point.
218 
219  const face& f = mesh_.faces()[facei];
220  const vector& area = mesh_.faceAreas()[facei];
221  const point& fc = mesh_.faceCentres()[facei];
222 
223  pointHit curHit = f.nearestPoint(sample, points);
224  const point& curPt = curHit.rawPoint();
225 
226  //
227  // 1] Check whether sample is above face
228  //
229 
230  if (curHit.hit())
231  {
232  // Nearest point inside face. Compare to face normal.
233 
234  if (debug & 2)
235  {
236  Pout<< " -> face hit:" << curPt
237  << " comparing to face normal " << area << endl;
238  }
239  return indexedOctree<treeDataFace>::getSide(area, sample - curPt);
240  }
241 
242  if (debug & 2)
243  {
244  Pout<< " -> face miss:" << curPt;
245  }
246 
247  //
248  // 2] Check whether intersection is on one of the face vertices or
249  // face centre
250  //
251 
252  const scalar typDimSqr = mag(area) + vSmall;
253 
254  forAll(f, fp)
255  {
256  if ((magSqr(points[f[fp]] - curPt)/typDimSqr) < tolSqr)
257  {
258  // Face intersection point equals face vertex fp
259 
260  // Calculate point normal (wrong: uses face normals instead of
261  // triangle normals)
262  const labelList& pFaces = mesh_.pointFaces()[f[fp]];
263 
264  vector pointNormal(Zero);
265 
266  forAll(pFaces, i)
267  {
268  if (isTreeFace_.get(pFaces[i]) == 1)
269  {
270  vector n = mesh_.faceAreas()[pFaces[i]];
271  n /= mag(n) + vSmall;
272 
273  pointNormal += n;
274  }
275  }
276 
277  if (debug & 2)
278  {
279  Pout<< " -> face point hit :" << points[f[fp]]
280  << " point normal:" << pointNormal
281  << " distance:"
282  << magSqr(points[f[fp]] - curPt)/typDimSqr << endl;
283  }
285  (
286  pointNormal,
287  sample - curPt
288  );
289  }
290  }
291  if ((magSqr(fc - curPt)/typDimSqr) < tolSqr)
292  {
293  // Face intersection point equals face centre. Normal at face centre
294  // is already average of face normals
295 
296  if (debug & 2)
297  {
298  Pout<< " -> centre hit:" << fc
299  << " distance:" << magSqr(fc - curPt)/typDimSqr << endl;
300  }
301 
302  return indexedOctree<treeDataFace>::getSide(area, sample - curPt);
303  }
304 
305 
306 
307  //
308  // 3] Get the 'real' edge the face intersection is on
309  //
310 
311  const labelList& myEdges = mesh_.faceEdges()[facei];
312 
313  forAll(myEdges, myEdgeI)
314  {
315  const edge& e = mesh_.edges()[myEdges[myEdgeI]];
316 
317  pointHit edgeHit =
319  (
320  points[e.start()],
321  points[e.end()]
322  ).nearestDist(sample);
323 
324 
325  if ((magSqr(edgeHit.rawPoint() - curPt)/typDimSqr) < tolSqr)
326  {
327  // Face intersection point lies on edge e
328 
329  // Calculate edge normal (wrong: uses face normals instead of
330  // triangle normals)
331  const labelList& eFaces = mesh_.edgeFaces()[myEdges[myEdgeI]];
332 
333  vector edgeNormal(Zero);
334 
335  forAll(eFaces, i)
336  {
337  if (isTreeFace_.get(eFaces[i]) == 1)
338  {
339  vector n = mesh_.faceAreas()[eFaces[i]];
340  n /= mag(n) + vSmall;
341 
342  edgeNormal += n;
343  }
344  }
345 
346  if (debug & 2)
347  {
348  Pout<< " -> real edge hit point:" << edgeHit.rawPoint()
349  << " comparing to edge normal:" << edgeNormal
350  << endl;
351  }
352 
353  // Found face intersection point on this edge. Compare to edge
354  // normal
356  (
357  edgeNormal,
358  sample - curPt
359  );
360  }
361  }
362 
363 
364  //
365  // 4] Get the internal edge the face intersection is on
366  //
367 
368  forAll(f, fp)
369  {
371  (
372  points[f[fp]],
373  fc
374  ).nearestDist(sample);
375 
376  if ((magSqr(edgeHit.rawPoint() - curPt)/typDimSqr) < tolSqr)
377  {
378  // Face intersection point lies on edge between two face triangles
379 
380  // Calculate edge normal as average of the two triangle normals
381  vector e = points[f[fp]] - fc;
382  vector ePrev = points[f[f.rcIndex(fp)]] - fc;
383  vector eNext = points[f[f.fcIndex(fp)]] - fc;
384 
385  vector nLeft = ePrev ^ e;
386  nLeft /= mag(nLeft) + vSmall;
387 
388  vector nRight = e ^ eNext;
389  nRight /= mag(nRight) + vSmall;
390 
391  if (debug & 2)
392  {
393  Pout<< " -> internal edge hit point:" << edgeHit.rawPoint()
394  << " comparing to edge normal "
395  << 0.5*(nLeft + nRight)
396  << endl;
397  }
398 
399  // Found face intersection point on this edge. Compare to edge
400  // normal
402  (
403  0.5*(nLeft + nRight),
404  sample - curPt
405  );
406  }
407  }
408 
409  if (debug & 2)
410  {
411  Pout<< "Did not find sample " << sample
412  << " anywhere related to nearest face " << facei << endl
413  << "Face:";
414 
415  forAll(f, fp)
416  {
417  Pout<< " vertex:" << f[fp] << " coord:" << points[f[fp]]
418  << endl;
419  }
420  }
421 
422  // Can't determine status of sample with respect to nearest face.
423  // Either
424  // - tolerances are wrong. (if e.g. face has zero area)
425  // - or (more likely) surface is not closed.
426 
427  return volumeType::unknown;
428 }
429 
430 
431 // Check if any point on shape is inside cubeBb.
433 (
434  const label index,
435  const treeBoundBox& cubeBb
436 ) const
437 {
438  // 1. Quick rejection: bb does not intersect face bb at all
439  if (cacheBb_)
440  {
441  if (!cubeBb.overlaps(bbs_[index]))
442  {
443  return false;
444  }
445  }
446  else
447  {
448  if (!cubeBb.overlaps(calcBb(faceLabels_[index])))
449  {
450  return false;
451  }
452  }
453 
454  const pointField& points = mesh_.points();
455 
456 
457  // 2. Check if one or more face points inside
458  label facei = faceLabels_[index];
459 
460  const face& f = mesh_.faces()[facei];
461  if (cubeBb.containsAny(points, f))
462  {
463  return true;
464  }
465 
466  // 3. Difficult case: all points are outside but connecting edges might
467  // go through cube. Use triangle-bounding box intersection.
468  const point& fc = mesh_.faceCentres()[facei];
469 
470  forAll(f, fp)
471  {
472  bool triIntersects = triangleFuncs::intersectBb
473  (
474  points[f[fp]],
475  points[f[f.fcIndex(fp)]],
476  fc,
477  cubeBb
478  );
479 
480  if (triIntersects)
481  {
482  return true;
483  }
484  }
485  return false;
486 }
487 
488 
489 void Foam::treeDataFace::findNearestOp::operator()
490 (
491  const labelUList& indices,
492  const point& sample,
493 
494  scalar& nearestDistSqr,
495  label& minIndex,
496  point& nearestPoint
497 ) const
498 {
499  const treeDataFace& shape = tree_.shapes();
500 
501  forAll(indices, i)
502  {
503  const label index = indices[i];
504 
505  const face& f = shape.mesh().faces()[shape.faceLabels()[index]];
506 
507  pointHit nearHit = f.nearestPoint(sample, shape.mesh().points());
508  scalar distSqr = sqr(nearHit.distance());
509 
510  if (distSqr < nearestDistSqr)
511  {
512  nearestDistSqr = distSqr;
513  minIndex = index;
514  nearestPoint = nearHit.rawPoint();
515  }
516  }
517 }
518 
519 
520 void Foam::treeDataFace::findNearestOp::operator()
521 (
522  const labelUList& indices,
523  const linePointRef& ln,
524 
525  treeBoundBox& tightest,
526  label& minIndex,
527  point& linePoint,
528  point& nearestPoint
529 ) const
530 {
532 }
533 
534 
535 bool Foam::treeDataFace::findIntersectOp::operator()
536 (
537  const label index,
538  const point& start,
539  const point& end,
540  point& intersectionPoint
541 ) const
542 {
543  const treeDataFace& shape = tree_.shapes();
544 
545  // Do quick rejection test
546  if (shape.cacheBb_)
547  {
548  const treeBoundBox& faceBb = shape.bbs_[index];
549 
550  if ((faceBb.posBits(start) & faceBb.posBits(end)) != 0)
551  {
552  // start and end in same block outside of faceBb.
553  return false;
554  }
555  }
556 
557  const label facei = shape.faceLabels_[index];
558 
559  const vector dir(end - start);
560 
561  pointHit inter = shape.mesh_.faces()[facei].intersection
562  (
563  start,
564  dir,
565  shape.mesh_.faceCentres()[facei],
566  shape.mesh_.points(),
568  );
569 
570  if (inter.hit() && inter.distance() <= 1)
571  {
572  // Note: no extra test on whether intersection is in front of us
573  // since using half_ray
574  intersectionPoint = inter.hitPoint();
575  return true;
576  }
577  else
578  {
579  return false;
580  }
581 }
582 
583 
584 // ************************************************************************* //
label n
#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
unsigned int get(const label) const
Get value at index I.
Definition: PackedListI.H:938
const Point & hitPoint() const
Return hit point.
Definition: PointHit.H:126
scalar distance() const
Return distance to hit.
Definition: PointHit.H:139
const Point & rawPoint() const
Return point with no checking.
Definition: PointHit.H:158
bool hit() const
Is there a hit.
Definition: PointHit.H:120
This class describes the interaction of (usually) a face and a point. It carries the info of a succes...
Definition: PointIndexHit.H:54
label index() const
Return index.
bool containsAny(const UList< point > &) const
Contains any of the points? (inside or on edge)
Definition: boundBox.C:258
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:61
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:76
Non-pointer based hierarchical recursive searching.
Definition: indexedOctree.H:72
static volumeType getSide(const vector &outsideNormal, const vector &vec)
Helper function to return the side. Returns outside if.
A line primitive.
Definition: line.H:71
PointHit< Point > nearestDist(const Point &p) const
Return nearest distance to line from a given point.
Definition: lineI.H:95
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:71
Cell-face mesh analysis engine.
Definition: primitiveMesh.H:75
const edgeList & edges() const
Return mesh edges. Uses calcEdges.
const vectorField & faceCentres() const
virtual const faceList & faces() const =0
Return faces.
const labelListList & pointFaces() const
virtual const pointField & points() const =0
Return mesh points.
const labelListList & edgeFaces() const
const labelListList & faceEdges() const
const vectorField & faceAreas() const
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
direction posBits(const point &) const
Position of point relative to bounding box.
Definition: treeBoundBox.C:456
findIntersectOp(const indexedOctree< treeDataFace > &tree)
Definition: treeDataFace.C:154
findNearestOp(const indexedOctree< treeDataFace > &tree)
Definition: treeDataFace.C:145
Encapsulation of data needed to search for faces.
Definition: treeDataFace.H:59
bool overlaps(const label index, const treeBoundBox &sampleBb) const
Does (bb of) shape at index overlap bb.
Definition: treeDataFace.C:433
treeDataFace(const bool cacheBb, const primitiveMesh &, const labelUList &)
Construct from mesh and subset of faces.
Definition: treeDataFace.C:83
const labelList & faceLabels() const
Definition: treeDataFace.H:178
const primitiveMesh & mesh() const
Definition: treeDataFace.H:183
volumeType getVolumeType(const indexedOctree< treeDataFace > &, const point &) const
Get type (inside,outside,mixed,unknown) of point w.r.t. surface.
Definition: treeDataFace.C:178
pointField shapePoints() const
Get representative point cloud for all shapes inside.
Definition: treeDataFace.C:164
static bool intersectBb(const point &p0, const point &p1, const point &p2, const treeBoundBox &cubeBb)
Does triangle intersect bounding box.
Foam::fvMesh mesh(Foam::IOobject(regionName, runTime.name(), runTime, Foam::IOobject::MUST_READ), false)
#define NotImplemented
Issue a FatalErrorIn for a function not currently implemented.
Definition: error.H:381
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
const pointField & points
const dimensionSet area
Namespace for OpenFOAM.
static const zero Zero
Definition: zero.H:97
const doubleScalar e
Definition: doubleScalar.H:106
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
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:288
errorManip< error > abort(error &err)
Definition: errorManip.H:131
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:42
vector point
Point is a vector.
Definition: point.H:41
tmp< DimensionedField< typename outerProduct< Type, Type >::type, GeoMesh, Field >> sqr(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
dimensioned< Type > min(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
prefixOSstream Pout(cout, "Pout")
Definition: IOstreams.H:53
tmp< DimensionedField< scalar, GeoMesh, Field > > mag(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
error FatalError
labelList identityMap(const label len)
Create identity map (map[i] == i) of given length.
Definition: ListOps.C:104
tmp< DimensionedField< scalar, GeoMesh, Field > > magSqr(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
defineTypeNameAndDebug(atmosphericBoundaryLayer, 0)
bool ln(const fileName &src, const fileName &dst)
Create a softlink. dst should not exist. Returns true if successful.
Definition: POSIX.C:908
dimensioned< Type > max(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
labelList f(nPoints)
Info<< "Finished reading KIVA file"<< endl;cellShapeList cellShapes(nPoints);labelList cellZoning(nPoints, -1);const cellModel &hex=*(cellModeller::lookup("hex"));labelList hexLabels(8);label activeCells=0;labelList pointMap(nPoints);forAll(pointMap, i){ pointMap[i]=i;}for(label i=0;i< nPoints;i++){ if(f[i] > 0.0) { hexLabels[0]=i;hexLabels[1]=i1tab[i];hexLabels[2]=i3tab[i1tab[i]];hexLabels[3]=i3tab[i];hexLabels[4]=i8tab[i];hexLabels[5]=i1tab[i8tab[i]];hexLabels[6]=i3tab[i1tab[i8tab[i]]];hexLabels[7]=i3tab[i8tab[i]];cellShapes[activeCells]=cellShape(hex, hexLabels);edgeList edges=cellShapes[activeCells].edges();forAll(edges, ei) { if(edges[ei].mag(points)< small) { label start=pointMap[edges[ei].start()];while(start !=pointMap[start]) { start=pointMap[start];} label end=pointMap[edges[ei].end()];while(end !=pointMap[end]) { end=pointMap[end];} label minLabel=min(start, end);pointMap[start]=pointMap[end]=minLabel;} } cellZoning[activeCells]=idreg[i];activeCells++;}}cellShapes.setSize(activeCells);cellZoning.setSize(activeCells);forAll(cellShapes, celli){ cellShape &cs=cellShapes[celli];forAll(cs, i) { cs[i]=pointMap[cs[i]];} cs.collapse();}label bcIDs[11]={-1, 0, 2, 4, -1, 5, -1, 6, 7, 8, 9};const label nBCs=12;const word *kivaPatchTypes[nBCs]={ &wallPolyPatch::typeName, &wallPolyPatch::typeName, &wallPolyPatch::typeName, &wallPolyPatch::typeName, &symmetryPolyPatch::typeName, &wedgePolyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &symmetryPolyPatch::typeName, &mergedCyclicPolyPatch::typeName};enum patchTypeNames{ PISTON, VALVE, LINER, CYLINDERHEAD, AXIS, WEDGE, INFLOW, OUTFLOW, PRESIN, PRESOUT, SYMMETRYPLANE, CYCLIC};const char *kivaPatchNames[nBCs]={ "piston", "valve", "liner", "cylinderHead", "axis", "wedge", "inflow", "outflow", "presin", "presout", "symmetryPlane", "cyclic"};List< SLList< face > > pFaces[nBCs]
Definition: readKivaGrid.H:229
volScalarField & p