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-2023 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 
96 const layerAndWeight layerAndWeight::min({-labelMax, NaN});
97 
98 
99 const layerAndWeight layerAndWeight::max({labelMax, NaN});
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 {
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
137  surfaceScalarField::Boundary& phiBf = phi.boundaryFieldRef();
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] +=
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 {
175  const nonConformalBoundary& ncb = nonConformalBoundary::New(mesh());
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((identityMap(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 {
203  const nonConformalBoundary& ncb = nonConformalBoundary::New(mesh());
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  (
233  ncb.ownerOrigBoundaryEdgeMeshEdge().size(),
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(),
258  ncb.ownerOrigBoundaryEdgeMeshEdge(),
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 
299  surfaceScalarField::Boundary& phiBf = phi.boundaryFieldRef();
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().name(),
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  (
591  FvFaceCellWave<meshPhiPreCorrectInfo>::template
592  sizesListList<List<List<meshPhiPreCorrectInfo>>>
593  (
594  FvFaceCellWave<meshPhiPreCorrectInfo>::template
595  listListSizes(subMesh.boundary()),
596  meshPhiPreCorrectInfo()
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
626  FvFaceCellWave<meshPhiPreCorrectInfo> preWave
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  (
641  FvFaceCellWave<meshPhiCorrectInfo>::template
642  sizesListList<List<List<meshPhiCorrectInfo>>>
643  (
644  FvFaceCellWave<meshPhiCorrectInfo>::template
645  listListSizes(subMesh.boundary()),
646  meshPhiCorrectInfo()
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
659  meshPhiCorrectInfo::trackData td
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]
687  : meshPhiCorrectInfo(meshPhiCorrectInfo::shape::face)
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]
706  : meshPhiCorrectInfo(meshPhiCorrectInfo::shape::face)
707  );
708  }
709  }
710  }
711 
712  // Correction wave
713  FvFaceCellWave<meshPhiCorrectInfo, meshPhiCorrectInfo::trackData> 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 {
857  const nonConformalBoundary& ncb = nonConformalBoundary::New(mesh());
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  {
866  tmp<surfaceScalarField::Boundary> tphib =
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  min
992  (
993  mag(phi.boundaryField()[errorPatchi][errorPatchFacei0])
994  /max(meshMagUfb[origPatchi][origPatchFacei], vSmall),
995  origPp.magFaceAreas()[origPatchFacei]
996  )
997  *origPp.faceNormals()[origPatchFacei];
998 
999  fvsPatchField<vector>& Sfp =
1000  SfSf.boundaryFieldRef()[errorPatchi];
1001  Sfp[errorPatchFacei0] += errorSf;
1002  Sfp[errorPatchFacei1] -= errorSf;
1003  }
1004  }
1005 
1006  // Wherever we find a movingWall-type boundary condition on an original
1007  // patch, override the corresponding error patch condition to
1008  // movingWallSlipVelocity
1009  HashTable<volVectorField*> fields(mesh().lookupClass<volVectorField>());
1010  forAllIter(typename HashTable<volVectorField*>, fields, iter)
1011  {
1012  typename volVectorField::Boundary& Ub = iter()->boundaryFieldRef();
1013 
1014  forAll(origPatchIDs, i)
1015  {
1016  const label origPatchi = origPatchIDs[i];
1017 
1018  typename volVectorField::Patch& origUp = Ub[origPatchi];
1019 
1020  if
1021  (
1022  isA<movingWallVelocityFvPatchVectorField>(origUp)
1023  || isA<movingWallSlipVelocityFvPatchVectorField>(origUp)
1024  )
1025  {
1026  const label errorPatchi = errorPatchIDs[i];
1027 
1028  Ub.set
1029  (
1030  errorPatchi,
1031  new movingWallSlipVelocityFvPatchVectorField
1032  (
1033  mesh().boundary()[errorPatchi],
1034  *iter()
1035  )
1036  );
1037  }
1038  }
1039  }
1040 }
1041 
1042 
1043 void Foam::fvMeshStitchers::moving::unconformCorrectMeshPhi
1044 (
1045  const SurfaceFieldBoundary<label>& polyFacesBf,
1046  surfaceVectorField& SfSf,
1047  surfaceVectorField& CfSf,
1048  surfaceScalarField& phi
1049 )
1050 {
1051  // !!! At present, the correction procedures that follow require the mesh
1052  // to be unconformed to its final topological state. This means we have to
1053  // call fvMesh::unconform twice; once to change the topology to allow for
1054  // calculation of the error areas and mesh flux corrections, and once again
1055  // to actually apply these error areas and mesh flux corrections. This is
1056  // OK for now (the operation is relatively quick), but ideally everything
1057  // would just be calculated in advance and applied to the mesh in one go,
1058  // and this function would only modify its arguments and leave calling
1059  // fvMesh::unconform to the base class.
1060  mesh().unconform(polyFacesBf, SfSf, CfSf);
1061  resizeFieldPatchFields(polyFacesBf, phi);
1062 
1063  // Set mesh fluxes on the original and cyclic faces as a proportion of
1064  // the area taken from the old original faces
1065  for (label i = 0; i <= phi.nOldTimes(); ++ i)
1066  {
1067  phi.oldTime(i).boundaryFieldRef() =
1068  nonConformalBoundaryField<scalar>
1069  (
1070  phi.oldTime(i).boundaryField(),
1071  phi.oldTime(i).boundaryField()
1072  );
1073  }
1074 
1075  // Correct the mesh flux error by modifying values on the internal
1076  // faces that are edge-connected to the owner orig patches.
1077  //
1078  // !!! This only corrects the new-time flux. For smooth operation with
1079  // second order time schemes on wonky meshes, this will probably need to
1080  // correct old time mesh fluxes, too.
1081  //
1082  if
1083  (
1084  mesh().foundObject<solutionControl>(solutionControl::typeName)
1085  && mesh().lookupObject<solutionControl>(solutionControl::typeName)
1086  .dict().lookup<Switch>("correctMeshPhi")
1087  )
1088  {
1089  unconformInternalFaceCorrectMeshPhi(phi);
1090  }
1091 
1092  // Correct the mesh flux error by adding flux and area to the error
1093  // patch faces
1094  unconformErrorFaceCorrectMeshPhi(polyFacesBf, SfSf, CfSf, phi);
1095 }
1096 
1097 
1098 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
1099 
1101 :
1102  fvMeshStitcher(mesh)
1103 {}
1104 
1105 
1106 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
1107 
1109 {}
1110 
1111 
1112 // ************************************************************************* //
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
#define forAllIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:459
Macros for easy insertion into run-time selection tables.
static const DimensionedField< Type, GeoMesh > & null()
Return a null DimensionedField.
static const char *const typeName
Definition: Field.H:105
DimensionedField< Type, GeoMesh > Internal
Type of the internal field from which this GeometricField is derived.
GeometricBoundaryField< Type, PatchField, GeoMesh > Boundary
Type of the boundary field.
static tmp< GeometricField< Type, PatchField, GeoMesh > > New(const word &name, const Internal &, const PtrList< PatchField< Type >> &)
Return a temporary field constructed from name,.
PatchField< Type > Patch
Type of the patch field of which the Boundary is composed.
An Istream is an abstract base class for all input systems (streams, files, token lists etc)....
Definition: Istream.H:60
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:57
label size() const
Return the number of elements in the UList.
Definition: UListI.H:311
static int myProcNo(const label communicator=0)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:429
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
Mesh manipulator that uses the intersection provided by the cyclic non-conformal poly patches to crea...
static tmp< Field< Type > > fieldRMapSum(const Field< Type > &f, const label size, const labelUList &addr)
Reverse-map sum the values of a field.
Mesh stitcher for moving meshes.
moving(fvMesh &)
Construct from fvMesh.
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:101
A zero-sized class without any storage. Used, for example, in HashSet.
Definition: nil.H:59
static bool constraintType(const word &pt)
Return true if the given type is a constraint type.
Definition: polyPatch.C:238
static void syncEdgeList(const polyMesh &, List< T > &, const CombineOp &cop, const T &nullValue, const TransformOp &top)
Synchronise values on all mesh edges.
Calculate the matrix for the laplacian of the field.
label patchi
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.name(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
const fvPatchList & patches
volScalarField & b
Definition: createFields.H:27
Info<< "Calculating turbulent flame speed field St\n"<< endl;volScalarField St(IOobject("St", runTime.name(), mesh, IOobject::NO_READ, IOobject::AUTO_WRITE), flameWrinkling->Xi() *Su);multivariateSurfaceInterpolationScheme< scalar >::fieldTable fields
Definition: createFields.H:230
bool valid(const PtrList< ModelType > &l)
addToRunTimeSelectionTable(fvMeshStitcher, stationary, fvMesh)
defineTypeNameAndDebug(stationary, 0)
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.
void surfaceIntegrate(Field< Type > &ivf, const SurfaceField< Type > &ssf)
tmp< fvMatrix< Type > > laplacian(const VolField< Type > &vf, const word &name)
Definition: fvmLaplacian.C:47
static Type NaN()
Return a primitive with all components set to NaN.
Namespace for OpenFOAM.
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
static const zero Zero
Definition: zero.H:97
List< word > wordList
A List of words.
Definition: fileName.H:54
List< label > labelList
A List of labels.
Definition: labelList.H:56
dimensionedScalar sign(const dimensionedScalar &ds)
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:59
fvMatrix< scalar > fvScalarMatrix
Definition: fvMatricesFwd.H:42
SurfaceField< scalar > surfaceScalarField
List< bool > boolList
Bool container classes.
Definition: boolList.H:50
List< scalar > scalarList
A List of scalars.
Definition: scalarList.H:50
vector point
Point is a vector.
Definition: point.H:41
layerAndWeight min(const layerAndWeight &a, const layerAndWeight &b)
const dimensionSet dimTime
Vector< scalar > vector
A scalar version of the templated Vector.
Definition: vector.H:49
void reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
const dimensionSet dimVolume
VolField< scalar > volScalarField
Definition: volFieldsFwd.H:61
dimensioned< scalar > mag(const dimensioned< Type > &)
layerAndWeight max(const layerAndWeight &a, const layerAndWeight &b)
HashSet< label, Hash< label > > labelHashSet
A HashSet with label keys.
Definition: HashSet.H:211
const dimensionSet dimVelocity
const dimensionSet dimArea
static const label labelMax
Definition: label.H:62
SurfaceField< vector > surfaceVectorField
labelList identityMap(const label len)
Create identity map (map[i] == i) of given length.
Definition: ListOps.C:104
List< Type > repeat(const UList< Type > &a, const UList< Type > &b)
UList< label > labelUList
Definition: UList.H:65
faceListList boundary(nPatches)
dictionary dict
friend Ostream & operator<<(Ostream &os, const layerAndWeight &l)
static const layerAndWeight max
friend bool operator!=(const layerAndWeight &a, const layerAndWeight &b)
friend Istream & operator>>(Istream &is, layerAndWeight &l)
friend bool operator==(const layerAndWeight &a, const layerAndWeight &b)
static const layerAndWeight min