fvMeshStitcher.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-2024 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 "fvMeshStitcher.H"
27 #include "globalIndex.H"
28 #include "fvcSurfaceIntegrate.H"
29 #include "MultiRegionList.H"
30 #include "meshObjects.H"
33 #include "nonConformalBoundary.H"
41 #include "polyTopoChangeMap.H"
42 #include "polyMeshMap.H"
43 #include "polyDistributionMap.H"
44 #include "syncTools.H"
45 #include "surfaceInterpolate.H"
46 #include "surfaceToVolVelocity.H"
47 
48 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
49 
50 namespace Foam
51 {
52 
53 template<class FaceList, class PointField>
55 (
57  const label edgei
58 )
59 {
60  return edge
61  (
62  p.meshPoints()[p.edges()[edgei][0]],
63  p.meshPoints()[p.edges()[edgei][1]]
64  );
65 }
66 
67 
68 bool any(const boolList& l)
69 {
70  bool result = false;
71  forAll(l, i)
72  {
73  if (l[i])
74  {
75  result = true;
76  break;
77  }
78  }
79  return returnReduce(result, andOp<bool>());
80 }
81 
82 }
83 
84 
85 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
86 
87 namespace Foam
88 {
91 }
92 
93 
94 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
95 
96 void Foam::fvMeshStitcher::regionNames(const fvMesh& mesh, wordHashSet& names)
97 {
98  names.insert(mesh.name());
99 
101  {
102  const fvPatch& fvp = mesh.boundary()[patchi];
103 
104  if (!isA<nonConformalMappedWallFvPatch>(fvp)) continue;
105 
106  const nonConformalMappedWallFvPatch& ncmwFvp =
107  refCast<const nonConformalMappedWallFvPatch>(fvp);
108 
109  if (!names.found(ncmwFvp.nbrRegionName()))
110  {
111  regionNames(ncmwFvp.nbrMesh(), names);
112  }
113  }
114 }
115 
116 
117 Foam::wordList Foam::fvMeshStitcher::regionNames() const
118 {
119  wordHashSet names;
120  regionNames(mesh_, names);
121  return names.toc();
122 }
123 
124 
125 Foam::UPtrList<Foam::fvMesh> Foam::fvMeshStitcher::regionMeshes()
126 {
127  const wordList names(regionNames());
128 
129  UPtrList<fvMesh> result(names.size());
130  forAll(names, i)
131  {
132  result.set
133  (
134  i,
135  &mesh_.time().lookupObjectRef<fvMesh>(names[i])
136  );
137  }
138 
139  return result;
140 }
141 
142 
143 Foam::UPtrList<const Foam::fvMesh> Foam::fvMeshStitcher::regionMeshes() const
144 {
145  const wordList names(regionNames());
146 
147  UPtrList<const fvMesh> result(names.size());
148  forAll(names, i)
149  {
150  result.set
151  (
152  i,
153  &mesh_.time().lookupObject<fvMesh>(names[i])
154  );
155  }
156 
157  return result;
158 }
159 
160 
161 Foam::IOobject Foam::fvMeshStitcher::polyFacesBfIO(const fvMesh& mesh)
162 {
163  return IOobject
164  (
165  "polyFaces",
166  mesh.time().findInstance
167  (
168  mesh.dbDir()/fvMesh::typeName,
169  "polyFaces",
171  ),
172  fvMesh::typeName,
173  mesh,
176  false
177  );
178 }
179 
180 
181 bool Foam::fvMeshStitcher::loadPolyFacesBf
182 (
183  IOobject& polyFacesBfIO,
184  SurfaceFieldBoundary<label>& polyFacesBf
185 )
186 {
187  bool loaded = false;
188 
189  // If the poly faces are in the region cache then use them and remove them.
190  // This mesh is now available, so we can look it up to access its poly
191  // faces from now on.
192  auto polyFacesBfIOIter = regionPolyFacesBfIOs_.find(mesh_.name());
193  auto polyFacesBfIter = regionPolyFacesBfs_.find(mesh_.name());
194  if (polyFacesBfIOIter != regionPolyFacesBfIOs_.end())
195  {
196  polyFacesBfIO =
197  autoPtr<IOobject>
198  (
199  regionPolyFacesBfIOs_.remove(polyFacesBfIOIter)
200  )();
201 
202  polyFacesBf.reset
203  (
204  tmp<SurfaceFieldBoundary<label>>
205  (
206  regionPolyFacesBfs_.remove(polyFacesBfIter)
207  )
208  );
209 
210  loaded = true;
211  }
212 
213  // If the faces are not in the region cache, then try and read them
214  if (!loaded)
215  {
216  typeIOobject<surfaceLabelField> io
217  (
218  fvMeshStitcher::polyFacesBfIO(mesh_)
219  );
220 
221  if (io.headerOk())
222  {
223  polyFacesBfIO = io;
224 
225  polyFacesBf.reset
226  (
227  surfaceLabelField(polyFacesBfIO, mesh_).boundaryField()
228  );
229 
230  loaded = true;
231  }
232  }
233 
234  // If nothing is available then return
235  if (!loaded) return false;
236 
237  // If the poly faces are available, then make sure all connected regions'
238  // poly faces have been read and are in the cache
239  forAll(mesh_.boundary(), patchi)
240  {
241  const fvPatch& fvp = mesh_.boundary()[patchi];
242 
243  if (!isA<nonConformalMappedWallFvPatch>(fvp)) continue;
244 
245  const nonConformalMappedWallFvPatch& ncmwFvp =
246  refCast<const nonConformalMappedWallFvPatch>(fvp);
247 
248  if (!ncmwFvp.haveNbr()) continue;
249 
250  if (regionPolyFacesBfs_.found(ncmwFvp.nbrMesh().name())) continue;
251 
252  IOobject io = fvMeshStitcher::polyFacesBfIO(ncmwFvp.nbrMesh());
253 
254  regionPolyFacesBfIOs_.insert
255  (
256  ncmwFvp.nbrMesh().name(),
257  new IOobject(io)
258  );
259 
260  regionPolyFacesBfs_.insert
261  (
262  ncmwFvp.nbrMesh().name(),
264  (
266  surfaceLabelField(io, ncmwFvp.nbrMesh()).boundaryField()
267  )
268  );
269  }
270 
271  return true;
272 }
273 
274 
275 const Foam::surfaceLabelField::Boundary& Foam::fvMeshStitcher::getPolyFacesBf
276 (
277  const word& regionName
278 ) const
279 {
280  if (regionPolyFacesBfs_.found(regionName))
281  {
282  return *regionPolyFacesBfs_[regionName];
283  }
284  else
285  {
286  return mesh_.time().lookupObject<fvMesh>(regionName).polyFacesBf();
287  }
288 }
289 
290 
291 void Foam::fvMeshStitcher::getOrigNbrBfs
292 (
293  const SurfaceFieldBoundary<label>& polyFacesBf,
294  const SurfaceFieldBoundary<vector>& SfBf,
295  const SurfaceFieldBoundary<vector>& CfBf,
296  tmp<SurfaceFieldBoundary<label>>& tOrigFacesNbrBf,
297  tmp<SurfaceFieldBoundary<vector>>& tOrigSfNbrBf,
298  tmp<SurfaceFieldBoundary<point>>& tOrigCfNbrBf
299 ) const
300 {
301  // Temporary full-sized surface fields for orig properties
302  surfaceLabelField origFaces
303  (
305  (
306  "origFaces",
307  mesh_,
308  dimensioned<label>(dimless, -1)
309  )
310  );
311  surfaceVectorField origSf
312  (
314  (
315  "origSf",
316  mesh_,
318  )
319  );
320  surfaceVectorField origCf
321  (
323  (
324  "origCf",
325  mesh_,
327  )
328  );
329 
330  // Communicate orig properties across cyclics and processor cyclics
331  forAll(mesh_.boundary(), patchi)
332  {
333  const fvPatch& fvp = mesh_.boundary()[patchi];
334 
335  if (isA<nonConformalCoupledFvPatch>(fvp))
336  {
337  const nonConformalCoupledFvPatch& nccFvp =
338  refCast<const nonConformalCoupledFvPatch>(fvp);
339 
340  origFaces.boundaryFieldRef()[patchi] =
341  polyFacesBf[patchi] - nccFvp.origPatch().start();
342  }
343 
344  // (set Sf and Cf on all coupled patches, so that transform operations
345  // in boundaryNeighbourField don't trigger a sigFpe)
346  if (isA<coupledFvPatch>(fvp))
347  {
348  origSf.boundaryFieldRef()[patchi] =
349  vectorField(mesh_.faceAreas(), polyFacesBf[patchi]);
350  origCf.boundaryFieldRef()[patchi] =
351  pointField(mesh_.faceCentres(), polyFacesBf[patchi]);
352  }
353  }
354  origFaces.boundaryFieldRef() =
355  origFaces.boundaryField().boundaryNeighbourField();
356  origSf.boundaryFieldRef() =
357  origSf.boundaryField().boundaryNeighbourField();
358  origCf.boundaryFieldRef() =
359  origCf.boundaryField().boundaryNeighbourField();
360 
361  // Communicate orig properties across mapped walls
362  forAll(mesh_.boundary(), patchi)
363  {
364  const fvPatch& fvp = mesh_.boundary()[patchi];
365 
366  if (!isA<nonConformalMappedWallFvPatch>(fvp)) continue;
367 
368  const nonConformalMappedWallFvPatch& ncmwFvp =
369  refCast<const nonConformalMappedWallFvPatch>(fvp);
370 
371  if (!ncmwFvp.haveNbr()) continue;
372 
373  const fvMesh& nbrMesh = ncmwFvp.nbrMesh();
374  const nonConformalMappedWallFvPatch& nbrPatch = ncmwFvp.nbrPatch();
375 
376  const fvsPatchLabelField& nbrPolyFacesPf =
377  getPolyFacesBf(nbrMesh.name())[nbrPatch.index()];
378 
379  origFaces.boundaryFieldRef()[patchi] =
381  (
382  nbrPolyFacesPf,
383  nbrPolyFacesPf - nbrPatch.origPatch().start(),
384  polyFacesBf[patchi]
385  );
386  origSf.boundaryFieldRef()[patchi] =
388  (
389  nbrPolyFacesPf,
390  vectorField(nbrMesh.faceAreas(), nbrPolyFacesPf),
391  polyFacesBf[patchi]
392  );
393  origCf.boundaryFieldRef()[patchi] =
395  (
396  nbrPolyFacesPf,
397  vectorField(nbrMesh.faceCentres(), nbrPolyFacesPf),
398  polyFacesBf[patchi]
399  );
400  }
401 
402  // Correct transformed face centres. See note in fvMesh::unconform
403  // regarding the transformation of cell centres. The same applies here.
404  forAll(mesh_.boundary(), patchi)
405  {
406  const fvPatch& fvp = mesh_.boundary()[patchi];
407 
408  fvsPatchVectorField& Cfp = origCf.boundaryFieldRef()[patchi];
409 
410  if (isA<nonConformalCoupledFvPatch>(fvp))
411  {
412  const nonConformalCoupledFvPatch& nccFvp =
413  refCast<const nonConformalCoupledFvPatch>(fvp);
414 
415  nccFvp.transform().invTransform(Cfp, Cfp);
416  nccFvp.transform().transformPosition(Cfp, Cfp);
417  }
418 
419  if (isA<nonConformalMappedWallFvPatch>(fvp))
420  {
421  const nonConformalMappedWallFvPatch& ncmwFvp =
422  refCast<const nonConformalMappedWallFvPatch>(fvp);
423 
424  if (ncmwFvp.haveNbr())
425  {
426  ncmwFvp.transform().invTransform(Cfp, Cfp);
427  ncmwFvp.transform().transformPosition(Cfp, Cfp);
428  }
429  }
430  }
431 
432  // Set the tmp boundary fields and discard the unused internal field
433  tOrigFacesNbrBf =
435  (
437  origFaces.boundaryField()
438  );
439  tOrigSfNbrBf =
441  (
443  origSf.boundaryField()
444  );
445  tOrigCfNbrBf =
447  (
449  origCf.boundaryField()
450  );
451 }
452 
453 
455 Foam::fvMeshStitcher::procFacesToIndices
456 (
457  const List<List<remote>>& faceOtherProcFaces,
458  const bool owner
459 )
460 {
461  // Compute interface sizes
462  labelList is(Pstream::nProcs(), 0);
463  forAll(faceOtherProcFaces, facei)
464  {
465  forAll(faceOtherProcFaces[facei], i)
466  {
467  const remote& otherProcFacei = faceOtherProcFaces[facei][i];
468 
469  is[otherProcFacei.proci] ++;
470  }
471  }
472 
473  // Allocate the indices
474  List<List<FixedList<label, 3>>> indices(Pstream::nProcs());
475  forAll(indices, proci)
476  {
477  indices[proci].resize(is[proci]);
478  }
479 
480  // Set the indices
481  is = 0;
482  forAll(faceOtherProcFaces, facei)
483  {
484  forAll(faceOtherProcFaces[facei], i)
485  {
486  const remote& otherProcFacei = faceOtherProcFaces[facei][i];
487 
488  FixedList<label, 3>& index =
489  indices[otherProcFacei.proci][is[otherProcFacei.proci] ++];
490 
491  index = {facei, otherProcFacei.elementi, i};
492 
493  if (!owner) Swap(index[0], index[1]);
494  }
495  }
496 
497  // Sort to ensure ordering
498  forAll(indices, proci)
499  {
500  sort(indices[proci]);
501  }
502 
503  return indices;
504 }
505 
506 
507 void Foam::fvMeshStitcher::matchIndices
508 (
509  const surfaceLabelField::Boundary& polyFacesBf,
510  const surfaceLabelField::Boundary& origFacesNbrBf,
511  List<List<FixedList<label, 3>>>& indices,
512  const fvPatch& ncFvp,
513  const polyPatch& origPp,
514  const labelList& patchis,
515  const labelList& patchOffsets,
516  const labelList& patchSizes,
517  const bool owner
518 )
519 {
520  // Create what the indices should be
521  List<List<FixedList<label, 3>>> indicesRef(indices.size());
522  forAll(indices, proci)
523  {
524  const label patchi = patchis[proci];
525 
526  if (patchi != -1)
527  {
528  indicesRef[proci].resize(patchSizes[proci]);
529 
530  for (label indexi = 0; indexi < patchSizes[proci]; ++ indexi)
531  {
532  FixedList<label, 3>& indexRef = indicesRef[proci][indexi];
533 
534  const label patchFacei = indexi + patchOffsets[proci];
535 
536  indexRef =
537  {
538  polyFacesBf[patchi][patchFacei] - origPp.start(),
539  origFacesNbrBf[patchi][patchFacei],
540  -1
541  };
542 
543  if (!owner) Swap(indexRef[0], indexRef[1]);
544  }
545  }
546  }
547 
548  // Populate the indices with the index of the coupling
549  label nCouplesRemoved = 0, nCouplesAdded = 0;
550  forAll(indices, proci)
551  {
552  const label patchi = patchis[proci];
553 
554  if (patchi != -1)
555  {
556  label refi = 0, i = 0;
557 
558  while
559  (
560  refi < indicesRef[proci].size()
561  && i < indices[proci].size()
562  )
563  {
564  const FixedList<label, 3> index
565  ({
566  indices[proci][i][0],
567  indices[proci][i][1],
568  -1
569  });
570 
571  FixedList<label, 3>& indexRef = indicesRef[proci][refi];
572 
573  if (index < indexRef)
574  {
575  nCouplesRemoved ++;
576  i ++;
577  }
578  else if (index == indexRef)
579  {
580  indexRef[2] = indices[proci][i][2];
581  refi ++;
582  i ++;
583  }
584  else // (index > indexRef)
585  {
586  nCouplesAdded ++;
587  refi ++;
588  }
589  }
590 
591  nCouplesRemoved += min(indices[proci].size() - i, 0);
592  nCouplesAdded += min(indicesRef[proci].size() - refi, 0);
593  }
594  }
595 
596  // Report if changes have been made
597  reduce(nCouplesRemoved, sumOp<label>());
598  reduce(nCouplesAdded, sumOp<label>());
599  if ((nCouplesRemoved || nCouplesAdded) && owner)
600  {
601  Info<< indent << nCouplesRemoved << '/' << nCouplesAdded
602  << " small couplings removed/added to " << ncFvp.name()
603  << endl;
604  }
605 
606  // Set the indices to the correct values
607  Swap(indices, indicesRef);
608 }
609 
610 
611 void Foam::fvMeshStitcher::createCouplings
612 (
613  surfaceLabelField::Boundary& polyFacesBf,
616  const tmp<surfaceVectorField::Boundary>& tOrigSfNbrBf,
617  const tmp<surfaceVectorField::Boundary>& tOrigCfNbrBf,
618  const List<List<FixedList<label, 3>>>& indices,
619  const List<DynamicList<couple>>& couples,
620  const polyPatch& origPp,
621  const labelList& patchis,
622  const labelList& patchOffsets,
623  const bool owner
624 )
625 {
626  // Add coupled faces into the non-conformal patches
627  forAll(patchis, proci)
628  {
629  const label patchi = patchis[proci];
630  const label patchOffset = patchOffsets[proci];
631 
632  if (patchi != -1)
633  {
634  forAll(indices[proci], indexi)
635  {
636  const label patchFacei = indexi + patchOffset;
637 
638  const label origFacei = indices[proci][indexi][!owner];
639  const label i = indices[proci][indexi][2];
640 
641  polyFacesBf[patchi][patchFacei] = origFacei + origPp.start();
642 
643  couple c;
644  if (i != -1)
645  {
646  c = couples[origFacei][i];
647  }
648  else
649  {
650  c =
651  couple
652  (
653  part
654  (
655  small*origPp.faceAreas()[origFacei],
656  origPp.faceCentres()[origFacei]
657  ),
658  part
659  (
660  small*tOrigSfNbrBf()[patchi][patchFacei],
661  tOrigCfNbrBf()[patchi][patchFacei]
662  )
663  );
664  }
665 
666  if (!owner)
667  {
668  c.nbr = c;
669  }
670 
671  SfBf[patchi][patchFacei] = c.nbr.area;
672  CfBf[patchi][patchFacei] = c.nbr.centre;
673 
674  if (i != -1)
675  {
676  part origP
677  (
678  SfBf[origPp.index()][origFacei],
679  CfBf[origPp.index()][origFacei]
680  );
681  origP -= c;
682 
683  SfBf[origPp.index()][origFacei] = origP.area;
684  CfBf[origPp.index()][origFacei] = origP.centre;
685  }
686  }
687  }
688  }
689 }
690 
691 
692 void Foam::fvMeshStitcher::createErrorAndEdgeParts
693 (
696  List<part>& origEdgeParts,
697  const patchToPatches::intersection& intersection,
698  const polyPatch& origPp
699 )
700 {
701  // Add error geometry to the original patches
702  forAll(intersection.srcErrorParts(), origFacei)
703  {
704  part origP
705  (
706  SfBf[origPp.index()][origFacei],
707  CfBf[origPp.index()][origFacei]
708  );
709  origP += intersection.srcErrorParts()[origFacei];
710 
711  SfBf[origPp.index()][origFacei] = origP.area;
712  CfBf[origPp.index()][origFacei] = origP.centre;
713  }
714 
715  // Store the orig edge parts
716  forAll(intersection.srcEdgeParts(), origEdgei)
717  {
718  origEdgeParts[origEdgei] += intersection.srcEdgeParts()[origEdgei];
719  }
720 }
721 
722 
723 void Foam::fvMeshStitcher::intersectNonConformalCyclic
724 (
725  const nonConformalCyclicFvPatch& nccFvp,
726  surfaceLabelField::Boundary& polyFacesBf,
729  const tmp<surfaceLabelField::Boundary>& tOrigFacesNbrBf,
730  const tmp<surfaceVectorField::Boundary>& tOrigSfNbrBf,
731  const tmp<surfaceVectorField::Boundary>& tOrigCfNbrBf,
732  List<part>& origEdgeParts
733 ) const
734 {
735  // Alias the original poly patches
736  const polyPatch& origPp = nccFvp.origPatch().patch();
737 
738  // Get the indices of the related (i.e., cyclic and processorCyclic)
739  // non-conformal patches. Index based on the connected processor.
740  labelList patchis(Pstream::nProcs(), -1);
741  patchis[Pstream::myProcNo()] = nccFvp.index();
742  forAll(mesh_.boundary(), patchj)
743  {
744  const fvPatch& fvp = mesh_.boundary()[patchj];
745 
746  if (isA<nonConformalProcessorCyclicFvPatch>(fvp))
747  {
748  const nonConformalProcessorCyclicFvPatch& ncpcFvp =
749  refCast<const nonConformalProcessorCyclicFvPatch>(fvp);
750 
751  if (ncpcFvp.referPatchIndex() == nccFvp.index())
752  {
753  patchis[ncpcFvp.neighbProcNo()] = patchj;
754  }
755  }
756  }
757 
758  // Get the intersection geometry
759  const patchToPatches::intersection& intersection =
760  nccFvp.owner()
761  ? nccFvp.nonConformalCyclicPatch().intersection()
762  : nccFvp.nbrPatch().nonConformalCyclicPatch().intersection();
763 
764  // Unpack the patchToPatch addressing into a list of indices
765  List<List<FixedList<label, 3>>> indices =
766  procFacesToIndices
767  (
768  nccFvp.owner()
769  ? intersection.srcTgtProcFaces()
770  : intersection.tgtSrcProcFaces(),
771  nccFvp.owner()
772  );
773 
774  // If addressing has been provided, then modify the indices to match
775  if (tOrigFacesNbrBf.valid())
776  {
777  labelList patchSizes(Pstream::nProcs(), -1);
778  forAll(patchis, proci)
779  {
780  if (patchis[proci] != -1)
781  {
782  patchSizes[proci] = polyFacesBf[patchis[proci]].size();
783  }
784  }
785 
786  matchIndices
787  (
788  polyFacesBf,
789  tOrigFacesNbrBf(),
790  indices,
791  nccFvp,
792  origPp,
793  patchis,
795  patchSizes,
796  nccFvp.owner()
797  );
798  }
799 
800  // Check the existence of the non-conformal patches to be added into and
801  // resize them as necessary
802  forAll(patchis, proci)
803  {
804  const label patchi = patchis[proci];
805  const label patchSize = indices[proci].size();
806 
807  if (patchi == -1 && patchSize)
808  {
810  << "Intersection generated coupling through "
811  << nccFvp.name() << " between processes "
812  << Pstream::myProcNo() << " and " << proci
813  << " but a corresponding processor interface was not found"
814  << exit(FatalError);
815  }
816 
817  if (patchi != -1)
818  {
819  polyFacesBf[patchi].resize(patchSize);
820  SfBf[patchi].resize(patchSize);
821  CfBf[patchi].resize(patchSize);
822  }
823  }
824 
825  // Create couplings by transferring geometry from the original to the
826  // non-conformal patches
827  createCouplings
828  (
829  polyFacesBf,
830  SfBf,
831  CfBf,
832  tOrigSfNbrBf,
833  tOrigCfNbrBf,
834  indices,
835  nccFvp.owner() ? intersection.srcCouples() : intersection.tgtCouples(),
836  origPp,
837  patchis,
839  nccFvp.owner()
840  );
841 
842  // Add error geometry to the original patches and store the edge parts
843  if (nccFvp.owner())
844  {
845  createErrorAndEdgeParts
846  (
847  SfBf,
848  CfBf,
849  origEdgeParts,
850  intersection,
851  origPp
852  );
853  }
854 }
855 
856 
857 void Foam::fvMeshStitcher::intersectNonConformalMappedWall
858 (
859  const nonConformalMappedWallFvPatch& ncmwFvp,
860  surfaceLabelField::Boundary& polyFacesBf,
863  const tmp<surfaceLabelField::Boundary>& tOrigFacesNbrBf,
864  const tmp<surfaceVectorField::Boundary>& tOrigSfNbrBf,
865  const tmp<surfaceVectorField::Boundary>& tOrigCfNbrBf,
866  List<part>& origEdgeParts
867 ) const
868 {
869  // Alias the original poly patch
870  const polyPatch& origPp = ncmwFvp.origPatch().patch();
871 
872  // Get the intersection geometry
873  const patchToPatches::intersection& intersection =
874  ncmwFvp.owner()
875  ? ncmwFvp.nonConformalMappedWallPatch().intersection()
876  : ncmwFvp.nbrPatch().nonConformalMappedWallPatch().intersection();
877 
878  // Unpack the patchToPatch addressing into a list of indices
879  List<List<FixedList<label, 3>>> indices =
880  procFacesToIndices
881  (
882  ncmwFvp.owner()
883  ? intersection.srcTgtProcFaces()
884  : intersection.tgtSrcProcFaces(),
885  ncmwFvp.owner()
886  );
887 
888  // Obtain the label patch field for the mapped wall poly faces
889  nonConformalMappedPolyFacesFvsPatchLabelField& polyFacesPf =
890  refCast<nonConformalMappedPolyFacesFvsPatchLabelField>
891  (
892  polyFacesBf[ncmwFvp.index()]
893  );
894 
895  // If addressing has been provided, then modify the indices to match
896  if (tOrigFacesNbrBf.valid())
897  {
898  matchIndices
899  (
900  polyFacesBf,
901  tOrigFacesNbrBf(),
902  indices,
903  ncmwFvp,
904  origPp,
905  labelList(Pstream::nProcs(), ncmwFvp.index()),
906  polyFacesPf.procOffsets(),
907  polyFacesPf.procSizes(),
908  ncmwFvp.owner()
909  );
910  }
911 
912  // Set the processor offsets within the mapped polyFacesBf patch field
913  {
914  label count = 0;
915 
916  forAll(indices, proci)
917  {
918  polyFacesPf.procOffsets()[proci] = count;
919 
920  count += indices[proci].size();
921  }
922 
923  polyFacesPf.resize(count);
924  SfBf[polyFacesPf.patch().index()].resize(count);
925  CfBf[polyFacesPf.patch().index()].resize(count);
926  };
927 
928  // Create a coupling by transferring geometry from the original to the
929  // non-conformal patch
930  createCouplings
931  (
932  polyFacesBf,
933  SfBf,
934  CfBf,
935  tOrigSfNbrBf,
936  tOrigCfNbrBf,
937  indices,
938  ncmwFvp.owner() ? intersection.srcCouples() : intersection.tgtCouples(),
939  origPp,
940  labelList(Pstream::nProcs(), ncmwFvp.index()),
941  polyFacesPf.procOffsets(),
942  ncmwFvp.owner()
943  );
944 
945  // Add error geometry to the original patch and store the edge parts
946  if (ncmwFvp.owner())
947  {
948  createErrorAndEdgeParts
949  (
950  SfBf,
951  CfBf,
952  origEdgeParts,
953  intersection,
954  origPp
955  );
956  }
957 }
958 
959 
961 Foam::fvMeshStitcher::calculateOwnerOrigBoundaryEdgeParts
962 (
963  const List<List<part>>& patchEdgeParts
964 ) const
965 {
966  const polyBoundaryMesh& pbMesh = mesh_.boundaryMesh();
967 
968  const nonConformalBoundary& ncb = nonConformalBoundary::New(mesh_);
969  const labelList& ownerOrigBoundaryPointMeshPoint =
970  ncb.ownerOrigBoundaryPointMeshPoint();
971  const labelList& ownerOrigBoundaryEdgeMeshEdge =
972  ncb.ownerOrigBoundaryEdgeMeshEdge();
973  const edgeList& ownerOrigBoundaryEdges = ncb.ownerOrigBoundaryEdges();
974  const edgeList& ownerOrigBoundaryMeshEdges =
975  ncb.ownerOrigBoundaryMeshEdges();
976 
977  // Sum up boundary edge parts, being careful with signs
978  labelList ownerOrigBoundaryEdgeNParts(ownerOrigBoundaryEdges.size(), 0);
979  List<part> ownerOrigBoundaryEdgeParts(ownerOrigBoundaryEdges.size());
980  forAll(patchEdgeParts, patchi)
981  {
982  if (patchEdgeParts[patchi].empty()) continue;
983 
984  const polyPatch& patch = pbMesh[patchi];
985 
986  const labelList& patchEdgeOwnerOrigBoundaryEdges =
987  ncb.patchEdgeOwnerOrigBoundaryEdges(patchi);
988 
989  forAll(patchEdgeParts[patchi], patchEdgei)
990  {
991  const label ownerOrigBoundaryEdgei =
992  patchEdgeOwnerOrigBoundaryEdges[patchEdgei];
993 
994  const label sign =
996  (
997  meshEdge(patch, patchEdgei),
998  ownerOrigBoundaryMeshEdges[ownerOrigBoundaryEdgei]
999  );
1000 
1001  const part& pU = patchEdgeParts[patchi][patchEdgei];
1002  const part p = sign > 0 ? pU : -pU;
1003 
1004  ownerOrigBoundaryEdgeNParts[ownerOrigBoundaryEdgei] ++;
1005  ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei] += p;
1006  }
1007  }
1008 
1009  // Give every point in the all boundary a global unique index
1010  const globalIndex globalOwnerOrigBoundaryPoints
1011  (
1012  ownerOrigBoundaryPointMeshPoint.size()
1013  );
1014  labelList ownerOrigBoundaryPointIndices
1015  (
1016  ownerOrigBoundaryPointMeshPoint.size()
1017  );
1018  forAll(ownerOrigBoundaryPointIndices, ownerOrigBoundaryPointi)
1019  {
1020  ownerOrigBoundaryPointIndices[ownerOrigBoundaryPointi] =
1021  globalOwnerOrigBoundaryPoints.toGlobal(ownerOrigBoundaryPointi);
1022  }
1024  (
1025  mesh_,
1026  ownerOrigBoundaryPointMeshPoint,
1027  ownerOrigBoundaryPointIndices,
1028  minEqOp<label>(),
1029  labelMax
1030  );
1031 
1032  // Synchronise the sign of the edge parts so that they can be
1033  // meaningfully summed. This is done using the sign of the global point
1034  // indices; if they are not in order, then reverse the part area.
1035  forAll(ownerOrigBoundaryEdgeParts, ownerOrigBoundaryEdgei)
1036  {
1037  const edge& e = ownerOrigBoundaryEdges[ownerOrigBoundaryEdgei];
1038 
1039  if
1040  (
1041  ownerOrigBoundaryPointIndices[e.start()]
1042  > ownerOrigBoundaryPointIndices[e.end()])
1043  {
1044  ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei] =
1045  - ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei];
1046  }
1047  }
1048 
1049  // Average the boundary edge parts across couplings
1051  (
1052  mesh_,
1053  ownerOrigBoundaryEdgeMeshEdge,
1054  ownerOrigBoundaryEdgeNParts,
1055  plusEqOp<label>(),
1056  label(0)
1057  );
1059  (
1060  mesh_,
1061  ownerOrigBoundaryEdgeMeshEdge,
1062  ownerOrigBoundaryEdgeParts,
1063  plusEqOp<part>(),
1064  part(),
1065  []
1066  (
1067  const transformer& vt,
1068  const bool forward,
1069  List<part>& fld
1070  )
1071  {
1072  if (forward)
1073  {
1074  forAll(fld, i)
1075  {
1076  if (magSqr(fld[i].area) != 0)
1077  {
1078  fld[i].area = vt.transform(fld[i].area);
1079  fld[i].centre = vt.transformPosition(fld[i].centre);
1080  }
1081  }
1082  }
1083  else
1084  {
1085  forAll(fld, i)
1086  {
1087  if (magSqr(fld[i].area) != 0)
1088  {
1089  fld[i].area = vt.invTransform(fld[i].area);
1090  fld[i].centre = vt.invTransformPosition(fld[i].centre);
1091  }
1092  }
1093  }
1094  }
1095  );
1096  forAll(ownerOrigBoundaryEdgeParts, ownerOrigBoundaryEdgei)
1097  {
1098  if (ownerOrigBoundaryEdgeNParts[ownerOrigBoundaryEdgei] != 0)
1099  {
1100  ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei].area /=
1101  ownerOrigBoundaryEdgeNParts[ownerOrigBoundaryEdgei];
1102  }
1103  }
1104 
1105  // Put the edge parts back to their original orientations
1106  forAll(ownerOrigBoundaryEdgeParts, ownerOrigBoundaryEdgei)
1107  {
1108  const edge& e = ownerOrigBoundaryEdges[ownerOrigBoundaryEdgei];
1109 
1110  if
1111  (
1112  ownerOrigBoundaryPointIndices[e.start()]
1113  > ownerOrigBoundaryPointIndices[e.end()]
1114  )
1115  {
1116  ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei] =
1117  - ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei];
1118  }
1119  }
1120 
1121  return ownerOrigBoundaryEdgeParts;
1122 }
1123 
1124 
1125 
1126 void Foam::fvMeshStitcher::applyOwnerOrigBoundaryEdgeParts
1127 (
1128  surfaceVectorField& SfSf,
1129  surfaceVectorField& CfSf,
1130  const List<part>& ownerOrigBoundaryEdgeParts
1131 ) const
1132 {
1133  const polyBoundaryMesh& pbMesh = mesh_.boundaryMesh();
1134 
1135  const nonConformalBoundary& ncb = nonConformalBoundary::New(mesh_);
1136  const labelList ownerOrigPatchIndices = ncb.ownerOrigPatchIndices();
1137  const labelList& ownerOrigBoundaryEdgeMeshEdge =
1138  ncb.ownerOrigBoundaryEdgeMeshEdge();
1139  const edgeList& ownerOrigBoundaryMeshEdges =
1140  ncb.ownerOrigBoundaryMeshEdges();
1141 
1142  boolList patchIsOwnerOrig(pbMesh.size(), false);
1143  UIndirectList<bool>(patchIsOwnerOrig, ownerOrigPatchIndices) = true;
1144 
1145  // Count the number of original faces attached to each boundary edge
1146  labelList ownerOrigBoundaryEdgeNOrigFaces
1147  (
1148  ownerOrigBoundaryEdgeMeshEdge.size(),
1149  0
1150  );
1151  forAll(ownerOrigBoundaryEdgeMeshEdge, ownerOrigBoundaryEdgei)
1152  {
1153  const label meshEdgei =
1154  ownerOrigBoundaryEdgeMeshEdge[ownerOrigBoundaryEdgei];
1155 
1156  forAll(mesh_.edgeFaces()[meshEdgei], edgeFacei)
1157  {
1158  const label facei = mesh_.edgeFaces()[meshEdgei][edgeFacei];
1159 
1160  const label patchi =
1161  mesh_.isInternalFace(facei)
1162  ? -1 : pbMesh.patchIndices()[facei - mesh_.nInternalFaces()];
1163 
1164  if (patchi != -1 && patchIsOwnerOrig[patchi])
1165  {
1166  ownerOrigBoundaryEdgeNOrigFaces[ownerOrigBoundaryEdgei] ++;
1167  }
1168  }
1169  }
1170 
1171  // Synchronise the boundary edge original face count
1173  (
1174  mesh_,
1175  ownerOrigBoundaryEdgeMeshEdge,
1176  ownerOrigBoundaryEdgeNOrigFaces,
1177  plusEqOp<label>(),
1178  label(0)
1179  );
1180 
1181  // If debugging, check that face changes are in sync
1182  tmp<surfaceLabelField> tChanged =
1183  debug
1184  ? surfaceLabelField::New("changed", mesh_, dimensioned<label>(dimless, 0))
1185  : tmp<surfaceLabelField>(nullptr);
1186 
1187  // Modify faces connected to the boundary edges
1188  forAll(ownerOrigBoundaryEdgeMeshEdge, ownerOrigBoundaryEdgei)
1189  {
1190  const label meshEdgei =
1191  ownerOrigBoundaryEdgeMeshEdge[ownerOrigBoundaryEdgei];
1192 
1193  const part& ownerOrigBoundaryEdgeP =
1194  ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei];
1195 
1196  switch (ownerOrigBoundaryEdgeNOrigFaces[ownerOrigBoundaryEdgei])
1197  {
1198  case 0:
1199  {
1200  continue;
1201  }
1202 
1203  // If this edge is connected to just one original face then add any
1204  // edge area into that face
1205  case 1:
1206  {
1207  label origFacei = -1, origPatchi = -1, origPatchFacei = -1;
1208  forAll(mesh_.edgeFaces()[meshEdgei], edgeFacei)
1209  {
1210  const label facei = mesh_.edgeFaces()[meshEdgei][edgeFacei];
1211 
1212  const label patchi =
1213  mesh_.isInternalFace(facei)
1214  ? -1
1215  : pbMesh.patchIndices()[facei - mesh_.nInternalFaces()];
1216 
1217  if (patchi != -1 && patchIsOwnerOrig[patchi])
1218  {
1219  origFacei = facei;
1220  origPatchi = patchi;
1221  origPatchFacei = facei - pbMesh[patchi].start();
1222  break;
1223  }
1224  }
1225 
1226  if (origFacei != -1)
1227  {
1228  vector& area =
1229  SfSf.boundaryFieldRef()[origPatchi][origPatchFacei];
1230  point& centre =
1231  CfSf.boundaryFieldRef()[origPatchi][origPatchFacei];
1232 
1233  const label sign =
1234  mesh_.faces()[origFacei].edgeDirection
1235  (
1236  ownerOrigBoundaryMeshEdges[ownerOrigBoundaryEdgei]
1237  );
1238 
1239  part p(area, centre);
1240  p +=
1241  sign > 0
1242  ? ownerOrigBoundaryEdgeP
1243  : -ownerOrigBoundaryEdgeP;
1244 
1245  area = p.area;
1246  centre = p.centre;
1247 
1248  if (debug)
1249  {
1250  tChanged->boundaryFieldRef()
1251  [origPatchi][origPatchFacei] = label(1);
1252  }
1253  }
1254 
1255  break;
1256  }
1257 
1258  // If this edge is connected to two original faces then add any
1259  // edge area into non-original connected faces
1260  case 2:
1261  {
1262  forAll(mesh_.edgeFaces()[meshEdgei], edgeFacei)
1263  {
1264  const label facei = mesh_.edgeFaces()[meshEdgei][edgeFacei];
1265 
1266  const label patchi =
1267  mesh_.isInternalFace(facei)
1268  ? -1
1269  : pbMesh.patchIndices()[facei - mesh_.nInternalFaces()];
1270 
1271  if (patchi != -1 && patchIsOwnerOrig[patchi])
1272  {
1273  continue;
1274  }
1275 
1276  const label patchFacei =
1277  patchi == -1 ? -1 : facei - pbMesh[patchi].start();
1278 
1279  vector& area =
1280  patchi == -1
1281  ? SfSf[facei]
1282  : SfSf.boundaryFieldRef()[patchi][patchFacei];
1283  point& centre =
1284  patchi == -1
1285  ? CfSf[facei]
1286  : CfSf.boundaryFieldRef()[patchi][patchFacei];
1287 
1288  const label sign =
1289  mesh_.faces()[facei].edgeDirection
1290  (
1291  ownerOrigBoundaryMeshEdges[ownerOrigBoundaryEdgei]
1292  );
1293 
1294  part p(area, centre);
1295  p +=
1296  sign < 0
1297  ? ownerOrigBoundaryEdgeP
1298  : -ownerOrigBoundaryEdgeP;
1299 
1300  area = p.area;
1301  centre = p.centre;
1302 
1303  if (debug && patchi != -1)
1304  {
1305  tChanged->boundaryFieldRef()
1306  [patchi][patchFacei] = label(1);
1307  }
1308  }
1309 
1310  break;
1311  }
1312 
1313  default:
1314  {
1316  << "Boundary is not manifold"
1317  << exit(FatalError);
1318  }
1319  }
1320  }
1321 
1322  // If debugging, check that face changes are in sync
1323  if (debug)
1324  {
1325  const surfaceLabelField::Boundary& changedBf =
1326  tChanged->boundaryField();
1327 
1328  const surfaceLabelField::Boundary changedNbrBf
1329  (
1331  changedBf.boundaryNeighbourField()
1332  );
1333 
1334  bool synchronised = true;
1335 
1336  forAll(changedBf, patchi)
1337  {
1338  const fvsPatchLabelField& changedPf = changedBf[patchi];
1339  const fvsPatchLabelField& changedNbrPf = changedNbrBf[patchi];
1340 
1341  if (!isA<processorFvPatch>(changedPf.patch())) continue;
1342 
1343  forAll(changedPf, patchFacei)
1344  {
1345  if (changedPf[patchFacei] != changedNbrPf[patchFacei])
1346  {
1347  const label facei =
1348  changedPf.patch().start() + patchFacei;
1349 
1351  << "Face not synchronised with centre at "
1352  << mesh_.faceCentres()[facei] << endl;
1353 
1354  synchronised = false;
1355  }
1356  }
1357  }
1358 
1359  if (!synchronised)
1360  {
1362  << exit(FatalError);
1363  }
1364  }
1365 }
1366 
1367 
1368 void Foam::fvMeshStitcher::stabiliseOrigPatchFaces
1369 (
1372 ) const
1373 {
1374  const nonConformalBoundary& ncb = nonConformalBoundary::New(mesh_);
1375  const labelList allOrigPatchIndices = ncb.allOrigPatchIndices();
1376 
1377  forAll(allOrigPatchIndices, i)
1378  {
1379  const label origPatchi = allOrigPatchIndices[i];
1380  const polyPatch& origPp = mesh_.boundaryMesh()[origPatchi];
1381 
1382  forAll(origPp, origPatchFacei)
1383  {
1384  const vector& a = origPp.faceAreas()[origPatchFacei];
1385  const point& c = origPp.faceCentres()[origPatchFacei];
1386 
1387  vector& Sf = SfBf[origPatchi][origPatchFacei];
1388  point& Cf = CfBf[origPatchi][origPatchFacei];
1389 
1390  // Determine the direction in which to stabilise. If the fv-face
1391  // points in the same direction as the poly-face, then stabilise in
1392  // the direction of the poly-face. If it is reversed, then
1393  // construct a tangent to both faces, and stabilise in the average
1394  // direction to this tangent and the poly-face.
1395  vector dSfHat;
1396  if ((Sf & a) >= 0)
1397  {
1398  dSfHat = normalised(a);
1399  }
1400  else
1401  {
1402  dSfHat = (Sf & Sf)*a - (Sf & a)*Sf;
1403  if ((dSfHat & a) <= 0) dSfHat = perpendicular(a);
1404  dSfHat = normalised(normalised(dSfHat) + normalised(a));
1405  }
1406 
1407  part SAndCf(Sf, Cf);
1408 
1409  SAndCf += part(small*mag(a)*dSfHat, c);
1410 
1411  Sf = SAndCf.area;
1412  Cf = SAndCf.centre;
1413  }
1414  }
1415 }
1416 
1417 
1418 void Foam::fvMeshStitcher::intersect
1419 (
1420  surfaceLabelField::Boundary& polyFacesBf,
1421  surfaceVectorField& SfSf,
1422  surfaceVectorField& CfSf,
1423  const boolList& patchCoupleds,
1424  const bool matchTopology
1425 ) const
1426 {
1427  const polyBoundaryMesh& pbMesh = mesh_.boundaryMesh();
1428 
1429  const nonConformalBoundary& ncb = nonConformalBoundary::New(mesh_);
1430  const labelList ownerOrigPatchIndices = ncb.ownerOrigPatchIndices();
1431 
1432  // Alias the boundary geometry fields
1433  surfaceVectorField::Boundary& SfBf = SfSf.boundaryFieldRef();
1434  surfaceVectorField::Boundary& CfBf = CfSf.boundaryFieldRef();
1435 
1436  // Create storage for and initialise the edge parts of source patches
1437  List<List<part>> patchEdgeParts(mesh_.boundary().size());
1438  forAll(ownerOrigPatchIndices, i)
1439  {
1440  const label origPatchi = ownerOrigPatchIndices[i];
1441 
1442  patchEdgeParts[origPatchi].resize
1443  (
1444  mesh_.boundaryMesh()[origPatchi].nEdges(),
1445  part(Zero)
1446  );
1447  }
1448 
1449  // If topology is specified, also create a boundary field of the original
1450  // patch indices on the neighbouring interface, as well as the
1451  // corresponding areas and centres for stabilisation purposes
1452  tmp<surfaceLabelField::Boundary> tOrigFacesNbrBf;
1453  tmp<surfaceVectorField::Boundary> tOrigSfNbrBf;
1454  tmp<surfaceVectorField::Boundary> tOrigCfNbrBf;
1455  if (matchTopology)
1456  {
1457  getOrigNbrBfs
1458  (
1459  polyFacesBf,
1460  SfBf,
1461  CfBf,
1462  tOrigFacesNbrBf,
1463  tOrigSfNbrBf,
1464  tOrigCfNbrBf
1465  );
1466  }
1467 
1468  // Create intersection geometry for all the coupled non-conformal patches
1469  forAll(mesh_.boundary(), patchi)
1470  {
1471  if (!patchCoupleds[patchi]) continue;
1472 
1473  const fvPatch& fvp = mesh_.boundary()[patchi];
1474 
1475  if (isA<nonConformalCyclicFvPatch>(fvp))
1476  {
1477  const nonConformalCyclicFvPatch& nccFvp =
1478  refCast<const nonConformalCyclicFvPatch>(fvp);
1479 
1480  intersectNonConformalCyclic
1481  (
1482  nccFvp,
1483  polyFacesBf,
1484  SfBf,
1485  CfBf,
1486  tOrigFacesNbrBf,
1487  tOrigSfNbrBf,
1488  tOrigCfNbrBf,
1489  patchEdgeParts[nccFvp.origPatchIndex()]
1490  );
1491  }
1492  else if (isA<nonConformalProcessorCyclicFvPatch>(fvp))
1493  {}
1494  else if (isA<nonConformalMappedWallFvPatch>(fvp))
1495  {
1496  const nonConformalMappedWallFvPatch& ncmwFvp =
1497  refCast<const nonConformalMappedWallFvPatch>(fvp);
1498 
1499  intersectNonConformalMappedWall
1500  (
1501  ncmwFvp,
1502  polyFacesBf,
1503  SfBf,
1504  CfBf,
1505  tOrigFacesNbrBf,
1506  tOrigSfNbrBf,
1507  tOrigCfNbrBf,
1508  patchEdgeParts[ncmwFvp.origPatchIndex()]
1509  );
1510  }
1511  else
1512  {
1514  << "Coupled patch type not recognised"
1515  << exit(FatalError);
1516  }
1517  }
1518 
1519  // Create small stabilisation geometry for all the any non-coupled
1520  // non-conformal patches
1521  forAll(mesh_.boundary(), patchi)
1522  {
1523  if (patchCoupleds[patchi]) continue;
1524 
1525  const fvPatch& fvp = mesh_.boundary()[patchi];
1526 
1527  if (!isA<nonConformalFvPatch>(fvp)) continue;
1528 
1529  const polyPatch& origPp =
1530  refCast<const nonConformalFvPatch>(fvp).origPatch().patch();
1531 
1532  SfBf[patchi] ==
1533  vectorField
1534  (
1535  small*origPp.faceAreas(),
1536  polyFacesBf[patchi] - origPp.start()
1537  );
1538  CfBf[patchi] ==
1539  vectorField
1540  (
1541  origPp.faceCentres(),
1542  polyFacesBf[patchi] - origPp.start()
1543  );
1544  }
1545 
1546  // Construct the boundary edge geometry
1547  List<part> ownerOrigBoundaryEdgeParts =
1548  calculateOwnerOrigBoundaryEdgeParts(patchEdgeParts);
1549 
1550  // Add the difference between patch edge parts and all boundary
1551  // edge parts to the adjacent patch faces. This is an error part.
1552  forAll(ownerOrigPatchIndices, i)
1553  {
1554  const label origPatchi = ownerOrigPatchIndices[i];
1555  const polyPatch& origPatch = pbMesh[origPatchi];
1556 
1557  const labelList& origPatchEdgeOwnerOrigBoundaryEdges =
1558  ncb.patchEdgeOwnerOrigBoundaryEdges(origPatchi);
1559 
1560  forAll(patchEdgeParts[origPatchi], origPatchEdgei)
1561  {
1562  const label ownerOrigBoundaryEdgei =
1563  origPatchEdgeOwnerOrigBoundaryEdges[origPatchEdgei];
1564 
1565  part errorP =
1566  patchEdgeParts[origPatchi][origPatchEdgei];
1567  errorP -= ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei];
1568 
1569  forAll(origPatch.edgeFaces()[origPatchEdgei], patchEdgeFacei)
1570  {
1571  const label patchFacei =
1572  origPatch.edgeFaces()[origPatchEdgei][patchEdgeFacei];
1573 
1574  part p
1575  (
1576  SfBf[origPatchi][patchFacei],
1577  CfBf[origPatchi][patchFacei]
1578  );
1579  p += errorP;
1580 
1581  SfBf[origPatchi][patchFacei] = p.area;
1582  CfBf[origPatchi][patchFacei] = p.centre;
1583  }
1584  }
1585  }
1586 
1587  // Use the boundary edge geometry to correct all edge-connected faces
1588  applyOwnerOrigBoundaryEdgeParts(SfSf, CfSf, ownerOrigBoundaryEdgeParts);
1589 
1590  // Stabilise the original patch faces
1591  stabiliseOrigPatchFaces(SfBf, CfBf);
1592 }
1593 
1594 
1595 bool Foam::fvMeshStitcher::disconnectThis
1596 (
1597  const bool changing,
1598  const bool geometric
1599 )
1600 {
1601  if (!stitches() || (changing && !dynamic()))
1602  {
1603  return false;
1604  }
1605 
1606  // Determine which patches are coupled
1607  const boolList patchCoupleds =
1608  geometric
1609  ? this->patchCoupleds()
1610  : boolList(mesh_.boundary().size(), false);
1611 
1612  if (any(patchCoupleds))
1613  {
1614  Info<< indent << typeName << ": Disconnecting" << incrIndent << endl;
1615  }
1616 
1617  // Map the non-conformal patch field data to the conformal faces in advance
1618  // of the non-conformal patches being removed
1619  if (changing)
1620  {
1621  preConformVolFields();
1622  preConformSurfaceFields();
1623  }
1624 
1625  // Conform the mesh
1626  if (mesh_.moving())
1627  {
1628  surfaceScalarField phi(mesh_.phi());
1629  for (label i = 1; i < mesh_.phi().nOldTimes(false); ++ i)
1630  {
1631  phi.oldTimeRef(i) == mesh_.phi().oldTime(i);
1632  }
1633  conformCorrectMeshPhi(phi);
1634  mesh_.conform(phi);
1635  }
1636  else
1637  {
1638  mesh_.conform();
1639  }
1640 
1641  // Resize all the affected patch fields
1642  resizePatchFields<VolField>();
1643  resizePatchFields<SurfaceField>();
1644 
1645  // Prevent hangs caused by processor cyclic patches using mesh geometry
1646  mesh_.deltaCoeffs();
1647 
1648  if (any(patchCoupleds) && geometric)
1649  {
1650  const volScalarField::Internal o(openness());
1651  Info<< indent << "Cell min/avg/max openness = "
1652  << gMin(o) << '/' << gAverage(o) << '/' << gMax(o) << endl;
1653 
1654  if (mesh_.moving())
1655  {
1656  for (label i = 0; i <= mesh_.phi().nOldTimes(false); ++ i)
1657  {
1658  const volScalarField::Internal vce(volumeConservationError(i));
1659  Info<< indent << "Cell min/avg/max ";
1660  for (label j = 0; j < i; ++ j) Info<< "old-";
1661  Info<< (i ? "time " : "") << "volume conservation error = "
1662  << gMin(vce) << '/' << gAverage(vce) << '/' << gMax(vce)
1663  << endl;
1664  }
1665  }
1666  }
1667 
1668  if (any(patchCoupleds))
1669  {
1670  Info<< decrIndent;
1671  }
1672 
1673  // Create null polyTopoChangeMap
1674  const polyTopoChangeMap map(mesh_);
1675 
1676  meshObjects::topoChange<fvMesh>(mesh_, map);
1677  meshObjects::topoChange<lduMesh>(mesh_, map);
1678 
1679  const_cast<Time&>(mesh_.time()).functionObjects().topoChange(map);
1680 
1681  return true;
1682 }
1683 
1684 
1685 bool Foam::fvMeshStitcher::connectThis
1686 (
1687  const bool changing,
1688  const bool geometric,
1689  const bool load
1690 )
1691 {
1692  if (!stitches() || (changing && !dynamic()))
1693  {
1694  return false;
1695  }
1696 
1697  // Create a copy of the conformal poly face addressing
1698  IOobject polyFacesBfIO(word::null, mesh_.pointsInstance(), mesh_);
1699  surfaceLabelField::Boundary polyFacesBf
1700  (
1702  mesh_.polyFacesBf()
1703  );
1704 
1705  // If starting up then load topology from disk, if it is available
1706  const bool matchTopology =
1707  load && loadPolyFacesBf(polyFacesBfIO, polyFacesBf);
1708 
1709  // Determine which patches are coupled
1710  const boolList patchCoupleds =
1711  (geometric || !matchTopology)
1712  ? this->patchCoupleds()
1713  : boolList(mesh_.boundary().size(), false);
1714 
1715  // Access all the intersections in advance. Makes the log nicer.
1716  forAll(mesh_.boundary(), patchi)
1717  {
1718  if (!patchCoupleds[patchi]) continue;
1719 
1720  const fvPatch& fvp = mesh_.boundary()[patchi];
1721 
1722  if (isA<nonConformalCyclicFvPatch>(fvp))
1723  {
1724  const nonConformalCyclicFvPatch& nccFvp =
1725  refCast<const nonConformalCyclicFvPatch>(fvp);
1726 
1727  if (nccFvp.owner())
1728  {
1729  nccFvp.nonConformalCyclicPatch().intersection();
1730  }
1731  }
1732  else if (isA<nonConformalProcessorCyclicFvPatch>(fvp))
1733  {}
1734  else if (isA<nonConformalMappedWallFvPatch>(fvp))
1735  {
1736  const nonConformalMappedWallFvPatch& ncmwFvp =
1737  refCast<const nonConformalMappedWallFvPatch>(fvp);
1738 
1739  const nonConformalMappedWallFvPatch& ownerNcmwFvp =
1740  ncmwFvp.owner() ? ncmwFvp : ncmwFvp.nbrPatch();
1741 
1742  ownerNcmwFvp.nonConformalMappedWallPatch().intersection();
1743  }
1744  else
1745  {
1747  << "Coupled patch type not recognised"
1748  << exit(FatalError);
1749  }
1750  }
1751 
1752  if (any(patchCoupleds))
1753  {
1754  Info<< indent << typeName << ": Connecting" << incrIndent << endl;
1755  }
1756 
1757  // Create copies of geometry fields to be modified
1758  surfaceVectorField Sf(mesh_.Sf().cloneUnSliced()());
1759  surfaceVectorField Cf(mesh_.Cf().cloneUnSliced()());
1760 
1761  // Construct non-conformal geometry
1762  intersect(polyFacesBf, Sf, Cf, patchCoupleds, matchTopology);
1763 
1764  // If the mesh is moving then create any additional non-conformal geometry
1765  // necessary to correct the mesh fluxes
1766  if (mesh_.moving())
1767  {
1768  createNonConformalCorrectMeshPhiGeometry(polyFacesBf, Sf, Cf);
1769  }
1770 
1771  // Make the mesh non-conformal
1772  if (mesh_.moving())
1773  {
1774  surfaceScalarField phi(mesh_.phi());
1775  for (label i = 1; i <= mesh_.phi().nOldTimes(false); ++ i)
1776  {
1777  phi.oldTimeRef(i) == mesh_.phi().oldTime(i);
1778  }
1779  unconformCorrectMeshPhi(polyFacesBf, Sf, Cf, phi);
1780  mesh_.unconform(polyFacesBf, Sf, Cf, phi);
1781  }
1782  else
1783  {
1784  mesh_.unconform(polyFacesBf, Sf, Cf);
1785  }
1786 
1787  // Reset the poly faces instance to that of any loaded topology
1788  if (load)
1789  {
1790  mesh_.setPolyFacesBfInstance(polyFacesBfIO.instance());
1791  }
1792 
1793  // Resize all the affected patch fields
1794  resizePatchFields<VolField>();
1795  resizePatchFields<SurfaceField>();
1796 
1797  // Map the non-conformal patch field data back from the conformal faces and
1798  // into the new non-conformal patches
1799  if (changing)
1800  {
1801  postUnconformVolFields();
1802  postUnconformSurfaceFields();
1803  }
1804 
1805  // Prevent hangs caused by processor cyclic patches using mesh geometry
1806  mesh_.deltaCoeffs();
1807 
1808  if (any(patchCoupleds) && geometric)
1809  {
1810  const volScalarField::Internal o(openness());
1811  Info<< indent << "Cell min/avg/max openness = "
1812  << gMin(o) << '/' << gAverage(o) << '/' << gMax(o) << endl;
1813 
1814  if (mesh_.moving())
1815  {
1816  for (label i = 0; i <= mesh_.phi().nOldTimes(false); ++ i)
1817  {
1818  const volScalarField::Internal vce(volumeConservationError(i));
1819  Info<< indent << "Cell min/avg/max ";
1820  for (label j = 0; j < i; ++ j) Info<< "old-";
1821  Info<< (i ? "time " : "") << "volume conservation error = "
1822  << gMin(vce) << '/' << gAverage(vce) << '/' << gMax(vce)
1823  << endl;
1824  }
1825  }
1826 
1827  if (mesh_.moving())
1828  {
1829  // Create a boundary field of the imbalance between the mesh fluxes
1830  // on either side of interfaces (like phiErrorb above)
1832  (
1834  mesh_.phi().boundaryField()
1835  );
1836  mfe += mesh_.phi().boundaryField().boundaryNeighbourField();
1837 
1838  // Determine the number of non-processor patches
1839  label nNonProcPatches = 0;
1840  forAll(mesh_.boundary(), patchi)
1841  {
1842  const fvPatch& fvp = mesh_.boundary()[patchi];
1843 
1844  if (!isA<processorFvPatch>(fvp))
1845  {
1846  if (nNonProcPatches != patchi)
1847  {
1849  << "Processor patches do not follow non-processor "
1850  << "patches" << exit(FatalError);
1851  }
1852 
1853  nNonProcPatches ++;
1854  }
1855  }
1856 
1857  // Work out the min, avg and max error for all non-conformal
1858  // cyclics, including any errors on referred processor cyclics
1859  scalarList minMfe(nNonProcPatches, vGreat);
1860  scalarField sumMfe(nNonProcPatches, 0), nSumMfe(nNonProcPatches, 0);
1861  scalarList maxMfe(nNonProcPatches, -vGreat);
1862  forAll(mfe, patchi)
1863  {
1864  const fvPatch& fvp = mesh_.boundary()[patchi];
1865 
1866  const label nccPatchi =
1867  isA<nonConformalCyclicFvPatch>(fvp)
1868  ? refCast<const nonConformalCyclicFvPatch>(fvp)
1869  .index()
1870  : isA<nonConformalProcessorCyclicFvPatch>(fvp)
1871  ? refCast<const nonConformalProcessorCyclicFvPatch>(fvp)
1872  .referPatchIndex()
1873  : -1;
1874 
1875  if (nccPatchi != -1)
1876  {
1877  minMfe[nccPatchi] =
1878  min(minMfe[nccPatchi], min(mfe[patchi]));
1879  sumMfe[nccPatchi] += sum(mfe[patchi]);
1880  nSumMfe[nccPatchi] += mfe[patchi].size();
1881  maxMfe[nccPatchi] =
1882  max(maxMfe[nccPatchi], max(mfe[patchi]));
1883  }
1884  }
1885  reduce(minMfe, ListOp<minOp<scalar>>());
1886  reduce(sumMfe, ListOp<minOp<scalar>>());
1887  reduce(nSumMfe, ListOp<minOp<scalar>>());
1888  reduce(maxMfe, ListOp<maxOp<scalar>>());
1889 
1890  // Report
1891  forAll(minMfe, patchi)
1892  {
1893  const fvPatch& fvp = mesh_.boundary()[patchi];
1894 
1895  if
1896  (
1897  isA<nonConformalCyclicFvPatch>(fvp)
1898  && refCast<const nonConformalCyclicFvPatch>(fvp).owner()
1899  && nSumMfe[patchi] > 0
1900  )
1901  {
1902  Info<< indent << fvp.name()
1903  << " min/avg/max mesh flux error = " << minMfe[patchi]
1904  << '/' << sumMfe[patchi]/(nSumMfe[patchi] + vSmall)
1905  << '/' << maxMfe[patchi] << endl;
1906  }
1907  }
1908  }
1909  }
1910 
1911  if (any(patchCoupleds))
1912  {
1913  Info<< decrIndent;
1914  }
1915 
1916  // Create null polyTopoChangeMap
1917  const polyTopoChangeMap map(mesh_);
1918 
1919  meshObjects::topoChange<fvMesh>(mesh_, map);
1920  meshObjects::topoChange<lduMesh>(mesh_, map);
1921 
1922  const_cast<Time&>(mesh_.time()).functionObjects().topoChange(map);
1923 
1924  return true;
1925 }
1926 
1927 
1928 void Foam::fvMeshStitcher::preConformVolFields()
1929 {
1930  #define PreConformVolFields(Type, nullArg) \
1931  preConformVolFields<Type>();
1933  #undef PreConformVolFields
1934 }
1935 
1936 
1937 void Foam::fvMeshStitcher::preConformSurfaceFields()
1938 {
1939  #define PreConformSurfaceFields(Type, nullArg) \
1940  preConformSurfaceFields<Type>();
1942  #undef PreConformSurfaceFields
1943 }
1944 
1945 
1946 void Foam::fvMeshStitcher::postUnconformVolFields()
1947 {
1948  #define PostUnconformVolFields(Type, nullArg) \
1949  postUnconformVolFields<Type>();
1951  #undef PostUnconformVolFields
1952 
1953  const nonConformalBoundary& ncb = nonConformalBoundary::New(mesh());
1954  const labelList origPatchIndices = ncb.allOrigPatchIndices();
1955  const labelList errorPatchIndices = ncb.allErrorPatchIndices();
1956 
1957  // Special handling for velocities. Wherever we find a movingWall-type
1958  // boundary condition on an original patch, override the corresponding
1959  // error patch condition to movingWallSlipVelocity.
1960  UPtrList<volVectorField> Us(mesh().fields<volVectorField>());
1961  forAll(Us, i)
1962  {
1963  volVectorField& U = Us[i];
1964 
1965  forAll(errorPatchIndices, i)
1966  {
1967  const label errorPatchi = errorPatchIndices[i];
1968  const label origPatchi = origPatchIndices[i];
1969 
1970  typename volVectorField::Patch& origUp =
1971  boundaryFieldRefNoUpdate(U)[origPatchi];
1972 
1973  if
1974  (
1975  isA<movingWallVelocityFvPatchVectorField>(origUp)
1976  || isA<movingWallSlipVelocityFvPatchVectorField>(origUp)
1977  )
1978  {
1979  boundaryFieldRefNoUpdate(U).set
1980  (
1981  errorPatchi,
1982  new movingWallSlipVelocityFvPatchVectorField
1983  (
1984  mesh().boundary()[errorPatchi],
1985  U
1986  )
1987  );
1988  }
1989  }
1990  }
1991 
1992  #define PostUnconformEvaluateVolFields(Type, nullArg) \
1993  postUnconformEvaluateVolFields<Type>();
1995  #undef PostUnconformEvaluateVolFields
1996 }
1997 
1998 
1999 void Foam::fvMeshStitcher::postUnconformSurfaceFields()
2000 {
2001  #define PostUnconformSurfaceFields(Type, nullArg) \
2002  postUnconformSurfaceFields<Type>();
2004  #undef PostUnconformSurfaceFields
2005 
2006  // Special handling for velocities. Recompute the surface velocity using an
2007  // interpolation of the volume velocity.
2008  UPtrList<surfaceVectorField> Ufs(mesh_.fields<surfaceVectorField>());
2009  forAll(Ufs, i)
2010  {
2011  surfaceVectorField& Uf = Ufs[i];
2012 
2013  const volVectorField& U = surfaceToVolVelocity(Uf);
2014 
2015  if (isNull(U)) continue;
2016 
2017  const surfaceVectorField UfInterpolated(fvc::interpolate(U));
2018 
2019  forAll(Uf.boundaryField(), patchi)
2020  {
2021  if (isA<nonConformalFvPatch>(mesh_.boundary()[patchi]))
2022  {
2023  boundaryFieldRefNoUpdate(Uf)[patchi] ==
2024  UfInterpolated.boundaryField()[patchi];
2025  }
2026  }
2027  }
2028 }
2029 
2030 
2031 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
2032 
2034 {
2035  boolList result(mesh_.boundary().size(), false);
2036 
2037  forAll(mesh_.boundary(), patchi)
2038  {
2039  const fvPatch& fvp = mesh_.boundary()[patchi];
2040 
2041  // Initially assume all cyclics can be coupled
2042  if (isA<nonConformalCyclicFvPatch>(fvp))
2043  {
2044  result[patchi] = true;
2045  }
2046 
2047  // If, however, a processorCyclic is found, and this is not a parallel
2048  // run, then the associated cyclic cannot be coupled. This works in a
2049  // single loop because the processorCyclic patches are after the
2050  // (global) cyclic patches.
2051  if (isA<nonConformalProcessorCyclicFvPatch>(fvp))
2052  {
2053  const nonConformalProcessorCyclicFvPatch& ncpcFvp =
2054  refCast<const nonConformalProcessorCyclicFvPatch>(fvp);
2055 
2056  result[patchi] = Pstream::parRun();
2057  result[ncpcFvp.referPatchIndex()] = Pstream::parRun();
2058  }
2059 
2060  // A mapped wall can be coupled if the neighbour mesh is available and
2061  // it is a parallel run, or the pair of walls is confined to a single
2062  // process
2063  if (isA<nonConformalMappedWallFvPatch>(fvp))
2064  {
2065  const nonConformalMappedWallFvPatch& ncmwFvp =
2066  refCast<const nonConformalMappedWallFvPatch>(fvp);
2067 
2068  result[patchi] =
2069  ncmwFvp.haveNbr()
2070  && (
2071  Pstream::parRun()
2073  (
2074  ncmwFvp.patch().size(),
2075  ncmwFvp.nbrPatch().patch().size()
2076  ) != -1
2077  );
2078  }
2079  }
2080 
2081  return result;
2082 }
2083 
2084 
2086 {
2087  bool result = false;
2088 
2089  forAll(mesh_.boundary(), patchi)
2090  {
2091  const fvPatch& fvp = mesh_.boundary()[patchi];
2092 
2093  if (!isA<nonConformalFvPatch>(fvp)) continue;
2094 
2095  const fvsPatchScalarField& magSfp =
2096  mesh_.magSf().boundaryField()[patchi];
2097 
2098  const polyPatch& origPp =
2099  refCast<const nonConformalFvPatch>(fvp).origPatch().patch();
2100 
2101  const scalarField origMagSfp
2102  (
2103  origPp.magFaceAreas(),
2104  mesh_.polyFacesBf()[patchi] - origPp.start()
2105  );
2106 
2107  if (max(magSfp/origMagSfp) > rootSmall)
2108  {
2109  result = true;
2110  }
2111  }
2112 
2113  reduce(result, orOp<bool>());
2114 
2115  return returnReduce(result, orOp<bool>());
2116 }
2117 
2118 
2121 {
2122  return
2123  mag(fvc::surfaceIntegrate(mesh_.Sf()))()*mesh_.V()
2124  /max
2125  (
2126  mag(fvc::surfaceSum(cmptMag(mesh_.Sf())))(),
2127  (small*sqr(cbrt(mesh_.V())))()
2128  )();
2129 }
2130 
2131 
2134 {
2135  if (0 > n || n > 1)
2136  {
2138  << "Can only compute volume conservation error for this time, or "
2139  << "the previous time" << exit(FatalError);
2140  }
2141 
2142  const surfaceScalarField& phi = mesh_.phi().oldTime(n);
2143 
2144  const dimensionedScalar deltaT =
2145  n == 0 ? mesh_.time().deltaT() : mesh_.time().deltaT0();
2146 
2147  const volScalarField::Internal& V = n == 0 ? mesh_.V() : mesh_.V0();
2148  const volScalarField::Internal& V0 = n == 0 ? mesh_.V0() : mesh_.V00();
2149 
2150  return fvc::surfaceIntegrate(phi*deltaT)() - (V - V0)/mesh_.V();
2151 }
2152 
2153 
2154 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
2155 
2157 :
2158  mesh_(mesh),
2159  ready_(false),
2160  regionPolyFacesBfIOs_(),
2161  regionPolyFacesBfs_()
2162 {}
2163 
2164 
2165 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
2166 
2168 {}
2169 
2170 
2171 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
2172 
2174 {
2175  forAll(mesh_.boundary(), patchi)
2176  {
2177  if (isA<nonConformalFvPatch>(mesh_.boundary()[patchi]))
2178  {
2179  return true;
2180  }
2181  }
2182 
2183  return false;
2184 }
2185 
2186 
2188 {
2189  UPtrList<const fvMesh> regionMeshes(this->regionMeshes());
2190 
2191  forAll(regionMeshes, i)
2192  {
2193  if (regionMeshes[i].dynamic())
2194  {
2195  return true;
2196  }
2197  }
2198 
2199  return false;
2200 }
2201 
2202 
2204 (
2205  const bool changing,
2206  const bool geometric
2207 )
2208 {
2209  // Disconnection can happen independently of the state of connected
2210  // regions. So, just disconnect immediately.
2211 
2212  return disconnectThis(changing, geometric);
2213 }
2214 
2215 
2217 (
2218  const bool changing,
2219  const bool geometric,
2220  const bool load
2221 )
2222 {
2223  if (!changing)
2224  {
2225  return connectThis(changing, geometric, load);
2226  }
2227 
2228  // Connection requires all regions to be available and at a consistent
2229  // state. So, we need to wait until they are all ready. So, if some are not
2230  // ready, then this function just flips the ready flag for this region. If
2231  // they are all ready, then this function stitches all of them.
2232 
2233  ready_ = true;
2234 
2235  // Get all the connected region meshes
2236  MultiRegionUList<fvMesh> regionMeshes(this->regionMeshes());
2237 
2238  // If any mesh is not ready, then don't do anything
2239  forAll(regionMeshes, i)
2240  {
2241  if (!regionMeshes[i]().stitcher().ready_)
2242  {
2243  return false;
2244  }
2245  }
2246 
2247  // All meshes are ready, so connect all of them up
2248  bool result = false;
2249  forAll(regionMeshes, i)
2250  {
2251  if (regionMeshes[i]().stitcher().connectThis(changing, geometric, load))
2252  {
2253  result = true;
2254  }
2255  }
2256 
2257  // Set all meshes as not ready, in preparation for the next iteration
2258  forAll(regionMeshes, i)
2259  {
2260  regionMeshes[i]().stitcher().ready_ = false;
2261  }
2262 
2263  return result;
2264 }
2265 
2266 
2267 void Foam::fvMeshStitcher::reconnect(const bool geometric) const
2268 {
2269  if (mesh_.conformal() || geometric == this->geometric())
2270  {
2271  return;
2272  }
2273 
2274  // Create a copy of the non-conformal poly face addressing
2275  surfaceLabelField::Boundary polyFacesBf
2276  (
2278  mesh_.polyFacesBf()
2279  );
2280 
2281  // Undo all non-conformal changes and clear all geometry and topology
2282  mesh_.conform();
2283 
2284  // Determine which patches are coupled
2285  const boolList patchCoupleds =
2286  geometric
2287  ? this->patchCoupleds()
2288  : boolList(mesh_.boundary().size(), false);
2289 
2290  // Create copies of geometry fields to be modified
2291  surfaceVectorField Sf(mesh_.Sf().cloneUnSliced()());
2292  surfaceVectorField Cf(mesh_.Cf().cloneUnSliced()());
2293 
2294  // Construct non-conformal geometry
2295  intersect(polyFacesBf, Sf, Cf, patchCoupleds, true);
2296 
2297  // Apply changes to the mesh
2298  mesh_.unconform(polyFacesBf, Sf, Cf);
2299 
2300  // Prevent hangs caused by processor cyclic patches using mesh geometry
2301  mesh_.deltaCoeffs();
2302 
2303  // Create null polyTopoChangeMap
2304  const polyTopoChangeMap map(mesh_);
2305 
2306  meshObjects::topoChange<fvMesh>(mesh_, map);
2307  meshObjects::topoChange<lduMesh>(mesh_, map);
2308 
2309  const_cast<Time&>(mesh_.time()).functionObjects().topoChange(map);
2310 }
2311 
2312 
2314 {}
2315 
2316 
2318 {}
2319 
2320 
2322 {}
2323 
2324 
2325 // ************************************************************************* //
label n
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
static nonConformalBoundary & New(const word &name, const polyMesh &mesh)
Construct and return the named DemandDrivenMeshObject.
Field with dimensions and associated with geometry type GeoMesh which is used to size the field and a...
static const DimensionedField< Type, GeoMesh > & null()
Return a null DimensionedField.
Generic GeometricBoundaryField class.
Generic GeometricField class.
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.
PatchField< Type > Patch
Type of the patch field of which the Boundary is composed.
static const GeometricField< Type, PatchField, GeoMesh > & null()
Return a null geometric field.
Boundary & boundaryFieldRef()
Return a reference to the boundary field.
static tmp< GeometricField< Type, PatchField, GeoMesh > > New(const word &name, const Internal &, const PtrList< PatchField< Type >> &, const HashPtrTable< Source > &=HashPtrTable< Source >())
Return a temporary field constructed from name,.
IOobject defines the attributes of an object for which implicit objectRegistry management is supporte...
Definition: IOobject.H:99
virtual const fileName & name() const
Return the name of the stream.
Definition: IOstream.H:294
const FieldType & oldTime() const
Return the old time field.
Definition: OldTimeField.C:315
A list of faces which address into the list of points.
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:76
static label nProcs(const label communicator=0)
Number of processes in parallel run.
Definition: UPstream.H:411
static bool & parRun()
Is this a parallel run?
Definition: UPstream.H:399
static int myProcNo(const label communicator=0)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:429
A templated 1D list of pointers to objects of type <T>, where the size of the array is known and used...
Definition: UPtrList.H:66
static Form uniform(const Cmpt &s)
Return a VectorSpace with all elements = s.
Definition: VectorSpaceI.H:168
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:61
static int compare(const edge &, const edge &)
Compare edges.
Definition: edgeI.H:36
Mesh manipulator that uses the intersection provided by the cyclic non-conformal poly patches to crea...
bool disconnect(const bool changing, const bool geometric)
Disconnect the mesh by removing faces from the nonConformalCyclics.
bool connect(const bool changing, const bool geometric, const bool load)
Connect the mesh by adding faces into the nonConformalCyclics.
void reconnect(const bool geometric) const
Re-compute the connection. Topology is preserved. Permits a change.
bool geometric() const
Is the connection "geometric", or has the topology just been.
fvMeshStitcher(fvMesh &)
Construct from fvMesh.
virtual void topoChange(const polyTopoChangeMap &)
Update corresponding to the given map.
virtual void distribute(const polyDistributionMap &)
Update corresponding to the given distribution map.
bool stitches() const
Does this stitcher do anything?
fvMesh & mesh()
Return the fvMesh.
virtual void mapMesh(const polyMeshMap &)
Update from another mesh using the given map.
tmp< DimensionedField< scalar, volMesh > > openness() const
Return the non-dimensional cell openness for debugging/checking.
tmp< DimensionedField< scalar, volMesh > > volumeConservationError(const label n) const
Return the non-dimensional old-time volume conservation error.
bool dynamic() const
Is the set of connected region meshes dynamic?
boolList patchCoupleds() const
Determine which patches are coupled; i.e., for which.
virtual ~fvMeshStitcher()
Destructor.
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:99
const fvBoundaryMesh & boundary() const
Return reference to boundary mesh.
Definition: fvMesh.C:857
const word & name() const
Return reference to name.
Definition: fvMesh.H:432
A finiteVolume patch using a polyPatch and a fvBoundaryMesh.
Definition: fvPatch.H:64
virtual label size() const
Return size.
Definition: fvPatch.H:138
An abstract base class with a fat-interface to all derived classes covering all possible ways in whic...
Definition: fvsPatchField.H:83
bool haveNbr() const
Is the neighbour available?
const fvPatch & patch() const
Reference to the fvPatch.
static tmp< Field< Type > > map(const fvsPatchLabelField &srcPolyFacesPf, const Field< Type > &srcFld, const fvsPatchLabelField &tgtPolyFacesPf)
Map/interpolate from one patch to another.
Wall fv patch which can do non-conformal mapping of values from another potentially non-globally conf...
const nonConformalMappedWallFvPatch & nbrPatch() const
Neighbour patch.
Non-conformal processor cyclic FV patch. As nonConformalCyclicFvPatch, but the neighbouring patch is ...
Class containing mesh-to-mesh mapping information after a mesh distribution where we send parts of me...
Class containing mesh-to-mesh mapping information.
Definition: polyMeshMap.H:51
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:70
label start() const
Return start label of this patch in the polyMesh face list.
Definition: polyPatch.H:280
const scalarField::subField magFaceAreas() const
Return face area magnitudes.
Definition: polyPatch.C:288
Class containing mesh-to-mesh mapping information after a change in polyMesh topology.
label referPatchIndex() const
Return the referring patch ID.
static void syncPointList(const polyMesh &, List< T > &, const CombineOp &cop, const T &nullValue, const TransformOp &top)
Synchronise values on all mesh points.
static void syncEdgeList(const polyMesh &, List< T > &, const CombineOp &cop, const T &nullValue, const TransformOp &top)
Synchronise values on all mesh edges.
A class for managing temporary objects.
Definition: tmp.H:55
static const word null
An empty word.
Definition: word.H:77
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
#define PreConformVolFields(Type, nullArg)
#define PostUnconformEvaluateVolFields(Type, nullArg)
#define PreConformSurfaceFields(Type, nullArg)
#define PostUnconformVolFields(Type, nullArg)
#define PostUnconformSurfaceFields(Type, nullArg)
Surface integrate surfaceField creating a volField. Surface sum a surfaceField creating a volField.
label patchi
gmvFile<< "tracers "<< particles.size()<< nl;{ pointField positions(particles.size());label particlei=0;forAllConstIter(Cloud< passiveParticle >, particles, iter) { positions[particlei++]=iter().position(mesh);} for(i=0;i< pTraits< point >::nComponents;i++) { forAll(positions, particlei) { gmvFile<< component(positions[particlei], i)<< ' ';} gmvFile<< nl;}}forAll(lagrangianScalarNames, i){ const word &name=lagrangianScalarNames[i];IOField< scalar > fld(IOobject(name, runTime.name(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
U
Definition: pEqn.H:72
const dimensionedScalar c
Speed of light in a vacuum.
tmp< VolField< Type > > surfaceSum(const SurfaceField< Type > &ssf)
static tmp< SurfaceField< Type > > interpolate(const VolField< Type > &tvf, const surfaceScalarField &faceFlux, Istream &schemeData)
Interpolate field onto faces using scheme given by Istream.
void surfaceIntegrate(Field< Type > &ivf, const SurfaceField< Type > &ssf)
label singleProcess(const label srcSize, const label tgtSize)
Determine whether this intersection is confined to a single processor or.
Namespace for OpenFOAM.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
static const zero Zero
Definition: zero.H:97
List< word > wordList
A List of words.
Definition: fileName.H:54
const doubleScalar e
Definition: doubleScalar.H:106
VolField< vector > volVectorField
Definition: volFieldsFwd.H:65
Ostream & decrIndent(Ostream &os)
Decrement the indent level.
Definition: Ostream.H:241
List< label > labelList
A List of labels.
Definition: labelList.H:56
defineRunTimeSelectionTable(reactionRateFlameArea, dictionary)
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
dimensionedSymmTensor sqr(const dimensionedVector &dv)
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:257
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &df)
const dimensionSet dimless
const word & regionName(const solver &region)
Definition: solver.H:209
SurfaceField< scalar > surfaceScalarField
messageStream Info
SurfaceField< label > surfaceLabelField
const dimensionSet dimLength
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:42
Ostream & incrIndent(Ostream &os)
Increment the indent level.
Definition: Ostream.H:234
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
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
layerAndWeight min(const layerAndWeight &a, const layerAndWeight &b)
Vector< scalar > vector
A scalar version of the templated Vector.
Definition: vector.H:49
fvsPatchField< label > fvsPatchLabelField
void reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
void sort(UList< T > &)
Definition: UList.C:115
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
edge meshEdge(const PrimitivePatch< FaceList, PointField > &p, const label edgei)
dimensionSet normalised(const dimensionSet &)
Definition: dimensionSet.C:510
dimensioned< scalar > mag(const dimensioned< Type > &)
defineTypeNameAndDebug(combustionModel, 0)
layerAndWeight max(const layerAndWeight &a, const layerAndWeight &b)
dimensioned< vector > dimensionedVector
Dimensioned vector obtained from generic dimensioned type.
Field< vector > vectorField
Specialisation of Field<T> for vector.
dimensionSet cmptMag(const dimensionSet &)
Definition: dimensionSet.C:296
bool any(const boolList &l)
Type gAverage(const FieldField< Field, Type > &f)
bool isNull(const T &t)
Return true if t is a reference to the nullObject of type T.
Definition: nullObjectI.H:58
HashSet wordHashSet
A HashSet with word keys.
Definition: HashSet.H:210
error FatalError
FOR_ALL_FIELD_TYPES(makeFieldSourceTypedef)
const dimensionSet dimArea
void Swap(T &a, T &b)
Definition: Swap.H:43
List< edge > edgeList
Definition: edgeList.H:38
label count(const ListType &l, typename ListType::const_reference x)
Count the number of occurrences of a value in a list.
dimensionedScalar cbrt(const dimensionedScalar &ds)
Type gMin(const FieldField< Field, Type > &f)
static const label labelMax
Definition: label.H:62
SurfaceField< vector > surfaceVectorField
dimensionSet perpendicular(const dimensionSet &)
Definition: dimensionSet.C:516
Ostream & indent(Ostream &os)
Indent stream.
Definition: Ostream.H:227
fvsPatchField< vector > fvsPatchVectorField
dimensioned< scalar > magSqr(const dimensioned< Type > &)
Type gMax(const FieldField< Field, Type > &f)
const volVectorField & surfaceToVolVelocity(const surfaceVectorField &Uf)
Get the cell velocity field corresponding to a given face velocity, or a.
faceListList boundary(nPatches)
const Foam::wordList regionNames(args.optionFound("allRegions") ? runTime .controlDict().subDict("regionSolvers").toc() :wordList(1, args.optionFound("region") ? args.optionRead< word >("region") :polyMesh::defaultRegion))
volScalarField & p
Return the vol-field velocity corresponding to a given surface-field velocity.