cellPointFace.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 "cellPointFace.H"
27 #include "linear.H"
28 
29 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
30 
31 template<class Type>
33 (
34  const vector& position,
35  const label nFace,
36  vector tetPoints[],
37  label tetLabelCandidate[],
38  label tetPointLabels[],
39  scalar phi[],
40  scalar phiCandidate[],
41  label& closestFace,
42  scalar& minDistance
43 ) const
44 {
45  bool foundTet = false;
46 
47  const labelList& thisFacePoints = this->mesh_.faces()[nFace];
48  tetPoints[2] = this->mesh_.faceCentres()[nFace];
49 
50  label pointi = 0;
51 
52  while (pointi < thisFacePoints.size() && !foundTet)
53  {
54  label nextPointLabel = (pointi + 1) % thisFacePoints.size();
55 
56  tetPointLabels[0] = thisFacePoints[pointi];
57  tetPointLabels[1] = thisFacePoints[nextPointLabel];
58 
59  tetPoints[0] = this->mesh_.points()[tetPointLabels[0]];
60  tetPoints[1] = this->mesh_.points()[tetPointLabels[1]];
61 
62  bool inside = true;
63  scalar dist = 0.0;
64 
65  for (label n=0; n<4; n++)
66  {
67  label p1 = (n + 1) % 4;
68  label p2 = (n + 2) % 4;
69  label p3 = (n + 3) % 4;
70 
71  vector referencePoint, faceNormal;
72  referencePoint = tetPoints[p1];
73 
74  faceNormal =
75  (tetPoints[p3] - tetPoints[p1])
76  ^ (tetPoints[p2] - tetPoints[p1]);
77 
78  faceNormal /= mag(faceNormal);
79 
80  // correct normal to point into the tet
81  vector v0 = tetPoints[n] - referencePoint;
82  scalar correct = v0 & faceNormal;
83  if (correct < 0)
84  {
85  faceNormal = -faceNormal;
86  }
87 
88  vector v1 = position - referencePoint + small*faceNormal;
89  scalar rightSide = v1 & faceNormal;
90 
91  // since normal is inwards, inside the tet
92  // is defined as all dot-products being positive
93  inside = inside && (rightSide >= 0);
94 
95  scalar phiLength = (position - referencePoint) & faceNormal;
96 
97  scalar maxLength =
98  max(vSmall, (tetPoints[n] - referencePoint) & faceNormal);
99 
100  phi[n] = phiLength/maxLength;
101 
102  dist += phi[n];
103  }
104 
105  if (!inside)
106  {
107  if (mag(dist - 1.0) < minDistance)
108  {
109  minDistance = mag(dist - 1.0);
110  closestFace = nFace;
111 
112  for (label i=0; i<4; i++)
113  {
114  phiCandidate[i] = phi[i];
115  }
116 
117  tetLabelCandidate[0] = tetPointLabels[0];
118  tetLabelCandidate[1] = tetPointLabels[1];
119  }
120  }
121 
122  foundTet = inside;
123 
124  pointi++;
125  }
126 
127  if (foundTet)
128  {
129  closestFace = nFace;
130  }
131 
132  return foundTet;
133 }
134 
135 
136 template<class Type>
138 (
139  const vector& position,
140  const label nFace,
141  label tetPointLabels[],
142  scalar phi[]
143 ) const
144 {
145  bool foundTriangle = false;
146  vector tetPoints[3];
147  const labelList& facePoints = this->mesh_.faces()[nFace];
148  tetPoints[2] = this->mesh_.faceCentres()[nFace];
149 
150  label pointi = 0;
151 
152  while (pointi < facePoints.size() && !foundTriangle)
153  {
154  // The triangle is constructed from face center and one
155  // face edge
156  label nextPointLabel = (pointi + 1) % facePoints.size();
157 
158  tetPointLabels[0] = facePoints[pointi];
159  tetPointLabels[1] = facePoints[nextPointLabel];
160 
161  tetPoints[0] = this->mesh_.points()[tetPointLabels[0]];
162  tetPoints[1] = this->mesh_.points()[tetPointLabels[1]];
163 
164  vector fc = (tetPoints[0] + tetPoints[1] + tetPoints[2])/3.0;
165 
166  vector newPos = position + small*(fc-position);
167 
168  // calculate triangle edge vectors and triangle face normal
169  // the 'i':th edge is opposite node i
170  vector edge[3], normal[3];
171  for (label i=0; i<3; i++)
172  {
173  label ip0 = (i+1) % 3;
174  label ipp = (i+2) % 3;
175  edge[i] = tetPoints[ipp]-tetPoints[ip0];
176  }
177 
178  vector triangleFaceNormal = edge[1] ^ edge[2];
179 
180  // calculate edge normal (pointing inwards)
181  for (label i=0; i<3; i++)
182  {
183  normal[i] = triangleFaceNormal ^ edge[i];
184  normal[i] /= mag(normal[i]) + vSmall;
185  }
186 
187  // check if position is inside triangle
188  bool inside = true;
189  for (label i=0; i<3; i++)
190  {
191  label ip = (i+1) % 3;
192  inside = inside && (((newPos - tetPoints[ip]) & edge[i]) >= 0);
193  }
194 
195  if (inside)
196  {
197  foundTriangle = true;
198 
199  // calculate phi-values
200  for (label i=0; i<3; i++)
201  {
202  label ip = (i+1) % 3;
203  scalar phiMax = max(vSmall, normal[i] & edge[ip]);
204  scalar phiLength = (position-tetPoints[ip]) & normal[i];
205  phi[i] = phiLength/phiMax;
206  }
207  }
208 
209  pointi++;
210  }
211 
212  return foundTriangle;
213 }
214 
215 
216 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
217 
218 template<class Type>
220 (
221  const VolField<Type>& psi
222 )
223 :
224  fieldInterpolation<Type, cellPointFace<Type>>(psi),
225  volPointInterpolation<Type>(psi),
226  psis_(linearInterpolate(psi))
227 {}
228 
229 
230 template<class Type>
232 (
233  const cellPointFace<Type>& i
234 )
235 :
236  fieldInterpolation<Type, cellPointFace<Type>>(i),
237  volPointInterpolation<Type>(i),
238  psis_(i.psis_.clone())
239 {}
240 
241 
242 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
243 
244 template<class Type>
246 (
247  const vector& position,
248  const label celli,
249  const label facei
250 ) const
251 {
252  Type ts[4];
253  vector tetPoints[4];
254  scalar phi[4], phiCandidate[4];
255  label tetLabelCandidate[2], tetPointLabels[2];
256 
257  Type t = Zero;
258 
259  // only use face information when the position is on a face
260  if (facei < 0)
261  {
262  const vector& cellCentre = this->mesh_.cellCentres()[celli];
263  const labelList& cellFaces = this->mesh_.cells()[celli];
264 
265  vector projection = position - cellCentre;
266  tetPoints[3] = cellCentre;
267 
268  // *********************************************************************
269  // project the cell-center through the point onto the face
270  // and get the closest face, ...
271  // *********************************************************************
272 
273  bool foundTet = false;
274  label closestFace = -1;
275  scalar minDistance = great;
276 
277  forAll(cellFaces, facei)
278  {
279  label nFace = cellFaces[facei];
280 
281  vector normal = this->mesh_.faceAreas()[nFace];
282  normal /= mag(normal);
283 
284  const vector& faceCentreTmp = this->mesh_.faceCentres()[nFace];
285 
286  scalar multiplierNumerator = (faceCentreTmp - cellCentre) & normal;
287  scalar multiplierDenominator = projection & normal;
288 
289  // if normal and projection are not orthogonal this could
290  // be the one...
291  if (mag(multiplierDenominator) > small)
292  {
293  scalar multiplier = multiplierNumerator/multiplierDenominator;
294  vector iPoint = cellCentre + multiplier*projection;
295  scalar dist = mag(position - iPoint);
296 
297  if (dist < minDistance)
298  {
299  closestFace = nFace;
300  minDistance = dist;
301  }
302  }
303  }
304 
305  // *********************************************************************
306  // find the tetrahedron containing 'position'
307  // from the cell center, face center and
308  // two other points on the face
309  // *********************************************************************
310 
311  minDistance = great;
312  if (closestFace != -1)
313  {
314  label nFace = closestFace;
315  foundTet = findTet
316  (
317  position,
318  nFace,
319  tetPoints,
320  tetLabelCandidate,
321  tetPointLabels,
322  phi,
323  phiCandidate,
324  closestFace,
325  minDistance
326  );
327  }
328 
329  if (!foundTet)
330  {
331  // check if the position is 'just' outside a tet
332  if (minDistance < 1.0e-5)
333  {
334  foundTet = true;
335  for (label i=0; i<4; i++)
336  {
337  phi[i] = phiCandidate[i];
338  }
339  tetPointLabels[0] = tetLabelCandidate[0];
340  tetPointLabels[1] = tetLabelCandidate[1];
341  }
342  }
343 
344  // *********************************************************************
345  // if the search failed check all the cell-faces
346  // *********************************************************************
347 
348  if (!foundTet)
349  {
350  minDistance = great;
351 
352  label facei = 0;
353  while (facei < cellFaces.size() && !foundTet)
354  {
355  label nFace = cellFaces[facei];
356  if (nFace < this->mesh_.faceAreas().size())
357  {
358  foundTet = findTet
359  (
360  position,
361  nFace,
362  tetPoints,
363  tetLabelCandidate,
364  tetPointLabels,
365  phi,
366  phiCandidate,
367  closestFace,
368  minDistance
369  );
370  }
371  facei++;
372  }
373  }
374 
375  if (!foundTet)
376  {
377  // check if the position is 'just' outside a tet
378  // this time with a more tolerant limit
379  if (minDistance < 1.0e-3)
380  {
381  foundTet = true;
382  for (label i=0; i<4; i++)
383  {
384  phi[i] = phiCandidate[i];
385  }
386  tetPointLabels[0] = tetLabelCandidate[0];
387  tetPointLabels[1] = tetLabelCandidate[1];
388  }
389  }
390 
391  // *********************************************************************
392  // if the tet was found do the interpolation,
393  // otherwise use the closest face value
394  // *********************************************************************
395 
396  if (foundTet)
397  {
398  for (label i=0; i<2; i++)
399  {
400  ts[i] = this->psip_[tetPointLabels[i]];
401  }
402 
403  if (closestFace < psis_.size())
404  {
405  ts[2] = psis_[closestFace];
406  }
407  else
408  {
409  label patchi =
410  this->mesh_.boundary().whichPatch(closestFace);
411 
412  // If the boundary patch is not empty use the face value
413  // else use the cell value
414  if (this->psi_.boundaryField()[patchi].size())
415  {
416  ts[2] = this->psi_.boundaryField()[patchi]
417  [
418  this->mesh_.boundary()[patchi].whichFace
419  (
420  closestFace
421  )
422  ];
423  }
424  else
425  {
426  ts[2] = this->psi_[celli];
427  }
428  }
429 
430  ts[3] = this->psi_[celli];
431 
432  for (label n=0; n<4; n++)
433  {
434  phi[n] = min(1.0, phi[n]);
435  phi[n] = max(0.0, phi[n]);
436 
437  t += phi[n]*ts[n];
438  }
439  }
440  else
441  {
443  << "search failed; using closest cellFace value" << endl
444  << "cell number " << celli << tab
445  << "position " << position << endl;
446 
447  if (closestFace < psis_.size())
448  {
449  t = psis_[closestFace];
450  }
451  else
452  {
453  label patchi =
454  this->mesh_.boundary().whichPatch(closestFace);
455 
456  // If the boundary patch is not empty use the face value
457  // else use the cell value
458  if (this->psi_.boundaryField()[patchi].size())
459  {
460  t = this->psi_.boundaryField()[patchi]
461  [
462  this->mesh_.boundary()[patchi].whichFace
463  (
464  closestFace
465  )
466  ];
467  }
468  else
469  {
470  t = this->psi_[celli];
471  }
472  }
473  }
474  }
475  else
476  {
477  bool foundTriangle = findTriangle
478  (
479  position,
480  facei,
481  tetPointLabels,
482  phi
483  );
484 
485  if (foundTriangle)
486  {
487  // add up the point values ...
488  for (label i=0; i<2; i++)
489  {
490  Type vel = this->psip_[tetPointLabels[i]];
491  t += phi[i]*vel;
492  }
493 
494  // ... and the face value
495  if (facei < psis_.size())
496  {
497  t += phi[2]*psis_[facei];
498  }
499  else
500  {
501  label patchi = this->mesh_.boundary().whichPatch(facei);
502 
503  // If the boundary patch is not empty use the face value
504  // else use the cell value
505  if (this->psi_.boundaryField()[patchi].size())
506  {
507  t += phi[2]*this->psi_.boundaryField()[patchi]
508  [this->mesh_.boundary()[patchi].whichFace(facei)];
509  }
510  else
511  {
512  t += phi[2]*this->psi_[celli];
513  }
514  }
515  }
516  else
517  {
518  // use face value only
519  if (facei < psis_.size())
520  {
521  t = psis_[facei];
522  }
523  else
524  {
525  label patchi = this->mesh_.boundary().whichPatch(facei);
526 
527  // If the boundary patch is not empty use the face value
528  // else use the cell value
529  if (this->psi_.boundaryField()[patchi].size())
530  {
531  t = this->psi_.boundaryField()[patchi]
532  [this->mesh_.boundary()[patchi].whichFace(facei)];
533  }
534  else
535  {
536  t = this->psi_[celli];
537  }
538  }
539  }
540  }
541 
542  return t;
543 }
544 
545 
546 // ************************************************************************* //
label n
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:449
Generic GeometricField class.
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
Piecewise-linear interpolation method. Uses volPointInterpolation to create values on the points and ...
Definition: cellPointFace.H:59
cellPointFace(const VolField< Type > &psi)
Construct from components.
Type interpolate(const vector &position, const label celli, const label facei=-1) const
Interpolate field to the given point in the given cell.
Base class for interpolations that require a vol-point interpolated field.
label patchi
const volScalarField & psi
#define InfoInFunction
Report an information message using Foam::Info.
void correct(const RdeltaTType &rDeltaT, const RhoType &rho, volScalarField &psi, const surfaceScalarField &phiCorr, const SpType &Sp)
point position(const polyMesh &mesh, const barycentric &coordinates, const label celli, const label facei, const label faceTrii, const scalar stepFraction)
Return the position given the coordinates and tet topology.
Definition: trackingI.H:224
static const zero Zero
Definition: zero.H:97
List< label > labelList
A List of labels.
Definition: labelList.H:56
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
static const char tab
Definition: Ostream.H:296
tmp< SurfaceField< Type > > linearInterpolate(const VolField< Type > &vf)
Definition: linear.H:108
Vector< scalar > vector
A scalar version of the templated Vector.
Definition: vector.H:49
dimensioned< Type > min(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
T clone(const T &t)
Definition: List.H:55
tmp< DimensionedField< scalar, GeoMesh, Field > > mag(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
dimensioned< Type > max(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)