sweptFaceAreaWeightAMI.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) 2017-2021 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 "sweptFaceAreaWeightAMI.H"
27 #include "cut.H"
28 #include "linearEqn.H"
29 #include "quadraticEqn.H"
30 #include "unitConversion.H"
31 #include "triSurface.H"
33 
34 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
35 
36 namespace Foam
37 {
38  defineTypeNameAndDebug(sweptFaceAreaWeightAMI, 0);
39  addToRunTimeSelectionTable(AMIMethod, sweptFaceAreaWeightAMI, components);
40 }
41 
42 const Foam::scalar Foam::sweptFaceAreaWeightAMI::minCutRatio_ = 10*small;
43 
44 const Foam::scalar Foam::sweptFaceAreaWeightAMI::maxDot_ =
45  - cos(degToRad(89.9));
46 
47 
48 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
49 
50 template<unsigned Size>
51 void Foam::sweptFaceAreaWeightAMI::writeCutTrisVTK
52 (
53  const cutTriList<Size>& tris,
54  const word& name
55 ) const
56 {
57  OFstream obj(typeName + "_" + name + ".vtk");
58 
59  obj << "# vtk DataFile Version 2.0" << endl
60  << triSurface::typeName << endl
61  << "ASCII" << endl
62  << "DATASET POLYDATA" << endl
63  << "POINTS " << 3*tris.size() << " float" << endl;
64 
65  for (label i = 0; i < tris.size(); ++ i)
66  {
67  for (label j = 0; j < 3; ++ j)
68  {
69  const vector& x = tris[i][j];
70  obj << x.x() << ' ' << x.y() << ' ' << x.z() << endl;
71  }
72  }
73 
74  obj << "POLYGONS " << tris.size() << ' ' << 4*tris.size() << endl;
75 
76  for (label i = 0; i < tris.size(); ++ i)
77  {
78  obj << 3 << ' ' << 3*i << ' ' << 3*i + 1 << ' ' << 3*i + 2 << endl;
79  }
80 }
81 
82 
83 void Foam::sweptFaceAreaWeightAMI::writeFaceOBJ
84 (
85  const face& f,
86  const pointField& ps,
87  const string& name
88 ) const
89 {
90  OFstream obj(typeName + "_" + name + ".obj");
91 
92  for (label i = 0; i < f.size(); ++ i)
93  {
94  const vector& x = ps[f[i]];
95  obj << "v " << x.x() << ' ' << x.y() << ' ' << x.z() << endl;
96  }
97 
98  obj << 'f';
99  for (label i = 0; i < f.size(); ++ i)
100  {
101  obj << ' ' << i + 1;
102  }
103  obj << endl;
104 }
105 
106 
107 void Foam::sweptFaceAreaWeightAMI::writeProjectionOBJ
108 (
109  const label srcN,
110  const FixedList<point, 4>& srcTri,
111  const FixedList<point, 4>& srcNrm
112 ) const
113 {
114  scalar l = 0;
115  for (label i = 0; i < srcN - 1; ++ i)
116  {
117  l = max(l, mag(srcTri[i] - srcTri[i + 1]));
118  }
119 
120  const label nu = 20, nv = 20;
121  const scalar u0 = 0, u1 = 1, v0 = -l, v1 = l;
122 
123  OFstream obj(typeName + "_projection.obj");
124 
125  for (label i = 0; i < srcN; ++ i)
126  {
127  const point& p0 = srcTri[i], p1 = srcTri[(i + 1) % srcN];
128  const vector& n0 = srcNrm[i], n1 = srcNrm[(i + 1) % srcN];
129 
130  for (label iu = 0; iu <= nu; ++ iu)
131  {
132  const scalar u = u0 + (u1 - u0)*scalar(iu)/nu;
133  for (label iv = 0; iv <= nv; ++ iv)
134  {
135  const scalar v = v0 + (v1 - v0)*scalar(iv)/nv;
136  const vector x = p0 + (p1 - p0)*u + (n0 + (n1 - n0)*u)*v;
137  obj << "v " << x.x() << ' ' << x.y() << ' ' << x.z() << endl;
138  }
139  }
140  }
141 
142  for (label i = 0; i < srcN; ++ i)
143  {
144  for (label iu = 0; iu < nu; ++ iu)
145  {
146  for (label iv = 0; iv < nv; ++ iv)
147  {
148  obj << "f "
149  << i*(nu + 1)*(nv + 1) + (nv + 1)*iu + iv + 1 << ' '
150  << i*(nu + 1)*(nv + 1) + (nv + 1)*iu + iv + 2 << ' '
151  << i*(nu + 1)*(nv + 1) + (nv + 1)*(iu + 1) + iv + 2 << ' '
152  << i*(nu + 1)*(nv + 1) + (nv + 1)*(iu + 1) + iv + 1<< endl;
153  }
154  }
155  }
156 }
157 
158 
159 Foam::label Foam::sweptFaceAreaWeightAMI::getSourceProjection
160 (
161  FixedList<point, 4>& srcTri,
162  FixedList<point, 4>& srcNrm,
163  const FixedList<point, 3>& tgtTri
164 ) const
165 {
166  // The target normal
167  const vector np = plane(tgtTri[0], tgtTri[1], tgtTri[2]).normal();
168 
169  // Dot products between the projection normals and the target plane
170  FixedList<scalar, 4> dots;
171  for (label i = 0; i < 3; ++ i)
172  {
173  dots[i] = srcNrm[i] & np;
174  }
175  dots[3] = 0;
176 
177  // Search though for the number of reversed directions and an index for
178  // both a reverse and a forward direction
179  label nR = 0, iR = -1, iF = -1;
180  for (label i = 0; i < 3; ++ i)
181  {
182  if (dots[i] > maxDot_)
183  {
184  ++ nR;
185  iR = i;
186  }
187  else
188  {
189  iF = i;
190  }
191  }
192 
193  // If all the normals hit in the forward or reverse direction then return
194  // the existing projection or no projection respectively
195  if (nR == 0 || nR == 3)
196  {
197  return 3 - nR;
198  }
199 
200  // One normal hits in the reverse direction
201  if (nR == 1)
202  {
203  /*
204  o j1
205  / \
206  / \
207  / \
208  / o jR
209  / / .
210  / / .
211  / / .
212  o - > - o . . . . (old iR)
213  i0 iR
214  */
215 
216  // Make a gap by duplicating the reverse the point
217  for (label j = 2; j >= iR; -- j)
218  {
219  srcTri[j + 1] = srcTri[j];
220  srcNrm[j + 1] = srcNrm[j];
221  dots[j + 1] = dots[j];
222  }
223 
224  const label jR = iR + 1;
225  const label i0 = (iR + 3) % 4, j1 = (jR + 1) % 4;
226 
227  const scalar wi = (dots[iR] - maxDot_)/(dots[iR] - dots[i0]);
228  const scalar wj = (dots[jR] - maxDot_)/(dots[jR] - dots[j1]);
229 
230  srcTri[iR] = srcTri[iR] + (srcTri[i0] - srcTri[iR])*wi;
231  srcTri[jR] = srcTri[jR] + (srcTri[j1] - srcTri[jR])*wj;
232 
233  srcNrm[iR] = srcNrm[iR] + (srcNrm[i0] - srcNrm[iR])*wi;
234  srcNrm[jR] = srcNrm[jR] + (srcNrm[j1] - srcNrm[jR])*wj;
235 
236  return 4;
237  }
238 
239  // Two normals hit in the reverse direction
240  if (nR == 2)
241  {
242  /*
243  . (old jR)
244  . .
245  jR o .
246  / \ .
247  / \ .
248  / \ .
249  / \ .
250  / \ .
251  o - - > - - o . . (old iR)
252  iF iR
253  */
254 
255  const label iR = (iF + 1) % 3, jR = (iF + 2) % 3;
256  const label i0 = (iR + 2) % 3, j1 = (jR + 1) % 3;
257 
258  const scalar wi = (dots[iR] - maxDot_)/(dots[iR] - dots[i0]);
259  const scalar wj = (dots[jR] - maxDot_)/(dots[jR] - dots[j1]);
260 
261  srcTri[iR] = srcTri[iR] + (srcTri[i0] - srcTri[iR])*wi;
262  srcTri[jR] = srcTri[jR] + (srcTri[j1] - srcTri[jR])*wj;
263 
264  srcNrm[iR] = srcNrm[iR] + (srcNrm[i0] - srcNrm[iR])*wi;
265  srcNrm[jR] = srcNrm[jR] + (srcNrm[j1] - srcNrm[jR])*wj;
266 
267  return 3;
268  }
269 
270  // This cannot happen
271  return -1;
272 }
273 
274 
275 Foam::plane Foam::sweptFaceAreaWeightAMI::getCutPlane
276 (
277  const point& p0,
278  const point& p1,
279  const vector& n0,
280  const vector& n1,
281  const FixedList<point, 3>& tgtTri
282 ) const
283 {
284  // The target plane
285  const plane tgtPln(tgtTri[0], tgtTri[1], tgtTri[2]);
286  const point& pp = tgtPln.refPoint();
287  const vector& np = tgtPln.normal();
288 
289  // Calculate the bounding intersection points. These are the locations at
290  // which the bounding lines of the projected surface intersect with the
291  // target plane.
292  const scalar v0 = ((pp - p0) & np)/(n0 & np);
293  const scalar v1 = ((pp - p1) & np)/(n1 & np);
294  Pair<point> cutPnts(p0 + v0*n0, p1 + v1*n1);
295  Pair<vector> cutNrms((p1 - p0 + n1*v0) ^ n0, (p1 - p0 - n0*v1) ^ n1);
296 
297  // Calculate edge intersections with the surface
298  for (label i = 0; i < 3; ++ i)
299  {
300  // The current target edge
301  const point& l0 = tgtTri[i], l1 = tgtTri[(i + 1)%3];
302 
303  // Form the quadratic in the surface parameter u
304  const vector k = l0 - p0;
305  const vector a = l0 - l1, b = p1 - p0, c = n0, d = n1 - n0;
306  const vector ka = k ^ a, ba = b ^ a, ca = c ^ a, da = d ^ a;
307  const scalar A = d & ba;
308  const scalar B = (c & ba) - (d & ka);
309  const scalar C = - c & ka;
310 
311  // Solve and extract the other parameters
312  const Roots<2> us = quadraticEqn(A, B, C).roots();
313  Pair<scalar> vs, ws;
314  Pair<vector> ns;
315  Pair<bool> cuts(false, false);
316  for (label j = 0; j < 2; ++ j)
317  {
318  if (us.type(j) == rootType::real)
319  {
320  const vector den = ca + da*us[j];
321  if (magSqr(den) > vSmall)
322  {
323  const vector vNum = ka - ba*us[j];
324  const vector wNum = (- k + b*us[j]) ^ (c + d*us[j]);
325  vs[j] = (vNum & den)/magSqr(den);
326  ws[j] = (wNum & den)/magSqr(den);
327  ns[j] = (b ^ (c + d*us[j])) + (n1 ^ n0)*vs[j];
328  const bool cutu = 0 < us[j] && us[j] < 1;
329  const bool cutw = 0 < ws[j] && ws[j] < 1;
330  cuts[j] = cutu && cutw;
331  }
332  }
333  }
334 
335  // If we have just one intersection in bounds then store it in the
336  // result list based on its direction
337  if (cuts[0] != cuts[1])
338  {
339  const label j = cuts[0] ? 0 : 1;
340  const scalar na = ns[j] & a;
341  cutPnts[na < 0] = l0 + (l1 - l0)*ws[j];
342  cutNrms[na < 0] = ns[j];
343  }
344  }
345 
346  // Generate and return the cut plane. If the cut points are not coincident
347  // then form a plane normal by crossing the displacement between the points
348  // by the target plane normal. If the points are coincident then use the
349  // projected surface normal evaluated at the first cut point.
350  const vector cutDelta = cutPnts[1] - cutPnts[0];
351  const bool coincident = mag(cutDelta) < minCutRatio_*mag(p1 - p0);
352  return plane(cutPnts[0], coincident ? cutNrms[0] : np ^ cutDelta);
353 };
354 
355 
356 // * * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * //
357 
359 (
360  const label srcFacei,
361  const label tgtFacei
362 ) const
363 {
364  const label debugTgtFace =
365  (- 1 - debug) % this->tgtPatch_.size() == tgtFacei
366  ? (- 1 - debug) / this->tgtPatch_.size() + 1 : 0;
367  const bool debugPrint = debug > 0 || debugTgtFace > 0;
368  const bool debugWrite = debug > 1 || debugTgtFace > 1;
369 
370  if (debugPrint)
371  {
372  Info<< "Inter area between source face #" << srcFacei
373  << " and target face #" << tgtFacei << (debugWrite ? "\n" : ": ");
374  }
375 
376  // Patch data
377  const pointField& srcPoints = this->srcPatch_.localPoints();
378  const pointField& tgtPoints = this->tgtPatch_.localPoints();
379  const vectorField& srcPointNormals = this->srcPatch_.pointNormals();
380 
381  // Faces
382  const face& srcFace = this->srcPatch_.localFaces()[srcFacei];
383  const face& tgtFace = this->tgtPatch_.localFaces()[tgtFacei];
384 
385  // Write out the faces
386  if (debugWrite)
387  {
388  writeFaceOBJ(srcFace, srcPoints, "source");
389  writeFaceOBJ(tgtFace, tgtPoints, "target");
390  }
391  if (debugTgtFace)
392  {
393  writeFaceOBJ(tgtFace, tgtPoints, "target" + name(tgtFacei));
394  }
395 
396  // Triangulate the faces
397  const faceAreaIntersect::triangulationMode triMode = this->triMode_;
398  triFaceList srcFaceTris, tgtFaceTris;
399  faceAreaIntersect::triangulate(srcFace, srcPoints, triMode, srcFaceTris);
400  faceAreaIntersect::triangulate(tgtFace, tgtPoints, triMode, tgtFaceTris);
401 
402  // Area sum
403  scalar areaMag = Zero;
404 
405  // Loop the target triangles
406  forAllConstIter(triFaceList, tgtFaceTris, tgtIter)
407  {
408  const FixedList<point, 3>
409  tgtTri =
410  {
411  tgtPoints[(*tgtIter)[0]],
412  tgtPoints[(*tgtIter)[this->reverseTarget_ ? 2 : 1]],
413  tgtPoints[(*tgtIter)[this->reverseTarget_ ? 1 : 2]]
414  };
415 
416  // Loop the source triangles
417  forAllConstIter(triFaceList, srcFaceTris, srcIter)
418  {
420  srcTri =
421  {
422  srcPoints[(*srcIter)[0]],
423  srcPoints[(*srcIter)[1]],
424  srcPoints[(*srcIter)[2]],
426  };
428  srcNrm =
429  {
430  srcPointNormals[(*srcIter)[0]],
431  srcPointNormals[(*srcIter)[1]],
432  srcPointNormals[(*srcIter)[2]],
433  vector::zero
434  };
435 
436  // Get the source projection modified for any reverse intersections
437  const label srcN = getSourceProjection(srcTri, srcNrm, tgtTri);
438  if (srcN <= 0)
439  {
440  continue;
441  }
442 
443  // Write the source projection
444  if (debugWrite)
445  {
446  writeProjectionOBJ(srcN, srcTri, srcNrm);
447  }
448 
449  // Create the initial cut triangle list
450  cutTriList<8> cutTris;
451  cutTris.append(tgtTri);
452 
453  // Write the initial triangle
454  if (debugWrite)
455  {
456  writeCutTrisVTK(cutTris, "tris0");
457  }
458 
459  // Do all but one of the cuts
460  for
461  (
462  label i = 0;
463  i < srcN - 1 && (debugWrite || cutTris.size());
464  ++ i
465  )
466  {
467  // Do the cut
468  const plane cutPlane =
469  getCutPlane
470  (
471  srcTri[i],
472  srcTri[i+1],
473  srcNrm[i],
474  srcNrm[i+1],
475  tgtTri
476  );
477 
478  cutTriList<8> cutTrisTmp;
479 
480  for (label j = 0; j < cutTris.size(); ++j)
481  {
482  triCut
483  (
484  cutTris[j],
485  cutPlane,
486  cut::noOp(),
487  cut::appendOp<cutTriList<8>>(cutTrisTmp)
488  );
489  }
490  Swap(cutTris, cutTrisTmp);
491 
492  // Write the triangles resulting from the cut
493  if (debugWrite)
494  {
495  writeCutTrisVTK(cutTris, "tris" + name(i + 1));
496  }
497  }
498 
499  // Do the last cut
500  const plane cutPlane =
501  getCutPlane
502  (
503  srcTri[srcN - 1],
504  srcTri[0],
505  srcNrm[srcN - 1],
506  srcNrm[0],
507  tgtTri
508  );
509  cutTriList<8> cutTrisTmp;
510  for (label i = 0; i < cutTris.size(); ++ i)
511  {
512  // Sum the area of the cut triangle
513  areaMag +=
514  mag
515  (
516  triCut
517  (
518  cutTris[i],
519  cutPlane,
520  cut::noOp(),
521  cut::areaOp()
522  )
523  );
524  // Store the cut triangle if it is needed for output
525  if (debugWrite || debugTgtFace)
526  {
527  triCut
528  (
529  cutTris[i],
530  cutPlane,
531  cut::noOp(),
532  cut::appendOp<cutTriList<8>>(cutTrisTmp)
533  );
534  }
535  }
536  Swap(cutTris, cutTrisTmp);
537 
538  // Write the triangles resulting from the cuts
539  if (debugTgtFace && cutTris.size())
540  {
541  static label writeCutTrisVTKIndex = 0;
542  writeCutTrisVTK
543  (
544  cutTris,
545  "target" + name(tgtFacei) + "_"
546  + "tris" + name(writeCutTrisVTKIndex ++)
547  );
548  }
549  if (debugWrite)
550  {
551  writeCutTrisVTK(cutTris, "tris" + name(srcN));
552 
553  Info << "view triangles then press ENTER to continue ...";
554  getchar();
555  }
556  }
557  }
558 
559  // Print the difference between this inter-area and that obtained by the
560  // standard algorithm built around faceAreaIntersect
561  if (debugPrint)
562  {
563  const scalar standardAreaMag =
565  (
566  srcFacei,
567  tgtFacei
568  );
569 
570  Info<<"standard=" << standardAreaMag << ", swept=" << areaMag
571  << endl;
572  }
573 
574  return areaMag;
575 }
576 
577 
578 Foam::scalar Foam::sweptFaceAreaWeightAMI::minWeight() const
579 {
580  return small;
581 }
582 
583 
584 Foam::scalar Foam::sweptFaceAreaWeightAMI::maxWalkAngle() const
585 {
586  return degToRad(180);
587 }
588 
589 
590 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
591 
593 {}
594 
595 
596 // ************************************************************************* //
virtual scalar interArea(const label srcFacei, const label tgtFacei) const
Area of intersection between source and target faces.
layerAndWeight max(const layerAndWeight &a, const layerAndWeight &b)
FvWallInfoData< WallInfo, label > label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:75
A 1D vector of objects of type <T> with a fixed size <Size>.
Definition: FixedList.H:54
#define forAllConstIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:477
Unit conversion functions.
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
Vector< scalar > vector
A scalar version of the templated Vector.
Definition: vector.H:49
Geometric class that creates a 2D plane and can return the intersection point between a line and the ...
Definition: plane.H:60
static void triangulate(const face &f, const pointField &points, const triangulationMode &triMode, triFaceList &faceTris)
Triangulate a face using the given triangulation mode.
Macros for easy insertion into run-time selection tables.
scalar degToRad(const scalar deg)
Conversion from degrees to radians.
dimensionedScalar j1(const dimensionedScalar &ds)
scalar u0
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:42
dimensionedScalar cos(const dimensionedScalar &ds)
void Swap(T &a, T &b)
Definition: Swap.H:43
cut::opAddResult< AboveOp, BelowOp >::type triCut(const FixedList< Point, 3 > &tri, const FixedList< scalar, 3 > &level, const AboveOp &aboveOp, const BelowOp &belowOp)
Cut a triangle along the zero plane defined by the given levels. Templated.
static const zero Zero
Definition: zero.H:97
const Cmpt & x() const
Definition: VectorI.H:75
dimensioned< scalar > magSqr(const dimensioned< Type > &)
addToRunTimeSelectionTable(ensightPart, ensightPartCells, istream)
defineTypeNameAndDebug(combustionModel, 0)
word name(const complex &)
Return a string representation of a complex.
Definition: complex.C:47
virtual scalar interArea(const label srcFacei, const label tgtFacei) const
Area of intersection between source and target faces.
vector point
Point is a vector.
Definition: point.H:41
messageStream Info
dimensioned< scalar > mag(const dimensioned< Type > &)
Namespace for OpenFOAM.
virtual ~sweptFaceAreaWeightAMI()
Destructor.