splitMeshRegions.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) 2011-2017 OpenFOAM Foundation
9  Copyright (C) 2015-2025 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 Application
28  splitMeshRegions
29 
30 Group
31  grpMeshManipulationUtilities
32 
33 Description
34  Splits mesh into multiple regions.
35 
36  Each region is defined as a domain whose cells can all be reached by
37  cell-face-cell walking without crossing
38  - boundary faces
39  - additional faces from faceset (-blockedFaces faceSet).
40  - any face between differing cellZones (-cellZones)
41 
42  Output is:
43  - volScalarField with regions as different scalars (-detectOnly)
44  or
45  - mesh with multiple regions and mapped patches. By default, a single
46  patch covers the entire interface between two regions. Alternatively,
47  the interface can be subdivided into multiple patches according to all
48  existing faceZones (-useFaceZones) or only explicitly specified
49  faceZones (-useSelectedFaceZones).
50  or
51  - mesh with cells put into cellZones (-makeCellZones)
52 
53  Note:
54 
55  - multiple cellZones can be combined into a single region (cluster)
56  for further analysis using the 'addZones' or 'combineZones' option:
57  -addZones '((allSolids zoneA "zoneB.*")(allFluids none otherZone))'
58  or
59  -combineZones '((zoneA "zoneB.*")(none otherZone))
60  This can be combined with e.g. 'cellZones' or 'cellZonesOnly'. The
61  addZones option supplies the destination region name as first element in
62  the list. The combineZones option synthesises the region name e.g.
63  zoneA_zoneB0_zoneB1
64  unless custom names are explicitly provided using the '-customRegionNames'
65  option (e.g. -customRegionNames '(solid fluid)').
66 
67  - cellZonesOnly does not do a walk and uses the cellZones only. Use
68  this if you don't mind having disconnected domains in a single region.
69  This option requires all cells to be in one (and one only) cellZone.
70 
71  - cellZonesFileOnly behaves like -cellZonesOnly but reads the cellZones
72  from the specified file. This allows one to explicitly specify the region
73  distribution and still have multiple cellZones per region.
74 
75  - prefixRegion prefixes all normal patches with region name (interface
76  (patches already have region name prefix)
77 
78  - Should work in parallel.
79  cellZones can differ on either side of processor boundaries in which case
80  the faces get moved from processor patch to mapped patch. Not very well
81  tested.
82 
83  - If a cell zone gets split into more than one region it can detect
84  the largest matching region (-sloppyCellZones). This will accept any
85  region that covers more than 50% of the zone. It has to be a subset
86  so cannot have any cells in any other zone.
87 
88  - If explicitly a single region has been selected (-largestOnly or
89  -insidePoint) its region name will be either
90  - name of a cellZone it matches to or
91  - "largestOnly" respectively "insidePoint" or
92  - polyMesh::defaultRegion if additionally -overwrite
93  (so it will overwrite the input mesh!)
94 
95  - writes maps like decomposePar back to original mesh:
96  - pointRegionAddressing : for every point in this region the point in
97  the original mesh
98  - cellRegionAddressing : ,, cell ,, cell ,,
99  - faceRegionAddressing : ,, face ,, face in
100  the original mesh + 'turning index'. For a face in the same orientation
101  this is the original facelabel+1, for a turned face this is -facelabel-1
102  - boundaryRegionAddressing : for every patch in this region the
103  patch in the original mesh (or -1 if added patch)
104 
105 \*---------------------------------------------------------------------------*/
106 
107 #include "argList.H"
108 #include "regionSplit.H"
109 #include "fvMeshSubset.H"
110 #include "IOobjectList.H"
111 #include "volFields.H"
112 #include "faceSet.H"
113 #include "cellSet.H"
114 #include "polyTopoChange.H"
115 #include "removeCells.H"
116 #include "edgeHashes.H"
117 #include "syncTools.H"
118 #include "ReadFields.H"
119 #include "mappedWallPolyPatch.H"
120 #include "fvMeshTools.H"
121 #include "processorMeshes.H"
122 
123 using namespace Foam;
124 
125 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
126 
127 // Prepend prefix to selected patches.
128 void renamePatches
129 (
130  fvMesh& mesh,
131  const word& prefix,
132  const labelList& patchesToRename
133 )
134 {
135  polyBoundaryMesh& polyPatches =
136  const_cast<polyBoundaryMesh&>(mesh.boundaryMesh());
137  forAll(patchesToRename, i)
138  {
139  label patchi = patchesToRename[i];
140  polyPatch& pp = polyPatches[patchi];
141 
142  if (isA<coupledPolyPatch>(pp))
143  {
145  << "Encountered coupled patch " << pp.name()
146  << ". Will only rename the patch itself,"
147  << " not any referred patches."
148  << " This might have to be done by hand."
149  << endl;
150  }
151 
152  pp.name() = prefix + '_' + pp.name();
153  }
154  // Recalculate any demand driven data (e.g. group to name lookup)
155  polyPatches.updateMesh();
156 }
157 
158 
159 template<class GeoField>
160 void subsetVolFields
161 (
162  const fvMesh& mesh,
163  const fvMesh& subMesh,
164  const labelList& cellMap,
165  const labelList& faceMap,
166  const labelHashSet& addedPatches
167 )
168 {
169  const labelList patchMap(identity(mesh.boundaryMesh().size()));
170 
171  for (const GeoField& fld : mesh.objectRegistry::csorted<GeoField>())
172  {
173  Info<< "Mapping field " << fld.name() << endl;
174 
175  tmp<GeoField> tSubFld
176  (
178  (
179  fld,
180  subMesh,
181  patchMap,
182  cellMap,
183  faceMap
184  )
185  );
186 
187  // Hack: set value to 0 for introduced patches (since don't
188  // get initialised.
189  forAll(tSubFld().boundaryField(), patchi)
190  {
191  if (addedPatches.found(patchi))
192  {
193  tSubFld.ref().boundaryFieldRef()[patchi] == Zero;
194  }
195  }
196 
197  // Store on subMesh
198  GeoField* subFld = tSubFld.ptr();
199  subFld->rename(fld.name());
200  subFld->writeOpt(IOobject::AUTO_WRITE);
201  subFld->store();
202  }
203 }
204 
205 
206 template<class GeoField>
207 void subsetSurfaceFields
208 (
209  const fvMesh& mesh,
210  const fvMesh& subMesh,
211  const labelList& cellMap,
212  const labelList& faceMap,
213  const labelHashSet& addedPatches
214 )
215 {
216  const labelList patchMap(identity(mesh.boundaryMesh().size()));
217 
218  for (const GeoField& fld : mesh.objectRegistry::csorted<GeoField>())
219  {
220  Info<< "Mapping field " << fld.name() << endl;
221 
222  tmp<GeoField> tSubFld
223  (
225  (
226  fld,
227  subMesh,
228  patchMap,
229  cellMap,
230  faceMap
231  )
232  );
233 
234  // Hack: set value to 0 for introduced patches (since don't
235  // get initialised.
236  forAll(tSubFld().boundaryField(), patchi)
237  {
238  if (addedPatches.found(patchi))
239  {
240  tSubFld.ref().boundaryFieldRef()[patchi] == Zero;
241  }
242  }
243 
244  // Store on subMesh
245  GeoField* subFld = tSubFld.ptr();
246  subFld->rename(fld.name());
247  subFld->writeOpt(IOobject::AUTO_WRITE);
248  subFld->store();
249  }
250 }
251 
252 // Select all cells not in the region
253 labelList getNonRegionCells(const labelList& cellRegion, const label regionI)
254 {
255  DynamicList<label> nonRegionCells(cellRegion.size());
256  forAll(cellRegion, celli)
257  {
258  if (cellRegion[celli] != regionI)
259  {
260  nonRegionCells.append(celli);
261  }
262  }
263  return labelList(std::move(nonRegionCells));
264 }
265 
266 
267 void addToInterface
268 (
269  const polyMesh& mesh,
270  const label zoneID,
271  const label ownRegion,
272  const label neiRegion,
273  EdgeMap<Map<label>>& regionsToSize
274 )
275 {
276  const auto interface(edge::sorted(ownRegion, neiRegion));
277 
278  auto iter = regionsToSize.find(interface);
279 
280  if (iter.good())
281  {
282  // Check if zone present
283  auto zoneIter = iter().find(zoneID);
284  if (zoneIter.good())
285  {
286  // Found zone. Increment count.
287  ++(*zoneIter);
288  }
289  else
290  {
291  // New or no zone. Insert with count 1.
292  iter().insert(zoneID, 1);
293  }
294  }
295  else
296  {
297  // Create new interface of size 1.
298  regionsToSize(interface).insert(zoneID, 1);
299  }
300 }
301 
302 // Get the zone ID for a face, respecting the restriction list
303 inline label getZoneID
304 (
305  const polyMesh& mesh,
306  const label facei,
307  const bool useFaceZones,
308  const labelHashSet& selectedFaceZoneIDs
309 )
310 {
311  const bool useSelectedFaceZones = !selectedFaceZoneIDs.empty();
312 
313  // Exit early if NEITHER feature is being used
314  if (!useFaceZones && !useSelectedFaceZones)
315  {
316  return -1;
317  }
318 
319  const label zoneID = mesh.faceZones().whichZone(facei);
320 
321  // If using selected face zones, check if the found zone is permitted
322  if ( zoneID != -1 && useSelectedFaceZones && !selectedFaceZoneIDs.found(zoneID) )
323  {
324  return -1;
325  }
326 
327  return zoneID;
328 }
329 
330 // Get region-region interface name and sizes.
331 // Returns interfaces as straight list for looping in identical order.
332 void getInterfaceSizes
333 (
334  const polyMesh& mesh,
335  const bool useFaceZones,
336  const labelHashSet& selectedFaceZoneIDs,
337  const labelList& cellRegion,
338  const wordList& regionNames,
339  edgeList& interfaces,
340  List<Pair<word>>& interfaceNames,
341  labelList& interfaceSizes,
342  labelList& faceToInterface
343 )
344 {
345  // From region-region to faceZone (or -1) to number of faces.
346 
347  EdgeMap<Map<label>> regionsToSize;
348 
349 
350  // Internal faces
351  // ~~~~~~~~~~~~~~
352 
353  forAll(mesh.faceNeighbour(), facei)
354  {
355  label ownRegion = cellRegion[mesh.faceOwner()[facei]];
356  label neiRegion = cellRegion[mesh.faceNeighbour()[facei]];
357 
358  if (ownRegion != neiRegion)
359  {
360  addToInterface
361  (
362  mesh,
363  getZoneID(mesh, facei, useFaceZones, selectedFaceZoneIDs),
364  ownRegion,
365  neiRegion,
366  regionsToSize
367  );
368  }
369  }
370 
371  // Boundary faces
372  // ~~~~~~~~~~~~~~
373 
374  // Neighbour cellRegion.
375  labelList coupledRegion(mesh.nBoundaryFaces());
376 
377  forAll(coupledRegion, i)
378  {
379  label celli = mesh.faceOwner()[i+mesh.nInternalFaces()];
380  coupledRegion[i] = cellRegion[celli];
381  }
382  syncTools::swapBoundaryFaceList(mesh, coupledRegion);
383 
384  forAll(coupledRegion, i)
385  {
386  label facei = i+mesh.nInternalFaces();
387  label ownRegion = cellRegion[mesh.faceOwner()[facei]];
388  label neiRegion = coupledRegion[i];
389 
390  if (ownRegion != neiRegion)
391  {
392  addToInterface
393  (
394  mesh,
395  getZoneID(mesh, facei, useFaceZones, selectedFaceZoneIDs),
396  ownRegion,
397  neiRegion,
398  regionsToSize
399  );
400  }
401  }
402 
403 
404  if (UPstream::parRun())
405  {
406  if (UPstream::master())
407  {
408  // Receive and add to my sizes
409  for (const int proci : UPstream::subProcs())
410  {
411  EdgeMap<Map<label>> slaveSizes;
412  IPstream::recv(slaveSizes, proci);
413 
414  forAllConstIters(slaveSizes, slaveIter)
415  {
416  const Map<label>& slaveInfo = *slaveIter;
417 
418  auto masterIter = regionsToSize.find(slaveIter.key());
419 
420  if (masterIter.good())
421  {
422  // Same inter-region
423  Map<label>& masterInfo = *masterIter;
424 
425  forAllConstIters(slaveInfo, iter)
426  {
427  const label zoneID = iter.key();
428  const label slaveSize = iter.val();
429 
430  auto zoneIter = masterInfo.find(zoneID);
431  if (zoneIter.good())
432  {
433  *zoneIter += slaveSize;
434  }
435  else
436  {
437  masterInfo.insert(zoneID, slaveSize);
438  }
439  }
440  }
441  else
442  {
443  regionsToSize.insert(slaveIter.key(), slaveInfo);
444  }
445  }
446  }
447  }
448  else
449  {
450  // send to master
451  OPstream::send(regionsToSize, UPstream::masterNo());
452  }
453  }
454 
455  // Rework
456 
457  Pstream::broadcast(regionsToSize);
458 
459 
460  // Now we have the global sizes of all inter-regions.
461  // Invert this on master and distribute.
462  label nInterfaces = 0;
463  forAllConstIters(regionsToSize, iter)
464  {
465  const Map<label>& info = iter.val();
466  nInterfaces += info.size();
467  }
468 
469  interfaces.setSize(nInterfaces);
470  interfaceNames.setSize(nInterfaces);
471  interfaceSizes.setSize(nInterfaces);
472  EdgeMap<Map<label>> regionsToInterface(nInterfaces);
473 
474  nInterfaces = 0;
475  forAllConstIters(regionsToSize, iter)
476  {
477  const edge& e = iter.key();
478  const Map<label>& info = iter.val();
479 
480  const word& name0 = regionNames[e[0]];
481  const word& name1 = regionNames[e[1]];
482 
483  forAllConstIters(info, infoIter)
484  {
485  interfaces[nInterfaces] = iter.key();
486  label zoneID = infoIter.key();
487  if (zoneID == -1)
488  {
489  interfaceNames[nInterfaces] = Pair<word>
490  (
491  name0 + "_to_" + name1,
492  name1 + "_to_" + name0
493  );
494  }
495  else
496  {
497  const word& zoneName = mesh.faceZones()[zoneID].name();
498  interfaceNames[nInterfaces] = Pair<word>
499  (
500  zoneName + "_" + name0 + "_to_" + name1,
501  zoneName + "_" + name1 + "_to_" + name0
502  );
503  }
504 
505  interfaceSizes[nInterfaces] = infoIter();
506  regionsToInterface(e).insert(zoneID, nInterfaces);
507 
508  nInterfaces++;
509  }
510  }
511 
512 
513  // Consistent interface information for all processors
515  (
517  interfaces,
518  interfaceNames,
519  interfaceSizes,
520  regionsToInterface
521  );
522 
523  // Mark all inter-region faces.
524  faceToInterface.setSize(mesh.nFaces(), -1);
525 
526  forAll(mesh.faceNeighbour(), facei)
527  {
528  label ownRegion = cellRegion[mesh.faceOwner()[facei]];
529  label neiRegion = cellRegion[mesh.faceNeighbour()[facei]];
530 
531  if (ownRegion != neiRegion)
532  {
533  const auto interface(edge::sorted(ownRegion, neiRegion));
534 
535  const label zoneID =
536  getZoneID(mesh, facei, useFaceZones, selectedFaceZoneIDs);
537 
538  faceToInterface[facei] = regionsToInterface[interface][zoneID];
539  }
540  }
541  forAll(coupledRegion, i)
542  {
543  label facei = i+mesh.nInternalFaces();
544  label ownRegion = cellRegion[mesh.faceOwner()[facei]];
545  label neiRegion = coupledRegion[i];
546 
547  if (ownRegion != neiRegion)
548  {
549  const auto interface(edge::sorted(ownRegion, neiRegion));
550 
551  const label zoneID =
552  getZoneID(mesh, facei, useFaceZones, selectedFaceZoneIDs);
553 
554  faceToInterface[facei] = regionsToInterface[interface][zoneID];
555  }
556  }
557 }
558 
559 
560 // Create mesh for region.
561 autoPtr<mapPolyMesh> createRegionMesh
562 (
563  const fvMesh& mesh,
564  // Region info
565  const labelList& cellRegion,
566  const label regionI,
567  const word& regionName,
568  // Interface info
569  const labelList& interfacePatches,
570  const labelList& faceToInterface,
571 
572  autoPtr<fvMesh>& newMesh
573 )
574 {
575  // Create dummy system/fv*
577 
578  // Neighbour cellRegion.
579  labelList coupledRegion(mesh.nBoundaryFaces());
580 
581  forAll(coupledRegion, i)
582  {
583  label celli = mesh.faceOwner()[i+mesh.nInternalFaces()];
584  coupledRegion[i] = cellRegion[celli];
585  }
586  syncTools::swapBoundaryFaceList(mesh, coupledRegion);
587 
588 
589  // Topology change container. Start off from existing mesh.
590  polyTopoChange meshMod(mesh);
591 
592  // Cell remover engine
593  removeCells cellRemover(mesh);
594 
595  // Select all but region cells
596  labelList cellsToRemove(getNonRegionCells(cellRegion, regionI));
597 
598  // Find out which faces will get exposed. Note that this
599  // gets faces in mesh face order. So both regions will get same
600  // face in same order (important!)
601  labelList exposedFaces = cellRemover.getExposedFaces(cellsToRemove);
602 
603  labelList exposedPatchIDs(exposedFaces.size());
604  forAll(exposedFaces, i)
605  {
606  label facei = exposedFaces[i];
607  label interfacei = faceToInterface[facei];
608 
609  label ownRegion = cellRegion[mesh.faceOwner()[facei]];
610  label neiRegion = -1;
611 
612  if (mesh.isInternalFace(facei))
613  {
614  neiRegion = cellRegion[mesh.faceNeighbour()[facei]];
615  }
616  else
617  {
618  neiRegion = coupledRegion[facei-mesh.nInternalFaces()];
619  }
620 
621 
622  // Check which side is being kept - determines which of the two
623  // patches will be used.
624 
625  label otherRegion = -1;
626 
627  if (ownRegion == regionI && neiRegion != regionI)
628  {
629  otherRegion = neiRegion;
630  }
631  else if (ownRegion != regionI && neiRegion == regionI)
632  {
633  otherRegion = ownRegion;
634  }
635  else
636  {
638  << "Exposed face:" << facei
639  << " fc:" << mesh.faceCentres()[facei]
640  << " has owner region " << ownRegion
641  << " and neighbour region " << neiRegion
642  << " when handling region:" << regionI
643  << exit(FatalError);
644  }
645 
646  // Find the patch.
647  if (regionI < otherRegion)
648  {
649  exposedPatchIDs[i] = interfacePatches[interfacei];
650  }
651  else
652  {
653  exposedPatchIDs[i] = interfacePatches[interfacei]+1;
654  }
655  }
656 
657  // Remove faces
658  cellRemover.setRefinement
659  (
660  cellsToRemove,
661  exposedFaces,
662  exposedPatchIDs,
663  meshMod
664  );
665 
666  autoPtr<mapPolyMesh> map = meshMod.makeMesh
667  (
668  newMesh,
669  IOobject
670  (
671  regionName,
672  mesh.time().timeName(),
673  mesh.time(),
674  IOobject::READ_IF_PRESENT, // read fv* if present
676  ),
677  mesh
678  );
679 
680  return map;
681 }
682 
683 
684 void createAndWriteRegion
685 (
686  const fvMesh& mesh,
687  const labelList& cellRegion,
688  const wordList& regionNames,
689  const bool prefixRegion,
690  const labelList& faceToInterface,
691  const labelList& interfacePatches,
692  const label regionI,
693  const word& newMeshInstance
694 )
695 {
696  Info<< "Creating mesh for region " << regionI
697  << ' ' << regionNames[regionI] << endl;
698 
699  autoPtr<fvMesh> newMesh;
700  autoPtr<mapPolyMesh> map = createRegionMesh
701  (
702  mesh,
703  cellRegion,
704  regionI,
705  regionNames[regionI],
706  interfacePatches,
707  faceToInterface,
708  newMesh
709  );
710 
711 
712  // Make map of all added patches
713  labelHashSet addedPatches(2*interfacePatches.size());
714  forAll(interfacePatches, interfacei)
715  {
716  addedPatches.insert(interfacePatches[interfacei]);
717  addedPatches.insert(interfacePatches[interfacei]+1);
718  }
719 
720 
721  Info<< "Mapping fields" << endl;
722 
723  // Map existing fields
724  newMesh().updateMesh(map());
725 
726  // Add subsetted fields
727  subsetVolFields<volScalarField>
728  (
729  mesh,
730  newMesh(),
731  map().cellMap(),
732  map().faceMap(),
733  addedPatches
734  );
735  subsetVolFields<volVectorField>
736  (
737  mesh,
738  newMesh(),
739  map().cellMap(),
740  map().faceMap(),
741  addedPatches
742  );
743  subsetVolFields<volSphericalTensorField>
744  (
745  mesh,
746  newMesh(),
747  map().cellMap(),
748  map().faceMap(),
749  addedPatches
750  );
751  subsetVolFields<volSymmTensorField>
752  (
753  mesh,
754  newMesh(),
755  map().cellMap(),
756  map().faceMap(),
757  addedPatches
758  );
759  subsetVolFields<volTensorField>
760  (
761  mesh,
762  newMesh(),
763  map().cellMap(),
764  map().faceMap(),
765  addedPatches
766  );
767 
768  subsetSurfaceFields<surfaceScalarField>
769  (
770  mesh,
771  newMesh(),
772  map().cellMap(),
773  map().faceMap(),
774  addedPatches
775  );
776  subsetSurfaceFields<surfaceVectorField>
777  (
778  mesh,
779  newMesh(),
780  map().cellMap(),
781  map().faceMap(),
782  addedPatches
783  );
784  subsetSurfaceFields<surfaceSphericalTensorField>
785  (
786  mesh,
787  newMesh(),
788  map().cellMap(),
789  map().faceMap(),
790  addedPatches
791  );
792  subsetSurfaceFields<surfaceSymmTensorField>
793  (
794  mesh,
795  newMesh(),
796  map().cellMap(),
797  map().faceMap(),
798  addedPatches
799  );
800  subsetSurfaceFields<surfaceTensorField>
801  (
802  mesh,
803  newMesh(),
804  map().cellMap(),
805  map().faceMap(),
806  addedPatches
807  );
808 
809 
810  const polyBoundaryMesh& newPatches = newMesh().boundaryMesh();
811  newPatches.checkParallelSync(true);
812 
813  // Delete empty patches
814  // ~~~~~~~~~~~~~~~~~~~~
815 
816  // Create reordering list to move patches-to-be-deleted to end
817  labelList oldToNew(newPatches.size(), -1);
818  DynamicList<label> sharedPatches(newPatches.size());
819  label newI = 0;
820 
821  Info<< "Deleting empty patches" << endl;
822 
823  // Assumes all non-proc boundaries are on all processors!
824  forAll(newPatches, patchi)
825  {
826  const polyPatch& pp = newPatches[patchi];
827 
828  if (!isA<processorPolyPatch>(pp))
829  {
830  if (returnReduceOr(pp.size()))
831  {
832  oldToNew[patchi] = newI;
833  if (!addedPatches.found(patchi))
834  {
835  sharedPatches.append(newI);
836  }
837  newI++;
838  }
839  }
840  }
841 
842  // Same for processor patches (but need no reduction)
843  forAll(newPatches, patchi)
844  {
845  const polyPatch& pp = newPatches[patchi];
846 
847  if (isA<processorPolyPatch>(pp) && pp.size())
848  {
849  oldToNew[patchi] = newI++;
850  }
851  }
852 
853  const label nNewPatches = newI;
854 
855  // Move all deleteable patches to the end
856  forAll(oldToNew, patchi)
857  {
858  if (oldToNew[patchi] == -1)
859  {
860  oldToNew[patchi] = newI++;
861  }
862  }
863 
864  //reorderPatches(newMesh(), oldToNew, nNewPatches);
865  fvMeshTools::reorderPatches(newMesh(), oldToNew, nNewPatches, true);
866 
867  // Rename shared patches with region name
868  if (prefixRegion)
869  {
870  Info<< "Prefixing patches with region name" << endl;
871 
872  renamePatches(newMesh(), regionNames[regionI], sharedPatches);
873  }
874 
875 
876  Info<< "Writing new mesh" << endl;
877 
878  newMesh().setInstance(newMeshInstance);
879  newMesh().write();
880  topoSet::removeFiles(newMesh());
881  processorMeshes::removeFiles(newMesh());
882 
883  // Write addressing files like decomposePar
884  Info<< "Writing addressing to base mesh" << endl;
885 
886  labelIOList pointProcAddressing
887  (
888  IOobject
889  (
890  "pointRegionAddressing",
891  newMesh().facesInstance(),
893  newMesh(),
897  ),
898  map().pointMap()
899  );
900  Info<< "Writing map " << pointProcAddressing.name()
901  << " from region" << regionI
902  << " points back to base mesh." << endl;
903  pointProcAddressing.write();
904 
905  labelIOList faceProcAddressing
906  (
907  IOobject
908  (
909  "faceRegionAddressing",
910  newMesh().facesInstance(),
912  newMesh(),
916  ),
917  newMesh().nFaces()
918  );
919  forAll(faceProcAddressing, facei)
920  {
921  // face + turning index. (see decomposePar)
922  // Is the face pointing in the same direction?
923  label oldFacei = map().faceMap()[facei];
924 
925  if
926  (
927  map().cellMap()[newMesh().faceOwner()[facei]]
928  == mesh.faceOwner()[oldFacei]
929  )
930  {
931  faceProcAddressing[facei] = oldFacei+1;
932  }
933  else
934  {
935  faceProcAddressing[facei] = -(oldFacei+1);
936  }
937  }
938  Info<< "Writing map " << faceProcAddressing.name()
939  << " from region" << regionI
940  << " faces back to base mesh." << endl;
941  faceProcAddressing.write();
942 
943  labelIOList cellProcAddressing
944  (
945  IOobject
946  (
947  "cellRegionAddressing",
948  newMesh().facesInstance(),
950  newMesh(),
954  ),
955  map().cellMap()
956  );
957  Info<< "Writing map " <<cellProcAddressing.name()
958  << " from region" << regionI
959  << " cells back to base mesh." << endl;
960  cellProcAddressing.write();
961 
962  labelIOList boundaryProcAddressing
963  (
964  IOobject
965  (
966  "boundaryRegionAddressing",
967  newMesh().facesInstance(),
969  newMesh(),
973  ),
974  labelList(nNewPatches, -1)
975  );
976  forAll(oldToNew, i)
977  {
978  if (!addedPatches.found(i))
979  {
980  label newI = oldToNew[i];
981  if (newI >= 0 && newI < nNewPatches)
982  {
983  boundaryProcAddressing[oldToNew[i]] = i;
984  }
985  }
986  }
987  Info<< "Writing map " << boundaryProcAddressing.name()
988  << " from region" << regionI
989  << " boundary back to base mesh." << endl;
990  boundaryProcAddressing.write();
991 }
992 
993 
994 // Create for every region-region interface with non-zero size two patches.
995 // First one is for minimumregion to maximumregion.
996 // Note that patches get created in same order on all processors (if parallel)
997 // since looping over synchronised 'interfaces'.
998 labelList addRegionPatches
999 (
1000  fvMesh& mesh,
1001  const wordList& regionNames,
1002  const edgeList& interfaces,
1003  const List<Pair<word>>& interfaceNames
1004 )
1005 {
1006  Info<< nl << "Adding patches" << nl << endl;
1007 
1008  labelList interfacePatches(interfaces.size());
1009 
1010  forAll(interfaces, interI)
1011  {
1012  const edge& e = interfaces[interI];
1013  const Pair<word>& names = interfaceNames[interI];
1014 
1015  //Info<< "For interface " << interI
1016  // << " between regions " << e
1017  // << " trying to add patches " << names << endl;
1018 
1019 
1020  mappedWallPolyPatch patch1
1021  (
1022  names[0],
1023  0, // overridden
1024  0, // overridden
1025  0, // overridden
1026  regionNames[e[1]], // sampleRegion
1028  names[1], // samplePatch
1029  point::zero, // offset
1030  mesh.boundaryMesh()
1031  );
1032 
1033  interfacePatches[interI] = fvMeshTools::addPatch
1034  (
1035  mesh,
1036  patch1,
1037  dictionary(), //optional per field value
1039  true //validBoundary
1040  );
1041 
1042  mappedWallPolyPatch patch2
1043  (
1044  names[1],
1045  0,
1046  0,
1047  0,
1048  regionNames[e[0]], // sampleRegion
1050  names[0],
1051  point::zero, // offset
1052  mesh.boundaryMesh()
1053  );
1055  (
1056  mesh,
1057  patch2,
1058  dictionary(), //optional per field value
1060  true //validBoundary
1061  );
1062 
1063  Info<< "For interface between region " << regionNames[e[0]]
1064  << " and " << regionNames[e[1]] << " added patches" << endl
1065  << " " << interfacePatches[interI]
1066  << "\t" << mesh.boundaryMesh()[interfacePatches[interI]].name()
1067  << endl
1068  << " " << interfacePatches[interI]+1
1069  << "\t" << mesh.boundaryMesh()[interfacePatches[interI]+1].name()
1070  << endl;
1071  }
1072  return interfacePatches;
1073 }
1074 
1075 
1076 // Find region that covers most of cell zone
1077 label findCorrespondingRegion
1078 (
1079  const labelList& existingZoneID, // per cell the (unique) zoneID
1080  const labelList& cellRegion,
1081  const label nCellRegions,
1082  const label zoneI,
1083  const label minOverlapSize
1084 )
1085 {
1086  // Per region the number of cells in zoneI
1087  labelList cellsInZone(nCellRegions, Zero);
1088 
1089  forAll(cellRegion, celli)
1090  {
1091  if (existingZoneID[celli] == zoneI)
1092  {
1093  cellsInZone[cellRegion[celli]]++;
1094  }
1095  }
1096 
1097  Pstream::listReduce(cellsInZone, sumOp<label>());
1098 
1099  // Pick region with largest overlap of zoneI
1100  label regionI = findMax(cellsInZone);
1101 
1102 
1103  if (cellsInZone[regionI] < minOverlapSize)
1104  {
1105  // Region covers too little of zone. Not good enough.
1106  regionI = -1;
1107  }
1108  else
1109  {
1110  // Check that region contains no cells that aren't in cellZone.
1111  forAll(cellRegion, celli)
1112  {
1113  if (cellRegion[celli] == regionI && existingZoneID[celli] != zoneI)
1114  {
1115  // celli in regionI but not in zoneI
1116  regionI = -1;
1117  break;
1118  }
1119  }
1120  // If one in error, all should be in error. Note that branch gets taken
1121  // on all procs.
1122  reduce(regionI, minOp<label>());
1123  }
1124 
1125  return regionI;
1126 }
1127 
1128 
1129 void getClusterID
1130 (
1131  const polyMesh& mesh,
1132  const cellZoneMesh& cellZones,
1133  const wordList& clusterNames,
1134  const labelListList& clusterToZones,
1135  labelList& clusterID,
1136  labelList& neiClusterID
1137 )
1138 {
1139  // Existing zoneID
1140  clusterID.setSize(mesh.nCells());
1141  clusterID = -1;
1142 
1143  forAll(clusterToZones, clusterI)
1144  {
1145  for (const label zoneI : clusterToZones[clusterI])
1146  {
1147  const cellZone& cz = cellZones[zoneI];
1148 
1149  forAll(cz, i)
1150  {
1151  label celli = cz[i];
1152  if (clusterID[celli] == -1)
1153  {
1154  clusterID[celli] = clusterI;
1155  }
1156  else
1157  {
1159  << "Cell " << celli << " with cell centre "
1160  << mesh.cellCentres()[celli]
1161  << " is multiple zones. This is not allowed." << endl
1162  << "It is in zone " << clusterNames[clusterID[celli]]
1163  << " and in zone " << clusterNames[clusterI]
1164  << exit(FatalError);
1165  }
1166  }
1167  }
1168  }
1169 
1170  // Neighbour zoneID.
1171  syncTools::swapBoundaryCellList(mesh, clusterID, neiClusterID);
1172 }
1173 
1174 
1175 word makeRegionName
1176 (
1177  const cellZoneMesh& czs,
1178  const label regioni,
1179  const labelList& zoneIDs
1180 )
1181 {
1182  // Synthesise region name. Equals the zone name if cluster consist of only
1183  // one zone
1184 
1185  if (zoneIDs.empty())
1186  {
1187  return word("domain") + Foam::name(regioni);
1188  }
1189  else
1190  {
1191  // Zone indices are in cellZone order ...
1192  word regionName(czs[zoneIDs[0]].name());
1193 
1194  // Synthesize name from appended cellZone names
1195  for (label i = 1; i < zoneIDs.size(); i++)
1196  {
1197  regionName += "_" + czs[zoneIDs[i]].name();
1198  }
1199  return regionName;
1200  }
1201 }
1202 
1203 
1204 void makeClusters
1205 (
1206  const List<wordRes>& zoneClusters,
1207  const wordList& zoneClusterNames,
1208  const cellZoneMesh& cellZones,
1209  wordList& clusterNames,
1210  labelListList& clusterToZones,
1211  labelList& zoneToCluster
1212 )
1213 {
1214  // Check if there are clustering for zones. If none every zone goes into
1215  // its own cluster.
1216 
1217  clusterNames.clear();
1218  clusterToZones.clear();
1219  zoneToCluster.setSize(cellZones.size());
1220  zoneToCluster = -1;
1221 
1222  if (zoneClusters.size())
1223  {
1224  forAll(zoneClusters, clusteri)
1225  {
1226  const labelList zoneIDs(cellZones.indices(zoneClusters[clusteri]));
1227  UIndirectList<label>(zoneToCluster, zoneIDs) = clusteri;
1228  clusterNames.append
1229  (
1230  zoneClusterNames[clusteri].size()
1231  ? zoneClusterNames[clusteri]
1232  : makeRegionName
1233  (
1234  cellZones,
1235  clusteri,
1236  zoneIDs
1237  )
1238  );
1239  clusterToZones.append(std::move(zoneIDs));
1240  }
1241 
1242  // Unclustered zone
1243  forAll(zoneToCluster, zonei)
1244  {
1245  if (zoneToCluster[zonei] == -1)
1246  {
1247  clusterNames.append(cellZones[zonei].name());
1248  clusterToZones.append(labelList(1, zonei));
1249  zoneToCluster[zonei] = clusterToZones.size();
1250  }
1251  }
1252  }
1253  else
1254  {
1255  for (const auto& cellZone : cellZones)
1256  {
1257  const label nClusters = clusterToZones.size();
1258  clusterNames.append(cellZone.name());
1259  clusterToZones.append(labelList(1, cellZone.index()));
1260  zoneToCluster[cellZone.index()] = nClusters;
1261  }
1262  }
1263 }
1264 
1265 
1266 void matchRegions
1267 (
1268  const bool sloppyCellZones,
1269  const polyMesh& mesh,
1270 
1271  const wordList& clusterNames,
1272  const labelListList& clusterToZones,
1273  const labelList& clusterID,
1274 
1275  const label nCellRegions,
1276  const labelList& cellRegion,
1277 
1278  labelListList& regionToZones,
1280  labelList& zoneToRegion
1281 )
1282 {
1283  const cellZoneMesh& cellZones = mesh.cellZones();
1284 
1285  regionToZones.setSize(nCellRegions);
1286  regionToZones = labelList();
1287  regionNames.setSize(nCellRegions);
1289  zoneToRegion.setSize(cellZones.size(), -1);
1290 
1291 
1292  // Sizes per cluster
1293  labelList clusterSizes(clusterToZones.size(), Zero);
1294  forAll(clusterToZones, clusterI)
1295  {
1296  for (const label zoneI : clusterToZones[clusterI])
1297  {
1298  clusterSizes[clusterI] += cellZones[zoneI].size();
1299  }
1300  reduce(clusterSizes[clusterI], sumOp<label>());
1301  }
1302 
1303  if (sloppyCellZones)
1304  {
1305  Info<< "Trying to match regions to existing cell zones;"
1306  << " region can be subset of cell zone." << nl << endl;
1307 
1308  forAll(clusterToZones, clusterI)
1309  {
1310  label regionI = findCorrespondingRegion
1311  (
1312  clusterID,
1313  cellRegion,
1314  nCellRegions,
1315  clusterI,
1316  label(0.5*clusterSizes[clusterI]) // minimum overlap
1317  );
1318 
1319  if (regionI != -1)
1320  {
1321  Info<< "Sloppily matched region " << regionI
1322  //<< " size " << regionSizes[regionI]
1323  << " to cluster " << clusterI
1324  << " size " << clusterSizes[clusterI]
1325  << endl;
1327  (
1328  zoneToRegion,
1329  clusterToZones[clusterI]
1330  ) = regionI;
1331  regionToZones[regionI] = clusterToZones[clusterI];
1332  regionNames[regionI] = clusterNames[clusterI];
1333  }
1334  }
1335  }
1336  else
1337  {
1338  Info<< "Trying to match regions to existing cell zones." << nl << endl;
1339 
1340  forAll(clusterToZones, clusterI)
1341  {
1342  label regionI = findCorrespondingRegion
1343  (
1344  clusterID,
1345  cellRegion,
1346  nCellRegions,
1347  clusterI,
1348  clusterSizes[clusterI] // require exact match
1349  );
1350 
1351  if (regionI != -1)
1352  {
1354  (
1355  zoneToRegion,
1356  clusterToZones[clusterI]
1357  ) = regionI;
1358  regionToZones[regionI] = clusterToZones[clusterI];
1359  regionNames[regionI] = clusterNames[clusterI];
1360  }
1361  }
1362  }
1363  // Allocate region names for unmatched regions.
1364  forAll(regionNames, regionI)
1365  {
1366  if (regionNames[regionI].empty())
1367  {
1368  regionNames[regionI] = makeRegionName
1369  (
1370  cellZones,
1371  regionI,
1372  regionToZones[regionI]
1373  );
1374  }
1375  }
1376 }
1377 
1378 
1379 void writeCellToRegion(const fvMesh& mesh, const labelList& cellRegion)
1380 {
1381  // Write to manual decomposition option
1382  {
1383  labelIOList cellToRegion
1384  (
1385  IOobject
1386  (
1387  "cellToRegion",
1388  mesh.facesInstance(),
1389  mesh,
1393  ),
1394  cellRegion
1395  );
1396  cellToRegion.write();
1397 
1398  Info<< "Writing region per cell file (for manual decomposition) to "
1399  << cellToRegion.objectPath() << nl << endl;
1400  }
1401  // Write for postprocessing
1402  {
1403  volScalarField cellToRegion
1404  (
1405  IOobject
1406  (
1407  "cellToRegion",
1408  mesh.time().timeName(),
1409  mesh,
1413  ),
1414  mesh,
1417  );
1418  forAll(cellRegion, celli)
1419  {
1420  cellToRegion[celli] = cellRegion[celli];
1421  }
1422  cellToRegion.write();
1423 
1424  Info<< "Writing region per cell as volScalarField to "
1425  << cellToRegion.objectPath() << nl << endl;
1426  }
1427 }
1428 
1429 
1430 
1431 
1432 int main(int argc, char *argv[])
1433 {
1435  (
1436  "Split mesh into multiple regions (detected by walking across faces)"
1437  );
1438  #include "addRegionOption.H"
1439  #include "addOverwriteOption.H"
1441  (
1442  "cellZones",
1443  "Additionally split cellZones off into separate regions"
1444  );
1446  (
1447  "cellZonesOnly",
1448  "Use cellZones only to split mesh into regions; do not use walking"
1449  );
1451  (
1452  "cellZonesFileOnly",
1453  "file",
1454  "Like -cellZonesOnly, but use specified file"
1455  );
1457  (
1458  "combineZones",
1459  "lists of cellZones",
1460  "Combine zones in follow-on analysis. Synthesises the region name by "
1461  "concatenation of zone names, e.g., zoneA_zoneB."
1462  );
1464  (
1465  "customRegionNames",
1466  "wordList",
1467  "list of custom region names to be used with combineZones option. "
1468  "Without this option, combineZones results in region names, which are "
1469  "a concatenation of different cellZone names."
1470  );
1472  (
1473  "addZones",
1474  "lists of cellZones",
1475  "Combine zones in follow-on analysis. Supplies the destination region "
1476  "name as the first cellZone name in the list."
1477  );
1479  (
1480  "blockedFaces",
1481  "faceSet",
1482  "Specify additional region boundaries that walking does not cross"
1483  );
1485  (
1486  "makeCellZones",
1487  "Place cells into cellZones instead of splitting mesh"
1488  );
1490  (
1491  "largestOnly",
1492  "Only write largest region"
1493  );
1495  (
1496  "insidePoint",
1497  "point",
1498  "Only write region containing point"
1499  );
1501  (
1502  "detectOnly",
1503  "Do not write mesh"
1504  );
1506  (
1507  "sloppyCellZones",
1508  "Try to match heuristically regions to existing cell zones"
1509  );
1511  (
1512  "useFaceZones",
1513  "Use all faceZones to patch inter-region faces instead of single patch"
1514  );
1516  (
1517  "useSelectedFaceZones",
1518  "wordList",
1519  "Use only the specified faceZones to patch inter-region faces instead "
1520  "of single patch"
1521  );
1523  (
1524  "prefixRegion",
1525  "Prefix region name to all patches, not just coupling patches"
1526  );
1527 
1528  argList::noFunctionObjects(); // Never use function objects
1529 
1530  #include "setRootCase.H"
1531  #include "createTime.H"
1532  #include "createNamedMesh.H"
1533 
1534  const word oldInstance = mesh.pointsInstance();
1535 
1536  word blockedFacesName;
1537  if (args.readIfPresent("blockedFaces", blockedFacesName))
1538  {
1539  Info<< "Reading blocked internal faces from faceSet "
1540  << blockedFacesName << nl << endl;
1541  }
1542 
1543  const bool makeCellZones = args.found("makeCellZones");
1544  const bool largestOnly = args.found("largestOnly");
1545  const bool insidePoint = args.found("insidePoint");
1546  const bool useCellZones = args.found("cellZones");
1547  const bool useCellZonesOnly = args.found("cellZonesOnly");
1548  const bool useCellZonesFile = args.found("cellZonesFileOnly");
1549  const bool combineZones = args.found("combineZones");
1550  const bool addZones = args.found("addZones");
1551  const bool overwrite = args.found("overwrite");
1552  const bool detectOnly = args.found("detectOnly");
1553  const bool sloppyCellZones = args.found("sloppyCellZones");
1554  const bool useFaceZones = args.found("useFaceZones");
1555  const bool prefixRegion = args.found("prefixRegion");
1556 
1557 
1558  if
1559  (
1560  (useCellZonesOnly || useCellZonesFile)
1561  && (useCellZones || blockedFacesName.size())
1562  )
1563  {
1565  << "You cannot specify both -cellZonesOnly or -cellZonesFileOnly"
1566  << " (which specify complete split)"
1567  << " in combination with -blockedFaces or -cellZones"
1568  << " (which imply a split based on topology)"
1569  << exit(FatalError);
1570  }
1571 
1572  labelHashSet selectedFaceZoneIDs;
1573  wordList selectedFaceZoneNames;
1574 
1575  const bool useSelectedFaceZones
1576  = args.readListIfPresent("useSelectedFaceZones", selectedFaceZoneNames);
1577 
1578  if (useFaceZones && useSelectedFaceZones)
1579  {
1581  << "You cannot specify both useFaceZones and useSelectedFaceZones"
1582  << exit(FatalError);
1583  }
1584 
1585  if (useFaceZones)
1586  {
1587  Info<< "Using current faceZones to divide inter-region interfaces"
1588  << " into multiple patches."
1589  << nl << endl;
1590  }
1591  else if (useSelectedFaceZones)
1592  {
1593  Info<< "Using specified faceZones to divide inter-region interfaces"
1594  << " into multiple patches."
1595  << nl << endl;
1596 
1597  forAll(selectedFaceZoneNames, i)
1598  {
1599  const label zoneID
1600  = mesh.faceZones().findZoneID(selectedFaceZoneNames[i]);
1601 
1602  if (zoneID != -1)
1603  {
1604  selectedFaceZoneIDs.insert(zoneID);
1605  }
1606  else
1607  {
1609  << "faceZone - " << selectedFaceZoneNames[i]
1610  << " not found in mesh." << endl;
1611  }
1612  }
1613  }
1614  else
1615  {
1616  Info<< "Creating single patch per inter-region interface."
1617  << nl << endl;
1618  }
1619 
1620 
1621 
1622  if (insidePoint && largestOnly)
1623  {
1625  << "You cannot specify both -largestOnly"
1626  << " (keep region with most cells)"
1627  << " and -insidePoint (keep region containing point)"
1628  << exit(FatalError);
1629  }
1630 
1631 
1632  // Make sure cellZone names consistent across processors
1634 
1635  List<wordRes> zoneClusters;
1636  wordList zoneClusterNames;
1637  if (combineZones)
1638  {
1639  if (addZones)
1640  {
1642  << "Cannot specify both combineZones and addZones"
1643  << exit(FatalError);
1644  }
1645  zoneClusters = args.get<List<wordRes>>("combineZones");
1646 
1647  if (args.found("customRegionNames"))
1648  {
1649  zoneClusterNames = args.get<wordList>("customRegionNames");
1650 
1651  if (zoneClusterNames.size() != zoneClusters.size())
1652  {
1654  << "Size of -customRegionNames ("
1655  << zoneClusterNames.size()
1656  << ") must match size of -combineZones ("
1657  << zoneClusters.size() << ")" << exit(FatalError);
1658  }
1659  }
1660  else
1661  {
1662  // Default behavior: empty names trigger automatic synthesis in
1663  // makeClusters() function
1664  zoneClusterNames.setSize(zoneClusters.size());
1665  zoneClusterNames = word::null;
1666  }
1667  }
1668  else if (addZones)
1669  {
1670  zoneClusters = args.get<List<wordRes>>("addZones");
1671  zoneClusterNames.setSize(zoneClusters.size());
1672  forAll(zoneClusters, clusteri)
1673  {
1674  // Pop of front - is name
1675 
1676  wordRes& wrs = zoneClusters[clusteri];
1677 
1678  zoneClusterNames[clusteri] = wrs[0];
1679 
1680  for (label i = 1; i < wrs.size(); i++)
1681  {
1682  wrs[i-1] = wrs[i];
1683  }
1684  wrs.setSize(wrs.size()-1);
1685  }
1686  }
1687 
1688 
1689  // Determine per cell the region it belongs to
1690  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1691 
1692  // cellRegion is the labelList with the region per cell.
1693  labelList cellRegion;
1694  // Region to zone(s)
1695  labelListList regionToZones;
1696  // Name of region
1698  // Zone to region
1699  labelList zoneToRegion;
1700 
1701  label nCellRegions = 0;
1702  if (useCellZonesOnly)
1703  {
1704  Info<< "Using current cellZones to split mesh into regions."
1705  << " This requires all"
1706  << " cells to be in one and only one cellZone." << nl << endl;
1707 
1708  // Collect sets of zones into clusters. If no cluster is just an identity
1709  // list (cluster 0 is cellZone 0 etc.)
1710  wordList clusterNames;
1711  labelListList clusterToZones;
1712  labelList zoneToCluster;
1713  makeClusters
1714  (
1715  zoneClusters,
1716  zoneClusterNames,
1717  mesh.cellZones(),
1718  clusterNames,
1719  clusterToZones,
1720  zoneToCluster
1721  );
1722 
1723  // Existing clusterID
1724  labelList clusterID(mesh.nCells(), -1);
1725  // Neighbour clusterID.
1726  labelList neiClusterID(mesh.nBoundaryFaces());
1727  getClusterID
1728  (
1729  mesh,
1730  mesh.cellZones(),
1731  clusterNames,
1732  clusterToZones,
1733  clusterID,
1734  neiClusterID
1735  );
1736 
1737  label unzonedCelli = clusterID.find(-1);
1738  if (unzonedCelli != -1)
1739  {
1741  << "For the cellZonesOnly option all cells "
1742  << "have to be in a cellZone." << endl
1743  << "Cell " << unzonedCelli
1744  << " at" << mesh.cellCentres()[unzonedCelli]
1745  << " is not in a cellZone. There might be more unzoned cells."
1746  << exit(FatalError);
1747  }
1748  cellRegion = clusterID;
1749  nCellRegions = gMax(cellRegion)+1;
1750  zoneToRegion = zoneToCluster;
1751  regionToZones = clusterToZones;
1752  regionNames = clusterNames;
1753  }
1754  else if (useCellZonesFile)
1755  {
1756  const word zoneFile(args["cellZonesFileOnly"]);
1757  Info<< "Reading split from cellZones file " << zoneFile << endl
1758  << "This requires all"
1759  << " cells to be in one and only one cellZone." << nl << endl;
1760 
1761  cellZoneMesh newCellZones
1762  (
1763  IOobject
1764  (
1765  zoneFile,
1766  mesh.facesInstance(),
1768  mesh,
1772  ),
1773  mesh
1774  );
1775 
1776  wordList clusterNames;
1777  labelListList clusterToZones;
1778  labelList zoneToCluster;
1779  makeClusters
1780  (
1781  zoneClusters,
1782  zoneClusterNames,
1783  newCellZones,
1784  clusterNames,
1785  clusterToZones,
1786  zoneToCluster
1787  );
1788 
1789 
1790  // Existing clusterID
1791  labelList clusterID(mesh.nCells(), -1);
1792  // Neighbour clusterID.
1793  labelList neiClusterID(mesh.nBoundaryFaces());
1794  getClusterID
1795  (
1796  mesh,
1797  newCellZones,
1798  clusterNames,
1799  clusterToZones,
1800  clusterID,
1801  neiClusterID
1802  );
1803 
1804 
1805  label unzonedCelli = clusterID.find(-1);
1806  if (unzonedCelli != -1)
1807  {
1809  << "For the cellZonesFileOnly option all cells "
1810  << "have to be in a cellZone." << endl
1811  << "Cell " << unzonedCelli
1812  << " at" << mesh.cellCentres()[unzonedCelli]
1813  << " is not in a cellZone. There might be more unzoned cells."
1814  << exit(FatalError);
1815  }
1816  cellRegion = clusterID;
1817  nCellRegions = gMax(cellRegion)+1;
1818  zoneToRegion = zoneToCluster;
1819  regionToZones = clusterToZones;
1820  regionNames = clusterNames;
1821  }
1822  else
1823  {
1824  // Determine connected regions
1825  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
1826 
1827  // Mark additional faces that are blocked
1828  boolList blockedFace;
1829 
1830  // Read from faceSet
1831  if (blockedFacesName.size())
1832  {
1833  faceSet blockedFaceSet(mesh, blockedFacesName);
1834  Info<< "Read "
1835  << returnReduce(blockedFaceSet.size(), sumOp<label>())
1836  << " blocked faces from set " << blockedFacesName << nl << endl;
1837 
1838  blockedFace.setSize(mesh.nFaces(), false);
1839 
1840  for (const label facei : blockedFaceSet)
1841  {
1842  blockedFace[facei] = true;
1843  }
1844  }
1845 
1846  // Collect sets of zones into clusters. If no cluster is just an
1847  // identity list (cluster 0 is cellZone 0 etc.)
1848  wordList clusterNames;
1849  labelListList clusterToZones;
1850  labelList zoneToCluster;
1851  makeClusters
1852  (
1853  zoneClusters,
1854  zoneClusterNames,
1855  mesh.cellZones(),
1856  clusterNames,
1857  clusterToZones,
1858  zoneToCluster
1859  );
1860 
1861  // Existing clusterID
1862  labelList clusterID(mesh.nCells(), -1);
1863  // Neighbour clusterID.
1864  labelList neiClusterID(mesh.nBoundaryFaces());
1865  getClusterID
1866  (
1867  mesh,
1868  mesh.cellZones(),
1869  clusterNames,
1870  clusterToZones,
1871  clusterID,
1872  neiClusterID
1873  );
1874 
1875 
1876  // Imply from differing cellZones
1877  if (useCellZones)
1878  {
1879  blockedFace.setSize(mesh.nFaces(), false);
1880 
1881  for (label facei = 0; facei < mesh.nInternalFaces(); facei++)
1882  {
1883  label ownCluster = clusterID[mesh.faceOwner()[facei]];
1884  label neiCluster = clusterID[mesh.faceNeighbour()[facei]];
1885 
1886  if (ownCluster != neiCluster)
1887  {
1888  blockedFace[facei] = true;
1889  }
1890  }
1891 
1892  // Different cellZones on either side of processor patch.
1893  forAll(neiClusterID, i)
1894  {
1895  label facei = i+mesh.nInternalFaces();
1896  label ownCluster = clusterID[mesh.faceOwner()[facei]];
1897  label neiCluster = neiClusterID[i];
1898 
1899  if (ownCluster != neiCluster)
1900  {
1901  blockedFace[facei] = true;
1902  }
1903  }
1904  }
1905 
1906  // Do a topological walk to determine regions
1907  regionSplit regions(mesh, blockedFace);
1908  nCellRegions = regions.nRegions();
1909  cellRegion.transfer(regions);
1910 
1911  // Match regions to zones
1912  matchRegions
1913  (
1914  sloppyCellZones,
1915  mesh,
1916 
1917  // cluster-to-zone and cluster-to-cell addressing
1918  clusterNames,
1919  clusterToZones,
1920  clusterID,
1921 
1922  // cell-to-region addressing
1923  nCellRegions,
1924  cellRegion,
1925 
1926  // matched zones
1927  regionToZones,
1928  regionNames,
1929  zoneToRegion
1930  );
1931 
1932  // Override any default region names if single region selected
1933  if (largestOnly || insidePoint)
1934  {
1935  forAll(regionToZones, regionI)
1936  {
1937  if (regionToZones[regionI].empty())
1938  {
1939  if (overwrite)
1940  {
1942  }
1943  else if (insidePoint)
1944  {
1945  regionNames[regionI] = "insidePoint";
1946  }
1947  else if (largestOnly)
1948  {
1949  regionNames[regionI] = "largestOnly";
1950  }
1951  }
1952  }
1953  }
1954  }
1955 
1956  Info<< endl << "Number of regions:" << nCellRegions << nl << endl;
1957 
1958 
1959  // Write decomposition to file
1960  writeCellToRegion(mesh, cellRegion);
1961 
1962 
1963 
1964  // Sizes per region
1965  // ~~~~~~~~~~~~~~~~
1966 
1967  labelList regionSizes(nCellRegions, Zero);
1968 
1969  forAll(cellRegion, celli)
1970  {
1971  regionSizes[cellRegion[celli]]++;
1972  }
1973  forAll(regionSizes, regionI)
1974  {
1975  reduce(regionSizes[regionI], sumOp<label>());
1976  }
1977 
1978  Info<< "Region\tCells" << nl
1979  << "------\t-----" << endl;
1980 
1981  forAll(regionSizes, regionI)
1982  {
1983  Info<< regionI << '\t' << regionSizes[regionI] << nl;
1984  }
1985  Info<< endl;
1986 
1987 
1988 
1989  // Print region to zone
1990  Info<< "Region\tZone\tName" << nl
1991  << "------\t----\t----" << endl;
1992  forAll(regionToZones, regionI)
1993  {
1994  Info<< regionI << '\t' << flatOutput(regionToZones[regionI])
1995  << '\t'
1996  << regionNames[regionI] << nl;
1997  }
1998  Info<< endl;
1999 
2000 
2001 
2002  // Since we're going to mess with patches and zones make sure all
2003  // is synchronised
2006 
2007 
2008  // Interfaces
2009  // ----------
2010  // per interface:
2011  // - the two regions on either side
2012  // - the name
2013  // - the (global) size
2014  edgeList interfaces;
2015  List<Pair<word>> interfaceNames;
2016  labelList interfaceSizes;
2017  // per face the interface
2018  labelList faceToInterface;
2019 
2020  getInterfaceSizes
2021  (
2022  mesh,
2023  useFaceZones,
2024  selectedFaceZoneIDs,
2025  cellRegion,
2026  regionNames,
2027  interfaces,
2028  interfaceNames,
2029  interfaceSizes,
2030  faceToInterface
2031  );
2032 
2033  Info<< "Sizes of interfaces between regions:" << nl << nl
2034  << "Interface\tRegion\tRegion\tFaces" << nl
2035  << "---------\t------\t------\t-----" << endl;
2036 
2037  forAll(interfaces, interI)
2038  {
2039  const edge& e = interfaces[interI];
2040 
2041  Info<< interI
2042  << "\t\t" << e[0] << "\t" << e[1]
2043  << "\t" << interfaceSizes[interI] << nl;
2044  }
2045  Info<< endl;
2046 
2047 
2048  if (detectOnly)
2049  {
2050  Info<< "End\n" << endl;
2051 
2052  return 0;
2053  }
2054 
2055 
2056  // Read objects in time directory
2057  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2058 
2059  IOobjectList objects(mesh, runTime.timeName());
2060 
2061  // Read vol fields.
2062 
2063  PtrList<volScalarField> vsFlds;
2064  ReadFields(mesh, objects, vsFlds);
2065 
2066  PtrList<volVectorField> vvFlds;
2067  ReadFields(mesh, objects, vvFlds);
2068 
2070  ReadFields(mesh, objects, vstFlds);
2071 
2072  PtrList<volSymmTensorField> vsymtFlds;
2073  ReadFields(mesh, objects, vsymtFlds);
2074 
2075  PtrList<volTensorField> vtFlds;
2076  ReadFields(mesh, objects, vtFlds);
2077 
2078  // Read surface fields.
2079 
2081  ReadFields(mesh, objects, ssFlds);
2082 
2084  ReadFields(mesh, objects, svFlds);
2085 
2087  ReadFields(mesh, objects, sstFlds);
2088 
2090  ReadFields(mesh, objects, ssymtFlds);
2091 
2093  ReadFields(mesh, objects, stFlds);
2094 
2095  Info<< endl;
2096 
2097 
2098  // Remove any demand-driven fields ('S', 'V' etc)
2099  mesh.clearOut();
2100 
2101 
2102  if (nCellRegions == 1)
2103  {
2104  Info<< "Only one region. Doing nothing." << endl;
2105  }
2106  else if (makeCellZones)
2107  {
2108  Info<< "Putting cells into cellZones instead of splitting mesh."
2109  << endl;
2110 
2111  // Check if region overlaps with existing zone. If so keep.
2112 
2113  for (label regionI = 0; regionI < nCellRegions; regionI++)
2114  {
2115  const labelList& zones = regionToZones[regionI];
2116 
2117  if (zones.size() == 1 && zones[0] != -1)
2118  {
2119  // Existing zone
2120  const label zoneI = zones[0];
2121  Info<< " Region " << regionI << " : corresponds to existing"
2122  << " cellZone "
2123  << zoneI << ' ' << mesh.cellZones()[zoneI].name() << endl;
2124  }
2125  else
2126  {
2127  // Create new cellZone.
2128  const labelList regionCells(findIndices(cellRegion, regionI));
2129 
2130  const word zoneName = "region" + Foam::name(regionI);
2131 
2132  label zoneI = mesh.cellZones().findZoneID(zoneName);
2133 
2134  if (zoneI == -1)
2135  {
2136  zoneI = mesh.cellZones().size();
2137  mesh.cellZones().setSize(zoneI+1);
2138  mesh.cellZones().set
2139  (
2140  zoneI,
2141  new cellZone
2142  (
2143  zoneName, //name
2144  std::move(regionCells), //addressing
2145  zoneI, //index
2146  mesh.cellZones() //cellZoneMesh
2147  )
2148  );
2149  }
2150  else
2151  {
2152  mesh.cellZones()[zoneI].clearAddressing();
2153  mesh.cellZones()[zoneI] = regionCells;
2154  }
2155  Info<< " Region " << regionI << " : created new cellZone "
2156  << zoneI << ' ' << mesh.cellZones()[zoneI].name() << endl;
2157  }
2158  }
2160 
2161  if (!overwrite)
2162  {
2163  ++runTime;
2165  }
2166  else
2167  {
2168  mesh.setInstance(oldInstance);
2169  }
2170 
2171  Info<< "Writing cellZones as new mesh to time " << runTime.timeName()
2172  << nl << endl;
2173 
2174  mesh.write();
2175 
2176 
2177  // Write cellSets for convenience
2178  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2179 
2180  Info<< "Writing cellSets corresponding to cellZones." << nl << endl;
2181 
2182  for (const auto& cz : mesh.cellZones())
2183  {
2184  cellSet(mesh, cz.name(), cz).write();
2185  }
2186  }
2187  else
2188  {
2189  // Add patches for interfaces
2190  // ~~~~~~~~~~~~~~~~~~~~~~~~~~
2191 
2192  // Add all possible patches. Empty ones get filtered later on.
2193  Info<< nl << "Adding patches" << nl << endl;
2194 
2195  labelList interfacePatches
2196  (
2197  addRegionPatches
2198  (
2199  mesh,
2200  regionNames,
2201  interfaces,
2202  interfaceNames
2203  )
2204  );
2205 
2206 
2207  if (!overwrite)
2208  {
2209  ++runTime;
2210  }
2211 
2212 
2213  // Create regions
2214  // ~~~~~~~~~~~~~~
2215 
2216  if (insidePoint)
2217  {
2218  const point insidePoint = args.get<point>("insidePoint");
2219 
2220  label regionI = -1;
2221 
2222  (void)mesh.tetBasePtIs();
2223 
2224  label celli = mesh.findCell(insidePoint);
2225 
2226  Info<< nl << "Found point " << insidePoint << " in cell " << celli
2227  << endl;
2228 
2229  if (celli != -1)
2230  {
2231  regionI = cellRegion[celli];
2232  }
2233 
2234  reduce(regionI, maxOp<label>());
2235 
2236  Info<< nl
2237  << "Subsetting region " << regionI
2238  << " containing point " << insidePoint << endl;
2239 
2240  if (regionI == -1)
2241  {
2243  << "Point " << insidePoint
2244  << " is not inside the mesh." << nl
2245  << "Bounding box of the mesh:" << mesh.bounds()
2246  << exit(FatalError);
2247  }
2248 
2249  createAndWriteRegion
2250  (
2251  mesh,
2252  cellRegion,
2253  regionNames,
2254  prefixRegion,
2255  faceToInterface,
2256  interfacePatches,
2257  regionI,
2258  (overwrite ? oldInstance : runTime.timeName())
2259  );
2260  }
2261  else if (largestOnly)
2262  {
2263  label regionI = findMax(regionSizes);
2264 
2265  Info<< nl
2266  << "Subsetting region " << regionI
2267  << " of size " << regionSizes[regionI]
2268  << " as named region " << regionNames[regionI] << endl;
2269 
2270  createAndWriteRegion
2271  (
2272  mesh,
2273  cellRegion,
2274  regionNames,
2275  prefixRegion,
2276  faceToInterface,
2277  interfacePatches,
2278  regionI,
2279  (overwrite ? oldInstance : runTime.timeName())
2280  );
2281  }
2282  else
2283  {
2284  // Split all
2285  for (label regionI = 0; regionI < nCellRegions; regionI++)
2286  {
2287  Info<< nl
2288  << "Region " << regionI << nl
2289  << "-------- " << endl;
2290 
2291  createAndWriteRegion
2292  (
2293  mesh,
2294  cellRegion,
2295  regionNames,
2296  prefixRegion,
2297  faceToInterface,
2298  interfacePatches,
2299  regionI,
2300  (overwrite ? oldInstance : runTime.timeName())
2301  );
2302  }
2303  }
2304  }
2305 
2306  Info<< "End\n" << endl;
2307 
2308  return 0;
2309 }
2310 
2311 
2312 // ************************************************************************* //
static const word & zeroGradientType() noexcept
The type name for zeroGradient patch fields.
Definition: fvPatchField.H:206
bool checkParallelSync(const bool report=false) const
Check whether all procs have all zones and in same order.
Definition: ZoneMesh.C:998
This class separates the mesh into distinct unconnected regions, each of which is then given a label ...
Definition: regionSplit.H:136
label findMax(const ListType &input, label start=0)
Linear search for the index of the max element, similar to std::max_element but for lists and returns...
static tmp< DimensionedField< Type, volMesh > > interpolate(const DimensionedField< Type, volMesh > &, const fvMesh &sMesh, const labelUList &cellMap)
Map volume internal (dimensioned) field.
static void noFunctionObjects(bool addWithOption=false)
Remove &#39;-noFunctionObjects&#39; option and ignore any occurrences.
Definition: argList.C:562
static void reorderPatches(fvMesh &, const labelList &oldToNew, const label nPatches, const bool validBoundary)
Reorder and remove trailing patches.
Definition: fvMeshTools.C:317
static void addNote(const string &note)
Add extra notes for the usage information.
Definition: argList.C:477
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:119
const labelIOList & zoneIDs
Definition: correctPhi.H:59
void clearAddressing()
Clear addressing.
Definition: ZoneMesh.C:944
nearest face on selected patch
A list of face labels.
Definition: faceSet.H:47
wordList ReadFields(const typename GeoMesh::Mesh &mesh, const IOobjectList &objects, PtrList< GeometricField< Type, PatchField, GeoMesh >> &fields, const bool syncPar=true, const bool readOldTime=false)
Read Geometric fields of templated type.
List of IOobjects with searching and retrieving facilities. Implemented as a HashTable, so the various sorted methods should be used if traversing in parallel.
Definition: IOobjectList.H:55
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
const fileName & facesInstance() const
Return the current instance directory for faces.
Definition: polyMesh.C:844
void transfer(List< T > &list)
Transfer the contents of the argument List into this list and annul the argument list.
Definition: List.C:347
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:130
static void broadcasts(const int communicator, Type &value, Args &&... values)
Broadcast multiple items to all communicator ranks. Does nothing in non-parallel. ...
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:652
void append(const T &val)
Append an element at the end of the list.
Definition: List.H:496
bool found(const Key &key) const
Same as contains()
Definition: HashTable.H:1370
virtual const labelList & faceNeighbour() const
Return face neighbour.
Definition: polyMesh.C:1107
const labelIOList & tetBasePtIs() const
Return the tetBasePtIs.
Definition: polyMesh.C:884
static void swapBoundaryCellList(const polyMesh &mesh, const UList< T > &cellData, List< T > &neighbourCellData, const bool parRun=UPstream::parRun())
Extract and swap to obtain neighbour cell values for all boundary faces.
const word & name() const noexcept
Return the object name.
Definition: IOobjectI.H:205
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
bool empty() const noexcept
True if List is empty (ie, size() is zero)
Definition: UList.H:702
Given list of cells to remove, insert all the topology changes.
Definition: removeCells.H:59
static void swapBoundaryFaceList(const polyMesh &mesh, UList< T > &faceValues, const bool parRun=UPstream::parRun())
Swap coupled boundary face values. Uses eqOp.
Definition: syncTools.H:524
static word meshSubDir
Return the mesh sub-directory name (usually "polyMesh")
Definition: polyMesh.H:412
void setSize(const label n)
Same as resize()
Definition: PtrList.H:360
engineTime & runTime
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:518
static bool & parRun() noexcept
Test if this a parallel run.
Definition: UPstream.H:1774
Required Classes.
void clearOut(const bool isMeshUpdate=false)
Clear all geometry and addressing.
Definition: fvMesh.C:227
static void addBoolOption(const word &optName, const string &usage="", bool advanced=false)
Add a bool option to validOptions with usage information.
Definition: argList.C:389
labelList findIndices(const ListType &input, typename ListType::const_reference val, label start=0)
Linear search to find all occurrences of given element.
interfaceProperties interface(alpha1, U, thermo->transportPropertiesDict())
Field reading functions for post-processing utilities.
wordList regionNames
static void broadcast(Type &value, const int communicator=UPstream::worldComm)
Broadcast content (contiguous or non-contiguous) to all communicator ranks. Does nothing in non-paral...
Ignore writing from objectRegistry::writeObject()
const dimensionSet dimless
Dimensionless.
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:360
static label worldComm
Communicator for all ranks. May differ from commGlobal() if local worlds are in use.
Definition: UPstream.H:1094
label nFaces() const noexcept
Number of mesh faces.
bool insert(const Key &key)
Insert a new entry, not overwriting existing entries.
Definition: HashSet.H:229
bool isInternalFace(const label faceIndex) const noexcept
Return true if given face label is internal to the mesh.
Pair< int > faceMap(const label facePi, const face &faceP, const label faceNi, const face &faceN)
void reduce(T &value, [[maybe_unused]] BinaryOp bop, [[maybe_unused]] const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Reduce inplace (cf. MPI Allreduce)
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:400
bool insert(const edge &key, const T &obj)
Copy insert a new entry, not overwriting existing entries.
Definition: HashTableI.H:152
label size() const noexcept
The number of elements in table.
Definition: HashTable.H:358
static void removeFiles(const polyMesh &mesh)
Helper: remove all procAddressing files from mesh instance.
Required Classes.
writeOption writeOpt() const noexcept
Get the write option.
static void removeFiles(const polyMesh &)
Helper: remove all sets files from mesh instance.
Definition: topoSet.C:693
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
const fileName & pointsInstance() const
Return the current instance directory for points.
Definition: polyMesh.C:838
dynamicFvMesh & mesh
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition: exprTraits.C:127
static void recv(Type &value, const int fromProcNo, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm, IOstreamOption::streamFormat fmt=IOstreamOption::BINARY)
Receive and deserialize a value. Uses operator>> for de-serialization.
Definition: IPstream.H:115
labelList indices(const wordRe &matcher, const bool useGroups=true) const
The (sorted) patch indices for all matches, optionally matching zone groups.
Definition: ZoneMesh.C:562
label findZoneID(const word &zoneName) const
Find zone index by name, return -1 if not found.
Definition: ZoneMesh.C:757
An edge is a list of two vertex labels. This can correspond to a directed graph edge or an edge on a ...
Definition: edge.H:59
void clear()
Clear the list, i.e. set size to zero.
Definition: ListI.H:133
const polyBoundaryMesh & boundaryMesh() const noexcept
Return boundary mesh.
Definition: polyMesh.H:611
A class for handling words, derived from Foam::string.
Definition: word.H:63
iterator find(const edge &key)
Find and return an iterator set at the hashed entry.
Definition: HashTableI.H:86
static label addPatch(fvMesh &mesh, const polyPatch &patch, const dictionary &patchFieldDict, const word &defaultPatchFieldType, const bool validBoundary)
Add patch. Inserts patch before all processor patches.
Definition: fvMeshTools.C:38
Determines a mapping between patch face centres and mesh cell or face centres and processors they&#39;re ...
static word defaultRegion
Return the default region name.
Definition: polyMesh.H:407
static constexpr int masterNo() noexcept
Relative rank for the master process - is always 0.
Definition: UPstream.H:1784
label size() const noexcept
The number of entries in the list.
Definition: UPtrListI.H:106
virtual const labelList & faceOwner() const
Return face owner.
Definition: polyMesh.C:1101
Reading is optional [identical to LAZY_READ].
static const word null
An empty word.
Definition: word.H:84
A List of wordRe with additional matching capabilities.
Definition: wordRes.H:53
static void addOption(const word &optName, const string &param="", const string &usage="", bool advanced=false)
Add an option to validOptions with usage information.
Definition: argList.C:400
virtual bool write(const token &tok)=0
Write token to stream or otherwise handle it.
T returnReduce(const T &value, BinaryOp bop, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Perform reduction on a copy, using specified binary operation.
label nInternalFaces() const noexcept
Number of internal faces.
static void createDummyFvMeshFiles(const objectRegistry &parent, const word &regionName, const bool verbose=false)
Create additional fvSchemes/fvSolution files.
Definition: fvMeshTools.C:1228
const vectorField & cellCentres() const
bool returnReduceOr(const bool value, const int communicator=UPstream::worldComm)
Perform logical (or) MPI Allreduce on a copy. Uses UPstream::reduceOr.
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:174
label find(const T &val) const
Find index of the first occurrence of the value.
Definition: UList.C:160
virtual bool write(const bool writeOnProc=true) const
Write mesh using IO settings from time.
Definition: fvMesh.C:1068
A polyBoundaryMesh is a polyPatch list with registered IO, a reference to the associated polyMesh...
bool empty() const noexcept
True if the hash table is empty.
Definition: HashTable.H:353
label index() const noexcept
The index of this zone in the zone list.
bool checkParallelSync(const bool report=false) const
Check whether all procs have all patches and in same order.
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
static word timeName(const scalar t, const int precision=precision_)
Return a time name for the given scalar time value formatted with the given precision.
Definition: Time.C:713
Map from edge (expressed as its endpoints) to value. Hashing (and ==) on an edge is symmetric...
Definition: edgeHashes.H:56
void updateMesh()
Correct polyBoundaryMesh after topology update.
Type gMax(const FieldField< Field, Type > &f)
const faceZoneMesh & faceZones() const noexcept
Return face zone mesh.
Definition: polyMesh.H:673
bool readListIfPresent(const word &optName, List< T > &list) const
If named option is present, get a List of values treating a single entry like a list of size 1...
Definition: argListI.H:387
A subset of mesh cells.
Definition: cellZone.H:58
gmvFile<< "tracers "<< particles.size()<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().x()<< ' ';}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().y()<< ' ';}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().z()<< ' ';}gmvFile<< nl;for(const word &name :lagrangianScalarNames){ IOField< scalar > fld(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
label whichZone(const label objectIndex) const
Given a global object index, return the zone it is in.
Definition: ZoneMesh.C:410
const vectorField & faceCentres() const
T get(const label index) const
Get a value from the argument at index.
Definition: argListI.H:271
void setInstance(const fileName &instance, const IOobjectOption::writeOption wOpt=IOobject::AUTO_WRITE)
Set the instance for mesh files.
Definition: polyMeshIO.C:29
#define WarningInFunction
Report a warning using Foam::Warning.
const word & name() const
Return reference to name.
Definition: fvMesh.H:387
Foam::word regionName(args.getOrDefault< word >("region", Foam::polyMesh::defaultRegion))
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
label nCells() const noexcept
Number of mesh cells.
A list of pointers to objects of type <T>, with allocation/deallocation management of the pointers...
Definition: PtrList.H:56
A collection of cell labels.
Definition: cellSet.H:47
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:78
A List with indirect addressing. Like IndirectList but does not store addressing. ...
Definition: faMatrix.H:52
Direct mesh changes based on v1.3 polyTopoChange syntax.
const word & name() const noexcept
The zone name.
Nothing to be read.
static const word & calculatedType() noexcept
The type name for calculated patch fields.
Definition: fvPatchField.H:189
Automatically write from objectRegistry::writeObject()
const cellZoneMesh & cellZones() const noexcept
Return cell zone mesh.
Definition: polyMesh.H:681
label findCell(const point &p, const cellDecomposition=CELL_TETS) const
Find cell enclosing this location and return index.
Definition: polyMesh.C:1496
messageStream Info
Information stream (stdout output on master, null elsewhere)
static bool master(label communicator=worldComm)
True if process corresponds to the master rank in the communicator.
Definition: UPstream.H:1807
const boundBox & bounds() const noexcept
Return mesh bounding box.
Definition: polyMesh.H:619
Pointer management similar to std::unique_ptr, with some additional methods and type checking...
Definition: HashPtrTable.H:48
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:76
bool readIfPresent(const word &optName, T &val) const
Read a value from the named option if present.
Definition: argListI.H:316
static edge sorted(label from, label to)
Create (in ascending order) from two vertex labels.
Definition: edgeI.H:32
List< label > labelList
A List of labels.
Definition: List.H:61
auto & names
A class for managing temporary objects.
Definition: HashPtrTable.H:50
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:69
label nBoundaryFaces() const noexcept
Number of boundary faces (== nFaces - nInternalFaces)
Foam::argList args(argc, argv)
void write(vtk::formatter &fmt, const Type &val, const label n=1)
Component-wise write of a value (N times)
bool send()
Send buffer contents now and not in destructor [advanced usage]. Returns true on success.
Definition: OPstreams.C:96
Defines the attributes of an object for which implicit objectRegistry management is supported...
Definition: IOobject.H:188
static void listReduce(UList< T > &values, BinaryOp bop, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Reduce list elements (list must be equal size on all ranks), applying bop to each list element...
void setSize(label n)
Alias for resize()
Definition: List.H:535
Do not request registration (bool: false)
bool found(const word &optName) const
Return true if the named option is found.
Definition: argListI.H:171
static rangeType subProcs(label communicator=worldComm)
Range of process indices for sub-processes.
Definition: UPstream.H:1959
uindirectPrimitivePatch pp(UIndirectList< face >(mesh.faces(), faceLabels), mesh.points())
Namespace for OpenFOAM.
forAllConstIters(mixture.phases(), phase)
Definition: pEqn.H:28
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...
FlatOutput::OutputAdaptor< Container, Delimiters > flatOutput(const Container &obj, Delimiters delim)
Global flatOutput() function with specified output delimiters.
Definition: FlatOutput.H:217
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127