splitMeshRegions.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2011-2020 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 Application
25  splitMeshRegions
26 
27 Description
28  Splits mesh into multiple regions.
29 
30  Each region is defined as a domain whose cells can all be reached by
31  cell-face-cell walking without crossing
32  - boundary faces
33  - additional faces from faceset (-blockedFaces faceSet).
34  - any face in between differing cellZones (-cellZones)
35 
36  Output is:
37  - volScalarField with regions as different scalars (-detectOnly)
38  or
39  - mesh with multiple regions and mapped patches. These patches
40  either cover the whole interface between two region (default) or
41  only part according to faceZones (-useFaceZones)
42  or
43  - mesh with cells put into cellZones (-makeCellZones)
44 
45  Note:
46  - cellZonesOnly does not do a walk and uses the cellZones only. Use
47  this if you don't mind having disconnected domains in a single region.
48  This option requires all cells to be in one (and one only) cellZone.
49 
50  - cellZonesFileOnly behaves like -cellZonesOnly but reads the cellZones
51  from the specified file. This allows one to explicitly specify the region
52  distribution and still have multiple cellZones per region.
53 
54  - useCellZonesOnly does not do a walk and uses the cellZones only. Use
55  this if you don't mind having disconnected domains in a single region.
56  This option requires all cells to be in one (and one only) cellZone.
57 
58  - prefixRegion prefixes all normal patches with region name (interface
59  (patches already have region name prefix)
60 
61  - Should work in parallel.
62  cellZones can differ on either side of processor boundaries in which case
63  the faces get moved from processor patch to directMapped patch. Not
64  the faces get moved from processor patch to mapped patch. Not
65  very well tested.
66 
67  - If a cell zone gets split into more than one region it can detect
68  the largest matching region (-sloppyCellZones). This will accept any
69  region that covers more than 50% of the zone. It has to be a subset
70  so cannot have any cells in any other zone.
71 
72  - If explicitly a single region has been selected (-largestOnly or
73  -insidePoint) its region name will be either
74  - name of a cellZone it matches to or
75  - "largestOnly" respectively "insidePoint" or
76  - polyMesh::defaultRegion if additionally -overwrite
77  (so it will overwrite the input mesh!)
78 
79  - writes maps like decomposePar back to original mesh:
80  - pointRegionAddressing : for every point in this region the point in
81  the original mesh
82  - cellRegionAddressing : ,, cell ,, cell ,,
83  - faceRegionAddressing : ,, face ,, face in
84  the original mesh + 'turning index'. For a face in the same orientation
85  this is the original facelabel+1, for a turned face this is -facelabel-1
86  - boundaryRegionAddressing : for every patch in this region the
87  patch in the original mesh (or -1 if added patch)
88 
89 \*---------------------------------------------------------------------------*/
90 
91 #include "SortableList.H"
92 #include "argList.H"
93 #include "regionSplit.H"
94 #include "fvMeshSubset.H"
95 #include "IOobjectList.H"
96 #include "volFields.H"
97 #include "faceSet.H"
98 #include "cellSet.H"
99 #include "polyTopoChange.H"
100 #include "removeCells.H"
101 #include "EdgeMap.H"
102 #include "syncTools.H"
103 #include "ReadFields.H"
104 #include "mappedWallPolyPatch.H"
105 #include "fvMeshTools.H"
107 
108 using namespace Foam;
109 
110 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
111 
112 // Standard default region base name
113 const word standardRegionName("region");
114 
115 
116 // Prepend prefix to selected patches.
117 void renamePatches
118 (
119  fvMesh& mesh,
120  const word& prefix,
121  const labelList& patchesToRename
122 )
123 {
124  polyBoundaryMesh& polyPatches =
125  const_cast<polyBoundaryMesh&>(mesh.boundaryMesh());
126  forAll(patchesToRename, i)
127  {
128  label patchi = patchesToRename[i];
129  polyPatch& pp = polyPatches[patchi];
130 
131  if (isA<coupledPolyPatch>(pp))
132  {
134  << "Encountered coupled patch " << pp.name()
135  << ". Will only rename the patch itself,"
136  << " not any referred patches."
137  << " This might have to be done by hand."
138  << endl;
139  }
140 
141  pp.name() = prefix + '_' + pp.name();
142  }
143  // Recalculate any demand driven data (e.g. group to name lookup)
144  polyPatches.updateMesh();
145 }
146 
147 
148 template<class GeoField>
149 void subsetVolFields
150 (
151  const fvMesh& mesh,
152  const fvMesh& subMesh,
153  const labelList& cellMap,
154  const labelList& faceMap,
155  const labelHashSet& addedPatches
156 )
157 {
158  const labelList patchMap(identity(mesh.boundaryMesh().size()));
159 
161  (
162  mesh.objectRegistry::lookupClass<GeoField>()
163  );
164  forAllConstIter(typename HashTable<const GeoField*>, fields, iter)
165  {
166  const GeoField& fld = *iter();
167 
168  Info<< "Mapping field " << fld.name() << endl;
169 
170  tmp<GeoField> tSubFld
171  (
173  (
174  fld,
175  subMesh,
176  patchMap,
177  cellMap,
178  faceMap
179  )
180  );
181 
182  // Hack: set value to 0 for introduced patches (since don't
183  // get initialised.
184  forAll(tSubFld().boundaryField(), patchi)
185  {
186  if (addedPatches.found(patchi))
187  {
188  tSubFld.ref().boundaryFieldRef()[patchi] ==
189  typename GeoField::value_type(Zero);
190  }
191  }
192 
193  // Store on subMesh
194  GeoField* subFld = tSubFld.ptr();
195  subFld->rename(fld.name());
196  subFld->writeOpt() = IOobject::AUTO_WRITE;
197  subFld->store();
198  }
199 }
200 
201 
202 template<class GeoField>
203 void subsetSurfaceFields
204 (
205  const fvMesh& mesh,
206  const fvMesh& subMesh,
207  const labelList& cellMap,
208  const labelList& faceMap,
209  const labelHashSet& addedPatches
210 )
211 {
212  const labelList patchMap(identity(mesh.boundaryMesh().size()));
213 
215  (
216  mesh.objectRegistry::lookupClass<GeoField>()
217  );
218  forAllConstIter(typename HashTable<const GeoField*>, fields, iter)
219  {
220  const GeoField& fld = *iter();
221 
222  Info<< "Mapping field " << fld.name() << endl;
223 
224  tmp<GeoField> tSubFld
225  (
227  (
228  fld,
229  subMesh,
230  patchMap,
231  cellMap,
232  faceMap
233  )
234  );
235 
236  // Hack: set value to 0 for introduced patches (since don't
237  // get initialised.
238  forAll(tSubFld().boundaryField(), patchi)
239  {
240  if (addedPatches.found(patchi))
241  {
242  tSubFld.ref().boundaryFieldRef()[patchi] ==
243  typename GeoField::value_type(Zero);
244  }
245  }
246 
247  // Store on subMesh
248  GeoField* subFld = tSubFld.ptr();
249  subFld->rename(fld.name());
250  subFld->writeOpt() = IOobject::AUTO_WRITE;
251  subFld->store();
252  }
253 }
254 
255 // Select all cells not in the region
256 labelList getNonRegionCells(const labelList& cellRegion, const label regioni)
257 {
258  DynamicList<label> nonRegionCells(cellRegion.size());
259  forAll(cellRegion, celli)
260  {
261  if (cellRegion[celli] != regioni)
262  {
263  nonRegionCells.append(celli);
264  }
265  }
266  return nonRegionCells.shrink();
267 }
268 
269 
270 void addToInterface
271 (
272  const polyMesh& mesh,
273  const label zoneID,
274  const label ownRegion,
275  const label neiRegion,
276  EdgeMap<Map<label>>& regionsToSize
277 )
278 {
279  edge interface
280  (
281  min(ownRegion, neiRegion),
282  max(ownRegion, neiRegion)
283  );
284 
285  EdgeMap<Map<label>>::iterator iter = regionsToSize.find
286  (
287  interface
288  );
289 
290  if (iter != regionsToSize.end())
291  {
292  // Check if zone present
293  Map<label>::iterator zoneFnd = iter().find(zoneID);
294  if (zoneFnd != iter().end())
295  {
296  // Found zone. Increment count.
297  zoneFnd()++;
298  }
299  else
300  {
301  // New or no zone. Insert with count 1.
302  iter().insert(zoneID, 1);
303  }
304  }
305  else
306  {
307  // Create new interface of size 1.
308  Map<label> zoneToSize;
309  zoneToSize.insert(zoneID, 1);
310  regionsToSize.insert(interface, zoneToSize);
311  }
312 }
313 
314 
315 // Get region-region interface name and sizes.
316 // Returns interfaces as straight list for looping in identical order.
317 void getInterfaceSizes
318 (
319  const polyMesh& mesh,
320  const bool useFaceZones,
321  const labelList& cellRegion,
322  const wordList& regionNames,
323 
324  edgeList& interfaces,
325  List<Pair<word>>& interfaceNames,
326  labelList& interfaceSizes,
327  labelList& faceToInterface
328 )
329 {
330  // From region-region to faceZone (or -1) to number of faces.
331 
332  EdgeMap<Map<label>> regionsToSize;
333 
334 
335  // Internal faces
336  // ~~~~~~~~~~~~~~
337 
338  forAll(mesh.faceNeighbour(), facei)
339  {
340  label ownRegion = cellRegion[mesh.faceOwner()[facei]];
341  label neiRegion = cellRegion[mesh.faceNeighbour()[facei]];
342 
343  if (ownRegion != neiRegion)
344  {
345  addToInterface
346  (
347  mesh,
348  (useFaceZones ? mesh.faceZones().whichZone(facei) : -1),
349  ownRegion,
350  neiRegion,
351  regionsToSize
352  );
353  }
354  }
355 
356  // Boundary faces
357  // ~~~~~~~~~~~~~~
358 
359  // Neighbour cellRegion.
360  labelList coupledRegion(mesh.nFaces()-mesh.nInternalFaces());
361 
362  forAll(coupledRegion, i)
363  {
364  label celli = mesh.faceOwner()[i+mesh.nInternalFaces()];
365  coupledRegion[i] = cellRegion[celli];
366  }
367  syncTools::swapBoundaryFaceList(mesh, coupledRegion);
368 
369  forAll(coupledRegion, i)
370  {
371  label facei = i+mesh.nInternalFaces();
372  label ownRegion = cellRegion[mesh.faceOwner()[facei]];
373  label neiRegion = coupledRegion[i];
374 
375  if (ownRegion != neiRegion)
376  {
377  addToInterface
378  (
379  mesh,
380  (useFaceZones ? mesh.faceZones().whichZone(facei) : -1),
381  ownRegion,
382  neiRegion,
383  regionsToSize
384  );
385  }
386  }
387 
388 
389  if (Pstream::parRun())
390  {
391  if (Pstream::master())
392  {
393  // Receive and add to my sizes
394  for
395  (
396  int slave=Pstream::firstSlave();
397  slave<=Pstream::lastSlave();
398  slave++
399  )
400  {
401  IPstream fromSlave(Pstream::commsTypes::blocking, slave);
402 
403  EdgeMap<Map<label>> slaveSizes(fromSlave);
404 
405  forAllConstIter(EdgeMap<Map<label>>, slaveSizes, slaveIter)
406  {
407  EdgeMap<Map<label>>::iterator masterIter =
408  regionsToSize.find(slaveIter.key());
409 
410  if (masterIter != regionsToSize.end())
411  {
412  // Same inter-region
413  const Map<label>& slaveInfo = slaveIter();
414  Map<label>& masterInfo = masterIter();
415 
416  forAllConstIter(Map<label>, slaveInfo, iter)
417  {
418  label zoneID = iter.key();
419  label slaveSize = iter();
420 
421  Map<label>::iterator zoneFnd = masterInfo.find
422  (
423  zoneID
424  );
425  if (zoneFnd != masterInfo.end())
426  {
427  zoneFnd() += slaveSize;
428  }
429  else
430  {
431  masterInfo.insert(zoneID, slaveSize);
432  }
433  }
434  }
435  else
436  {
437  regionsToSize.insert(slaveIter.key(), slaveIter());
438  }
439  }
440  }
441  }
442  else
443  {
444  // Send to master
445  {
446  OPstream toMaster
447  (
450  );
451  toMaster << regionsToSize;
452  }
453  }
454  }
455 
456  // Rework
457 
458  Pstream::scatter(regionsToSize);
459 
460 
461 
462  // Now we have the global sizes of all inter-regions.
463  // Invert this on master and distribute.
464  label nInterfaces = 0;
465  forAllConstIter(EdgeMap<Map<label>>, regionsToSize, iter)
466  {
467  const Map<label>& info = iter();
468  nInterfaces += info.size();
469  }
470 
471  interfaces.setSize(nInterfaces);
472  interfaceNames.setSize(nInterfaces);
473  interfaceSizes.setSize(nInterfaces);
474  EdgeMap<Map<label>> regionsToInterface(nInterfaces);
475 
476  nInterfaces = 0;
477  forAllConstIter(EdgeMap<Map<label>>, regionsToSize, iter)
478  {
479  const edge& e = iter.key();
480  const word& name0 = regionNames[e[0]];
481  const word& name1 = regionNames[e[1]];
482 
483  const Map<label>& info = iter();
484  forAllConstIter(Map<label>, info, infoIter)
485  {
486  interfaces[nInterfaces] = iter.key();
487  label zoneID = infoIter.key();
488  if (zoneID == -1)
489  {
490  interfaceNames[nInterfaces] = Pair<word>
491  (
492  name0 + "_to_" + name1,
493  name1 + "_to_" + name0
494  );
495  }
496  else
497  {
498  const word& zoneName = mesh.faceZones()[zoneID].name();
499  interfaceNames[nInterfaces] = Pair<word>
500  (
501  zoneName + "_" + name0 + "_to_" + name1,
502  zoneName + "_" + name1 + "_to_" + name0
503  );
504  }
505  interfaceSizes[nInterfaces] = infoIter();
506 
507  if (regionsToInterface.found(e))
508  {
509  regionsToInterface[e].insert(zoneID, nInterfaces);
510  }
511  else
512  {
513  Map<label> zoneAndInterface;
514  zoneAndInterface.insert(zoneID, nInterfaces);
515  regionsToInterface.insert(e, zoneAndInterface);
516  }
517  nInterfaces++;
518  }
519  }
520 
521 
522  // Now all processor have consistent interface information
523 
524  Pstream::scatter(interfaces);
525  Pstream::scatter(interfaceNames);
526  Pstream::scatter(interfaceSizes);
527  Pstream::scatter(regionsToInterface);
528 
529  // Mark all inter-region faces.
530  faceToInterface.setSize(mesh.nFaces(), -1);
531 
532  forAll(mesh.faceNeighbour(), facei)
533  {
534  label ownRegion = cellRegion[mesh.faceOwner()[facei]];
535  label neiRegion = cellRegion[mesh.faceNeighbour()[facei]];
536 
537  if (ownRegion != neiRegion)
538  {
539  label zoneID = -1;
540  if (useFaceZones)
541  {
542  zoneID = mesh.faceZones().whichZone(facei);
543  }
544 
545  edge interface
546  (
547  min(ownRegion, neiRegion),
548  max(ownRegion, neiRegion)
549  );
550 
551  faceToInterface[facei] = regionsToInterface[interface][zoneID];
552  }
553  }
554  forAll(coupledRegion, i)
555  {
556  label facei = i+mesh.nInternalFaces();
557  label ownRegion = cellRegion[mesh.faceOwner()[facei]];
558  label neiRegion = coupledRegion[i];
559 
560  if (ownRegion != neiRegion)
561  {
562  label zoneID = -1;
563  if (useFaceZones)
564  {
565  zoneID = mesh.faceZones().whichZone(facei);
566  }
567 
568  edge interface
569  (
570  min(ownRegion, neiRegion),
571  max(ownRegion, neiRegion)
572  );
573 
574  faceToInterface[facei] = regionsToInterface[interface][zoneID];
575  }
576  }
577 }
578 
579 
580 // Create mesh for region.
581 autoPtr<mapPolyMesh> createRegionMesh
582 (
583  const fvMesh& mesh,
584  // Region info
585  const labelList& cellRegion,
586  const label regioni,
587  const word& regionName,
588  // Interface info
589  const labelList& interfacePatches,
590  const labelList& faceToInterface,
591 
592  autoPtr<fvMesh>& newMesh
593 )
594 {
595  // Create dummy system/fv*
596  {
597  IOobject io
598  (
599  "fvSchemes",
600  mesh.time().system(),
601  regionName,
602  mesh,
605  false
606  );
607 
608  Info<< "Testing:" << io.objectPath() << endl;
609 
610  if (!io.typeHeaderOk<IOdictionary>(true))
611  // if (!exists(io.objectPath()))
612  {
613  Info<< "Writing dummy " << regionName/io.name() << endl;
614  dictionary dummyDict;
615  dictionary divDict;
616  dummyDict.add("divSchemes", divDict);
617  dictionary gradDict;
618  dummyDict.add("gradSchemes", gradDict);
619  dictionary laplDict;
620  dummyDict.add("laplacianSchemes", laplDict);
621 
622  IOdictionary(io, dummyDict).regIOobject::write();
623  }
624  }
625  {
626  IOobject io
627  (
628  "fvSolution",
629  mesh.time().system(),
630  regionName,
631  mesh,
634  false
635  );
636 
637  if (!io.typeHeaderOk<IOdictionary>(true))
638  // if (!exists(io.objectPath()))
639  {
640  Info<< "Writing dummy " << regionName/io.name() << endl;
641  dictionary dummyDict;
642  IOdictionary(io, dummyDict).regIOobject::write();
643  }
644  }
645 
646 
647  // Neighbour cellRegion.
648  labelList coupledRegion(mesh.nFaces()-mesh.nInternalFaces());
649 
650  forAll(coupledRegion, i)
651  {
652  label celli = mesh.faceOwner()[i+mesh.nInternalFaces()];
653  coupledRegion[i] = cellRegion[celli];
654  }
655  syncTools::swapBoundaryFaceList(mesh, coupledRegion);
656 
657 
658  // Topology change container. Start off from existing mesh.
659  polyTopoChange meshMod(mesh);
660 
661  // Cell remover engine
662  removeCells cellRemover(mesh);
663 
664  // Select all but region cells
665  labelList cellsToRemove(getNonRegionCells(cellRegion, regioni));
666 
667  // Find out which faces will get exposed. Note that this
668  // gets faces in mesh face order. So both regions will get same
669  // face in same order (important!)
670  labelList exposedFaces = cellRemover.getExposedFaces(cellsToRemove);
671 
672  labelList exposedPatchIDs(exposedFaces.size());
673  forAll(exposedFaces, i)
674  {
675  label facei = exposedFaces[i];
676  label interfacei = faceToInterface[facei];
677 
678  label ownRegion = cellRegion[mesh.faceOwner()[facei]];
679  label neiRegion = -1;
680 
681  if (mesh.isInternalFace(facei))
682  {
683  neiRegion = cellRegion[mesh.faceNeighbour()[facei]];
684  }
685  else
686  {
687  neiRegion = coupledRegion[facei-mesh.nInternalFaces()];
688  }
689 
690 
691  // Check which side is being kept - determines which of the two
692  // patches will be used.
693 
694  label otherRegion = -1;
695 
696  if (ownRegion == regioni && neiRegion != regioni)
697  {
698  otherRegion = neiRegion;
699  }
700  else if (ownRegion != regioni && neiRegion == regioni)
701  {
702  otherRegion = ownRegion;
703  }
704  else
705  {
707  << "Exposed face:" << facei
708  << " fc:" << mesh.faceCentres()[facei]
709  << " has owner region " << ownRegion
710  << " and neighbour region " << neiRegion
711  << " when handling region:" << regioni
712  << exit(FatalError);
713  }
714 
715  // Find the patch.
716  if (regioni < otherRegion)
717  {
718  exposedPatchIDs[i] = interfacePatches[interfacei];
719  }
720  else
721  {
722  exposedPatchIDs[i] = interfacePatches[interfacei]+1;
723  }
724  }
725 
726  // Remove faces
727  cellRemover.setRefinement
728  (
729  cellsToRemove,
730  exposedFaces,
731  exposedPatchIDs,
732  meshMod
733  );
734 
735  autoPtr<mapPolyMesh> map = meshMod.makeMesh
736  (
737  newMesh,
738  IOobject
739  (
740  regionName,
741  mesh.time().timeName(),
742  mesh.time(),
745  ),
746  mesh
747  );
748 
749  return map;
750 }
751 
752 
753 void createAndWriteRegion
754 (
755  const fvMesh& mesh,
756  const labelList& cellRegion,
757  const wordList& regionNames,
758  const bool prefixRegion,
759  const labelList& faceToInterface,
760  const labelList& interfacePatches,
761  const label regioni,
762  const word& newMeshInstance
763 )
764 {
765  Info<< "Creating mesh for region " << regioni
766  << ' ' << regionNames[regioni] << endl;
767 
768  autoPtr<fvMesh> newMesh;
769  autoPtr<mapPolyMesh> map = createRegionMesh
770  (
771  mesh,
772  cellRegion,
773  regioni,
774  regionNames[regioni],
775  interfacePatches,
776  faceToInterface,
777  newMesh
778  );
779 
780 
781  // Make map of all added patches
782  labelHashSet addedPatches(2*interfacePatches.size());
783  forAll(interfacePatches, interfacei)
784  {
785  addedPatches.insert(interfacePatches[interfacei]);
786  addedPatches.insert(interfacePatches[interfacei]+1);
787  }
788 
789 
790  Info<< "Mapping fields" << endl;
791 
792  // Map existing fields
793  newMesh().updateMesh(map());
794 
795  // Add subsetted fields
796  subsetVolFields<volScalarField>
797  (
798  mesh,
799  newMesh(),
800  map().cellMap(),
801  map().faceMap(),
802  addedPatches
803  );
804  subsetVolFields<volVectorField>
805  (
806  mesh,
807  newMesh(),
808  map().cellMap(),
809  map().faceMap(),
810  addedPatches
811  );
812  subsetVolFields<volSphericalTensorField>
813  (
814  mesh,
815  newMesh(),
816  map().cellMap(),
817  map().faceMap(),
818  addedPatches
819  );
820  subsetVolFields<volSymmTensorField>
821  (
822  mesh,
823  newMesh(),
824  map().cellMap(),
825  map().faceMap(),
826  addedPatches
827  );
828  subsetVolFields<volTensorField>
829  (
830  mesh,
831  newMesh(),
832  map().cellMap(),
833  map().faceMap(),
834  addedPatches
835  );
836 
837  subsetSurfaceFields<surfaceScalarField>
838  (
839  mesh,
840  newMesh(),
841  map().cellMap(),
842  map().faceMap(),
843  addedPatches
844  );
845  subsetSurfaceFields<surfaceVectorField>
846  (
847  mesh,
848  newMesh(),
849  map().cellMap(),
850  map().faceMap(),
851  addedPatches
852  );
853  subsetSurfaceFields<surfaceSphericalTensorField>
854  (
855  mesh,
856  newMesh(),
857  map().cellMap(),
858  map().faceMap(),
859  addedPatches
860  );
861  subsetSurfaceFields<surfaceSymmTensorField>
862  (
863  mesh,
864  newMesh(),
865  map().cellMap(),
866  map().faceMap(),
867  addedPatches
868  );
869  subsetSurfaceFields<surfaceTensorField>
870  (
871  mesh,
872  newMesh(),
873  map().cellMap(),
874  map().faceMap(),
875  addedPatches
876  );
877 
878 
879  const polyBoundaryMesh& newPatches = newMesh().boundaryMesh();
880  newPatches.checkParallelSync(true);
881 
882  // Delete empty patches
883  // ~~~~~~~~~~~~~~~~~~~~
884 
885  // Create reordering list to move patches-to-be-deleted to end
886  labelList oldToNew(newPatches.size(), -1);
887  DynamicList<label> sharedPatches(newPatches.size());
888  label newI = 0;
889 
890  Info<< "Deleting empty patches" << endl;
891 
892  // Assumes all non-proc boundaries are on all processors!
893  forAll(newPatches, patchi)
894  {
895  const polyPatch& pp = newPatches[patchi];
896 
897  if (!isA<processorPolyPatch>(pp))
898  {
899  if (returnReduce(pp.size(), sumOp<label>()) > 0)
900  {
901  oldToNew[patchi] = newI;
902  if (!addedPatches.found(patchi))
903  {
904  sharedPatches.append(newI);
905  }
906  newI++;
907  }
908  }
909  }
910 
911  // Same for processor patches (but need no reduction)
912  forAll(newPatches, patchi)
913  {
914  const polyPatch& pp = newPatches[patchi];
915 
916  if (isA<processorPolyPatch>(pp) && pp.size())
917  {
918  oldToNew[patchi] = newI++;
919  }
920  }
921 
922  const label nNewPatches = newI;
923 
924  // Move all deleteable patches to the end
925  forAll(oldToNew, patchi)
926  {
927  if (oldToNew[patchi] == -1)
928  {
929  oldToNew[patchi] = newI++;
930  }
931  }
932 
933  // reorderPatches(newMesh(), oldToNew, nNewPatches);
934  fvMeshTools::reorderPatches(newMesh(), oldToNew, nNewPatches, true);
935 
936  // Rename shared patches with region name
937  if (prefixRegion)
938  {
939  Info<< "Prefixing patches with region name" << endl;
940 
941  renamePatches(newMesh(), regionNames[regioni], sharedPatches);
942  }
943 
944 
945  Info<< "Writing new mesh" << endl;
946 
947  newMesh().setInstance(newMeshInstance);
948  newMesh().write();
949 
950  // Write addressing files like decomposePar
951  Info<< "Writing addressing to base mesh" << endl;
952 
953  labelIOList pointProcAddressing
954  (
955  IOobject
956  (
957  "pointRegionAddressing",
958  newMesh().facesInstance(),
959  newMesh().meshSubDir,
960  newMesh(),
961  IOobject::NO_READ,
962  IOobject::NO_WRITE,
963  false
964  ),
965  map().pointMap()
966  );
967  Info<< "Writing map " << pointProcAddressing.name()
968  << " from region" << regioni
969  << " points back to base mesh." << endl;
970  pointProcAddressing.write();
971 
973  (
974  IOobject
975  (
976  "faceRegionAddressing",
977  newMesh().facesInstance(),
978  newMesh().meshSubDir,
979  newMesh(),
980  IOobject::NO_READ,
981  IOobject::NO_WRITE,
982  false
983  ),
984  newMesh().nFaces()
985  );
986  forAll(faceProcAddressing, facei)
987  {
988  // face + turning index. (see decomposePar)
989  // Is the face pointing in the same direction?
990  label oldFacei = map().faceMap()[facei];
991 
992  if
993  (
994  map().cellMap()[newMesh().faceOwner()[facei]]
995  == mesh.faceOwner()[oldFacei]
996  )
997  {
998  faceProcAddressing[facei] = oldFacei+1;
999  }
1000  else
1001  {
1002  faceProcAddressing[facei] = -(oldFacei+1);
1003  }
1004  }
1005  Info<< "Writing map " << faceProcAddressing.name()
1006  << " from region" << regioni
1007  << " faces back to base mesh." << endl;
1008  faceProcAddressing.write();
1009 
1010  labelIOList cellProcAddressing
1011  (
1012  IOobject
1013  (
1014  "cellRegionAddressing",
1015  newMesh().facesInstance(),
1016  newMesh().meshSubDir,
1017  newMesh(),
1018  IOobject::NO_READ,
1019  IOobject::NO_WRITE,
1020  false
1021  ),
1022  map().cellMap()
1023  );
1024  Info<< "Writing map " <<cellProcAddressing.name()
1025  << " from region" << regioni
1026  << " cells back to base mesh." << endl;
1027  cellProcAddressing.write();
1028 
1029  labelIOList boundaryProcAddressing
1030  (
1031  IOobject
1032  (
1033  "boundaryRegionAddressing",
1034  newMesh().facesInstance(),
1035  newMesh().meshSubDir,
1036  newMesh(),
1037  IOobject::NO_READ,
1038  IOobject::NO_WRITE,
1039  false
1040  ),
1041  labelList(nNewPatches, -1)
1042  );
1043  forAll(oldToNew, i)
1044  {
1045  if (!addedPatches.found(i))
1046  {
1047  label newI = oldToNew[i];
1048  if (newI >= 0 && newI < nNewPatches)
1049  {
1050  boundaryProcAddressing[oldToNew[i]] = i;
1051  }
1052  }
1053  }
1054  Info<< "Writing map " << boundaryProcAddressing.name()
1055  << " from region" << regioni
1056  << " boundary back to base mesh." << endl;
1057  boundaryProcAddressing.write();
1058 }
1059 
1060 
1061 // Create for every region-region interface with non-zero size two patches.
1062 // First one is for minimumregion to maximumregion.
1063 // Note that patches get created in same order on all processors (if parallel)
1064 // since looping over synchronised 'interfaces'.
1065 labelList addRegionPatches
1066 (
1067  fvMesh& mesh,
1068  const wordList& regionNames,
1069  const edgeList& interfaces,
1070  const List<Pair<word>>& interfaceNames
1071 )
1072 {
1073  Info<< nl << "Adding patches" << nl << endl;
1074 
1075  labelList interfacePatches(interfaces.size());
1076 
1077  forAll(interfaces, interI)
1078  {
1079  const edge& e = interfaces[interI];
1080  const Pair<word>& names = interfaceNames[interI];
1081 
1082  // Info<< "For interface " << interI
1083  // << " between regions " << e
1084  // << " trying to add patches " << names << endl;
1085 
1086 
1087  mappedWallPolyPatch patch1
1088  (
1089  names[0],
1090  0, // overridden
1091  0, // overridden
1092  0, // overridden
1093  regionNames[e[1]], // sampleRegion
1095  names[1], // samplePatch
1096  point::zero, // offset
1097  mesh.boundaryMesh()
1098  );
1099 
1100  interfacePatches[interI] = fvMeshTools::addPatch
1101  (
1102  mesh,
1103  patch1,
1104  dictionary(), // optional per field value
1106  true // validBoundary
1107  );
1108 
1109  mappedWallPolyPatch patch2
1110  (
1111  names[1],
1112  0,
1113  0,
1114  0,
1115  regionNames[e[0]], // sampleRegion
1117  names[0],
1118  point::zero, // offset
1119  mesh.boundaryMesh()
1120  );
1122  (
1123  mesh,
1124  patch2,
1125  dictionary(), // optional per field value
1127  true // validBoundary
1128  );
1129 
1130  Info<< "For interface between region " << regionNames[e[0]]
1131  << " and " << regionNames[e[1]] << " added patches" << endl
1132  << " " << interfacePatches[interI]
1133  << "\t" << mesh.boundaryMesh()[interfacePatches[interI]].name()
1134  << endl
1135  << " " << interfacePatches[interI]+1
1136  << "\t" << mesh.boundaryMesh()[interfacePatches[interI]+1].name()
1137  << endl;
1138  }
1139  return interfacePatches;
1140 }
1141 
1142 
1143 // Find region that covers most of cell zone
1144 label findCorrespondingRegion
1145 (
1146  const labelList& existingZoneID, // per cell the (unique) zoneID
1147  const labelList& cellRegion,
1148  const label nCellRegions,
1149  const label zoneI,
1150  const label minOverlapSize
1151 )
1152 {
1153  // Per region the number of cells in zoneI
1154  labelList cellsInZone(nCellRegions, 0);
1155 
1156  forAll(cellRegion, celli)
1157  {
1158  if (existingZoneID[celli] == zoneI)
1159  {
1160  cellsInZone[cellRegion[celli]]++;
1161  }
1162  }
1163 
1165  Pstream::listCombineScatter(cellsInZone);
1166 
1167  // Pick region with largest overlap of zoneI
1168  label regioni = findMax(cellsInZone);
1169 
1170 
1171  if (cellsInZone[regioni] < minOverlapSize)
1172  {
1173  // Region covers too little of zone. Not good enough.
1174  regioni = -1;
1175  }
1176  else
1177  {
1178  // Check that region contains no cells that aren't in cellZone.
1179  forAll(cellRegion, celli)
1180  {
1181  if (cellRegion[celli] == regioni && existingZoneID[celli] != zoneI)
1182  {
1183  // celli in regioni but not in zoneI
1184  regioni = -1;
1185  break;
1186  }
1187  }
1188  // If one in error, all should be in error. Note that branch gets taken
1189  // on all procs.
1190  reduce(regioni, minOp<label>());
1191  }
1192 
1193  return regioni;
1194 }
1195 
1196 
1197 // Get zone per cell
1198 // - non-unique zoning
1199 // - coupled zones
1200 void getZoneID
1201 (
1202  const polyMesh& mesh,
1203  const cellZoneMesh& cellZones,
1204  labelList& zoneID,
1205  labelList& neiZoneID
1206 )
1207 {
1208  // Existing zoneID
1209  zoneID.setSize(mesh.nCells());
1210  zoneID = -1;
1211 
1212  forAll(cellZones, zoneI)
1213  {
1214  const cellZone& cz = cellZones[zoneI];
1215 
1216  forAll(cz, i)
1217  {
1218  label celli = cz[i];
1219  if (zoneID[celli] == -1)
1220  {
1221  zoneID[celli] = zoneI;
1222  }
1223  else
1224  {
1226  << "Cell " << celli << " with cell centre "
1227  << mesh.cellCentres()[celli]
1228  << " is multiple zones. This is not allowed." << endl
1229  << "It is in zone " << cellZones[zoneID[celli]].name()
1230  << " and in zone " << cellZones[zoneI].name()
1231  << exit(FatalError);
1232  }
1233  }
1234  }
1235 
1236  // Neighbour zoneID.
1237  neiZoneID.setSize(mesh.nFaces()-mesh.nInternalFaces());
1238 
1239  forAll(neiZoneID, i)
1240  {
1241  neiZoneID[i] = zoneID[mesh.faceOwner()[i+mesh.nInternalFaces()]];
1242  }
1243  syncTools::swapBoundaryFaceList(mesh, neiZoneID);
1244 }
1245 
1246 
1247 void matchRegions
1248 (
1249  const bool sloppyCellZones,
1250  const polyMesh& mesh,
1251 
1252  const label nCellRegions,
1253  const labelList& cellRegion,
1254 
1255  const word& defaultRegionName,
1256 
1257  labelList& regionToZone,
1258  wordList& regionNames,
1259  labelList& zoneToRegion
1260 )
1261 {
1262  const cellZoneMesh& cellZones = mesh.cellZones();
1263 
1264  regionToZone.setSize(nCellRegions, -1);
1265  regionNames.setSize(nCellRegions);
1266  zoneToRegion.setSize(cellZones.size(), -1);
1267 
1268  // Get current per cell zoneID
1269  labelList zoneID(mesh.nCells(), -1);
1270  labelList neiZoneID(mesh.nFaces()-mesh.nInternalFaces());
1271  getZoneID(mesh, cellZones, zoneID, neiZoneID);
1272 
1273  // Sizes per cellzone
1274  labelList zoneSizes(cellZones.size(), 0);
1275  {
1276  List<wordList> zoneNames(Pstream::nProcs());
1277  zoneNames[Pstream::myProcNo()] = cellZones.names();
1278  Pstream::gatherList(zoneNames);
1279  Pstream::scatterList(zoneNames);
1280 
1281  forAll(zoneNames, proci)
1282  {
1283  if (zoneNames[proci] != zoneNames[0])
1284  {
1286  << "cellZones not synchronised across processors." << endl
1287  << "Master has cellZones " << zoneNames[0] << endl
1288  << "Processor " << proci
1289  << " has cellZones " << zoneNames[proci]
1290  << exit(FatalError);
1291  }
1292  }
1293 
1294  forAll(cellZones, zoneI)
1295  {
1296  zoneSizes[zoneI] = returnReduce
1297  (
1298  cellZones[zoneI].size(),
1299  sumOp<label>()
1300  );
1301  }
1302  }
1303 
1304 
1305  if (sloppyCellZones)
1306  {
1307  Info<< "Trying to match regions to existing cell zones;"
1308  << " region can be subset of cell zone." << nl << endl;
1309 
1310  forAll(cellZones, zoneI)
1311  {
1312  label regioni = findCorrespondingRegion
1313  (
1314  zoneID,
1315  cellRegion,
1316  nCellRegions,
1317  zoneI,
1318  label(0.5*zoneSizes[zoneI]) // minimum overlap
1319  );
1320 
1321  if (regioni != -1)
1322  {
1323  Info<< "Sloppily matched region " << regioni
1324  //<< " size " << regionSizes[regioni]
1325  << " to zone " << zoneI << " size " << zoneSizes[zoneI]
1326  << endl;
1327  zoneToRegion[zoneI] = regioni;
1328  regionToZone[regioni] = zoneI;
1329  regionNames[regioni] = cellZones[zoneI].name();
1330  }
1331  }
1332  }
1333  else
1334  {
1335  Info<< "Trying to match regions to existing cell zones." << nl << endl;
1336 
1337  forAll(cellZones, zoneI)
1338  {
1339  label regioni = findCorrespondingRegion
1340  (
1341  zoneID,
1342  cellRegion,
1343  nCellRegions,
1344  zoneI,
1345  1 // minimum overlap
1346  );
1347 
1348  if (regioni != -1)
1349  {
1350  zoneToRegion[zoneI] = regioni;
1351  regionToZone[regioni] = zoneI;
1352  regionNames[regioni] = cellZones[zoneI].name();
1353  }
1354  }
1355  }
1356 
1357  label nUnmatchedRegions = 0;
1358 
1359  forAll(regionToZone, regioni)
1360  {
1361  if (regionToZone[regioni] == -1)
1362  {
1363  nUnmatchedRegions++;
1364  }
1365  }
1366 
1367  if (nUnmatchedRegions)
1368  {
1369  label nUnmatchedi = 1;
1370 
1371  // Allocate region names for unmatched regions.
1372  forAll(regionToZone, regioni)
1373  {
1374  if (regionToZone[regioni] == -1)
1375  {
1376  if
1377  (
1378  nUnmatchedRegions == 1
1379  && defaultRegionName != standardRegionName
1380  )
1381  {
1382  regionNames[regioni] = defaultRegionName;
1383  }
1384  else
1385  {
1386  regionNames[regioni] =
1387  defaultRegionName + Foam::name(nUnmatchedi++);
1388  }
1389  }
1390  }
1391  }
1392 }
1393 
1394 
1395 void writeCellToRegion(const fvMesh& mesh, const labelList& cellRegion)
1396 {
1397  // Write to manual decomposition option
1398  {
1399  labelIOList cellToRegion
1400  (
1401  IOobject
1402  (
1403  "cellToRegion",
1404  mesh.facesInstance(),
1405  mesh,
1408  false
1409  ),
1410  cellRegion
1411  );
1412  cellToRegion.write();
1413 
1414  Info<< "Writing region per cell file (for manual decomposition) to "
1415  << cellToRegion.localObjectPath() << nl << endl;
1416  }
1417  // Write for postprocessing
1418  {
1419  volScalarField cellToRegion
1420  (
1421  IOobject
1422  (
1423  "cellToRegion",
1424  mesh.time().timeName(),
1425  mesh,
1428  false
1429  ),
1430  mesh,
1432  zeroGradientFvPatchScalarField::typeName
1433  );
1434  forAll(cellRegion, celli)
1435  {
1436  cellToRegion[celli] = cellRegion[celli];
1437  }
1438  cellToRegion.write();
1439 
1440  Info<< "Writing region per cell as volScalarField to "
1441  << cellToRegion.localObjectPath() << nl << endl;
1442  }
1443 }
1444 
1445 
1446 
1447 
1448 int main(int argc, char *argv[])
1449 {
1451  (
1452  "splits mesh into multiple regions (detected by walking across faces)"
1453  );
1454  #include "addRegionOption.H"
1455  #include "addOverwriteOption.H"
1457  (
1458  "cellZones",
1459  "additionally split cellZones off into separate regions"
1460  );
1462  (
1463  "cellZonesOnly",
1464  "use cellZones only to split mesh into regions; do not use walking"
1465  );
1467  (
1468  "cellZonesFileOnly",
1469  "file",
1470  "like -cellZonesOnly, but use specified file"
1471  );
1473  (
1474  "blockedFaces",
1475  "faceSet",
1476  "specify additional region boundaries that walking does not cross"
1477  );
1479  (
1480  "makeCellZones",
1481  "place cells into cellZones instead of splitting mesh"
1482  );
1484  (
1485  "largestOnly",
1486  "only write largest region"
1487  );
1489  (
1490  "insidePoint",
1491  "point",
1492  "only write region containing point"
1493  );
1495  (
1496  "detectOnly",
1497  "do not write mesh"
1498  );
1500  (
1501  "sloppyCellZones",
1502  "try to match heuristically regions to existing cell zones"
1503  );
1505  (
1506  "useFaceZones",
1507  "use faceZones to patch inter-region faces instead of single patch"
1508  );
1510  (
1511  "prefixRegion",
1512  "prefix region name to all patches, not just coupling patches"
1513  );
1515  (
1516  "defaultRegionName",
1517  "name",
1518  "base name of the unspecified regions, defaults to \"region\""
1519  );
1521  (
1522  "noFields",
1523  "do not update fields"
1524  );
1525 
1526  #include "setRootCase.H"
1527  #include "createTime.H"
1528 
1530 
1531  #include "createNamedMesh.H"
1532 
1533  const word oldInstance = mesh.pointsInstance();
1534 
1535  word blockedFacesName;
1536  if (args.optionReadIfPresent("blockedFaces", blockedFacesName))
1537  {
1538  Info<< "Reading blocked internal faces from faceSet "
1539  << blockedFacesName << nl << endl;
1540  }
1541 
1542  const bool makeCellZones = args.optionFound("makeCellZones");
1543  const bool largestOnly = args.optionFound("largestOnly");
1544  const bool insidePoint = args.optionFound("insidePoint");
1545  const bool useCellZones = args.optionFound("cellZones");
1546  const bool useCellZonesOnly = args.optionFound("cellZonesOnly");
1547  const bool useCellZonesFile = args.optionFound("cellZonesFileOnly");
1548  const bool overwrite = args.optionFound("overwrite");
1549  const bool detectOnly = args.optionFound("detectOnly");
1550  const bool sloppyCellZones = args.optionFound("sloppyCellZones");
1551  const bool useFaceZones = args.optionFound("useFaceZones");
1552  const bool prefixRegion = args.optionFound("prefixRegion");
1553  const bool fields = !args.optionFound("noFields");
1554 
1555  if
1556  (
1557  (useCellZonesOnly || useCellZonesFile)
1558  && (useCellZones || blockedFacesName.size())
1559  )
1560  {
1562  << "You cannot specify both -cellZonesOnly or -cellZonesFileOnly"
1563  << " (which specify complete split)"
1564  << " in combination with -blockedFaces or -cellZones"
1565  << " (which imply a split based on topology)"
1566  << exit(FatalError);
1567  }
1568 
1569 
1570  if (useFaceZones)
1571  {
1572  Info<< "Using current faceZones to divide inter-region interfaces"
1573  << " into multiple patches."
1574  << nl << endl;
1575  }
1576  else
1577  {
1578  Info<< "Creating single patch per inter-region interface."
1579  << nl << endl;
1580  }
1581 
1582 
1583  if (insidePoint && largestOnly)
1584  {
1586  << "You cannot specify both -largestOnly"
1587  << " (keep region with most cells)"
1588  << " and -insidePoint (keep region containing point)"
1589  << exit(FatalError);
1590  }
1591 
1592  const word defaultRegionName
1593  (
1594  args.optionLookupOrDefault("defaultRegionName", standardRegionName)
1595  );
1596 
1597  const cellZoneMesh& cellZones = mesh.cellZones();
1598 
1599  // Existing zoneID
1600  labelList zoneID(mesh.nCells(), -1);
1601  // Neighbour zoneID.
1602  labelList neiZoneID(mesh.nFaces()-mesh.nInternalFaces());
1603  getZoneID(mesh, cellZones, zoneID, neiZoneID);
1604 
1605 
1606 
1607  // Determine per cell the region it belongs to
1608  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1609 
1610  // cellRegion is the labelList with the region per cell.
1611  labelList cellRegion;
1612  // Region per zone
1613  labelList regionToZone;
1614  // Name of region
1615  wordList regionNames;
1616  // Zone to region
1617  labelList zoneToRegion;
1618 
1619  label nCellRegions = 0;
1620  if (useCellZonesOnly)
1621  {
1622  Info<< "Using current cellZones to split mesh into regions."
1623  << " This requires all"
1624  << " cells to be in one and only one cellZone." << nl << endl;
1625 
1626  label unzonedCelli = findIndex(zoneID, -1);
1627  if (unzonedCelli != -1)
1628  {
1630  << "For the cellZonesOnly option all cells "
1631  << "have to be in a cellZone." << endl
1632  << "Cell " << unzonedCelli
1633  << " at" << mesh.cellCentres()[unzonedCelli]
1634  << " is not in a cellZone. There might be more unzoned cells."
1635  << exit(FatalError);
1636  }
1637  cellRegion = zoneID;
1638  nCellRegions = gMax(cellRegion)+1;
1639  regionToZone.setSize(nCellRegions);
1640  regionNames.setSize(nCellRegions);
1641  zoneToRegion.setSize(cellZones.size(), -1);
1642  for (label regioni = 0; regioni < nCellRegions; regioni++)
1643  {
1644  regionToZone[regioni] = regioni;
1645  zoneToRegion[regioni] = regioni;
1646  regionNames[regioni] = cellZones[regioni].name();
1647  }
1648  }
1649  else if (useCellZonesFile)
1650  {
1651  const word zoneFile = args.option("cellZonesFileOnly");
1652  Info<< "Reading split from cellZones file " << zoneFile << endl
1653  << "This requires all"
1654  << " cells to be in one and only one cellZone." << nl << endl;
1655 
1656  cellZoneMesh newCellZones
1657  (
1658  IOobject
1659  (
1660  zoneFile,
1661  mesh.facesInstance(),
1663  mesh,
1666  false
1667  ),
1668  mesh
1669  );
1670 
1671  labelList newZoneID(mesh.nCells(), -1);
1672  labelList newNeiZoneID(mesh.nFaces()-mesh.nInternalFaces());
1673  getZoneID(mesh, newCellZones, newZoneID, newNeiZoneID);
1674 
1675  label unzonedCelli = findIndex(newZoneID, -1);
1676  if (unzonedCelli != -1)
1677  {
1679  << "For the cellZonesFileOnly option all cells "
1680  << "have to be in a cellZone." << endl
1681  << "Cell " << unzonedCelli
1682  << " at" << mesh.cellCentres()[unzonedCelli]
1683  << " is not in a cellZone. There might be more unzoned cells."
1684  << exit(FatalError);
1685  }
1686  cellRegion = newZoneID;
1687  nCellRegions = gMax(cellRegion)+1;
1688  zoneToRegion.setSize(newCellZones.size(), -1);
1689  regionToZone.setSize(nCellRegions);
1690  regionNames.setSize(nCellRegions);
1691  for (label regioni = 0; regioni < nCellRegions; regioni++)
1692  {
1693  regionToZone[regioni] = regioni;
1694  zoneToRegion[regioni] = regioni;
1695  regionNames[regioni] = newCellZones[regioni].name();
1696  }
1697  }
1698  else
1699  {
1700  // Determine connected regions
1701  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
1702 
1703  // Mark additional faces that are blocked
1704  boolList blockedFace;
1705 
1706  // Read from faceSet
1707  if (blockedFacesName.size())
1708  {
1709  faceSet blockedFaceSet(mesh, blockedFacesName);
1710  Info<< "Read "
1711  << returnReduce(blockedFaceSet.size(), sumOp<label>())
1712  << " blocked faces from set " << blockedFacesName << nl << endl;
1713 
1714  blockedFace.setSize(mesh.nFaces(), false);
1715 
1716  forAllConstIter(faceSet, blockedFaceSet, iter)
1717  {
1718  blockedFace[iter.key()] = true;
1719  }
1720  }
1721 
1722  // Imply from differing cellZones
1723  if (useCellZones)
1724  {
1725  blockedFace.setSize(mesh.nFaces(), false);
1726 
1727  for (label facei = 0; facei < mesh.nInternalFaces(); facei++)
1728  {
1729  label own = mesh.faceOwner()[facei];
1730  label nei = mesh.faceNeighbour()[facei];
1731 
1732  if (zoneID[own] != zoneID[nei])
1733  {
1734  blockedFace[facei] = true;
1735  }
1736  }
1737 
1738  // Different cellZones on either side of processor patch.
1739  forAll(neiZoneID, i)
1740  {
1741  label facei = i+mesh.nInternalFaces();
1742 
1743  if (zoneID[mesh.faceOwner()[facei]] != neiZoneID[i])
1744  {
1745  blockedFace[facei] = true;
1746  }
1747  }
1748  }
1749 
1750  // Do a topological walk to determine regions
1751  regionSplit regions(mesh, blockedFace);
1752  nCellRegions = regions.nRegions();
1753  cellRegion.transfer(regions);
1754 
1755  // Make up region names. If possible match them to existing zones.
1756  matchRegions
1757  (
1758  sloppyCellZones,
1759  mesh,
1760  nCellRegions,
1761  cellRegion,
1762  defaultRegionName,
1763 
1764  regionToZone,
1765  regionNames,
1766  zoneToRegion
1767  );
1768 
1769  // Override any default region names if single region selected
1770  if (largestOnly || insidePoint)
1771  {
1772  forAll(regionToZone, regioni)
1773  {
1774  if (regionToZone[regioni] == -1)
1775  {
1776  if (overwrite)
1777  {
1778  regionNames[regioni] = polyMesh::defaultRegion;
1779  }
1780  else if (insidePoint)
1781  {
1782  regionNames[regioni] = "insidePoint";
1783  }
1784  else if (largestOnly)
1785  {
1786  regionNames[regioni] = "largestOnly";
1787  }
1788  }
1789  }
1790  }
1791  }
1792 
1793  Info<< endl << "Number of regions:" << nCellRegions << nl << endl;
1794 
1795 
1796  // Write decomposition to file
1797  writeCellToRegion(mesh, cellRegion);
1798 
1799 
1800 
1801  // Sizes per region
1802  // ~~~~~~~~~~~~~~~~
1803 
1804  labelList regionSizes(nCellRegions, 0);
1805 
1806  forAll(cellRegion, celli)
1807  {
1808  regionSizes[cellRegion[celli]]++;
1809  }
1810  forAll(regionSizes, regioni)
1811  {
1812  reduce(regionSizes[regioni], sumOp<label>());
1813  }
1814 
1815  Info<< "Region\tCells" << nl
1816  << "------\t-----" << endl;
1817 
1818  forAll(regionSizes, regioni)
1819  {
1820  Info<< regioni << '\t' << regionSizes[regioni] << nl;
1821  }
1822  Info<< endl;
1823 
1824 
1825 
1826  // Print region to zone
1827  Info<< "Region\tZone\tName" << nl
1828  << "------\t----\t----" << endl;
1829  forAll(regionToZone, regioni)
1830  {
1831  Info<< regioni << '\t' << regionToZone[regioni] << '\t'
1832  << regionNames[regioni] << nl;
1833  }
1834  Info<< endl;
1835 
1836 
1837 
1838  // Since we're going to mess with patches and zones make sure all
1839  // is synchronised
1840  mesh.boundaryMesh().checkParallelSync(true);
1841  mesh.faceZones().checkParallelSync(true);
1842 
1843 
1844  // Interfaces
1845  // ----------
1846  // per interface:
1847  // - the two regions on either side
1848  // - the name
1849  // - the (global) size
1850  edgeList interfaces;
1851  List<Pair<word>> interfaceNames;
1852  labelList interfaceSizes;
1853  // per face the interface
1854  labelList faceToInterface;
1855 
1856  getInterfaceSizes
1857  (
1858  mesh,
1859  useFaceZones,
1860  cellRegion,
1861  regionNames,
1862 
1863  interfaces,
1864  interfaceNames,
1865  interfaceSizes,
1866  faceToInterface
1867  );
1868 
1869  Info<< "Sizes of interfaces between regions:" << nl << nl
1870  << "Interface\tRegion\tRegion\tFaces" << nl
1871  << "---------\t------\t------\t-----" << endl;
1872 
1873  forAll(interfaces, interI)
1874  {
1875  const edge& e = interfaces[interI];
1876 
1877  Info<< interI
1878  << "\t\t" << e[0] << '\t' << e[1]
1879  << '\t' << interfaceSizes[interI] << nl;
1880  }
1881  Info<< endl;
1882 
1883 
1884  if (detectOnly)
1885  {
1886  return 0;
1887  }
1888 
1889 
1890  // Read objects in time directory
1891  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1892 
1894 
1895  if (fields) Info<< "Reading geometric fields" << nl << endl;
1896 
1897  #include "readVolFields.H"
1898  #include "readSurfaceFields.H"
1899  // #include "readPointFields.H"
1900 
1901  Info<< endl;
1902 
1903 
1904  // Remove any demand-driven fields ('S', 'V' etc)
1905  mesh.clearOut();
1906 
1907 
1908  if (nCellRegions == 1)
1909  {
1910  Info<< "Only one region. Doing nothing." << endl;
1911  }
1912  else if (makeCellZones)
1913  {
1914  Info<< "Putting cells into cellZones instead of splitting mesh."
1915  << endl;
1916 
1917  // Check if region overlaps with existing zone. If so keep.
1918 
1919  for (label regioni = 0; regioni < nCellRegions; regioni++)
1920  {
1921  label zoneI = regionToZone[regioni];
1922 
1923  if (zoneI != -1)
1924  {
1925  Info<< " Region " << regioni << " : corresponds to existing"
1926  << " cellZone "
1927  << zoneI << ' ' << cellZones[zoneI].name() << endl;
1928  }
1929  else
1930  {
1931  // Create new cellZone.
1932  labelList regionCells = findIndices(cellRegion, regioni);
1933 
1934  word zoneName = "region" + Foam::name(regioni);
1935 
1936  zoneI = cellZones.findZoneID(zoneName);
1937 
1938  if (zoneI == -1)
1939  {
1940  zoneI = cellZones.size();
1941  mesh.cellZones().setSize(zoneI+1);
1942  mesh.cellZones().set
1943  (
1944  zoneI,
1945  new cellZone
1946  (
1947  zoneName, // name
1948  regionCells, // addressing
1949  zoneI, // index
1950  cellZones // cellZoneMesh
1951  )
1952  );
1953  }
1954  else
1955  {
1956  mesh.cellZones()[zoneI].clearAddressing();
1957  mesh.cellZones()[zoneI] = regionCells;
1958  }
1959  Info<< " Region " << regioni << " : created new cellZone "
1960  << zoneI << ' ' << cellZones[zoneI].name() << endl;
1961  }
1962  }
1964 
1965  if (!overwrite)
1966  {
1967  runTime++;
1968  mesh.setInstance(runTime.timeName());
1969  }
1970  else
1971  {
1972  mesh.setInstance(oldInstance);
1973  }
1974 
1975  Info<< "Writing cellZones as new mesh to time " << runTime.timeName()
1976  << nl << endl;
1977 
1978  mesh.write();
1979 
1980 
1981  // Write cellSets for convenience
1982  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1983 
1984  Info<< "Writing cellSets corresponding to cellZones." << nl << endl;
1985 
1986  forAll(cellZones, zoneI)
1987  {
1988  const cellZone& cz = cellZones[zoneI];
1989 
1990  cellSet(mesh, cz.name(), cz).write();
1991  }
1992  }
1993  else
1994  {
1995  // Add patches for interfaces
1996  // ~~~~~~~~~~~~~~~~~~~~~~~~~~
1997 
1998  // Add all possible patches. Empty ones get filtered later on.
1999  Info<< nl << "Adding patches" << nl << endl;
2000 
2001  labelList interfacePatches
2002  (
2003  addRegionPatches
2004  (
2005  mesh,
2006  regionNames,
2007  interfaces,
2008  interfaceNames
2009  )
2010  );
2011 
2012 
2013  if (!overwrite)
2014  {
2015  runTime++;
2016  }
2017 
2018 
2019  // Create regions
2020  // ~~~~~~~~~~~~~~
2021 
2022  if (insidePoint)
2023  {
2024  const point insidePoint = args.optionRead<point>("insidePoint");
2025 
2026  label regioni = -1;
2027 
2028  (void)mesh.tetBasePtIs();
2029 
2030  label celli = mesh.findCell(insidePoint);
2031 
2032  Info<< nl << "Found point " << insidePoint << " in cell " << celli
2033  << endl;
2034 
2035  if (celli != -1)
2036  {
2037  regioni = cellRegion[celli];
2038  }
2039 
2040  reduce(regioni, maxOp<label>());
2041 
2042  Info<< nl
2043  << "Subsetting region " << regioni
2044  << " containing point " << insidePoint << endl;
2045 
2046  if (regioni == -1)
2047  {
2049  << "Point " << insidePoint
2050  << " is not inside the mesh." << nl
2051  << "Bounding box of the mesh:" << mesh.bounds()
2052  << exit(FatalError);
2053  }
2054 
2055  createAndWriteRegion
2056  (
2057  mesh,
2058  cellRegion,
2059  regionNames,
2060  prefixRegion,
2061  faceToInterface,
2062  interfacePatches,
2063  regioni,
2064  (overwrite ? oldInstance : runTime.timeName())
2065  );
2066  }
2067  else if (largestOnly)
2068  {
2069  label regioni = findMax(regionSizes);
2070 
2071  Info<< nl
2072  << "Subsetting region " << regioni
2073  << " of size " << regionSizes[regioni]
2074  << " as named region " << regionNames[regioni] << endl;
2075 
2076  createAndWriteRegion
2077  (
2078  mesh,
2079  cellRegion,
2080  regionNames,
2081  prefixRegion,
2082  faceToInterface,
2083  interfacePatches,
2084  regioni,
2085  (overwrite ? oldInstance : runTime.timeName())
2086  );
2087  }
2088  else
2089  {
2090  // Split all
2091  for (label regioni = 0; regioni < nCellRegions; regioni++)
2092  {
2093  Info<< nl
2094  << "Region " << regioni << nl
2095  << "-------- " << endl;
2096 
2097  createAndWriteRegion
2098  (
2099  mesh,
2100  cellRegion,
2101  regionNames,
2102  prefixRegion,
2103  faceToInterface,
2104  interfacePatches,
2105  regioni,
2106  (overwrite ? oldInstance : runTime.timeName())
2107  );
2108  }
2109  }
2110  }
2111 
2112  Info<< "End\n" << endl;
2113 
2114  return 0;
2115 }
2116 
2117 
2118 // ************************************************************************* //
const polyBoundaryMesh & boundaryMesh() const
Return boundary mesh.
Definition: polyMesh.H:434
bool checkParallelSync(const bool report=false) const
Check whether all procs have all zones and in same order. Return.
Definition: ZoneMesh.C:428
This class separates the mesh into distinct unconnected regions, each of which is then given a label ...
Definition: regionSplit.H:119
void clearOut()
Clear all geometry and addressing.
Definition: fvMesh.C:240
static void scatterList(const List< commsStruct > &comms, List< T > &Values, const int tag, const label comm)
Scatter data. Reverse of gatherList.
static void reorderPatches(fvMesh &, const labelList &oldToNew, const label nPatches, const bool validBoundary)
Reorder and remove trailing patches. If validBoundary call is parallel.
Definition: fvMeshTools.C:140
static void listCombineScatter(const List< commsStruct > &comms, List< T > &Value, const int tag, const label comm)
Scatter data. Reverse of combineGather.
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
void clearAddressing()
Clear addressing.
Definition: ZoneMesh.C:387
virtual Ostream & write(const char)=0
Write character.
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
const word & name() const
Return name.
static int masterNo()
Process index of the master.
Definition: UPstream.H:417
const word & name() const
Return name.
Definition: IOobject.H:303
A list of face labels.
Definition: faceSet.H:48
bool set(const label) const
Is element set.
Definition: PtrListI.H:65
List of IOobjects with searching and retrieving facilities.
Definition: IOobjectList.H:50
const faceZoneMesh & faceZones() const
Return face zone mesh.
Definition: polyMesh.H:476
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
const fileName & facesInstance() const
Return the current instance directory for faces.
Definition: polyMesh.C:808
static iteratorEnd end()
iteratorEnd set to beyond the end of any HashTable
Definition: HashTable.H:112
void off()
Switch the function objects off.
error FatalError
A list of keyword definitions, which are a keyword followed by any number of values (e...
Definition: dictionary.H:158
dimensioned< Type > max(const dimensioned< Type > &, const dimensioned< Type > &)
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:319
label nInternalFaces() const
virtual const labelList & faceNeighbour() const
Return face neighbour.
Definition: polyMesh.C:1175
const labelIOList & tetBasePtIs() const
Return the tetBasePtIs.
Definition: polyMesh.C:848
static int firstSlave()
Process index of first slave.
Definition: UPstream.H:446
PtrList< labelIOList > & faceProcAddressing
label nFaces() const
Foam::word regionName
static word meshSubDir
Return the mesh sub-directory name (usually "polyMesh")
Definition: polyMesh.H:312
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
static int myProcNo(const label communicator=0)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:429
Given list of cells to remove insert all the topology changes.
Definition: removeCells.H:59
label nCells() const
engineTime & runTime
static word defaultRegion
Return the default region name.
Definition: polyMesh.H:309
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
bool optionFound(const word &opt) const
Return true if the named option is found.
Definition: argListI.H:114
static bool master(const label communicator=0)
Am I the master process.
Definition: UPstream.H:423
labelList identity(const label len)
Create identity map (map[i] == i) of given length.
Definition: ListOps.C:104
Field reading functions for post-processing utilities.
static word timeName(const scalar, const int precision=precision_)
Return time name of given scalar time.
Definition: Time.C:622
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:239
labelList findIndices(const ListType &, typename ListType::const_reference, const label start=0)
Find all occurrences of given element. Linear search.
virtual bool write(const bool write=true) const
Write mesh using IO settings from time.
Definition: fvMesh.C:1035
bool insert(const Key &key)
Insert a new entry.
Definition: HashSet.H:111
static void listCombineGather(const List< commsStruct > &comms, List< T > &Value, const CombineOp &cop, const int tag, const label comm)
bool optionReadIfPresent(const word &opt, T &) const
Read a value from the named option if present.
Definition: argListI.H:204
label size() const
Return number of elements in table.
Definition: HashTableI.H:65
bool add(entry *, bool mergeEntry=false)
Add a new entry.
Definition: dictionary.C:1056
multivariateSurfaceInterpolationScheme< scalar >::fieldTable fields
Definition: createFields.H:103
IOdictionary is derived from dictionary and IOobject to give the dictionary automatic IO functionalit...
Definition: IOdictionary.H:52
Input inter-processor communications stream.
Definition: IPstream.H:50
bool insert(const edge &, const T &newElmt)
Insert a new hashedEntry.
Definition: HashTableI.H:80
iterator find(const edge &)
Find and return an iterator set at the hashedEntry.
Definition: HashTable.C:142
T optionLookupOrDefault(const word &opt, const T &deflt) const
Read a value from the named option if present.
Definition: argListI.H:243
const fileName & pointsInstance() const
Return the current instance directory for points.
Definition: polyMesh.C:802
dynamicFvMesh & mesh
T optionRead(const word &opt) const
Read a value from the named option.
Definition: argListI.H:193
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:58
bool found(const Key &) const
Return true if hashedEntry is found in table.
Definition: HashTable.C:113
A class for handling words, derived from string.
Definition: word.H:59
const cellZoneMesh & cellZones() const
Return cell zone mesh.
Definition: polyMesh.H:482
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:33
Determines a mapping between patch face centres and mesh cell or face centres and processors they&#39;re ...
This boundary condition is not designed to be evaluated; it is assumed that the value is assigned via...
static void addOption(const word &opt, const string &param="", const string &usage="")
Add to an option to validOptions with usage information.
Definition: argList.C:128
virtual const labelList & faceOwner() const
Return face owner.
Definition: polyMesh.C:1169
void write(std::ostream &os, const bool binary, List< floatScalar > &fField)
Write floats ascii or binary.
List< label > labelList
A List of labels.
Definition: labelList.H:56
static const zero Zero
Definition: zero.H:97
const word & name() const
Return name.
Definition: zone.H:147
An STL-conforming hash table.
Definition: HashTable.H:61
const vectorField & cellCentres() const
forAllConstIter(PtrDictionary< phaseModel >, mixture.phases(), phase)
Definition: pEqn.H:29
void setSize(const label)
Reset size of PtrList. If extending the PtrList, new entries are.
Definition: PtrList.C:131
const word & system() const
Return system name.
Definition: TimePaths.H:114
static void scatter(const List< commsStruct > &comms, T &Value, const int tag, const label comm)
Scatter data. Distribute without modification. Reverse of gather.
Foam::polyBoundaryMesh.
label findMax(const ListType &, const label start=0)
Find index of max element (and larger than given element).
bool checkParallelSync(const bool report=false) const
Check whether all procs have all patches and in same order. Return.
static const char nl
Definition: Ostream.H:260
Map from edge (expressed as its endpoints) to value.
Definition: EdgeMap.H:47
void updateMesh()
Correct polyBoundaryMesh after topology update.
Type gMax(const FieldField< Field, Type > &f)
objects
Output inter-processor communications stream.
Definition: OPstream.H:50
label findIndex(const ListType &, typename ListType::const_reference, const label start=0)
Find first occurrence of given element and return index,.
dimensioned< Type > min(const dimensioned< Type > &, const dimensioned< Type > &)
A subset of mesh cells.
Definition: cellZone.H:61
void reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
word name(const complex &)
Return a string representation of a complex.
Definition: complex.C:47
void setInstance(const fileName &)
Set the instance for mesh files.
Definition: polyMeshIO.C:32
label whichZone(const label objectIndex) const
Given a global object index, return the zone it is in.
Definition: ZoneMesh.C:221
label size() const
Return the number of elements in the UPtrList.
Definition: UPtrListI.H:29
void setSize(const label)
Reset size of List.
Definition: List.C:281
const boundBox & bounds() const
Return mesh bounding box.
Definition: polyMesh.H:440
writeOption writeOpt() const
Definition: IOobject.H:363
static bool & parRun()
Is this a parallel run?
Definition: UPstream.H:399
static label nProcs(const label communicator=0)
Number of processes in parallel run.
Definition: UPstream.H:411
label patchi
const dimensionSet dimless(0, 0, 0, 0, 0, 0, 0)
Definition: dimensionSets.H:47
const functionObjectList & functionObjects() const
Return the list of function objects.
Definition: Time.H:409
#define WarningInFunction
Report a warning using Foam::Warning.
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
A collection of cell labels.
Definition: cellSet.H:48
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:78
Direct mesh changes based on v1.3 polyTopoChange syntax.
label findCell(const point &p, const cellDecomposition=CELL_TETS) const
Find cell enclosing this location and return index.
Definition: polyMesh.C:1606
wordList names() const
Return a list of zone names.
Definition: ZoneMesh.C:256
messageStream Info
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
An auto-pointer similar to the STL auto_ptr but with automatic casting to a reference to the type and...
Definition: PtrList.H:52
const doubleScalar e
Elementary charge.
Definition: doubleScalar.H:105
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:74
static void addBoolOption(const word &opt, const string &usage="")
Add to a bool option to validOptions with usage information.
Definition: argList.C:118
static void swapBoundaryFaceList(const polyMesh &mesh, UList< T > &l)
Swap coupled boundary face values.
Definition: syncTools.H:430
static void addNote(const string &)
Add extra notes for the usage information.
Definition: argList.C:159
A class for managing temporary objects.
Definition: PtrList.H:53
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:66
Foam::argList args(argc, argv)
IOobject defines the attributes of an object for which implicit objectRegistry management is supporte...
Definition: IOobject.H:92
static void gatherList(const List< commsStruct > &comms, List< T > &Values, const int tag, const label comm)
Gather data but keep individual values separate.
static tmp< GeometricField< Type, fvPatchField, volMesh > > interpolate(const GeometricField< Type, fvPatchField, volMesh > &, const fvMesh &sMesh, const labelList &patchMap, const labelList &cellMap, const labelList &faceMap)
Map volume field.
void transfer(List< T > &)
Transfer the contents of the argument List into this list.
Definition: List.C:342
const string & option(const word &opt) const
Return the argument string associated with the named option.
Definition: argListI.H:108
Namespace for OpenFOAM.
static int lastSlave(const label communicator=0)
Process index of last slave.
Definition: UPstream.H:452