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-2025 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  (
173  mesh.dbDir()/fvMesh::typeName,
174  "polyFaces",
176  ),
177  fvMesh::typeName,
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
479  List<List<FixedList<label, 3>>> indices(Pstream::nProcs());
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)
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 
553  // Populate the indices with the index of the coupling
554  label nCouplesRemoved = 0, nCouplesAdded = 0;
555  forAll(indices, proci)
556  {
557  const label patchi = patchis[proci];
558 
559  if (patchi != -1)
560  {
561  label refi = 0, i = 0;
562 
563  DynamicList<FixedList<label, 3>> removedIndices;
564 
565  while
566  (
567  refi < indicesRef[proci].size()
568  && i < indices[proci].size()
569  )
570  {
571  const FixedList<label, 3> index
572  ({
573  indices[proci][i][0],
574  indices[proci][i][1],
575  0
576  });
577 
578  FixedList<label, 3>& indexRef = indicesRef[proci][refi];
579 
580  if (index < indexRef)
581  {
582  nCouplesRemoved ++;
583  removedIndices.append
584  ({
585  indices[proci][i][0],
586  indices[proci][i][1],
587  - indices[proci][i][2]
588  });
589  i ++;
590  }
591  else if (index == indexRef)
592  {
593  indexRef[2] = indices[proci][i][2];
594  refi ++;
595  i ++;
596  }
597  else // (index > indexRef)
598  {
599  nCouplesAdded ++;
600  refi ++;
601  }
602  }
603 
604  nCouplesRemoved += min(indices[proci].size() - i, 0);
605  nCouplesAdded += min(indicesRef[proci].size() - refi, 0);
606 
607  indicesRef[proci].append(removedIndices);
608  }
609  }
610 
611  // Report if changes have been made
612  reduce(nCouplesRemoved, sumOp<label>());
613  reduce(nCouplesAdded, sumOp<label>());
614  if ((nCouplesRemoved || nCouplesAdded) && owner)
615  {
616  Info<< indent << nCouplesRemoved << '/' << nCouplesAdded
617  << " small couplings removed/added to " << ncFvp.name()
618  << endl;
619  }
620 
621  // Set the indices to the correct values
622  Swap(indices, indicesRef);
623 }
624 
625 
626 Foam::label Foam::fvMeshStitcher::nValidIndices
627 (
628  const List<FixedList<label, 3>>& indices
629 )
630 {
631  label n = indices.size();
632  while (n > 0 && indices[n - 1][2] < 0) n --;
633  return n;
634 }
635 
636 
637 void Foam::fvMeshStitcher::createCouplings
638 (
639  surfaceLabelField::Boundary& polyFacesBf,
642  const tmp<surfaceVectorField::Boundary>& tOrigSfNbrBf,
643  const tmp<surfaceVectorField::Boundary>& tOrigCfNbrBf,
644  const List<List<FixedList<label, 3>>>& indices,
645  const List<DynamicList<couple>>& couples,
646  const polyPatch& origPp,
647  const labelList& patchis,
648  const labelList& patchOffsets,
649  const bool owner
650 )
651 {
652  // Add coupled faces into the non-conformal patches
653  forAll(patchis, proci)
654  {
655  const label patchi = patchis[proci];
656  const label patchOffset = patchOffsets[proci];
657 
658  if (patchi != -1)
659  {
660  forAll(indices[proci], indexi)
661  {
662  const label origFacei = indices[proci][indexi][!owner];
663  const label i = indices[proci][indexi][2];
664 
665  const label patchFacei = i >= 0 ? indexi + patchOffset : -1;
666 
667  couple c;
668  if (i != 0)
669  {
670  c = couples[origFacei][mag(i) - 1];
671  }
672  else
673  {
674  c =
675  couple
676  (
677  part
678  (
679  small*origPp.faceAreas()[origFacei],
680  origPp.faceCentres()[origFacei]
681  ),
682  part
683  (
684  small*tOrigSfNbrBf()[patchi][patchFacei],
685  tOrigCfNbrBf()[patchi][patchFacei]
686  )
687  );
688  }
689 
690  // The two parts of the coupling. The projection is to the
691  // neighbour, so the other-side is always taken from the
692  // neighbouring patch faces.
693  const part& pThis = c, & pOther = owner ? c.nbr : c;
694 
695  // Remove the area from the corresponding original face
696  if (i >= 0 || owner)
697  {
698  part origP
699  (
700  SfBf[origPp.index()][origFacei],
701  CfBf[origPp.index()][origFacei]
702  );
703  origP -= pThis;
704  if (i < 0 && owner) origP += pOther;
705 
706  SfBf[origPp.index()][origFacei] = origP.area;
707  CfBf[origPp.index()][origFacei] = origP.centre;
708  }
709 
710  // Add the new coupled face
711  if (i >= 0)
712  {
713  polyFacesBf[patchi][patchFacei] =
714  origFacei + origPp.start();
715  SfBf[patchi][patchFacei] = pOther.area;
716  CfBf[patchi][patchFacei] = pOther.centre;
717  }
718  }
719  }
720  }
721 }
722 
723 
724 void Foam::fvMeshStitcher::createErrorAndEdgeParts
725 (
728  List<part>& origEdgeParts,
729  const patchToPatches::intersection& intersection,
730  const polyPatch& origPp
731 )
732 {
733  // Add error geometry to the original patches
734  forAll(intersection.srcErrorParts(), origFacei)
735  {
736  part origP
737  (
738  SfBf[origPp.index()][origFacei],
739  CfBf[origPp.index()][origFacei]
740  );
741  origP += intersection.srcErrorParts()[origFacei];
742 
743  SfBf[origPp.index()][origFacei] = origP.area;
744  CfBf[origPp.index()][origFacei] = origP.centre;
745  }
746 
747  // Store the orig edge parts
748  forAll(intersection.srcEdgeParts(), origEdgei)
749  {
750  origEdgeParts[origEdgei] += intersection.srcEdgeParts()[origEdgei];
751  }
752 }
753 
754 
755 void Foam::fvMeshStitcher::intersectNonConformalCyclic
756 (
757  const nonConformalCyclicFvPatch& nccFvp,
758  surfaceLabelField::Boundary& polyFacesBf,
761  const tmp<surfaceLabelField::Boundary>& tOrigFacesNbrBf,
762  const tmp<surfaceVectorField::Boundary>& tOrigSfNbrBf,
763  const tmp<surfaceVectorField::Boundary>& tOrigCfNbrBf,
764  List<part>& origEdgeParts
765 ) const
766 {
767  // Alias the original poly patches
768  const polyPatch& origPp = nccFvp.origPatch().patch();
769 
770  // Get the indices of the related (i.e., cyclic and processorCyclic)
771  // non-conformal patches. Index based on the connected processor.
772  labelList patchis(Pstream::nProcs(), -1);
773  patchis[Pstream::myProcNo()] = nccFvp.index();
774  forAll(mesh_.boundary(), patchj)
775  {
776  const fvPatch& fvp = mesh_.boundary()[patchj];
777 
778  if (isA<nonConformalProcessorCyclicFvPatch>(fvp))
779  {
780  const nonConformalProcessorCyclicFvPatch& ncpcFvp =
781  refCast<const nonConformalProcessorCyclicFvPatch>(fvp);
782 
783  if (ncpcFvp.referPatchIndex() == nccFvp.index())
784  {
785  patchis[ncpcFvp.neighbProcNo()] = patchj;
786  }
787  }
788  }
789 
790  // Get the intersection geometry
791  const patchToPatches::intersection& intersection =
792  nccFvp.owner()
793  ? nccFvp.nonConformalCyclicPatch().intersection()
794  : nccFvp.nbrPatch().nonConformalCyclicPatch().intersection();
795 
796  // Unpack the patchToPatch addressing into a list of indices
797  List<List<FixedList<label, 3>>> indices =
798  procFacesToIndices
799  (
800  nccFvp.owner()
801  ? intersection.srcTgtProcFaces()
802  : intersection.tgtSrcProcFaces(),
803  nccFvp.owner()
804  );
805 
806  // If addressing has been provided, then modify the indices to match
807  if (tOrigFacesNbrBf.valid())
808  {
809  labelList patchSizes(Pstream::nProcs(), -1);
810  forAll(patchis, proci)
811  {
812  if (patchis[proci] != -1)
813  {
814  patchSizes[proci] = polyFacesBf[patchis[proci]].size();
815  }
816  }
817 
818  matchIndices
819  (
820  polyFacesBf,
821  tOrigFacesNbrBf(),
822  indices,
823  nccFvp,
824  origPp,
825  patchis,
827  patchSizes,
828  nccFvp.owner()
829  );
830  }
831 
832  // Check the existence of the non-conformal patches to be added into and
833  // resize them as necessary
834  forAll(patchis, proci)
835  {
836  const label patchi = patchis[proci];
837  const label patchSize = nValidIndices(indices[proci]);
838 
839  if (patchi == -1 && patchSize)
840  {
842  << "Intersection generated coupling through "
843  << nccFvp.name() << " between processes "
844  << Pstream::myProcNo() << " and " << proci
845  << " but a corresponding processor interface was not found"
846  << exit(FatalError);
847  }
848 
849  if (patchi != -1)
850  {
851  polyFacesBf[patchi].resize(patchSize);
852  SfBf[patchi].resize(patchSize);
853  CfBf[patchi].resize(patchSize);
854  }
855  }
856 
857  // Create couplings by transferring geometry from the original to the
858  // non-conformal patches
859  createCouplings
860  (
861  polyFacesBf,
862  SfBf,
863  CfBf,
864  tOrigSfNbrBf,
865  tOrigCfNbrBf,
866  indices,
867  nccFvp.owner() ? intersection.srcCouples() : intersection.tgtCouples(),
868  origPp,
869  patchis,
871  nccFvp.owner()
872  );
873 
874  // Add error geometry to the original patches and store the edge parts
875  if (nccFvp.owner())
876  {
877  createErrorAndEdgeParts
878  (
879  SfBf,
880  CfBf,
881  origEdgeParts,
882  intersection,
883  origPp
884  );
885  }
886 }
887 
888 
889 void Foam::fvMeshStitcher::intersectNonConformalMappedWall
890 (
891  const nonConformalMappedWallFvPatch& ncmwFvp,
892  surfaceLabelField::Boundary& polyFacesBf,
895  const tmp<surfaceLabelField::Boundary>& tOrigFacesNbrBf,
896  const tmp<surfaceVectorField::Boundary>& tOrigSfNbrBf,
897  const tmp<surfaceVectorField::Boundary>& tOrigCfNbrBf,
898  List<part>& origEdgeParts
899 ) const
900 {
901  // Alias the original poly patch
902  const polyPatch& origPp = ncmwFvp.origPatch().patch();
903 
904  // Get the intersection geometry
905  const patchToPatches::intersection& intersection =
906  ncmwFvp.owner()
907  ? ncmwFvp.nonConformalMappedWallPatch().intersection()
908  : ncmwFvp.nbrPatch().nonConformalMappedWallPatch().intersection();
909 
910  // Unpack the patchToPatch addressing into a list of indices
911  List<List<FixedList<label, 3>>> indices =
912  procFacesToIndices
913  (
914  ncmwFvp.owner()
915  ? intersection.srcTgtProcFaces()
916  : intersection.tgtSrcProcFaces(),
917  ncmwFvp.owner()
918  );
919 
920  // Obtain the label patch field for the mapped wall poly faces
921  nonConformalMappedPolyFacesFvsPatchLabelField& polyFacesPf =
922  refCast<nonConformalMappedPolyFacesFvsPatchLabelField>
923  (
924  polyFacesBf[ncmwFvp.index()]
925  );
926 
927  // If addressing has been provided, then modify the indices to match
928  if (tOrigFacesNbrBf.valid())
929  {
930  matchIndices
931  (
932  polyFacesBf,
933  tOrigFacesNbrBf(),
934  indices,
935  ncmwFvp,
936  origPp,
937  labelList(Pstream::nProcs(), ncmwFvp.index()),
938  polyFacesPf.procOffsets(),
939  polyFacesPf.procSizes(),
940  ncmwFvp.owner()
941  );
942  }
943 
944  // Set the processor offsets within the mapped polyFacesBf patch field
945  {
946  label count = 0;
947 
948  forAll(indices, proci)
949  {
950  polyFacesPf.procOffsets()[proci] = count;
951 
952  count += nValidIndices(indices[proci]);
953  }
954 
955  polyFacesPf.resize(count);
956  SfBf[polyFacesPf.patch().index()].resize(count);
957  CfBf[polyFacesPf.patch().index()].resize(count);
958  };
959 
960  // Create a coupling by transferring geometry from the original to the
961  // non-conformal patch
962  createCouplings
963  (
964  polyFacesBf,
965  SfBf,
966  CfBf,
967  tOrigSfNbrBf,
968  tOrigCfNbrBf,
969  indices,
970  ncmwFvp.owner() ? intersection.srcCouples() : intersection.tgtCouples(),
971  origPp,
972  labelList(Pstream::nProcs(), ncmwFvp.index()),
973  polyFacesPf.procOffsets(),
974  ncmwFvp.owner()
975  );
976 
977  // Add error geometry to the original patch and store the edge parts
978  if (ncmwFvp.owner())
979  {
980  createErrorAndEdgeParts
981  (
982  SfBf,
983  CfBf,
984  origEdgeParts,
985  intersection,
986  origPp
987  );
988  }
989 }
990 
991 
993 Foam::fvMeshStitcher::calculateOwnerOrigBoundaryEdgeParts
994 (
995  const List<List<part>>& patchEdgeParts
996 ) const
997 {
998  const polyBoundaryMesh& pbMesh = mesh_.boundaryMesh();
999 
1000  const nonConformalBoundary& ncb = nonConformalBoundary::New(mesh_);
1001  const labelList& ownerOrigBoundaryPointMeshPoint =
1002  ncb.ownerOrigBoundaryPointMeshPoint();
1003  const labelList& ownerOrigBoundaryEdgeMeshEdge =
1004  ncb.ownerOrigBoundaryEdgeMeshEdge();
1005  const edgeList& ownerOrigBoundaryEdges = ncb.ownerOrigBoundaryEdges();
1006  const edgeList& ownerOrigBoundaryMeshEdges =
1007  ncb.ownerOrigBoundaryMeshEdges();
1008 
1009  // Sum up boundary edge parts, being careful with signs
1010  labelList ownerOrigBoundaryEdgeNParts(ownerOrigBoundaryEdges.size(), 0);
1011  List<part> ownerOrigBoundaryEdgeParts(ownerOrigBoundaryEdges.size());
1012  forAll(patchEdgeParts, patchi)
1013  {
1014  if (patchEdgeParts[patchi].empty()) continue;
1015 
1016  const polyPatch& patch = pbMesh[patchi];
1017 
1018  const labelList& patchEdgeOwnerOrigBoundaryEdges =
1019  ncb.patchEdgeOwnerOrigBoundaryEdges(patchi);
1020 
1021  forAll(patchEdgeParts[patchi], patchEdgei)
1022  {
1023  const label ownerOrigBoundaryEdgei =
1024  patchEdgeOwnerOrigBoundaryEdges[patchEdgei];
1025 
1026  const label sign =
1028  (
1029  meshEdge(patch, patchEdgei),
1030  ownerOrigBoundaryMeshEdges[ownerOrigBoundaryEdgei]
1031  );
1032 
1033  const part& pU = patchEdgeParts[patchi][patchEdgei];
1034  const part p = sign > 0 ? pU : -pU;
1035 
1036  ownerOrigBoundaryEdgeNParts[ownerOrigBoundaryEdgei] ++;
1037  ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei] += p;
1038  }
1039  }
1040 
1041  // Give every point in the all boundary a global unique index
1042  const globalIndex globalOwnerOrigBoundaryPoints
1043  (
1044  ownerOrigBoundaryPointMeshPoint.size()
1045  );
1046  labelList ownerOrigBoundaryPointIndices
1047  (
1048  ownerOrigBoundaryPointMeshPoint.size()
1049  );
1050  forAll(ownerOrigBoundaryPointIndices, ownerOrigBoundaryPointi)
1051  {
1052  ownerOrigBoundaryPointIndices[ownerOrigBoundaryPointi] =
1053  globalOwnerOrigBoundaryPoints.toGlobal(ownerOrigBoundaryPointi);
1054  }
1056  (
1057  mesh_,
1058  ownerOrigBoundaryPointMeshPoint,
1059  ownerOrigBoundaryPointIndices,
1060  minEqOp<label>(),
1061  labelMax
1062  );
1063 
1064  // Synchronise the sign of the edge parts so that they can be
1065  // meaningfully summed. This is done using the sign of the global point
1066  // indices; if they are not in order, then reverse the part area.
1067  forAll(ownerOrigBoundaryEdgeParts, ownerOrigBoundaryEdgei)
1068  {
1069  const edge& e = ownerOrigBoundaryEdges[ownerOrigBoundaryEdgei];
1070 
1071  if
1072  (
1073  ownerOrigBoundaryPointIndices[e.start()]
1074  > ownerOrigBoundaryPointIndices[e.end()])
1075  {
1076  ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei] =
1077  - ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei];
1078  }
1079  }
1080 
1081  // Average the boundary edge parts across couplings
1083  (
1084  mesh_,
1085  ownerOrigBoundaryEdgeMeshEdge,
1086  ownerOrigBoundaryEdgeNParts,
1087  plusEqOp<label>(),
1088  label(0)
1089  );
1091  (
1092  mesh_,
1093  ownerOrigBoundaryEdgeMeshEdge,
1094  ownerOrigBoundaryEdgeParts,
1095  plusEqOp<part>(),
1096  part(),
1097  []
1098  (
1099  const transformer& vt,
1100  const bool forward,
1101  List<part>& fld
1102  )
1103  {
1104  if (forward)
1105  {
1106  forAll(fld, i)
1107  {
1108  if (magSqr(fld[i].area) != 0)
1109  {
1110  fld[i].area = vt.transform(fld[i].area);
1111  fld[i].centre = vt.transformPosition(fld[i].centre);
1112  }
1113  }
1114  }
1115  else
1116  {
1117  forAll(fld, i)
1118  {
1119  if (magSqr(fld[i].area) != 0)
1120  {
1121  fld[i].area = vt.invTransform(fld[i].area);
1122  fld[i].centre = vt.invTransformPosition(fld[i].centre);
1123  }
1124  }
1125  }
1126  }
1127  );
1128  forAll(ownerOrigBoundaryEdgeParts, ownerOrigBoundaryEdgei)
1129  {
1130  if (ownerOrigBoundaryEdgeNParts[ownerOrigBoundaryEdgei] != 0)
1131  {
1132  ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei].area /=
1133  ownerOrigBoundaryEdgeNParts[ownerOrigBoundaryEdgei];
1134  }
1135  }
1136 
1137  // Put the edge parts back to their original orientations
1138  forAll(ownerOrigBoundaryEdgeParts, ownerOrigBoundaryEdgei)
1139  {
1140  const edge& e = ownerOrigBoundaryEdges[ownerOrigBoundaryEdgei];
1141 
1142  if
1143  (
1144  ownerOrigBoundaryPointIndices[e.start()]
1145  > ownerOrigBoundaryPointIndices[e.end()]
1146  )
1147  {
1148  ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei] =
1149  - ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei];
1150  }
1151  }
1152 
1153  return ownerOrigBoundaryEdgeParts;
1154 }
1155 
1156 
1157 void Foam::fvMeshStitcher::applyOwnerOrigBoundaryEdgeParts
1158 (
1159  surfaceVectorField& SfSf,
1160  surfaceVectorField& CfSf,
1161  const List<part>& ownerOrigBoundaryEdgeParts
1162 ) const
1163 {
1164  const polyBoundaryMesh& pbMesh = mesh_.boundaryMesh();
1165 
1166  const nonConformalBoundary& ncb = nonConformalBoundary::New(mesh_);
1167  const labelList ownerOrigPatchIndices = ncb.ownerOrigPatchIndices();
1168  const labelList& ownerOrigBoundaryEdgeMeshEdge =
1169  ncb.ownerOrigBoundaryEdgeMeshEdge();
1170  const edgeList& ownerOrigBoundaryMeshEdges =
1171  ncb.ownerOrigBoundaryMeshEdges();
1172 
1173  boolList patchIsOwnerOrig(pbMesh.size(), false);
1174  UIndirectList<bool>(patchIsOwnerOrig, ownerOrigPatchIndices) = true;
1175 
1176  // Count the number of original faces attached to each boundary edge
1177  labelList ownerOrigBoundaryEdgeNOrigFaces
1178  (
1179  ownerOrigBoundaryEdgeMeshEdge.size(),
1180  0
1181  );
1182  forAll(ownerOrigBoundaryEdgeMeshEdge, ownerOrigBoundaryEdgei)
1183  {
1184  const label meshEdgei =
1185  ownerOrigBoundaryEdgeMeshEdge[ownerOrigBoundaryEdgei];
1186 
1187  forAll(mesh_.edgeFaces()[meshEdgei], edgeFacei)
1188  {
1189  const label facei = mesh_.edgeFaces()[meshEdgei][edgeFacei];
1190 
1191  const label patchi =
1192  mesh_.isInternalFace(facei)
1193  ? -1
1194  : pbMesh.patchIndices()[facei - mesh_.nInternalFaces()];
1195 
1196  if (patchi != -1 && patchIsOwnerOrig[patchi])
1197  {
1198  ownerOrigBoundaryEdgeNOrigFaces[ownerOrigBoundaryEdgei] ++;
1199  }
1200  }
1201  }
1202 
1203  // Synchronise the boundary edge original face count
1205  (
1206  mesh_,
1207  ownerOrigBoundaryEdgeMeshEdge,
1208  ownerOrigBoundaryEdgeNOrigFaces,
1209  plusEqOp<label>(),
1210  label(0)
1211  );
1212 
1213  // If debugging, check that face changes are in sync
1214  tmp<surfaceLabelField> tChanged =
1215  debug
1216  ? surfaceLabelField::New("changed", mesh_, dimensioned<label>(dimless, 0))
1217  : tmp<surfaceLabelField>(nullptr);
1218 
1219  // Modify faces connected to the boundary edges
1220  forAll(ownerOrigBoundaryEdgeMeshEdge, ownerOrigBoundaryEdgei)
1221  {
1222  const label meshEdgei =
1223  ownerOrigBoundaryEdgeMeshEdge[ownerOrigBoundaryEdgei];
1224 
1225  const part& ownerOrigBoundaryEdgeP =
1226  ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei];
1227 
1228  switch (ownerOrigBoundaryEdgeNOrigFaces[ownerOrigBoundaryEdgei])
1229  {
1230  case 0:
1231  {
1232  continue;
1233  }
1234 
1235  // If this edge is connected to just one original face then add any
1236  // edge area into that face
1237  case 1:
1238  {
1239  label origFacei = -1, origPatchi = -1, origPatchFacei = -1;
1240  forAll(mesh_.edgeFaces()[meshEdgei], edgeFacei)
1241  {
1242  const label facei = mesh_.edgeFaces()[meshEdgei][edgeFacei];
1243 
1244  const label patchi =
1245  mesh_.isInternalFace(facei)
1246  ? -1
1247  : pbMesh.patchIndices()[facei - mesh_.nInternalFaces()];
1248 
1249  if (patchi != -1 && patchIsOwnerOrig[patchi])
1250  {
1251  origFacei = facei;
1252  origPatchi = patchi;
1253  origPatchFacei = facei - pbMesh[patchi].start();
1254  break;
1255  }
1256  }
1257 
1258  if (origFacei != -1)
1259  {
1260  vector& area =
1261  SfSf.boundaryFieldRef()[origPatchi][origPatchFacei];
1262  point& centre =
1263  CfSf.boundaryFieldRef()[origPatchi][origPatchFacei];
1264 
1265  const label sign =
1266  mesh_.faces()[origFacei].edgeDirection
1267  (
1268  ownerOrigBoundaryMeshEdges[ownerOrigBoundaryEdgei]
1269  );
1270 
1271  part p(area, centre);
1272  p +=
1273  sign > 0
1274  ? ownerOrigBoundaryEdgeP
1275  : -ownerOrigBoundaryEdgeP;
1276 
1277  area = p.area;
1278  centre = p.centre;
1279 
1280  if (debug)
1281  {
1282  tChanged->boundaryFieldRef()
1283  [origPatchi][origPatchFacei] = label(1);
1284  }
1285  }
1286 
1287  break;
1288  }
1289 
1290  // If this edge is connected to two original faces then add any
1291  // edge area into non-original connected faces
1292  case 2:
1293  {
1294  forAll(mesh_.edgeFaces()[meshEdgei], edgeFacei)
1295  {
1296  const label facei = mesh_.edgeFaces()[meshEdgei][edgeFacei];
1297 
1298  const label patchi =
1299  mesh_.isInternalFace(facei)
1300  ? -1
1301  : pbMesh.patchIndices()[facei - mesh_.nInternalFaces()];
1302 
1303  if (patchi != -1 && patchIsOwnerOrig[patchi])
1304  {
1305  continue;
1306  }
1307 
1308  const label patchFacei =
1309  patchi == -1 ? -1 : facei - pbMesh[patchi].start();
1310 
1311  vector& area =
1312  patchi == -1
1313  ? SfSf[facei]
1314  : SfSf.boundaryFieldRef()[patchi][patchFacei];
1315  point& centre =
1316  patchi == -1
1317  ? CfSf[facei]
1318  : CfSf.boundaryFieldRef()[patchi][patchFacei];
1319 
1320  const label sign =
1321  mesh_.faces()[facei].edgeDirection
1322  (
1323  ownerOrigBoundaryMeshEdges[ownerOrigBoundaryEdgei]
1324  );
1325 
1326  part p(area, centre);
1327  p +=
1328  sign < 0
1329  ? ownerOrigBoundaryEdgeP
1330  : -ownerOrigBoundaryEdgeP;
1331 
1332  area = p.area;
1333  centre = p.centre;
1334 
1335  if (debug && patchi != -1)
1336  {
1337  tChanged->boundaryFieldRef()
1338  [patchi][patchFacei] = label(1);
1339  }
1340  }
1341 
1342  break;
1343  }
1344 
1345  default:
1346  {
1348  << "Boundary is not manifold"
1349  << exit(FatalError);
1350  }
1351  }
1352  }
1353 
1354  // If debugging, check that face changes are in sync
1355  if (debug)
1356  {
1357  const surfaceLabelField::Boundary& changedBf =
1358  tChanged->boundaryField();
1359 
1360  const surfaceLabelField::Boundary changedNbrBf
1361  (
1363  changedBf.boundaryNeighbourField()
1364  );
1365 
1366  bool synchronised = true;
1367 
1368  forAll(changedBf, patchi)
1369  {
1370  const fvsPatchLabelField& changedPf = changedBf[patchi];
1371  const fvsPatchLabelField& changedNbrPf = changedNbrBf[patchi];
1372 
1373  if (!isA<processorFvPatch>(changedPf.patch())) continue;
1374 
1375  forAll(changedPf, patchFacei)
1376  {
1377  if (changedPf[patchFacei] != changedNbrPf[patchFacei])
1378  {
1379  const label facei =
1380  changedPf.patch().start() + patchFacei;
1381 
1383  << "Face not synchronised with centre at "
1384  << mesh_.faceCentres()[facei] << endl;
1385 
1386  synchronised = false;
1387  }
1388  }
1389  }
1390 
1391  if (!synchronised)
1392  {
1394  << exit(FatalError);
1395  }
1396  }
1397 }
1398 
1399 
1400 void Foam::fvMeshStitcher::stabiliseOrigPatchFaces
1401 (
1404 ) const
1405 {
1406  const nonConformalBoundary& ncb = nonConformalBoundary::New(mesh_);
1407  const labelList allOrigPatchIndices = ncb.allOrigPatchIndices();
1408 
1409  forAll(allOrigPatchIndices, i)
1410  {
1411  const label origPatchi = allOrigPatchIndices[i];
1412  const polyPatch& origPp = mesh_.boundaryMesh()[origPatchi];
1413 
1414  forAll(origPp, origPatchFacei)
1415  {
1416  const vector& a = origPp.faceAreas()[origPatchFacei];
1417  const point& c = origPp.faceCentres()[origPatchFacei];
1418 
1419  vector& Sf = SfBf[origPatchi][origPatchFacei];
1420  point& Cf = CfBf[origPatchi][origPatchFacei];
1421 
1422  // Determine the direction in which to stabilise. If the fv-face
1423  // points in the same direction as the poly-face, then stabilise in
1424  // the direction of the poly-face. If it is reversed, then
1425  // construct a tangent to both faces, and stabilise in the average
1426  // direction to this tangent and the poly-face.
1427  vector dSfHat;
1428  if ((Sf & a) >= 0)
1429  {
1430  dSfHat = normalised(a);
1431  }
1432  else
1433  {
1434  dSfHat = (Sf & Sf)*a - (Sf & a)*Sf;
1435  if ((dSfHat & a) <= 0) dSfHat = perpendicular(a);
1436  dSfHat = normalised(normalised(dSfHat) + normalised(a));
1437  }
1438 
1439  part SAndCf(Sf, Cf);
1440 
1441  SAndCf += part(small*mag(a)*dSfHat, c);
1442 
1443  Sf = SAndCf.area;
1444  Cf = SAndCf.centre;
1445  }
1446  }
1447 }
1448 
1449 
1450 void Foam::fvMeshStitcher::intersect
1451 (
1452  surfaceLabelField::Boundary& polyFacesBf,
1453  surfaceVectorField& SfSf,
1454  surfaceVectorField& CfSf,
1455  const boolList& patchCoupleds,
1456  const bool matchTopology
1457 ) const
1458 {
1459  const polyBoundaryMesh& pbMesh = mesh_.boundaryMesh();
1460 
1461  const nonConformalBoundary& ncb = nonConformalBoundary::New(mesh_);
1462  const labelList ownerOrigPatchIndices = ncb.ownerOrigPatchIndices();
1463  const edgeList& ownerOrigBoundaryMeshEdges =
1464  ncb.ownerOrigBoundaryMeshEdges();
1465 
1466  // Alias the boundary geometry fields
1467  surfaceVectorField::Boundary& SfBf = SfSf.boundaryFieldRef();
1468  surfaceVectorField::Boundary& CfBf = CfSf.boundaryFieldRef();
1469 
1470  // Create storage for and initialise the edge parts of source patches
1471  List<List<part>> patchEdgeParts(mesh_.boundary().size());
1472  forAll(ownerOrigPatchIndices, i)
1473  {
1474  const label origPatchi = ownerOrigPatchIndices[i];
1475 
1476  patchEdgeParts[origPatchi].resize
1477  (
1478  mesh_.boundaryMesh()[origPatchi].nEdges(),
1479  part(Zero)
1480  );
1481  }
1482 
1483  // If topology is specified, also create a boundary field of the original
1484  // patch indices on the neighbouring interface, as well as the
1485  // corresponding areas and centres for stabilisation purposes
1486  tmp<surfaceLabelField::Boundary> tOrigFacesNbrBf;
1487  tmp<surfaceVectorField::Boundary> tOrigSfNbrBf;
1488  tmp<surfaceVectorField::Boundary> tOrigCfNbrBf;
1489  if (matchTopology)
1490  {
1491  getOrigNbrBfs
1492  (
1493  polyFacesBf,
1494  SfBf,
1495  CfBf,
1496  tOrigFacesNbrBf,
1497  tOrigSfNbrBf,
1498  tOrigCfNbrBf
1499  );
1500  }
1501 
1502  // Create intersection geometry for all the coupled non-conformal patches
1503  forAll(mesh_.boundary(), patchi)
1504  {
1505  if (!patchCoupleds[patchi]) continue;
1506 
1507  const fvPatch& fvp = mesh_.boundary()[patchi];
1508 
1509  if (isA<nonConformalCyclicFvPatch>(fvp))
1510  {
1511  const nonConformalCyclicFvPatch& nccFvp =
1512  refCast<const nonConformalCyclicFvPatch>(fvp);
1513 
1514  intersectNonConformalCyclic
1515  (
1516  nccFvp,
1517  polyFacesBf,
1518  SfBf,
1519  CfBf,
1520  tOrigFacesNbrBf,
1521  tOrigSfNbrBf,
1522  tOrigCfNbrBf,
1523  patchEdgeParts[nccFvp.origPatchIndex()]
1524  );
1525  }
1526  else if (isA<nonConformalProcessorCyclicFvPatch>(fvp))
1527  {}
1528  else if (isA<nonConformalMappedWallFvPatch>(fvp))
1529  {
1530  const nonConformalMappedWallFvPatch& ncmwFvp =
1531  refCast<const nonConformalMappedWallFvPatch>(fvp);
1532 
1533  intersectNonConformalMappedWall
1534  (
1535  ncmwFvp,
1536  polyFacesBf,
1537  SfBf,
1538  CfBf,
1539  tOrigFacesNbrBf,
1540  tOrigSfNbrBf,
1541  tOrigCfNbrBf,
1542  patchEdgeParts[ncmwFvp.origPatchIndex()]
1543  );
1544  }
1545  else
1546  {
1548  << "Coupled patch type not recognised"
1549  << exit(FatalError);
1550  }
1551  }
1552 
1553  // Create small stabilisation geometry for all the any non-coupled
1554  // non-conformal patches
1555  forAll(mesh_.boundary(), patchi)
1556  {
1557  if (patchCoupleds[patchi]) continue;
1558 
1559  const fvPatch& fvp = mesh_.boundary()[patchi];
1560 
1561  if (!isA<nonConformalFvPatch>(fvp)) continue;
1562 
1563  const polyPatch& origPp =
1564  refCast<const nonConformalFvPatch>(fvp).origPatch().patch();
1565 
1566  SfBf[patchi] ==
1567  vectorField
1568  (
1569  small*origPp.faceAreas(),
1570  polyFacesBf[patchi] - origPp.start()
1571  );
1572  CfBf[patchi] ==
1573  vectorField
1574  (
1575  origPp.faceCentres(),
1576  polyFacesBf[patchi] - origPp.start()
1577  );
1578  }
1579 
1580  // Construct the boundary edge geometry
1581  List<part> ownerOrigBoundaryEdgeParts =
1582  calculateOwnerOrigBoundaryEdgeParts(patchEdgeParts);
1583 
1584  // Add the difference between patch edge parts and all boundary
1585  // edge parts to the adjacent patch faces. This is an error part.
1586  forAll(ownerOrigPatchIndices, i)
1587  {
1588  const label origPatchi = ownerOrigPatchIndices[i];
1589  const polyPatch& origPatch = pbMesh[origPatchi];
1590 
1591  const labelList& origPatchEdgeOwnerOrigBoundaryEdges =
1592  ncb.patchEdgeOwnerOrigBoundaryEdges(origPatchi);
1593 
1594  forAll(patchEdgeParts[origPatchi], origPatchEdgei)
1595  {
1596  const label ownerOrigBoundaryEdgei =
1597  origPatchEdgeOwnerOrigBoundaryEdges[origPatchEdgei];
1598 
1599  const label sign =
1601  (
1602  meshEdge(origPatch, origPatchEdgei),
1603  ownerOrigBoundaryMeshEdges[ownerOrigBoundaryEdgei]
1604  );
1605 
1606  part errorP =
1607  patchEdgeParts[origPatchi][origPatchEdgei];
1608  errorP -=
1609  sign > 0
1610  ? ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei]
1611  : -ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei];
1612 
1613  forAll(origPatch.edgeFaces()[origPatchEdgei], patchEdgeFacei)
1614  {
1615  const label patchFacei =
1616  origPatch.edgeFaces()[origPatchEdgei][patchEdgeFacei];
1617 
1618  const label sign =
1619  origPatch.localFaces()[patchFacei].edgeDirection
1620  (
1621  origPatch.edges()[origPatchEdgei]
1622  );
1623 
1624  part p
1625  (
1626  SfBf[origPatchi][patchFacei],
1627  CfBf[origPatchi][patchFacei]
1628  );
1629  p += sign > 0 ? errorP : -errorP;
1630 
1631  SfBf[origPatchi][patchFacei] = p.area;
1632  CfBf[origPatchi][patchFacei] = p.centre;
1633  }
1634  }
1635  }
1636 
1637  // Use the boundary edge geometry to correct all edge-connected faces
1638  applyOwnerOrigBoundaryEdgeParts(SfSf, CfSf, ownerOrigBoundaryEdgeParts);
1639 
1640  // Stabilise the original patch faces
1641  stabiliseOrigPatchFaces(SfBf, CfBf);
1642 }
1643 
1644 
1645 bool Foam::fvMeshStitcher::disconnectThis
1646 (
1647  const bool changing,
1648  const bool geometric
1649 )
1650 {
1651  if (!stitches()) return false;
1652 
1653  // Determine which patches are coupled
1654  const boolList patchCoupleds =
1655  geometric
1656  ? this->patchCoupleds()
1657  : boolList(mesh_.boundary().size(), false);
1658 
1659  // Map the non-conformal patch field data to the conformal faces in advance
1660  // of the non-conformal patches being removed
1661  if (changing)
1662  {
1663  preConformSurfaceFields();
1664  preConformVolFields();
1665  }
1666 
1667  // Conform the mesh
1668  if (mesh_.moving())
1669  {
1670  surfaceScalarField phi(mesh_.phi());
1671  for (label i = 1; i < mesh_.phi().nOldTimes(false); ++ i)
1672  {
1673  phi.oldTimeRef(i) == mesh_.phi().oldTime(i);
1674  }
1675  conformCorrectMeshPhi(phi);
1676  mesh_.conform(phi);
1677  }
1678  else
1679  {
1680  mesh_.conform();
1681  }
1682 
1683  // Resize all the affected patch fields
1684  resizePatchFields<SurfaceField>();
1685  resizePatchFields<VolField>();
1686 
1687  // Create null polyTopoChangeMap
1688  const polyTopoChangeMap map(mesh_);
1689 
1690  meshObjects::topoChange<fvMesh>(mesh_, map);
1691  meshObjects::topoChange<lduMesh>(mesh_, map);
1692 
1693  const_cast<Time&>(mesh_.time()).functionObjects().topoChange(map);
1694 
1695  return true;
1696 }
1697 
1698 
1699 bool Foam::fvMeshStitcher::connectThis
1700 (
1701  const bool changing,
1702  const bool geometric,
1703  const bool load
1704 )
1705 {
1706  if (!stitches()) return false;
1707 
1708  // Create a copy of the conformal poly face addressing
1709  IOobject polyFacesBfIO(word::null, mesh_.pointsInstance(), mesh_);
1710  surfaceLabelField::Boundary polyFacesBf
1711  (
1713  mesh_.polyFacesBf()
1714  );
1715 
1716  // If starting up then load topology from disk, if it is available
1717  const bool matchTopology =
1718  load && loadPolyFacesBf(polyFacesBfIO, polyFacesBf);
1719 
1720  // Determine which patches are coupled
1721  const boolList patchCoupleds =
1722  (geometric || !matchTopology)
1723  ? this->patchCoupleds()
1724  : boolList(mesh_.boundary().size(), false);
1725 
1726  // Access all the intersections in advance. Makes the log nicer.
1727  forAll(mesh_.boundary(), patchi)
1728  {
1729  if (!patchCoupleds[patchi]) continue;
1730 
1731  const fvPatch& fvp = mesh_.boundary()[patchi];
1732 
1733  if (isA<nonConformalCyclicFvPatch>(fvp))
1734  {
1735  const nonConformalCyclicFvPatch& nccFvp =
1736  refCast<const nonConformalCyclicFvPatch>(fvp);
1737 
1738  if (nccFvp.owner())
1739  {
1740  nccFvp.nonConformalCyclicPatch().intersection();
1741  }
1742  }
1743  else if (isA<nonConformalProcessorCyclicFvPatch>(fvp))
1744  {}
1745  else if (isA<nonConformalMappedWallFvPatch>(fvp))
1746  {
1747  const nonConformalMappedWallFvPatch& ncmwFvp =
1748  refCast<const nonConformalMappedWallFvPatch>(fvp);
1749 
1750  const nonConformalMappedWallFvPatch& ownerNcmwFvp =
1751  ncmwFvp.owner() ? ncmwFvp : ncmwFvp.nbrPatch();
1752 
1753  ownerNcmwFvp.nonConformalMappedWallPatch().intersection();
1754  }
1755  else
1756  {
1758  << "Coupled patch type not recognised"
1759  << exit(FatalError);
1760  }
1761  }
1762 
1763  if (any(patchCoupleds))
1764  {
1765  Info<< indent << typeName << ": Connecting" << incrIndent << endl;
1766  }
1767 
1768  // Create copies of geometry fields to be modified
1769  surfaceVectorField Sf(mesh_.Sf().cloneUnSliced()());
1770  surfaceVectorField Cf(mesh_.Cf().cloneUnSliced()());
1771 
1772  // Construct non-conformal geometry
1773  intersect(polyFacesBf, Sf, Cf, patchCoupleds, matchTopology);
1774 
1775  // If the mesh is moving then create any additional non-conformal geometry
1776  // necessary to correct the mesh fluxes
1777  if (mesh_.moving())
1778  {
1779  createNonConformalCorrectMeshPhiGeometry(polyFacesBf, Sf, Cf);
1780  }
1781 
1782  // Make the mesh non-conformal
1783  if (mesh_.moving())
1784  {
1785  surfaceScalarField phi(mesh_.phi());
1786  for (label i = 1; i <= mesh_.phi().nOldTimes(false); ++ i)
1787  {
1788  phi.oldTimeRef(i) == mesh_.phi().oldTime(i);
1789  }
1790  unconformCorrectMeshPhi(polyFacesBf, Sf, Cf, phi);
1791  mesh_.unconform(polyFacesBf, Sf, Cf, phi);
1792  }
1793  else
1794  {
1795  mesh_.unconform(polyFacesBf, Sf, Cf);
1796  }
1797 
1798  // Reset the poly faces instance to that of any loaded topology
1799  if (load)
1800  {
1801  mesh_.setPolyFacesBfInstance(polyFacesBfIO.instance());
1802  }
1803 
1804  // Resize all the affected patch fields
1805  resizePatchFields<SurfaceField>();
1806  resizePatchFields<VolField>();
1807 
1808  // Map the non-conformal patch field data back from the conformal faces and
1809  // into the new non-conformal patches
1810  if (changing)
1811  {
1812  postUnconformSurfaceFields();
1813  postUnconformVolFields();
1814  }
1815 
1816  // Prevent hangs caused by processor cyclic patches using mesh geometry
1817  mesh_.deltaCoeffs();
1818 
1819  if (any(patchCoupleds))
1820  {
1821  const volScalarField::Internal o(openness());
1822  const scalar gMaxO = gMax(o);
1823  Info<< indent << "Cell min/average/max openness = "
1824  << gMin(o) << '/' << gAverage(o) << '/' << gMaxO << endl;
1825  if (gMaxO > rootSmall)
1826  {
1828  << "Maximum openness of " << gMaxO
1829  << " is not tolerable" << exit(FatalError);
1830  }
1831 
1832  if (mesh_.moving())
1833  {
1834  for (label i = 0; i <= mesh_.phi().nOldTimes(false); ++ i)
1835  {
1836  const volScalarField::Internal vce(volumeConservationError(i));
1837  const scalar gMaxVce = gMax(vce);
1838  Info<< indent << "Cell min/average/max ";
1839  for (label j = 0; j < i; ++ j) Info<< "old-";
1840  Info<< (i ? "time " : "") << "volume conservation error = "
1841  << gMin(vce) << '/' << gAverage(vce) << '/' << gMaxVce
1842  << endl;
1843  if (gMaxVce > rootSmall)
1844  {
1846  << "Maximum volume conservation error of " << gMaxVce
1847  << " is not tolerable" << exit(FatalError);
1848  }
1849  }
1850  }
1851 
1852  if (mesh_.moving())
1853  {
1854  // Create a boundary field of the imbalance between the mesh fluxes
1855  // on either side of interfaces
1857  (
1859  mesh_.phi().boundaryField()
1860  );
1861  mfe += mesh_.phi().boundaryField().boundaryNeighbourField();
1862 
1863  // Determine the number of non-processor patches
1864  label nNonProcPatches = 0;
1865  forAll(mesh_.boundary(), patchi)
1866  {
1867  const fvPatch& fvp = mesh_.boundary()[patchi];
1868 
1869  if (!isA<processorFvPatch>(fvp))
1870  {
1871  if (nNonProcPatches != patchi)
1872  {
1874  << "Processor patches do not follow non-processor "
1875  << "patches" << exit(FatalError);
1876  }
1877 
1878  nNonProcPatches ++;
1879  }
1880  }
1881 
1882  // Work out the min, avg and max error for all non-conformal
1883  // cyclics, including any errors on referred processor cyclics
1884  scalarList minMfe(nNonProcPatches, vGreat);
1885  scalarField sumMfe(nNonProcPatches, 0), nSumMfe(nNonProcPatches, 0);
1886  scalarList maxMfe(nNonProcPatches, -vGreat);
1887  forAll(mfe, patchi)
1888  {
1889  const fvPatch& fvp = mesh_.boundary()[patchi];
1890 
1891  const label nccPatchi =
1892  isA<nonConformalCyclicFvPatch>(fvp)
1893  ? refCast<const nonConformalCyclicFvPatch>(fvp)
1894  .index()
1895  : isA<nonConformalProcessorCyclicFvPatch>(fvp)
1896  ? refCast<const nonConformalProcessorCyclicFvPatch>(fvp)
1897  .referPatchIndex()
1898  : -1;
1899 
1900  if (nccPatchi != -1)
1901  {
1902  minMfe[nccPatchi] =
1903  min(minMfe[nccPatchi], min(mfe[patchi]));
1904  sumMfe[nccPatchi] += sum(mfe[patchi]);
1905  nSumMfe[nccPatchi] += mfe[patchi].size();
1906  maxMfe[nccPatchi] =
1907  max(maxMfe[nccPatchi], max(mfe[patchi]));
1908  }
1909  }
1910  reduce(minMfe, ListOp<minOp<scalar>>());
1911  reduce(sumMfe, ListOp<sumOp<scalar>>());
1912  reduce(nSumMfe, ListOp<sumOp<scalar>>());
1913  reduce(maxMfe, ListOp<maxOp<scalar>>());
1914 
1915  // Report
1916  forAll(minMfe, patchi)
1917  {
1918  const fvPatch& fvp = mesh_.boundary()[patchi];
1919 
1920  if
1921  (
1922  isA<nonConformalCyclicFvPatch>(fvp)
1923  && refCast<const nonConformalCyclicFvPatch>(fvp).owner()
1924  && nSumMfe[patchi] > 0
1925  )
1926  {
1927  Info<< indent << fvp.name()
1928  << " min/average/max mesh flux error = "
1929  << minMfe[patchi] << '/'
1930  << sumMfe[patchi]/(nSumMfe[patchi] + vSmall) << '/'
1931  << maxMfe[patchi] << endl;
1932  }
1933  }
1934  }
1935 
1936  const volScalarField::Internal pvf(projectedVolumeFraction());
1937  const scalar gMaxPvf = gMax(pvf);
1938  Info<< indent << "Cell min/average/max projected volume fraction = "
1939  << gMin(pvf) << '/' << gAverage(pvf) << '/' << gMaxPvf << endl;
1940  if (gMaxPvf > minWarnProjectedVolumeFraction_)
1941  {
1943  << "Maximum projected volume fraction " << gMaxPvf << " may "
1944  << "cause instability." << nl << indent << "Volumetric "
1945  << "distortion can be minimised by making the side of the "
1946  << "interface" << nl << indent << "with smaller or more "
1947  << "finely layered cells the neighbour." << nl << indent
1948  << "This is done by specifying this side second to "
1949  << "createNonConformalCouples;" << nl << indent << "either on "
1950  << "the command line, or in the patches (and regions) entries "
1951  << "within" << nl << indent << "the "
1952  << "createNonConformalCouplesDict." << endl;
1953  }
1954  }
1955 
1956  if (any(patchCoupleds))
1957  {
1958  Info<< decrIndent;
1959  }
1960 
1961  // Create null polyTopoChangeMap
1962  const polyTopoChangeMap map(mesh_);
1963 
1964  meshObjects::topoChange<fvMesh>(mesh_, map);
1965  meshObjects::topoChange<lduMesh>(mesh_, map);
1966 
1967  const_cast<Time&>(mesh_.time()).functionObjects().topoChange(map);
1968 
1969  return true;
1970 }
1971 
1972 
1973 void Foam::fvMeshStitcher::preConformSurfaceFields()
1974 {
1975  #define PreConformSurfaceFields(Type, nullArg) \
1976  preConformSurfaceFields<Type>();
1978  #undef PreConformSurfaceFields
1979 }
1980 
1981 
1982 void Foam::fvMeshStitcher::preConformVolFields()
1983 {
1984  #define PreConformVolFields(Type, nullArg) \
1985  preConformVolFields<Type>();
1987  #undef PreConformVolFields
1988 }
1989 
1990 
1991 template<>
1992 void Foam::fvMeshStitcher::postUnconformSurfaceFields<Foam::vector>()
1993 {
1994  if (mesh_.topoChanged())
1995  {
1996  UPtrList<surfaceVectorField> Ufs(mesh_.curFields<surfaceVectorField>());
1997 
1998  forAll(Ufs, i)
1999  {
2000  surfaceVectorField& Uf = Ufs[i];
2001 
2002  const volVectorField& U = surfaceToVolVelocity(Uf);
2003 
2004  if (isNull(U)) Uf.clearOldTimes();
2005  }
2006  }
2007 
2008  UPtrList<surfaceVectorField> fields(mesh_.fields<surfaceVectorField>());
2009 
2010  forAll(fields, i)
2011  {
2013  (
2014  fields[i].boundaryFieldRefNoStoreOldTimes()
2015  );
2016  }
2017 }
2018 
2019 
2020 void Foam::fvMeshStitcher::postUnconformSurfaceFields()
2021 {
2022  #define PostUnconformSurfaceFields(Type, nullArg) \
2023  postUnconformSurfaceFields<Type>();
2025  #undef PostUnconformSurfaceFields
2026 }
2027 
2028 
2029 void Foam::fvMeshStitcher::postUnconformVolFields()
2030 {
2031  #define PostUnconformVolFields(Type, nullArg) \
2032  postUnconformVolFields<Type>();
2034  #undef PostUnconformVolFields
2035 
2036  const nonConformalBoundary& ncb = nonConformalBoundary::New(mesh());
2037  const labelList origPatchIndices = ncb.allOrigPatchIndices();
2038  const labelList errorPatchIndices = ncb.allErrorPatchIndices();
2039 
2040  // Special handling for velocities. Wherever we find a movingWall-type
2041  // boundary condition on an original patch, override the corresponding
2042  // error patch condition to movingWallSlipVelocity.
2043  UPtrList<volVectorField> Us(mesh().fields<volVectorField>());
2044  forAll(Us, i)
2045  {
2046  volVectorField& U = Us[i];
2047 
2048  forAll(errorPatchIndices, i)
2049  {
2050  const label errorPatchi = errorPatchIndices[i];
2051  const label origPatchi = origPatchIndices[i];
2052 
2053  typename volVectorField::Patch& origUp =
2054  U.boundaryFieldRefNoStoreOldTimes()[origPatchi];
2055 
2056  if
2057  (
2058  isA<movingWallVelocityFvPatchVectorField>(origUp)
2059  || isA<movingWallSlipVelocityFvPatchVectorField>(origUp)
2060  )
2061  {
2062  U.boundaryFieldRefNoStoreOldTimes().set
2063  (
2064  errorPatchi,
2065  new movingWallSlipVelocityFvPatchVectorField
2066  (
2067  mesh().boundary()[errorPatchi],
2068  U
2069  )
2070  );
2071  }
2072  }
2073  }
2074 
2075  #define PostUnconformEvaluateVolFields(Type, nullArg) \
2076  postUnconformEvaluateVolFields<Type>();
2078  #undef PostUnconformEvaluateVolFields
2079 
2080  // Special handling for velocities. Recompute the surface velocity using an
2081  // interpolation of the volume velocity.
2082  UPtrList<surfaceVectorField> Ufs(mesh_.fields<surfaceVectorField>());
2083  forAll(Ufs, i)
2084  {
2085  surfaceVectorField& Uf = Ufs[i];
2086 
2087  const volVectorField& U = surfaceToVolVelocity(Uf);
2088 
2089  if (isNull(U)) continue;
2090 
2091  const surfaceVectorField UfInterpolated(fvc::interpolate(U));
2092 
2093  forAll(Uf.boundaryField(), patchi)
2094  {
2095  if (isA<nonConformalFvPatch>(mesh_.boundary()[patchi]))
2096  {
2097  Uf.boundaryFieldRefNoStoreOldTimes()[patchi] ==
2098  UfInterpolated.boundaryField()[patchi];
2099  }
2100  }
2101  }
2102 }
2103 
2104 
2105 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
2106 
2108 {
2109  boolList result(mesh_.boundary().size(), false);
2110 
2111  forAll(mesh_.boundary(), patchi)
2112  {
2113  const fvPatch& fvp = mesh_.boundary()[patchi];
2114 
2115  // Initially assume all cyclics can be coupled
2116  if (isA<nonConformalCyclicFvPatch>(fvp))
2117  {
2118  result[patchi] = true;
2119  }
2120 
2121  // If, however, a processorCyclic is found, and this is not a parallel
2122  // run, then the associated cyclic cannot be coupled. This works in a
2123  // single loop because the processorCyclic patches are after the
2124  // (global) cyclic patches.
2125  if (isA<nonConformalProcessorCyclicFvPatch>(fvp))
2126  {
2127  const nonConformalProcessorCyclicFvPatch& ncpcFvp =
2128  refCast<const nonConformalProcessorCyclicFvPatch>(fvp);
2129 
2130  result[patchi] = Pstream::parRun();
2131  result[ncpcFvp.referPatchIndex()] = Pstream::parRun();
2132  }
2133 
2134  // A mapped wall can be coupled if the neighbour mesh is available and
2135  // it is a parallel run, or the pair of walls is confined to a single
2136  // process
2137  if (isA<nonConformalMappedWallFvPatch>(fvp))
2138  {
2139  const nonConformalMappedWallFvPatch& ncmwFvp =
2140  refCast<const nonConformalMappedWallFvPatch>(fvp);
2141 
2142  result[patchi] =
2143  ncmwFvp.haveNbr()
2144  && (
2145  Pstream::parRun()
2147  (
2148  ncmwFvp.patch().size(),
2149  ncmwFvp.nbrPatch().patch().size()
2150  ) != -1
2151  );
2152  }
2153  }
2154 
2155  return result;
2156 }
2157 
2158 
2160 {
2161  bool result = false;
2162 
2163  forAll(mesh_.boundary(), patchi)
2164  {
2165  const fvPatch& fvp = mesh_.boundary()[patchi];
2166 
2167  if (!isA<nonConformalFvPatch>(fvp)) continue;
2168 
2169  const fvsPatchScalarField& magSfp =
2170  mesh_.magSf().boundaryField()[patchi];
2171 
2172  const polyPatch& origPp =
2173  refCast<const nonConformalFvPatch>(fvp).origPatch().patch();
2174 
2175  const scalarField origMagSfp
2176  (
2177  origPp.magFaceAreas(),
2178  mesh_.polyFacesBf()[patchi] - origPp.start()
2179  );
2180 
2181  if (max(magSfp/origMagSfp) > rootSmall)
2182  {
2183  result = true;
2184  }
2185  }
2186 
2187  reduce(result, orOp<bool>());
2188 
2189  return returnReduce(result, orOp<bool>());
2190 }
2191 
2192 
2195 {
2196  return
2197  mag(fvc::surfaceIntegrate(mesh_.Sf()))*mesh_.V()
2198  /max
2199  (
2200  mag(fvc::surfaceSum(cmptMag(mesh_.Sf())))(),
2201  (small*sqr(cbrt(mesh_.V())))()
2202  )();
2203 }
2204 
2205 
2208 {
2209  if (0 > n || n > 1)
2210  {
2212  << "Can only compute volume conservation error for this time, or "
2213  << "the previous time" << exit(FatalError);
2214  }
2215 
2216  const surfaceScalarField& phi = mesh_.phi().oldTime(n);
2217 
2218  const dimensionedScalar deltaT =
2219  n == 0 ? mesh_.time().deltaT() : mesh_.time().deltaT0();
2220 
2221  const volScalarField::Internal& V = n == 0 ? mesh_.V() : mesh_.V0();
2222  const volScalarField::Internal& V0 = n == 0 ? mesh_.V0() : mesh_.V00();
2223 
2224  return fvc::surfaceIntegrate(phi*deltaT)() - (V - V0)/mesh_.V();
2225 }
2226 
2227 
2230 {
2231  return mag
2232  (
2233  fvc::surfaceIntegrate(mesh_.Sf() & mesh_.Cf())
2234  /mesh_.nSolutionD()
2235  - 1
2236  );
2237 }
2238 
2239 
2240 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
2241 
2243 :
2244  mesh_(mesh),
2245  ready_(false),
2246  regionPolyFacesBfIOs_(),
2247  regionPolyFacesBfs_()
2248 {}
2249 
2250 
2251 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
2252 
2254 {}
2255 
2256 
2257 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
2258 
2260 {
2261  // Meshes in a local sub-directory are not stitched
2262  if (!mesh_.local().empty())
2263  {
2264  return false;
2265  }
2266 
2267  // Other meshes with non-conformal patches are stitched
2268  forAll(mesh_.boundary(), patchi)
2269  {
2270  if (isA<nonConformalFvPatch>(mesh_.boundary()[patchi]))
2271  {
2272  return true;
2273  }
2274  }
2275 
2276  return false;
2277 }
2278 
2279 
2281 (
2282  const bool changing,
2283  const bool geometric
2284 )
2285 {
2286  // Don't do anything if we are already disconnected
2287  if (mesh_.conformal()) return false;
2288 
2289  if (!changing)
2290  {
2291  return disconnectThis(changing, geometric);
2292  }
2293 
2294  // Get all the connected region meshes
2295  MultiRegionUList<fvMesh> regionMeshes(this->regionMeshes());
2296 
2297  // Disconnect them all
2298  bool result = false;
2299  forAll(regionMeshes, i)
2300  {
2301  if (regionMeshes[i]().stitcher().disconnectThis(changing, geometric))
2302  {
2303  result = true;
2304  }
2305  }
2306 
2307  return result;
2308 }
2309 
2310 
2312 (
2313  const bool changing,
2314  const bool geometric,
2315  const bool load
2316 )
2317 {
2318  if (!changing)
2319  {
2320  return connectThis(changing, geometric, load);
2321  }
2322 
2323  // Connection requires all regions to be available and at a consistent
2324  // state. So, we need to wait until they are all ready. So, if some are not
2325  // ready, then this function just flips the ready flag for this region. If
2326  // they are all ready, then this function stitches all of them.
2327 
2328  ready_ = true;
2329 
2330  // Get all the connected region meshes
2331  MultiRegionUList<fvMesh> regionMeshes(this->regionMeshes());
2332 
2333  // If any mesh is not ready, then don't do anything
2334  forAll(regionMeshes, i)
2335  {
2336  if (!regionMeshes[i]().stitcher().ready_)
2337  {
2338  return false;
2339  }
2340  }
2341 
2342  // All meshes are ready, so connect all of them up
2343  bool result = false;
2344  forAll(regionMeshes, i)
2345  {
2346  if (regionMeshes[i]().stitcher().connectThis(changing, geometric, load))
2347  {
2348  result = true;
2349  }
2350  }
2351 
2352  // Set all meshes as not ready, in preparation for the next iteration
2353  forAll(regionMeshes, i)
2354  {
2355  regionMeshes[i]().stitcher().ready_ = false;
2356  }
2357 
2358  return result;
2359 }
2360 
2361 
2362 void Foam::fvMeshStitcher::reconnect(const bool geometric) const
2363 {
2364  if (mesh_.conformal() || geometric == this->geometric())
2365  {
2366  return;
2367  }
2368 
2369  // Create a copy of the non-conformal poly face addressing
2370  surfaceLabelField::Boundary polyFacesBf
2371  (
2373  mesh_.polyFacesBf()
2374  );
2375 
2376  // Undo all non-conformal changes and clear all geometry and topology
2377  mesh_.conform();
2378 
2379  // Determine which patches are coupled
2380  const boolList patchCoupleds =
2381  geometric
2382  ? this->patchCoupleds()
2383  : boolList(mesh_.boundary().size(), false);
2384 
2385  // Create copies of geometry fields to be modified
2386  surfaceVectorField Sf(mesh_.Sf().cloneUnSliced()());
2387  surfaceVectorField Cf(mesh_.Cf().cloneUnSliced()());
2388 
2389  // Construct non-conformal geometry
2390  intersect(polyFacesBf, Sf, Cf, patchCoupleds, true);
2391 
2392  // Apply changes to the mesh
2393  mesh_.unconform(polyFacesBf, Sf, Cf);
2394 
2395  // Prevent hangs caused by processor cyclic patches using mesh geometry
2396  mesh_.deltaCoeffs();
2397 
2398  // Create null polyTopoChangeMap
2399  const polyTopoChangeMap map(mesh_);
2400 
2401  meshObjects::topoChange<fvMesh>(mesh_, map);
2402  meshObjects::topoChange<lduMesh>(mesh_, map);
2403 
2404  const_cast<Time&>(mesh_.time()).functionObjects().topoChange(map);
2405 }
2406 
2407 
2409 {}
2410 
2411 
2413 {}
2414 
2415 
2417 {}
2418 
2419 
2420 // ************************************************************************* //
label n
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:433
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, PrimitiveField > & null()
Return a null DimensionedField.
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:647
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.
fvMeshStitcher(fvMesh &)
Construct from fvMesh.
tmp< DimensionedField< scalar, volMesh > > projectedVolumeFraction() const
Return the projected volume fraction for debugging/checking.
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.
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:96
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:420
const fvBoundaryMesh & boundary() const
Return reference to boundary mesh.
Definition: fvMesh.C:962
const word & name() const
Return reference to name.
Definition: fvMesh.H:434
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:86
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 ...
const fileName & dbDir() const
Local directory path of this objectRegistry relative to the time.
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
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.
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.
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:63
Ostream & decrIndent(Ostream &os)
Decrement the indent level.
Definition: Ostream.H:242
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
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:258
const dimensionSet dimless
const word & regionName(const solver &region)
Definition: solver.H:218
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:235
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
void mag(LagrangianPatchField< scalar > &f, const LagrangianPatchField< Type > &f1)
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:507
defineTypeNameAndDebug(combustionModel, 0)
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
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.
void cbrt(LagrangianPatchField< scalar > &f, const LagrangianPatchField< scalar > &f1)
void sqr(LagrangianPatchField< typename outerProduct< Type, Type >::type > &f, const LagrangianPatchField< Type > &f1)
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.
void magSqr(LagrangianPatchField< scalar > &f, const LagrangianPatchField< Type > &f1)
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:513
Ostream & indent(Ostream &os)
Indent stream.
Definition: Ostream.H:228
fvsPatchField< vector > fvsPatchVectorField
static const char nl
Definition: Ostream.H:267
Type gMax(const FieldField< Field, Type > &f)
void cmptMag(LagrangianPatchField< Type > &f, const LagrangianPatchField< Type > &f1)
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.