domainDecompositionNonConformal.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) 2011-2025 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "domainDecomposition.H"
27 #include "cyclicFvPatch.H"
28 #include "processorCyclicFvPatch.H"
34 
35 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
36 
37 namespace Foam
38 {
39 
41 (
42  const labelPair& procs,
43  const fvPatch& fvp,
44  const fvPatch& nbrFvp,
45  const labelUList& polyFacesPf,
46  const labelUList& nbrPolyFacesPf
47 )
48 {
49  if (polyFacesPf.size() != nbrPolyFacesPf.size())
50  {
52  << "Coupled patches " << fvp.name() << " and "
53  << nbrFvp.name() << " are not the same size"
54  << exit(FatalError);
55  }
56 
57  if (polyFacesPf.size())
58  {
59  for (label i = 1; i < polyFacesPf.size(); ++ i)
60  {
61  if
62  (
63  polyFacesPf[i - 1] > polyFacesPf[i]
64  ? true
65  : polyFacesPf[i - 1] == polyFacesPf[i]
66  ? nbrPolyFacesPf[i - 1] >= nbrPolyFacesPf[i]
67  : false
68  )
69  {
71  << "Coupled patches " << fvp.name()
72  << " and " << nbrFvp.name()
73  << " are not in order";
74 
75  if (procs[0] == procs[1])
76  {
78  << " on process #" << procs[0];
79  }
80 
82  << exit(FatalError);
83  }
84  }
85  }
86 
88  << "Coupled patches " << fvp.name()
89  << " and " << nbrFvp.name()
90  << " are in order" << endl;
91 }
92 
93 
95 (
96  const label& proci,
97  const fvPatch& fvp,
98  const labelList& polyFacesPf
99 )
100 {
101  if (fvp.size())
102  {
103  for (label i = 1; i < fvp.size(); ++ i)
104  {
105  if (polyFacesPf[i - 1] > polyFacesPf[i])
106  {
108  << "Error patch " << fvp.name()
109  << " is not in order";
110 
111  if (proci > 0)
112  {
114  << " on process #" << proci;
115  }
116 
118  << exit(FatalError);
119  }
120  }
121  }
122 
124  << "Error patch " << fvp.name()
125  << " is in order" << endl;
126 }
127 
128 
130 (
131  const fvMesh& completeMesh,
132  const multiDomainDecomposition& regionMeshes
133 )
134 {
135  forAll(completeMesh.boundary(), patchi)
136  {
137  const fvPatch& fvp = completeMesh.boundary()[patchi];
138 
139  // Cyclic patches
140  if
141  (
142  isA<nonConformalCyclicFvPatch>(fvp)
143  && refCast<const nonConformalCoupledFvPatch>(fvp).owner()
144  )
145  {
146  const label nccPatchi = patchi;
147  const label nbrNccPatchi =
148  refCast<const nonConformalCyclicFvPatch>(fvp)
149  .nbrPatchIndex();
150 
152  (
153  {-labelMax, labelMax},
154  completeMesh.boundary()[nccPatchi],
155  completeMesh.boundary()[nbrNccPatchi],
156  completeMesh.polyFacesBf()[nccPatchi],
157  completeMesh.polyFacesBf()[nbrNccPatchi]
158  );
159  }
160 
161  // Mapped patches
162  if (isA<nonConformalMappedWallFvPatch>(fvp))
163  {
164  const label ncmwPatchi = patchi;
165 
166  const nonConformalMappedWallFvPatch& ncmwFvp =
167  refCast<const nonConformalMappedWallFvPatch>(fvp);
168 
169  const fvsPatchLabelField& polyFacesPf =
170  completeMesh.polyFacesBf()[ncmwPatchi];
171 
172  const domainDecomposition& nbrDecomposition =
173  regionMeshes[ncmwFvp.nbrRegionName()]();
174 
175  const fvMesh& nbrCompleteMesh = nbrDecomposition.completeMesh();
176 
177  if (nbrCompleteMesh.conformal()) continue;
178 
179  const label nbrNcmwPatchi =
180  nbrCompleteMesh.boundary()[ncmwFvp.nbrPatchName()].index();
181 
182  const nonConformalMappedWallFvPatch& nbrNcmwFvp =
183  refCast<const nonConformalMappedWallFvPatch>
184  (nbrCompleteMesh.boundary()[nbrNcmwPatchi]);
185 
186  const fvsPatchLabelField& nbrPolyFacesPf =
187  nbrCompleteMesh.polyFacesBf()[nbrNcmwPatchi];
188 
190  (
191  {-labelMax, labelMax},
192  ncmwFvp.owner() ? ncmwFvp : nbrNcmwFvp,
193  ncmwFvp.owner() ? nbrNcmwFvp : ncmwFvp,
194  ncmwFvp.owner() ? polyFacesPf : nbrPolyFacesPf,
195  ncmwFvp.owner() ? nbrPolyFacesPf : polyFacesPf
196  );
197  }
198 
199  // Error patches
200  if (isA<nonConformalErrorFvPatch>(fvp))
201  {
202  const label ncePatchi = patchi;
203 
205  (
206  -labelMax,
207  completeMesh.boundary()[ncePatchi],
208  completeMesh.polyFacesBf()[ncePatchi]
209  );
210  }
211  }
212 }
213 
214 
216 (
217  const PtrList<fvMesh>& procMeshes,
218  const multiDomainDecomposition& regionMeshes
219 )
220 {
221  forAll(procMeshes, proci)
222  {
223  forAll(procMeshes[proci].boundary(), patchi)
224  {
225  const fvPatch& fvp =
226  procMeshes[proci].boundary()[patchi];
227 
228  // Cyclic patches
229  if
230  (
231  isA<nonConformalCoupledFvPatch>(fvp)
232  && refCast<const nonConformalCoupledFvPatch>(fvp).owner()
233  )
234  {
235  const label nccPatchi = patchi;
236 
237  label nbrProci = -1, nbrNccPatchi = -1;
238  if (isA<cyclicFvPatch>(fvp))
239  {
240  nbrProci = proci;
241  nbrNccPatchi =
242  refCast<const cyclicFvPatch>(fvp).nbrPatchIndex();
243  }
244  else if (isA<processorCyclicFvPatch>(fvp))
245  {
246  typedef processorCyclicFvPatch PcFvp;
247 
248  const PcFvp& pcFvp = refCast<const PcFvp>(fvp);
249 
250  nbrProci = pcFvp.neighbProcNo();
251 
252  const fvBoundaryMesh& nbrFvPatches =
253  procMeshes[nbrProci].boundary();
254 
255  forAll(nbrFvPatches, nbrNccPatchj)
256  {
257  const fvPatch& nbrFvp =
258  nbrFvPatches[nbrNccPatchj];
259 
260  if (isA<PcFvp>(nbrFvp))
261  {
262  const PcFvp& nbrPcFvp =
263  refCast<const PcFvp>(nbrFvp);
264 
265  if
266  (
267  nbrPcFvp.neighbProcNo()
268  == proci
269  && nbrPcFvp.referPatchIndex()
270  == pcFvp.referPatch().nbrPatchIndex()
271  )
272  {
273  nbrNccPatchi = nbrNccPatchj;
274  break;
275  }
276  }
277  }
278 
279  if (nbrNccPatchi == -1)
280  {
282  << "Opposite processor patch not found for "
283  << "patch " << fvp.name() << " on proc #"
284  << proci << exit(FatalError);
285  }
286  }
287  else
288  {
290  << "Non-conformal-coupled type not recognised "
291  << "for patch " << fvp.name() << " on proc #"
292  << proci << exit(FatalError);
293  }
294 
296  (
297  {proci, nbrProci},
298  procMeshes[proci].boundary()[nccPatchi],
299  procMeshes[nbrProci].boundary()[nbrNccPatchi],
300  procMeshes[proci].polyFacesBf()[nccPatchi],
301  procMeshes[nbrProci].polyFacesBf()[nbrNccPatchi]
302  );
303  }
304 
305  // Mapped patches
306  if (isA<nonConformalMappedWallFvPatch>(fvp))
307  {
308  const label ncmwPatchi = patchi;
309 
310  const nonConformalMappedWallFvPatch& ncmwFvp =
311  refCast<const nonConformalMappedWallFvPatch>(fvp);
312 
313  typedef
315  NcmpfFvsplf;
316 
317  const NcmpfFvsplf& polyFacesPf =
318  refCast<const NcmpfFvsplf>
319  (procMeshes[proci].polyFacesBf()[ncmwPatchi]);
320 
321  forAll(procMeshes, nbrProci)
322  {
323  const fvMesh& nbrProcMesh =
324  regionMeshes[ncmwFvp.nbrRegionName()]()
325  .procMeshes()[nbrProci];
326 
327  if (nbrProcMesh.conformal()) continue;
328 
329  const label nbrNcmwPatchi =
330  nbrProcMesh
331  .boundary()[ncmwFvp.nbrPatchName()]
332  .index();
333 
334  const nonConformalMappedWallFvPatch& nbrNcmwFvp =
335  refCast<const nonConformalMappedWallFvPatch>
336  (nbrProcMesh.boundary()[nbrNcmwPatchi]);
337 
338  const NcmpfFvsplf& nbrPolyFacesPf =
339  refCast<const NcmpfFvsplf>
340  (nbrProcMesh.polyFacesBf()[nbrNcmwPatchi]);
341 
342  const SubList<label> subPolyFacesPf
343  (
344  procMeshes[proci].polyFacesBf()[ncmwPatchi],
345  polyFacesPf.procSizes()[nbrProci],
346  polyFacesPf.procOffsets()[nbrProci]
347  );
348 
349  const SubList<label> nbrSubPolyFacesPf
350  (
351  nbrProcMesh.polyFacesBf()[nbrNcmwPatchi],
352  nbrPolyFacesPf.procSizes()[proci],
353  nbrPolyFacesPf.procOffsets()[proci]
354  );
355 
357  (
358  {
359  ncmwFvp.owner() ? proci : nbrProci,
360  ncmwFvp.owner() ? nbrProci : proci
361  },
362  ncmwFvp.owner() ? ncmwFvp : nbrNcmwFvp,
363  ncmwFvp.owner() ? nbrNcmwFvp : ncmwFvp,
364  ncmwFvp.owner() ? subPolyFacesPf : nbrSubPolyFacesPf,
365  ncmwFvp.owner() ? nbrSubPolyFacesPf : subPolyFacesPf
366  );
367  }
368  }
369 
370  // Error patches
371  if (isA<nonConformalErrorFvPatch>(fvp))
372  {
373  const label ncePatchi = patchi;
374 
376  (
377  proci,
378  procMeshes[proci].boundary()[ncePatchi],
379  procMeshes[proci].polyFacesBf()[ncePatchi]
380  );
381  }
382  }
383  }
384 }
385 
386 
387 }
388 
389 
390 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
391 
392 bool Foam::domainDecomposition::sortReconstructNonConformalCyclicAddressing_ =
394  (
395  "sortReconstructNonConformalCyclicAddressing",
396  0
397  );
398 
399 
400 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
401 
402 bool Foam::domainDecomposition::completeConformal() const
403 {
404  return completeMesh().conformal();
405 }
406 
407 
408 bool Foam::domainDecomposition::procsConformal() const
409 {
410  forAll(procMeshes_, proci)
411  {
412  if (!procMeshes_[proci].conformal())
413  {
414  return false;
415  }
416  }
417 
418  return true;
419 }
420 
421 
422 Foam::labelList Foam::domainDecomposition::completeFaceAddressing() const
423 {
424  labelList result (completeMesh().nFaces(), -labelMax);
425 
426  forAll(procMeshes_, proci)
427  {
428  forAll(procFaceAddressing()[proci], procFacei)
429  {
430  const label facei =
431  mag(procFaceAddressing()[proci][procFacei]) - 1;
432 
433  result[facei] = result[facei] == -labelMax ? procFacei : -1;
434  }
435  }
436 
437  return result;
438 }
439 
440 
442 Foam::domainDecomposition::nonConformalCyclicProcCyclics() const
443 {
444  List<labelPairLabelTable> result(completeMesh().boundary().size());
445 
446  forAll(procMeshes_, proci)
447  {
448  const fvMesh& procMesh = procMeshes_[proci];
449 
450  forAll(procMesh.boundary(), procPatchi)
451  {
452  const fvPatch& fvp = procMesh.boundary()[procPatchi];
453 
454  if (isA<nonConformalCyclicFvPatch>(fvp))
455  {
456  result[procPatchi].insert
457  (
458  labelPair(proci, proci),
459  procPatchi
460  );
461  }
462 
463  if (isA<nonConformalProcessorCyclicFvPatch>(fvp))
464  {
465  const processorCyclicFvPatch& pcFvp =
466  refCast<const processorCyclicFvPatch>(fvp);
467 
468  result[pcFvp.referPatchIndex()].insert
469  (
470  labelPair(proci, pcFvp.neighbProcNo()),
471  procPatchi
472  );
473  }
474  }
475  }
476 
477  return result;
478 }
479 
480 
482 Foam::domainDecomposition::nonConformalMappedWallProcOffsets
483 (
484  const bool appendSize
485 ) const
486 {
487  PtrList<labelListList> result(completeMesh().boundary().size());
488 
489  const surfaceLabelField::Boundary& polyFacesBf =
490  completeMesh().polyFacesBf();
491 
492  forAll(completeMesh().boundary(), ncmwPatchi)
493  {
494  const fvPatch& fvp = completeMesh().boundary()[ncmwPatchi];
495 
496  if (!isA<nonConformalMappedWallFvPatch>(fvp)) continue;
497 
498  const nonConformalMappedWallFvPatch& ncmwFvp =
499  refCast<const nonConformalMappedWallFvPatch>(fvp);
500 
501  const domainDecomposition& nbrDecomposition =
502  regionMeshes_[ncmwFvp.nbrRegionName()]();
503 
504  const fvMesh& nbrCompleteMesh = nbrDecomposition.completeMesh();
505 
506  const label nbrNcmwPatchi =
507  nbrCompleteMesh.boundary()[ncmwFvp.nbrPatchName()].index();
508 
509  const surfaceLabelField::Boundary& nbrPolyFacesBf =
510  nbrCompleteMesh.polyFacesBf();
511 
512  result.set
513  (
514  ncmwPatchi,
515  new labelListList(nProcs(), labelList(nProcs() + appendSize, 0))
516  );
517 
518  // Determine the number of faces in each processor block of the
519  // mapped patches
520  labelListList& procNbrProcCounts = result[ncmwPatchi];
521  forAll(polyFacesBf[ncmwPatchi], ncmwPatchFacei)
522  {
523  const label facei = polyFacesBf[ncmwPatchi][ncmwPatchFacei];
524  const label celli = completeMesh().faceOwner()[facei];
525  const label proci = cellProc_[celli];
526 
527  const label nbrFacei =
528  nbrPolyFacesBf[nbrNcmwPatchi][ncmwPatchFacei];
529  const label nbrCelli =
530  nbrCompleteMesh.faceOwner()[nbrFacei];
531  const label nbrProci = nbrDecomposition.cellProc()[nbrCelli];
532 
533  procNbrProcCounts[proci][nbrProci] ++;
534  }
535 
536  // Convert the counts to cumulative sums (i.e., offsets)
537  forAll(procNbrProcCounts, proci)
538  {
539  label count = 0;
540 
541  forAll(procNbrProcCounts[proci], nbrProci)
542  {
543  const label count0 = count;
544  count += procNbrProcCounts[proci][nbrProci];
545  procNbrProcCounts[proci][nbrProci] = count0;
546  }
547 
548  if (appendSize)
549  {
550  procNbrProcCounts[proci].last() = count;
551  }
552  }
553  }
554 
555  return result;
556 }
557 
558 
559 void Foam::domainDecomposition::decomposeNonConformalCyclicAddressing
560 (
561  const label nccPatchi,
562  const List<labelPairLabelTable>& nonConformalCyclicProcCyclics,
563  List<List<DynamicList<label>>>& nonConformalProcFaceAddressingBf
564 ) const
565 {
566  const surfaceLabelField::Boundary& polyFacesBf =
567  completeMesh().polyFacesBf();
568 
569  const nonConformalCyclicFvPatch& nccFvp =
570  refCast<const nonConformalCyclicFvPatch>
571  (
572  completeMesh().boundary()[nccPatchi]
573  );
574 
575  if (!nccFvp.owner()) return;
576 
577  const label nbrNccPatchi = nccFvp.nbrPatchIndex();
578 
579  forAll(polyFacesBf[nccPatchi], nccPatchFacei)
580  {
581  const label facei = polyFacesBf[nccPatchi][nccPatchFacei];
582  const label celli = completeMesh().faceOwner()[facei];
583  const label proci = cellProc_[celli];
584 
585  const label nbrFacei = polyFacesBf[nbrNccPatchi][nccPatchFacei];
586  const label nbrCelli = completeMesh().faceOwner()[nbrFacei];
587  const label nbrProci = cellProc_[nbrCelli];
588 
589  const label procNccPatchi =
590  nonConformalCyclicProcCyclics
591  [nccPatchi][labelPair(proci, nbrProci)];
592  const label nbrProcNccPatchi =
593  nonConformalCyclicProcCyclics
594  [nbrNccPatchi][labelPair(nbrProci, proci)];
595 
596  nonConformalProcFaceAddressingBf[proci][procNccPatchi]
597  .append(nccPatchFacei + 1);
598  nonConformalProcFaceAddressingBf[nbrProci][nbrProcNccPatchi]
599  .append(nccPatchFacei + 1);
600  }
601 }
602 
603 
604 void Foam::domainDecomposition::decomposeNonConformalMappedWallAddressing
605 (
606  const label ncmwPatchi,
607  PtrList<labelListList>& nonConformalMappedWallProcOffsets,
608  List<List<DynamicList<label>>>& nonConformalProcFaceAddressingBf
609 ) const
610 {
611  const surfaceLabelField::Boundary& polyFacesBf =
612  completeMesh().polyFacesBf();
613 
614  const nonConformalMappedWallFvPatch& ncmwFvp =
615  refCast<const nonConformalMappedWallFvPatch>
616  (
617  completeMesh().boundary()[ncmwPatchi]
618  );
619 
620  const domainDecomposition& nbrDecomposition =
621  regionMeshes_[ncmwFvp.nbrRegionName()]();
622 
623  const fvMesh& nbrCompleteMesh = nbrDecomposition.completeMesh();
624 
625  const surfaceLabelField::Boundary& nbrPolyFacesBf =
626  nbrCompleteMesh.polyFacesBf();
627 
628  const label nbrNcmwPatchi =
629  nbrCompleteMesh.boundary()[ncmwFvp.nbrPatchName()].index();
630 
631  // Resize the addressing as necessary
632  labelListList& procOffsets =
633  nonConformalMappedWallProcOffsets[ncmwPatchi];
634  forAll(procOffsets, proci)
635  {
636  nonConformalProcFaceAddressingBf[proci][ncmwPatchi]
637  .resize(procOffsets[proci].last());
638  }
639 
640  // Insert the poly face addressing into the result. Use the
641  // procOffsets array as the index into each processor block.
642  forAll(polyFacesBf[ncmwPatchi], ncmwPatchFacei)
643  {
644  const label facei = polyFacesBf[ncmwPatchi][ncmwPatchFacei];
645  const label celli = completeMesh().faceOwner()[facei];
646  const label proci = cellProc_[celli];
647 
648  const label nbrFacei = nbrPolyFacesBf[nbrNcmwPatchi][ncmwPatchFacei];
649  const label nbrCelli = nbrCompleteMesh.faceOwner()[nbrFacei];
650  const label nbrProci = nbrDecomposition.cellProc()[nbrCelli];
651 
652  nonConformalProcFaceAddressingBf
653  [proci][ncmwPatchi][procOffsets[proci][nbrProci] ++] =
654  ncmwPatchFacei + 1;
655  }
656 }
657 
658 
659 void Foam::domainDecomposition::decomposeNonConformalErrorAddressing
660 (
661  const label ncePatchi,
662  List<List<DynamicList<label>>>& nonConformalProcFaceAddressingBf
663 ) const
664 {
665  const surfaceLabelField::Boundary& polyFacesBf =
666  completeMesh().polyFacesBf();
667 
668  forAll(polyFacesBf[ncePatchi], ncePatchFacei)
669  {
670  const label facei = polyFacesBf[ncePatchi][ncePatchFacei];
671  const label celli = completeMesh().faceOwner()[facei];
672  const label proci = cellProc_[celli];
673 
674  nonConformalProcFaceAddressingBf[proci][ncePatchi]
675  .append(ncePatchFacei + 1);
676  }
677 }
678 
679 
680 void Foam::domainDecomposition::reconstructNonConformalCyclicAddressing
681 (
682  const label nccPatchi,
683  const List<labelPairLabelTable>& nonConformalCyclicProcCyclics,
684  List<List<DynamicList<label>>>& nonConformalProcFaceAddressingBf
685 ) const
686 {
687  const nonConformalCyclicFvPatch& nccFvp =
688  refCast<const nonConformalCyclicFvPatch>
689  (
690  completeMesh().boundary()[nccPatchi]
691  );
692 
693  if (!nccFvp.owner()) return;
694 
695  const label nbrNccPatchi = nccFvp.nbrPatchIndex();
696 
697  // Initialise counters
698  label nccPatchFacei = 0;
699  labelPairLabelTable procNccPatchFaceis;
701  (
702  labelPairLabelTable,
703  nonConformalCyclicProcCyclics[nccPatchi],
704  iter
705  )
706  {
707  procNccPatchFaceis.insert(iter.key(), 0);
708  }
709 
710  // Create each complete face in turn. For each complete face, loop all the
711  // processor interfaces and find the "next" face; i.e., that with the
712  // smallest owner-neighbour face indices.
713  while (true)
714  {
715  labelPair procNbrProc(labelMax, labelMax);
716  labelPair faceNbrFace(labelMax, labelMax);
717 
718  forAllConstIter(labelPairLabelTable, procNccPatchFaceis, iter)
719  {
720  const label proci = iter.key().first();
721  const label nbrProci = iter.key().second();
722 
723  const labelPair procNbrProcStar(proci, nbrProci);
724  const labelPair nbrProcProcStar(nbrProci, proci);
725 
726  const label procNccPatchi =
727  nonConformalCyclicProcCyclics[nccPatchi][procNbrProcStar];
728  const label nbrProcNccPatchi =
729  nonConformalCyclicProcCyclics[nbrNccPatchi][nbrProcProcStar];
730 
731  const label procNccPatchFacei = iter();
732  const label size =
733  procMeshes_[proci].polyFacesBf()[procNccPatchi].size();
734 
735  if (procNccPatchFacei >= size) continue;
736 
737  const label procFacei =
738  procMeshes_[proci].polyFacesBf()
739  [procNccPatchi][procNccPatchFacei];
740  const label nbrProcFacei =
741  procMeshes_[nbrProci].polyFacesBf()
742  [nbrProcNccPatchi][procNccPatchFacei];
743 
744  const labelPair faceNbrFaceStar
745  (
746  procFaceAddressing_[proci][procFacei] - 1,
747  procFaceAddressing_[nbrProci][nbrProcFacei] - 1
748  );
749 
750  if (faceNbrFace > faceNbrFaceStar)
751  {
752  procNbrProc = procNbrProcStar;
753  faceNbrFace = faceNbrFaceStar;
754  }
755  }
756 
757  if (faceNbrFace == labelPair(labelMax, labelMax))
758  {
759  break;
760  }
761  else
762  {
763  const label proci = procNbrProc.first();
764  const label nbrProci = procNbrProc.second();
765 
766  const labelPair nbrProcProc(nbrProci, proci);
767 
768  const label procNccPatchi =
769  nonConformalCyclicProcCyclics[nccPatchi][procNbrProc];
770  const label nbrProcNccPatchi =
771  nonConformalCyclicProcCyclics[nbrNccPatchi][nbrProcProc];
772 
773  nonConformalProcFaceAddressingBf[proci][procNccPatchi]
774  .append(nccPatchFacei + 1);
775  nonConformalProcFaceAddressingBf[nbrProci][nbrProcNccPatchi]
776  .append(nccPatchFacei + 1);
777 
778  nccPatchFacei ++;
779  procNccPatchFaceis[procNbrProc] ++;
780  }
781  }
782 }
783 
784 
785 void Foam::domainDecomposition::sortReconstructNonConformalCyclicAddressing
786 (
787  const label nccPatchi,
788  const List<labelPairLabelTable>& nonConformalCyclicProcCyclics,
789  List<List<DynamicList<label>>>& nonConformalProcFaceAddressingBf
790 ) const
791 {
792  const nonConformalCyclicFvPatch& nccFvp =
793  refCast<const nonConformalCyclicFvPatch>
794  (
795  completeMesh().boundary()[nccPatchi]
796  );
797 
798  if (!nccFvp.owner()) return;
799 
800  const label nbrNccPatchi = nccFvp.nbrPatchIndex();
801 
802  // Resize the relevant patches
804  (
805  labelPairLabelTable,
806  nonConformalCyclicProcCyclics[nccPatchi],
807  iter
808  )
809  {
810  const label proci = iter.key().first();
811  const label nbrProci = iter.key().second();
812 
813  const label procNccPatchi = iter();
814  const label nbrProcNccPatchi =
815  nonConformalCyclicProcCyclics
816  [nbrNccPatchi][labelPair(nbrProci, proci)];
817 
818  const labelUList& procPolyFacesPf =
819  procMeshes_[proci].polyFacesBf()[procNccPatchi];
820  const labelUList& nbrProcPolyFacesPf =
821  procMeshes_[nbrProci].polyFacesBf()[nbrProcNccPatchi];
822 
823  nonConformalProcFaceAddressingBf[proci][procNccPatchi]
824  .resize(procPolyFacesPf.size());
825  nonConformalProcFaceAddressingBf[nbrProci][nbrProcNccPatchi]
826  .resize(nbrProcPolyFacesPf.size());
827  }
828 
829  // Obtain references to the original patches
830  const fvPatch& origFvp = nccFvp.origPatch();
831  const fvPatch& nbrOrigFvp = nccFvp.nbrPatch().origPatch();
832 
833  // Create a list of "indices", containing every label relevant to each
834  // non-conformal face
835  DynamicList<FixedList<label, 7>> indices;
837  (
838  labelPairLabelTable,
839  nonConformalCyclicProcCyclics[nccPatchi],
840  iter
841  )
842  {
843  const label proci = iter.key().first();
844  const label nbrProci = iter.key().second();
845 
846  const label procNccPatchi = iter();
847  const label nbrProcNccPatchi =
848  nonConformalCyclicProcCyclics
849  [nbrNccPatchi][labelPair(nbrProci, proci)];
850 
851  const labelUList& procPolyFacesPf =
852  procMeshes_[proci].polyFacesBf()[procNccPatchi];
853  const labelUList& nbrProcPolyFacesPf =
854  procMeshes_[nbrProci].polyFacesBf()[nbrProcNccPatchi];
855 
856  forAll(procPolyFacesPf, procPatchFacei)
857  {
858  const label procPolyFacei =
859  procPolyFacesPf[procPatchFacei];
860  const label nbrProcPolyFacei =
861  nbrProcPolyFacesPf[procPatchFacei];
862 
863  const label completePolyFacei =
864  procFaceAddressing_[proci][procPolyFacei] - 1;
865  const label nbcCompletePolyFacei =
866  procFaceAddressing_[nbrProci][nbrProcPolyFacei] - 1;
867 
868  indices.append
869  ({
870  proci,
871  nbrProci,
872  procNccPatchi,
873  nbrProcNccPatchi,
874  completePolyFacei - origFvp.start(),
875  nbcCompletePolyFacei - nbrOrigFvp.start(),
876  procPatchFacei
877  });
878  }
879  }
880 
881  // Sort the indices by the owner poly face, then by the neighbour poly face
883  (
884  indices,
885  [](const FixedList<label, 7>& a, const FixedList<label, 7>& b)
886  {
887  return labelPair(a[4], a[5]) < labelPair(b[4], b[5]);
888  }
889  );
890 
891  // Unpack into the addressing
892  forAll(indices, i)
893  {
894  const label proci = indices[i][0];
895  const label nbrProci = indices[i][1];
896  const label procNccPatchi = indices[i][2];
897  const label nbrProcNccPatchi = indices[i][3];
898  //const label completePolyPatchFacei = indices[i][4];
899  //const label nbrCompletePolyPatchFacei = indices[i][5];
900  const label procPatchFacei = indices[i][6];
901 
902  nonConformalProcFaceAddressingBf
903  [proci][procNccPatchi][procPatchFacei] = i + 1;
904  nonConformalProcFaceAddressingBf
905  [nbrProci][nbrProcNccPatchi][procPatchFacei] = i + 1;
906  }
907 }
908 
909 
910 void Foam::domainDecomposition::reconstructNonConformalMappedWallAddressing
911 (
912  const label ncmwPatchi,
913  List<List<DynamicList<label>>>& nonConformalProcFaceAddressingBf
914 ) const
915 {
916  const nonConformalMappedWallFvPatch& ncmwFvp =
917  refCast<const nonConformalMappedWallFvPatch>
918  (
919  completeMesh().boundary()[ncmwPatchi]
920  );
921 
922  const bool owner = ncmwFvp.owner();
923 
924  const domainDecomposition& nbrDecomposition =
925  regionMeshes_[ncmwFvp.nbrRegionName()]();
926 
927  const label nbrNcmwPatchi =
928  nbrDecomposition.completeMesh()
929  .boundary()[ncmwFvp.nbrPatchName()]
930  .index();
931 
932  // Allocate space for the addressing
933  forAll(procMeshes(), proci)
934  {
935  nonConformalProcFaceAddressingBf[proci][ncmwPatchi].resize
936  (
937  procMeshes()[proci].polyFacesBf()[ncmwPatchi].size()
938  );
939  }
940 
941  // Get the processor offsets from the non-conformal mapped wall patches,
942  // and append an additional final value for the total size
943  auto calcProcOffsets = []
944  (
945  const domainDecomposition& meshes,
946  const label ncmwPatchi
947  )
948  {
949  labelListList result(meshes.nProcs());
950 
951  forAll(meshes.procMeshes(), proci)
952  {
953  typedef
954  nonConformalMappedPolyFacesFvsPatchLabelField
955  NcmpfFvsplf;
956 
957  const NcmpfFvsplf& polyFacesPf =
958  refCast<const NcmpfFvsplf>
959  (meshes.procMeshes()[proci].polyFacesBf()[ncmwPatchi]);
960 
961  result[proci] = polyFacesPf.procOffsets();
962  result[proci].append(polyFacesPf.size());
963  }
964 
965  return result;
966  };
967  const labelListList procOffsets =
968  calcProcOffsets(*this, ncmwPatchi);
969  const labelListList nbrProcOffsets =
970  calcProcOffsets(nbrDecomposition, nbrNcmwPatchi);
971 
972  // Initialise counters
973  label ncmwPatchFacei = 0;
974  labelListList procNcmwPatchFaceis(nProcs(), labelList(nProcs(), 0));
975 
976  // Create each complete face in turn. For each complete face, loop all the
977  // processor interfaces and find the "next" face; i.e., that with the
978  // smallest owner-neighbour face indices.
979  while (true)
980  {
981  labelPair procNbrProc(labelMax, labelMax);
982  labelPair faceNbrFace(labelMax, labelMax);
983 
984  forAll(procNcmwPatchFaceis, proci)
985  {
986  forAll(procNcmwPatchFaceis[proci], nbrProci)
987  {
988  const labelPair procNbrProcStar(proci, nbrProci);
989 
990  const label procNcmwPatchFacei =
991  procNcmwPatchFaceis[proci][nbrProci];
992  const label size =
993  procOffsets[proci][nbrProci + 1]
994  - procOffsets[proci][nbrProci];
995 
996  if (procNcmwPatchFacei >= size) continue;
997 
998  const label procFacei =
999  procMeshes_[proci].polyFacesBf()
1000  [ncmwPatchi]
1001  [procNcmwPatchFacei + procOffsets[proci][nbrProci]];
1002  const label nbrProcFacei =
1003  nbrDecomposition.procMeshes()[nbrProci].polyFacesBf()
1004  [nbrNcmwPatchi]
1005  [procNcmwPatchFacei + nbrProcOffsets[nbrProci][proci]];
1006 
1007  const labelPair faceNbrFaceStar
1008  (
1009  procFaceAddressing_[proci][procFacei] - 1,
1010  nbrDecomposition
1011  .procFaceAddressing_[nbrProci][nbrProcFacei] - 1
1012  );
1013 
1014  if
1015  (
1016  owner
1017  ? faceNbrFace > faceNbrFaceStar
1018  : reverse(faceNbrFace) > reverse(faceNbrFaceStar)
1019  )
1020  {
1021  procNbrProc = procNbrProcStar;
1022  faceNbrFace = faceNbrFaceStar;
1023  }
1024  }
1025  }
1026 
1027  if (faceNbrFace == labelPair(labelMax, labelMax))
1028  {
1029  break;
1030  }
1031  else
1032  {
1033  const label proci = procNbrProc.first();
1034  const label nbrProci = procNbrProc.second();
1035 
1036  nonConformalProcFaceAddressingBf
1037  [proci]
1038  [ncmwPatchi]
1039  [
1040  procOffsets[proci][nbrProci]
1041  + procNcmwPatchFaceis[proci][nbrProci]
1042  ] = ncmwPatchFacei + 1;
1043 
1044  ncmwPatchFacei ++;
1045  procNcmwPatchFaceis[proci][nbrProci] ++;
1046  }
1047  }
1048 }
1049 
1050 
1051 void Foam::domainDecomposition::reconstructNonConformalErrorAddressing
1052 (
1053  const label ncePatchi,
1054  List<List<DynamicList<label>>>& nonConformalProcFaceAddressingBf
1055 ) const
1056 {
1057  // Initialise counters
1058  label ncePatchFacei = 0;
1059  labelList procNcePatchFaceis(nProcs(), 0);
1060 
1061  // Create each complete face in turn. For each complete face, loop all the
1062  // processor patches and find the "next" face; i.e., that with the smallest
1063  // face index.
1064  while (true)
1065  {
1066  label facei = labelMax, proci = labelMax;
1067 
1068  forAll(procNcePatchFaceis, procStari)
1069  {
1070  const label size =
1071  procMeshes_[procStari].polyFacesBf()[ncePatchi].size();
1072 
1073  if (procNcePatchFaceis[procStari] >= size) continue;
1074 
1075  const label procFacei =
1076  procMeshes_[procStari].polyFacesBf()
1077  [ncePatchi][procNcePatchFaceis[procStari]];
1078 
1079  const label faceStari =
1080  procFaceAddressing_[procStari][procFacei] - 1;
1081 
1082  if (facei > faceStari)
1083  {
1084  facei = faceStari;
1085  proci = procStari;
1086  }
1087  }
1088 
1089  if (facei == labelMax)
1090  {
1091  break;
1092  }
1093  else
1094  {
1095  nonConformalProcFaceAddressingBf[proci][ncePatchi]
1096  .append(ncePatchFacei + 1);
1097 
1098  ncePatchFacei ++;
1099  procNcePatchFaceis[proci] ++;
1100  }
1101  }
1102 }
1103 
1104 
1106 Foam::domainDecomposition::nonConformalProcFaceAddressingBf() const
1107 {
1108  validateComplete();
1109  validateProcs();
1110 
1111  // Build non-conformal finite volume face addressing for each processor
1112  List<List<DynamicList<label>>> result(nProcs());
1113  forAll(result, proci)
1114  {
1115  result[proci].resize
1116  (
1117  procMeshes_[proci].boundary().size()
1118  );
1119  }
1120 
1121  if (completeConformal() && procsConformal())
1122  {
1123  // Nothing to do
1124  }
1125  else if (!completeConformal())
1126  {
1127  const List<labelPairLabelTable> nonConformalCyclicProcCyclics =
1128  this->nonConformalCyclicProcCyclics();
1129 
1130  PtrList<labelListList> nonConformalMappedWallProcOffsets =
1131  this->nonConformalMappedWallProcOffsets(true);
1132 
1133  // Decompose non-conformal addressing
1134  forAll(completeMesh().boundary(), ncPatchi)
1135  {
1136  const fvPatch& fvp = completeMesh().boundary()[ncPatchi];
1137 
1138  if (isA<nonConformalCyclicFvPatch>(fvp))
1139  {
1140  decomposeNonConformalCyclicAddressing
1141  (
1142  ncPatchi,
1143  nonConformalCyclicProcCyclics,
1144  result
1145  );
1146  }
1147 
1148  if (isA<nonConformalMappedWallFvPatch>(fvp))
1149  {
1150  decomposeNonConformalMappedWallAddressing
1151  (
1152  ncPatchi,
1153  nonConformalMappedWallProcOffsets,
1154  result
1155  );
1156  }
1157 
1158  if (isA<nonConformalErrorFvPatch>(fvp))
1159  {
1160  decomposeNonConformalErrorAddressing(ncPatchi, result);
1161  }
1162  }
1163  }
1164  else // if (!procsConformal())
1165  {
1166  const List<labelPairLabelTable> nonConformalCyclicProcCyclics =
1167  this->nonConformalCyclicProcCyclics();
1168 
1169  // Reconstruct non-conformal addressing
1170  forAll(completeMesh().boundary(), ncPatchi)
1171  {
1172  const fvPatch& fvp = completeMesh().boundary()[ncPatchi];
1173 
1174  if (isA<nonConformalCyclicFvPatch>(fvp))
1175  {
1176  if (sortReconstructNonConformalCyclicAddressing_)
1177  {
1178  sortReconstructNonConformalCyclicAddressing
1179  (
1180  ncPatchi,
1181  nonConformalCyclicProcCyclics,
1182  result
1183  );
1184  }
1185  else
1186  {
1187  reconstructNonConformalCyclicAddressing
1188  (
1189  ncPatchi,
1190  nonConformalCyclicProcCyclics,
1191  result
1192  );
1193  }
1194  }
1195 
1196  if (isA<nonConformalMappedWallFvPatch>(fvp))
1197  {
1198  reconstructNonConformalMappedWallAddressing(ncPatchi, result);
1199  }
1200 
1201  if (isA<nonConformalErrorFvPatch>(fvp))
1202  {
1203  reconstructNonConformalErrorAddressing(ncPatchi, result);
1204  }
1205  }
1206  }
1207 
1208  return result;
1209 }
1210 
1211 
1212 void Foam::domainDecomposition::unconformComplete()
1213 {
1214  surfaceLabelField::Boundary polyFacesBf
1215  (
1217  completeMesh().polyFacesBf()
1218  );
1219  surfaceVectorField Sf(completeMesh().Sf().cloneUnSliced());
1220  surfaceVectorField Cf(completeMesh().Cf().cloneUnSliced());
1221 
1222  forAll(procMeshes_, proci)
1223  {
1224  const fvMesh& procMesh = procMeshes_[proci];
1225 
1226  const surfaceLabelField::Boundary& faceAddressingBf =
1227  procFaceAddressingBf()[proci];
1228 
1229  forAll(procMesh.boundary(), procNcPatchi)
1230  {
1231  const fvPatch& fvp = procMesh.boundary()[procNcPatchi];
1232 
1233  if (!isA<nonConformalFvPatch>(fvp)) continue;
1234 
1235  const label completeNcPatchi =
1236  isA<processorCyclicFvPatch>(fvp)
1237  ? refCast<const processorCyclicFvPatch>(fvp)
1238  .referPatchIndex()
1239  : procNcPatchi;
1240 
1241  const label size =
1242  max
1243  (
1244  max(mag(faceAddressingBf[procNcPatchi])),
1245  polyFacesBf[completeNcPatchi].size()
1246  );
1247 
1248  polyFacesBf[completeNcPatchi].resize(size, -1);
1249  polyFacesBf[completeNcPatchi].labelField::rmap
1250  (
1251  mag
1252  (
1253  labelField
1254  (
1255  procFaceAddressing_[proci],
1256  procMesh.polyFacesBf()[procNcPatchi]
1257  )
1258  ) - 1,
1259  mag(faceAddressingBf[procNcPatchi]) - 1
1260  );
1261 
1262  // Set dummy data for the face geometry. This should not be
1263  // used during reconstruction.
1264  Sf.boundaryFieldRef()[completeNcPatchi].resize(size, Zero);
1265  Cf.boundaryFieldRef()[completeNcPatchi].resize(size, Zero);
1266  }
1267  }
1268 
1269  completeMesh_->unconform
1270  (
1271  polyFacesBf,
1272  Sf,
1273  Cf,
1274  NullObjectRef<surfaceScalarField>(),
1275  false
1276  );
1277 
1278  completeMesh_->setPolyFacesBfInstance(procMeshes_[0].polyFacesBfInstance());
1279 
1280  // Check ordering
1281  if (debug)
1282  {
1283  checkCompleteMeshOrdering(completeMesh(), regionMeshes_);
1284  }
1285 }
1286 
1287 
1288 void Foam::domainDecomposition::unconformProcs()
1289 {
1290  const labelList completeFaceAddressing =
1291  this->completeFaceAddressing();
1292 
1293  const PtrList<labelListList> nonConformalMappedWallProcOffsets =
1294  this->nonConformalMappedWallProcOffsets(false);
1295 
1296  forAll(procMeshes_, proci)
1297  {
1298  fvMesh& procMesh = procMeshes_[proci];
1299 
1300  const surfaceLabelField::Boundary& faceAddressingBf =
1301  procFaceAddressingBf()[proci];
1302 
1303  surfaceLabelField::Boundary polyFacesBf
1304  (
1306  procMesh.polyFacesBf()
1307  );
1308  surfaceVectorField Sf(procMesh.Sf().cloneUnSliced());
1309  surfaceVectorField Cf(procMesh.Cf().cloneUnSliced());
1310 
1311  forAll(procMesh.boundary(), procNcPatchi)
1312  {
1313  const fvPatch& fvp = procMesh.boundary()[procNcPatchi];
1314 
1315  if (!isA<nonConformalFvPatch>(fvp)) continue;
1316 
1317  const label completeNcPatchi =
1318  isA<processorCyclicFvPatch>(fvp)
1319  ? refCast<const processorCyclicFvPatch>(fvp)
1320  .referPatchIndex()
1321  : procNcPatchi;
1322 
1323  polyFacesBf[procNcPatchi] =
1324  labelField
1325  (
1326  completeFaceAddressing,
1327  labelField
1328  (
1329  completeMesh().polyFacesBf()[completeNcPatchi],
1330  mag(faceAddressingBf[procNcPatchi]) - 1
1331  )
1332  );
1333 
1334  if (isA<nonConformalMappedWallFvPatch>(fvp))
1335  {
1336  typedef
1337  nonConformalMappedPolyFacesFvsPatchLabelField
1338  NcmpfFvsplf;
1339 
1340  refCast<NcmpfFvsplf>(polyFacesBf[procNcPatchi]).procOffsets() =
1341  nonConformalMappedWallProcOffsets[procNcPatchi][proci];
1342  }
1343 
1344  const label size = polyFacesBf[procNcPatchi].size();
1345 
1346  // Set dummy data for the face geometry. This should not be
1347  // used during decomposition.
1348  Sf.boundaryFieldRef()[procNcPatchi].resize(size, Zero);
1349  Cf.boundaryFieldRef()[procNcPatchi].resize(size, Zero);
1350  }
1351 
1352  procMesh.unconform
1353  (
1354  polyFacesBf,
1355  Sf,
1356  Cf,
1357  NullObjectRef<surfaceScalarField>(),
1358  false
1359  );
1360 
1361  procMesh.setPolyFacesBfInstance(completeMesh().polyFacesBfInstance());
1362  }
1363 
1364  // Check ordering
1365  if (debug)
1366  {
1367  checkProcMeshesOrdering(procMeshes(), regionMeshes_);
1368  }
1369 }
1370 
1371 
1372 void Foam::domainDecomposition::unconform()
1373 {
1374  if (completeConformal() && procsConformal())
1375  {
1376  // Nothing to do
1377  }
1378  else if (!completeConformal() && procsConformal())
1379  {
1380  unconformProcs();
1381  }
1382  else if (completeConformal() && !procsConformal())
1383  {
1384  unconformComplete();
1385  }
1386  else // if (!completeConformal() && !procsConformal())
1387  {
1388  // Assume everything is consistent and do nothing
1389  }
1390 }
1391 
1392 
1393 // ************************************************************************* //
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:433
#define forAllConstIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:476
GeometricBoundaryField< Type, GeoMesh, PrimitiveField > Boundary
Type of the boundary field.
static const GeometricField< Type, GeoMesh, PrimitiveField > & null()
Return a null geometric field.
A templated 1D list of pointers to objects of type <T>, where the size of the array is known and used...
Definition: PtrList.H:75
A List obtained as a section of another List.
Definition: SubList.H:56
label size() const
Return the number of elements in the UList.
Definition: UListI.H:311
Automatic domain decomposition class for finite-volume meshes.
const fvMesh & completeMesh() const
Access the global mesh.
Foam::fvBoundaryMesh.
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:96
const fvBoundaryMesh & boundary() const
Return reference to boundary mesh.
Definition: fvMesh.C:962
const GeometricBoundaryField< label, surfaceMesh > & polyFacesBf() const
Return face-poly-face addressing.
Definition: fvMesh.C:985
bool conformal() const
Return whether the fvMesh is conformal with the polyMesh.
Definition: fvMesh.C:979
A finiteVolume patch using a polyPatch and a fvBoundaryMesh.
Definition: fvPatch.H:64
virtual label size() const
Return size.
Definition: fvPatch.H:138
virtual const word & name() const
Return name.
Definition: fvPatch.H:126
An abstract base class with a fat-interface to all derived classes covering all possible ways in whic...
Definition: fvsPatchField.H:86
const word & nbrPatchName() const
Name of the patch to map from.
const word & nbrRegionName() const
Name of the region to map from.
Wall fv patch which can do non-conformal mapping of values from another potentially non-globally conf...
bool owner() const
Is this patch the owner?
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
label patchi
volScalarField & b
Definition: createFields.H:27
#define InfoInFunction
Report an information message using Foam::Info.
int optimisationSwitch(const char *name, const int defaultValue=0)
Lookup optimisation switch or add default value.
Definition: debug.C:249
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
Pair< label > labelPair
Label pair.
Definition: labelPair.H:48
List< label > labelList
A List of labels.
Definition: labelList.H:56
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
void checkNonConformalErrorPatchOrdering(const label &proci, const fvPatch &fvp, const labelList &polyFacesPf)
void checkProcMeshesOrdering(const PtrList< fvMesh > &procMeshes, const multiDomainDecomposition &regionMeshes)
void mag(LagrangianPatchField< scalar > &f, const LagrangianPatchField< Type > &f1)
void reverse(UList< T > &, const label n)
Definition: UListI.H:334
List< labelList > labelListList
A List of labelList.
Definition: labelList.H:57
layerAndWeight max(const layerAndWeight &a, const layerAndWeight &b)
Field< label > labelField
Specialisation of Field<T> for label.
Definition: labelField.H:49
error FatalError
void stableSort(UList< T > &)
Definition: UList.C:129
label count(const ListType &l, typename ListType::const_reference x)
Count the number of occurrences of a value in a list.
static const label labelMax
Definition: label.H:62
SurfaceField< vector > surfaceVectorField
UList< label > labelUList
Definition: UList.H:65
void checkCompleteMeshOrdering(const fvMesh &completeMesh, const multiDomainDecomposition &regionMeshes)
void checkNonConformalCoupledPatchOrdering(const labelPair &procs, const fvPatch &fvp, const fvPatch &nbrFvp, const labelUList &polyFacesPf, const labelUList &nbrPolyFacesPf)
faceListList boundary(nPatches)