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