isoAdvection.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) 2016-2017 OpenCFD Ltd.
10  Copyright (C) 2019 Johan Roenby
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 "isoAdvection.H"
31 #include "volFields.H"
32 #include "interpolationCellPoint.H"
33 #include "volPointInterpolation.H"
34 #include "fvcSurfaceIntegrate.H"
35 #include "fvcGrad.H"
36 #include "upwind.H"
37 #include "cellSet.H"
38 #include "meshTools.H"
39 #include "OBJstream.H"
40 
41 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
42 
43 namespace Foam
44 {
45  defineTypeNameAndDebug(isoAdvection, 0);
46 }
47 
48 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
49 
50 Foam::isoAdvection::isoAdvection
51 (
53  const surfaceScalarField& phi,
54  const volVectorField& U
55 )
56 :
57  // General data
58  mesh_(alpha1.mesh()),
59  dict_(mesh_.solverDict(alpha1.name())),
60  alpha1_(alpha1),
61  alpha1In_(alpha1.ref()),
62  phi_(phi),
63  U_(U),
64  dVf_
65  (
66  IOobject
67  (
68  "dVf_",
69  mesh_.time().timeName(),
70  mesh_,
71  IOobject::NO_READ,
72  IOobject::NO_WRITE
73  ),
74  mesh_,
76  ),
77  advectionTime_(0),
78 
79  // Interpolation data
80  ap_(mesh_.nPoints()),
81 
82  // Tolerances and solution controls
83  nAlphaBounds_(dict_.lookupOrDefault<label>("nAlphaBounds", 3)),
84  isoFaceTol_(dict_.lookupOrDefault<scalar>("isoFaceTol", 1e-8)),
85  surfCellTol_(dict_.lookupOrDefault<scalar>("surfCellTol", 1e-8)),
86  gradAlphaBasedNormal_(dict_.lookupOrDefault("gradAlphaNormal", false)),
87  writeIsoFacesToFile_(dict_.lookupOrDefault("writeIsoFaces", false)),
88 
89  // Cell cutting data
90  surfCells_(label(0.2*mesh_.nCells())),
91  isoCutCell_(mesh_, ap_),
92  isoCutFace_(mesh_, ap_),
93  cellIsBounded_(mesh_.nCells(), false),
94  checkBounding_(mesh_.nCells(), false),
95  bsFaces_(label(0.2*mesh_.nBoundaryFaces())),
96  bsx0_(bsFaces_.size()),
97  bsn0_(bsFaces_.size()),
98  bsUn0_(bsFaces_.size()),
99  bsf0_(bsFaces_.size()),
100 
101  // Parallel run data
102  procPatchLabels_(mesh_.boundary().size()),
103  surfaceCellFacesOnProcPatches_(0)
104 {
107 
108  // Prepare lists used in parallel runs
109  if (Pstream::parRun())
110  {
111  // Force calculation of required demand driven data (else parallel
112  // communication may crash)
113  mesh_.cellCentres();
114  mesh_.cellVolumes();
115  mesh_.faceCentres();
116  mesh_.faceAreas();
117  mesh_.magSf();
118  mesh_.boundaryMesh().patchID();
119  mesh_.cellPoints();
120  mesh_.cellCells();
121  mesh_.cells();
122 
123  // Get boundary mesh and resize the list for parallel comms
124  const polyBoundaryMesh& patches = mesh_.boundaryMesh();
125 
126  surfaceCellFacesOnProcPatches_.resize(patches.size());
127 
128  // Append all processor patch labels to the list
129  forAll(patches, patchi)
130  {
131  if
132  (
133  isA<processorPolyPatch>(patches[patchi])
134  && patches[patchi].size() > 0
135  )
136  {
137  procPatchLabels_.append(patchi);
138  }
139  }
140  }
141 }
142 
143 
144 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
145 
146 void Foam::isoAdvection::timeIntegratedFlux()
147 {
148  // Get time step
149  const scalar dt = mesh_.time().deltaTValue();
150 
151  // Create object for interpolating velocity to isoface centres
152  interpolationCellPoint<vector> UInterp(U_);
153 
154  // For each downwind face of each surface cell we "isoadvect" to find dVf
155  label nSurfaceCells = 0;
156 
157  // Clear out the data for re-use and reset list containing information
158  // whether cells could possibly need bounding
159  clearIsoFaceData();
160 
161  // Get necessary references
162  const scalarField& phiIn = phi_.primitiveField();
163  const scalarField& magSfIn = mesh_.magSf().primitiveField();
164  scalarField& dVfIn = dVf_.primitiveFieldRef();
165 
166  // Get necessary mesh data
167  const cellList& cellFaces = mesh_.cells();
168  const labelList& own = mesh_.faceOwner();
169  const labelList& nei = mesh_.faceNeighbour();
170  const labelListList& cellCells = mesh_.cellCells();
171 
172  // Calculate alpha vertex values, ap_, or cell normals (used to get
173  // interface-vertex distance function if gradAlphaBasedNormal_)
174  volVectorField cellNormals("cellN", fvc::grad(alpha1_));
175  if (gradAlphaBasedNormal_)
176  {
177  // Calculate gradient of alpha1 and normalise and smoothen it.
178  normaliseAndSmooth(cellNormals);
179  }
180  else
181  {
182  // Interpolating alpha1 cell centre values to mesh points (vertices)
183  ap_ = volPointInterpolation::New(mesh_).interpolate(alpha1_);
184  }
185 
186  // Storage for isoFace points. Only used if writeIsoFacesToFile_
187  DynamicList<List<point>> isoFacePts;
188 
189  // Loop through all cells
190  forAll(alpha1In_, celli)
191  {
192  // If not a surface cell continue to next cell
193  if (!isASurfaceCell(celli)) continue;
194 
195  // This is a surface cell, increment counter, append and mark cell
196  // Note: We also have the cellStatus below where the cell might not have
197  // an isoface. So maybe the counter and append should be put there.
198  nSurfaceCells++;
199  surfCells_.append(celli);
200  checkBounding_[celli] = true;
201 
202  DebugInfo
203  << "\n------------ Cell " << celli << " with alpha1 = "
204  << alpha1In_[celli] << " and 1-alpha1 = "
205  << 1.0 - alpha1In_[celli] << " ------------"
206  << endl;
207 
208  if (gradAlphaBasedNormal_)
209  {
210  vectorField& cellNormalsIn = cellNormals.primitiveFieldRef();
211  setCellVertexValues(celli, cellNormalsIn);
212  }
213 
214  // Calculate isoFace centre x0, normal n0 at time t
215 
216  // Calculate cell status (-1: cell is fully below the isosurface, 0:
217  // cell is cut, 1: cell is fully above the isosurface)
218  label maxIter = 100; // NOTE: make it a debug switch
219  label cellStatus = isoCutCell_.vofCutCell
220  (
221  celli,
222  alpha1In_[celli],
223  isoFaceTol_,
224  maxIter
225  );
226 
227  // If cell is not cut move on to next cell
228  if (cellStatus != 0) continue;
229 
230  // If cell is cut calculate isoface unit normal
231  const scalar f0(isoCutCell_.isoValue());
232  const point& x0(isoCutCell_.isoFaceCentre());
233  vector n0(isoCutCell_.isoFaceArea());
234  n0 /= (mag(n0));
235 
236  if (writeIsoFacesToFile_ && mesh_.time().writeTime())
237  {
238  isoFacePts.append(isoCutCell_.isoFacePoints());
239  }
240 
241  // Get the speed of the isoface by interpolating velocity and
242  // dotting it with isoface unit normal
243  const scalar Un0 = UInterp.interpolate(x0, celli) & n0;
244 
245  DebugInfo
246  << "calcIsoFace gives initial surface: \nx0 = " << x0
247  << ", \nn0 = " << n0 << ", \nf0 = " << f0 << ", \nUn0 = "
248  << Un0 << endl;
249 
250  // Estimate time integrated flux through each downwind face
251  // Note: looping over all cell faces - in reduced-D, some of
252  // these faces will be on empty patches
253  const cell& celliFaces = cellFaces[celli];
254  forAll(celliFaces, fi)
255  {
256  const label facei = celliFaces[fi];
257 
258  if (mesh_.isInternalFace(facei))
259  {
260  bool isDownwindFace = false;
261  label otherCell = -1;
262 
263  if (celli == own[facei])
264  {
265  if (phiIn[facei] > 10*SMALL)
266  {
267  isDownwindFace = true;
268  }
269 
270  otherCell = nei[facei];
271  }
272  else
273  {
274  if (phiIn[facei] < -10*SMALL)
275  {
276  isDownwindFace = true;
277  }
278 
279  otherCell = own[facei];
280  }
281 
282  if (isDownwindFace)
283  {
284  dVfIn[facei] = isoCutFace_.timeIntegratedFaceFlux
285  (
286  facei,
287  x0,
288  n0,
289  Un0,
290  f0,
291  dt,
292  phiIn[facei],
293  magSfIn[facei]
294  );
295  }
296 
297  // We want to check bounding of neighbour cells to
298  // surface cells as well:
299  checkBounding_[otherCell] = true;
300 
301  // Also check neighbours of neighbours.
302  // Note: consider making it a run time selectable
303  // extension level (easily done with recursion):
304  // 0 - only neighbours
305  // 1 - neighbours of neighbours
306  // 2 - ...
307  // Note: We will like all point neighbours to interface cells to
308  // be checked. Especially if the interface leaves a cell during
309  // a time step, it may enter a point neighbour which should also
310  // be treated like a surface cell. Its interface normal should
311  // somehow be inherrited from its upwind cells from which it
312  // receives the interface.
313  const labelList& nNeighbourCells = cellCells[otherCell];
314  forAll(nNeighbourCells, ni)
315  {
316  checkBounding_[nNeighbourCells[ni]] = true;
317  }
318  }
319  else
320  {
321  bsFaces_.append(facei);
322  bsx0_.append(x0);
323  bsn0_.append(n0);
324  bsUn0_.append(Un0);
325  bsf0_.append(f0);
326 
327  // Note: we must not check if the face is on the
328  // processor patch here.
329  }
330  }
331  }
332 
333  // Get references to boundary fields
334  const polyBoundaryMesh& boundaryMesh = mesh_.boundaryMesh();
335  const surfaceScalarField::Boundary& phib = phi_.boundaryField();
336  const surfaceScalarField::Boundary& magSfb = mesh_.magSf().boundaryField();
337  surfaceScalarField::Boundary& dVfb = dVf_.boundaryFieldRef();
338  const label nInternalFaces = mesh_.nInternalFaces();
339 
340  // Loop through boundary surface faces
341  forAll(bsFaces_, i)
342  {
343  // Get boundary face index (in the global list)
344  const label facei = bsFaces_[i];
345  const label patchi = boundaryMesh.patchID()[facei - nInternalFaces];
346  const label start = boundaryMesh[patchi].start();
347 
348  if (phib[patchi].size())
349  {
350  const label patchFacei = facei - start;
351  const scalar phiP = phib[patchi][patchFacei];
352 
353  if (phiP > 10*SMALL)
354  {
355  const scalar magSf = magSfb[patchi][patchFacei];
356 
357  dVfb[patchi][patchFacei] = isoCutFace_.timeIntegratedFaceFlux
358  (
359  facei,
360  bsx0_[i],
361  bsn0_[i],
362  bsUn0_[i],
363  bsf0_[i],
364  dt,
365  phiP,
366  magSf
367  );
368 
369  // Check if the face is on processor patch and append it to
370  // the list if necessary
371  checkIfOnProcPatch(facei);
372  }
373  }
374  }
375 
376  // Synchronize processor patches
377  syncProcPatches(dVf_, phi_);
378 
379  writeIsoFaces(isoFacePts);
380 
381  Info<< "Number of isoAdvector surface cells = "
382  << returnReduce(nSurfaceCells, sumOp<label>()) << endl;
383 }
384 
385 
386 void Foam::isoAdvection::setCellVertexValues
387 (
388  const label celli,
389  const vectorField& cellNormalsIn
390 )
391 {
392  const labelListList& cellPoints = mesh_.cellPoints();
393  const vectorField& cellCentres = mesh_.cellCentres();
394  const pointField& points = mesh_.points();
395  const labelList& cp = cellPoints[celli];
396  const point& cellCentre = cellCentres[celli];
397  forAll(cp, vi)
398  {
399  const point& vertex = points[cp[vi]];
400  ap_[cp[vi]] = (vertex - cellCentre) & cellNormalsIn[celli];
401  }
402 }
403 
404 
405 void Foam::isoAdvection::normaliseAndSmooth
406 (
407  volVectorField& cellN
408 )
409 {
410  const labelListList& cellPoints = mesh_.cellPoints();
411  const vectorField& cellCentres = mesh_.cellCentres();
412  const pointField& points = mesh_.points();
413 
414  vectorField& cellNIn = cellN.primitiveFieldRef();
415  cellNIn /= (mag(cellNIn) + SMALL);
416  vectorField vertexN(mesh_.nPoints(), Zero);
417  vertexN = volPointInterpolation::New(mesh_).interpolate(cellN);
418  vertexN /= (mag(vertexN) + SMALL);
419  // Interpolate vertex normals back to cells
420  forAll(cellNIn, celli)
421  {
422  const labelList& cp = cellPoints[celli];
423  vector cellNi(Zero);
424  const point& cellCentre = cellCentres[celli];
425  forAll(cp, pointI)
426  {
427  point vertex = points[cp[pointI]];
428  scalar w = 1.0/mag(vertex - cellCentre);
429  cellNi += w*vertexN[cp[pointI]];
430  }
431  cellNIn[celli] = cellNi/(mag(cellNi) + SMALL);
432  }
433 }
434 
435 
436 void Foam::isoAdvection::setDownwindFaces
437 (
438  const label celli,
439  DynamicLabelList& downwindFaces
440 ) const
441 {
443 
444  // Get necessary mesh data and cell information
445  const labelList& own = mesh_.faceOwner();
446  const cellList& cells = mesh_.cells();
447  const cell& c = cells[celli];
448 
449  downwindFaces.clear();
450 
451  // Check all faces of the cell
452  forAll(c, fi)
453  {
454  // Get face and corresponding flux
455  const label facei = c[fi];
456  const scalar phi = faceValue(phi_, facei);
457 
458  if (own[facei] == celli)
459  {
460  if (phi > 10*SMALL)
461  {
462  downwindFaces.append(facei);
463  }
464  }
465  else if (phi < -10*SMALL)
466  {
467  downwindFaces.append(facei);
468  }
469  }
470 
471  downwindFaces.shrink();
472 }
473 
474 
475 void Foam::isoAdvection::limitFluxes()
476 {
478 
479  // Get time step size
480  const scalar dt = mesh_.time().deltaT().value();
481 
482  volScalarField alphaNew(alpha1_ - fvc::surfaceIntegrate(dVf_));
483  const scalar aTol = 1.0e-12; // Note: tolerances
484  scalar maxAlphaMinus1 = gMax(alphaNew) - 1; // max(alphaNew - 1);
485  scalar minAlpha = gMin(alphaNew); // min(alphaNew);
486  const label nUndershoots = 20; // sum(neg0(alphaNew + aTol));
487  const label nOvershoots = 20; // sum(pos0(alphaNew - 1 - aTol));
488  cellIsBounded_ = false;
489 
490  Info << "isoAdvection: Before conservative bounding: min(alpha) = "
491  << minAlpha << ", max(alpha) = 1 + " << maxAlphaMinus1 << endl;
492 
493  // Loop number of bounding steps
494  for (label n = 0; n < nAlphaBounds_; n++)
495  {
496  if (maxAlphaMinus1 > aTol) // Note: tolerances
497  {
498  DebugInfo << "Bound from above... " << endl;
499 
500 // scalarField& dVfcorrected = dVf_.primitiveFieldRef();
501 
502  surfaceScalarField dVfcorrected("dVfcorrected", dVf_);
503  DynamicList<label> correctedFaces(3*nOvershoots);
504  boundFromAbove(alpha1In_, dVfcorrected, correctedFaces);
505 
506  forAll(correctedFaces, fi)
507  {
508  label facei = correctedFaces[fi];
509 
510  // Change to treat boundaries consistently
511  setFaceValue(dVf_, facei, faceValue(dVfcorrected, facei));
512  }
513 
514  syncProcPatches(dVf_, phi_);
515  }
516 
517  if (minAlpha < -aTol) // Note: tolerances
518  {
519  DebugInfo << "Bound from below... " << endl;
520 
521  scalarField alpha2(1.0 - alpha1In_);
522  surfaceScalarField dVfcorrected
523  (
524  "dVfcorrected",
525  phi_*dimensionedScalar("dt", dimTime, dt) - dVf_
526  );
527 // dVfcorrected -= dVf_; // phi_ and dVf_ have same sign and dVf_ is
528  // the portion of phi_*dt that is water.
529  // If phi_ > 0 then dVf_ > 0 and mag(phi_*dt-dVf_) < mag(phi_*dt) as
530  // it should.
531  // If phi_ < 0 then dVf_ < 0 and mag(phi_*dt-dVf_) < mag(phi_*dt) as
532  // it should.
533  DynamicList<label> correctedFaces(3*nUndershoots);
534  boundFromAbove(alpha2, dVfcorrected, correctedFaces);
535  forAll(correctedFaces, fi)
536  {
537  label facei = correctedFaces[fi];
538 
539  // Change to treat boundaries consistently
540  scalar phi = faceValue(phi_, facei);
541  scalar dVcorr = faceValue(dVfcorrected, facei);
542  setFaceValue(dVf_, facei, phi*dt - dVcorr);
543  }
544 
545  syncProcPatches(dVf_, phi_);
546  }
547 
548  if (debug)
549  {
550  // Check if still unbounded
551  scalarField alphaNew(alpha1In_ - fvc::surfaceIntegrate(dVf_)());
552  label maxAlphaMinus1 = max(alphaNew - 1);
553  scalar minAlpha = min(alphaNew);
554  label nUndershoots = sum(neg0(alphaNew + aTol));
555  label nOvershoots = sum(pos0(alphaNew - 1 - aTol));
556  Info<< "After bounding number " << n + 1 << " of time "
557  << mesh_.time().value() << ":" << endl;
558  Info<< "nOvershoots = " << nOvershoots << " with max(alphaNew-1) = "
559  << maxAlphaMinus1 << " and nUndershoots = " << nUndershoots
560  << " with min(alphaNew) = " << minAlpha << endl;
561  }
562  }
563 }
564 
565 
566 void Foam::isoAdvection::boundFromAbove
567 (
568  const scalarField& alpha1,
569  surfaceScalarField& dVf,
570  DynamicList<label>& correctedFaces
571 )
572 {
574 
575  correctedFaces.clear();
576  scalar aTol = 10*SMALL; // Note: tolerances
577 
578  const scalarField& meshV = mesh_.cellVolumes();
579  const scalar dt = mesh_.time().deltaTValue();
580 
581  DynamicList<label> downwindFaces(10);
582  DynamicList<label> facesToPassFluidThrough(downwindFaces.size());
583  DynamicList<scalar> dVfmax(downwindFaces.size());
584  DynamicList<scalar> phi(downwindFaces.size());
585 
586  // Loop through alpha cell centred field
587  forAll(alpha1, celli)
588  {
589  if (checkBounding_.test(celli))
590  {
591  const scalar Vi = meshV[celli];
592  scalar alpha1New = alpha1[celli] - netFlux(dVf, celli)/Vi;
593  scalar alphaOvershoot = alpha1New - 1.0;
594  scalar fluidToPassOn = alphaOvershoot*Vi;
595  label nFacesToPassFluidThrough = 1;
596 
597  bool firstLoop = true;
598 
599  // First try to pass surplus fluid on to neighbour cells that are
600  // not filled and to which dVf < phi*dt
601  while (alphaOvershoot > aTol && nFacesToPassFluidThrough > 0)
602  {
603  DebugInfo
604  << "\n\nBounding cell " << celli
605  << " with alpha overshooting " << alphaOvershoot
606  << endl;
607 
608  facesToPassFluidThrough.clear();
609  dVfmax.clear();
610  phi.clear();
611 
612  cellIsBounded_.set(celli);
613 
614  // Find potential neighbour cells to pass surplus phase to
615  setDownwindFaces(celli, downwindFaces);
616 
617  scalar dVftot = 0;
618  nFacesToPassFluidThrough = 0;
619 
620  forAll(downwindFaces, fi)
621  {
622  const label facei = downwindFaces[fi];
623  const scalar phif = faceValue(phi_, facei);
624  const scalar dVff = faceValue(dVf, facei);
625  const scalar maxExtraFaceFluidTrans = mag(phif*dt - dVff);
626 
627  // dVf has same sign as phi and so if phi>0 we have
628  // mag(phi_[facei]*dt) - mag(dVf[facei]) = phi_[facei]*dt
629  // - dVf[facei]
630  // If phi < 0 we have mag(phi_[facei]*dt) -
631  // mag(dVf[facei]) = -phi_[facei]*dt - (-dVf[facei]) > 0
632  // since mag(dVf) < phi*dt
633  DebugInfo
634  << "downwindFace " << facei
635  << " has maxExtraFaceFluidTrans = "
636  << maxExtraFaceFluidTrans << endl;
637 
638  if (maxExtraFaceFluidTrans/Vi > aTol)
639  {
640 // if (maxExtraFaceFluidTrans/Vi > aTol &&
641 // mag(dVfIn[facei])/Vi > aTol) //Last condition may be
642 // important because without this we will flux through uncut
643 // downwind faces
644  facesToPassFluidThrough.append(facei);
645  phi.append(phif);
646  dVfmax.append(maxExtraFaceFluidTrans);
647  dVftot += mag(phif*dt);
648  }
649  }
650 
651  DebugInfo
652  << "\nfacesToPassFluidThrough: "
653  << facesToPassFluidThrough << ", dVftot = "
654  << dVftot << " m3 corresponding to dalpha = "
655  << dVftot/Vi << endl;
656 
657  forAll(facesToPassFluidThrough, fi)
658  {
659  const label facei = facesToPassFluidThrough[fi];
660  scalar fluidToPassThroughFace =
661  fluidToPassOn*mag(phi[fi]*dt)/dVftot;
662 
663  nFacesToPassFluidThrough +=
664  pos0(dVfmax[fi] - fluidToPassThroughFace);
665 
666  fluidToPassThroughFace =
667  min(fluidToPassThroughFace, dVfmax[fi]);
668 
669  scalar dVff = faceValue(dVf, facei);
670  dVff += sign(phi[fi])*fluidToPassThroughFace;
671  setFaceValue(dVf, facei, dVff);
672 
673  if (firstLoop)
674  {
675  checkIfOnProcPatch(facei);
676  correctedFaces.append(facei);
677  }
678  }
679 
680  firstLoop = false;
681  alpha1New = alpha1[celli] - netFlux(dVf, celli)/Vi;
682  alphaOvershoot = alpha1New - 1.0;
683  fluidToPassOn = alphaOvershoot*Vi;
684 
685  DebugInfo
686  << "\nNew alpha for cell " << celli << ": "
687  << alpha1New << endl;
688  }
689  }
690  }
691 
692  DebugInfo << "correctedFaces = " << correctedFaces << endl;
693 }
694 
695 
696 Foam::scalar Foam::isoAdvection::netFlux
697 (
698  const surfaceScalarField& dVf,
699  const label celli
700 ) const
701 {
702  scalar dV = 0;
703 
704  // Get face indices
705  const cell& c = mesh_.cells()[celli];
706 
707  // Get mesh data
708  const labelList& own = mesh_.faceOwner();
709 
710  forAll(c, fi)
711  {
712  const label facei = c[fi];
713  const scalar dVff = faceValue(dVf, facei);
714 
715  if (own[facei] == celli)
716  {
717  dV += dVff;
718  }
719  else
720  {
721  dV -= dVff;
722  }
723  }
724 
725  return dV;
726 }
727 
728 
729 void Foam::isoAdvection::syncProcPatches
730 (
731  surfaceScalarField& dVf,
732  const surfaceScalarField& phi
733 )
734 {
735  const polyBoundaryMesh& patches = mesh_.boundaryMesh();
736 
737  if (Pstream::parRun())
738  {
739  PstreamBuffers pBufs(Pstream::commsTypes::nonBlocking);
740 
741  // Send
742  forAll(procPatchLabels_, i)
743  {
744  const label patchi = procPatchLabels_[i];
745 
746  const processorPolyPatch& procPatch =
747  refCast<const processorPolyPatch>(patches[patchi]);
748 
749  UOPstream toNbr(procPatch.neighbProcNo(), pBufs);
750  const scalarField& pFlux = dVf.boundaryField()[patchi];
751 
752  const List<label>& surfCellFacesOnProcPatch =
753  surfaceCellFacesOnProcPatches_[patchi];
754 
755  const UIndirectList<scalar> dVfPatch
756  (
757  pFlux,
758  surfCellFacesOnProcPatch
759  );
760 
761  toNbr << surfCellFacesOnProcPatch << dVfPatch;
762  }
763 
764  pBufs.finishedSends();
765 
766 
767  // Receive and combine
768  forAll(procPatchLabels_, patchLabeli)
769  {
770  const label patchi = procPatchLabels_[patchLabeli];
771 
772  const processorPolyPatch& procPatch =
773  refCast<const processorPolyPatch>(patches[patchi]);
774 
775  UIPstream fromNeighb(procPatch.neighbProcNo(), pBufs);
776  List<label> faceIDs;
777  List<scalar> nbrdVfs;
778 
779  fromNeighb >> faceIDs >> nbrdVfs;
780 
781  if (debug)
782  {
783  Pout<< "Received at time = " << mesh_.time().value()
784  << ": surfCellFacesOnProcPatch = " << faceIDs << nl
785  << "Received at time = " << mesh_.time().value()
786  << ": dVfPatch = " << nbrdVfs << endl;
787  }
788 
789  // Combine fluxes
790  scalarField& localFlux = dVf.boundaryFieldRef()[patchi];
791 
792  forAll(faceIDs, i)
793  {
794  const label facei = faceIDs[i];
795  localFlux[facei] = - nbrdVfs[i];
796  if (debug && mag(localFlux[facei] + nbrdVfs[i]) > 10*SMALL)
797  {
798  Pout<< "localFlux[facei] = " << localFlux[facei]
799  << " and nbrdVfs[i] = " << nbrdVfs[i]
800  << " for facei = " << facei << endl;
801  }
802  }
803  }
804 
805  if (debug)
806  {
807  // Write out results for checking
808  forAll(procPatchLabels_, patchLabeli)
809  {
810  const label patchi = procPatchLabels_[patchLabeli];
811  const scalarField& localFlux = dVf.boundaryField()[patchi];
812  Pout<< "time = " << mesh_.time().value() << ": localFlux = "
813  << localFlux << endl;
814  }
815  }
816 
817  // Reinitialising list used for minimal parallel communication
818  forAll(surfaceCellFacesOnProcPatches_, patchi)
819  {
820  surfaceCellFacesOnProcPatches_[patchi].clear();
821  }
822  }
823 }
824 
825 
826 void Foam::isoAdvection::checkIfOnProcPatch(const label facei)
827 {
828  if (!mesh_.isInternalFace(facei))
829  {
830  const polyBoundaryMesh& pbm = mesh_.boundaryMesh();
831  const label patchi = pbm.patchID()[facei - mesh_.nInternalFaces()];
832 
833  if (isA<processorPolyPatch>(pbm[patchi]) && pbm[patchi].size())
834  {
835  const label patchFacei = pbm[patchi].whichFace(facei);
836  surfaceCellFacesOnProcPatches_[patchi].append(patchFacei);
837  }
838  }
839 }
840 
841 
843 {
845 
846  scalar advectionStartTime = mesh_.time().elapsedCpuTime();
847 
848  // Initialising dVf with upwind values
849  // i.e. phi[facei]*alpha1[upwindCell[facei]]*dt
850  dVf_ = upwind<scalar>(mesh_, phi_).flux(alpha1_)*mesh_.time().deltaT();
851 
852  // Do the isoAdvection on surface cells
853  timeIntegratedFlux();
854 
855  // Adjust alpha for mesh motion
856  if (mesh_.moving())
857  {
858  alpha1In_ *= (mesh_.Vsc0()/mesh_.Vsc());
859  }
860 
861  // Adjust dVf for unbounded cells
862  limitFluxes();
863 
864  // Advect the free surface
865  alpha1_ -= fvc::surfaceIntegrate(dVf_);
866  alpha1_.correctBoundaryConditions();
867 
868  scalar maxAlphaMinus1 = gMax(alpha1In_) - 1;
869  scalar minAlpha = gMin(alpha1In_);
870  Info << "isoAdvection: After conservative bounding: min(alpha) = "
871  << minAlpha << ", max(alpha) = 1 + " << maxAlphaMinus1 << endl;
872 
873  // Apply non-conservative bounding mechanisms (clipping and snapping)
874  // Note: We should be able to write out alpha before this is done!
875  applyBruteForceBounding();
876 
877  // Write surface cell set and bound cell set if required by user
878  writeSurfaceCells();
879  writeBoundedCells();
880 
881  advectionTime_ += (mesh_.time().elapsedCpuTime() - advectionStartTime);
882  Info << "isoAdvection: time consumption = "
883  << label(100*advectionTime_/(mesh_.time().elapsedCpuTime() + SMALL))
884  << "%" << endl;
885 }
886 
887 
889 {
890  bool alpha1Changed = false;
891 
892  scalar snapAlphaTol = dict_.lookupOrDefault<scalar>("snapTol", 0);
893  if (snapAlphaTol > 0)
894  {
895  alpha1_ =
896  alpha1_
897  *pos0(alpha1_ - snapAlphaTol)
898  *neg0(alpha1_ - (1.0 - snapAlphaTol))
899  + pos0(alpha1_ - (1.0 - snapAlphaTol));
900 
901  alpha1Changed = true;
902  }
903 
904  if (dict_.lookupOrDefault("clip", true))
905  {
906  alpha1_ = min(scalar(1), max(scalar(0), alpha1_));
907  alpha1Changed = true;
908  }
909 
910  if (alpha1Changed)
911  {
912  alpha1_.correctBoundaryConditions();
913  }
914 }
915 
916 
918 {
919  if (!mesh_.time().writeTime()) return;
920 
921  if (dict_.lookupOrDefault("writeSurfCells", false))
922  {
923  cellSet cSet
924  (
925  IOobject
926  (
927  "surfCells",
928  mesh_.time().timeName(),
929  mesh_,
931  )
932  );
933 
934  cSet.insert(surfCells_);
935 
936  cSet.write();
937  }
938 }
939 
940 
942 {
943  if (!mesh_.time().writeTime()) return;
944 
945  if (dict_.lookupOrDefault("writeBoundedCells", false))
946  {
947  cellSet cSet
948  (
949  IOobject
950  (
951  "boundedCells",
952  mesh_.time().timeName(),
953  mesh_,
955  )
956  );
957 
958  for (const label celli : cellIsBounded_)
959  {
960  cSet.insert(celli);
961  }
962 
963  cSet.write();
964  }
965 }
966 
967 
969 (
970  const DynamicList<List<point>>& faces
971 ) const
972 {
973 
974  if (!writeIsoFacesToFile_ || !mesh_.time().writeTime()) return;
975 
976  // Writing isofaces to obj file for inspection, e.g. in paraview
977  const fileName outputFile
978  (
979  mesh_.time().globalPath()
980  / "isoFaces"
981  / word::printf("isoFaces_%012d.obj", mesh_.time().timeIndex())
982  );
983 
984  if (Pstream::parRun())
985  {
986  // Collect points from all the processors
988  allProcFaces[Pstream::myProcNo()] = faces;
989  Pstream::gatherList(allProcFaces);
990 
991  if (Pstream::master())
992  {
993  mkDir(outputFile.path());
994  OBJstream os(outputFile);
995  Info<< nl << "isoAdvection: writing iso faces to file: "
996  << os.name() << nl << endl;
997 
998  face f;
999  forAll(allProcFaces, proci)
1000  {
1001  const DynamicList<List<point>>& procFacePts =
1002  allProcFaces[proci];
1003 
1004  forAll(procFacePts, i)
1005  {
1006  const List<point>& facePts = procFacePts[i];
1007 
1008  if (facePts.size() != f.size())
1009  {
1010  f = face(identity(facePts.size()));
1011  }
1012 
1013  os.write(f, facePts, false);
1014  }
1015  }
1016  }
1017  }
1018  else
1019  {
1020  mkDir(outputFile.path());
1021  OBJstream os(outputFile);
1022  Info<< nl << "isoAdvection: writing iso faces to file: "
1023  << os.name() << nl << endl;
1024 
1025  face f;
1026  forAll(faces, i)
1027  {
1028  const List<point>& facePts = faces[i];
1029 
1030  if (facePts.size() != f.size())
1031  {
1032  f = face(identity(facePts.size()));
1033  }
1034 
1035  os.write(f, facePts, false);
1036  }
1037  }
1038 }
1039 
1040 
1041 // ************************************************************************* //
Foam::expressions::patchExpr::debug
int debug
Static debugging option.
Foam::labelList
List< label > labelList
A List of labels.
Definition: List.H:74
volFields.H
Foam::pointField
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:44
Foam::scalarField
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
Definition: primitiveFieldsFwd.H:52
Foam::IOobject
Defines the attributes of an object for which implicit objectRegistry management is supported,...
Definition: IOobject.H:104
meshTools.H
ref
rDeltaT ref()
Foam::UPstream::commsTypes::nonBlocking
Foam::isoAdvection::writeSurfaceCells
void writeSurfaceCells() const
Return cellSet of surface cells.
Definition: isoAdvection.C:917
Foam::fvc::grad
tmp< GeometricField< typename outerProduct< vector, Type >::type, fvPatchField, volMesh >> grad(const GeometricField< Type, fvsPatchField, surfaceMesh > &ssf)
Definition: fvcGrad.C:54
Foam::fileName
A class for handling file names.
Definition: fileName.H:69
Foam::returnReduce
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
Definition: PstreamReduceOps.H:94
Foam::OBJstream
OFstream that keeps track of vertices.
Definition: OBJstream.H:56
Foam::fileName::path
static std::string path(const std::string &str)
Return directory path name (part before last /)
Definition: fileNameI.H:186
Foam::primitiveMesh::cellPoints
const labelListList & cellPoints() const
Definition: primitiveMeshCellPoints.C:34
Foam::Zero
static constexpr const zero Zero
Global zero.
Definition: zero.H:128
Foam::upwind
Upwind differencing scheme class.
Definition: upwind.H:56
Foam::DynamicList
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
Definition: DynamicList.H:57
alpha2
const volScalarField & alpha2
Definition: setRegionFluidFields.H:9
Foam::UPstream::nProcs
static label nProcs(const label communicator=0)
Number of processes in parallel run.
Definition: UPstream.H:426
Foam::UPstream::parRun
static bool & parRun()
Is this a parallel run?
Definition: UPstream.H:414
Foam::MeshObject< fvMesh, UpdateableMeshObject, volPointInterpolation >::New
static const volPointInterpolation & New(const fvMesh &mesh, Args &&... args)
Get existing or create a new MeshObject.
Definition: MeshObject.C:48
Foam::neg0
dimensionedScalar neg0(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:210
Foam::primitiveMesh::cells
const cellList & cells() const
Definition: primitiveMeshCells.C:138
Foam::isoAdvection::applyBruteForceBounding
void applyBruteForceBounding()
Apply the bounding based on user inputs.
Definition: isoAdvection.C:888
Foam::polyMesh::boundaryMesh
const polyBoundaryMesh & boundaryMesh() const
Return boundary mesh.
Definition: polyMesh.H:435
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:337
Foam::meshTools::otherCell
label otherCell(const primitiveMesh &mesh, const label celli, const label facei)
Return cell on other side of face. Throws error.
Definition: meshTools.C:579
Foam::pos0
dimensionedScalar pos0(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:188
alpha1
const volScalarField & alpha1
Definition: setRegionFluidFields.H:8
Foam::Pout
prefixOSstream Pout
An Ostream wrapper for parallel output to std::cout.
Foam::volPointInterpolation::interpolate
tmp< GeometricField< Type, pointPatchField, pointMesh > > interpolate(const GeometricField< Type, fvPatchField, volMesh > &) const
Interpolate volField using inverse distance weighting.
Foam::sign
dimensionedScalar sign(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:166
Foam::min
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:33
Foam::isoAdvection::writeIsoFaces
void writeIsoFaces(const DynamicList< List< point >> &isoFacePts) const
Write isoface points to .obj file.
Definition: isoAdvection.C:969
Foam::GAMGAgglomeration::size
label size() const
Definition: GAMGAgglomeration.H:325
forAll
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:290
interpolationCellPoint.H
Foam::vectorField
Field< vector > vectorField
Specialisation of Field<T> for vector.
Definition: primitiveFieldsFwd.H:54
nPoints
label nPoints
Definition: gmvOutputHeader.H:2
n
label n
Definition: TABSMDCalcMethod2.H:31
Foam::polyBoundaryMesh::patchID
const labelList & patchID() const
Per boundary face label the patch index.
Definition: polyBoundaryMesh.C:452
Foam::isoCutFace::debug
static int debug
Definition: isoCutFace.H:137
Foam::dimTime
const dimensionSet dimTime(0, 0, 1, 0, 0, 0, 0)
Definition: dimensionSets.H:54
Foam::label
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:62
Foam::regIOobject::write
virtual bool write(const bool valid=true) const
Write using setting from DB.
Definition: regIOobjectWrite.C:170
Foam::fvMesh::magSf
const surfaceScalarField & magSf() const
Return cell face area magnitudes.
Definition: fvMeshGeometry.C:347
Foam::Info
messageStream Info
Information stream (uses stdout - output is on the master only)
DebugInFunction
#define DebugInFunction
Report an information message using Foam::Info.
Definition: messageStream.H:356
Foam::name
word name(const complex &c)
Return string representation of complex.
Definition: complex.C:76
Foam::DynamicList::append
DynamicList< T, SizeMin > & append(const T &val)
Append an element to the end of this list.
Definition: DynamicListI.H:472
upwind.H
Foam::cellList
List< cell > cellList
A List of cells.
Definition: cellListFwd.H:47
Foam::primitiveMesh::cellCells
const labelListList & cellCells() const
Definition: primitiveMeshCellCells.C:102
Foam::limitedSurfaceInterpolationScheme::flux
virtual tmp< GeometricField< Type, fvsPatchField, surfaceMesh > > flux(const GeometricField< Type, fvPatchField, volMesh > &) const
Return the interpolation weighting factors.
Definition: limitedSurfaceInterpolationScheme.C:195
Foam::dimensionedScalar
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
Definition: dimensionedScalarFwd.H:43
phi
surfaceScalarField & phi
Definition: setRegionFluidFields.H:8
Foam::volScalarField
GeometricField< scalar, fvPatchField, volMesh > volScalarField
Definition: volFieldsFwd.H:57
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
fvcSurfaceIntegrate.H
Surface integrate surfaceField creating a volField. Surface sum a surfaceField creating a volField.
Foam::MeshObject< lduMesh, GeometricMeshObject, GAMGAgglomeration >::mesh_
const lduMesh & mesh_
Definition: MeshObject.H:96
timeName
word timeName
Definition: getTimeIndex.H:3
mesh
dynamicFvMesh & mesh
Definition: createDynamicFvMesh.H:6
Foam
Namespace for OpenFOAM.
Definition: atmBoundaryLayer.C:33
Foam::primitiveMesh::cellVolumes
const scalarField & cellVolumes() const
Definition: primitiveMeshCellCentresAndVols.C:187
Foam::cellSet
A collection of cell labels.
Definition: cellSet.H:51
Foam::vector
Vector< scalar > vector
A scalar version of the templated Vector.
Definition: vector.H:51
isoAdvection.H
Foam::UPstream::myProcNo
static int myProcNo(const label communicator=0)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:444
Foam::UPstream::master
static bool master(const label communicator=0)
Am I the master process.
Definition: UPstream.H:438
Foam::volVectorField
GeometricField< vector, fvPatchField, volMesh > volVectorField
Definition: volFieldsFwd.H:60
volPointInterpolation.H
Foam::labelListList
List< labelList > labelListList
A List of labelList.
Definition: labelList.H:56
U
U
Definition: pEqn.H:72
Foam::primitiveMesh::cellCentres
const vectorField & cellCentres() const
Definition: primitiveMeshCellCentresAndVols.C:176
Foam::Pstream::gatherList
static void gatherList(const List< commsStruct > &comms, List< T > &Values, const int tag, const label comm)
Gather data but keep individual values separate.
Definition: gatherScatterList.C:52
DebugInfo
#define DebugInfo
Report an information message using Foam::Info.
Definition: messageStream.H:350
Foam::cp
bool cp(const fileName &src, const fileName &dst, const bool followLink=true)
Copy the source to the destination (recursively if necessary).
Definition: MSwindows.C:802
Foam::nl
constexpr char nl
Definition: Ostream.H:372
f
labelList f(nPoints)
Foam::surfaceScalarField
GeometricField< scalar, fvsPatchField, surfaceMesh > surfaceScalarField
Definition: surfaceFieldsFwd.H:54
Foam::List
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: HashTable.H:102
Foam::fvc::surfaceIntegrate
void surfaceIntegrate(Field< Type > &ivf, const GeometricField< Type, fvsPatchField, surfaceMesh > &ssf)
Definition: fvcSurfaceIntegrate.C:46
fvcGrad.H
Calculate the gradient of the given field.
Foam::mag
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
Foam::start
label ListType::const_reference const label start
Definition: ListOps.H:408
Foam::primitiveMesh::faceCentres
const vectorField & faceCentres() const
Definition: primitiveMeshFaceCentresAndAreas.C:145
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
Foam::List::clear
void clear()
Clear the list, i.e. set size to zero.
Definition: ListI.H:115
Foam::HashSet::insert
bool insert(const Key &key)
Insert a new entry, not overwriting existing entries.
Definition: HashSet.H:182
Foam::sum
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &df)
Definition: DimensionedFieldFunctions.C:327
patches
const polyBoundaryMesh & patches
Definition: convertProcessorPatches.H:65
Foam::constant::universal::c
const dimensionedScalar c
Speed of light in a vacuum.
Foam::word::printf
static word printf(const char *fmt, const PrimitiveType &val)
Use a printf-style formatter for a primitive.
Foam::dimVol
const dimensionSet dimVol(dimVolume)
Older spelling for dimVolume.
Definition: dimensionSets.H:65
Foam::face
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:74
cells
const cellShapeList & cells
Definition: gmvOutputHeader.H:3
cellSet.H
Foam::isoAdvection::advect
void advect()
Advect the free surface. Updates alpha field, taking into account.
Definition: isoAdvection.C:842
phib
phib
Definition: pEqn.H:189
Foam::gMin
Type gMin(const FieldField< Field, Type > &f)
Definition: FieldFieldFunctions.C:593
Foam::point
vector point
Point is a vector.
Definition: point.H:43
Foam::IOobject::NO_READ
Definition: IOobject.H:123
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
Foam::isoCutCell::debug
static int debug
Definition: isoCutCell.H:157
Foam::isoAdvection::writeBoundedCells
void writeBoundedCells() const
Return cellSet of bounded cells.
Definition: isoAdvection.C:941
Foam::defineTypeNameAndDebug
defineTypeNameAndDebug(combustionModel, 0)
boundary
faceListList boundary
Definition: createBlockMesh.H:4
Foam::gMax
Type gMax(const FieldField< Field, Type > &f)
Definition: FieldFieldFunctions.C:592
OBJstream.H
Foam::primitiveMesh::faceAreas
const vectorField & faceAreas() const
Definition: primitiveMeshFaceCentresAndAreas.C:156