cutFaceAdvect.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2016-2017 DHI
9  Copyright (C) 2018-2019 Johan Roenby
10  Copyright (C) 2019-2020 DLR
11 -------------------------------------------------------------------------------
12 License
13  This file is part of OpenFOAM.
14 
15  OpenFOAM is free software: you can redistribute it and/or modify it
16  under the terms of the GNU General Public License as published by
17  the Free Software Foundation, either version 3 of the License, or
18  (at your option) any later version.
19 
20  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
21  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
22  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23  for more details.
24 
25  You should have received a copy of the GNU General Public License
26  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
27 
28 \*---------------------------------------------------------------------------*/
29 
30 #include "cutFaceAdvect.H"
31 #include "OFstream.H"
32 
33 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
34 
36 (
37  const fvMesh& mesh,
38  const volScalarField& alpha1
39 )
40 :
41  cutFace(mesh),
42  mesh_(mesh),
43  alpha1_(alpha1),
44  subFaceCentre_(Zero),
45  subFaceArea_(Zero),
46  subFacePoints_(10),
47  surfacePoints_(4),
48  pointStatus_(10),
49  weight_(10),
50  pTimes_(10),
51  faceStatus_(-1)
52 {
53  clearStorage();
54 }
55 
56 
57 // * * * * * * * * * * * Public Member Functions * * * * * * * * * * * * * //
58 
60 (
61  const label faceI,
62  const vector& normal,
63  const vector& base
64 )
65 {
66  clearStorage();
67 
68  const face& f = mesh_.faces()[faceI];
69 
70  label inLiquid = 0;
71  label firstFullySubmergedPoint = -1;
72 
73  // Loop face and calculate pointStatus
74  forAll(f, i)
75  {
76  scalar value = (mesh_.points()[f[i]] - base) & normal;
77  if (mag(value) < 10 * SMALL)
78  {
79  value = 0;
80  }
81  pointStatus_.append(value);
82  if (pointStatus_[i] > 10 * SMALL)
83  {
84  inLiquid++;
85  if (firstFullySubmergedPoint == -1)
86  {
87  firstFullySubmergedPoint = i;
88  }
89  }
90  }
91 
92  if (inLiquid == f.size()) // fluid face
93  {
94  faceStatus_ = -1;
95  subFaceCentre_ = mesh_.faceCentres()[faceI];
96  subFaceArea_ = mesh_.faceAreas()[faceI];
97  return faceStatus_;
98  }
99  else if (inLiquid == 0) // gas face
100  {
101  faceStatus_ = 1;
102  subFaceCentre_ = Zero;
103  subFaceArea_ = Zero;
104  return faceStatus_;
105  }
106 
107  cutFace::calcSubFace
108  (
109  faceI,
110  pointStatus_,
111  firstFullySubmergedPoint,
112  subFacePoints_,
113  surfacePoints_,
114  faceStatus_,
115  subFaceCentre_,
116  subFaceArea_
117  );
118 
119  return faceStatus_;
120 }
121 
122 
124 (
125  const face& f,
126  const pointField& points,
127  const scalarField& val,
128  const scalar cutValue
129 )
130 {
131  clearStorage();
132 
133  label inLiquid = 0;
134  label firstFullySubmergedPoint = -1;
135  scalarList pointStatus(f.size());
136 
137  // Loop face and calculate pointStatus
138  forAll(f, i)
139  {
140  pointStatus[i] = val[f[i]] - cutValue;
141  if (mag(pointStatus[i]) < 10 * SMALL)
142  {
143  pointStatus[i] = 0;
144  }
145  if (pointStatus[i] > 10 * SMALL)
146  {
147  ++inLiquid;
148  if (firstFullySubmergedPoint == -1)
149  {
150  firstFullySubmergedPoint = i;
151  }
152  }
153  }
154 
155  if (inLiquid == f.size()) // fluid face
156  {
157  faceStatus_ = -1;
158  subFaceCentre_ = f.centre(points);
159  subFaceArea_ = f.areaNormal(points);
160  return faceStatus_;
161  }
162  else if (inLiquid == 0) // gas face
163  {
164  faceStatus_ = 1;
165  subFaceCentre_ = Zero;
166  subFaceArea_ = Zero;
167  return faceStatus_;
168  }
169 
170  cutFace::calcSubFace
171  (
172  f,
173  points,
174  pointStatus,
175  firstFullySubmergedPoint,
176  subFacePoints_,
177  surfacePoints_,
178  faceStatus_,
179  subFaceCentre_,
180  subFaceArea_
181  );
182 
183  return faceStatus_;
184 }
185 
186 
188 (
189  const label faceI,
190  const vector& x0,
191  const vector& n0, // has to has the length of 1
192  const scalar Un0,
193  const scalar dt,
194  const scalar phi,
195  const scalar magSf
196 )
197 {
198  clearStorage();
199 
200 /* Temporarily taken out
201  // Treating rare cases where isoface normal is not calculated properly
202  if (mag(n0) < 0.5)
203  {
204  scalar alphaf = 0.0;
205  scalar waterInUpwindCell = 0.0;
206 
207  if (phi > 0 || !mesh_.isInternalFace(faceI))
208  {
209  const label upwindCell = mesh_.faceOwner()[faceI];
210  alphaf = alpha1_[upwindCell];
211  waterInUpwindCell = alphaf * mesh_.V()[upwindCell];
212  }
213  else
214  {
215  const label upwindCell = mesh_.faceNeighbour()[faceI];
216  alphaf = alpha1_[upwindCell];
217  waterInUpwindCell = alphaf * mesh_.V()[upwindCell];
218  }
219 
220  return min(alphaf * phi * dt, waterInUpwindCell);
221  }*/
222 
223  // Find sorted list of times where the isoFace will arrive at face points
224  // given initial position x0 and velocity Un0*n0
225 
226  // Get points for this face
227  const face& f = mesh_.faces()[faceI];
228  const label nPoints = f.size();
229 
230  if (mag(Un0) > 1e-12) // Note: tolerances
231  {
232  // Here we estimate time of arrival to the face points from their normal
233  // distance to the initial surface and the surface normal velocity
234 
235  for (const scalar fi : f)
236  {
237  scalar value = ((mesh_.points()[fi] - x0) & n0) / Un0;
238  if (mag(value) < 10 * SMALL)
239  {
240  value = 0;
241  }
242  pTimes_.append(value);
243  }
244 
245  scalar dVf = 0;
246 
247  // Check if pTimes changes direction more than twice when looping face
248  label nShifts = 0;
249  forAll(pTimes_, pi) // i have no clue what this is
250  {
251  const label oldEdgeSign =
252  sign(pTimes_[(pi + 1) % nPoints] - pTimes_[pi]);
253  const label newEdgeSign =
254  sign(pTimes_[(pi + 2) % nPoints] - pTimes_[(pi + 1) % nPoints]);
255 
256  if (newEdgeSign != oldEdgeSign)
257  {
258  nShifts++;
259  }
260  }
261 
262  if (nShifts == 2 || nShifts == 0)
263  {
264  dVf = phi / magSf * timeIntegratedArea(faceI, dt, magSf, Un0);
265  }
266  else if (nShifts > 2) // triangle decompose the non planar face
267  {
268  const pointField fPts(f.points(mesh_.points()));
269  pointField fPts_tri(3);
270  scalarField pTimes_tri(3);
271  fPts_tri[0] = mesh_.faceCentres()[faceI];
272  pTimes_tri[0] = ((fPts_tri[0] - x0) & n0)/Un0;
273  scalar area = 0;
274  for (label pi = 0; pi < nPoints; ++pi)
275  {
276  fPts_tri[1] = fPts[pi];
277  pTimes_tri[1] = pTimes_[pi];
278  fPts_tri[2] = fPts[(pi + 1) % nPoints];
279  pTimes_tri[2] = pTimes_[(pi + 1) % nPoints];
280  const scalar magSf_tri =
281  mag
282  (
283  0.5
284  *(fPts_tri[2] - fPts_tri[0])
285  ^(fPts_tri[1] - fPts_tri[0])
286  );
287  area += magSf_tri;
288  const scalar phi_tri = phi*magSf_tri/magSf;
289  dVf +=
290  phi_tri/magSf_tri
291  *timeIntegratedArea
292  (
293  fPts_tri,
294  pTimes_tri,
295  dt,
296  magSf_tri,
297  Un0
298  );
299  }
300  }
301  else
302  {
303  if (debug)
304  {
306  << "Warning: nShifts = " << nShifts << " on face "
307  << faceI << " with pTimes = " << pTimes_
308  << " owned by cell " << mesh_.faceOwner()[faceI]
309  << endl;
310  }
311  }
312 
313  return dVf;
314  }
315  else
316  {
317  // Un0 is almost zero and isoFace is treated as stationary
318  calcSubFace(faceI, -n0, x0);
319  const scalar alphaf = mag(subFaceArea() / magSf);
320 
321  if (debug)
322  {
324  << "Un0 is almost zero (" << Un0
325  << ") - calculating dVf on face " << faceI
326  << " using subFaceFraction giving alphaf = " << alphaf
327  << endl;
328  }
329 
330  return phi * dt * alphaf;
331  }
332 }
333 
334 
336 (
337  const label faceI,
338  const scalarField& times,
339  const scalar Un0,
340  const scalar dt,
341  const scalar phi,
342  const scalar magSf
343 )
344 {
345  clearStorage();
346 
347  label nPoints = times.size();
348 
349  {
350  // Here we estimate time of arrival to the face points from their normal
351  // distance to the initial surface and the surface normal velocity
352 
353  pTimes_.append(times);
354 
355  scalar dVf = 0;
356 
357  // Check if pTimes changes direction more than twice when looping face
358  label nShifts = 0;
359  forAll(pTimes_, pi) // i have no clue what this is
360  {
361  const label oldEdgeSign =
362  sign(pTimes_[(pi + 1) % nPoints] - pTimes_[pi]);
363  const label newEdgeSign =
364  sign(pTimes_[(pi + 2) % nPoints] - pTimes_[(pi + 1) % nPoints]);
365 
366  if (newEdgeSign != oldEdgeSign)
367  {
368  ++nShifts;
369  }
370  }
371 
372  if (nShifts == 2)
373  {
374  dVf = phi/magSf*timeIntegratedArea(faceI, dt, magSf, Un0);
375  }
376  // not possible to decompose face
377  return dVf;
378  }
379 }
380 
381 
383 (
384  const pointField& fPts,
385  const scalarField& pTimes,
386  const scalar dt,
387  const scalar magSf,
388  const scalar Un0
389 )
390 {
391  // Initialise time integrated area returned by this function
392  scalar tIntArea = 0.0;
393 
394  // Finding ordering of vertex points
395  const labelList order(Foam::sortedOrder(pTimes));
396  const scalar firstTime = pTimes[order.first()];
397  const scalar lastTime = pTimes[order.last()];
398 
399  // Dealing with case where face is not cut by surface during time interval
400  // [0,dt] because face was already passed by surface
401  if (lastTime <= 0)
402  {
403  // If all face cuttings were in the past and cell is filling up (Un0>0)
404  // then face must be full during whole time interval
405  tIntArea = magSf * dt * pos0(Un0);
406  return tIntArea;
407  }
408 
409  // Dealing with case where face is not cut by surface during time interval
410  // [0, dt] because dt is too small for surface to reach closest face point
411  if (firstTime >= dt)
412  {
413  // If all cuttings are in the future but non of them within [0,dt] then
414  // if cell is filling up (Un0 > 0) face must be empty during whole time
415  // interval
416  tIntArea = magSf * dt * (1 - pos0(Un0));
417  return tIntArea;
418  }
419 
420  // If we reach this point in the code some part of the face will be swept
421  // during [tSmall, dt-tSmall]. However, it may be the case that there are no
422  // vertex times within the interval. This will happen sometimes for small
423  // time steps where both the initial and the final face-interface
424  // intersection line (FIIL) will be along the same two edges.
425 
426  // Face-interface intersection line (FIIL) to be swept across face
427  DynamicList<point> FIIL(3);
428  // Submerged area at beginning of each sub time interval time
429  scalar initialArea = 0.0;
430  // Running time keeper variable for the integration process
431  scalar time = 0.0;
432 
433  // Special treatment of first sub time interval
434  if (firstTime > 0)
435  {
436  // If firstTime > 0 the face is uncut in the time interval
437  // [0, firstTime] and hence fully submerged in fluid A or B.
438  // If Un0 > 0 cell is filling up and it must initially be empty.
439  // If Un0 < 0 cell must initially be full(y immersed in fluid A).
440  time = firstTime;
441  initialArea = magSf * (1.0 - pos0(Un0));
442  tIntArea = initialArea * time;
443  cutPoints(fPts, pTimes, time, FIIL);
444  }
445  else
446  {
447  // If firstTime <= 0 then face is initially cut and we must
448  // calculate the initial submerged area and FIIL:
449  time = 0.0;
450  // Note: calcSubFace assumes well-defined 2-point FIIL!!!!
451  // calcSubFace(fPts, -sign(Un0)*pTimes, time);
452  // calcSubFace(fPts, -sign(Un0)*pTimes, time)
453  calcSubFace(face(identity(pTimes.size())), fPts, pTimes, time);
454  initialArea = mag(subFaceArea());
455  cutPoints(fPts, pTimes, time, FIIL);
456  }
457 
458  // Making sorted array of all vertex times that are between max(0,firstTime)
459  // and dt and further than tSmall from the previous time.
460  DynamicList<scalar> sortedTimes(pTimes.size());
461  {
462  scalar prevTime = time;
463  const scalar tSmall = max(1e-6*dt, 10*SMALL);
464 
465  for (const scalar timeI : order)
466  {
467  if (timeI > prevTime + tSmall && timeI <= dt)
468  {
469  sortedTimes.append(timeI);
470  prevTime = timeI;
471  }
472  }
473  }
474 
475  // Sweeping all quadrilaterals corresponding to the intervals defined above
476  for (const scalar newTime : sortedTimes)
477  {
478  // New face-interface intersection line
479  DynamicList<point> newFIIL(3);
480  cutPoints(fPts, pTimes, newTime, newFIIL);
481 
482  // quadrilateral area coefficients
483  scalar alpha = 0, beta = 0;
484  quadAreaCoeffs(FIIL, newFIIL, alpha, beta);
485  // Integration of area(t) = A*t^2+B*t from t = 0 to 1
486  tIntArea += (newTime - time) *
487  (initialArea + sign(Un0) * (alpha/3.0 + 0.5*beta));
488  // Adding quad area to submerged area
489  initialArea += sign(Un0)*(alpha + beta);
490 
491  FIIL = newFIIL;
492  time = newTime;
493  }
494 
495  // Special treatment of last time interval
496  if (lastTime > dt)
497  {
498  // FIIL will end up cutting the face at dt
499  // New face-interface intersection line
500  DynamicList<point> newFIIL(3);
501  cutPoints(fPts, pTimes, dt, newFIIL);
502 
503  // quadrilateral area coefficients
504  scalar alpha = 0, beta = 0;
505  quadAreaCoeffs(FIIL, newFIIL, alpha, beta);
506  // Integration of area(t) = A*t^2+B*t from t = 0 to 1
507  tIntArea += (dt - time) *
508  (initialArea + sign(Un0)*(alpha / 3.0 + 0.5 * beta));
509  }
510  else
511  {
512  // FIIL will leave the face at lastTime and face will be fully in fluid
513  // A or fluid B in the time interval from lastTime to dt.
514  tIntArea += magSf*(dt - lastTime)*pos0(Un0);
515  }
516 
517  return tIntArea;
518 }
519 
520 
521 void Foam::cutFaceAdvect::isoFacesToFile
522 (
523  const DynamicList<List<point>>& faces,
524  const word& filNam,
525  const word& filDir
526 ) const
527 {
528  // Writing isofaces to vtk file for inspection in paraview
529 
530  fileName outputFile(filDir/(filNam + ".vtk"));
531 
532  mkDir(filDir);
533  Info<< "Writing file: " << outputFile << endl;
534 
535  OFstream os(outputFile);
536  os << "# vtk DataFile Version 2.0" << nl
537  << filNam << nl
538  << "ASCII" << nl
539  << "DATASET POLYDATA" << nl;
540 
541  label nPoints{0};
542  for (const List<point>& f : faces)
543  {
544  nPoints += f.size();
545  }
546 
547  os << "POINTS " << nPoints << " float" << nl;
548  for (const List<point>& f : faces)
549  {
550  for (const point& p : f)
551  {
552  os << p.x() << ' ' << p.y() << ' ' << p.z() << nl;
553  }
554  }
555 
556  os << "POLYGONS "
557  << faces.size() << ' ' << (nPoints + faces.size()) << nl;
558 
559  label pointi = 0;
560  for (const List<point>& f : faces)
561  {
562  label endp = f.size();
563  os << endp;
564 
565  endp += pointi;
566 
567  while (pointi < endp)
568  {
569  os << ' ' << pointi;
570  ++pointi;
571  }
572  os << nl;
573  }
574 }
575 
576 
578 (
579  const label faceI,
580  const label sign,
581  const scalar cutValue
582 )
583 {
584  // clearStorage();
585  const face& f = mesh_.faces()[faceI];
586  label inLiquid = 0;
587  label firstFullySubmergedPoint = -1;
588 
589  // Loop face and calculate pointStatus
590  forAll(f, i)
591  {
592  scalar value = (sign * pTimes_[i] - cutValue);
593 
594  if (mag(value) < 10 * SMALL)
595  {
596  value = 0;
597  }
598  pointStatus_.append(value);
599  if (pointStatus_[i] > 10 * SMALL)
600  {
601  inLiquid++;
602  if (firstFullySubmergedPoint == -1)
603  {
604  firstFullySubmergedPoint = i;
605  }
606  }
607  }
608 
609  if (inLiquid == f.size()) // fluid face
610  {
611  faceStatus_ = -1;
612  subFaceCentre_ = mesh_.faceCentres()[faceI];
613  subFaceArea_ = mesh_.faceAreas()[faceI];
614  return faceStatus_;
615  }
616  else if (inLiquid == 0) // gas face
617  {
618  faceStatus_ = 1;
619  subFaceCentre_ = Zero;
620  subFaceArea_ = Zero;
621  return faceStatus_;
622  }
623 
625  (
626  faceI,
627  pointStatus_,
628  firstFullySubmergedPoint,
629  subFacePoints_,
630  surfacePoints_,
631  faceStatus_,
632  subFaceCentre_,
633  subFaceArea_
634  );
635 
636  return faceStatus_;
637 }
638 
639 
641 (
642  const label faceI,
643  const scalar dt,
644  const scalar magSf,
645  const scalar Un0
646 )
647 {
648  // Initialise time integrated area returned by this function
649  scalar tIntArea = 0.0;
650 
651  // Finding ordering of vertex points
652  const labelList order(Foam::sortedOrder(pTimes_));
653  const scalar firstTime = pTimes_[order.first()];
654  const scalar lastTime = pTimes_[order.last()];
655 
656  // Dealing with case where face is not cut by surface during time interval
657  // [0,dt] because face was already passed by surface
658  if (lastTime <= 0)
659  {
660  // If all face cuttings were in the past and cell is filling up (Un0>0)
661  // then face must be full during whole time interval
662  tIntArea = magSf* dt * pos0(Un0);
663  return tIntArea;
664  }
665 
666  // Dealing with case where face is not cut by surface during time interval
667  // [0, dt] because dt is too small for surface to reach closest face point
668  if (firstTime >= dt)
669  {
670  // If all cuttings are in the future but non of them within [0,dt] then
671  // if cell is filling up (Un0 > 0) face must be empty during whole time
672  // interval
673  tIntArea = magSf * dt * (1 - pos0(Un0));
674  return tIntArea;
675  }
676 
677  // If we reach this point in the code some part of the face will be swept
678  // during [tSmall, dt-tSmall]. However, it may be the case that there are no
679  // vertex times within the interval. This will happen sometimes for small
680  // time steps where both the initial and the final face-interface
681  // intersection line (FIIL) will be along the same two edges.
682 
683  // Face-interface intersection line (FIIL) to be swept across face
684  DynamicList<point> FIIL(3);
685  // Submerged area at beginning of each sub time interval time
686  scalar initialArea = 0.0;
687  // Running time keeper variable for the integration process
688  scalar time = 0.0;
689 
690  // Special treatment of first sub time interval
691  if (firstTime > 0)
692  {
693  // If firstTime > 0 the face is uncut in the time interval
694  // [0, firstTime] and hence fully submerged in fluid A or B.
695  // If Un0 > 0 cell is filling up and it must initially be empty.
696  // If Un0 < 0 cell must initially be full(y immersed in fluid A).
697  time = firstTime;
698  initialArea = magSf * (1.0 - pos0(Un0));
699  tIntArea = initialArea * time;
700  cutPoints(faceI, time, FIIL);
701  }
702  else
703  {
704  // If firstTime <= 0 then face is initially cut and we must
705  // calculate the initial submerged area and FIIL:
706  time = 0.0;
707  // Note: calcSubFace assumes well-defined 2-point FIIL!!!!
708  calcSubFace(faceI, -sign(Un0), time);
709  initialArea = mag(subFaceArea());
710  cutPoints(faceI, time, FIIL);
711  }
712 
713  // Making sorted array of all vertex times that are between max(0,firstTime)
714  // and dt and further than tSmall from the previous time.
715  DynamicList<scalar> sortedTimes(pTimes_.size());
716  {
717  scalar prevTime = time;
718  const scalar tSmall = max(1e-6*dt, 10*SMALL);
719  for (const label oI : order)
720  {
721  const scalar timeI = pTimes_[oI];
722  if (timeI > prevTime + tSmall && timeI <= dt)
723  {
724  sortedTimes.append(timeI);
725  prevTime = timeI;
726  }
727  }
728  }
729 
730  // Sweeping all quadrilaterals corresponding to the intervals defined above
731  for (const scalar newTime : sortedTimes)
732  {
733  // New face-interface intersection line
734  DynamicList<point> newFIIL(3);
735  cutPoints(faceI, newTime, newFIIL);
736 
737  // quadrilateral area coefficients
738  scalar alpha = 0, beta = 0;
739 
740  quadAreaCoeffs(FIIL, newFIIL, alpha, beta);
741  // Integration of area(t) = A*t^2+B*t from t = 0 to 1
742  tIntArea +=
743  (newTime - time)
744  * (initialArea + sign(Un0)
745  * (alpha / 3.0 + 0.5 * beta));
746  // Adding quad area to submerged area
747  initialArea += sign(Un0) * (alpha + beta);
748 
749  FIIL = newFIIL;
750  time = newTime;
751  }
752 
753  // Special treatment of last time interval
754  if (lastTime > dt)
755  {
756  // FIIL will end up cutting the face at dt
757  // New face-interface intersection line
758  DynamicList<point> newFIIL(3);
759  cutPoints(faceI, dt, newFIIL);
760 
761  // quadrilateral area coefficients
762  scalar alpha = 0, beta = 0;
763  quadAreaCoeffs(FIIL, newFIIL, alpha, beta);
764  // Integration of area(t) = A*t^2+B*t from t = 0 to 1
765  tIntArea +=
766  (dt - time)
767  * (initialArea + sign(Un0) * (alpha / 3.0 + 0.5 * beta));
768  }
769  else
770  {
771  // FIIL will leave the face at lastTime and face will be fully in fluid
772  // A or fluid B in the time interval from lastTime to dt.
773  tIntArea += magSf * (dt - lastTime) * pos0(Un0);
774  }
775 
776  return tIntArea;
777 }
778 
779 
781 (
782  const DynamicList<point>& pf0,
783  const DynamicList<point>& pf1,
784  scalar& alpha,
785  scalar& beta
786 ) const
787 {
788  // Number of points in provided face-interface intersection lines
789  const label np0 = pf0.size();
790  const label np1 = pf1.size();
791 
792  // quad area coeffs such that area(t) = alpha*t^2 + beta*t.
793  // With time interval normalised, we have full quadArea = alpha + beta
794  // and time integrated quad area = alpha/3 + beta/2;
795  alpha = 0.0;
796  beta = 0.0;
797 
798  if (np0 && np1)
799  {
800  // Initialising quadrilateral vertices A, B, C and D
801  vector A(pf0[0]);
802  vector C(pf1[0]);
803  vector B(pf0[0]);
804  vector D(pf1[0]);
805 
806  if (np0 == 2)
807  {
808  B = pf0[1];
809  }
810  else if (np0 > 2)
811  {
812  WarningInFunction << "Vertex face was cut at pf0 = " << pf0 << endl;
813  }
814 
815  if (np1 == 2)
816  {
817  D = pf1[1];
818  }
819  else if (np1 > 2)
820  {
821  WarningInFunction << "Vertex face was cut at pf1 = " << pf1 << endl;
822  }
823 
824  // Swapping pf1 points if pf0 and pf1 point in same general direction
825  // (because we want a quadrilateral ABCD where pf0 = AB and pf1 = CD)
826  if (((B - A) & (D - C)) > 0)
827  {
828  vector tmp = D;
829  D = C;
830  C = tmp;
831  }
832 
833  // Defining local coordinates (xhat, yhat) for area integration of swept
834  // quadrilateral ABCD such that A = (0,0), B = (Bx,0), C = (Cx,Cy) and
835  // D = (Dx,Dy) with Cy = 0 and Dy > 0.
836 
837  const scalar Bx = mag(B - A);
838 
839  vector xhat(Zero);
840  if (Bx > 10 * SMALL)
841  {
842  // If |AB| > 0 ABCD we use AB to define xhat
843  xhat = (B - A) / mag(B - A);
844  }
845  else if (mag(C - D) > 10 * SMALL)
846  {
847  // If |AB| ~ 0 ABCD is a triangle ACD and we use CD for xhat
848  xhat = (C - D) / mag(C - D);
849  }
850  else
851  {
852  return;
853  }
854 
855  // Defining vertical axis in local coordinates
856  vector yhat = D - A;
857  yhat -= ((yhat & xhat) * xhat);
858 
859  if (mag(yhat) > 10 * SMALL)
860  {
861  yhat /= mag(yhat);
862 
863  const scalar Cx = (C - A) & xhat;
864  const scalar Cy = mag((C - A) & yhat);
865  const scalar Dx = (D - A) & xhat;
866  const scalar Dy = mag((D - A) & yhat);
867 
868  // area = ((Cx - Bx)*Dy - Dx*Cy)/6.0 + 0.25*Bx*(Dy + Cy);
869  alpha = 0.5 * ((Cx - Bx) * Dy - Dx * Cy);
870  beta = 0.5 * Bx * (Dy + Cy);
871  }
872  }
873  else
874  {
876  << "Vertex face was cut at " << pf0 << " and at "
877  << pf1 << endl;
878  }
879 }
880 
881 
883 (
884  const label faceI,
885  const scalar f0,
886  DynamicList<point>& cutPoints
887 )
888 {
889  const face& f = mesh_.faces()[faceI];
890  const label nPoints = f.size();
891  scalar f1(pTimes_[0]);
892 
893  // Snapping vertex value to f0 if very close (needed for 2D cases)
894  if (mag(f1 - f0) < 10 * SMALL)
895  {
896  f1 = f0;
897  }
898 
899  forAll(f, pi)
900  {
901  label pi2 = (pi + 1) % nPoints;
902  scalar f2 = pTimes_[pi2];
903 
904  // Snapping vertex value
905  if (mag(f2 - f0) < 10 * SMALL)
906  {
907  f2 = f0;
908  }
909 
910  if ((f1 < f0 && f2 > f0) || (f1 > f0 && f2 < f0))
911  {
912  const scalar s = (f0 - f1) / (f2 - f1);
913  cutPoints.append
914  (
915  mesh_.points()[f[pi]]
916  + s*(mesh_.points()[f[pi2]] - mesh_.points()[f[pi]])
917  );
918  }
919  else if (f1 == f0)
920  {
921  cutPoints.append(mesh_.points()[f[pi]]);
922  }
923  f1 = f2;
924  }
925 
926  if (cutPoints.size() > 2)
927  {
929  << "cutPoints = " << cutPoints
930  << " for pts = " << f.points(mesh_.points())
931  << ", f - f0 = " << f - f0 << " and f0 = " << f0
932  << endl;
933  }
934 }
935 
936 
938 (
939  const pointField& pts,
940  const scalarField& f,
941  const scalar f0,
942  DynamicList<point>& cutPoints
943 )
944 {
945  const label nPoints = pts.size();
946  scalar f1(f[0]);
947 
948  // Snapping vertex value to f0 if very close (needed for 2D cases)
949  if (mag(f1 - f0) < 10 * SMALL)
950  {
951  f1 = f0;
952  }
953 
954  forAll(pts, pi)
955  {
956  label pi2 = (pi + 1) % nPoints;
957  scalar f2 = f[pi2];
958 
959  // Snapping vertex value
960  if (mag(f2 - f0) < 10 * SMALL)
961  {
962  f2 = f0;
963  }
964 
965  if ((f1 < f0 && f2 > f0) || (f1 > f0 && f2 < f0))
966  {
967  const scalar s = (f0 - f1) / (f2 - f1);
968  cutPoints.append(pts[pi] + s * (pts[pi2] - pts[pi]));
969  }
970  else if (f1 == f0)
971  {
972  cutPoints.append(pts[pi]);
973  }
974  f1 = f2;
975  }
976 
977  if (cutPoints.size() > 2)
978  {
980  << "cutPoints = " << cutPoints << " for pts = " << pts
981  << ", f - f0 = " << f - f0 << " and f0 = " << f0
982  << endl;
983  }
984 }
985 
986 
988 {
989  return subFaceCentre_;
990 }
991 
992 
994 {
995  return subFaceArea_;
996 }
997 
998 
1000 {
1001  return subFacePoints_;
1002 }
1003 
1004 
1006 {
1007  return surfacePoints_;
1008 }
1009 
1010 
1012 {
1013  subFaceCentre_ = Zero;
1014  subFaceArea_ = Zero;
1015  subFacePoints_.clear();
1016  surfacePoints_.clear();
1017  pointStatus_.clear();
1018  pTimes_.clear();
1019  weight_.clear();
1020  faceStatus_ = -1;
1021 }
1022 
1023 
1024 // ************************************************************************* //
Foam::expressions::patchExpr::debug
int debug
Static debugging option.
cutFaceAdvect.H
p
volScalarField & p
Definition: createFieldRefs.H:8
Foam::word
A class for handling words, derived from Foam::string.
Definition: word.H:62
Foam::fileName
A class for handling file names.
Definition: fileName.H:69
s
gmvFile<< "tracers "<< particles.size()<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().x()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().y()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
Definition: gmvOutputSpray.H:25
Foam::cutFaceAdvect::timeIntegratedFaceFlux
scalar timeIntegratedFaceFlux(const label faceI, const vector &x0, const vector &n0, const scalar Un0, const scalar dt, const scalar phi, const scalar magSf)
Calculate time integrated flux for a face.
Definition: cutFaceAdvect.C:188
Foam::cutFaceAdvect::clearStorage
void clearStorage()
Resets internal variables.
Definition: cutFaceAdvect.C:1011
Foam::tmp
A class for managing temporary objects.
Definition: PtrList.H:61
Foam::Zero
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:131
Foam::DynamicList< point >
Foam::constant::atomic::alpha
const dimensionedScalar alpha
Fine-structure constant: default SI units: [].
Definition: readThermalProperties.H:212
Foam::cutFaceAdvect::quadAreaCoeffs
void quadAreaCoeffs(const DynamicPointList &pf0, const DynamicPointList &pf1, scalar &quadArea, scalar &intQuadArea) const
For face with vertices p calculate its area and integrated area.
Definition: cutFaceAdvect.C:781
B
static const Foam::dimensionedScalar B("", Foam::dimless, 18.678)
Foam::cutFaceAdvect::subFacePoints
const DynamicList< point > & subFacePoints() const
Returns the cut edge of the cutted face.
Definition: cutFaceAdvect.C:999
A
static const Foam::dimensionedScalar A("", Foam::dimPressure, 611.21)
Foam::cutFaceAdvect::subFaceArea
const vector & subFaceArea() const
Returns area vector of cutted face.
Definition: cutFaceAdvect.C:993
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:350
Foam::pos0
dimensionedScalar pos0(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:188
alpha1
const volScalarField & alpha1
Definition: setRegionFluidFields.H:8
Foam::cutFaceAdvect::cutFaceAdvect
cutFaceAdvect(const fvMesh &mesh, const volScalarField &alpha1)
Construct from fvMesh and a scalarField.
Definition: cutFaceAdvect.C:36
Foam::sign
dimensionedScalar sign(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:166
Foam::cutFaceAdvect::cutPoints
void cutPoints(const label faceI, const scalar f0, DynamicList< point > &cutPoints)
Get cutPoints of face.
Definition: cutFaceAdvect.C:883
C
volScalarField & C
Definition: readThermalProperties.H:102
forAll
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:296
Foam::cutFaceAdvect::calcSubFace
label calcSubFace(const label faceI, const vector &normal, const vector &base)
Calculates cut centre and cut area (plicReconstruction)
Definition: cutFaceAdvect.C:60
OFstream.H
nPoints
label nPoints
Definition: gmvOutputHeader.H:2
Foam::Field< vector >
Foam::Info
messageStream Info
Information stream (uses stdout - output is on the master only)
Foam::DynamicList::append
DynamicList< T, SizeMin > & append(const T &val)
Append an element to the end of this list.
Definition: DynamicListI.H:474
beta
dimensionedScalar beta("beta", dimless/dimTemperature, laminarTransport)
Foam::cutFaceAdvect::subFaceCentre
const point & subFaceCentre() const
Returns centre of cutted face.
Definition: cutFaceAdvect.C:987
phi
surfaceScalarField & phi
Definition: setRegionFluidFields.H:8
Foam::max
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:47
mesh
dynamicFvMesh & mesh
Definition: createDynamicFvMesh.H:6
Foam::cutFaceAdvect::surfacePoints
const DynamicList< point > & surfacePoints() const
Returns point of the cutted face in sorted order.
Definition: cutFaceAdvect.C:1005
Foam::fvMesh
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:83
Foam::cutFaceAdvect::timeIntegratedArea
scalar timeIntegratedArea(const label faceI, const scalar dt, const scalar magSf, const scalar Un0)
Calculate time integrated area for a face.
Definition: cutFaceAdvect.C:641
Foam::OFstream
Output to file stream, using an OSstream.
Definition: OFstream.H:53
Foam::cutFace
Base class for cutting a face, faceI, of an fvMesh, mesh_, at its intersections.
Definition: cutFace.H:59
Foam::constant::mathematical::pi
constexpr scalar pi(M_PI)
Foam::nl
constexpr char nl
Definition: Ostream.H:385
f
labelList f(nPoints)
Foam::cutFace::calcSubFace
void calcSubFace(const label faceI, const scalarList &pointStatus, label firstFullySubmergedPoint, DynamicList< point > &subFacePoints, DynamicList< point > &surfacePoints, label &faceStatus, vector &subFaceCentre, vector &subFaceArea)
Definition: cutFace.C:39
Foam::Vector< scalar >
Foam::List< scalar >
Foam::mag
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
points
const pointField & points
Definition: gmvOutputHeader.H:1
Foam::constant::electromagnetic::e
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
Foam::identity
labelList identity(const label len, label start=0)
Create identity map of the given length with (map[i] == i)
Definition: labelList.C:38
D
const dimensionedScalar & D
Definition: solveBulkSurfactant.H:4
Foam::fieldTypes::area
const wordList area
Standard area field types (scalar, vector, tensor, etc)
Foam::face
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:72
Foam::sortedOrder
labelList sortedOrder(const UList< T > &input)
Return the (stable) sort order for the list.
Foam::point
vector point
Point is a vector.
Definition: point.H:43
Foam::C
Graphite solid properties.
Definition: C.H:50
Foam::GeometricField< scalar, fvPatchField, volMesh >
Foam::mkDir
bool mkDir(const fileName &pathName, mode_t mode=0777)
Make a directory and return an error if it could not be created.
Definition: MSwindows.C:507
WarningInFunction
#define WarningInFunction
Report a warning using Foam::Warning.
Definition: messageStream.H:303