lduPrimitiveMesh.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2013-2017 OpenFOAM Foundation
9  Copyright (C) 2019-2023 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "lduPrimitiveMesh.H"
30 #include "processorLduInterface.H"
32 #include "edgeHashes.H"
33 #include "labelPair.H"
34 #include "processorGAMGInterface.H"
35 
36 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
37 
38 namespace Foam
39 {
41 }
42 
43 
44 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
45 
47 (
48  const label size,
49  const labelUList& l,
50  const labelUList& u
51 )
52 {
53  forAll(l, facei)
54  {
55  if (u[facei] < l[facei])
56  {
58  << "Reversed face. Problem at face " << facei
59  << " l:" << l[facei] << " u:" << u[facei]
60  << abort(FatalError);
61  }
62  if (l[facei] < 0 || u[facei] < 0 || u[facei] >= size)
63  {
65  << "Illegal cell label. Problem at face " << facei
66  << " l:" << l[facei] << " u:" << u[facei]
67  << abort(FatalError);
68  }
69  }
70 
71  for (label facei=1; facei < l.size(); ++facei)
72  {
73  if (l[facei-1] > l[facei])
74  {
76  << "Lower not in incremental cell order."
77  << " Problem at face " << facei
78  << " l:" << l[facei] << " u:" << u[facei]
79  << " previous l:" << l[facei-1]
80  << abort(FatalError);
81  }
82  else if (l[facei-1] == l[facei])
83  {
84  // Same cell.
85  if (u[facei-1] > u[facei])
86  {
88  << "Upper not in incremental cell order."
89  << " Problem at face " << facei
90  << " l:" << l[facei] << " u:" << u[facei]
91  << " previous u:" << u[facei-1]
92  << abort(FatalError);
93  }
94  }
95  }
96 }
97 
98 
100 (
101  const lduMesh& mesh,
102  const globalIndex& globalNumbering
103 )
104 {
105  const lduAddressing& addr = mesh.lduAddr();
106  lduInterfacePtrsList interfaces = mesh.interfaces();
107 
108  const label myProci = UPstream::myProcNo(mesh.comm());
109 
110  const labelList globalIndices
111  (
113  (
114  addr.size(),
115  globalNumbering.localStart(myProci)
116  )
117  );
118 
119  // Get the interface cells
120  PtrList<labelList> nbrGlobalCells(interfaces.size());
121  {
122  const label startOfRequests = UPstream::nRequests();
123 
124  // Initialise transfer of restrict addressing on the interface
125  forAll(interfaces, inti)
126  {
127  if (interfaces.set(inti))
128  {
129  interfaces[inti].initInternalFieldTransfer
130  (
132  globalIndices
133  );
134  }
135  }
136 
137  UPstream::waitRequests(startOfRequests);
138 
139  forAll(interfaces, inti)
140  {
141  if (interfaces.set(inti))
142  {
143  nbrGlobalCells.set
144  (
145  inti,
146  new labelList
147  (
148  interfaces[inti].internalFieldTransfer
149  (
151  globalIndices
152  )
153  )
154  );
155  }
156  }
157  }
158 
159 
160  // Scan the neighbour list to find out how many times the cell
161  // appears as a neighbour of the face. Done this way to avoid guessing
162  // and resizing list
163  labelList nNbrs(addr.size(), Zero);
164 
165  const labelUList& nbr = addr.upperAddr();
166  const labelUList& own = addr.lowerAddr();
167 
168  {
169  forAll(nbr, facei)
170  {
171  nNbrs[nbr[facei]]++;
172  nNbrs[own[facei]]++;
173  }
174 
175  forAll(interfaces, inti)
176  {
177  if (interfaces.set(inti))
178  {
179  for (const label celli : interfaces[inti].faceCells())
180  {
181  nNbrs[celli]++;
182  }
183  }
184  }
185  }
186 
187 
188  // Create cell-cells addressing
189  labelListList cellCells(addr.size());
190 
191  forAll(cellCells, celli)
192  {
193  cellCells[celli].setSize(nNbrs[celli], -1);
194  }
195 
196  // Reset the list of number of neighbours to zero
197  nNbrs = 0;
198 
199  // Scatter the neighbour faces
200  forAll(nbr, facei)
201  {
202  const label c0 = own[facei];
203  const label c1 = nbr[facei];
204 
205  cellCells[c0][nNbrs[c0]++] = globalIndices[c1];
206  cellCells[c1][nNbrs[c1]++] = globalIndices[c0];
207  }
208  forAll(interfaces, inti)
209  {
210  if (interfaces.set(inti))
211  {
212  const labelUList& faceCells = interfaces[inti].faceCells();
213 
214  forAll(faceCells, facei)
215  {
216  const label c0 = faceCells[facei];
217  cellCells[c0][nNbrs[c0]++] = nbrGlobalCells[inti][facei];
218  }
219  }
220  }
221 
222  return cellCells;
223 }
224 
225 
226 Foam::label Foam::lduPrimitiveMesh::totalSize
227 (
228  const PtrList<lduPrimitiveMesh>& meshes
229 )
230 {
231  label size = 0;
232 
233  for (const lduPrimitiveMesh& msh : meshes)
234  {
235  size += msh.lduAddr().size();
236  }
237  return size;
238 }
239 
240 
242 (
243  const label nCells,
244  const labelUList& lower,
245  const labelUList& upper
246 )
247 {
248  labelList nNbrs(nCells, Zero);
249 
250  // Count number of upper neighbours
251  forAll(lower, facei)
252  {
253  if (upper[facei] < lower[facei])
254  {
256  << "Problem at face:" << facei
257  << " lower:" << lower[facei]
258  << " upper:" << upper[facei]
259  << exit(FatalError);
260  }
261  nNbrs[lower[facei]]++;
262  }
263 
264  // Construct cell-upper cell addressing
265  labelList offsets(nCells+1);
266  offsets[0] = 0;
267  forAll(nNbrs, celli)
268  {
269  offsets[celli+1] = offsets[celli]+nNbrs[celli];
270  }
271 
272  nNbrs = offsets;
273 
274  labelList cellToFaces(offsets.last());
275  forAll(upper, facei)
276  {
277  const label celli = lower[facei];
278  cellToFaces[nNbrs[celli]++] = facei;
279  }
280 
281  // Sort
282 
283  labelList oldToNew(lower.size());
284 
285  DynamicList<label> order;
286  DynamicList<label> nbr;
287 
288  label newFacei = 0;
289 
290  for (label celli = 0; celli < nCells; ++celli)
291  {
292  const label startOfCell = offsets[celli];
293  const label nNbr = offsets[celli+1] - startOfCell;
294 
295  nbr.resize(nNbr);
296  order.resize(nNbr);
297 
298  forAll(nbr, i)
299  {
300  nbr[i] = upper[cellToFaces[offsets[celli]+i]];
301  }
302  sortedOrder(nbr, order);
303 
304  for (const label index : order)
305  {
306  oldToNew[cellToFaces[startOfCell + index]] = newFacei++;
307  }
308  }
309 
310  return oldToNew;
311 }
312 
313 
314 Foam::label Foam::lduPrimitiveMesh::findConnectedInterface
315 (
316  const lduMesh& myMesh,
317  const PtrList<lduPrimitiveMesh>& otherMeshes,
318  const labelPairList& procAndInterfaces,
319 
320  const label nbrProci,
321  const label myRank
322 ) const
323 {
324  // Find mesh, interfacei in procAndInterfaces
325  label nbrInti = -1;
326  for (const auto& procAndInterface : procAndInterfaces)
327  {
328  const label proci = procAndInterface[0];
329 
330  if (proci == nbrProci)
331  {
332  const label interfacei = procAndInterface[1];
333  const lduInterfacePtrsList interfaces =
334  mesh
335  (
336  myMesh,
337  otherMeshes,
338  proci
339  ).interfaces();
340  const processorLduInterface& pldui =
341  refCast<const processorLduInterface>
342  (
343  interfaces[interfacei]
344  );
345 
346  if (pldui.neighbProcNo() == myRank)
347  {
348  nbrInti = procAndInterface[1];
349  break;
350  }
351  }
352  }
353 
354 
355  if (nbrInti == -1)
356  {
358  << "procAndInterfaces:" << procAndInterfaces << abort(FatalError);
359  }
360  return nbrInti;
361 }
362 
363 
364 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
365 
366 Foam::lduPrimitiveMesh::lduPrimitiveMesh
367 (
368  const label nCells,
369  labelList& l,
370  labelList& u,
371  const label comm,
372  bool reuse
373 )
374 :
375  lduAddressing(nCells),
376  lowerAddr_(l, reuse),
377  upperAddr_(u, reuse),
378  comm_(comm)
379 {
380  if (debug && lowerAddr_.size())
381  {
382  if (max(lowerAddr_) >= nCells || min(lowerAddr_) < 0)
383  {
384  FatalErrorInFunction << "Illegal lower addressing."
385  << " nCells:" << nCells
386  << " max(lower):" << max(lowerAddr_)
387  << " min(lower):" << min(lowerAddr_)
388  << exit(FatalError);
389  }
390  }
391  if (debug && upperAddr_.size())
392  {
393  if (max(upperAddr_) >= nCells || min(upperAddr_) < 0)
394  {
395  FatalErrorInFunction << "Illegal upper addressing."
396  << " nCells:" << nCells
397  << " max(upper):" << max(upperAddr_)
398  << " min(upper):" << min(upperAddr_)
400  }
401  }
402 }
403 
404 
406 (
407  lduInterfacePtrsList& interfaces,
408  const lduSchedule& ps
409 )
410 {
411  interfaces_ = interfaces;
412  patchSchedule_ = ps;
413 
414  // Create interfaces
415  primitiveInterfaces_.setSize(interfaces_.size());
416  forAll(interfaces_, i)
417  {
418  if (interfaces_.set(i))
419  {
420  primitiveInterfaces_.set(i, &interfaces_[i]);
421  }
422  }
423 }
424 
425 
426 Foam::lduPrimitiveMesh::lduPrimitiveMesh
427 (
428  const label nCells
429 )
430 :
431  lduAddressing(nCells),
432  comm_(0)
433 {}
434 
435 
436 Foam::lduPrimitiveMesh::lduPrimitiveMesh
437 (
438  const label nCells,
439  labelList& l,
440  labelList& u,
441  PtrList<const lduInterface>& primitiveInterfaces,
442  const lduSchedule& ps,
443  const label comm
444 )
445 :
446  lduAddressing(nCells),
447  lowerAddr_(l, true),
448  upperAddr_(u, true),
449  primitiveInterfaces_(),
450  patchSchedule_(ps),
451  comm_(comm)
452 {
453  if (debug && lowerAddr_.size())
454  {
455  if (max(lowerAddr_) >= nCells || min(lowerAddr_) < 0)
456  {
457  FatalErrorInFunction << "Illegal lower addressing."
458  << " nCells:" << nCells
459  << " max(lower):" << max(lowerAddr_)
460  << " min(lower):" << min(lowerAddr_)
461  << exit(FatalError);
462  }
463  }
464  if (debug && upperAddr_.size())
465  {
466  if (max(upperAddr_) >= nCells || min(upperAddr_) < 0)
467  {
468  FatalErrorInFunction << "Illegal upper addressing."
469  << " nCells:" << nCells
470  << " max(upper):" << max(upperAddr_)
471  << " min(upper):" << min(upperAddr_)
472  << exit(FatalError);
473  }
474  }
475 
476 
477  primitiveInterfaces_.transfer(primitiveInterfaces);
478 
479  // Create interfaces
480  interfaces_.setSize(primitiveInterfaces_.size());
481  forAll(primitiveInterfaces_, i)
482  {
483  if (primitiveInterfaces_.set(i))
484  {
485  interfaces_.set(i, &primitiveInterfaces_[i]);
486  }
487  }
488 }
489 
490 
491 Foam::lduPrimitiveMesh::lduPrimitiveMesh
492 (
493  const label comm,
494  const labelList& procAgglomMap,
495 
496  const labelList& procIDs,
497  const lduMesh& myMesh,
498  const PtrList<lduPrimitiveMesh>& otherMeshes,
499 
500  labelList& cellOffsets,
501  labelList& faceOffsets,
503  labelListList& boundaryMap,
504  labelListListList& boundaryFaceMap
505 )
506 :
507  lduAddressing(myMesh.lduAddr().size() + totalSize(otherMeshes)),
508  comm_(comm)
509 {
510  const label currentComm = myMesh.comm();
511 
512  forAll(otherMeshes, i)
513  {
514  if (otherMeshes[i].comm() != currentComm)
515  {
517  << "Communicator " << otherMeshes[i].comm()
518  << " at index " << i
519  << " differs from that of predecessor "
520  << currentComm
521  << endl; //exit(FatalError);
522  }
523  }
524 
525  const label nMeshes = otherMeshes.size()+1;
526 
527  const label myAgglom = procAgglomMap[UPstream::myProcNo(currentComm)];
529  {
530  Pout<< "I am " << UPstream::myProcNo(currentComm)
531  << " agglomerating into " << myAgglom
532  << " as are " << findIndices(procAgglomMap, myAgglom)
533  << endl;
534  }
535 
536  const lduInterfacePtrsList myInterfaces = myMesh.interfaces();
537 
538  forAll(procIDs, i)
539  {
540  if (procAgglomMap[procIDs[i]] != procAgglomMap[procIDs[0]])
541  {
543  << "Processor " << procIDs[i]
544  << " agglomerates to " << procAgglomMap[procIDs[i]]
545  << " whereas other processors " << procIDs
546  << " agglomerate to "
547  << labelUIndList(procAgglomMap, procIDs)
548  << exit(FatalError);
549  }
550  }
551 
552 
553  // Cells get added in order.
554  cellOffsets.setSize(nMeshes+1);
555  cellOffsets[0] = 0;
556  for (label procMeshI = 0; procMeshI < nMeshes; ++procMeshI)
557  {
558  const lduMesh& procMesh = mesh(myMesh, otherMeshes, procMeshI);
559 
560  cellOffsets[procMeshI+1] =
561  cellOffsets[procMeshI]
562  + procMesh.lduAddr().size();
563  }
564 
565 
566  // Faces initially get added in order, sorted later
567  labelList internalFaceOffsets(nMeshes+1);
568  internalFaceOffsets[0] = 0;
569  for (label procMeshI = 0; procMeshI < nMeshes; ++procMeshI)
570  {
571  const lduMesh& procMesh = mesh(myMesh, otherMeshes, procMeshI);
572 
573  internalFaceOffsets[procMeshI+1] =
574  internalFaceOffsets[procMeshI]
575  + procMesh.lduAddr().lowerAddr().size();
576  }
577 
578  // Count how faces get added. Proc interfaces inbetween get merged.
579 
580  // Merged proc interfaces: map from two coarse processors back to
581  // - procMeshes
582  // - interface in procMesh
583  // (estimate size from number of patches of mesh0)
584  EdgeMap<labelPairList> mergedMap(2*myInterfaces.size());
585 
586  // Unmerged proc interfaces: map from two coarse processors back to
587  // - procMeshes
588  // - interface in procMesh
589  EdgeMap<labelPairList> unmergedMap(mergedMap.size());
590 
591  // (unmerged) global interfaces. These are present on all processors
592  // in the same order (and keep the order in the merged mesh)
593  List<DynamicList<label>> procToGlobal(nMeshes);
594 
595 
596  boundaryMap.setSize(nMeshes);
597  boundaryFaceMap.setSize(nMeshes);
598 
599 
600  labelList nCoupledFaces(nMeshes, Zero);
601 
602  for (label procMeshI = 0; procMeshI < nMeshes; ++procMeshI)
603  {
605  mesh(myMesh, otherMeshes, procMeshI).interfaces();
606 
607  // Initialise all boundaries as merged
608  boundaryMap[procMeshI].setSize(interfaces.size(), -1);
609  boundaryFaceMap[procMeshI].setSize(interfaces.size());
610 
611  // Get sorted order of processors
612  forAll(interfaces, intI)
613  {
614  if (interfaces.set(intI))
615  {
616  const lduInterface& ldui = interfaces[intI];
617 
618  if (isA<processorLduInterface>(ldui))
619  {
620  if (isA<processorCyclicGAMGInterface>(ldui))
621  {
623  << "At mesh from processor " << procIDs[procMeshI]
624  << " have interface " << intI
625  << " of unhandled type " << ldui.type()
626  << ". Adapt decomposition to avoid these"
627  << exit(FatalError);
628  }
629 
630  const processorLduInterface& pldui =
631  refCast<const processorLduInterface>(ldui);
632 
633  label agglom0 = procAgglomMap[pldui.myProcNo()];
634  label agglom1 = procAgglomMap[pldui.neighbProcNo()];
635 
636  const edge procEdge(agglom0, agglom1);
637 
638  if (agglom0 != myAgglom && agglom1 != myAgglom)
639  {
641  << "At mesh from processor " << procIDs[procMeshI]
642  << " have interface " << intI
643  << " with myProcNo:" << pldui.myProcNo()
644  << " with neighbProcNo:" << pldui.neighbProcNo()
645  << exit(FatalError);
646  }
647  else if (agglom0 == myAgglom && agglom1 == myAgglom)
648  {
649  // Merged interface
650  if (debug)
651  {
652  Pout<< "merged proc interface: myProcNo:"
653  << pldui.myProcNo()
654  << " nbr:" << pldui.neighbProcNo()
655  << " size:" << ldui.faceCells().size()
656  << endl;
657  }
658 
659  const label nbrProcMeshI =
660  procIDs.find(pldui.neighbProcNo());
661 
662  if (procMeshI < nbrProcMeshI)
663  {
664  // I am 'master' since get lowest numbered cells
665  nCoupledFaces[procMeshI] += ldui.faceCells().size();
666  }
667 
668  mergedMap(procEdge).append
669  (
670  labelPair(procMeshI, intI)
671  );
672  }
673  else
674  {
675  if (debug)
676  {
677  Pout<< "external proc interface: myProcNo:"
678  << pldui.myProcNo()
679  << " nbr:" << pldui.neighbProcNo()
680  << " size:" << ldui.faceCells().size()
681  << endl;
682  }
683 
684  unmergedMap(procEdge).append
685  (
686  labelPair(procMeshI, intI)
687  );
688  }
689  }
690  else
691  {
692  // Still external (non proc) interface
693  procToGlobal[procMeshI].append(intI);
694  }
695  }
696  }
697  }
698 
699 
700  // Check that all processors have any global patches in the same order
701  if (debug)
702  {
703  const auto& global0 = procToGlobal[0];
704  for (label procMeshI = 1; procMeshI < nMeshes; ++procMeshI)
705  {
706  const auto& global = procToGlobal[procMeshI];
707  if (global != global0)
708  {
710  << "At mesh from processor " << procIDs[procMeshI]
711  << " have global interfaces " << global
712  << " which differ from those on processor "
713  << procIDs[procMeshI]
714  << " : " << global0
715  << exit(FatalError);
716  }
717  }
718  }
719 
720 
721  if (debug)
722  {
723  Pout<< "Global interfaces:" << endl;
724  const auto& global0 = procToGlobal[0];
725  for (const label intI : global0)
726  {
727  Pout<< " interfacei:" << intI
728  << " type:" << myInterfaces[intI].type()
729  << endl;
730 
731  }
732  Pout<< endl;
733 
734  Pout<< "Remaining interfaces:" << endl;
735  for (const auto& iter : unmergedMap.csorted())
736  {
737  Pout<< " agglom procEdge:" << iter.key() << endl;
738  const labelPairList& elems = iter.val();
739  forAll(elems, i)
740  {
741  label procMeshI = elems[i][0];
742  label interfacei = elems[i][1];
744  mesh(myMesh, otherMeshes, procMeshI).interfaces();
745 
746  const processorLduInterface& pldui =
747  refCast<const processorLduInterface>
748  (
749  interfaces[interfacei]
750  );
751 
752  Pout<< " proc:" << procIDs[procMeshI]
753  << " interfacei:" << interfacei
754  << " between:" << pldui.myProcNo()
755  << " and:" << pldui.neighbProcNo()
756  << " localsize:"
757  << interfaces[interfacei].faceCells().size()
758  << endl;
759  }
760  Pout<< endl;
761  }
762  Pout<< endl;
763 
764  Pout<< "Merged interfaces:" << endl;
765  //forAllConstIters(mergedMap, iter)
766  for (const auto& iter : mergedMap.csorted())
767  {
768  Pout<< " agglom procEdge:" << iter.key() << endl;
769  const labelPairList& elems = iter.val();
770 
771  forAll(elems, i)
772  {
773  label procMeshI = elems[i][0];
774  label interfacei = elems[i][1];
776  mesh(myMesh, otherMeshes, procMeshI).interfaces();
777  const processorLduInterface& pldui =
778  refCast<const processorLduInterface>
779  (
780  interfaces[interfacei]
781  );
782 
783  Pout<< " proc:" << procIDs[procMeshI]
784  << " interfacei:" << interfacei
785  << " between:" << pldui.myProcNo()
786  << " and:" << pldui.neighbProcNo()
787  << " localsize:"
788  << interfaces[interfacei].faceCells().size()
789  << endl;
790  }
791  Pout<< endl;
792  }
793  Pout<< endl;
794  }
795 
796 
797  // Adapt faceOffsets for internal interfaces
798  faceOffsets.setSize(nMeshes+1);
799  faceOffsets[0] = 0;
800  faceMap.setSize(nMeshes);
801  for (label procMeshI = 0; procMeshI < nMeshes; ++procMeshI)
802  {
803  const lduMesh& procMesh = mesh(myMesh, otherMeshes, procMeshI);
804  label nInternal = procMesh.lduAddr().lowerAddr().size();
805 
806  faceOffsets[procMeshI+1] =
807  faceOffsets[procMeshI]
808  + nInternal
809  + nCoupledFaces[procMeshI];
810 
811  labelList& map = faceMap[procMeshI];
812  map.setSize(nInternal);
813  forAll(map, i)
814  {
815  map[i] = faceOffsets[procMeshI] + i;
816  }
817  }
818 
819 
820  // Combine upper and lower
821  lowerAddr_.setSize(faceOffsets.last(), -1);
822  upperAddr_.setSize(lowerAddr_.size(), -1);
823 
824 
825  // Old internal faces and resolved coupled interfaces
826  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
827 
828  for (label procMeshI = 0; procMeshI < nMeshes; ++procMeshI)
829  {
830  const lduMesh& procMesh = mesh(myMesh, otherMeshes, procMeshI);
831 
832  const labelUList& l = procMesh.lduAddr().lowerAddr();
833  const labelUList& u = procMesh.lduAddr().upperAddr();
834 
835  // Add internal faces
836  label allFacei = faceOffsets[procMeshI];
837 
838  forAll(l, facei)
839  {
840  lowerAddr_[allFacei] = cellOffsets[procMeshI]+l[facei];
841  upperAddr_[allFacei] = cellOffsets[procMeshI]+u[facei];
842  allFacei++;
843  }
844 
845 
846  // Add merged interfaces
847  const lduInterfacePtrsList interfaces = procMesh.interfaces();
848 
849  forAll(interfaces, intI)
850  {
851  if (interfaces.set(intI))
852  {
853  const lduInterface& ldui = interfaces[intI];
854 
855  if (isA<processorLduInterface>(ldui))
856  {
857  const processorLduInterface& pldui =
858  refCast<const processorLduInterface>(ldui);
859 
860  // Look up corresponding interfaces
861  label myP = pldui.myProcNo();
862  label nbrP = pldui.neighbProcNo();
863  label nbrProcMeshI = procIDs.find(nbrP);
864 
865  if (procMeshI < nbrProcMeshI)
866  {
867  // I am 'master' since my cell numbers will be lower
868  // since cells get added in procMeshI order.
869 
870  const label agglom0 = procAgglomMap[myP];
871  const label agglom1 = procAgglomMap[nbrP];
872 
873  const auto fnd =
874  mergedMap.cfind(edge(agglom0, agglom1));
875 
876  if (fnd.good())
877  {
878  const labelPairList& elems = fnd();
879 
880  const label nbrIntI = findConnectedInterface
881  (
882  myMesh,
883  otherMeshes,
884  elems,
885  nbrProcMeshI,
886  procIDs[procMeshI]
887  );
888 
889  const lduInterfacePtrsList nbrInterfaces = mesh
890  (
891  myMesh,
892  otherMeshes,
893  nbrProcMeshI
894  ).interfaces();
895 
896 
897  const labelUList& faceCells =
898  ldui.faceCells();
899  const labelUList& nbrFaceCells =
900  nbrInterfaces[nbrIntI].faceCells();
901 
902  if (faceCells.size() != nbrFaceCells.size())
903  {
905  << "faceCells:" << faceCells
906  << " nbrFaceCells:" << nbrFaceCells
907  << abort(FatalError);
908  }
909 
910 
911  labelList& bfMap =
912  boundaryFaceMap[procMeshI][intI];
913  labelList& nbrBfMap =
914  boundaryFaceMap[nbrProcMeshI][nbrIntI];
915 
916  bfMap.setSize(faceCells.size());
917  nbrBfMap.setSize(faceCells.size());
918 
919  forAll(faceCells, pfI)
920  {
921  lowerAddr_[allFacei] =
922  cellOffsets[procMeshI]+faceCells[pfI];
923  bfMap[pfI] = allFacei;
924  upperAddr_[allFacei] =
925  cellOffsets[nbrProcMeshI]+nbrFaceCells[pfI];
926  nbrBfMap[pfI] = (-allFacei-1);
927  allFacei++;
928  }
929  }
930  }
931  }
932  }
933  }
934  }
935 
936 
937  // Sort upper-tri order
938  {
939  labelList oldToNew
940  (
942  (
943  cellOffsets.last(), //nCells
944  lowerAddr_,
945  upperAddr_
946  )
947  );
948 
949  forAll(faceMap, procMeshI)
950  {
951  labelList& map = faceMap[procMeshI];
952  forAll(map, i)
953  {
954  if (map[i] >= 0)
955  {
956  map[i] = oldToNew[map[i]];
957  }
958  else
959  {
960  label allFacei = -map[i]-1;
961  map[i] = -oldToNew[allFacei]-1;
962  }
963  }
964  }
965 
966 
967  inplaceReorder(oldToNew, lowerAddr_);
968  inplaceReorder(oldToNew, upperAddr_);
969 
970  forAll(boundaryFaceMap, proci)
971  {
972  const labelList& bMap = boundaryMap[proci];
973  forAll(bMap, intI)
974  {
975  if (bMap[intI] == -1)
976  {
977  // Merged interface
978  labelList& bfMap = boundaryFaceMap[proci][intI];
979 
980  forAll(bfMap, i)
981  {
982  if (bfMap[i] >= 0)
983  {
984  bfMap[i] = oldToNew[bfMap[i]];
985  }
986  else
987  {
988  label allFacei = -bfMap[i]-1;
989  bfMap[i] = (-oldToNew[allFacei]-1);
990  }
991  }
992  }
993  }
994  }
995  }
996 
997 
998  // Kept interfaces
999  // ~~~~~~~~~~~~~~~
1000 
1001  interfaces_.setSize(unmergedMap.size() + procToGlobal[0].size());
1002  primitiveInterfaces_.setSize(interfaces_.size());
1003 
1004  label allInterfacei = 0;
1005 
1006 
1007  // Global interfaces
1008  // ~~~~~~~~~~~~~~~~~
1009  // (e.g. cyclicAMI)
1010 
1011  {
1012  const auto& global0 = procToGlobal[0];
1013 
1014  for (const label interfacei : global0)
1015  {
1016  //Pout<< " interfacei:" << interfacei
1017  // << " type:" << interfaces[interfacei].type()
1018  // << endl;
1019 
1020  // Just add all individual face-cells in processor order
1021 
1022  // Count
1023  label n = 0;
1024  for (label procMeshI = 0; procMeshI < nMeshes; ++procMeshI)
1025  {
1026  const auto& procMesh = mesh(myMesh, otherMeshes, procMeshI);
1027  n += procMesh.interfaces()[interfacei].faceCells().size();
1028  }
1029 
1030  // Size
1031  labelField allFaceCells(n);
1032  labelField allFaceRestrictAddressing(n);
1033  labelList faceOffsets(nMeshes+1, 0);
1034  lduInterfacePtrsList allProcInterfaces(nMeshes);
1035  n = 0;
1036 
1037  // Fill
1038  for (label procMeshI = 0; procMeshI < nMeshes; ++procMeshI)
1039  {
1040  faceOffsets[procMeshI] = n;
1041 
1042  const auto& procMesh = mesh(myMesh, otherMeshes, procMeshI);
1043  const lduInterfacePtrsList interfaces = procMesh.interfaces();
1044 
1045  allProcInterfaces.set(procMeshI, &interfaces[interfacei]);
1046  boundaryMap[procMeshI][interfacei] = allInterfacei;
1047  labelList& bfMap = boundaryFaceMap[procMeshI][interfacei];
1048 
1049  const labelUList& l = interfaces[interfacei].faceCells();
1050  bfMap.setSize(l.size());
1051 
1052  forAll(l, facei)
1053  {
1054  allFaceCells[n] = cellOffsets[procMeshI]+l[facei];
1055  allFaceRestrictAddressing[n] = n;
1056  bfMap[facei] = n;
1057  n++;
1058  }
1059  }
1060 
1061  // For convenience populate last element with size
1062  faceOffsets.last() = n;
1063 
1064 
1065  const auto& myFineInterface =
1066  refCast<const GAMGInterface>(myInterfaces[interfacei]);
1067 
1068  autoPtr<GAMGInterface> ppPtr
1069  (
1070  myFineInterface.clone
1071  (
1072  allInterfacei,
1073  interfaces_,
1074  global0,
1075  allFaceCells,
1076  allFaceRestrictAddressing,
1077  faceOffsets,
1078  allProcInterfaces,
1079  comm_,
1080  myAgglom,
1081  procAgglomMap
1082  )
1083  );
1084 
1085  primitiveInterfaces_.set
1086  (
1087  allInterfacei,
1088  ppPtr.ptr()
1089  );
1090 
1091  interfaces_.set(allInterfacei, &primitiveInterfaces_[allInterfacei]);
1092 
1093  if (debug)
1094  {
1095  Pout<< "Created " << interfaces_[allInterfacei].type()
1096  << " interface at " << allInterfacei
1097  << " comm:" << comm_
1098  << " myProcNo:" << myAgglom
1099  << " nFaces:" << allFaceCells.size()
1100  << endl;
1101  }
1102 
1103  allInterfacei++;
1104  }
1105  }
1106 
1107 
1108  // Unmerged processor interfaces
1109  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1110 
1111  //forAllConstIters(unmergedMap, iter)
1112  for (const auto& iter : unmergedMap.csorted())
1113  {
1114  const labelPairList& elems = iter.val();
1115 
1116  // Sort processors in increasing order so both sides walk through in
1117  // same order.
1118  labelPairList procPairs(elems.size());
1119  forAll(elems, i)
1120  {
1121  const labelPair& elem = elems[i];
1122  label procMeshI = elem[0];
1123  label interfacei = elem[1];
1125  (
1126  myMesh,
1127  otherMeshes,
1128  procMeshI
1129  ).interfaces();
1130 
1131  const processorLduInterface& pldui =
1132  refCast<const processorLduInterface>
1133  (
1134  interfaces[interfacei]
1135  );
1136  label myProcNo = pldui.myProcNo();
1137  label nbrProcNo = pldui.neighbProcNo();
1138  procPairs[i] = labelPair
1139  (
1140  min(myProcNo, nbrProcNo),
1141  max(myProcNo, nbrProcNo)
1142  );
1143  }
1144 
1145  labelList order(sortedOrder(procPairs));
1146 
1147  // Count
1148  label n = 0;
1149 
1150  forAll(order, i)
1151  {
1152  const labelPair& elem = elems[order[i]];
1153  label procMeshI = elem[0];
1154  label interfacei = elem[1];
1156  (
1157  myMesh,
1158  otherMeshes,
1159  procMeshI
1160  ).interfaces();
1161 
1162  n += interfaces[interfacei].faceCells().size();
1163  }
1164 
1165  // Size
1166  labelField allFaceCells(n);
1167  labelField allFaceRestrictAddressing(n);
1168  n = 0;
1169 
1170  // Fill
1171  forAll(order, i)
1172  {
1173  const labelPair& elem = elems[order[i]];
1174  label procMeshI = elem[0];
1175  label interfacei = elem[1];
1177  (
1178  myMesh,
1179  otherMeshes,
1180  procMeshI
1181  ).interfaces();
1182 
1183  boundaryMap[procMeshI][interfacei] = allInterfacei;
1184  labelList& bfMap = boundaryFaceMap[procMeshI][interfacei];
1185 
1186  const labelUList& l = interfaces[interfacei].faceCells();
1187  bfMap.setSize(l.size());
1188 
1189  forAll(l, facei)
1190  {
1191  allFaceCells[n] = cellOffsets[procMeshI]+l[facei];
1192  allFaceRestrictAddressing[n] = n;
1193  bfMap[facei] = n;
1194  n++;
1195  }
1196  }
1197 
1198 
1199  // Find out local and remote processor in new communicator
1200 
1201  label neighbProcNo = -1;
1202 
1203  // See what the two processors map onto
1204 
1205  if (iter.key()[0] == myAgglom)
1206  {
1207  if (iter.key()[1] == myAgglom)
1208  {
1210  << "problem procEdge:" << iter.key()
1211  << exit(FatalError);
1212  }
1213 
1214  neighbProcNo = iter.key()[1];
1215  }
1216  else
1217  {
1218  if (iter.key()[1] != myAgglom)
1219  {
1221  << "problem procEdge:" << iter.key()
1222  << exit(FatalError);
1223  }
1224 
1225  neighbProcNo = iter.key()[0];
1226  }
1227 
1228  primitiveInterfaces_.set
1229  (
1230  allInterfacei,
1231  new processorGAMGInterface
1232  (
1233  allInterfacei,
1234  interfaces_,
1235  allFaceCells,
1236  allFaceRestrictAddressing,
1237  comm_,
1238  myAgglom,
1239  neighbProcNo,
1240  tensorField(), // forwardT
1241  Pstream::msgType() // tag
1242  )
1243  );
1244  interfaces_.set(allInterfacei, &primitiveInterfaces_[allInterfacei]);
1245 
1246  if (debug)
1247  {
1248  Pout<< "Created " << interfaces_[allInterfacei].type()
1249  << " interface at " << allInterfacei
1250  << " comm:" << comm_
1251  << " myProcNo:" << myAgglom
1252  << " neighbProcNo:" << neighbProcNo
1253  << " nFaces:" << allFaceCells.size()
1254  << endl;
1255  }
1256 
1257 
1258  allInterfacei++;
1259  }
1260 
1261 
1262  if (allInterfacei != interfaces_.size())
1263  {
1264  FatalErrorInFunction << "allInterfacei:" << allInterfacei
1265  << " interfaces_:" << interfaces_.size() << exit(FatalError);
1266  }
1267 
1268 
1269  if (debug)
1270  {
1271  Pout<< endl
1272  << "Created new lduPrimitiveMesh:" << nl
1273  << " cells:" << this->lduAddr().size() << nl
1274  << " internal face lower:"
1275  << this->lduAddr().lowerAddr().size() << nl
1276  << " internal faces upper:"
1277  << this->lduAddr().upperAddr().size()
1278  << endl;
1279  forAll(interfaces_, i)
1280  {
1281  if (interfaces_.set(i))
1282  {
1283  Pout<< " interface:" << i << " type:" << interfaces_[i].type()
1284  << nl
1285  << " faceCells:" << flatOutput(interfaces_[i].faceCells())
1286  << endl;
1287  }
1288  }
1289 
1290 
1291  Pout<< "Original input meshes:" << endl;
1292  forAll(boundaryMap, procMeshI)
1293  {
1294  const auto& procMesh = mesh(myMesh, otherMeshes, procMeshI);
1295  const lduInterfacePtrsList interfaces = procMesh.interfaces();
1296 
1297  Pout<< " proc:" << procMeshI
1298  << " interfaces:" << interfaces.size() << endl;
1299  forAll(interfaces, inti)
1300  {
1301  if (interfaces.set(inti))
1302  {
1303  Pout<< " int:" << inti
1304  << " type:" << interfaces[inti].type()
1305  << " size:" << interfaces[inti].faceCells().size()
1306  << " maps to:" << boundaryMap[procMeshI][inti]
1307  << endl;
1308  }
1309  }
1310  }
1311  }
1312 
1313 
1314  patchSchedule_ = nonBlockingSchedule<processorGAMGInterface>(interfaces_);
1315 
1316  if (debug)
1317  {
1318  checkUpperTriangular(cellOffsets.last(), lowerAddr_, upperAddr_);
1319  }
1320 }
1321 
1322 
1323 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
1324 
1326 (
1327  const lduMesh& myMesh,
1328  const PtrList<lduPrimitiveMesh>& otherMeshes,
1329  const label meshI
1331 {
1332  return (meshI == 0 ? myMesh : otherMeshes[meshI-1]);
1333 }
1334 
1335 
1337 (
1338  const label comm,
1339  const lduMesh& mesh,
1340  PtrList<lduPrimitiveMesh>& otherMeshes
1341 )
1342 {
1343  // Force calculation of schedule (since does parallel comms)
1344  (void)mesh.lduAddr().patchSchedule();
1345 
1346  // Use PstreamBuffers
1347  PstreamBuffers pBufs(comm);
1348 
1349  // Send to master
1350  if (!Pstream::master(comm))
1351  {
1352  const lduAddressing& addressing = mesh.lduAddr();
1353 
1354  lduInterfacePtrsList interfaces(mesh.interfaces());
1355  boolList validInterface(interfaces.size());
1356  forAll(interfaces, intI)
1357  {
1358  validInterface[intI] = interfaces.set(intI);
1359  }
1360 
1361  UOPstream toMaster(UPstream::masterNo(), pBufs);
1362 
1363  toMaster
1364  << addressing.size()
1365  << addressing.lowerAddr()
1366  << addressing.upperAddr()
1367  << validInterface;
1368 
1369  forAll(interfaces, intI)
1370  {
1371  if (interfaces.set(intI))
1372  {
1373  const GAMGInterface& interface = refCast<const GAMGInterface>
1374  (
1375  interfaces[intI]
1376  );
1377 
1378  toMaster << interface.type();
1379  interface.write(toMaster);
1380  }
1381  }
1382  }
1383 
1384  // Wait for finish
1385  pBufs.finishedGathers();
1386 
1387  // Consume
1388  if (Pstream::master(comm))
1389  {
1390  const label nProcs = UPstream::nProcs(comm);
1391 
1392  // Master.
1393  otherMeshes.setSize(nProcs-1);
1394 
1395  for (const int proci : UPstream::subProcs(comm))
1396  {
1397  UIPstream fromProc(proci, pBufs);
1398 
1399  const label nCells = readLabel(fromProc);
1400  labelList lowerAddr(fromProc);
1401  labelList upperAddr(fromProc);
1402  const boolList validInterface(fromProc);
1403 
1404 
1405  // Construct mesh without interfaces
1406  otherMeshes.set
1407  (
1408  proci-1,
1409  new lduPrimitiveMesh
1410  (
1411  nCells,
1412  lowerAddr,
1413  upperAddr,
1414  mesh.comm(),
1415  true // reuse
1416  )
1417  );
1418 
1419  // Construct GAMGInterfaces
1420  lduInterfacePtrsList newInterfaces(validInterface.size());
1421  forAll(validInterface, intI)
1422  {
1423  if (validInterface[intI])
1424  {
1425  word coupleType(fromProc);
1426 
1427  newInterfaces.set
1428  (
1429  intI,
1431  (
1432  coupleType,
1433  intI,
1434  otherMeshes[proci-1].rawInterfaces(),
1435  fromProc
1436  ).ptr()
1437  );
1438  }
1439  }
1440 
1441  otherMeshes[proci-1].addInterfaces
1442  (
1443  newInterfaces,
1444  nonBlockingSchedule<processorGAMGInterface>
1445  (
1446  newInterfaces
1447  )
1448  );
1449  }
1450  }
1451 }
1452 
1453 
1454 // ************************************************************************* //
virtual lduInterfacePtrsList interfaces() const =0
Return a list of pointers for each patch with only those pointing to interfaces being set...
Field< label > labelField
Specialisation of Field<T> for label.
Definition: labelField.H:48
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:116
static autoPtr< GAMGInterface > New(const label index, const lduInterfacePtrsList &coarseInterfaces, const lduInterface &fineInterface, const labelField &localRestrictAddressing, const labelField &neighbourRestrictAddressing, const label fineLevelIndex, const label coarseComm)
Return a pointer to a new interface created on freestore given.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
void addInterfaces(lduInterfacePtrsList &interfaces, const lduSchedule &ps)
Add interfaces to a mesh constructed without.
void resize(const label len)
Adjust allocated size of list.
Definition: ListI.H:153
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:608
labelList sortedOrder(const UList< T > &input)
Return the (stable) sort order for the list.
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:40
static label nRequests() noexcept
Number of outstanding requests (on the internal list of requests)
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
Type & refCast(U &obj)
A dynamic_cast (for references) to Type reference.
Definition: typeInfo.H:172
void inplaceReorder(const labelUList &oldToNew, ListType &input, const bool prune=false)
Inplace reorder the elements of a list.
UIndirectList< label > labelUIndList
UIndirectList of labels.
Definition: IndirectList.H:65
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
Abstract base class for meshes which provide LDU addressing for the construction of lduMatrix and LDU...
Definition: lduMesh.H:53
label readLabel(const char *buf)
Parse entire buffer as a label, skipping leading/trailing whitespace.
Definition: label.H:63
string upper(const std::string &s)
Return string copy transformed with std::toupper on each character.
Definition: stringOps.C:1187
PtrList< const lduInterface > & primitiveInterfaces()
Return a non-const list of primitive interfaces.
labelList findIndices(const ListType &input, typename ListType::const_reference val, label start=0)
Linear search to find all occurrences of given element.
static int & msgType() noexcept
Message tag of standard messages.
Definition: UPstream.H:1252
interfaceProperties interface(alpha1, U, thermo->transportPropertiesDict())
List< lduScheduleEntry > lduSchedule
A List of lduSchedule entries.
Definition: lduSchedule.H:46
List< labelPair > labelPairList
List of labelPair.
Definition: labelPair.H:33
static int myProcNo(const label communicator=worldComm)
Rank of this process in the communicator (starting from masterNo()). Can be negative if the process i...
Definition: UPstream.H:1086
List< labelList > labelListList
List of labelList.
Definition: labelList.H:38
virtual const labelUList & lowerAddr() const =0
Return lower addressing.
static void waitRequests()
Wait for all requests to finish.
Definition: UPstream.H:1561
UList< label > labelUList
A UList of labels.
Definition: UList.H:78
Pair< int > faceMap(const label facePi, const face &faceP, const label faceNi, const face &faceN)
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
static label nProcs(const label communicator=worldComm)
Number of ranks in parallel run (for given communicator). It is 1 for serial run. ...
Definition: UPstream.H:1077
virtual label comm() const
Return communicator used for parallel communication.
Definition: fvMesh.H:415
void setSize(const label n)
Alias for resize()
Definition: List.H:320
dynamicFvMesh & mesh
label size() const noexcept
Return number of equations.
labelList identity(const label len, label start=0)
Return an identity map of the given length with (map[i] == i), works like std::iota() but returning a...
Definition: labelLists.C:44
virtual const labelUList & upperAddr() const =0
Return upper addressing.
virtual const lduAddressing & lduAddr() const
Return ldu addressing.
Definition: fvMesh.C:752
static constexpr int masterNo() noexcept
Relative rank for the master process - is always 0.
Definition: UPstream.H:1071
label size() const noexcept
The number of entries in the list.
Definition: UPtrListI.H:106
virtual label comm() const
Return communicator used for parallel communication.
virtual const lduAddressing & lduAddr() const
Return ldu addressing.
UPtrList< const lduInterface > lduInterfacePtrsList
Store lists of lduInterface as a UPtrList.
Foam::PtrList< Foam::fvMesh > meshes(regionNames.size())
Simplest concrete lduMesh that stores the addressing needed by lduMatrix.
List< labelListList > labelListListList
List of labelListList.
Definition: labelList.H:41
static const lduMesh & mesh(const lduMesh &mesh0, const PtrList< lduPrimitiveMesh > &otherMeshes, const label meshI)
Select either mesh0 (meshI is 0) or otherMeshes[meshI-1].
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:26
errorManip< error > abort(error &err)
Definition: errorManip.H:139
const T * set(const label i) const
Return const pointer to element (can be nullptr), or nullptr for out-of-range access (ie...
Definition: PtrList.H:159
static void gather(const label agglomComm, const lduMesh &mesh, PtrList< lduPrimitiveMesh > &otherMeshes)
Gather meshes from other processors using agglomComm.
int debug
Static debugging option.
Pair< label > labelPair
A pair of labels.
Definition: Pair.H:51
defineTypeNameAndDebug(combustionModel, 0)
Buffers for inter-processor communications streams (UOPstream, UIPstream).
void setSize(const label newLen)
Same as resize()
Definition: PtrList.H:337
Field< tensor > tensorField
Specialisation of Field<T> for tensor.
string lower(const std::string &s)
Return string copy transformed with std::tolower on each character.
Definition: stringOps.C:1171
#define WarningInFunction
Report a warning using Foam::Warning.
virtual lduInterfacePtrsList interfaces() const
Return a list of pointers for each patch.
Definition: fvMesh.C:769
static void checkUpperTriangular(const label size, const labelUList &l, const labelUList &u)
Check if in upper-triangular ordering.
A list of pointers to objects of type <T>, with allocation/deallocation management of the pointers...
Definition: List.H:55
static labelListList globalCellCells(const lduMesh &mesh, const globalIndex &globalNumbering)
Calculate global cell-cells.
static bool master(const label communicator=worldComm)
True if process corresponds to the master rank in the communicator.
Definition: UPstream.H:1094
const dimensionedScalar c1
First radiation constant: default SI units: [W/m2].
"nonBlocking" (immediate) : (MPI_Isend, MPI_Irecv)
label n
The class contains the addressing required by the lduMatrix: upper, lower and losort.
static rangeType subProcs(const label communicator=worldComm)
Range of process indices for sub-processes.
Definition: UPstream.H:1197
List< label > labelList
A List of labels.
Definition: List.H:62
void setSize(const label n)
Alias for resize()
Definition: UPtrList.H:840
virtual lduInterfacePtrsList interfaces() const
Return a list of pointers for each patch with only those pointing to interfaces being set...
List< bool > boolList
A List of bools.
Definition: List.H:60
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
static labelList upperTriOrder(const label nCells, const labelUList &lower, const labelUList &upper)
Calculate upper-triangular order.
Namespace for OpenFOAM.
virtual const lduSchedule & patchSchedule() const =0
Return patch field evaluation schedule.
const T * set(const label i) const
Return const pointer to element (can be nullptr), or nullptr for out-of-range access (ie...
Definition: UPtrList.H:366
FlatOutput::OutputAdaptor< Container, Delimiters > flatOutput(const Container &obj, Delimiters delim)
Global flatOutput() function with specified output delimiters.
Definition: FlatOutput.H:225
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127