fvMeshStitchersMoving.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) 2021-2022 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 "fvMeshStitchersMoving.H"
27 #include "FvFaceCellWave.H"
28 #include "fvMeshSubset.H"
29 #include "fvmLaplacian.H"
30 #include "meshPhiCorrectInfo.H"
31 #include "meshPhiPreCorrectInfo.H"
34 #include "regionSplit.H"
35 #include "solutionControl.H"
36 #include "syncTools.H"
39 
40 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
41 
42 namespace Foam
43 {
44 
45 template<class Type>
46 inline List<Type> repeat(const UList<Type>& a, const UList<Type>& b)
47 {
48  List<Type> result(a.size() + b.size());
49  forAll(a, i)
50  {
51  result[2*i] = a[i];
52  result[2*i + 1] = b[i];
53  }
54  return result;
55 }
56 
57 
58 template<class Type>
59 inline List<Type> repeat(const UList<Type>& l)
60 {
61  return repeat(l, l);
62 }
63 
64 
66 {
68  scalar weight;
69 
70  static const layerAndWeight min, max;
71 
72  typedef nil cmptType;
73 
74  friend bool operator==(const layerAndWeight& a, const layerAndWeight& b)
75  {
76  return a.layer == b.layer && a.weight == b.weight;
77  }
78 
79  friend bool operator!=(const layerAndWeight& a, const layerAndWeight& b)
80  {
81  return !(a == b);
82  }
83 
84  friend Ostream& operator<<(Ostream& os, const layerAndWeight& l)
85  {
86  return os << l.layer << token::SPACE << l.weight;
87  }
88 
90  {
91  return is >> l.layer >> l.weight;
92  }
93 };
94 
95 
97 
98 
100 
101 
103 {
104  return a.layer > b.layer ? a : b;
105 }
106 
107 
109 {
110  return a.layer < b.layer ? a : b;
111 }
112 
113 
114 }
115 
116 
117 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
118 
119 namespace Foam
120 {
121 namespace fvMeshStitchers
122 {
123  defineTypeNameAndDebug(moving, 0);
125 }
126 }
127 
128 
129 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
130 
131 void Foam::fvMeshStitchers::moving::conformCorrectMeshPhi
132 (
133  surfaceScalarField& phi
134 )
135 {
136  // Add the non-conformal parts of the mesh flux into the original faces
138  forAll(phiBf, nccPatchi)
139  {
140  if (isA<nonConformalFvPatch>(phiBf[nccPatchi].patch()))
141  {
142  const nonConformalFvPatch& ncFvp =
143  refCast<const nonConformalFvPatch>(phiBf[nccPatchi].patch());
144 
145  const label origPatchi = ncFvp.origPatchID();
146  const fvPatch& origFvp = ncFvp.origPatch();
147 
148  const labelList nccOrigPatchFace =
149  ncFvp.polyFaces() - origFvp.start();
150 
151  for (label i = 0; i <= phi.nOldTimes(); ++ i)
152  {
153  phi.oldTime(i).boundaryFieldRef()[origPatchi] +=
154  fieldRMapSum
155  (
156  phi.oldTime(i).boundaryField()[nccPatchi],
157  origFvp.size(),
158  nccOrigPatchFace
159  );
160 
161  phi.oldTime(i).boundaryFieldRef()[nccPatchi] = 0;
162  }
163  }
164  }
165 }
166 
167 
168 void Foam::fvMeshStitchers::moving::createNonConformalCorrectMeshPhiGeometry
169 (
170  surfaceLabelField::Boundary& polyFacesBf,
171  surfaceVectorField& SfSf,
172  surfaceVectorField& CfSf
173 )
174 {
176  const labelList origPatchIDs = ncb.allOrigPatchIDs();
177  const labelList errorPatchIDs = ncb.allErrorPatchIDs();
178 
179  forAll(origPatchIDs, i)
180  {
181  const label origPatchi = origPatchIDs[i];
182  const polyPatch& origPp = mesh().boundaryMesh()[origPatchi];
183 
184  const label errorPatchi = errorPatchIDs[i];
185 
186  polyFacesBf[errorPatchi] =
187  repeat((identity(origPp.size()) + origPp.start())());
188 
189  SfSf.boundaryFieldRef()[errorPatchi] =
190  repeat
191  (
192  (rootVSmall*origPp.faceNormals())(),
193  (-rootVSmall*origPp.faceNormals())()
194  );
195  CfSf.boundaryFieldRef()[errorPatchi] =
196  repeat(origPp.faceCentres());
197  }
198 }
199 
200 
201 Foam::labelHashSet Foam::fvMeshStitchers::moving::ownerCoupledCellSet()
202 {
204 
205  // For every boundary face, count how many owner-orig faces it is connected
206  // to (will be 1 or 0)
207  labelList bFaceNSet(mesh().nFaces() - mesh().nInternalFaces(), 0);
208  forAll(mesh().boundary(), nccPatchi)
209  {
210  const fvPatch& fvp = mesh().boundary()[nccPatchi];
211 
212  if (!isA<nonConformalCoupledFvPatch>(fvp)) continue;
213 
214  const nonConformalCoupledFvPatch& nccFvp =
215  refCast<const nonConformalCoupledFvPatch>(fvp);
216 
217  if (!nccFvp.owner()) continue;
218 
219  forAll(nccFvp, nccPatchFacei)
220  {
221  bFaceNSet
222  [
223  mesh().polyFacesBf()[nccPatchi][nccPatchFacei]
224  - mesh().nInternalFaces()
225  ] = 1;
226  }
227  }
228 
229  // For every boundary edge, count how many owner-orig faces it is connected
230  // to (should be 0, 1 or 2)
231  labelList ownerOrigBoundaryEdgeNSet
232  (
234  0
235  );
236  forAll(ncb.ownerOrigBoundaryEdgeMeshEdge(), ownerOrigBoundaryEdgei)
237  {
238  const label meshEdgei =
239  ncb.ownerOrigBoundaryEdgeMeshEdge()[ownerOrigBoundaryEdgei];
240 
241  forAll(mesh().edgeFaces()[meshEdgei], edgeFacei)
242  {
243  const label facei = mesh().edgeFaces()[meshEdgei][edgeFacei];
244 
245  if (mesh().isInternalFace(facei)) continue;
246 
247  const label bFacei = facei - mesh().nInternalFaces();
248 
249  if (bFaceNSet[bFacei])
250  {
251  ownerOrigBoundaryEdgeNSet[ownerOrigBoundaryEdgei] += 1;
252  }
253  }
254  }
256  (
257  mesh(),
259  ownerOrigBoundaryEdgeNSet,
260  plusEqOp<label>(),
261  label(0)
262  );
263 
264  // Isolate the cells that are edge connected to the owner patches. This
265  // will form the sub-mesh in which the correction is computed.
266  labelHashSet set;
267  forAll(ncb.ownerOrigBoundaryEdgeMeshEdge(), ownerOrigBoundaryEdgei)
268  {
269  if (ownerOrigBoundaryEdgeNSet[ownerOrigBoundaryEdgei] < 2) continue;
270 
271  const label meshEdgei =
272  ncb.ownerOrigBoundaryEdgeMeshEdge()[ownerOrigBoundaryEdgei];
273 
274  forAll(mesh().edgeFaces()[meshEdgei], edgeFacei)
275  {
276  const label facei = mesh().edgeFaces()[meshEdgei][edgeFacei];
277 
278  set.insert(mesh().faceOwner()[facei]);
279 
280  if (facei < mesh().nInternalFaces())
281  {
282  set.insert(mesh().faceNeighbour()[facei]);
283  }
284  }
285  }
286 
287  return set;
288 }
289 
290 
291 void Foam::fvMeshStitchers::moving::unconformInternalFaceCorrectMeshPhi
292 (
293  surfaceScalarField& phi
294 )
295 {
296  const surfaceScalarField::Boundary& magSfBf =
297  mesh().magSf().boundaryField();
298 
300 
301  // Step 1: Construct some boundary information
302 
303  // For each boundary face mark if it is an owner orig face and sum the total
304  // non-conformal coupled area if so
305  boolList bFaceIsOwnerOrig(mesh().nFaces() - mesh().nInternalFaces(), false);
306  scalarList bFaceNccMagSf(mesh().nFaces() - mesh().nInternalFaces(), Zero);
307  forAll(mesh().boundary(), nccPatchi)
308  {
309  const fvPatch& fvp = mesh().boundary()[nccPatchi];
310 
311  if (!isA<nonConformalCoupledFvPatch>(fvp)) continue;
312 
313  const nonConformalCoupledFvPatch& nccFvp =
314  refCast<const nonConformalCoupledFvPatch>(fvp);
315 
316  if (!nccFvp.owner()) continue;
317 
318  forAll(nccFvp, nccPatchFacei)
319  {
320  const label bFacei =
321  nccFvp.polyFaces()[nccPatchFacei] - mesh().nInternalFaces();
322 
323  bFaceIsOwnerOrig[bFacei] = true;
324  bFaceNccMagSf[bFacei] += magSfBf[nccPatchi][nccPatchFacei];
325  }
326  }
327 
328  // Step 2: Construct a sub-mesh for all cells that are connected to the
329  // owner patches.
330 
331  // Isolate the cells that are edge connected to the owner patches. This
332  // will form the sub-mesh in which the correction is computed.
333  labelHashSet subCellSet = ownerCoupledCellSet();
334 
335  // Create a mesh for the cells that are edge connected to coupled faces
336  // of the owner patches. This is where the correction will be computed.
337  fvMeshSubset subsetter(mesh());
338  subsetter.setLargeCellSubset(ownerCoupledCellSet());
339  const fvMesh& subMesh = subsetter.subMesh();
340  subMesh.deltaCoeffs();
341 
342  // Determine the disconnected regions of the sub mesh
343  const regionSplit subMeshRegions(subMesh);
344  const label subNRegions = subMeshRegions.nRegions();
345 
346  // Map from mesh boundary face to sub-mesh region
347  labelList bFaceSubRegion(mesh().nFaces() - mesh().nInternalFaces(), -1);
348  forAll(subsetter.faceMap(), subFacei)
349  {
350  const label facei = subsetter.faceMap()[subFacei];
351 
352  if (mesh().isInternalFace(facei)) continue;
353 
354  const label bFacei = facei - mesh().nInternalFaces();
355 
356  bFaceSubRegion[bFacei] =
357  subMeshRegions[subMesh.faceOwner()[subFacei]];
358  }
359 
360  // Get a single reference cell for each region
361  labelList subMeshRegionRefCells(subNRegions, -1);
362  {
363  static const label proci = Pstream::myProcNo();
364 
365  labelList subMeshRegionRefProcs(subNRegions, labelMax);
366  forAll(subMeshRegions, subCelli)
367  {
368  subMeshRegionRefProcs[subMeshRegions[subCelli]] = proci;
369  }
370  reduce(subMeshRegionRefProcs, ListOp<minOp<label>>());
371 
372  forAll(subMeshRegions, subCelli)
373  {
374  if
375  (
376  subMeshRegionRefProcs[subMeshRegions[subCelli]] == proci
377  && subMeshRegionRefCells[subMeshRegions[subCelli]] == -1
378  )
379  {
380  subMeshRegionRefCells[subMeshRegions[subCelli]] = subCelli;
381  }
382  }
383  }
384 
385  // Step 3: Synchronise the flux across the interfaces
386 
387  // Create a synchronised mesh flux for the coupled patches. Set both sides
388  // to the neighbour value so that all the error is on the owner side.
390  (
392  synchronisedBoundaryField<scalar>(phiBf, true, 0, 1)
393  );
394 
395  // Determine the total mesh flux error and area magnitude for each region
396  scalarList regionPhiError(subNRegions, scalar(0));
397  scalarList regionMagSf(subNRegions, vSmall);
398  forAll(phiBf, nccPatchi)
399  {
400  const fvPatch& fvp = mesh().boundary()[nccPatchi];
401 
402  if (!isA<nonConformalCoupledFvPatch>(fvp)) continue;
403 
404  const nonConformalCoupledFvPatch& nccFvp =
405  refCast<const nonConformalCoupledFvPatch>(fvp);
406 
407  if (!nccFvp.owner()) continue;
408 
409  forAll(nccFvp, nccPatchFacei)
410  {
411  const label subRegioni =
412  bFaceSubRegion
413  [
414  mesh().polyFacesBf()[nccPatchi][nccPatchFacei]
415  - mesh().nInternalFaces()
416  ];
417 
418  regionPhiError[subRegioni] +=
419  syncPhiBf[nccPatchi][nccPatchFacei]
420  - phiBf[nccPatchi][nccPatchFacei];
421 
422  regionMagSf[subRegioni] +=
423  magSfBf[nccPatchi][nccPatchFacei];
424  }
425  }
426  reduce(regionPhiError, ListOp<sumOp<scalar>>());
427  reduce(regionMagSf, ListOp<sumOp<scalar>>());
428 
429  // Synchronise the mesh fluxes, but offset so that the total flux for each
430  // region is the same as for the non-synchronised mesh fluxes
431  forAll(phiBf, nccPatchi)
432  {
433  const fvPatch& fvp = mesh().boundary()[nccPatchi];
434 
435  if (!isA<nonConformalCoupledFvPatch>(fvp)) continue;
436 
437  const nonConformalCoupledFvPatch& nccFvp =
438  refCast<const nonConformalCoupledFvPatch>(fvp);
439 
440  if (!nccFvp.owner()) continue;
441 
442  forAll(nccFvp, nccPatchFacei)
443  {
444  const label subRegioni =
445  bFaceSubRegion
446  [
447  mesh().polyFacesBf()[nccPatchi][nccPatchFacei]
448  - mesh().nInternalFaces()
449  ];
450 
451  phiBf[nccPatchi][nccPatchFacei] =
452  syncPhiBf[nccPatchi][nccPatchFacei]
453  - magSfBf[nccPatchi][nccPatchFacei]
454  /regionMagSf[subRegioni]
455  *regionPhiError[subRegioni];
456  }
457  }
458 
459  // Step 4: Set up a system on the sub-mesh with which to solve for a flux
460  // that corrects the volume conservation error in the cells connected to
461  // synchronised faces
462 
463  // Map volumes to the sub mesh
464  const volScalarField::Internal subV(subsetter.interpolate(mesh().V()));
465  const volScalarField::Internal subV0(subsetter.interpolate(mesh().V0()));
466 
467  // Map mesh flux to the sub mesh, accumulating when a face is split into
468  // multiple non-conformal parts
469  surfaceScalarField subPhi
470  (
472  (
473  "phi",
474  subMesh,
476  )
477  );
478  forAll(subPhi, subFacei)
479  {
480  const label facei = subsetter.faceMap()[subFacei];
481 
482  subPhi[subFacei] = phi[facei];
483  }
484  forAll(subPhi.boundaryField(), subPatchi)
485  {
486  const fvPatch& subFvp = subPhi.boundaryField()[subPatchi].patch();
487 
488  forAll(subPhi.boundaryField()[subPatchi], subPatchFacei)
489  {
490  const label subFacei = subFvp.start() + subPatchFacei;
491  const label facei = subsetter.faceMap()[subFacei];
492 
493  if (mesh().isInternalFace(facei))
494  {
495  const label s = sign(subsetter.faceFlipMap()[subFacei]);
496 
497  subPhi.boundaryFieldRef()[subPatchi][subPatchFacei] =
498  s*phi[facei];
499  }
500  else
501  {
502  const label bFacei = facei - mesh().nInternalFaces();
503 
504  const labelUList patches =
505  mesh().polyBFacePatches()[bFacei];
506  const labelUList patchFaces =
507  mesh().polyBFacePatchFaces()[bFacei];
508 
509  forAll(patches, i)
510  {
511  subPhi.boundaryFieldRef()[subPatchi][subPatchFacei] +=
512  phiBf[patches[i]][patchFaces[i]];
513  }
514  }
515  }
516  }
517 
518  // Calculate the volume conservation error for the sub mesh
519  const volScalarField::Internal subVce
520  (
521  fvc::surfaceIntegrate(subPhi*subMesh.time().deltaT())()
522  - (subV - subV0)/subV
523  );
524 
525  // Construct boundary conditions for the sub-mesh potential. Zero gradient
526  // is used for all non-constraint boundaries (i.e., this is a closed
527  // domain) and for exposed internal faces, which therefore require a patch
528  // type override
529  wordList MeshPhiPatchTypes(subMesh.boundary().size());
530  forAll(subMesh.boundary(), patchi)
531  {
532  const fvPatch& subFvp = subMesh.boundary()[patchi];
533  MeshPhiPatchTypes[patchi] =
534  polyPatch::constraintType(subFvp.type())
535  && !isA<internalFvPatch>(subFvp)
536  ? subFvp.type()
538  }
539 
540  // Solve
541  volScalarField MeshPhi
542  (
543  IOobject
544  (
545  "MeshPhi",
546  subMesh.time().timeName(),
547  subMesh,
550  ),
551  subMesh,
553  MeshPhiPatchTypes,
554  subMesh.boundaryMesh().types()
555  );
556 
557  subMesh.schemes().setFluxRequired(MeshPhi.name());
558 
559  fvScalarMatrix MeshPhiEqn
560  (
561  fvm::laplacian(MeshPhi) + subVce
562  );
563 
564  forAll(subMeshRegionRefCells, i)
565  {
566  MeshPhiEqn.setReference(subMeshRegionRefCells[i], 0);
567  }
568 
569  MeshPhiEqn.solve();
570 
571  // Create a correction for the sub-mesh face fluxes
572  surfaceScalarField subDeltaPhi(MeshPhiEqn.flux()/mesh().time().deltaT());
573 
574  // Step 5: Currently, the computed correction fixes the volume conservation
575  // error to the level of the solve tolerance. We would rather the volume
576  // conservation error be at round off and the error be in the mismatch
577  // across the couplings, as the mismatch will be fixed later when area and
578  // mesh flux is added to the error faces. So, here, we correct the
579  // correction (!) by propagating the error from the cells furthest from the
580  // interface back to the interface. This is done using waves.
581 
582  // Dynamic memory for wave initialisation
583  DynamicList<labelPair> subChangedPatchAndFaces;
584  DynamicList<meshPhiPreCorrectInfo> subChangedFacePci;
585  DynamicList<meshPhiCorrectInfo> subChangedFaceCi;
586 
587  // Allocate data for the pre-correction wave
588  List<meshPhiPreCorrectInfo> subInternalFacePci(subMesh.nInternalFaces());
589  List<List<meshPhiPreCorrectInfo>> subPatchFacePci
590  (
592  sizesListList<List<List<meshPhiPreCorrectInfo>>>
593  (
595  listListSizes(subMesh.boundary()),
597  )
598  );
599  List<meshPhiPreCorrectInfo> subCellPci(subMesh.nCells());
600 
601  // Initialisation for the pre-correction wave
602  subChangedPatchAndFaces.clear();
603  subChangedFacePci.clear();
604  forAll(subMesh.boundary(), subPatchi)
605  {
606  const fvPatch& subFvp = subMesh.boundary()[subPatchi];
607 
608  forAll(subFvp, subPatchFacei)
609  {
610  const label subFacei = subFvp.start() + subPatchFacei;
611  const label facei = subsetter.faceMap()[subFacei];
612  const label bFacei = facei - mesh().nInternalFaces();
613 
614  if (bFacei >= 0 && bFaceIsOwnerOrig[bFacei])
615  {
616  subChangedPatchAndFaces.append({subPatchi, subPatchFacei});
617  subChangedFacePci.append
618  (
619  meshPhiPreCorrectInfo(0, bFaceNccMagSf[bFacei])
620  );
621  }
622  }
623  }
624 
625  // Pre-correction wave
627  (
628  subMesh,
629  subInternalFacePci,
630  subPatchFacePci,
631  subCellPci
632  );
633  preWave.setFaceInfo(subChangedPatchAndFaces, subChangedFacePci);
634  const label nWaveLayers =
635  preWave.iterate(subMesh.globalData().nTotalCells() + 1);
636 
637  // Allocate data for the correction wave
638  List<meshPhiCorrectInfo> subInternalFaceCi(subMesh.nInternalFaces());
639  List<List<meshPhiCorrectInfo>> subPatchFaceCi
640  (
642  sizesListList<List<List<meshPhiCorrectInfo>>>
643  (
645  listListSizes(subMesh.boundary()),
647  )
648  );
649  List<meshPhiCorrectInfo> subCellCi(subMesh.nCells());
650 
651  // Calculate the current error in the rate of change of volume
652  const volScalarField::Internal dVdtError
653  (
654  (subV - subV0)/subMesh.time().deltaT()
655  - fvc::surfaceIntegrate(subPhi + subDeltaPhi)()*subV
656  );
657 
658  // Construct track data for the correction wave
660  (
661  subInternalFacePci,
662  subPatchFacePci,
663  subCellPci,
664  dVdtError
665  );
666 
667  // Wave backwards through the layers to generate the corrections. Note that
668  // this has to be done in stages, so that later layers complete in their
669  // entirety before the earlier layers begin. Otherwise they interfere.
670  for (label waveLayeri = nWaveLayers - 1; waveLayeri >= 0; waveLayeri --)
671  {
672  // The layer indices on the faces that we want to wave from
673  const label faceLayeri = (waveLayeri + 1)*2;
674 
675  // Initialisation for the correction wave
676  subChangedPatchAndFaces.clear();
677  subChangedFaceCi.clear();
678  forAll(subInternalFacePci, subFacei)
679  {
680  if (subInternalFacePci[subFacei].layer() == faceLayeri)
681  {
682  subChangedPatchAndFaces.append({-1, subFacei});
683  subChangedFaceCi.append
684  (
685  subInternalFaceCi[subFacei].valid(td)
686  ? subInternalFaceCi[subFacei]
688  );
689  }
690  }
691  forAll(subPatchFacePci, subPatchi)
692  {
693  forAll(subPatchFacePci[subPatchi], subPatchFacei)
694  {
695  if
696  (
697  subPatchFacePci[subPatchi][subPatchFacei].layer()
698  == faceLayeri
699  )
700  {
701  subChangedPatchAndFaces.append({subPatchi, subPatchFacei});
702  subChangedFaceCi.append
703  (
704  subPatchFaceCi[subPatchi][subPatchFacei].valid(td)
705  ? subPatchFaceCi[subPatchi][subPatchFacei]
707  );
708  }
709  }
710  }
711 
712  // Correction wave
714  (
715  subMesh,
716  subInternalFaceCi,
717  subPatchFaceCi,
718  subCellCi,
719  td
720  );
721  wave.setFaceInfo(subChangedPatchAndFaces, subChangedFaceCi);
722  wave.iterate(1);
723  }
724 
725  // Apply corrections
726  forAll(subInternalFaceCi, subFacei)
727  {
728  subDeltaPhi.primitiveFieldRef()[subFacei] +=
729  subInternalFaceCi[subFacei].deltaPhi();
730  }
731  forAll(subPatchFacePci, subPatchi)
732  {
733  forAll(subPatchFacePci[subPatchi], subPatchFacei)
734  {
735  subDeltaPhi.boundaryFieldRef()[subPatchi][subPatchFacei] +=
736  subPatchFaceCi[subPatchi][subPatchFacei].deltaPhi();
737  }
738  }
739 
740  // Step 6: Apply the corrections to the mesh flux
741 
742  // Correct the internal mesh face fluxes
743  forAll(subPhi, subFacei)
744  {
745  phi[subsetter.faceMap()[subFacei]] =
746  subPhi[subFacei] + subDeltaPhi[subFacei];
747  }
748 
749  // Map the sub-mesh flux changes to the conformal mesh boundary
751  (
752  mesh().boundary(),
755  );
756  deltaPhiBf = 0;
757  forAll(subMesh.boundary(), subPatchi)
758  {
759  const label patchi = subsetter.patchMap()[subPatchi];
760 
761  if (patchi == -1) continue;
762 
763  const fvPatch& subFvp = subMesh.boundary()[subPatchi];
764  const fvPatch& fvp = mesh().boundary()[patchi];
765 
766  const bool coupled = subFvp.coupled();
767 
768  forAll(subMesh.boundary()[subPatchi], subPatchFacei)
769  {
770  const label facei =
771  subsetter.faceMap()[subFvp.start() + subPatchFacei];
772 
773  const label patchFacei = facei - fvp.start();
774 
775  if (coupled)
776  {
777  deltaPhiBf[patchi][patchFacei] =
778  subPhi.boundaryField()[subPatchi][subPatchFacei]
779  + subDeltaPhi.boundaryField()[subPatchi][subPatchFacei]
780  - phiBf[patchi][patchFacei];
781  }
782  else
783  {
784  deltaPhiBf[patchi][patchFacei] =
785  subDeltaPhi.boundaryField()[subPatchi][subPatchFacei];
786  }
787  }
788  }
789 
790  // Move the changes from the owner-orig to the non-conformal coupled faces
791  forAll(mesh().boundary(), nccPatchi)
792  {
793  const fvPatch& fvp = mesh().boundary()[nccPatchi];
794 
795  if (!isA<nonConformalCoupledFvPatch>(fvp)) continue;
796 
797  const nonConformalCoupledFvPatch& nccFvp =
798  refCast<const nonConformalCoupledFvPatch>(fvp);
799 
800  if (!nccFvp.owner()) continue;
801 
802  const label origPatchi = nccFvp.origPatchID();
803  const fvPatch& origFvp = nccFvp.origPatch();
804 
805  forAll(nccFvp, nccPatchFacei)
806  {
807  const label bFacei =
808  nccFvp.polyFaces()[nccPatchFacei] - mesh().nInternalFaces();
809 
810  const label origPatchFacei =
811  nccFvp.polyFaces()[nccPatchFacei] - origFvp.start();
812 
813  const scalar deltaPhi =
814  magSfBf[nccPatchi][nccPatchFacei]
815  /bFaceNccMagSf[bFacei]
816  *deltaPhiBf[origPatchi][origPatchFacei];
817 
818  deltaPhiBf[nccPatchi][nccPatchFacei] = deltaPhi;
819  }
820  }
821  forAll(mesh().boundary(), nccPatchi)
822  {
823  const fvPatch& fvp = mesh().boundary()[nccPatchi];
824 
825  if (!isA<nonConformalCoupledFvPatch>(fvp)) continue;
826 
827  const nonConformalCoupledFvPatch& nccFvp =
828  refCast<const nonConformalCoupledFvPatch>(fvp);
829 
830  if (!nccFvp.owner()) continue;
831 
832  const label origPatchi = nccFvp.origPatchID();
833  const fvPatch& origFvp = nccFvp.origPatch();
834 
835  forAll(nccFvp, nccPatchFacei)
836  {
837  const label origPatchFacei =
838  nccFvp.polyFaces()[nccPatchFacei] - origFvp.start();
839 
840  deltaPhiBf[origPatchi][origPatchFacei] = 0;
841  }
842  }
843 
844  // Correct the boundary mesh face fluxes
845  phiBf += deltaPhiBf;
846 }
847 
848 
849 void Foam::fvMeshStitchers::moving::unconformErrorFaceCorrectMeshPhi
850 (
851  const surfaceLabelField::Boundary& polyFacesBf,
852  surfaceVectorField& SfSf,
853  surfaceVectorField& CfSf,
854  surfaceScalarField& phi
855 )
856 {
858  const labelList origPatchIDs = ncb.allOrigPatchIDs();
859  const labelList errorPatchIDs = ncb.allErrorPatchIDs();
860 
861  // Synchronise the mesh fluxes on both sides of coupled patches. Store
862  // the change made to the mesh flux as an error.
863  PtrList<surfaceScalarField::Boundary> phiErrorbs(phi.nOldTimes() + 1);
864  for (label i = 0; i <= phi.nOldTimes(); ++ i)
865  {
867  synchronisedBoundaryField<scalar>(phi.oldTime(i).boundaryField());
868 
869  phiErrorbs.set
870  (
871  i,
873  (
875  phi.oldTime(i).boundaryField()
876  )
877  );
878  phiErrorbs[i] = phi.oldTime(i).boundaryField() - tphib();
879 
880  phi.oldTime(i).boundaryFieldRef() = tphib;
881  }
882 
883  // Add the mesh flux error into the error patch so that the mesh fluxes
884  // and volume changes still match
885  forAll(mesh().boundary(), nccPatchi)
886  {
887  const fvPatch& fvp = mesh().boundary()[nccPatchi];
888 
889  if (isA<nonConformalCoupledFvPatch>(fvp))
890  {
891  const nonConformalCoupledFvPatch& nccFvp =
892  refCast<const nonConformalCoupledFvPatch>(fvp);
893 
894  const label origPatchi = nccFvp.origPatchID();
895  const polyPatch& origPp = mesh().boundaryMesh()[origPatchi];
896 
897  const label errorPatchi = nccFvp.errorPatchID();
898 
899  forAll(nccFvp, nccPatchFacei)
900  {
901  const label origPatchFacei =
902  nccFvp.polyFaces()[nccPatchFacei] - origPp.start();
903 
904  const label errorPatchFacei0 = 2*origPatchFacei;
905  const label errorPatchFacei1 = 2*origPatchFacei + 1;
906 
907  for (label i = 0; i <= phi.nOldTimes(); ++ i)
908  {
909  fvsPatchField<scalar>& phip =
910  phi.oldTime(i).boundaryFieldRef()[errorPatchi];
911  phip[errorPatchFacei0] +=
912  phiErrorbs[i][nccPatchi][nccPatchFacei]/2;
913  phip[errorPatchFacei1] +=
914  phiErrorbs[i][nccPatchi][nccPatchFacei]/2;
915  }
916  }
917  }
918  }
919 
920  // Create a boundary field with a mesh velocity magnitude. Take the
921  // maximum mesh velocity on either side of non-conformal-coupled faces,
922  // and then average that into the original faces. This means even
923  // stationary original faces have a velocity magnitude stored that is
924  // representative of the interface motion.
925  tmp<surfaceScalarField> tnccMeshMagUf =
927  (
928  "nccMeshMagUf",
929  mesh(),
931  );
932  surfaceScalarField::Boundary& tnccMeshMagUfb =
933  tnccMeshMagUf.ref().boundaryFieldRef();
934  forAll(mesh().boundary(), nccPatchi)
935  {
936  const fvPatch& fvp = mesh().boundary()[nccPatchi];
937 
938  if (isA<nonConformalCoupledFvPatch>(fvp))
939  {
940  const nonConformalCoupledFvPatch& nccFvp =
941  refCast<const nonConformalCoupledFvPatch>(fvp);
942 
943  const fvPatch& origFvp = nccFvp.origPatch();
944 
945  forAll(nccFvp, nccPatchFacei)
946  {
947  const label origPatchFacei =
948  nccFvp.polyFaces()[nccPatchFacei]
949  - origFvp.start();
950 
951  const point& origC =
952  origFvp.patch().faceCentres()[origPatchFacei];
953  const point origC0 =
954  origFvp.patch()[origPatchFacei]
955  .centre(mesh().oldPoints());
956 
957  tnccMeshMagUfb[nccPatchi][nccPatchFacei] =
958  mag(origC - origC0)/mesh().time().deltaTValue();
959  }
960  }
961  }
962  tnccMeshMagUfb =
963  max
964  (
965  tnccMeshMagUfb,
966  tnccMeshMagUfb.boundaryNeighbourField()()
967  );
969  (
971  conformalNccBoundaryField<scalar>(tnccMeshMagUfb)
972  );
973  tnccMeshMagUf.clear();
974 
975  // Resize the error patch faces so that mesh flux divided by area
976  // results in a velocity equal to the mesh velocity of the original
977  // patch face
978  forAll(origPatchIDs, i)
979  {
980  const label origPatchi = origPatchIDs[i];
981  const polyPatch& origPp = mesh().boundaryMesh()[origPatchi];
982 
983  const label errorPatchi = errorPatchIDs[i];
984 
985  forAll(origPp, origPatchFacei)
986  {
987  const label errorPatchFacei0 = 2*origPatchFacei;
988  const label errorPatchFacei1 = 2*origPatchFacei + 1;
989 
990  const vector errorSf =
991  origPp.faceNormals()[origPatchFacei]
992  *phi.boundaryField()[errorPatchi][errorPatchFacei0]
993  /max(meshMagUfb[origPatchi][origPatchFacei], vSmall);
994 
995  fvsPatchField<vector>& Sfp =
996  SfSf.boundaryFieldRef()[errorPatchi];
997  Sfp[errorPatchFacei0] += errorSf;
998  Sfp[errorPatchFacei1] -= errorSf;
999  }
1000  }
1001 
1002  // Wherever we find a movingWall-type boundary condition on an original
1003  // patch, override the corresponding error patch condition to
1004  // movingWallSlipVelocity
1005  HashTable<volVectorField*> fields(mesh().lookupClass<volVectorField>());
1006  forAllIter(typename HashTable<volVectorField*>, fields, iter)
1007  {
1008  typename volVectorField::Boundary& Ub = iter()->boundaryFieldRef();
1009 
1010  forAll(origPatchIDs, i)
1011  {
1012  const label origPatchi = origPatchIDs[i];
1013 
1014  typename volVectorField::Patch& origUp = Ub[origPatchi];
1015 
1016  if
1017  (
1018  isA<movingWallVelocityFvPatchVectorField>(origUp)
1019  || isA<movingWallSlipVelocityFvPatchVectorField>(origUp)
1020  )
1021  {
1022  const label errorPatchi = errorPatchIDs[i];
1023 
1024  Ub.set
1025  (
1026  errorPatchi,
1028  (
1029  mesh().boundary()[errorPatchi],
1030  *iter()
1031  )
1032  );
1033  }
1034  }
1035  }
1036 }
1037 
1038 
1039 void Foam::fvMeshStitchers::moving::unconformCorrectMeshPhi
1040 (
1041  const SurfaceFieldBoundary<label>& polyFacesBf,
1042  surfaceVectorField& SfSf,
1043  surfaceVectorField& CfSf,
1044  surfaceScalarField& phi
1045 )
1046 {
1047  // !!! At present, the correction procedures that follow require the mesh
1048  // to be unconformed to its final topological state. This means we have to
1049  // call fvMesh::unconform twice; once to change the topology to allow for
1050  // calculation of the error areas and mesh flux corrections, and once again
1051  // to actually apply these error areas and mesh flux corrections. This is
1052  // OK for now (the operation is relatively quick), but ideally everything
1053  // would just be calculated in advance and applied to the mesh in one go,
1054  // and this function would only modify its arguments and leave calling
1055  // fvMesh::unconform to the base class.
1056  mesh().unconform(polyFacesBf, SfSf, CfSf);
1057  resizeFieldPatchFields(polyFacesBf, phi);
1058 
1059  // Set mesh fluxes on the original and cyclic faces as a proportion of
1060  // the area taken from the old original faces
1061  for (label i = 0; i <= phi.nOldTimes(); ++ i)
1062  {
1063  phi.oldTime(i).boundaryFieldRef() =
1064  nonConformalBoundaryField<scalar>
1065  (
1066  phi.oldTime(i).boundaryField(),
1067  phi.oldTime(i).boundaryField()
1068  );
1069  }
1070 
1071  // Correct the mesh flux error by modifying values on the internal
1072  // faces that are edge-connected to the owner orig patches.
1073  //
1074  // !!! This only corrects the new-time flux. For smooth operation with
1075  // second order time schemes on wonky meshes, this will probably need to
1076  // correct old time mesh fluxes, too.
1077  //
1078  if
1079  (
1080  mesh().foundObject<solutionControl>(solutionControl::typeName)
1081  && mesh().lookupObject<solutionControl>(solutionControl::typeName)
1082  .dict().lookup<Switch>("correctMeshPhi")
1083  )
1084  {
1085  unconformInternalFaceCorrectMeshPhi(phi);
1086  }
1087 
1088  // Correct the mesh flux error by adding flux and area to the error
1089  // patch faces
1090  unconformErrorFaceCorrectMeshPhi(polyFacesBf, SfSf, CfSf, phi);
1091 }
1092 
1093 
1094 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
1095 
1097 :
1098  fvMeshStitcher(mesh)
1099 {}
1100 
1101 
1102 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
1103 
1105 {}
1106 
1107 
1108 // ************************************************************************* //
const fvPatchList & patches
const polyBoundaryMesh & boundaryMesh() const
Return boundary mesh.
Definition: polyMesh.H:453
dimensionedScalar sign(const dimensionedScalar &ds)
This class separates the mesh into distinct unconnected regions, each of which is then given a label ...
Definition: regionSplit.H:116
dictionary dict
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
Wave propagation of information through grid. Every iteration information goes through one layer of c...
const GeometricField< Type, PatchField, GeoMesh > & oldTime() const
Return old time field.
FvWallInfoData< WallInfo, label > label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
const dimensionSet dimArea
void clear() const
If object pointer points to valid object:
Definition: tmpI.H:237
const fvSchemes & schemes() const
Return the fvSchemes.
Definition: fvMesh.C:1672
Tracking data. Mostly just a collection of references to the.
bool set(const label) const
Is element set.
Definition: PtrListI.H:65
List< Type > repeat(const UList< Type > &a, const UList< Type > &b)
void surfaceIntegrate(Field< Type > &ivf, const GeometricField< Type, fvsPatchField, surfaceMesh > &ssf)
Mesh object that stores an all boundary patch and mapping to and from it and the mesh and the individ...
virtual label start() const
Return start label of this patch in the polyMesh face list.
Definition: fvPatch.H:151
label nInternalFaces() const
labelList allErrorPatchIDs() const
Return a list of the error patch indices.
const labelList & ownerOrigBoundaryEdgeMeshEdge() const
Get a map from owner-orig boundary edge to mesh edge.
const Boundary & boundaryField() const
Return const-reference to the boundary field.
T & ref() const
Return non-const reference or generate a fatal error.
Definition: tmpI.H:181
#define forAllIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:459
An Istream is an abstract base class for all input systems (streams, files, token lists etc)...
Definition: Istream.H:57
friend bool operator!=(const layerAndWeight &a, const layerAndWeight &b)
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
void setReference(const label celli, const Type &value, const bool forceReference=false)
Set reference level for solution.
Definition: fvMatrix.C:563
Solution control class.
Mesh manipulator that uses the intersection provided by the cyclic non-conformal poly patches to crea...
static int myProcNo(const label communicator=0)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:429
label nCells() const
static tmp< GeometricField< scalar, fvsPatchField, surfaceMesh > > New(const word &name, const Internal &, const PtrList< fvsPatchField< scalar >> &)
Return a temporary field constructed from name,.
const dimensionedScalar b
Wien displacement law constant: default SI units: [m K].
Definition: createFields.H:27
A finiteVolume patch using a polyPatch and a fvBoundaryMesh.
Definition: fvPatch.H:63
label nTotalCells() const
Return total number of cells in decomposed mesh.
A simple wrapper around bool so that it can be read as a word: true/false, on/off, yes/no, y/n, t/f, or none/any.
Definition: Switch.H:60
Calculate the matrix for the laplacian of the field.
Abstract base class with a fat-interface to all derived classes covering all possible ways in which t...
Definition: fvPatchField.H:66
labelList identity(const label len)
Create identity map (map[i] == i) of given length.
Definition: ListOps.C:104
void setFluxRequired(const word &name) const
Definition: fvSchemes.C:487
const labelList & faceFlipMap() const
Return face map with sign to encode flipped faces.
label nOldTimes() const
Return the number of old time fields stored.
wordList types() const
Return a list of patch types.
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:372
fvMesh & mesh
Macros for easy insertion into run-time selection tables.
const labelList & faceMap() const
Return face map.
static const layerAndWeight max
static bool constraintType(const word &pt)
Return true if the given type is a constraint type.
Definition: polyPatch.C:265
const Vector< Cmpt > & centre(const Foam::List< Vector< Cmpt >> &) const
Return *this (used for point which is a typedef to Vector<scalar>.
Definition: VectorI.H:116
const labelList & patchMap() const
Return patch map.
const fvMesh & subMesh() const
Return reference to subset mesh.
const dimensionSet dimTime
friend Istream & operator>>(Istream &is, layerAndWeight &l)
gmvFile<< "tracers "<< particles.size()<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().x()<< " ";}gmvFile<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().y()<< " ";}gmvFile<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().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))
moving(fvMesh &)
Construct from fvMesh.
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects...
Definition: DynamicList.H:56
static const DimensionedField< Type, GeoMesh > & null()
Return a null DimensionedField.
label errorPatchID() const
Error patch ID.
This boundary condition provides a slip velocity condition for cases with moving walls.
static void syncEdgeList(const polyMesh &, List< T > &, const CombineOp &cop, const T &nullValue, const TransformOp &top)
Synchronise values on all mesh edges.
void clear()
Clear the list, i.e. set size to zero.
Definition: ListI.H:125
static word timeName(const scalar, const int precision=curPrecision_)
Return time name of given scalar time.
Definition: Time.C:666
const polyPatch & patch() const
Return the polyPatch.
Definition: fvPatch.H:139
static const layerAndWeight min
labelList allOrigPatchIDs() const
Return a list of the orig patch indices.
friend Ostream & operator<<(Ostream &os, const layerAndWeight &l)
This boundary condition is not designed to be evaluated; it is assumed that the value is assigned via...
virtual const labelList & faceOwner() const
Return face owner.
Definition: polyMesh.C:1237
DynamicList< T, SizeInc, SizeMult, SizeDiv > & append(const T &)
Append an element at the end of the list.
Definition: DynamicListI.H:296
A special matrix type and solver, designed for finite volume solutions of scalar equations. Face addressing is used to make all matrix assembly and solution loops vectorise.
Definition: fvPatchField.H:72
const globalMeshData & globalData() const
Return parallel info.
Definition: polyMesh.C:1435
static const label labelMax
Definition: label.H:62
static const zero Zero
Definition: zero.H:97
faceListList boundary(nPatches)
tmp< GeometricBoundaryField > boundaryNeighbourField() const
Return BoundaryField of the values on the other side of couples.
const Field< PointType > & faceNormals() const
Return face normals for patch.
An STL-conforming hash table.
Definition: HashTable.H:61
virtual label size() const
Return size.
Definition: fvPatch.H:157
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Definition: Ostream.H:54
SolverPerformance< Type > solve(const dictionary &)
Solve segregated or coupled returning the solution statistics.
Definition: fvMatrixSolve.C:58
addToRunTimeSelectionTable(ensightPart, ensightPartCells, istream)
void setFaceInfo(const List< labelPair > &changedPatchAndFaces, const List< Type > &changedFacesInfo)
Set initial changed faces.
const dimensionSet dimVelocity
defineTypeNameAndDebug(combustionModel, 0)
bool owner() const
Does this side own the patch?
static nonConformalBoundary & New(polyMesh &mesh)
Definition: MeshObject.C:54
Post-processing mesh subset tool. Given the original mesh and the list of selected cells...
Definition: fvMeshSubset.H:73
void reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
Non-conformal coupled FV patch. As nonConformalFvPatch, but is also coupled to another non-conformal ...
This boundary condition applies a zero-gradient condition from the patch internal field onto the patc...
const labelList & polyFaces() const
Return face face-poly-faces.
label size() const
Return the number of elements in the UPtrList.
Definition: UPtrListI.H:29
static tmp< GeometricField< Type, fvPatchField, volMesh > > interpolate(const GeometricField< Type, fvPatchField, volMesh > &, const fvMesh &sMesh, const labelList &patchMap, const labelList &cellMap, const labelList &faceMap)
Map volume field.
Info<< "Reading field p_rgh\"<< endl;volScalarField p_rgh(IOobject("p_rgh", runTime.timeName(), mesh, IOobject::MUST_READ, IOobject::AUTO_WRITE), mesh);pressureReference pressureReference(p, p_rgh, pimple.dict(), thermo.incompressible());mesh.schemes().setFluxRequired(p_rgh.name());hydrostaticInitialisation(p_rgh, p, rho, U, gh, ghf, pRef, thermo, pimple.dict());Info<< "Creating field dpdt\"<< endl;volScalarField dpdt(IOobject("dpdt", runTime.timeName(), mesh), mesh, dimensionedScalar(p.dimensions()/dimTime, 0));Info<< "Creating field kinetic energy K\"<< endl;volScalarField K("K", 0.5 *magSqr(U));dimensionedScalar initialMass=fvc::domainIntegrate(rho);multivariateSurfaceInterpolationScheme< scalar >::fieldTable fields
Definition: createFields.H:131
label patchi
Boundary & boundaryFieldRef()
Return a reference to the boundary field.
const fvPatch & origPatch() const
Original patch.
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
tmp< fvMatrix< Type > > laplacian(const GeometricField< Type, fvPatchField, volMesh > &vf, const word &name)
Definition: fvmLaplacian.C:46
A templated 1D list of pointers to objects of type <T>, where the size of the array is known and used...
Definition: List.H:70
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:95
label start() const
Return start label of this patch in the polyMesh face list.
Definition: polyPatch.H:306
Field with dimensions and associated with geometry type GeoMesh which is used to size the field and a...
Operator to apply a binary operation to a pair of lists.
Definition: ListOps.H:284
label nRegions() const
Return total number of regions.
Definition: regionSplit.H:198
const dimensionSet dimVolume
dimensioned< scalar > mag(const dimensioned< Type > &)
tmp< GeometricField< Type, fvsPatchField, surfaceMesh > > flux() const
Return the face-flux field from the matrix.
Definition: fvMatrix.C:927
label origPatchID() const
Original patch ID.
A class for managing temporary objects.
Definition: PtrList.H:53
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:66
label size() const
Return the number of elements in the UList.
Definition: UListI.H:311
void clear()
Clear the addressed list, i.e. set the size to zero.
Definition: DynamicListI.H:236
friend bool operator==(const layerAndWeight &a, const layerAndWeight &b)
IOobject defines the attributes of an object for which implicit objectRegistry management is supporte...
Definition: IOobject.H:98
virtual label iterate(const label maxIter)
Iterate until no changes or maxIter reached. Returns actual.
Non-conformal FV patch. Provides the necessary interface for a FV patch which does not conform to the...
virtual bool coupled() const
Return true if this patch is coupled.
Definition: fvPatch.H:163
label wave(const fvMesh &mesh, const List< labelPair > &changedPatchAndFaces, const label nCorrections, GeometricField< scalar, PatchField, GeoMesh > &distance, TrackingData &td, GeometricField< DataType, PatchField, GeoMesh > &... data)
Wave distance (and maybe additional) data from faces. If nCorrections is.
An abstract base class with a fat-interface to all derived classes covering all possible ways in whic...
Definition: fvsPatchField.H:65
void setLargeCellSubset(const labelList &region, const label currentRegion, const label patchID=-1, const bool syncCouples=true)
Set the subset from all cells with region == currentRegion.
Definition: fvMeshSubset.C:895
const surfaceScalarField & deltaCoeffs() const
Return reference to cell-centre difference coefficients.
dimensionedScalar deltaT() const
Return time step.
Definition: TimeStateI.H:46
Namespace for OpenFOAM.
A zero-sized class without any storage. Used, for example, in HashSet.
Definition: nil.H:58
const fvBoundaryMesh & boundary() const
Return reference to boundary mesh.
Definition: fvMesh.C:800
const vectorField::subField faceCentres() const
Return face centres.
Definition: polyPatch.C:303