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