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