domainDecomposition.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-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 "domainDecomposition.H"
27 #include "decompositionMethod.H"
28 #include "IOobjectList.H"
29 #include "cellSet.H"
30 #include "faceSet.H"
31 #include "pointSet.H"
32 #include "hexRef8Data.H"
33 #include "cyclicFvPatch.H"
34 #include "processorCyclicFvPatch.H"
38 
39 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
40 
41 namespace Foam
42 {
43  defineTypeNameAndDebug(domainDecomposition, 0);
44 }
45 
46 
47 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
48 
49 void Foam::domainDecomposition::decomposePoints()
50 {
51  for (label proci = 0; proci < nProcs(); proci++)
52  {
53  fvMesh& procMesh = procMeshes_[proci];
54 
55  const label pointsCompare =
56  compareInstances
57  (
58  completeMesh().pointsInstance(),
59  procMeshes_[proci].pointsInstance()
60  );
61 
62  if (pointsCompare == -1)
63  {
64  procMesh.setPoints
65  (
67  (
68  completeMesh().points(),
69  procPointAddressing_[proci]
70  )
71  );
72  }
73  }
74 }
75 
76 
77 void Foam::domainDecomposition::reconstructPoints()
78 {
79  const label pointsCompare =
80  compareInstances
81  (
82  completeMesh().pointsInstance(),
83  procMeshes_[0].pointsInstance()
84  );
85 
86  if (pointsCompare == 1)
87  {
88  pointField completePoints(completeMesh().nPoints());
89 
90  for (label proci = 0; proci < nProcs(); proci++)
91  {
92  const fvMesh& procMesh = procMeshes_[proci];
93 
94  completePoints.rmap(procMesh.points(), procPointAddressing_[proci]);
95  }
96 
97  completeMesh_->setPoints(completePoints);
98  }
99 }
100 
101 
102 bool Foam::domainDecomposition::completeConformal() const
103 {
104  return completeMesh().conformal();
105 }
106 
107 
108 bool Foam::domainDecomposition::procsConformal() const
109 {
110  forAll(procMeshes_, proci)
111  {
112  if (!procMeshes_[proci].conformal())
113  {
114  return false;
115  }
116  }
117 
118  return true;
119 }
120 
121 
122 void Foam::domainDecomposition::unconform()
123 {
124  // ...
125  auto checkNonConformalCoupledPatchOrdering = []
126  (
127  const labelPair& procs,
128  const fvPatch& fvp,
129  const fvPatch& nbrFvp,
130  const labelList& polyFacesPf,
131  const labelList& nbrPolyFacesPf
132  )
133  {
134  if (fvp.size() != nbrFvp.size())
135  {
137  << "Coupled patches " << fvp.name() << " and "
138  << nbrFvp.name() << " are not the same size"
139  << exit(FatalError);
140  }
141 
142  if (fvp.size())
143  {
144  for (label i = 1; i < fvp.size(); ++ i)
145  {
146  if
147  (
148  polyFacesPf[i - 1] > polyFacesPf[i]
149  ? true
150  : polyFacesPf[i - 1] == polyFacesPf[i]
151  ? nbrPolyFacesPf[i - 1] >= nbrPolyFacesPf[i]
152  : false
153  )
154  {
156  << "Coupled patches " << fvp.name()
157  << " and " << nbrFvp.name()
158  << " are not in order";
159 
160  if (procs[0] == procs[1])
161  {
163  << " on process #" << procs[0];
164  }
165 
167  << exit(FatalError);
168  }
169  }
170  }
171 
173  << "Coupled patches " << fvp.name()
174  << " and " << nbrFvp.name()
175  << " are in order" << endl;
176  };
177 
178  // ...
179  auto checkNonConformalErrorPatchOrdering = []
180  (
181  const label& proci,
182  const fvPatch& fvp,
183  const labelList& polyFacesPf
184  )
185  {
186  if (fvp.size())
187  {
188  for (label i = 1; i < fvp.size(); ++ i)
189  {
190  if (polyFacesPf[i - 1] > polyFacesPf[i])
191  {
193  << "Error patch " << fvp.name()
194  << " is not in order";
195 
196  if (proci > 0)
197  {
199  << " on process #" << proci;
200  }
201 
203  << exit(FatalError);
204  }
205  }
206  }
207 
209  << "Error patch " << fvp.name()
210  << " is in order" << endl;
211  };
212 
213  if (completeConformal() && procsConformal())
214  {
215  // Nothing to do
216  }
217  else if (!completeConformal() && procsConformal())
218  {
219  // Construct the reverse of proc-face-face addressing
220  labelList faceProcFace(completeMesh().nFaces(), -labelMax);
221  forAll(procMeshes_, proci)
222  {
223  forAll(procFaceAddressing()[proci], procFacei)
224  {
225  const label facei =
226  mag(procFaceAddressing()[proci][procFacei]) - 1;
227 
228  faceProcFace[facei] =
229  faceProcFace[facei] == -labelMax ? procFacei : -1;
230  }
231  }
232 
233  // Map the polyFacesBf from the complete to the processor meshes and
234  // unconform the processor meshes. Set dummy data for the face
235  // geometry. This should not be used during decomposition.
236  forAll(procMeshes_, proci)
237  {
238  fvMesh& procMesh = procMeshes_[proci];
239 
240  const surfaceLabelField::Boundary& faceAddressingBf =
241  procFaceAddressingBf()[proci];
242 
243  surfaceLabelField::Boundary polyFacesBf
244  (
246  procMesh.polyFacesBf()
247  );
248  surfaceVectorField Sf(procMesh.Sf().cloneUnSliced());
249  surfaceVectorField Cf(procMesh.Cf().cloneUnSliced());
250 
251  forAll(procMesh.boundary(), procNccPatchi)
252  {
253  const fvPatch& fvp = procMesh.boundary()[procNccPatchi];
254 
255  if (isA<nonConformalFvPatch>(fvp))
256  {
257  const label completeNccPatchi =
258  isA<processorCyclicFvPatch>(fvp)
259  ? refCast<const processorCyclicFvPatch>(fvp)
260  .referPatchID()
261  : procNccPatchi;
262 
263  polyFacesBf[procNccPatchi] =
264  labelField
265  (
266  faceProcFace,
267  labelField
268  (
269  completeMesh().polyFacesBf()[completeNccPatchi],
270  mag(faceAddressingBf[procNccPatchi]) - 1
271  )
272  );
273 
274  const label size = polyFacesBf[procNccPatchi].size();
275 
276  Sf.boundaryFieldRef()[procNccPatchi].resize(size, Zero);
277  Cf.boundaryFieldRef()[procNccPatchi].resize(size, Zero);
278  }
279  }
280 
281  procMesh.unconform
282  (
283  polyFacesBf,
284  Sf,
285  Cf,
286  NullObjectRef<surfaceScalarField>(),
287  false
288  );
289  }
290 
291  // Check ordering
292  if (debug)
293  {
294  forAll(procMeshes_, proci)
295  {
296  forAll(procMeshes_[proci].boundary(), patchi)
297  {
298  const fvPatch& fvp =
299  procMeshes_[proci].boundary()[patchi];
300 
301  // Coupled patches
302  if
303  (
304  isA<nonConformalCoupledFvPatch>(fvp)
305  && refCast<const nonConformalCoupledFvPatch>(fvp).owner()
306  )
307  {
308  const label nccPatchi = patchi;
309 
310  label nbrProci = -1, nbrNccPatchi = -1;
311  if (isA<cyclicFvPatch>(fvp))
312  {
313  nbrProci = proci;
314  nbrNccPatchi =
315  refCast<const cyclicFvPatch>(fvp).nbrPatchID();
316  }
317  else if (isA<processorCyclicFvPatch>(fvp))
318  {
319  typedef processorCyclicFvPatch PcFvp;
320 
321  const PcFvp& pcFvp = refCast<const PcFvp>(fvp);
322 
323  nbrProci = pcFvp.neighbProcNo();
324 
325  const fvBoundaryMesh& nbrFvPatches =
326  procMeshes_[nbrProci].boundary();
327 
328  forAll(nbrFvPatches, nbrNccPatchj)
329  {
330  const fvPatch& nbrFvp =
331  nbrFvPatches[nbrNccPatchj];
332 
333  if (isA<PcFvp>(nbrFvp))
334  {
335  const PcFvp& nbrPcFvp =
336  refCast<const PcFvp>(nbrFvp);
337 
338  if
339  (
340  nbrPcFvp.neighbProcNo()
341  == proci
342  && nbrPcFvp.referPatchID()
343  == pcFvp.referPatch().nbrPatchID()
344  )
345  {
346  nbrNccPatchi = nbrNccPatchj;
347  break;
348  }
349  }
350  }
351 
352  if (nbrNccPatchi == -1)
353  {
355  << "Opposite processor patch not found for "
356  << "patch " << fvp.name() << " on proc #"
357  << proci << exit(FatalError);
358  }
359  }
360  else
361  {
363  << "Non-conformal-coupled type not recognised "
364  << "for patch " << fvp.name() << " on proc #"
365  << proci << exit(FatalError);
366  }
367 
368  checkNonConformalCoupledPatchOrdering
369  (
370  {proci, nbrProci},
371  procMeshes_[proci].boundary()[nccPatchi],
372  procMeshes_[nbrProci].boundary()[nbrNccPatchi],
373  procMeshes_[proci].polyFacesBf()[nccPatchi],
374  procMeshes_[nbrProci].polyFacesBf()[nbrNccPatchi]
375  );
376  }
377 
378  // Error patches
379  if (isA<nonConformalErrorFvPatch>(fvp))
380  {
381  const label ncePatchi = patchi;
382 
383  checkNonConformalErrorPatchOrdering
384  (
385  proci,
386  procMeshes_[proci].boundary()[ncePatchi],
387  procMeshes_[proci].polyFacesBf()[ncePatchi]
388  );
389  }
390  }
391  }
392  }
393  }
394  else if (completeConformal() && !procsConformal())
395  {
396  // Map the polyFacesBf from the processor to the complete meshes and
397  // unconform the complete mesh. Set dummy data for the face
398  // geometry. This should not be used during decomposition.
399  surfaceLabelField::Boundary polyFacesBf
400  (
402  completeMesh().polyFacesBf()
403  );
404  surfaceVectorField Sf(completeMesh().Sf().cloneUnSliced());
405  surfaceVectorField Cf(completeMesh().Cf().cloneUnSliced());
406 
407  forAll(procMeshes_, proci)
408  {
409  const fvMesh& procMesh = procMeshes_[proci];
410 
411  const surfaceLabelField::Boundary& faceAddressingBf =
412  procFaceAddressingBf()[proci];
413 
414  forAll(procMesh.boundary(), procNccPatchi)
415  {
416  const fvPatch& fvp = procMesh.boundary()[procNccPatchi];
417 
418  if (isA<nonConformalFvPatch>(fvp))
419  {
420  const label completeNccPatchi =
421  isA<processorCyclicFvPatch>(fvp)
422  ? refCast<const processorCyclicFvPatch>(fvp)
423  .referPatchID()
424  : procNccPatchi;
425 
426  const label size =
427  max
428  (
429  max(mag(faceAddressingBf[procNccPatchi])),
430  polyFacesBf[completeNccPatchi].size()
431  );
432 
433  polyFacesBf[completeNccPatchi].resize(size, -1);
434  Sf.boundaryFieldRef()[completeNccPatchi].resize(size, Zero);
435  Cf.boundaryFieldRef()[completeNccPatchi].resize(size, Zero);
436 
437  polyFacesBf[completeNccPatchi].labelField::rmap
438  (
439  mag
440  (
441  labelField
442  (
443  procFaceAddressing_[proci],
444  procMesh.polyFacesBf()[procNccPatchi]
445  )
446  ) - 1,
447  mag(faceAddressingBf[procNccPatchi]) - 1
448  );
449  }
450  }
451  }
452 
453  completeMesh_->unconform
454  (
455  polyFacesBf,
456  Sf,
457  Cf,
458  NullObjectRef<surfaceScalarField>(),
459  false
460  );
461 
462  // Check ordering
463  if (debug)
464  {
465  forAll(completeMesh().boundary(), patchi)
466  {
467  const fvPatch& fvp = completeMesh().boundary()[patchi];
468 
469  // Coupled patches
470  if
471  (
472  isA<nonConformalCyclicFvPatch>(fvp)
473  && refCast<const nonConformalCoupledFvPatch>(fvp).owner()
474  )
475  {
476  const label nccPatchi = patchi;
477 
478  const label nbrNccPatchi =
479  refCast<const nonConformalCyclicFvPatch>(fvp)
480  .nbrPatchID();
481 
482  checkNonConformalCoupledPatchOrdering
483  (
484  {-labelMax, labelMax},
485  completeMesh().boundary()[nccPatchi],
486  completeMesh().boundary()[nbrNccPatchi],
487  completeMesh().polyFacesBf()[nccPatchi],
488  completeMesh().polyFacesBf()[nbrNccPatchi]
489  );
490  }
491 
492  // Error patches
493  if (isA<nonConformalErrorFvPatch>(fvp))
494  {
495  const label ncePatchi = patchi;
496 
497  checkNonConformalErrorPatchOrdering
498  (
499  -labelMax,
500  completeMesh().boundary()[ncePatchi],
501  completeMesh().polyFacesBf()[ncePatchi]
502  );
503  }
504  }
505  }
506  }
507  else // if (!completeConformal() && !procsConformal())
508  {
509  // Assume everything is consistent and do nothing
510  }
511 }
512 
513 
514 Foam::label Foam::domainDecomposition::compareInstances
515 (
516  const fileName& a,
517  const fileName& b
518 ) const
519 {
520  const word& constant = runTimes_.completeTime().constant();
521 
522  if (a == constant && b == constant) return 0;
523 
524  if (a == constant) return +1;
525 
526  if (b == constant) return -1;
527 
528  const scalar aValue = instant(a).value();
529  const scalar bValue = instant(b).value();
530 
531  if (aValue < bValue) return +1;
532 
533  if (aValue > bValue) return -1;
534 
535  return 0;
536 }
537 
538 
539 void Foam::domainDecomposition::validateComplete() const
540 {
541  if (!completeMesh_.valid())
542  {
544  << "Complete data requested but complete mesh has not been "
545  << "generated or read" << exit(FatalError);
546  }
547 }
548 
549 
550 void Foam::domainDecomposition::validateProcs() const
551 {
552  if (!procMeshes_.set(0))
553  {
555  << "Decomposed data requested but decomposed mesh has not been "
556  << "generated or read" << exit(FatalError);
557  }
558 }
559 
560 
561 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
562 
564 (
565  const processorRunTimes& runTimes,
566  const word& regionName
567 )
568 :
569  runTimes_(runTimes),
570  regionName_(regionName),
571  completeMesh_(nullptr),
572  procMeshes_(nProcs()),
573  procPointAddressing_(nProcs()),
574  procFaceAddressing_(nProcs()),
575  procCellAddressing_(nProcs()),
576  procFaceAddressingBf_()
577 {}
578 
579 
580 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
581 
583 {}
584 
585 
586 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
587 
589 {
590  completeMesh_.reset
591  (
592  new fvMesh
593  (
594  IOobject
595  (
596  regionName_,
597  runTimes_.completeTime().timeName(),
598  runTimes_.completeTime(),
601  false
602  ),
603  false
604  )
605  );
606 }
607 
608 
610 {
611  for (label proci = 0; proci < nProcs(); proci++)
612  {
613  procMeshes_.set
614  (
615  proci,
616  new fvMesh
617  (
618  IOobject
619  (
620  regionName_,
621  runTimes_.procTimes()[proci].timeName(),
622  runTimes_.procTimes()[proci],
625  false
626  ),
627  false
628  )
629  );
630  }
631 }
632 
633 
635 {
636  for (label proci = 0; proci < nProcs(); proci++)
637  {
638  const fvMesh& procMesh = procMeshes_[proci];
639 
640  procPointAddressing_[proci] =
642  (
643  IOobject
644  (
645  "pointProcAddressing",
646  procMesh.facesInstance(),
647  procMesh.meshSubDir,
648  procMesh,
651  false
652  )
653  );
654 
655  procFaceAddressing_[proci] =
657  (
658  IOobject
659  (
660  "faceProcAddressing",
661  procMesh.facesInstance(),
662  procMesh.meshSubDir,
663  procMesh,
666  false
667  )
668  );
669 
670  procCellAddressing_[proci] =
672  (
673  IOobject
674  (
675  "cellProcAddressing",
676  procMesh.facesInstance(),
677  procMesh.meshSubDir,
678  procMesh,
681  false
682  )
683  );
684  }
685 }
686 
687 
689 {
690  validateComplete();
691  validateProcs();
692 
693  // Do read-update on all meshes
695  completeMesh_->readUpdate(fvMesh::stitchType::nonGeometric);
696  forAll(runTimes_.procTimes(), proci)
697  {
698  fvMesh::readUpdateState procStat =
699  procMeshes_[proci].readUpdate(fvMesh::stitchType::nonGeometric);
700  if (procStat > stat)
701  {
702  stat = procStat;
703  }
704  }
705 
706  const label facesCompare =
707  compareInstances
708  (
709  completeMesh().facesInstance(),
710  procMeshes_[0].facesInstance()
711  );
712 
713  const label pointsCompare =
714  compareInstances
715  (
716  completeMesh().pointsInstance(),
717  procMeshes_[0].pointsInstance()
718  );
719 
720  // If the complete mesh has newer topology then we need to decompose
721  if (facesCompare == -1)
722  {
723  decompose();
724  }
725 
726  // If the processor meshes have newer topology then we need to reconstruct
727  if (facesCompare == +1)
728  {
729  reconstruct();
730  }
731 
732  // If there has been matching topology change then re-read the addressing
733  if (facesCompare == 0 && stat >= fvMesh::TOPO_CHANGE)
734  {
735  procFaceAddressingBf_.clear();
736  readAddressing();
737  }
738 
739  // If non conformal meshes have changed then clear the finite volume
740  // addressing
741  if
742  (
743  (!completeConformal() || !procsConformal())
744  && stat != fvMesh::UNCHANGED
745  )
746  {
747  procFaceAddressingBf_.clear();
748  }
749 
750  // If the complete mesh has newer points then we need to update the
751  // processor points and we might need to re-unconform the processor meshes
752  if (pointsCompare == -1)
753  {
754  if (!procsConformal())
755  {
756  forAll(procMeshes_, proci) procMeshes_[proci].conform();
757  decomposePoints();
758  unconform();
759  }
760  else
761  {
762  decomposePoints();
763  }
764  }
765 
766  // If the processor meshes have newer points then we need to update the
767  // complete points and we might need to re-unconform the complete mesh
768  if (pointsCompare == +1)
769  {
770  if (!completeConformal())
771  {
772  completeMesh_->conform();
773  reconstructPoints();
774  unconform();
775  }
776  else
777  {
778  reconstructPoints();
779  }
780  }
781 
782  return stat;
783 }
784 
785 
787 {
788  labelList result(completeMesh().nCells());
789 
790  forAll(procCellAddressing_, proci)
791  {
792  forAll(procCellAddressing_[proci], procCelli)
793  {
794  result[procCellAddressing_[proci][procCelli]] = proci;
795  }
796  }
797 
798  return result;
799 }
800 
801 
804 {
805  validateComplete();
806  validateProcs();
807 
808  if (procFaceAddressingBf_.empty())
809  {
810  // Construct cell-to-proc addressing (this is a bit wasteful, as it been
811  // available before but was thrown away)
812  const labelList cellToProc(this->cellToProc());
813 
814  // Map from reference patch and processors to the interface patch
815  typedef HashTable<label, labelPair, Hash<labelPair>> labelPairTable;
816  List<labelPairTable> refPatchProcPatchTable
817  (
818  completeMesh().boundary().size()
819  );
820  forAll(procMeshes_, proci)
821  {
822  const fvMesh& procMesh = procMeshes_[proci];
823 
824  forAll(procMesh.boundary(), procPatchi)
825  {
826  const fvPatch& fvp = procMesh.boundary()[procPatchi];
827 
828  if (isA<cyclicFvPatch>(fvp))
829  {
830  refPatchProcPatchTable[procPatchi].insert
831  (
832  labelPair(proci, proci),
833  procPatchi
834  );
835  }
836  else if (isA<processorCyclicFvPatch>(fvp))
837  {
838  const processorCyclicFvPatch& pcFvp =
839  refCast<const processorCyclicFvPatch>(fvp);
840 
841  refPatchProcPatchTable[pcFvp.referPatchID()].insert
842  (
843  labelPair(proci, pcFvp.neighbProcNo()),
844  procPatchi
845  );
846  }
847  }
848  }
849 
850  // Build non-conformal finite volume face addressing for each processor
852  nonConformalProcFaceAddressingBf(nProcs());
853  forAll(nonConformalProcFaceAddressingBf, proci)
854  {
855  nonConformalProcFaceAddressingBf[proci].resize
856  (
857  procMeshes_[proci].boundary().size()
858  );
859  }
860  if (completeMesh().conformal() && procMeshes_[0].conformal())
861  {
862  // Nothing to do
863  }
864  else if (!completeMesh().conformal())
865  {
866  // Decompose non-conformal addressing
867  const surfaceLabelField::Boundary& polyFacesBf =
868  completeMesh().polyFacesBf();
869 
870  // Cyclic patches
871  forAll(completeMesh().boundary(), nccPatchi)
872  {
873  const fvPatch& fvp = completeMesh().boundary()[nccPatchi];
874 
875  if (!isA<nonConformalCyclicFvPatch>(fvp)) continue;
876 
877  const nonConformalCyclicFvPatch& nccFvp =
878  refCast<const nonConformalCyclicFvPatch>(fvp);
879 
880  if (!nccFvp.owner()) continue;
881 
882  const label nccNbrPatchi = nccFvp.nbrPatchID();
883 
884  forAll(polyFacesBf[nccPatchi], nccPatchFacei)
885  {
886  const label facei = polyFacesBf[nccPatchi][nccPatchFacei];
887  const label celli = completeMesh().faceOwner()[facei];
888  const label proci = cellToProc[celli];
889 
890  const label nbrFacei =
891  polyFacesBf[nccNbrPatchi][nccPatchFacei];
892  const label nbrCelli =
893  completeMesh().faceOwner()[nbrFacei];
894  const label nbrProci = cellToProc[nbrCelli];
895 
896  const label procNccPatchi =
897  refPatchProcPatchTable
898  [nccPatchi][labelPair(proci, nbrProci)];
899  const label nbrProcNccPatchi =
900  refPatchProcPatchTable
901  [nccNbrPatchi][labelPair(nbrProci, proci)];
902 
903  nonConformalProcFaceAddressingBf[proci][procNccPatchi]
904  .append(nccPatchFacei + 1);
905  nonConformalProcFaceAddressingBf[nbrProci][nbrProcNccPatchi]
906  .append(nccPatchFacei + 1);
907  }
908  }
909 
910  // Error patches
911  forAll(completeMesh().boundary(), ncePatchi)
912  {
913  const fvPatch& fvp = completeMesh().boundary()[ncePatchi];
914 
915  if (!isA<nonConformalErrorFvPatch>(fvp)) continue;
916 
917  forAll(polyFacesBf[ncePatchi], ncePatchFacei)
918  {
919  const label facei = polyFacesBf[ncePatchi][ncePatchFacei];
920  const label celli = completeMesh().faceOwner()[facei];
921  const label proci = cellToProc[celli];
922 
923  nonConformalProcFaceAddressingBf[proci][ncePatchi]
924  .append(ncePatchFacei + 1);
925  }
926  }
927  }
928  else // if (!procMeshes_[0].conformal())
929  {
930  // Reconstruct non-conformal addressing
931 
932  // Cyclic patches
933  forAll(completeMesh().boundary(), nccPatchi)
934  {
935  const fvPatch& fvp = completeMesh().boundary()[nccPatchi];
936 
937  if (!isA<nonConformalCyclicFvPatch>(fvp)) continue;
938 
939  const nonConformalCyclicFvPatch& nccFvp =
940  refCast<const nonConformalCyclicFvPatch>(fvp);
941 
942  if (!nccFvp.owner()) continue;
943 
944  const label nccNbrPatchi = nccFvp.nbrPatchID();
945 
946  label count = 0;
947  labelPairTable procCounts;
949  (
950  labelPairTable,
951  refPatchProcPatchTable[nccPatchi],
952  iter
953  )
954  {
955  procCounts.insert(iter.key(), 0);
956  }
957 
958  while (true)
959  {
960  labelPair procNbrProc(labelMax, labelMax);
961  labelPair faceNbrFace(labelMax, labelMax);
962 
963  forAllConstIter(labelPairTable, procCounts, iter)
964  {
965  const label proci = iter.key().first();
966  const label nbrProci = iter.key().second();
967 
968  const labelPair procNbrProcStar(proci, nbrProci);
969  const labelPair nbrProcProcStar(nbrProci, proci);
970 
971  const label procNccPatchi =
972  refPatchProcPatchTable
973  [nccPatchi][procNbrProcStar];
974  const label nbrProcNccPatchi =
975  refPatchProcPatchTable
976  [nccNbrPatchi][nbrProcProcStar];
977 
978  const label size =
979  procMeshes_[proci]
980  .polyFacesBf()[procNccPatchi]
981  .size();
982 
983  if (iter() >= size) continue;
984 
985  const label procFacei =
986  procMeshes_[proci].polyFacesBf()
987  [procNccPatchi][iter()];
988  const label nbrProcFacei =
989  procMeshes_[nbrProci].polyFacesBf()
990  [nbrProcNccPatchi][iter()];
991 
992  const labelPair faceNbrFaceStar
993  (
994  procFaceAddressing_[proci][procFacei] - 1,
995  procFaceAddressing_[nbrProci][nbrProcFacei] - 1
996  );
997 
998  if (faceNbrFace > faceNbrFaceStar)
999  {
1000  procNbrProc = procNbrProcStar;
1001  faceNbrFace = faceNbrFaceStar;
1002  }
1003  }
1004 
1005  if (faceNbrFace == labelPair(labelMax, labelMax))
1006  {
1007  break;
1008  }
1009  else
1010  {
1011  const label proci = procNbrProc.first();
1012  const label nbrProci = procNbrProc.second();
1013 
1014  const labelPair nbrProcProc(nbrProci, proci);
1015 
1016  const label procNccPatchi =
1017  refPatchProcPatchTable[nccPatchi][procNbrProc];
1018  const label nbrProcNccPatchi =
1019  refPatchProcPatchTable[nccNbrPatchi][nbrProcProc];
1020 
1021  nonConformalProcFaceAddressingBf
1022  [proci][procNccPatchi].append(count + 1);
1023  nonConformalProcFaceAddressingBf
1024  [nbrProci][nbrProcNccPatchi].append(count + 1);
1025 
1026  count ++;
1027  procCounts[procNbrProc] ++;
1028  }
1029  }
1030  }
1031 
1032  // Error patches
1033  forAll(completeMesh().boundary(), ncePatchi)
1034  {
1035  const fvPatch& fvp = completeMesh().boundary()[ncePatchi];
1036 
1037  if (!isA<nonConformalErrorFvPatch>(fvp)) continue;
1038 
1039  label count = 0;
1040  labelList procCounts(nProcs(), 0);
1041 
1042  while (true)
1043  {
1044  label facei = labelMax, proci = labelMax;
1045 
1046  forAll(procCounts, procStari)
1047  {
1048  const label size =
1049  procMeshes_[procStari]
1050  .polyFacesBf()[ncePatchi]
1051  .size();
1052 
1053  if (procCounts[procStari] >= size) continue;
1054 
1055  const label procFacei =
1056  procMeshes_[procStari].polyFacesBf()
1057  [ncePatchi][procCounts[procStari]];
1058 
1059  const label faceStari =
1060  procFaceAddressing_[procStari][procFacei] - 1;
1061 
1062  if (facei > faceStari)
1063  {
1064  facei = faceStari;
1065  proci = procStari;
1066  }
1067  }
1068 
1069  if (facei == labelMax)
1070  {
1071  break;
1072  }
1073  else
1074  {
1075  nonConformalProcFaceAddressingBf
1076  [proci][ncePatchi].append(count + 1);
1077 
1078  count ++;
1079  procCounts[proci] ++;
1080  }
1081  }
1082  }
1083  }
1084 
1085  // Build finite volume face addressing boundary fields
1086  procFaceAddressingBf_.resize(nProcs());
1087  forAll(procMeshes_, proci)
1088  {
1089  const fvMesh& procMesh = procMeshes_[proci];
1090 
1091  procFaceAddressingBf_.set
1092  (
1093  proci,
1095  (
1096  procMesh.boundary(),
1098  calculatedFvsPatchLabelField::typeName
1099  )
1100  );
1101 
1102  forAll(procMesh.boundary(), procPatchi)
1103  {
1104  const fvPatch& fvp = procMesh.boundary()[procPatchi];
1105 
1106  if (isA<nonConformalFvPatch>(fvp))
1107  {
1108  procFaceAddressingBf_[proci][procPatchi] =
1109  nonConformalProcFaceAddressingBf[proci][procPatchi];
1110  }
1111  else if (isA<processorCyclicFvPatch>(fvp))
1112  {
1113  const label completePatchi =
1114  refCast<const processorCyclicFvPatch>(fvp)
1115  .referPatchID();
1116 
1117  procFaceAddressingBf_[proci][procPatchi] =
1118  mag(fvp.patchSlice(procFaceAddressing_[proci]))
1119  - completeMesh().boundaryMesh()[completePatchi].start();
1120  }
1121  else if (isA<processorFvPatch>(fvp))
1122  {
1123  procFaceAddressingBf_[proci][procPatchi] =
1124  fvp.patchSlice(procFaceAddressing_[proci]);
1125  }
1126  else
1127  {
1128  procFaceAddressingBf_[proci][procPatchi] =
1129  mag(fvp.patchSlice(procFaceAddressing_[proci]))
1130  - completeMesh().boundaryMesh()[procPatchi].start();
1131  }
1132  }
1133  }
1134  }
1135 
1136  return procFaceAddressingBf_;
1137 }
1138 
1139 
1141 {
1142  for (label proci = 0; proci < nProcs(); proci++)
1143  {
1144  const fvMesh& procMesh = procMeshes_[proci];
1145 
1146  labelIOList pointProcAddressing
1147  (
1148  IOobject
1149  (
1150  "pointProcAddressing",
1151  procMesh.facesInstance(),
1152  procMesh.meshSubDir,
1153  procMesh,
1156  ),
1157  procPointAddressing_[proci]
1158  );
1159  pointProcAddressing.write();
1160 
1161  labelIOList faceProcAddressing
1162  (
1163  IOobject
1164  (
1165  "faceProcAddressing",
1166  procMesh.facesInstance(),
1167  procMesh.meshSubDir,
1168  procMesh,
1171  ),
1172  procFaceAddressing_[proci]
1173  );
1174  faceProcAddressing.write();
1175 
1176  labelIOList cellProcAddressing
1177  (
1178  IOobject
1179  (
1180  "cellProcAddressing",
1181  procMesh.facesInstance(),
1182  procMesh.meshSubDir,
1183  procMesh,
1186  ),
1187  procCellAddressing_[proci]
1188  );
1189  cellProcAddressing.write();
1190  }
1191 }
1192 
1193 
1194 void Foam::domainDecomposition::writeComplete(const bool doSets) const
1195 {
1196  // Set the precision of the points data to be min 10
1198 
1199  // Write the processor mesh
1200  completeMesh().write();
1201 
1202  // Sets
1203  if (doSets)
1204  {
1205  // Scan to find all sets
1206  HashTable<label> cSetNames;
1207  HashTable<label> fSetNames;
1208  HashTable<label> pSetNames;
1209  for (label proci = 0; proci < nProcs(); proci++)
1210  {
1211  const fvMesh& procMesh = procMeshes_[proci];
1212 
1214  (
1215  procMesh,
1216  runTimes_.procTimes()[proci].timeName(),
1217  polyMesh::meshSubDir/"sets"
1218  );
1219 
1220  IOobjectList cSets(objects.lookupClass(cellSet::typeName));
1221  forAllConstIter(IOobjectList, cSets, iter)
1222  {
1223  cSetNames.insert(iter.key(), cSetNames.size());
1224  }
1225 
1226  IOobjectList fSets(objects.lookupClass(faceSet::typeName));
1227  forAllConstIter(IOobjectList, fSets, iter)
1228  {
1229  fSetNames.insert(iter.key(), fSetNames.size());
1230  }
1231  IOobjectList pSets(objects.lookupClass(pointSet::typeName));
1232  forAllConstIter(IOobjectList, pSets, iter)
1233  {
1234  pSetNames.insert(iter.key(), pSetNames.size());
1235  }
1236  }
1237 
1238  // Reconstruct all sets
1239  if (cSetNames.size() || fSetNames.size() || pSetNames.size())
1240  {
1241  PtrList<cellSet> cellSets(cSetNames.size());
1242  PtrList<faceSet> faceSets(fSetNames.size());
1243  PtrList<pointSet> pointSets(pSetNames.size());
1244 
1245  Info<< "Reconstructing sets:" << endl;
1246  if (cSetNames.size())
1247  {
1248  Info<< " cellSets "
1249  << cSetNames.sortedToc() << endl;
1250  }
1251  if (fSetNames.size())
1252  {
1253  Info<< " faceSets "
1254  << fSetNames.sortedToc() << endl;
1255  }
1256  if (pSetNames.size())
1257  {
1258  Info<< " pointSets "
1259  << pSetNames.sortedToc() << endl;
1260  }
1261 
1262  // Load sets
1263  for (label proci = 0; proci < nProcs(); proci++)
1264  {
1265  const fvMesh& procMesh = procMeshes_[proci];
1266 
1268  (
1269  procMesh,
1270  runTimes_.procTimes()[proci].timeName(),
1271  polyMesh::meshSubDir/"sets"
1272  );
1273 
1274  // cellSets
1275  const labelList& cellMap = procCellAddressing()[proci];
1276 
1277  IOobjectList cSets
1278  (
1279  objects.lookupClass(cellSet::typeName)
1280  );
1281 
1282  forAllConstIter(IOobjectList, cSets, iter)
1283  {
1284  // Load cellSet
1285  const cellSet procSet(*iter());
1286  label setI = cSetNames[iter.key()];
1287  if (!cellSets.set(setI))
1288  {
1289  cellSets.set
1290  (
1291  setI,
1292  new cellSet
1293  (
1294  completeMesh(),
1295  iter.key(),
1296  procSet.size()
1297  )
1298  );
1299  }
1300  cellSet& cSet = cellSets[setI];
1301  cSet.instance() = runTimes_.completeTime().timeName();
1302 
1303  forAllConstIter(cellSet, procSet, iter)
1304  {
1305  cSet.insert(cellMap[iter.key()]);
1306  }
1307  }
1308 
1309  // faceSets
1310  const labelList& faceMap = procFaceAddressing()[proci];
1311 
1312  IOobjectList fSets
1313  (
1314  objects.lookupClass(faceSet::typeName)
1315  );
1316 
1317  forAllConstIter(IOobjectList, fSets, iter)
1318  {
1319  // Load faceSet
1320  const faceSet procSet(*iter());
1321  label setI = fSetNames[iter.key()];
1322  if (!faceSets.set(setI))
1323  {
1324  faceSets.set
1325  (
1326  setI,
1327  new faceSet
1328  (
1329  completeMesh(),
1330  iter.key(),
1331  procSet.size()
1332  )
1333  );
1334  }
1335  faceSet& fSet = faceSets[setI];
1336  fSet.instance() = runTimes_.completeTime().timeName();
1337 
1338  forAllConstIter(faceSet, procSet, iter)
1339  {
1340  fSet.insert(mag(faceMap[iter.key()]) - 1);
1341  }
1342  }
1343 
1344  // pointSets
1345  const labelList& pointMap = procPointAddressing()[proci];
1346 
1347  IOobjectList pSets
1348  (
1349  objects.lookupClass(pointSet::typeName)
1350  );
1351 
1352  forAllConstIter(IOobjectList, pSets, iter)
1353  {
1354  // Load pointSet
1355  const pointSet propSet(*iter());
1356  label setI = pSetNames[iter.key()];
1357  if (!pointSets.set(setI))
1358  {
1359  pointSets.set
1360  (
1361  setI,
1362  new pointSet
1363  (
1364  completeMesh(),
1365  iter.key(),
1366  propSet.size()
1367  )
1368  );
1369  }
1370  pointSet& pSet = pointSets[setI];
1371  pSet.instance() = runTimes_.completeTime().timeName();
1372 
1373  forAllConstIter(pointSet, propSet, iter)
1374  {
1375  pSet.insert(pointMap[iter.key()]);
1376  }
1377  }
1378  }
1379 
1380  // Write sets
1381  forAll(cellSets, i)
1382  {
1383  cellSets[i].write();
1384  }
1385  forAll(faceSets, i)
1386  {
1387  faceSets[i].write();
1388  }
1389  forAll(pointSets, i)
1390  {
1391  pointSets[i].write();
1392  }
1393  }
1394  }
1395 
1396  // Refinement data (if any)
1397  UPtrList<const labelList> cellMaps(nProcs());
1398  UPtrList<const labelList> pointMaps(nProcs());
1399  PtrList<const hexRef8Data> refinementDatas(nProcs());
1400  for (label proci = 0; proci < nProcs(); proci++)
1401  {
1402  const fvMesh& procMesh = procMeshes_[proci];
1403 
1404  cellMaps.set(proci, &procCellAddressing_[proci]);
1405  pointMaps.set(proci, &procPointAddressing_[proci]);
1406  refinementDatas.set
1407  (
1408  proci,
1409  new hexRef8Data
1410  (
1411  IOobject
1412  (
1413  "dummy",
1414  completeMesh().facesInstance(),
1416  procMesh,
1419  false
1420  )
1421  )
1422  );
1423  }
1424  hexRef8Data
1425  (
1426  IOobject
1427  (
1428  "dummy",
1429  completeMesh().facesInstance(),
1431  completeMesh(),
1434  false
1435  ),
1436  cellMaps,
1437  pointMaps,
1438  refinementDatas
1439  ).write();
1440 
1441  // Write decomposition addressing
1442  writeAddressing();
1443 }
1444 
1445 
1446 void Foam::domainDecomposition::writeProcs(const bool doSets) const
1447 {
1448  // Read sets
1451  PtrList<const pointSet> pointSets;
1452  if (doSets)
1453  {
1455  (
1456  completeMesh(),
1457  completeMesh().facesInstance(),
1458  "polyMesh/sets"
1459  );
1460  {
1461  IOobjectList cSets(objects.lookupClass(cellSet::typeName));
1462  forAllConstIter(IOobjectList, cSets, iter)
1463  {
1464  cellSets.append(new cellSet(*iter()));
1465  }
1466  }
1467  {
1468  IOobjectList fSets(objects.lookupClass(faceSet::typeName));
1469  forAllConstIter(IOobjectList, fSets, iter)
1470  {
1471  faceSets.append(new faceSet(*iter()));
1472  }
1473  }
1474  {
1475  IOobjectList pSets(objects.lookupClass(pointSet::typeName));
1476  forAllConstIter(IOobjectList, pSets, iter)
1477  {
1478  pointSets.append(new pointSet(*iter()));
1479  }
1480  }
1481  }
1482 
1483  // Read refinement data (if any)
1485  (
1486  IOobject
1487  (
1488  "dummy",
1489  completeMesh().facesInstance(),
1491  completeMesh(),
1494  false
1495  )
1496  );
1497 
1498  // Write out the meshes
1499  for (label proci = 0; proci < nProcs(); proci++)
1500  {
1501  const fvMesh& procMesh = procMeshes_[proci];
1502 
1503  // Set the precision of the points data to be min 10
1505 
1506  // Write the processor mesh
1507  procMesh.write();
1508 
1509  // Write any sets
1510  if (doSets)
1511  {
1512  forAll(cellSets, i)
1513  {
1514  const cellSet& cs = cellSets[i];
1515  cellSet set(procMesh, cs.name(), cs.size()/nProcs());
1516  forAll(procCellAddressing_[proci], i)
1517  {
1518  if (cs.found(procCellAddressing_[proci][i]))
1519  {
1520  set.insert(i);
1521  }
1522  }
1523  set.write();
1524  }
1525  forAll(faceSets, i)
1526  {
1527  const faceSet& cs = faceSets[i];
1528  faceSet set(procMesh, cs.name(), cs.size()/nProcs());
1529  forAll(procFaceAddressing_[proci], i)
1530  {
1531  if (cs.found(mag(procFaceAddressing_[proci][i])-1))
1532  {
1533  set.insert(i);
1534  }
1535  }
1536  set.write();
1537  }
1538  forAll(pointSets, i)
1539  {
1540  const pointSet& cs = pointSets[i];
1541  pointSet set(procMesh, cs.name(), cs.size()/nProcs());
1542  forAll(procPointAddressing_[proci], i)
1543  {
1544  if (cs.found(procPointAddressing_[proci][i]))
1545  {
1546  set.insert(i);
1547  }
1548  }
1549  set.write();
1550  }
1551  }
1552 
1553  // Write refinement data (if any)
1554  hexRef8Data
1555  (
1556  IOobject
1557  (
1558  "dummy",
1559  completeMesh().facesInstance(),
1561  procMesh,
1564  false
1565  ),
1566  refinementData,
1567  procCellAddressing_[proci],
1568  procPointAddressing_[proci]
1569  ).write();
1570  }
1571 
1572  // Write decomposition addressing
1573  writeAddressing();
1574 }
1575 
1576 
1577 // ************************************************************************* //
void writeProcs(const bool doSets) const
Write the decomposed meshes and associated data.
void append(T *)
Append an element at the end of the list.
Definition: PtrListI.H:39
Field< label > labelField
Specialisation of Field<T> for label.
Definition: labelField.H:49
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
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.
const word & name() const
Return name.
Definition: IOobject.H:315
A list of face labels.
Definition: faceSet.H:48
bool set(const label) const
Is element set.
Definition: PtrListI.H:65
List of IOobjects with searching and retrieving facilities.
Definition: IOobjectList.H:50
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
const fileName & facesInstance() const
Return the current instance directory for faces.
Definition: polyMesh.C:888
A set of point labels.
Definition: pointSet.H:48
error FatalError
virtual label nbrPatchID() const
Return neighbour.
Definition: cyclicFvPatch.H:96
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:306
#define forAllConstIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:477
labelList cellToProc() const
Return the distribution as an FV field for writing.
static word meshSubDir
Return the mesh sub-directory name (usually "polyMesh")
Definition: polyMesh.H:328
static unsigned int defaultPrecision()
Return the default precision.
Definition: IOstream.H:458
GeometricField< vector, fvsPatchField, surfaceMesh > surfaceVectorField
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
label count(const ListType &l, typename ListType::const_reference x)
Count the number of occurrences of a value in a list.
Non-conformal cyclic FV patch. As nonConformalCoupledFvPatch, but the neighbouring patch is local and...
GeometricBoundaryField< Type, PatchField, GeoMesh > Boundary
Type of the boundary field.
virtual ~domainDecomposition()
Destructor.
void resize(const label)
Alias for setSize(const label)
Definition: ListI.H:138
virtual bool write(const bool write=true) const
Write mesh using IO settings from time.
Definition: fvMesh.C:1658
bool insert(const Key &key)
Insert a new entry.
Definition: HashSet.H:111
label size() const
Return number of elements in table.
Definition: HashTableI.H:65
Pair< int > faceMap(const label facePi, const face &faceP, const label faceNi, const face &faceN)
virtual int neighbProcNo() const
Return neighbour processor number.
bool insert(const Key &, const T &newElmt)
Insert a new hashedEntry.
Definition: HashTableI.H:80
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:42
An ordered pair of two objects of type <T> with first() and second() elements.
Definition: contiguous.H:49
const pointField & points
bool found(const Key &) const
Return true if hashedEntry is found in table.
Definition: HashTable.C:113
A class for handling words, derived from string.
Definition: word.H:59
label nPoints
void append(const T &)
Append an element at the end of the list.
Definition: ListI.H:178
fvMesh::readUpdateState readUpdate()
...
Pair< label > labelPair
Label pair.
Definition: labelPair.H:48
static const label labelMax
Definition: label.H:62
List< label > labelList
A List of labels.
Definition: labelList.H:56
static const zero Zero
Definition: zero.H:97
faceListList boundary(nPatches)
An STL-conforming hash table.
Definition: HashTable.H:61
const PtrList< surfaceLabelField::Boundary > & procFaceAddressingBf() const
Access the labels of faces for each processor (see notes above)
A templated 1D list of pointers to objects of type <T>, where the size of the array is known and used...
Definition: UPtrList.H:54
domainDecomposition(const processorRunTimes &procRunTimes, const word &regionName)
Construct from processor run times and region name.
bool write() const
Write.
Definition: hexRef8Data.C:372
void writeComplete(const bool doSets) const
Write the decomposed meshes and associated data.
bool set(const label) const
Is element set.
Definition: UPtrListI.H:78
objects
defineTypeNameAndDebug(combustionModel, 0)
const List< T >::subList patchSlice(const List< T > &l) const
Slice list to patch.
Definition: fvPatch.H:191
Generic GeometricBoundaryField class.
Definition: fvMesh.H:80
const Type & second() const
Return second.
Definition: Pair.H:110
static const GeometricField< Type, PatchField, GeoMesh > & null()
Return a null geometric field.
Transfers refinement levels such that slow transition between levels is maintained. Used in FaceCellWave.
void writeAddressing() const
Write the decomposition addressing.
IOobjectList lookupClass(const word &className) const
Return the list for all IOobjects of a given class.
Definition: IOobjectList.C:190
bool set(const Key &, const T &newElmt)
Assign a new hashedEntry, overwriting existing entries.
Definition: HashTableI.H:91
tmp< GeometricField< typename outerProduct< vector, Type >::type, fvPatchField, volMesh >> reconstruct(const GeometricField< Type, fvsPatchField, surfaceMesh > &ssf)
label patchi
virtual bool owner() const
Does this side own the patch ?
A templated 1D list of pointers to objects of type <T>, where the size of the array is known and used...
Definition: List.H:70
A collection of cell labels.
Definition: cellSet.H:48
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:95
List< Key > sortedToc() const
Return the table of contents as a sorted list.
Definition: HashTable.C:217
messageStream Info
dimensioned< scalar > mag(const dimensioned< Type > &)
Various for reading/decomposing/reconstructing/distributing refinement data.
Definition: hexRef8Data.H:57
fileName & instance() const
Return the instance directory, constant, system, <time> etc.
Definition: IOobject.C:355
readUpdateState
Enumeration defining the state of the mesh after a read update.
Definition: polyMesh.H:90
virtual bool write(const bool write=true) const
Write using setting from DB.
IOobject defines the attributes of an object for which implicit objectRegistry management is supporte...
Definition: IOobject.H:98
const Type & first() const
Return first.
Definition: Pair.H:98
label referPatchID() const
Return the referring patch ID.
IOList< label > labelIOList
Label container classes.
Definition: labelIOList.H:42
Namespace for OpenFOAM.
const fvBoundaryMesh & boundary() const
Return reference to boundary mesh.
Definition: fvMesh.C:800
#define InfoInFunction
Report an information message using Foam::Info.