isoAdvectionTemplates.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  Modified code Copyright (C) 2016-2017 OpenCFD Ltd.
10  Modified code Copyright (C) 2019-2020 DLR
11  Modified code Copyright (C) 2021 Johan Roenby
12 -------------------------------------------------------------------------------
13 License
14  This file is part of OpenFOAM.
15 
16  OpenFOAM is free software: you can redistribute it and/or modify it
17  under the terms of the GNU General Public License as published by
18  the Free Software Foundation, either version 3 of the License, or
19  (at your option) any later version.
20 
21  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
22  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
23  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
24  for more details.
25 
26  You should have received a copy of the GNU General Public License
27  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
28 
29 \*---------------------------------------------------------------------------*/
30 
31 #include "isoAdvection.H"
32 #include "fvcSurfaceIntegrate.H"
33 #include "upwind.H"
34 
35 // ************************************************************************* //
36 
37 template<typename Type>
38 Type Foam::isoAdvection::faceValue
39 (
40  const GeometricField<Type, fvsPatchField, surfaceMesh>& f,
41  const label facei
42 ) const
43 {
44  if (mesh_.isInternalFace(facei))
45  {
46  return f.primitiveField()[facei];
47  }
48  else
49  {
50  const polyBoundaryMesh& pbm = mesh_.boundaryMesh();
51 
52  // Boundary face. Find out which face of which patch
53  const label patchi = pbm.patchID(facei);
54 
55  if (patchi < 0 || patchi >= pbm.size())
56  {
58  << "Cannot find patch for face " << facei
59  << abort(FatalError);
60  }
61 
62  // Handle empty patches
63  const polyPatch& pp = pbm[patchi];
64  if (isA<emptyPolyPatch>(pp) || pp.empty())
65  {
66  return pTraits<Type>::zero;
67  }
68 
69  const label patchFacei = pp.whichFace(facei);
70  return f.boundaryField()[patchi][patchFacei];
71  }
72 }
73 
74 
75 template<typename Type>
76 void Foam::isoAdvection::setFaceValue
77 (
78  GeometricField<Type, fvsPatchField, surfaceMesh>& f,
79  const label facei,
80  const Type& value
81 ) const
82 {
83  if (mesh_.isInternalFace(facei))
84  {
85  f.primitiveFieldRef()[facei] = value;
86  }
87  else
88  {
89  const polyBoundaryMesh& pbm = mesh_.boundaryMesh();
90 
91  // Boundary face. Find out which face of which patch
92  const label patchi = pbm.patchID(facei);
93 
94  if (patchi < 0 || patchi >= pbm.size())
95  {
97  << "Cannot find patch for face " << facei
98  << abort(FatalError);
99  }
100 
101  // Handle empty patches
102  const polyPatch& pp = pbm[patchi];
103  if (isA<emptyPolyPatch>(pp) || pp.empty())
104  {
105  return;
106  }
107 
108  const label patchFacei = pp.whichFace(facei);
109  f.boundaryFieldRef()[patchi][patchFacei] = value;
110  }
111 }
112 
113 
114 template<class SpType, class SuType>
115 void Foam::isoAdvection::limitFluxes
116 (
117  const SpType& Sp,
118  const SuType& Su
119 )
120 {
121  addProfilingInFunction(geometricVoF);
123 
124  const scalar aTol = 100*SMALL; // Note: tolerances
125  scalar maxAlphaMinus1 = gMax(alpha1_) - 1; // max(alphaNew - 1);
126  scalar minAlpha = gMin(alpha1_); // min(alphaNew);
127  const label nOvershoots = 20; // sum(pos0(alphaNew - 1 - aTol));
128 
129  const labelList& owner = mesh_.faceOwner();
130  const labelList& neighbour = mesh_.faceNeighbour();
131 
132  DebugInfo
133  << "isoAdvection: Before conservative bounding: min(alpha) = "
134  << minAlpha << ", max(alpha) = 1 + " << maxAlphaMinus1 << endl;
135 
136  surfaceScalarField dVfcorrectionValues("dVfcorrectionValues", dVf_*0.0);
137 
138  bitSet needBounding(mesh_.nCells(),false);
139  needBounding.set(surfCells_);
140 
141  extendMarkedCells(needBounding);
142 
143  // Loop number of bounding steps
144  for (label n = 0; n < nAlphaBounds_; n++)
145  {
146  if (maxAlphaMinus1 > aTol || minAlpha < -aTol) // Note: tolerances
147  {
148  DebugInfo << "boundAlpha... " << endl;
149 
150  DynamicList<label> correctedFaces(3*nOvershoots);
151  dVfcorrectionValues = dimensionedScalar("0",dimVolume,0.0);
152  boundFlux(needBounding, dVfcorrectionValues, correctedFaces,Sp,Su);
153 
154  correctedFaces.append
155  (
156  syncProcPatches(dVfcorrectionValues, phi_,true)
157  );
158 
159  labelHashSet alreadyUpdated;
160  for (const label facei : correctedFaces)
161  {
162  if (alreadyUpdated.insert(facei))
163  {
164  checkIfOnProcPatch(facei);
165  const label own = owner[facei];
166  scalar Vown = mesh_.V()[own];
167  if (porosityEnabled_)
168  {
169  Vown *= porosityPtr_->primitiveField()[own];
170  }
171  alpha1_[own] -= faceValue(dVfcorrectionValues, facei)/Vown;
172 
173  if (mesh_.isInternalFace(facei))
174  {
175  const label nei = neighbour[facei];
176  scalar Vnei = mesh_.V()[nei];
177  if (porosityEnabled_)
178  {
179  Vnei *= porosityPtr_->primitiveField()[nei];
180  }
181  alpha1_[nei] +=
182  faceValue(dVfcorrectionValues, facei)/Vnei;
183  }
184 
185  // Change to treat boundaries consistently
186  const scalar corrVf =
187  faceValue(dVf_, facei)
188  + faceValue(dVfcorrectionValues, facei);
189 
190  setFaceValue(dVf_, facei, corrVf);
191 
192  // If facei is on a cyclic patch correct dVf of facej on
193  // neighbour patch and alpha value of facej's owner cell.
194  if (!mesh_.isInternalFace(facei))
195  {
196  const polyBoundaryMesh& boundaryMesh =
197  mesh_.boundaryMesh();
198  const label patchi = boundaryMesh.patchID(facei);
199  const polyPatch& pp = boundaryMesh[patchi];
200  const cyclicPolyPatch* cpp = isA<cyclicPolyPatch>(pp);
201  const label patchFacei = pp.whichFace(facei);
202 
203  if (cpp)
204  {
205  const label neiPatchID(cpp->neighbPolyPatchID());
207  dVf_.boundaryFieldRef();
208  dVfb[neiPatchID][patchFacei] =
209  -dVfb[patchi][patchFacei];
210  const polyPatch& np = boundaryMesh[neiPatchID];
211  const label globalFacei = np.start() + patchFacei;
212  const label neiOwn(owner[globalFacei]);
213  scalar VneiOwn = mesh_.V()[neiOwn];
214  if (porosityEnabled_)
215  {
216  VneiOwn *=
217  porosityPtr_->primitiveField()[neiOwn];
218  }
219  alpha1_[neiOwn] +=
220  faceValue(dVfcorrectionValues, facei)/VneiOwn;
221  }
222  }
223 
224  }
225 
226  }
227 
228  syncProcPatches(dVf_, phi_);
229  }
230  else
231  {
232  break;
233  }
234 
235  maxAlphaMinus1 = gMax(alpha1_) - 1; // max(alphaNew - 1);
236  minAlpha = gMin(alpha1_); // min(alphaNew);
237 
238  if (debug)
239  {
240  // Check if still unbounded
241  //scalarField alphaNew(alpha1In_ - fvc::surfaceIntegrate(dVf_)());
242  label maxAlphaMinus1 = max(alpha1_.primitiveField() - 1);
243  scalar minAlpha = min(alpha1_.primitiveField());
244  label nUndershoots = sum(neg0(alpha1_.primitiveField() + aTol));
245  label nOvershoots = sum(pos0(alpha1_.primitiveField() - 1 - aTol));
246 
247  Info<< "After bounding number " << n + 1 << " of time "
248  << mesh_.time().value() << ":" << nl
249  << "nOvershoots = " << nOvershoots << " with max(alpha1_-1) = "
250  << maxAlphaMinus1 << " and nUndershoots = " << nUndershoots
251  << " with min(alpha1_) = " << minAlpha << endl;
252  }
253  }
254 
255  alpha1_.correctBoundaryConditions();
256 
257 }
258 
259 
260 template<class SpType, class SuType>
261 void Foam::isoAdvection::boundFlux
262 (
263  const bitSet& nextToInterface,
264  surfaceScalarField& dVfcorrectionValues,
265  DynamicList<label>& correctedFaces,
266  const SpType& Sp,
267  const SuType& Su
268 )
269 {
270  addProfilingInFunction(geometricVoF);
272  const scalar rDeltaT = 1.0/mesh_.time().deltaTValue();
273 
274  correctedFaces.clear();
275  const scalar aTol = 100*SMALL; // Note: tolerances
276 
277  const scalarField& meshV = mesh_.cellVolumes();
278  const scalar dt = mesh_.time().deltaTValue();
279 
280  DynamicList<label> downwindFaces(10);
281  DynamicList<label> facesToPassFluidThrough(downwindFaces.size());
282  DynamicList<scalar> dVfmax(downwindFaces.size());
283  DynamicList<scalar> phi(downwindFaces.size());
284  const volScalarField& alphaOld = alpha1_.oldTime();
285 
286  // Loop through alpha cell centred field
287  for (label celli: nextToInterface)
288  {
289  if (alpha1_[celli] < -aTol || alpha1_[celli] > 1 + aTol)
290  {
291  scalar Vi = meshV[celli];
292  if (porosityEnabled_)
293  {
294  Vi *= porosityPtr_->primitiveField()[celli];
295  }
296 
297  scalar alphaOvershoot =
298  pos0(alpha1_[celli] - 1)*(alpha1_[celli] - 1)
299  + neg0(alpha1_[celli])*alpha1_[celli];
300 
301  scalar fluidToPassOn = alphaOvershoot*Vi;
302  label nFacesToPassFluidThrough = 1;
303 
304  bool firstLoop = true;
305 
306  // First try to pass surplus fluid on to neighbour cells that are
307  // not filled and to which dVf < phi*dt
308  for (label iter=0; iter<10; iter++)
309  {
310  if (mag(alphaOvershoot) < aTol || nFacesToPassFluidThrough == 0)
311  {
312  break;
313  }
314 
315  DebugInfo
316  << "\n\nBounding cell " << celli
317  << " with alpha overshooting " << alphaOvershoot
318  << endl;
319 
320  facesToPassFluidThrough.clear();
321  dVfmax.clear();
322  phi.clear();
323 
324  // Find potential neighbour cells to pass surplus phase to
325  setDownwindFaces(celli, downwindFaces);
326 
327  scalar dVftot = 0;
328  nFacesToPassFluidThrough = 0;
329 
330  for (const label facei : downwindFaces)
331  {
332  const scalar phif = faceValue(phi_, facei);
333 
334  const scalar dVff =
335  faceValue(dVf_, facei)
336  + faceValue(dVfcorrectionValues, facei);
337 
338  const scalar maxExtraFaceFluidTrans =
339  mag(pos0(fluidToPassOn)*phif*dt - dVff);
340 
341  // dVf has same sign as phi and so if phi>0 we have
342  // mag(phi_[facei]*dt) - mag(dVf[facei]) = phi_[facei]*dt
343  // - dVf[facei]
344  // If phi < 0 we have mag(phi_[facei]*dt) -
345  // mag(dVf[facei]) = -phi_[facei]*dt - (-dVf[facei]) > 0
346  // since mag(dVf) < phi*dt
347  DebugInfo
348  << "downwindFace " << facei
349  << " has maxExtraFaceFluidTrans = "
350  << maxExtraFaceFluidTrans << endl;
351 
352  if (maxExtraFaceFluidTrans/Vi > aTol)
353  {
354 // if (maxExtraFaceFluidTrans/Vi > aTol &&
355 // mag(dVfIn[facei])/Vi > aTol) //Last condition may be
356 // important because without this we will flux through uncut
357 // downwind faces
358  facesToPassFluidThrough.append(facei);
359  phi.append(phif);
360  dVfmax.append(maxExtraFaceFluidTrans);
361  dVftot += mag(phif*dt);
362  }
363  }
364 
365  DebugInfo
366  << "\nfacesToPassFluidThrough: "
367  << facesToPassFluidThrough << ", dVftot = "
368  << dVftot << " m3 corresponding to dalpha = "
369  << dVftot/Vi << endl;
370 
371  forAll(facesToPassFluidThrough, fi)
372  {
373  const label facei = facesToPassFluidThrough[fi];
374  scalar fluidToPassThroughFace =
375  mag(fluidToPassOn)*mag(phi[fi]*dt)/dVftot;
376 
377  nFacesToPassFluidThrough +=
378  pos0(dVfmax[fi] - fluidToPassThroughFace);
379 
380  fluidToPassThroughFace =
381  min(fluidToPassThroughFace, dVfmax[fi]);
382 
383  scalar dVff = faceValue(dVfcorrectionValues, facei);
384 
385  dVff +=
386  sign(phi[fi])*fluidToPassThroughFace*sign(fluidToPassOn);
387 
388  setFaceValue(dVfcorrectionValues, facei, dVff);
389 
390  if (firstLoop)
391  {
392  checkIfOnProcPatch(facei);
393  correctedFaces.append(facei);
394  }
395  }
396 
397  firstLoop = false;
398 
399  scalar alpha1New =
400  (
401  alphaOld[celli]*rDeltaT + Su[celli]
402  - netFlux(dVf_, celli)/Vi*rDeltaT
403  - netFlux(dVfcorrectionValues, celli)/Vi*rDeltaT
404  )
405  /
406  (rDeltaT - Sp[celli]);
407 
408  alphaOvershoot =
409  pos0(alpha1New - 1)*(alpha1New - 1)
410  + neg0(alpha1New)*alpha1New;
411 
412  fluidToPassOn = alphaOvershoot*Vi;
413 
414  DebugInfo
415  << "\nNew alpha for cell " << celli << ": "
416  << alpha1New << endl;
417  }
418  }
419  }
420 
421  DebugInfo << "correctedFaces = " << correctedFaces << endl;
422 }
423 
424 
425 template<class SpType, class SuType>
426 void Foam::isoAdvection::advect(const SpType& Sp, const SuType& Su)
427 {
428  addProfilingInFunction(geometricVoF);
430 
431  if (mesh_.topoChanging())
432  {
433  setProcessorPatches();
434  }
435 
436  scalar advectionStartTime = mesh_.time().elapsedCpuTime();
437 
438  const scalar rDeltaT = 1.0/mesh_.time().deltaTValue();
439 
440  // reconstruct the interface
441  surf_->reconstruct();
442 
443  if (timeIndex_ < mesh_.time().timeIndex())
444  {
445  timeIndex_= mesh_.time().timeIndex();
446  surf_->normal().oldTime() = surf_->normal();
447  surf_->centre().oldTime() = surf_->centre();
448  }
449 
450  // Initialising dVf with upwind values
451  // i.e. phi[facei]*alpha1[upwindCell[facei]]*dt
452  dVf_ = upwind<scalar>(mesh_, phi_).flux(alpha1_)*mesh_.time().deltaT();
453 
454  // Do the isoAdvection on surface cells
455  timeIntegratedFlux();
456 
457  // Adjust alpha for mesh motion
458  if (mesh_.moving())
459  {
460  alpha1In_ *= (mesh_.Vsc0()/mesh_.Vsc());
461  }
462 
463  // Advect the free surface
464  if (porosityEnabled_)
465  {
466  // Should Su and Sp also be divided by porosity?
467  alpha1_.primitiveFieldRef() =
468  (
469  alpha1_.oldTime().primitiveField()*rDeltaT + Su.field()
470  - fvc::surfaceIntegrate(dVf_)().primitiveField()*rDeltaT
471  /porosityPtr_->primitiveField()
472  )/(rDeltaT - Sp.field());
473  }
474  else
475  {
476  alpha1_.primitiveFieldRef() =
477  (
478  alpha1_.oldTime().primitiveField()*rDeltaT
479  + Su.field()
480  - fvc::surfaceIntegrate(dVf_)().primitiveField()*rDeltaT
481  )/(rDeltaT - Sp.field());
482  }
483 
484  alpha1_.correctBoundaryConditions();
485 
486  // Adjust dVf for unbounded cells
487  limitFluxes(Sp, Su);
488 
489  scalar maxAlphaMinus1 = gMax(alpha1In_) - 1;
490  scalar minAlpha = gMin(alpha1In_);
491 
492  Info<< "isoAdvection: After conservative bounding: min(alpha) = "
493  << minAlpha << ", max(alpha) = 1 + " << maxAlphaMinus1 << endl;
494 
495  // Apply non-conservative bounding mechanisms (clipping and snapping)
496  // Note: We should be able to write out alpha before this is done!
497  applyBruteForceBounding();
498 
499  // Write surface cell set and bound cell set if required by user
500  writeSurfaceCells();
501 
502  advectionTime_ += (mesh_.time().elapsedCpuTime() - advectionStartTime);
503  DebugInfo
504  << "isoAdvection: time consumption = "
505  << label(100*advectionTime_/(mesh_.time().elapsedCpuTime() + SMALL))
506  << "%" << endl;
507 
508  alphaPhi_ = dVf_/mesh_.time().deltaT();
509 }
510 
511 
512 // ************************************************************************* //
dimensionedScalar sign(const dimensionedScalar &ds)
const polyBoundaryMesh & pbm
Surface integrate surfaceField creating a volField. Surface sum a surfaceField creating a volField...
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &f1)
const GeometricField< Type, PatchField, GeoMesh > & oldTime() const
Return old time field.
const Internal::FieldType & primitiveField() const noexcept
Return a const-reference to the internal field values.
zeroField Su
Definition: alphaSuSp.H:1
void surfaceIntegrate(Field< Type > &ivf, const GeometricField< Type, fvsPatchField, surfaceMesh > &ssf)
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:608
Type gMin(const FieldField< Field, Type > &f)
tmp< surfaceScalarField > flux(const volVectorField &vvf)
Return the face-flux field obtained from the given volVectorField.
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:40
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
const labelList & patchID() const
Per boundary face label the patch index.
GeometricBoundaryField< scalar, fvsPatchField, surfaceMesh > Boundary
Type of boundary fields.
bool isInternalFace(const label faceIndex) const noexcept
Return true if given face label is internal to the mesh.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
GeometricField< scalar, fvPatchField, volMesh > volScalarField
Definition: volFieldsFwd.H:72
HashSet< label, Hash< label > > labelHashSet
A HashSet of labels, uses label hasher.
Definition: HashSet.H:85
const dimensionSet dimVolume(pow3(dimLength))
Definition: dimensionSets.H:58
const polyBoundaryMesh & boundaryMesh() const noexcept
Return boundary mesh.
Definition: polyMesh.H:609
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
#define DebugInFunction
Report an information message using Foam::Info.
label size() const noexcept
The number of entries in the list.
Definition: UPtrListI.H:106
dimensionedScalar neg0(const dimensionedScalar &ds)
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:26
errorManip< error > abort(error &err)
Definition: errorManip.H:139
#define DebugInfo
Report an information message using Foam::Info.
dimensionedScalar pos0(const dimensionedScalar &ds)
int debug
Static debugging option.
Type gMax(const FieldField< Field, Type > &f)
labelList f(nPoints)
zeroField field() const noexcept
Method name compatibility with DimensionedField.
Definition: zeroField.H:66
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
void advect(const SpType &Sp, const SuType &Su)
Advect the free surface. Updates alpha field, taking into account.
#define addProfilingInFunction(Name)
Define profiling trigger with specified name and description corresponding to the compiler-defined fu...
messageStream Info
Information stream (stdout output on master, null elsewhere)
label n
List< label > labelList
A List of labels.
Definition: List.H:62
GeometricField< scalar, fvsPatchField, surfaceMesh > surfaceScalarField
uindirectPrimitivePatch pp(UIndirectList< face >(mesh.faces(), faceLabels), mesh.points())
zeroField Sp
Definition: alphaSuSp.H:2