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-2022 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 "MeshObject.H"
30 #include "syncTools.H"
31 #include "surfaceToVolVelocity.H"
32 
33 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
34 
35 namespace Foam
36 {
37 
38 template<class FaceList, class PointField>
39 edge meshEdge
40 (
42  const label edgei
43 )
44 {
45  return edge
46  (
47  p.meshPoints()[p.edges()[edgei][0]],
48  p.meshPoints()[p.edges()[edgei][1]]
49  );
50 }
51 
52 }
53 
54 
55 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
56 
57 namespace Foam
58 {
61 }
62 
63 
64 const Foam::word Foam::fvMeshStitcher::nccFieldPrefix_ =
65  fvMeshStitcher::typeName + ":";
66 
67 
68 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
69 
70 void Foam::fvMeshStitcher::intersectNonConformalCyclic
71 (
72  const nonConformalCyclicFvPatch& nccFvp,
73  surfaceLabelField::Boundary& polyFacesBf,
76  const tmp<surfaceLabelField::Boundary>& tOrigFacesNbrBf,
77  const tmp<surfaceVectorField::Boundary>& tOrigSfNbrBf,
78  const tmp<surfaceVectorField::Boundary>& tOrigCfNbrBf,
79  List<part>& origEdgeParts
80 ) const
81 {
82  const nonConformalCyclicFvPatch& nbrNccFvp = nccFvp.nbrPatch();
83 
84  // Alias the original poly patches
85  const polyPatch& origPp = nccFvp.origPatch().patch();
86  const polyPatch& nbrOrigPp = nbrNccFvp.origPatch().patch();
87 
88  // Get the indices of the non-conformal patches
89  labelList patchis(Pstream::nProcs(), -1);
90  labelList nbrPatchis(Pstream::nProcs(), -1);
91  patchis[Pstream::myProcNo()] = nccFvp.index();
92  nbrPatchis[Pstream::myProcNo()] = nbrNccFvp.index();
93  forAll(mesh_.boundary(), patchj)
94  {
95  const fvPatch& fvp = mesh_.boundary()[patchj];
96 
97  if (isA<nonConformalProcessorCyclicFvPatch>(fvp))
98  {
99  const nonConformalProcessorCyclicFvPatch& ncpcFvp =
100  refCast<const nonConformalProcessorCyclicFvPatch>(fvp);
101 
102  if (ncpcFvp.referPatchID() == nccFvp.index())
103  {
104  patchis[ncpcFvp.neighbProcNo()] = patchj;
105  }
106  if (ncpcFvp.referPatchID() == nbrNccFvp.index())
107  {
108  nbrPatchis[ncpcFvp.neighbProcNo()] = patchj;
109  }
110  }
111  }
112 
113  // Get the intersection geometry
116 
117  // Unpack the patchToPatch addressing into lists of indices (fixed lists of
118  // 3 labels; owner face, neighbour face, couple index). These will be used
119  // to create the non-conformal faces, so sort them to make sure the
120  // non-conformal interfaces are ordered.
121  auto procFacesToIndices = []
122  (
123  const List<List<patchToPatch::procFace>>& faceOtherProcFaces,
124  const bool owner
125  )
126  {
127  // Compute interface sizes
128  labelList is(Pstream::nProcs(), 0);
129  forAll(faceOtherProcFaces, facei)
130  {
131  forAll(faceOtherProcFaces[facei], i)
132  {
133  const patchToPatch::procFace& otherProcFacei =
134  faceOtherProcFaces[facei][i];
135 
136  is[otherProcFacei.proci] ++;
137  }
138  }
139 
140  // Allocate the indices
142  forAll(indices, proci)
143  {
144  indices[proci].resize(is[proci]);
145  }
146 
147  // Set the indices
148  is = 0;
149  forAll(faceOtherProcFaces, facei)
150  {
151  forAll(faceOtherProcFaces[facei], i)
152  {
153  const patchToPatch::procFace& otherProcFacei =
154  faceOtherProcFaces[facei][i];
155 
156  FixedList<label, 3>& index =
157  indices[otherProcFacei.proci][is[otherProcFacei.proci] ++];
158 
159  index = {facei, otherProcFacei.facei, i};
160 
161  if (!owner) Swap(index[0], index[1]);
162  }
163  }
164 
165  // Sort to ensure ordering
166  forAll(indices, proci)
167  {
168  sort(indices[proci]);
169  }
170 
171  return indices;
172  };
173 
175  procFacesToIndices(intersection.srcTgtProcFaces(), true);
176 
177  List<List<FixedList<label, 3>>> nbrIndices =
178  procFacesToIndices(intersection.tgtSrcProcFaces(), false);
179 
180  // If addressing has been provided, then modify the indices to match. When
181  // a coupling has to be added, the couple index is set to -1. This
182  // indicates that there is no geometry in the patchToPatch engine for this
183  // coupling, and for a small stabilisation value to be used instead.
184  auto matchIndices = [&polyFacesBf, &tOrigFacesNbrBf]
185  (
187  const nonConformalCyclicFvPatch& nccFvp,
188  const polyPatch& origPp,
189  const labelList& patchis,
190  const bool owner
191  )
192  {
193  const surfaceLabelField::Boundary& origFacesNbrBf = tOrigFacesNbrBf();
194 
195  // Create what the indices should be
196  List<List<FixedList<label, 3>>> indicesRef(indices.size());
197  forAll(indices, proci)
198  {
199  const label patchi = patchis[proci];
200 
201  if (patchi != -1)
202  {
203  indicesRef[proci].resize(polyFacesBf[patchi].size());
204 
205  forAll(polyFacesBf[patchi], patchFacei)
206  {
207  FixedList<label, 3>& indexRef =
208  indicesRef[proci][patchFacei];
209 
210  indexRef =
211  {
212  polyFacesBf[patchi][patchFacei] - origPp.start(),
213  origFacesNbrBf[patchi][patchFacei],
214  -1
215  };
216 
217  if (!owner) Swap(indexRef[0], indexRef[1]);
218  }
219  }
220  }
221 
222  // Populate the indices with the index of the coupling
223  label nCouplesRemoved = 0, nCouplesAdded = 0;
224  forAll(indices, proci)
225  {
226  const label patchi = patchis[proci];
227 
228  if (patchi != -1)
229  {
230  label patchFacei = 0, i = 0;
231 
232  while
233  (
234  patchFacei < polyFacesBf[patchi].size()
235  && i < indices[proci].size()
236  )
237  {
238  const FixedList<label, 3> index
239  ({
240  indices[proci][i][0],
241  indices[proci][i][1],
242  -1
243  });
244 
245  FixedList<label, 3>& indexRef =
246  indicesRef[proci][patchFacei];
247 
248  if (index < indexRef)
249  {
250  nCouplesRemoved ++;
251  i ++;
252  }
253  else if (index == indexRef)
254  {
255  indexRef[2] = indices[proci][i][2];
256  patchFacei ++;
257  i ++;
258  }
259  else // (index > indexRef)
260  {
261  nCouplesAdded ++;
262  patchFacei ++;
263  }
264  }
265 
266  nCouplesRemoved +=
267  min(indices[proci].size() - i, 0);
268  nCouplesAdded +=
269  min(polyFacesBf[patchi].size() - patchFacei, 0);
270  }
271  }
272 
273  // Report if changes have been made
274  reduce(nCouplesRemoved, sumOp<label>());
275  reduce(nCouplesAdded, sumOp<label>());
276  if ((nCouplesRemoved || nCouplesAdded) && nccFvp.owner())
277  {
278  Info<< indent << nCouplesRemoved << '/' << nCouplesAdded
279  << " small couplings removed/added to " << nccFvp.name()
280  << endl;
281  }
282 
283  // Set the indices to the correct values
284  Swap(indices, indicesRef);
285  };
286 
287  if (tOrigFacesNbrBf.valid())
288  {
289  matchIndices(indices, nccFvp, origPp, patchis, true);
290  matchIndices(nbrIndices, nbrNccFvp, nbrOrigPp, nbrPatchis, false);
291  }
292 
293  // Create couplings by transferring geometry from the original to the
294  // non-conformal patches
295  auto createCouplings =
296  [
297  &polyFacesBf,
298  &tOrigSfNbrBf,
299  &tOrigCfNbrBf,
300  &SfBf,
301  &CfBf
302  ]
303  (
304  const List<List<FixedList<label, 3>>>& indices,
305  const List<DynamicList<couple>>& couples,
306  const nonConformalCyclicFvPatch& nccFvp,
307  const polyPatch& origPp,
308  const labelList& patchis,
309  const bool owner
310  )
311  {
312  forAll(patchis, proci)
313  {
314  const label patchi = patchis[proci];
315  const label patchSize = indices[proci].size();
316 
317  if (patchi == -1 && patchSize)
318  {
320  << "Intersection generated coupling through "
321  << nccFvp.name() << " between processes "
322  << Pstream::myProcNo() << " and " << proci
323  << " but a corresponding processor interface was not found"
324  << exit(FatalError);
325  }
326 
327  if (patchi != -1)
328  {
329  polyFacesBf[patchi].resize(patchSize);
330  SfBf[patchi].resize(patchSize);
331  CfBf[patchi].resize(patchSize);
332 
333  forAll(indices[proci], patchFacei)
334  {
335  const label origFacei = indices[proci][patchFacei][!owner];
336  const label i = indices[proci][patchFacei][2];
337 
338  polyFacesBf[patchi][patchFacei] =
339  origFacei + origPp.start();
340 
341  couple c;
342  if (i != -1)
343  {
344  c = couples[origFacei][i];
345  }
346  else
347  {
348  c =
349  couple
350  (
351  part
352  (
353  small*origPp.faceAreas()[origFacei],
354  origPp.faceCentres()[origFacei]
355  ),
356  part
357  (
358  small*tOrigSfNbrBf()[patchi][patchFacei],
359  tOrigCfNbrBf()[patchi][patchFacei]
360  )
361  );
362  }
363  if (!owner)
364  {
365  c.nbr = c;
366  }
367 
368  SfBf[patchi][patchFacei] = c.nbr.area;
369  CfBf[patchi][patchFacei] = c.nbr.centre;
370 
371  part origP
372  (
373  SfBf[origPp.index()][origFacei],
374  CfBf[origPp.index()][origFacei]
375  );
376  origP -= c;
377 
378  SfBf[origPp.index()][origFacei] = origP.area;
379  CfBf[origPp.index()][origFacei] = origP.centre;
380  }
381  }
382  }
383  };
384 
385  createCouplings
386  (
387  indices,
388  intersection.srcCouples(),
389  nccFvp,
390  origPp,
391  patchis,
392  true
393  );
394 
395  createCouplings
396  (
397  nbrIndices,
398  intersection.tgtCouples(),
399  nbrNccFvp,
400  nbrOrigPp,
401  nbrPatchis,
402  false
403  );
404 
405  // Add error geometry to the original patches
406  forAll(intersection.srcErrorParts(), origFacei)
407  {
408  part origP
409  (
410  SfBf[origPp.index()][origFacei],
411  CfBf[origPp.index()][origFacei]
412  );
413  origP += intersection.srcErrorParts()[origFacei];
414 
415  SfBf[origPp.index()][origFacei] = origP.area;
416  CfBf[origPp.index()][origFacei] = origP.centre;
417  }
418 
419  // Store the orig edge parts
420  forAll(intersection.srcEdgeParts(), origEdgei)
421  {
422  origEdgeParts[origEdgei] += intersection.srcEdgeParts()[origEdgei];
423  }
424 }
425 
426 
428 Foam::fvMeshStitcher::calculateOwnerOrigBoundaryEdgeParts
429 (
430  const List<List<part>>& patchEdgeParts
431 ) const
432 {
433  const polyBoundaryMesh& pbMesh = mesh_.boundaryMesh();
434 
436  const labelList& ownerOrigBoundaryPointMeshPoint =
438  const labelList& ownerOrigBoundaryEdgeMeshEdge =
440  const edgeList& ownerOrigBoundaryEdges = ncb.ownerOrigBoundaryEdges();
441  const edgeList& ownerOrigBoundaryMeshEdges =
443 
444  // Sum up boundary edge parts, being careful with signs
445  labelList ownerOrigBoundaryEdgeNParts(ownerOrigBoundaryEdges.size(), 0);
446  List<part> ownerOrigBoundaryEdgeParts(ownerOrigBoundaryEdges.size());
447  forAll(patchEdgeParts, patchi)
448  {
449  if (patchEdgeParts[patchi].empty()) continue;
450 
451  const polyPatch& patch = pbMesh[patchi];
452 
453  const labelList& patchEdgeOwnerOrigBoundaryEdges =
455 
456  forAll(patchEdgeParts[patchi], patchEdgei)
457  {
458  const label ownerOrigBoundaryEdgei =
459  patchEdgeOwnerOrigBoundaryEdges[patchEdgei];
460 
461  const label sign =
463  (
464  meshEdge(patch, patchEdgei),
465  ownerOrigBoundaryMeshEdges[ownerOrigBoundaryEdgei]
466  );
467 
468  const part& pU = patchEdgeParts[patchi][patchEdgei];
469  const part p = sign > 0 ? pU : -pU;
470 
471  ownerOrigBoundaryEdgeNParts[ownerOrigBoundaryEdgei] ++;
472  ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei] += p;
473  }
474  }
475 
476  // Give every point in the all boundary a global unique index
477  const globalIndex globalOwnerOrigBoundaryPoints
478  (
479  ownerOrigBoundaryPointMeshPoint.size()
480  );
481  labelList ownerOrigBoundaryPointIndices
482  (
483  ownerOrigBoundaryPointMeshPoint.size()
484  );
485  forAll(ownerOrigBoundaryPointIndices, ownerOrigBoundaryPointi)
486  {
487  ownerOrigBoundaryPointIndices[ownerOrigBoundaryPointi] =
488  globalOwnerOrigBoundaryPoints.toGlobal(ownerOrigBoundaryPointi);
489  }
491  (
492  mesh_,
493  ownerOrigBoundaryPointMeshPoint,
494  ownerOrigBoundaryPointIndices,
495  minEqOp<label>(),
496  labelMax
497  );
498 
499  // Synchronise the sign of the edge parts so that they can be
500  // meaningfully summed. This is done using the sign of the global point
501  // indices; if they are not in order, then reverse the part area.
502  forAll(ownerOrigBoundaryEdgeParts, ownerOrigBoundaryEdgei)
503  {
504  const edge& e = ownerOrigBoundaryEdges[ownerOrigBoundaryEdgei];
505 
506  if
507  (
508  ownerOrigBoundaryPointIndices[e.start()]
509  > ownerOrigBoundaryPointIndices[e.end()])
510  {
511  ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei] =
512  - ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei];
513  }
514  }
515 
516  // Average the boundary edge parts across couplings
518  (
519  mesh_,
520  ownerOrigBoundaryEdgeMeshEdge,
521  ownerOrigBoundaryEdgeNParts,
522  plusEqOp<label>(),
523  label(0)
524  );
526  (
527  mesh_,
528  ownerOrigBoundaryEdgeMeshEdge,
529  ownerOrigBoundaryEdgeParts,
530  plusEqOp<part>(),
531  part(),
532  []
533  (
534  const transformer& vt,
535  const bool forward,
536  List<part>& fld
537  )
538  {
539  if (forward)
540  {
541  forAll(fld, i)
542  {
543  if (magSqr(fld[i].area) != 0)
544  {
545  fld[i].area = vt.transform(fld[i].area);
546  fld[i].centre = vt.transformPosition(fld[i].centre);
547  }
548  }
549  }
550  else
551  {
552  forAll(fld, i)
553  {
554  if (magSqr(fld[i].area) != 0)
555  {
556  fld[i].area = vt.invTransform(fld[i].area);
557  fld[i].centre = vt.invTransformPosition(fld[i].centre);
558  }
559  }
560  }
561  }
562  );
563  forAll(ownerOrigBoundaryEdgeParts, ownerOrigBoundaryEdgei)
564  {
565  if (ownerOrigBoundaryEdgeNParts[ownerOrigBoundaryEdgei] != 0)
566  {
567  ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei].area /=
568  ownerOrigBoundaryEdgeNParts[ownerOrigBoundaryEdgei];
569  }
570  }
571 
572  // Put the edge parts back to their original orientations
573  forAll(ownerOrigBoundaryEdgeParts, ownerOrigBoundaryEdgei)
574  {
575  const edge& e = ownerOrigBoundaryEdges[ownerOrigBoundaryEdgei];
576 
577  if
578  (
579  ownerOrigBoundaryPointIndices[e.start()]
580  > ownerOrigBoundaryPointIndices[e.end()]
581  )
582  {
583  ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei] =
584  - ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei];
585  }
586  }
587 
588  return ownerOrigBoundaryEdgeParts;
589 }
590 
591 
592 
593 void Foam::fvMeshStitcher::applyOwnerOrigBoundaryEdgeParts
594 (
595  surfaceVectorField& SfSf,
596  surfaceVectorField& CfSf,
597  const List<part>& ownerOrigBoundaryEdgeParts
598 ) const
599 {
600  const polyBoundaryMesh& pbMesh = mesh_.boundaryMesh();
601 
603  const labelList ownerOrigPatchIDs = ncb.ownerOrigPatchIDs();
604  const labelList& ownerOrigBoundaryEdgeMeshEdge =
606  const edgeList& ownerOrigBoundaryMeshEdges =
608 
609  boolList patchIsOwnerOrig(pbMesh.size(), false);
610  UIndirectList<bool>(patchIsOwnerOrig, ownerOrigPatchIDs) = true;
611 
612  // Count the number of original faces attached to each boundary edge
613  labelList ownerOrigBoundaryEdgeNOrigFaces
614  (
615  ownerOrigBoundaryEdgeMeshEdge.size(),
616  0
617  );
618  forAll(ownerOrigBoundaryEdgeMeshEdge, ownerOrigBoundaryEdgei)
619  {
620  const label meshEdgei =
621  ownerOrigBoundaryEdgeMeshEdge[ownerOrigBoundaryEdgei];
622 
623  forAll(mesh_.edgeFaces()[meshEdgei], edgeFacei)
624  {
625  const label facei = mesh_.edgeFaces()[meshEdgei][edgeFacei];
626 
627  const label patchi =
628  mesh_.isInternalFace(facei)
629  ? -1 : pbMesh.patchID()[facei - mesh_.nInternalFaces()];
630 
631  if (patchi != -1 && patchIsOwnerOrig[patchi])
632  {
633  ownerOrigBoundaryEdgeNOrigFaces[ownerOrigBoundaryEdgei] ++;
634  }
635  }
636  }
637 
638  // Synchronise the boundary edge original face count
640  (
641  mesh_,
642  ownerOrigBoundaryEdgeMeshEdge,
643  ownerOrigBoundaryEdgeNOrigFaces,
644  plusEqOp<label>(),
645  label(0)
646  );
647 
648  // If debugging, check that face changes are in sync
649  tmp<surfaceLabelField> tChanged =
650  debug
651  ? surfaceLabelField::New("changed", mesh_, dimensioned<label>(dimless, 0))
652  : tmp<surfaceLabelField>(nullptr);
653 
654  // Modify faces connected to the boundary edges
655  forAll(ownerOrigBoundaryEdgeMeshEdge, ownerOrigBoundaryEdgei)
656  {
657  const label meshEdgei =
658  ownerOrigBoundaryEdgeMeshEdge[ownerOrigBoundaryEdgei];
659 
660  const part& ownerOrigBoundaryEdgeP =
661  ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei];
662 
663  switch (ownerOrigBoundaryEdgeNOrigFaces[ownerOrigBoundaryEdgei])
664  {
665  case 0:
666  {
667  continue;
668  }
669 
670  // If this edge is connected to just one original face then add any
671  // edge area into that face
672  case 1:
673  {
674  label origFacei = -1, origPatchi = -1, origPatchFacei = -1;
675  forAll(mesh_.edgeFaces()[meshEdgei], edgeFacei)
676  {
677  const label facei = mesh_.edgeFaces()[meshEdgei][edgeFacei];
678 
679  const label patchi =
680  mesh_.isInternalFace(facei)
681  ? -1 : pbMesh.patchID()[facei - mesh_.nInternalFaces()];
682 
683  if (patchi != -1 && patchIsOwnerOrig[patchi])
684  {
685  origFacei = facei;
686  origPatchi = patchi;
687  origPatchFacei = facei - pbMesh[patchi].start();
688  break;
689  }
690  }
691 
692  if (origFacei != -1)
693  {
694  vector& area =
695  SfSf.boundaryFieldRef()[origPatchi][origPatchFacei];
696  point& centre =
697  CfSf.boundaryFieldRef()[origPatchi][origPatchFacei];
698 
699  const label sign =
700  mesh_.faces()[origFacei].edgeDirection
701  (
702  ownerOrigBoundaryMeshEdges[ownerOrigBoundaryEdgei]
703  );
704 
705  part p(area, centre);
706  p +=
707  sign > 0
708  ? ownerOrigBoundaryEdgeP
709  : -ownerOrigBoundaryEdgeP;
710 
711  area = p.area;
712  centre = p.centre;
713 
714  if (debug)
715  {
716  tChanged->boundaryFieldRef()
717  [origPatchi][origPatchFacei] = label(1);
718  }
719  }
720 
721  break;
722  }
723 
724  // If this edge is connected to two original faces then add any
725  // edge area into non-original connected faces
726  case 2:
727  {
728  forAll(mesh_.edgeFaces()[meshEdgei], edgeFacei)
729  {
730  const label facei = mesh_.edgeFaces()[meshEdgei][edgeFacei];
731 
732  const label patchi =
733  mesh_.isInternalFace(facei)
734  ? -1 : pbMesh.patchID()[facei - mesh_.nInternalFaces()];
735 
736  if (patchi != -1 && patchIsOwnerOrig[patchi])
737  {
738  continue;
739  }
740 
741  const label patchFacei =
742  patchi == -1 ? -1 : facei - pbMesh[patchi].start();
743 
744  vector& area =
745  patchi == -1
746  ? SfSf[facei]
747  : SfSf.boundaryFieldRef()[patchi][patchFacei];
748  point& centre =
749  patchi == -1
750  ? CfSf[facei]
751  : CfSf.boundaryFieldRef()[patchi][patchFacei];
752 
753  const label sign =
754  mesh_.faces()[facei].edgeDirection
755  (
756  ownerOrigBoundaryMeshEdges[ownerOrigBoundaryEdgei]
757  );
758 
759  part p(area, centre);
760  p +=
761  sign < 0
762  ? ownerOrigBoundaryEdgeP
763  : -ownerOrigBoundaryEdgeP;
764 
765  area = p.area;
766  centre = p.centre;
767 
768  if (debug && patchi != -1)
769  {
770  tChanged->boundaryFieldRef()
771  [patchi][patchFacei] = label(1);
772  }
773  }
774 
775  break;
776  }
777 
778  default:
779  {
781  << "Boundary is not manifold"
782  << exit(FatalError);
783  }
784  }
785  }
786 
787  // If debugging, check that face changes are in sync
788  if (debug)
789  {
790  const surfaceLabelField::Boundary& changedBf =
791  tChanged->boundaryField();
792 
793  const surfaceLabelField::Boundary changedNbrBf
794  (
796  changedBf.boundaryNeighbourField()
797  );
798 
799  bool synchronised = true;
800 
801  forAll(changedBf, patchi)
802  {
803  const fvsPatchLabelField& changedPf = changedBf[patchi];
804  const fvsPatchLabelField& changedNbrPf = changedNbrBf[patchi];
805 
806  if (!isA<processorFvPatch>(changedPf.patch())) continue;
807 
808  forAll(changedPf, patchFacei)
809  {
810  if (changedPf[patchFacei] != changedNbrPf[patchFacei])
811  {
812  const label facei =
813  changedPf.patch().start() + patchFacei;
814 
816  << "Face not synchronised with centre at "
817  << mesh_.faceCentres()[facei] << endl;
818 
819  synchronised = false;
820  }
821  }
822  }
823 
824  if (!synchronised)
825  {
827  << exit(FatalError);
828  }
829  }
830 }
831 
832 
833 void Foam::fvMeshStitcher::stabiliseOrigPatchFaces
834 (
837 ) const
838 {
840  const labelList allOrigPatchIDs = ncb.allOrigPatchIDs();
841 
842  forAll(allOrigPatchIDs, i)
843  {
844  const label origPatchi = allOrigPatchIDs[i];
845  const polyPatch& origPp = mesh_.boundaryMesh()[origPatchi];
846 
847  forAll(origPp, origPatchFacei)
848  {
849  part p
850  (
851  SfBf[origPatchi][origPatchFacei],
852  CfBf[origPatchi][origPatchFacei]
853  );
854 
855  const part smallP
856  (
857  small*origPp.faceAreas()[origPatchFacei],
858  origPp.faceCentres()[origPatchFacei]
859  );
860 
861  p += smallP;
862 
863  SfBf[origPatchi][origPatchFacei] = p.area;
864  CfBf[origPatchi][origPatchFacei] = p.centre;
865  }
866  }
867 }
868 
869 
870 void Foam::fvMeshStitcher::intersectNonConformalCyclics
871 (
872  surfaceLabelField::Boundary& polyFacesBf,
873  surfaceVectorField& SfSf,
874  surfaceVectorField& CfSf,
875  const bool haveTopology
876 ) const
877 {
878  const polyBoundaryMesh& pbMesh = mesh_.boundaryMesh();
879 
881  const labelList ownerOrigPatchIDs = ncb.ownerOrigPatchIDs();
882 
883  // Alias the boundary geometry fields
886 
887  // Create storage for and initialise the edge parts of source patches
888  List<List<part>> patchEdgeParts(mesh_.boundary().size());
889  forAll(ownerOrigPatchIDs, i)
890  {
891  const label origPatchi = ownerOrigPatchIDs[i];
892 
893  patchEdgeParts[origPatchi].resize
894  (
895  mesh_.boundaryMesh()[origPatchi].nEdges(),
896  part(Zero)
897  );
898  }
899 
900  // If topology is specified, also create a boundary field of the original
901  // patch indices on the neighbouring interface, as well as the
902  // corresponding areas and centres for stabilisation purposes
903  tmp<surfaceLabelField::Boundary> tOrigFacesNbrBf;
906  if (haveTopology)
907  {
908  surfaceLabelField origFaces
909  (
911  (
912  "origFaces",
913  mesh_,
915  )
916  );
917  surfaceVectorField origSf("origSf", mesh_.Sf());
918  surfaceVectorField origCf("origCf", mesh_.Cf());
919 
920  forAll(mesh_.boundary(), patchi)
921  {
922  const fvPatch& fvp = mesh_.boundary()[patchi];
923 
924  if (!isA<nonConformalFvPatch>(fvp)) continue;
925 
926  const nonConformalFvPatch& ncFvp =
927  refCast<const nonConformalFvPatch>(fvp);
928 
929  origFaces.boundaryFieldRef()[patchi] =
930  polyFacesBf[patchi] - ncFvp.origPatch().start();
931  origSf.boundaryFieldRef()[patchi] =
932  vectorField(mesh_.faceAreas(), polyFacesBf[patchi]);
933  origCf.boundaryFieldRef()[patchi] =
934  pointField(mesh_.faceCentres(), polyFacesBf[patchi]);
935  }
936 
937  tOrigFacesNbrBf =
939  (
942  );
943  tOrigSfNbrBf =
945  (
948  );
949  tOrigCfNbrBf =
951  (
954  );
955 
956  // See note in fvMesh::unconform regarding the transformation
957  // of cell centres. The same applies here.
958  forAll(tOrigCfNbrBf(), patchi)
959  {
960  fvsPatchVectorField& Cfp = tOrigCfNbrBf.ref()[patchi];
961  if (Cfp.patch().coupled())
962  {
963  const transformer& t =
964  refCast<const coupledFvPatch>(Cfp.patch())
965  .transform();
966  t.invTransform(Cfp, Cfp);
967  t.transformPosition(Cfp, Cfp);
968  }
969  }
970  }
971 
972  // Do the intersections
973  forAll(mesh_.boundary(), patchi)
974  {
975  const fvPatch& fvp = mesh_.boundary()[patchi];
976 
977  if (!isA<nonConformalCyclicFvPatch>(fvp)) continue;
978 
979  const nonConformalCyclicFvPatch& nccFvp =
980  refCast<const nonConformalCyclicFvPatch>(fvp);
981 
982  if (!nccFvp.owner()) continue;
983 
984  intersectNonConformalCyclic
985  (
986  nccFvp,
987  polyFacesBf,
988  SfBf,
989  CfBf,
990  tOrigFacesNbrBf,
991  tOrigSfNbrBf,
992  tOrigCfNbrBf,
993  patchEdgeParts[nccFvp.origPatchID()]
994  );
995  }
996 
997  // Construct the boundary edge geometry
998  List<part> ownerOrigBoundaryEdgeParts =
999  calculateOwnerOrigBoundaryEdgeParts(patchEdgeParts);
1000 
1001  // Add the difference between patch edge parts and all boundary
1002  // edge parts to the adjacent patch faces. This is an error part.
1003  forAll(ownerOrigPatchIDs, i)
1004  {
1005  const label origPatchi = ownerOrigPatchIDs[i];
1006  const polyPatch& origPatch = pbMesh[origPatchi];
1007 
1008  const labelList& origPatchEdgeOwnerOrigBoundaryEdges =
1009  ncb.patchEdgeOwnerOrigBoundaryEdges(origPatchi);
1010 
1011  forAll(patchEdgeParts[origPatchi], origPatchEdgei)
1012  {
1013  const label ownerOrigBoundaryEdgei =
1014  origPatchEdgeOwnerOrigBoundaryEdges[origPatchEdgei];
1015 
1016  part errorP =
1017  patchEdgeParts[origPatchi][origPatchEdgei];
1018  errorP -= ownerOrigBoundaryEdgeParts[ownerOrigBoundaryEdgei];
1019 
1020  forAll(origPatch.edgeFaces()[origPatchEdgei], patchEdgeFacei)
1021  {
1022  const label patchFacei =
1023  origPatch.edgeFaces()[origPatchEdgei][patchEdgeFacei];
1024 
1025  part p
1026  (
1027  SfBf[origPatchi][patchFacei],
1028  CfBf[origPatchi][patchFacei]
1029  );
1030  p += errorP;
1031 
1032  SfBf[origPatchi][patchFacei] = p.area;
1033  CfBf[origPatchi][patchFacei] = p.centre;
1034  }
1035  }
1036  }
1037 
1038  // Use the boundary edge geometry to correct all edge-connected faces
1039  applyOwnerOrigBoundaryEdgeParts(SfSf, CfSf, ownerOrigBoundaryEdgeParts);
1040 
1041  // Stabilise
1042  stabiliseOrigPatchFaces(SfBf, CfBf);
1043 }
1044 
1045 
1046 template<class NonConformalFvPatch>
1047 inline void Foam::fvMeshStitcher::createNonConformalStabilisationGeometry
1049  const surfaceLabelField::Boundary& polyFacesBf,
1050  surfaceVectorField& SfSf,
1051  surfaceVectorField& CfSf
1052 ) const
1053 {
1054  // Alias the boundary geometry fields
1057 
1058  // Create small stabilisation geometry
1059  forAll(mesh_.boundary(), patchi)
1060  {
1061  const fvPatch& fvp = mesh_.boundary()[patchi];
1062 
1063  if (!isA<NonConformalFvPatch>(fvp)) continue;
1064 
1065  const polyPatch& origPp =
1066  refCast<const nonConformalFvPatch>(fvp).origPatch().patch();
1067 
1068  SfBf[patchi] ==
1069  vectorField
1070  (
1071  small*origPp.faceAreas(),
1072  polyFacesBf[patchi] - origPp.start()
1073  );
1074  CfBf[patchi] ==
1075  vectorField
1076  (
1077  origPp.faceCentres(),
1078  polyFacesBf[patchi] - origPp.start()
1079  );
1080  }
1081 }
1082 
1083 
1084 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
1085 
1087 {
1088  bool result = false;
1089 
1090  forAll(mesh_.boundary(), patchi)
1091  {
1092  const fvPatch& fvp = mesh_.boundary()[patchi];
1093 
1094  if (!isA<nonConformalFvPatch>(fvp)) continue;
1095 
1096  const fvsPatchScalarField& magSfp =
1097  mesh_.magSf().boundaryField()[patchi];
1098 
1099  const polyPatch& origPp =
1100  refCast<const nonConformalFvPatch>(fvp).origPatch().patch();
1101 
1102  const scalarField origMagSfp
1103  (
1104  origPp.magFaceAreas(),
1105  mesh_.polyFacesBf()[patchi] - origPp.start()
1106  );
1107 
1108  if (max(magSfp/origMagSfp) > rootSmall)
1109  {
1110  result = true;
1111  }
1112  }
1113 
1114  reduce(result, orOp<bool>());
1115 
1116  return returnReduce(result, orOp<bool>());
1117 }
1118 
1119 
1122 {
1123  return
1124  mag(fvc::surfaceIntegrate(mesh_.Sf()))()*mesh_.V()
1125  /max
1126  (
1127  mag(fvc::surfaceSum(cmptMag(mesh_.Sf())))(),
1128  (small*sqr(cbrt(mesh_.V())))()
1129  )();
1130 }
1131 
1132 
1135 {
1136  if (0 > n || n > 1)
1137  {
1139  << "Can only compute volume conservation error for this time, or "
1140  << "the previous time" << exit(FatalError);
1141  }
1142 
1143  const surfaceScalarField& phi = mesh_.phi().oldTime(n);
1144 
1145  const dimensionedScalar deltaT =
1146  n == 0 ? mesh_.time().deltaT() : mesh_.time().deltaT0();
1147 
1148  const volScalarField::Internal& V = n == 0 ? mesh_.V() : mesh_.V0();
1149  const volScalarField::Internal& V0 = n == 0 ? mesh_.V0() : mesh_.V00();
1150 
1151  return fvc::surfaceIntegrate(phi*deltaT)() - (V - V0)/mesh_.V();
1152 }
1153 
1154 
1155 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
1156 
1158 :
1159  mesh_(mesh)
1160 {}
1161 
1162 
1163 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
1164 
1166 {}
1167 
1168 
1169 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
1170 
1172 {
1173  forAll(mesh_.boundary(), patchi)
1174  {
1175  if (isA<nonConformalFvPatch>(mesh_.boundary()[patchi]))
1176  {
1177  return true;
1178  }
1179  }
1180 
1181  return false;
1182 }
1183 
1184 
1186 {}
1187 
1188 
1190 {}
1191 
1192 
1195  const bool changing,
1196  const bool geometric
1197 )
1198 {
1199  if (!stitches() || (changing && !mesh_.dynamic()))
1200  {
1201  return false;
1202  }
1203 
1204  // Is this mesh coupled?
1205  const bool coupled = Pstream::parRun() || !mesh_.time().processorCase();
1206 
1207  if (coupled && geometric)
1208  {
1209  Info<< indent << typeName << ": Disconnecting" << incrIndent << endl;
1210  }
1211 
1212  if (changing)
1213  {
1214  // Pre-conform surface fields. This splits the original and cyclic
1215  // parts of the interface fields into separate boundary fields, with
1216  // both sets of values store on the original faces. The original field
1217  // overwrites the existing boundary values, whilst the cyclic field is
1218  // stored as a separate field for use later.
1219  preConformSurfaceFields();
1220  }
1221 
1222  // Conform the mesh
1223  if (mesh_.moving())
1224  {
1225  surfaceScalarField phi(mesh_.phi());
1226  conformCorrectMeshPhi(phi);
1227  mesh_.conform(phi);
1228  }
1229  else
1230  {
1231  mesh_.conform();
1232  }
1233 
1234  // Resize all the affected patch fields
1235  resizePatchFields<VolField>();
1236  resizePatchFields<SurfaceField>();
1237 
1238  // Prevent hangs caused by processor cyclic patches using mesh geometry
1239  mesh_.deltaCoeffs();
1240 
1241  if (coupled && geometric)
1242  {
1244  Info<< indent << "Cell min/avg/max openness = "
1245  << gMin(o) << '/' << gAverage(o) << '/' << gMax(o) << endl;
1246 
1247  for (label i = 0; mesh_.moving() && i <= mesh_.phi().nOldTimes(); ++ i)
1248  {
1250  Info<< indent << "Cell min/avg/max ";
1251  for (label j = 0; j < i; ++ j) Info<< "old-";
1252  Info<< (i ? "time " : "") << "volume conservation error = "
1253  << gMin(vce) << '/' << gAverage(vce) << '/' << gMax(vce)
1254  << endl;
1255  }
1256 
1257  }
1258 
1259  if (coupled && geometric)
1260  {
1261  Info<< decrIndent;
1262  }
1263 
1264  meshObject::movePoints<fvMesh>(mesh_);
1265  meshObject::movePoints<lduMesh>(mesh_);
1266 
1267  const_cast<Time&>(mesh_.time()).functionObjects().movePoints(mesh_);
1268 
1269  return true;
1270 }
1271 
1272 
1275  const bool changing,
1276  const bool geometric,
1277  const bool load
1278 )
1279 {
1280  if (!stitches() || (changing && !mesh_.dynamic()))
1281  {
1282  return false;
1283  }
1284 
1285  // Is this mesh coupled?
1286  const bool coupled = Pstream::parRun() || !mesh_.time().processorCase();
1287 
1288  // Create a copy of the conformal poly face addressing
1289  surfaceLabelField::Boundary polyFacesBf
1290  (
1292  mesh_.polyFacesBf()
1293  );
1294 
1295  // If starting up then load topology from disk, if it is available
1296  bool haveTopology = false;
1297  if (load)
1298  {
1299  const IOobject polyFacesBfIO(mesh_.polyFacesBfIO(IOobject::MUST_READ));
1300 
1301  if (fileHandler().isFile(polyFacesBfIO.objectPath(false)))
1302  {
1303  haveTopology = true;
1304 
1305  // Read the boundary field but then set default values for conformal
1306  // patches as these patches will have had a uniform invalid index
1307  // set in order to save disk space
1308  surfaceLabelField::Boundary polyFacesBfRead
1309  (
1311  surfaceLabelField(polyFacesBfIO, mesh_).boundaryField()
1312  );
1313  forAll(mesh_.boundary(), patchi)
1314  {
1315  if (isA<nonConformalFvPatch>(mesh_.boundary()[patchi]))
1316  {
1317  polyFacesBf[patchi] = polyFacesBfRead[patchi];
1318  }
1319  }
1320  }
1321  }
1322 
1323  // Access all the intersections in advance. Makes the log nicer.
1324  if (coupled && (geometric || !haveTopology))
1325  {
1326  forAll(mesh_.boundary(), patchi)
1327  {
1328  const fvPatch& fvp = mesh_.boundary()[patchi];
1329 
1330  if (!isA<nonConformalCyclicFvPatch>(fvp)) continue;
1331 
1332  const nonConformalCyclicFvPatch& nccFvp =
1333  refCast<const nonConformalCyclicFvPatch>(fvp);
1334 
1335  if (!nccFvp.owner()) continue;
1336 
1338  }
1339  }
1340 
1341  if (coupled && (geometric || !haveTopology))
1342  {
1343  Info<< indent << typeName << ": Connecting" << incrIndent << endl;
1344  }
1345 
1346  // Create copies of geometry fields to be modified
1347  surfaceVectorField Sf(mesh_.Sf().cloneUnSliced()());
1348  surfaceVectorField Cf(mesh_.Cf().cloneUnSliced()());
1349 
1350  // Construct non-conformal geometry
1351  if (coupled && (geometric || !haveTopology))
1352  {
1353  // Do the intersection and create the non-conformal cyclic faces
1354  intersectNonConformalCyclics(polyFacesBf, Sf, Cf, haveTopology);
1355 
1356  // Create stabilisation geometry on any error faces specified in the
1357  // addressing
1358  createNonConformalStabilisationGeometry<nonConformalErrorFvPatch>
1359  (
1360  polyFacesBf,
1361  Sf,
1362  Cf
1363  );
1364  }
1365  else
1366  {
1367  // If not coupled, then create stabilisation geometry on all
1368  // non-conformal faces specified in the addressing
1369  createNonConformalStabilisationGeometry<nonConformalFvPatch>
1370  (
1371  polyFacesBf,
1372  Sf,
1373  Cf
1374  );
1375  }
1376 
1377  // If the mesh is moving then create any additional non-conformal geometry
1378  // necessary to correct the mesh fluxes
1379  if (mesh_.moving())
1380  {
1381  createNonConformalCorrectMeshPhiGeometry(polyFacesBf, Sf, Cf);
1382  }
1383 
1384  // Make the mesh non-conformal
1385  if (mesh_.moving())
1386  {
1387  surfaceScalarField phi(mesh_.phi());
1388  unconformCorrectMeshPhi(polyFacesBf, Sf, Cf, phi);
1389  mesh_.unconform(polyFacesBf, Sf, Cf, phi);
1390  }
1391  else
1392  {
1393  mesh_.unconform(polyFacesBf, Sf, Cf);
1394  }
1395 
1396  // Store all old-time fields. If we don't do this then we risk triggering a
1397  // store in the middle of mapping and potentially overwriting a mapped
1398  // old-time field with a not-yet-mapped new-time field.
1399  storeOldTimeFields<VolField>();
1400  storeOldTimeFields<SurfaceField>();
1401 
1402  // Resize all the affected patch fields
1403  resizePatchFields<VolField>();
1404  resizePatchFields<SurfaceField>();
1405 
1406  if (changing)
1407  {
1408  // Post-non-conform surface fields. This reconstructs the original and
1409  // cyclic parts of the interface fields from separate original and
1410  // cyclic parts. The original part was store in the same field, whilst
1411  // the cyclic part was separately registered.
1412  postNonConformSurfaceFields();
1413 
1414  // Volume fields are assumed to be intensive. So, the value on a face
1415  // which has changed in size can be retained without modification. New
1416  // faces need values to be set. This is done by evaluating all the
1417  // nonConformalCoupled patch fields.
1418  evaluateVolFields();
1419 
1420  // Do special post-non-conformation for surface velocities.
1421  postNonConformSurfaceVelocities();
1422  }
1423 
1424  // Prevent hangs caused by processor cyclic patches using mesh geometry
1425  mesh_.deltaCoeffs();
1426 
1427  if (coupled && geometric)
1428  {
1430  Info<< indent << "Cell min/avg/max openness = "
1431  << gMin(o) << '/' << gAverage(o) << '/' << gMax(o) << endl;
1432 
1433  for (label i = 0; mesh_.moving() && i <= mesh_.phi().nOldTimes(); ++ i)
1434  {
1436  Info<< indent << "Cell min/avg/max ";
1437  for (label j = 0; j < i; ++ j) Info<< "old-";
1438  Info<< (i ? "time " : "") << "volume conservation error = "
1439  << gMin(vce) << '/' << gAverage(vce) << '/' << gMax(vce)
1440  << endl;
1441  }
1442 
1443  if (mesh_.moving())
1444  {
1445  // Create a boundary field of the imbalance between the mesh fluxes
1446  // on either side of interfaces (like phiErrorb above)
1448  (
1450  mesh_.phi().boundaryField()
1451  );
1452  mfe += mesh_.phi().boundaryField().boundaryNeighbourField();
1453 
1454  // Determine the number of non-processor patches
1455  label nNonProcPatches = 0;
1456  forAll(mesh_.boundary(), patchi)
1457  {
1458  const fvPatch& fvp = mesh_.boundary()[patchi];
1459 
1460  if (!isA<processorFvPatch>(fvp))
1461  {
1462  if (nNonProcPatches != patchi)
1463  {
1465  << "Processor patches do not follow non-processor "
1466  << "patches" << exit(FatalError);
1467  }
1468 
1469  nNonProcPatches ++;
1470  }
1471  }
1472 
1473  // Work out the min, avg and max error for all non-conformal
1474  // cyclics, including any errors on referred processor cyclics
1475  scalarList minMfe(nNonProcPatches, vGreat);
1476  scalarField sumMfe(nNonProcPatches, 0), nSumMfe(nNonProcPatches, 0);
1477  scalarList maxMfe(nNonProcPatches, -vGreat);
1478  forAll(mfe, patchi)
1479  {
1480  const fvPatch& fvp = mesh_.boundary()[patchi];
1481 
1482  const label nccPatchi =
1483  isA<nonConformalCyclicFvPatch>(fvp)
1484  ? refCast<const nonConformalCyclicFvPatch>(fvp)
1485  .index()
1486  : isA<nonConformalProcessorCyclicFvPatch>(fvp)
1487  ? refCast<const nonConformalProcessorCyclicFvPatch>(fvp)
1488  .referPatchID()
1489  : -1;
1490 
1491  if (nccPatchi != -1)
1492  {
1493  minMfe[nccPatchi] =
1494  min(minMfe[nccPatchi], min(mfe[patchi]));
1495  sumMfe[nccPatchi] += sum(mfe[patchi]);
1496  nSumMfe[nccPatchi] += mfe[patchi].size();
1497  maxMfe[nccPatchi] =
1498  max(maxMfe[nccPatchi], max(mfe[patchi]));
1499  }
1500  }
1501  reduce(minMfe, ListOp<minOp<scalar>>());
1502  reduce(sumMfe, ListOp<minOp<scalar>>());
1503  reduce(nSumMfe, ListOp<minOp<scalar>>());
1504  reduce(maxMfe, ListOp<maxOp<scalar>>());
1505 
1506  // Report
1507  forAll(minMfe, patchi)
1508  {
1509  const fvPatch& fvp = mesh_.boundary()[patchi];
1510 
1511  if
1512  (
1513  isA<nonConformalCyclicFvPatch>(fvp)
1514  && refCast<const nonConformalCyclicFvPatch>(fvp).owner()
1515  && nSumMfe[patchi] > 0
1516  )
1517  {
1518  Info<< indent << fvp.name()
1519  << " min/avg/max mesh flux error = " << minMfe[patchi]
1520  << '/' << sumMfe[patchi]/(nSumMfe[patchi] + vSmall)
1521  << '/' << maxMfe[patchi] << endl;
1522  }
1523  }
1524  }
1525  }
1526 
1527  if (coupled && (geometric || !haveTopology))
1528  {
1529  Info<< decrIndent;
1530  }
1531 
1532  meshObject::movePoints<fvMesh>(mesh_);
1533  meshObject::movePoints<lduMesh>(mesh_);
1534 
1535  const_cast<Time&>(mesh_.time()).functionObjects().movePoints(mesh_);
1536 
1537  return true;
1538 }
1539 
1540 
1541 void Foam::fvMeshStitcher::reconnect(const bool geometric) const
1542 {
1543  if (mesh_.conformal() || geometric == this->geometric())
1544  {
1545  return;
1546  }
1547 
1548  // Create a copy of the conformal poly face addressing
1549  surfaceLabelField::Boundary polyFacesBf
1550  (
1552  mesh_.polyFacesBf()
1553  );
1554 
1555  // Undo all non-conformal changes and clear all geometry and topology
1556  mesh_.conform();
1557 
1558  // Create copies of geometry fields to be modified
1559  surfaceVectorField Sf(mesh_.Sf().cloneUnSliced()());
1560  surfaceVectorField Cf(mesh_.Cf().cloneUnSliced()());
1561 
1562  // Construct non-conformal geometry
1563  if (geometric)
1564  {
1565  // Do the intersection and create the non-conformal cyclic faces
1566  intersectNonConformalCyclics(polyFacesBf, Sf, Cf, true);
1567 
1568  // Create stabilisation geometry on any error faces specified in the
1569  // addressing
1570  createNonConformalStabilisationGeometry<nonConformalErrorFvPatch>
1571  (
1572  polyFacesBf,
1573  Sf,
1574  Cf
1575  );
1576  }
1577  else
1578  {
1579  // If not coupled, then create stabilisation geometry on all
1580  // non-conformal faces specified in the addressing
1581  createNonConformalStabilisationGeometry<nonConformalFvPatch>
1582  (
1583  polyFacesBf,
1584  Sf,
1585  Cf
1586  );
1587  }
1588 
1589  // Apply changes to the mesh
1590  mesh_.unconform(polyFacesBf, Sf, Cf);
1591 
1592  // Prevent hangs caused by processor cyclic patches using mesh geometry
1593  mesh_.deltaCoeffs();
1594 
1595  meshObject::movePoints<fvMesh>(mesh_);
1596  meshObject::movePoints<lduMesh>(mesh_);
1597 }
1598 
1599 
1600 // ************************************************************************* //
virtual bool disconnect(const bool changing, const bool geometric)
Disconnect the mesh by removing faces from the nonConformalCyclics.
dimensionedScalar sign(const dimensionedScalar &ds)
Surface integrate surfaceField creating a volField. Surface sum a surfaceField creating a volField...
List< List< procFace > > srcTgtProcFaces() const
For each source face, the coupled target procs and faces.
Return the vol-field velocity corresponding to a given surface-field velocity.
const List< DynamicList< couple > > & srcCouples() const
For each source face, the source and target areas for each.
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
const surfaceVectorField & Sf() const
Return cell face area vectors.
bool processorCase() const
Return true if this is a processor case.
Definition: TimePaths.H:89
layerAndWeight max(const layerAndWeight &a, const layerAndWeight &b)
FvWallInfoData< WallInfo, label > label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Ostream & indent(Ostream &os)
Indent stream.
Definition: Ostream.H:221
Vector-tensor class used to perform translations, rotations and scaling operations in 3D space...
Definition: transformer.H:83
const List< part > & srcErrorParts() const
For each source face, the area associated with mismatch.
void surfaceIntegrate(Field< Type > &ivf, const GeometricField< Type, fvsPatchField, surfaceMesh > &ssf)
bool moving() const
Is mesh moving.
Definition: polyMesh.H:525
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
Mesh object that stores an all boundary patch and mapping to and from it and the mesh and the individ...
label index() const
Return the index of this patch in the fvBoundaryMesh.
Definition: fvPatch.H:175
error FatalError
List< List< procFace > > tgtSrcProcFaces() const
For each target face, the coupled source procs and faces.
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:306
virtual label start() const
Return start label of this patch in the polyMesh face list.
Definition: fvPatch.H:151
Structure to conveniently store processor and face indices.
Definition: patchToPatch.H:60
const surfaceVectorField & Cf() const
Return face centres.
Type gMin(const FieldField< Field, Type > &f)
label facei
The face index.
Definition: patchToPatch.H:66
const List< DynamicList< couple > > & tgtCouples() const
For each target face, the target and source areas for each.
const labelList & ownerOrigBoundaryEdgeMeshEdge() const
Get a map from owner-orig boundary edge to mesh edge.
const Boundary & boundaryField() const
Return const-reference to the boundary field.
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: HashTable.H:59
const patchToPatches::intersection & intersection() const
Access the intersection engine.
dimensionedSymmTensor sqr(const dimensionedVector &dv)
const polyBoundaryMesh & boundaryMesh() const
Return boundaryMesh reference.
Definition: polyPatch.C:297
const labelList & patchEdgeOwnerOrigBoundaryEdges(const label patchi) const
Get a map from patch edge to owner-orig boundary edge.
bool geometric() const
Is the connection "geometric", or has the topology just been.
const edgeList & ownerOrigBoundaryEdges() const
Get the owner-orig boundary edges, referring to boundary points.
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
static int compare(const edge &, const edge &)
Compare edges.
Definition: edgeI.H:36
void unconform(const GeometricBoundaryField< label, fvsPatchField, surfaceMesh > &polyFacesBf, const surfaceVectorField &Sf, const surfaceVectorField &Cf, const surfaceScalarField &phi=NullObjectRef< surfaceScalarField >(), const bool sync=true)
Unconform the fvMesh from the polyMesh.
Definition: fvMesh.C:1335
Mesh manipulator that uses the intersection provided by the cyclic non-conformal poly patches to crea...
label proci
The processor index.
Definition: patchToPatch.H:63
static int myProcNo(const label communicator=0)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:429
static tmp< GeometricField< Type, PatchField, GeoMesh > > New(const word &name, const Internal &, const PtrList< PatchField< Type >> &)
Return a temporary field constructed from name,.
Non-conformal processor cyclic FV patch. As nonConformalCyclicFvPatch, but the neighbouring patch is ...
const surfaceScalarField & phi() const
Return cell face motion fluxes.
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
A finiteVolume patch using a polyPatch and a fvBoundaryMesh.
Definition: fvPatch.H:63
const labelList & patchID() const
Per boundary face label the patch index.
Holds information (coordinate and normal) regarding nearest wall point.
Non-conformal cyclic FV patch. As nonConformalCoupledFvPatch, but the neighbouring patch is local and...
tmp< DimensionedField< scalar, volMesh > > volumeConservationError(const label n) const
Return the non-dimensional old-time volume conservation error.
GeometricBoundaryField< Type, PatchField, GeoMesh > Boundary
Type of the boundary field.
const dimensionSet dimless
Generic dimensioned Type class.
label nOldTimes() const
Return the number of old time fields stored.
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:372
void resize(const label)
Alias for setSize(const label)
Definition: ListI.H:138
Class to generate patchToPatch coupling geometry. A full geometric intersection is done between a fac...
fvMesh & mesh
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:69
const dimensionedScalar c
Speed of light in a vacuum.
const labelList & meshPoints() const
Return labelList of mesh points in patch. They are constructed.
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &df)
virtual int neighbProcNo() const
Return neighbour processor number.
const Vector< Cmpt > & centre(const Foam::List< Vector< Cmpt >> &) const
Return *this (used for point which is a typedef to Vector<scalar>.
Definition: VectorI.H:116
A list of faces which address into the list of points.
Calculates a unique integer (label so might not have enough room - 2G max) for processor + local inde...
Definition: globalIndex.H:63
Foam::intersection.
Definition: intersection.H:49
fvMeshStitcher(fvMesh &)
Construct from fvMesh.
vector invTransformPosition(const vector &v) const
Inverse transform the given position.
Definition: transformerI.H:177
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:42
gmvFile<< "tracers "<< particles.size()<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().x()<< ' ';}gmvFile<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().y()<< ' ';}gmvFile<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().z()<< ' ';}gmvFile<< nl;forAll(lagrangianScalarNames, i){ const word &name=lagrangianScalarNames[i];IOField< scalar > fld(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
virtual void movePoints()
Update local data for mesh motion.
void Swap(T &a, T &b)
Definition: Swap.H:43
static const DimensionedField< Type, GeoMesh > & null()
Return a null DimensionedField.
bool conformal() const
Return whether the fvMesh is conformal with the polyMesh.
Definition: fvMesh.C:817
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:58
static void syncEdgeList(const polyMesh &, List< T > &, const CombineOp &cop, const T &nullValue, const TransformOp &top)
Synchronise values on all mesh edges.
bool valid() const
Is this temporary object valid,.
Definition: tmpI.H:167
A class for handling words, derived from string.
Definition: word.H:59
const GeometricBoundaryField< label, fvsPatchField, surfaceMesh > & polyFacesBf() const
Return face-poly-face addressing.
Definition: fvMesh.C:839
Class containing mesh-to-mesh mapping information after a change in polyMesh topology.
const polyPatch & patch() const
Return the polyPatch.
Definition: fvPatch.H:139
labelList allOrigPatchIDs() const
Return a list of the orig patch indices.
void sort(UList< T > &)
Definition: UList.C:115
virtual void updateMesh(const polyTopoChangeMap &)
Update local data for topology changes.
dimensionedScalar cbrt(const dimensionedScalar &ds)
const nonConformalCyclicPolyPatch & nonConformalCyclicPatch() const
Poly patch.
dimensionSet cmptMag(const dimensionSet &)
Definition: dimensionSet.C:275
virtual bool connect(const bool changing, const bool geometric, const bool load)
Connect the mesh by adding faces into the nonConformalCyclics.
layerAndWeight min(const layerAndWeight &a, const layerAndWeight &b)
phi
Definition: correctPhi.H:3
const labelListList & edgeFaces() const
Return edge-face addressing.
const edgeList & edges() const
Return list of edges, address into LOCAL point list.
static const label labelMax
Definition: label.H:62
static const zero Zero
Definition: zero.H:97
const fileOperation & fileHandler()
Get current file handler.
tmp< GeometricBoundaryField > boundaryNeighbourField() const
Return BoundaryField of the values on the other side of couples.
void conform(const surfaceScalarField &phi=NullObjectRef< surfaceScalarField >())
Conform the fvMesh to the polyMesh.
Definition: fvMesh.C:1312
Foam::polyBoundaryMesh.
bool stitches() const
Does this stitcher do anything?
const nonConformalCyclicFvPatch & nbrPatch() const
Neighbour patch.
dimensioned< scalar > magSqr(const dimensioned< Type > &)
const vectorField::subField faceAreas() const
Return face areas.
Definition: polyPatch.C:309
defineRunTimeSelectionTable(reactionRateFlameArea, dictionary)
Type gMax(const FieldField< Field, Type > &f)
defineTypeNameAndDebug(combustionModel, 0)
virtual ~fvMeshStitcher()
Destructor.
static nonConformalBoundary & New(polyMesh &mesh)
Definition: MeshObject.C:54
Ostream & decrIndent(Ostream &os)
Decrement the indent level.
Definition: Ostream.H:235
void reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
Generic GeometricBoundaryField class.
Definition: fvMesh.H:80
static const GeometricField< Type, PatchField, GeoMesh > & null()
Return a null geometric field.
const labelList & ownerOrigBoundaryPointMeshPoint() const
Get a map from owner-orig boundary point to mesh point.
label size() const
Return the number of elements in the UPtrList.
Definition: UPtrListI.H:29
const fvPatch & patch() const
Return patch.
const edgeList & ownerOrigBoundaryMeshEdges() const
Get the owner-orig boundary edges, referring to mesh points.
edge meshEdge(const PrimitivePatch< FaceList, PointField > &p, const label edgei)
static bool & parRun()
Is this a parallel run?
Definition: UPstream.H:399
static label nProcs(const label communicator=0)
Number of processes in parallel run.
Definition: UPstream.H:411
label patchi
const scalarField::subField magFaceAreas() const
Return face area magnitudes.
Definition: polyPatch.C:315
const List< part > & srcEdgeParts() const
For each source edge, the non-coupled geometry associated.
virtual bool owner() const
Does this side own the patch ?
Boundary & boundaryFieldRef()
Return a reference to the boundary field.
const fvPatch & origPatch() const
Original patch.
tmp< GeometricField< Type, PatchField, GeoMesh > > cloneUnSliced() const
Clone un-sliced.
Type gAverage(const FieldField< Field, Type > &f)
label end() const
Return end vertex label.
Definition: edgeI.H:92
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:95
A List with indirect addressing.
Definition: fvMatrix.H:106
label start() const
Return start label of this patch in the polyMesh face list.
Definition: polyPatch.H:306
Field with dimensions and associated with geometry type GeoMesh which is used to size the field and a...
Operator to apply a binary operation to a pair of lists.
Definition: ListOps.H:284
bool dynamic() const
Is this mesh dynamic?
Definition: fvMesh.C:578
static void syncPointList(const polyMesh &, List< T > &, const CombineOp &cop, const T &nullValue, const TransformOp &top)
Synchronise values on all mesh points.
void resize(const label)
Alias for setSize(const label)
Definition: PtrListI.H:32
messageStream Info
dimensioned< scalar > mag(const dimensioned< Type > &)
label n
GeometricField< label, fvsPatchField, surfaceMesh > surfaceLabelField
Field< vector > vectorField
Specialisation of Field<T> for vector.
Type transform(const Type &) const
Transform the given type.
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
tmp< GeometricField< Type, fvPatchField, volMesh > > surfaceSum(const GeometricField< Type, fvsPatchField, surfaceMesh > &ssf)
vector transformPosition(const vector &v) const
Transform the given position.
Definition: transformerI.H:153
const doubleScalar e
Elementary charge.
Definition: doubleScalar.H:105
virtual const word & name() const
Return name.
Definition: fvPatch.H:145
tmp< DimensionedField< scalar, volMesh > > openness() const
Return the non-dimensional cell openness for debugging/checking.
labelList ownerOrigPatchIDs() const
Return a list of the owner-orig patch indices.
label origPatchID() const
Original patch ID.
volScalarField & p
IOobject polyFacesBfIO(IOobject::readOption r) const
Get the poly faces IO object.
Definition: fvMesh.C:823
A class for managing temporary objects.
Definition: PtrList.H:53
void reconnect(const bool geometric) const
Re-compute the connection. Topology is preserved. Permits a change.
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:66
Type invTransform(const Type &) const
Inverse transform the given type.
virtual bool isFile(const fileName &, const bool checkVariants=true, const bool followLink=true) const =0
Does the name exist as a file in the file system?
Ostream & incrIndent(Ostream &os)
Increment the indent level.
Definition: Ostream.H:228
IOobject defines the attributes of an object for which implicit objectRegistry management is supporte...
Definition: IOobject.H:98
Non-conformal FV patch. Provides the necessary interface for a FV patch which does not conform to the...
virtual bool coupled() const
Return true if this patch is coupled.
Definition: fvPatch.H:163
label referPatchID() const
Return the referring patch ID.
An abstract base class with a fat-interface to all derived classes covering all possible ways in whic...
Definition: fvsPatchField.H:65
const surfaceScalarField & deltaCoeffs() const
Return reference to cell-centre difference coefficients.
label start() const
Return start vertex label.
Definition: edgeI.H:81
Namespace for OpenFOAM.
dimensionSet transform(const dimensionSet &)
Definition: dimensionSet.C:483
const fvBoundaryMesh & boundary() const
Return reference to boundary mesh.
Definition: fvMesh.C:800
const vectorField::subField faceCentres() const
Return face centres.
Definition: polyPatch.C:303