1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
7 -------------------------------------------------------------------------------
9 This file is part of OpenFOAM.
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.
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
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/>.
28 Splits mesh into multiple regions.
30 Each region is defined as a domain whose cells can all be reached by
31 cell-face-cell walking without crossing
33 - additional faces from faceset (-blockedFaces faceSet).
34 - any face inbetween differing cellZones (-cellZones)
37 - volScalarField with regions as different scalars (-detectOnly)
39 - mesh with multiple regions and directMapped patches. These patches
40 either cover the whole interface between two region (default) or
41 only part according to faceZones (-useFaceZones)
43 - mesh with cells put into cellZones (-makeCellZones)
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.
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.
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.
59 - Should work in parallel.
60 cellZones can differ on either side of processor boundaries in which case
61 the faces get moved from processor patch to directMapped patch. Not
64 - If a cell zone gets split into more than one region it can detect
65 the largest matching region (-sloppyCellZones). This will accept any
66 region that covers more than 50% of the zone. It has to be a subset
67 so cannot have any cells in any other zone.
69 - writes maps like decomposePar back to original mesh:
70 - pointRegionAddressing : for every point in this region the point in
72 - cellRegionAddressing : ,, cell ,, cell ,,
73 - faceRegionAddressing : ,, face ,, face in
74 the original mesh + 'turning index'. For a face in the same orientation
75 this is the original facelabel+1, for a turned face this is -facelabel-1
76 - boundaryRegionAddressing : for every patch in this region the
77 patch in the original mesh (or -1 if added patch)
78 \*---------------------------------------------------------------------------*/
80 #include "SortableList.H"
82 #include "regionSplit.H"
83 #include "fvMeshSubset.H"
84 #include "IOobjectList.H"
85 #include "volFields.H"
88 #include "polyTopoChange.H"
89 #include "removeCells.H"
91 #include "syncTools.H"
92 #include "ReadFields.H"
93 #include "directMappedWallPolyPatch.H"
94 #include "zeroGradientFvPatchFields.H"
98 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
100 template<class GeoField>
101 void addPatchFields(fvMesh& mesh, const word& patchFieldType)
103 HashTable<const GeoField*> flds
105 mesh.objectRegistry::lookupClass<GeoField>()
108 forAllConstIter(typename HashTable<const GeoField*>, flds, iter)
110 const GeoField& fld = *iter();
112 typename GeoField::GeometricBoundaryField& bfld =
113 const_cast<typename GeoField::GeometricBoundaryField&>
118 label sz = bfld.size();
123 GeoField::PatchFieldType::New
127 fld.dimensionedInternalField()
134 // Remove last patch field
135 template<class GeoField>
136 void trimPatchFields(fvMesh& mesh, const label nPatches)
138 HashTable<const GeoField*> flds
140 mesh.objectRegistry::lookupClass<GeoField>()
143 forAllConstIter(typename HashTable<const GeoField*>, flds, iter)
145 const GeoField& fld = *iter();
147 const_cast<typename GeoField::GeometricBoundaryField&>
155 // Reorder patch field
156 template<class GeoField>
157 void reorderPatchFields(fvMesh& mesh, const labelList& oldToNew)
159 HashTable<const GeoField*> flds
161 mesh.objectRegistry::lookupClass<GeoField>()
164 forAllConstIter(typename HashTable<const GeoField*>, flds, iter)
166 const GeoField& fld = *iter();
168 typename GeoField::GeometricBoundaryField& bfld =
169 const_cast<typename GeoField::GeometricBoundaryField&>
174 bfld.reorder(oldToNew);
179 // Adds patch if not yet there. Returns patchID.
180 label addPatch(fvMesh& mesh, const polyPatch& patch)
182 polyBoundaryMesh& polyPatches =
183 const_cast<polyBoundaryMesh&>(mesh.boundaryMesh());
185 label patchI = polyPatches.findPatchID(patch.name());
188 if (polyPatches[patchI].type() == patch.type())
195 FatalErrorIn("addPatch(fvMesh&, const polyPatch*)")
196 << "Already have patch " << patch.name()
197 << " but of type " << patch.type()
203 label insertPatchI = polyPatches.size();
204 label startFaceI = mesh.nFaces();
206 forAll(polyPatches, patchI)
208 const polyPatch& pp = polyPatches[patchI];
210 if (isA<processorPolyPatch>(pp))
212 insertPatchI = patchI;
213 startFaceI = pp.start();
219 // Below is all quite a hack. Feel free to change once there is a better
220 // mechanism to insert and reorder patches.
222 // Clear local fields and e.g. polyMesh parallelInfo.
225 label sz = polyPatches.size();
227 fvBoundaryMesh& fvPatches = const_cast<fvBoundaryMesh&>(mesh.boundary());
229 // Add polyPatch at the end
230 polyPatches.setSize(sz+1);
237 insertPatchI, //index
242 fvPatches.setSize(sz+1);
248 polyPatches[sz], // point to newly added polyPatch
253 addPatchFields<volScalarField>
256 calculatedFvPatchField<scalar>::typeName
258 addPatchFields<volVectorField>
261 calculatedFvPatchField<vector>::typeName
263 addPatchFields<volSphericalTensorField>
266 calculatedFvPatchField<sphericalTensor>::typeName
268 addPatchFields<volSymmTensorField>
271 calculatedFvPatchField<symmTensor>::typeName
273 addPatchFields<volTensorField>
276 calculatedFvPatchField<tensor>::typeName
281 addPatchFields<surfaceScalarField>
284 calculatedFvPatchField<scalar>::typeName
286 addPatchFields<surfaceVectorField>
289 calculatedFvPatchField<vector>::typeName
291 addPatchFields<surfaceSphericalTensorField>
294 calculatedFvPatchField<sphericalTensor>::typeName
296 addPatchFields<surfaceSymmTensorField>
299 calculatedFvPatchField<symmTensor>::typeName
301 addPatchFields<surfaceTensorField>
304 calculatedFvPatchField<tensor>::typeName
307 // Create reordering list
308 // patches before insert position stay as is
309 labelList oldToNew(sz+1);
310 for (label i = 0; i < insertPatchI; i++)
314 // patches after insert position move one up
315 for (label i = insertPatchI; i < sz; i++)
319 // appended patch gets moved to insert position
320 oldToNew[sz] = insertPatchI;
322 // Shuffle into place
323 polyPatches.reorder(oldToNew);
324 fvPatches.reorder(oldToNew);
326 reorderPatchFields<volScalarField>(mesh, oldToNew);
327 reorderPatchFields<volVectorField>(mesh, oldToNew);
328 reorderPatchFields<volSphericalTensorField>(mesh, oldToNew);
329 reorderPatchFields<volSymmTensorField>(mesh, oldToNew);
330 reorderPatchFields<volTensorField>(mesh, oldToNew);
331 reorderPatchFields<surfaceScalarField>(mesh, oldToNew);
332 reorderPatchFields<surfaceVectorField>(mesh, oldToNew);
333 reorderPatchFields<surfaceSphericalTensorField>(mesh, oldToNew);
334 reorderPatchFields<surfaceSymmTensorField>(mesh, oldToNew);
335 reorderPatchFields<surfaceTensorField>(mesh, oldToNew);
341 // Reorder and delete patches.
345 const labelList& oldToNew,
346 const label nNewPatches
349 polyBoundaryMesh& polyPatches =
350 const_cast<polyBoundaryMesh&>(mesh.boundaryMesh());
351 fvBoundaryMesh& fvPatches = const_cast<fvBoundaryMesh&>(mesh.boundary());
353 // Shuffle into place
354 polyPatches.reorder(oldToNew);
355 fvPatches.reorder(oldToNew);
357 reorderPatchFields<volScalarField>(mesh, oldToNew);
358 reorderPatchFields<volVectorField>(mesh, oldToNew);
359 reorderPatchFields<volSphericalTensorField>(mesh, oldToNew);
360 reorderPatchFields<volSymmTensorField>(mesh, oldToNew);
361 reorderPatchFields<volTensorField>(mesh, oldToNew);
362 reorderPatchFields<surfaceScalarField>(mesh, oldToNew);
363 reorderPatchFields<surfaceVectorField>(mesh, oldToNew);
364 reorderPatchFields<surfaceSphericalTensorField>(mesh, oldToNew);
365 reorderPatchFields<surfaceSymmTensorField>(mesh, oldToNew);
366 reorderPatchFields<surfaceTensorField>(mesh, oldToNew);
369 polyPatches.setSize(nNewPatches);
370 fvPatches.setSize(nNewPatches);
371 trimPatchFields<volScalarField>(mesh, nNewPatches);
372 trimPatchFields<volVectorField>(mesh, nNewPatches);
373 trimPatchFields<volSphericalTensorField>(mesh, nNewPatches);
374 trimPatchFields<volSymmTensorField>(mesh, nNewPatches);
375 trimPatchFields<volTensorField>(mesh, nNewPatches);
376 trimPatchFields<surfaceScalarField>(mesh, nNewPatches);
377 trimPatchFields<surfaceVectorField>(mesh, nNewPatches);
378 trimPatchFields<surfaceSphericalTensorField>(mesh, nNewPatches);
379 trimPatchFields<surfaceSymmTensorField>(mesh, nNewPatches);
380 trimPatchFields<surfaceTensorField>(mesh, nNewPatches);
384 template<class GeoField>
388 const fvMesh& subMesh,
389 const labelList& cellMap,
390 const labelList& faceMap,
391 const labelHashSet& addedPatches
394 const labelList patchMap(identity(mesh.boundaryMesh().size()));
396 HashTable<const GeoField*> fields
398 mesh.objectRegistry::lookupClass<GeoField>()
400 forAllConstIter(typename HashTable<const GeoField*>, fields, iter)
402 const GeoField& fld = *iter();
404 Info<< "Mapping field " << fld.name() << endl;
406 tmp<GeoField> tSubFld
408 fvMeshSubset::interpolate
418 // Hack: set value to 0 for introduced patches (since don't
420 forAll(tSubFld().boundaryField(), patchI)
422 if (addedPatches.found(patchI))
424 tSubFld().boundaryField()[patchI] ==
425 pTraits<typename GeoField::value_type>::zero;
430 GeoField* subFld = tSubFld.ptr();
431 subFld->rename(fld.name());
432 subFld->writeOpt() = IOobject::AUTO_WRITE;
438 template<class GeoField>
439 void subsetSurfaceFields
442 const fvMesh& subMesh,
443 const labelList& faceMap,
444 const labelHashSet& addedPatches
447 const labelList patchMap(identity(mesh.boundaryMesh().size()));
449 HashTable<const GeoField*> fields
451 mesh.objectRegistry::lookupClass<GeoField>()
453 forAllConstIter(typename HashTable<const GeoField*>, fields, iter)
455 const GeoField& fld = *iter();
457 Info<< "Mapping field " << fld.name() << endl;
459 tmp<GeoField> tSubFld
461 fvMeshSubset::interpolate
470 // Hack: set value to 0 for introduced patches (since don't
472 forAll(tSubFld().boundaryField(), patchI)
474 if (addedPatches.found(patchI))
476 tSubFld().boundaryField()[patchI] ==
477 pTraits<typename GeoField::value_type>::zero;
482 GeoField* subFld = tSubFld.ptr();
483 subFld->rename(fld.name());
484 subFld->writeOpt() = IOobject::AUTO_WRITE;
489 // Select all cells not in the region
490 labelList getNonRegionCells(const labelList& cellRegion, const label regionI)
492 DynamicList<label> nonRegionCells(cellRegion.size());
493 forAll(cellRegion, cellI)
495 if (cellRegion[cellI] != regionI)
497 nonRegionCells.append(cellI);
500 return nonRegionCells.shrink();
506 const polyMesh& mesh,
508 const label ownRegion,
509 const label neiRegion,
510 EdgeMap<Map<label> >& regionsToSize
515 min(ownRegion, neiRegion),
516 max(ownRegion, neiRegion)
519 EdgeMap<Map<label> >::iterator iter = regionsToSize.find
524 if (iter != regionsToSize.end())
526 // Check if zone present
527 Map<label>::iterator zoneFnd = iter().find(zoneID);
528 if (zoneFnd != iter().end())
530 // Found zone. Increment count.
535 // New or no zone. Insert with count 1.
536 iter().insert(zoneID, 1);
541 // Create new interface of size 1.
542 Map<label> zoneToSize;
543 zoneToSize.insert(zoneID, 1);
544 regionsToSize.insert(interface, zoneToSize);
549 // Get region-region interface name and sizes.
550 // Returns interfaces as straight list for looping in identical order.
551 void getInterfaceSizes
553 const polyMesh& mesh,
554 const bool useFaceZones,
555 const labelList& cellRegion,
556 const wordList& regionNames,
558 edgeList& interfaces,
559 List<Pair<word> >& interfaceNames,
560 labelList& interfaceSizes,
561 labelList& faceToInterface
564 // From region-region to faceZone (or -1) to number of faces.
566 EdgeMap<Map<label> > regionsToSize;
572 forAll(mesh.faceNeighbour(), faceI)
574 label ownRegion = cellRegion[mesh.faceOwner()[faceI]];
575 label neiRegion = cellRegion[mesh.faceNeighbour()[faceI]];
577 if (ownRegion != neiRegion)
582 (useFaceZones ? mesh.faceZones().whichZone(faceI) : -1),
593 // Neighbour cellRegion.
594 labelList coupledRegion(mesh.nFaces()-mesh.nInternalFaces());
596 forAll(coupledRegion, i)
598 label cellI = mesh.faceOwner()[i+mesh.nInternalFaces()];
599 coupledRegion[i] = cellRegion[cellI];
601 syncTools::swapBoundaryFaceList(mesh, coupledRegion);
603 forAll(coupledRegion, i)
605 label faceI = i+mesh.nInternalFaces();
606 label ownRegion = cellRegion[mesh.faceOwner()[faceI]];
607 label neiRegion = coupledRegion[i];
609 if (ownRegion != neiRegion)
614 (useFaceZones ? mesh.faceZones().whichZone(faceI) : -1),
623 if (Pstream::parRun())
625 if (Pstream::master())
627 // Receive and add to my sizes
630 int slave=Pstream::firstSlave();
631 slave<=Pstream::lastSlave();
635 IPstream fromSlave(Pstream::blocking, slave);
637 EdgeMap<Map<label> > slaveSizes(fromSlave);
639 forAllConstIter(EdgeMap<Map<label> >, slaveSizes, slaveIter)
641 EdgeMap<Map<label> >::iterator masterIter =
642 regionsToSize.find(slaveIter.key());
644 if (masterIter != regionsToSize.end())
647 const Map<label>& slaveInfo = slaveIter();
648 Map<label>& masterInfo = masterIter();
650 forAllConstIter(Map<label>, slaveInfo, iter)
652 label zoneID = iter.key();
653 label slaveSize = iter();
655 Map<label>::iterator zoneFnd = masterInfo.find
659 if (zoneFnd != masterInfo.end())
661 zoneFnd() += slaveSize;
665 masterInfo.insert(zoneID, slaveSize);
671 regionsToSize.insert(slaveIter.key(), slaveIter());
680 OPstream toMaster(Pstream::blocking, Pstream::masterNo());
681 toMaster << regionsToSize;
688 Pstream::scatter(regionsToSize);
692 // Now we have the global sizes of all inter-regions.
693 // Invert this on master and distribute.
694 label nInterfaces = 0;
695 forAllConstIter(EdgeMap<Map<label> >, regionsToSize, iter)
697 const Map<label>& info = iter();
698 nInterfaces += info.size();
701 interfaces.setSize(nInterfaces);
702 interfaceNames.setSize(nInterfaces);
703 interfaceSizes.setSize(nInterfaces);
704 EdgeMap<Map<label> > regionsToInterface(nInterfaces);
707 forAllConstIter(EdgeMap<Map<label> >, regionsToSize, iter)
709 const edge& e = iter.key();
710 const word& name0 = regionNames[e[0]];
711 const word& name1 = regionNames[e[1]];
713 const Map<label>& info = iter();
714 forAllConstIter(Map<label>, info, infoIter)
716 interfaces[nInterfaces] = iter.key();
717 label zoneID = infoIter.key();
720 interfaceNames[nInterfaces] = Pair<word>
722 name0 + "_to_" + name1,
723 name1 + "_to_" + name0
728 const word& zoneName = mesh.faceZones()[zoneID].name();
729 interfaceNames[nInterfaces] = Pair<word>
731 zoneName + "_" + name0 + "_to_" + name1,
732 zoneName + "_" + name1 + "_to_" + name0
735 interfaceSizes[nInterfaces] = infoIter();
737 Map<label> zoneAndInterface;
738 zoneAndInterface.insert(zoneID, nInterfaces);
739 regionsToInterface.insert(e, zoneAndInterface);
746 // Now all processor have consistent interface information
748 Pstream::scatter(interfaces);
749 Pstream::scatter(interfaceNames);
750 Pstream::scatter(interfaceSizes);
751 Pstream::scatter(regionsToInterface);
753 // Mark all inter-region faces.
754 faceToInterface.setSize(mesh.nFaces(), -1);
756 forAll(mesh.faceNeighbour(), faceI)
758 label ownRegion = cellRegion[mesh.faceOwner()[faceI]];
759 label neiRegion = cellRegion[mesh.faceNeighbour()[faceI]];
761 if (ownRegion != neiRegion)
766 zoneID = mesh.faceZones().whichZone(faceI);
771 min(ownRegion, neiRegion),
772 max(ownRegion, neiRegion)
775 faceToInterface[faceI] = regionsToInterface[interface][zoneID];
778 forAll(coupledRegion, i)
780 label faceI = i+mesh.nInternalFaces();
781 label ownRegion = cellRegion[mesh.faceOwner()[faceI]];
782 label neiRegion = coupledRegion[i];
784 if (ownRegion != neiRegion)
789 zoneID = mesh.faceZones().whichZone(faceI);
794 min(ownRegion, neiRegion),
795 max(ownRegion, neiRegion)
798 faceToInterface[faceI] = regionsToInterface[interface][zoneID];
804 // Create mesh for region.
805 autoPtr<mapPolyMesh> createRegionMesh
809 const labelList& cellRegion,
811 const word& regionName,
813 const labelList& interfacePatches,
814 const labelList& faceToInterface,
816 autoPtr<fvMesh>& newMesh
819 // Create dummy system/fv*
824 mesh.time().system(),
832 Info<< "Testing:" << io.objectPath() << endl;
835 // if (!exists(io.objectPath()))
837 Info<< "Writing dummy " << regionName/io.name() << endl;
838 dictionary dummyDict;
840 dummyDict.add("divSchemes", divDict);
842 dummyDict.add("gradSchemes", gradDict);
844 dummyDict.add("laplacianSchemes", laplDict);
846 IOdictionary(io, dummyDict).regIOobject::write();
853 mesh.time().system(),
862 //if (!exists(io.objectPath()))
864 Info<< "Writing dummy " << regionName/io.name() << endl;
865 dictionary dummyDict;
866 IOdictionary(io, dummyDict).regIOobject::write();
871 // Neighbour cellRegion.
872 labelList coupledRegion(mesh.nFaces()-mesh.nInternalFaces());
874 forAll(coupledRegion, i)
876 label cellI = mesh.faceOwner()[i+mesh.nInternalFaces()];
877 coupledRegion[i] = cellRegion[cellI];
879 syncTools::swapBoundaryFaceList(mesh, coupledRegion);
882 // Topology change container. Start off from existing mesh.
883 polyTopoChange meshMod(mesh);
885 // Cell remover engine
886 removeCells cellRemover(mesh);
888 // Select all but region cells
889 labelList cellsToRemove(getNonRegionCells(cellRegion, regionI));
891 // Find out which faces will get exposed. Note that this
892 // gets faces in mesh face order. So both regions will get same
893 // face in same order (important!)
894 labelList exposedFaces = cellRemover.getExposedFaces(cellsToRemove);
896 labelList exposedPatchIDs(exposedFaces.size());
897 forAll(exposedFaces, i)
899 label faceI = exposedFaces[i];
900 label interfaceI = faceToInterface[faceI];
902 label ownRegion = cellRegion[mesh.faceOwner()[faceI]];
903 label neiRegion = -1;
905 if (mesh.isInternalFace(faceI))
907 neiRegion = cellRegion[mesh.faceNeighbour()[faceI]];
911 neiRegion = coupledRegion[faceI-mesh.nInternalFaces()];
915 // Check which side is being kept - determines which of the two
916 // patches will be used.
918 label otherRegion = -1;
920 if (ownRegion == regionI && neiRegion != regionI)
922 otherRegion = neiRegion;
924 else if (ownRegion != regionI && neiRegion == regionI)
926 otherRegion = ownRegion;
930 FatalErrorIn("createRegionMesh(..)")
931 << "Exposed face:" << faceI
932 << " fc:" << mesh.faceCentres()[faceI]
933 << " has owner region " << ownRegion
934 << " and neighbour region " << neiRegion
935 << " when handling region:" << regionI
940 if (regionI < otherRegion)
942 exposedPatchIDs[i] = interfacePatches[interfaceI];
946 exposedPatchIDs[i] = interfacePatches[interfaceI]+1;
951 cellRemover.setRefinement
959 autoPtr<mapPolyMesh> map = meshMod.makeMesh
965 mesh.time().timeName(),
977 void createAndWriteRegion
980 const labelList& cellRegion,
981 const wordList& regionNames,
982 const labelList& faceToInterface,
983 const labelList& interfacePatches,
985 const word& newMeshInstance
988 Info<< "Creating mesh for region " << regionI
989 << ' ' << regionNames[regionI] << endl;
991 autoPtr<fvMesh> newMesh;
992 autoPtr<mapPolyMesh> map = createRegionMesh
997 regionNames[regionI],
1004 // Make map of all added patches
1005 labelHashSet addedPatches(2*interfacePatches.size());
1006 forAll(interfacePatches, interfaceI)
1008 addedPatches.insert(interfacePatches[interfaceI]);
1009 addedPatches.insert(interfacePatches[interfaceI]+1);
1012 Info<< "Mapping fields" << endl;
1014 // Map existing fields
1015 newMesh().updateMesh(map());
1017 // Add subsetted fields
1018 subsetVolFields<volScalarField>
1026 subsetVolFields<volVectorField>
1034 subsetVolFields<volSphericalTensorField>
1042 subsetVolFields<volSymmTensorField>
1050 subsetVolFields<volTensorField>
1059 subsetSurfaceFields<surfaceScalarField>
1066 subsetSurfaceFields<surfaceVectorField>
1073 subsetSurfaceFields<surfaceSphericalTensorField>
1080 subsetSurfaceFields<surfaceSymmTensorField>
1087 subsetSurfaceFields<surfaceTensorField>
1096 const polyBoundaryMesh& newPatches = newMesh().boundaryMesh();
1097 newPatches.checkParallelSync(true);
1099 // Delete empty patches
1100 // ~~~~~~~~~~~~~~~~~~~~
1102 // Create reordering list to move patches-to-be-deleted to end
1103 labelList oldToNew(newPatches.size(), -1);
1106 Info<< "Deleting empty patches" << endl;
1108 // Assumes all non-proc boundaries are on all processors!
1109 forAll(newPatches, patchI)
1111 const polyPatch& pp = newPatches[patchI];
1113 if (!isA<processorPolyPatch>(pp))
1115 if (returnReduce(pp.size(), sumOp<label>()) > 0)
1117 oldToNew[patchI] = newI++;
1122 // Same for processor patches (but need no reduction)
1123 forAll(newPatches, patchI)
1125 const polyPatch& pp = newPatches[patchI];
1127 if (isA<processorPolyPatch>(pp) && pp.size())
1129 oldToNew[patchI] = newI++;
1133 const label nNewPatches = newI;
1135 // Move all deleteable patches to the end
1136 forAll(oldToNew, patchI)
1138 if (oldToNew[patchI] == -1)
1140 oldToNew[patchI] = newI++;
1144 reorderPatches(newMesh(), oldToNew, nNewPatches);
1147 Info<< "Writing new mesh" << endl;
1149 newMesh().setInstance(newMeshInstance);
1152 // Write addressing files like decomposePar
1153 Info<< "Writing addressing to base mesh" << endl;
1155 labelIOList pointProcAddressing
1159 "pointRegionAddressing",
1160 newMesh().facesInstance(),
1161 newMesh().meshSubDir,
1169 Info<< "Writing map " << pointProcAddressing.name()
1170 << " from region" << regionI
1171 << " points back to base mesh." << endl;
1172 pointProcAddressing.write();
1174 labelIOList faceProcAddressing
1178 "faceRegionAddressing",
1179 newMesh().facesInstance(),
1180 newMesh().meshSubDir,
1188 forAll(faceProcAddressing, faceI)
1190 // face + turning index. (see decomposePar)
1191 // Is the face pointing in the same direction?
1192 label oldFaceI = map().faceMap()[faceI];
1196 map().cellMap()[newMesh().faceOwner()[faceI]]
1197 == mesh.faceOwner()[oldFaceI]
1200 faceProcAddressing[faceI] = oldFaceI+1;
1204 faceProcAddressing[faceI] = -(oldFaceI+1);
1207 Info<< "Writing map " << faceProcAddressing.name()
1208 << " from region" << regionI
1209 << " faces back to base mesh." << endl;
1210 faceProcAddressing.write();
1212 labelIOList cellProcAddressing
1216 "cellRegionAddressing",
1217 newMesh().facesInstance(),
1218 newMesh().meshSubDir,
1226 Info<< "Writing map " <<cellProcAddressing.name()
1227 << " from region" << regionI
1228 << " cells back to base mesh." << endl;
1229 cellProcAddressing.write();
1231 labelIOList boundaryProcAddressing
1235 "boundaryRegionAddressing",
1236 newMesh().facesInstance(),
1237 newMesh().meshSubDir,
1243 labelList(nNewPatches, -1)
1247 if (!addedPatches.found(i))
1249 label newI = oldToNew[i];
1250 if (newI >= 0 && newI < nNewPatches)
1252 boundaryProcAddressing[oldToNew[i]] = i;
1256 Info<< "Writing map " << boundaryProcAddressing.name()
1257 << " from region" << regionI
1258 << " boundary back to base mesh." << endl;
1259 boundaryProcAddressing.write();
1263 // Create for every region-region interface with non-zero size two patches.
1264 // First one is for minimumregion to maximumregion.
1265 // Note that patches get created in same order on all processors (if parallel)
1266 // since looping over synchronised 'interfaces'.
1267 labelList addRegionPatches
1270 const wordList& regionNames,
1271 const edgeList& interfaces,
1272 const List<Pair<word> >& interfaceNames
1275 Info<< nl << "Adding patches" << nl << endl;
1277 labelList interfacePatches(interfaces.size());
1279 forAll(interfaces, interI)
1281 const edge& e = interfaces[interI];
1282 const Pair<word>& names = interfaceNames[interI];
1284 //Info<< "For interface " << interI
1285 // << " between regions " << e
1286 // << " trying to add patches " << names << endl;
1289 directMappedWallPolyPatch patch1
1295 regionNames[e[1]], // sampleRegion
1296 directMappedPatchBase::NEARESTPATCHFACE,
1297 names[1], // samplePatch
1298 point::zero, // offset
1302 interfacePatches[interI] = addPatch(mesh, patch1);
1304 directMappedWallPolyPatch patch2
1310 regionNames[e[0]], // sampleRegion
1311 directMappedPatchBase::NEARESTPATCHFACE,
1313 point::zero, // offset
1316 addPatch(mesh, patch2);
1318 Info<< "For interface between region " << regionNames[e[0]]
1319 << " and " << regionNames[e[1]] << " added patches" << endl
1320 << " " << interfacePatches[interI]
1321 << "\t" << mesh.boundaryMesh()[interfacePatches[interI]].name()
1323 << " " << interfacePatches[interI]+1
1324 << "\t" << mesh.boundaryMesh()[interfacePatches[interI]+1].name()
1327 return interfacePatches;
1331 // Find region that covers most of cell zone
1332 label findCorrespondingRegion
1334 const labelList& existingZoneID, // per cell the (unique) zoneID
1335 const labelList& cellRegion,
1336 const label nCellRegions,
1338 const label minOverlapSize
1341 // Per region the number of cells in zoneI
1342 labelList cellsInZone(nCellRegions, 0);
1344 forAll(cellRegion, cellI)
1346 if (existingZoneID[cellI] == zoneI)
1348 cellsInZone[cellRegion[cellI]]++;
1352 Pstream::listCombineGather(cellsInZone, plusEqOp<label>());
1353 Pstream::listCombineScatter(cellsInZone);
1355 // Pick region with largest overlap of zoneI
1356 label regionI = findMax(cellsInZone);
1359 if (cellsInZone[regionI] < minOverlapSize)
1361 // Region covers too little of zone. Not good enough.
1366 // Check that region contains no cells that aren't in cellZone.
1367 forAll(cellRegion, cellI)
1369 if (cellRegion[cellI] == regionI && existingZoneID[cellI] != zoneI)
1371 // cellI in regionI but not in zoneI
1376 // If one in error, all should be in error. Note that branch gets taken
1378 reduce(regionI, minOp<label>());
1385 // Get zone per cell
1386 // - non-unique zoning
1390 const polyMesh& mesh,
1391 const cellZoneMesh& cellZones,
1393 labelList& neiZoneID
1397 zoneID.setSize(mesh.nCells());
1400 forAll(cellZones, zoneI)
1402 const cellZone& cz = cellZones[zoneI];
1406 label cellI = cz[i];
1407 if (zoneID[cellI] == -1)
1409 zoneID[cellI] = zoneI;
1413 FatalErrorIn("getZoneID(..)")
1414 << "Cell " << cellI << " with cell centre "
1415 << mesh.cellCentres()[cellI]
1416 << " is multiple zones. This is not allowed." << endl
1417 << "It is in zone " << cellZones[zoneID[cellI]].name()
1418 << " and in zone " << cellZones[zoneI].name()
1419 << exit(FatalError);
1424 // Neighbour zoneID.
1425 neiZoneID.setSize(mesh.nFaces()-mesh.nInternalFaces());
1427 forAll(neiZoneID, i)
1429 neiZoneID[i] = zoneID[mesh.faceOwner()[i+mesh.nInternalFaces()]];
1431 syncTools::swapBoundaryFaceList(mesh, neiZoneID);
1437 const bool sloppyCellZones,
1438 const polyMesh& mesh,
1440 const label nCellRegions,
1441 const labelList& cellRegion,
1443 labelList& regionToZone,
1444 wordList& regionNames,
1445 labelList& zoneToRegion
1448 const cellZoneMesh& cellZones = mesh.cellZones();
1450 regionToZone.setSize(nCellRegions, -1);
1451 regionNames.setSize(nCellRegions);
1452 zoneToRegion.setSize(cellZones.size(), -1);
1454 // Get current per cell zoneID
1455 labelList zoneID(mesh.nCells(), -1);
1456 labelList neiZoneID(mesh.nFaces()-mesh.nInternalFaces());
1457 getZoneID(mesh, cellZones, zoneID, neiZoneID);
1459 // Sizes per cellzone
1460 labelList zoneSizes(cellZones.size(), 0);
1462 List<wordList> zoneNames(Pstream::nProcs());
1463 zoneNames[Pstream::myProcNo()] = cellZones.names();
1464 Pstream::gatherList(zoneNames);
1465 Pstream::scatterList(zoneNames);
1467 forAll(zoneNames, procI)
1469 if (zoneNames[procI] != zoneNames[0])
1471 FatalErrorIn("matchRegions(..)")
1472 << "cellZones not synchronised across processors." << endl
1473 << "Master has cellZones " << zoneNames[0] << endl
1474 << "Processor " << procI
1475 << " has cellZones " << zoneNames[procI]
1476 << exit(FatalError);
1480 forAll(cellZones, zoneI)
1482 zoneSizes[zoneI] = returnReduce
1484 cellZones[zoneI].size(),
1491 if (sloppyCellZones)
1493 Info<< "Trying to match regions to existing cell zones;"
1494 << " region can be subset of cell zone." << nl << endl;
1496 forAll(cellZones, zoneI)
1498 label regionI = findCorrespondingRegion
1504 label(0.5*zoneSizes[zoneI]) // minimum overlap
1509 Info<< "Sloppily matched region " << regionI
1510 //<< " size " << regionSizes[regionI]
1511 << " to zone " << zoneI << " size " << zoneSizes[zoneI]
1513 zoneToRegion[zoneI] = regionI;
1514 regionToZone[regionI] = zoneI;
1515 regionNames[regionI] = cellZones[zoneI].name();
1521 Info<< "Trying to match regions to existing cell zones." << nl << endl;
1523 forAll(cellZones, zoneI)
1525 label regionI = findCorrespondingRegion
1531 1 // minimum overlap
1536 zoneToRegion[zoneI] = regionI;
1537 regionToZone[regionI] = zoneI;
1538 regionNames[regionI] = cellZones[zoneI].name();
1542 // Allocate region names for unmatched regions.
1543 forAll(regionToZone, regionI)
1545 if (regionToZone[regionI] == -1)
1547 regionNames[regionI] = "domain" + Foam::name(regionI);
1553 void writeCellToRegion(const fvMesh& mesh, const labelList& cellRegion)
1555 // Write to manual decomposition option
1557 labelIOList cellToRegion
1562 mesh.facesInstance(),
1570 cellToRegion.write();
1572 Info<< "Writing region per cell file (for manual decomposition) to "
1573 << cellToRegion.objectPath() << nl << endl;
1575 // Write for postprocessing
1577 volScalarField cellToRegion
1582 mesh.time().timeName(),
1589 dimensionedScalar("zero", dimless, 0),
1590 zeroGradientFvPatchScalarField::typeName
1592 forAll(cellRegion, cellI)
1594 cellToRegion[cellI] = cellRegion[cellI];
1596 cellToRegion.write();
1598 Info<< "Writing region per cell as volScalarField to "
1599 << cellToRegion.objectPath() << nl << endl;
1607 int main(int argc, char *argv[])
1611 "splits mesh into multiple regions (detected by walking across faces)"
1613 #include "addOverwriteOption.H"
1614 argList::addBoolOption
1617 "additionally split cellZones off into separate regions"
1619 argList::addBoolOption
1622 "use cellZones only to split mesh into regions; do not use walking"
1626 "cellZonesFileOnly",
1628 "like -cellZonesOnly, but use specified file"
1634 "specify additional region boundaries that walking does not cross"
1636 argList::addBoolOption
1639 "place cells into cellZones instead of splitting mesh"
1641 argList::addBoolOption
1644 "only write largest region"
1650 "only write region containing point"
1652 argList::addBoolOption
1657 argList::addBoolOption
1660 "try to match heuristically regions to existing cell zones"
1662 argList::addBoolOption
1665 "use faceZones to patch inter-region faces instead of single patch"
1668 #include "setRootCase.H"
1669 #include "createTime.H"
1670 runTime.functionObjects().off();
1671 #include "createMesh.H"
1672 const word oldInstance = mesh.pointsInstance();
1674 word blockedFacesName;
1675 if (args.optionReadIfPresent("blockedFaces", blockedFacesName))
1677 Info<< "Reading blocked internal faces from faceSet "
1678 << blockedFacesName << nl << endl;
1681 const bool makeCellZones = args.optionFound("makeCellZones");
1682 const bool largestOnly = args.optionFound("largestOnly");
1683 const bool insidePoint = args.optionFound("insidePoint");
1684 const bool useCellZones = args.optionFound("cellZones");
1685 const bool useCellZonesOnly = args.optionFound("cellZonesOnly");
1686 const bool useCellZonesFile = args.optionFound("cellZonesFileOnly");
1687 const bool overwrite = args.optionFound("overwrite");
1688 const bool detectOnly = args.optionFound("detectOnly");
1689 const bool sloppyCellZones = args.optionFound("sloppyCellZones");
1690 const bool useFaceZones = args.optionFound("useFaceZones");
1694 (useCellZonesOnly || useCellZonesFile)
1695 && (useCellZones || blockedFacesName.size())
1698 FatalErrorIn(args.executable())
1699 << "You cannot specify both -cellZonesOnly or -cellZonesFileOnly"
1700 << " (which specify complete split)"
1701 << " in combination with -blockedFaces or -cellZones"
1702 << " (which imply a split based on topology)"
1703 << exit(FatalError);
1709 Info<< "Using current faceZones to divide inter-region interfaces"
1710 << " into multiple patches."
1715 Info<< "Creating single patch per inter-region interface."
1721 if (insidePoint && largestOnly)
1723 FatalErrorIn(args.executable())
1724 << "You cannot specify both -largestOnly"
1725 << " (keep region with most cells)"
1726 << " and -insidePoint (keep region containing point)"
1727 << exit(FatalError);
1731 const cellZoneMesh& cellZones = mesh.cellZones();
1734 labelList zoneID(mesh.nCells(), -1);
1735 // Neighbour zoneID.
1736 labelList neiZoneID(mesh.nFaces()-mesh.nInternalFaces());
1737 getZoneID(mesh, cellZones, zoneID, neiZoneID);
1741 // Determine per cell the region it belongs to
1742 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1744 // cellRegion is the labelList with the region per cell.
1745 labelList cellRegion;
1747 labelList regionToZone;
1749 wordList regionNames;
1751 labelList zoneToRegion;
1753 label nCellRegions = 0;
1754 if (useCellZonesOnly)
1756 Info<< "Using current cellZones to split mesh into regions."
1757 << " This requires all"
1758 << " cells to be in one and only one cellZone." << nl << endl;
1760 label unzonedCellI = findIndex(zoneID, -1);
1761 if (unzonedCellI != -1)
1763 FatalErrorIn(args.executable())
1764 << "For the cellZonesOnly option all cells "
1765 << "have to be in a cellZone." << endl
1766 << "Cell " << unzonedCellI
1767 << " at" << mesh.cellCentres()[unzonedCellI]
1768 << " is not in a cellZone. There might be more unzoned cells."
1769 << exit(FatalError);
1771 cellRegion = zoneID;
1772 nCellRegions = gMax(cellRegion)+1;
1773 regionToZone.setSize(nCellRegions);
1774 regionNames.setSize(nCellRegions);
1775 zoneToRegion.setSize(cellZones.size(), -1);
1776 for (label regionI = 0; regionI < nCellRegions; regionI++)
1778 regionToZone[regionI] = regionI;
1779 zoneToRegion[regionI] = regionI;
1780 regionNames[regionI] = cellZones[regionI].name();
1783 else if (useCellZonesFile)
1785 const word zoneFile = args.option("cellZonesFileOnly");
1786 Info<< "Reading split from cellZones file " << zoneFile << endl
1787 << "This requires all"
1788 << " cells to be in one and only one cellZone." << nl << endl;
1790 cellZoneMesh newCellZones
1795 mesh.facesInstance(),
1796 polyMesh::meshSubDir,
1798 IOobject::MUST_READ,
1805 labelList newZoneID(mesh.nCells(), -1);
1806 labelList newNeiZoneID(mesh.nFaces()-mesh.nInternalFaces());
1807 getZoneID(mesh, newCellZones, newZoneID, newNeiZoneID);
1809 label unzonedCellI = findIndex(newZoneID, -1);
1810 if (unzonedCellI != -1)
1812 FatalErrorIn(args.executable())
1813 << "For the cellZonesFileOnly option all cells "
1814 << "have to be in a cellZone." << endl
1815 << "Cell " << unzonedCellI
1816 << " at" << mesh.cellCentres()[unzonedCellI]
1817 << " is not in a cellZone. There might be more unzoned cells."
1818 << exit(FatalError);
1820 cellRegion = newZoneID;
1821 nCellRegions = gMax(cellRegion)+1;
1822 zoneToRegion.setSize(newCellZones.size(), -1);
1823 regionToZone.setSize(nCellRegions);
1824 regionNames.setSize(nCellRegions);
1825 for (label regionI = 0; regionI < nCellRegions; regionI++)
1827 regionToZone[regionI] = regionI;
1828 zoneToRegion[regionI] = regionI;
1829 regionNames[regionI] = newCellZones[regionI].name();
1834 // Determine connected regions
1835 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
1837 // Mark additional faces that are blocked
1838 boolList blockedFace;
1840 // Read from faceSet
1841 if (blockedFacesName.size())
1843 faceSet blockedFaceSet(mesh, blockedFacesName);
1845 << returnReduce(blockedFaceSet.size(), sumOp<label>())
1846 << " blocked faces from set " << blockedFacesName << nl << endl;
1848 blockedFace.setSize(mesh.nFaces(), false);
1850 forAllConstIter(faceSet, blockedFaceSet, iter)
1852 blockedFace[iter.key()] = true;
1856 // Imply from differing cellZones
1859 blockedFace.setSize(mesh.nFaces(), false);
1861 for (label faceI = 0; faceI < mesh.nInternalFaces(); faceI++)
1863 label own = mesh.faceOwner()[faceI];
1864 label nei = mesh.faceNeighbour()[faceI];
1866 if (zoneID[own] != zoneID[nei])
1868 blockedFace[faceI] = true;
1872 // Different cellZones on either side of processor patch.
1873 forAll(neiZoneID, i)
1875 label faceI = i+mesh.nInternalFaces();
1877 if (zoneID[mesh.faceOwner()[faceI]] != neiZoneID[i])
1879 blockedFace[faceI] = true;
1884 // Do a topological walk to determine regions
1885 regionSplit regions(mesh, blockedFace);
1886 nCellRegions = regions.nRegions();
1887 cellRegion.transfer(regions);
1889 // Make up region names. If possible match them to existing zones.
1903 Info<< endl << "Number of regions:" << nCellRegions << nl << endl;
1906 // Write decomposition to file
1907 writeCellToRegion(mesh, cellRegion);
1914 labelList regionSizes(nCellRegions, 0);
1916 forAll(cellRegion, cellI)
1918 regionSizes[cellRegion[cellI]]++;
1920 forAll(regionSizes, regionI)
1922 reduce(regionSizes[regionI], sumOp<label>());
1925 Info<< "Region\tCells" << nl
1926 << "------\t-----" << endl;
1928 forAll(regionSizes, regionI)
1930 Info<< regionI << '\t' << regionSizes[regionI] << nl;
1936 // Print region to zone
1937 Info<< "Region\tZone\tName" << nl
1938 << "------\t----\t----" << endl;
1939 forAll(regionToZone, regionI)
1941 Info<< regionI << '\t' << regionToZone[regionI] << '\t'
1942 << regionNames[regionI] << nl;
1948 // Since we're going to mess with patches and zones make sure all
1950 mesh.boundaryMesh().checkParallelSync(true);
1951 mesh.faceZones().checkParallelSync(true);
1957 // - the two regions on either side
1959 // - the (global) size
1960 edgeList interfaces;
1961 List<Pair<word> > interfaceNames;
1962 labelList interfaceSizes;
1963 // per face the interface
1964 labelList faceToInterface;
1979 Info<< "Sizes of interfaces between regions:" << nl << nl
1980 << "Interface\tRegion\tRegion\tFaces" << nl
1981 << "---------\t------\t------\t-----" << endl;
1983 forAll(interfaces, interI)
1985 const edge& e = interfaces[interI];
1988 << "\t\t" << e[0] << '\t' << e[1]
1989 << '\t' << interfaceSizes[interI] << nl;
2000 // Read objects in time directory
2001 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2003 IOobjectList objects(mesh, runTime.timeName());
2007 PtrList<volScalarField> vsFlds;
2008 ReadFields(mesh, objects, vsFlds);
2010 PtrList<volVectorField> vvFlds;
2011 ReadFields(mesh, objects, vvFlds);
2013 PtrList<volSphericalTensorField> vstFlds;
2014 ReadFields(mesh, objects, vstFlds);
2016 PtrList<volSymmTensorField> vsymtFlds;
2017 ReadFields(mesh, objects, vsymtFlds);
2019 PtrList<volTensorField> vtFlds;
2020 ReadFields(mesh, objects, vtFlds);
2022 // Read surface fields.
2024 PtrList<surfaceScalarField> ssFlds;
2025 ReadFields(mesh, objects, ssFlds);
2027 PtrList<surfaceVectorField> svFlds;
2028 ReadFields(mesh, objects, svFlds);
2030 PtrList<surfaceSphericalTensorField> sstFlds;
2031 ReadFields(mesh, objects, sstFlds);
2033 PtrList<surfaceSymmTensorField> ssymtFlds;
2034 ReadFields(mesh, objects, ssymtFlds);
2036 PtrList<surfaceTensorField> stFlds;
2037 ReadFields(mesh, objects, stFlds);
2042 // Remove any demand-driven fields ('S', 'V' etc)
2046 if (nCellRegions == 1)
2048 Info<< "Only one region. Doing nothing." << endl;
2050 else if (makeCellZones)
2052 Info<< "Putting cells into cellZones instead of splitting mesh."
2055 // Check if region overlaps with existing zone. If so keep.
2057 for (label regionI = 0; regionI < nCellRegions; regionI++)
2059 label zoneI = regionToZone[regionI];
2063 Info<< " Region " << regionI << " : corresponds to existing"
2065 << zoneI << ' ' << cellZones[zoneI].name() << endl;
2069 // Create new cellZone.
2070 labelList regionCells = findIndices(cellRegion, regionI);
2072 word zoneName = "region" + Foam::name(regionI);
2074 zoneI = cellZones.findZoneID(zoneName);
2078 zoneI = cellZones.size();
2079 mesh.cellZones().setSize(zoneI+1);
2080 mesh.cellZones().set
2086 regionCells, //addressing
2088 cellZones //cellZoneMesh
2094 mesh.cellZones()[zoneI].clearAddressing();
2095 mesh.cellZones()[zoneI] = regionCells;
2097 Info<< " Region " << regionI << " : created new cellZone "
2098 << zoneI << ' ' << cellZones[zoneI].name() << endl;
2101 mesh.cellZones().writeOpt() = IOobject::AUTO_WRITE;
2106 mesh.setInstance(runTime.timeName());
2110 mesh.setInstance(oldInstance);
2113 Info<< "Writing cellZones as new mesh to time " << runTime.timeName()
2119 // Write cellSets for convenience
2120 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2122 Info<< "Writing cellSets corresponding to cellZones." << nl << endl;
2124 forAll(cellZones, zoneI)
2126 const cellZone& cz = cellZones[zoneI];
2128 cellSet(mesh, cz.name(), cz).write();
2133 // Add patches for interfaces
2134 // ~~~~~~~~~~~~~~~~~~~~~~~~~~
2136 // Add all possible patches. Empty ones get filtered later on.
2137 Info<< nl << "Adding patches" << nl << endl;
2139 labelList interfacePatches
2162 const point insidePoint = args.optionRead<point>("insidePoint");
2166 label cellI = mesh.findCell(insidePoint);
2168 Info<< nl << "Found point " << insidePoint << " in cell " << cellI
2173 regionI = cellRegion[cellI];
2176 reduce(regionI, maxOp<label>());
2179 << "Subsetting region " << regionI
2180 << " containing point " << insidePoint << endl;
2184 FatalErrorIn(args.executable())
2185 << "Point " << insidePoint
2186 << " is not inside the mesh." << nl
2187 << "Bounding box of the mesh:" << mesh.bounds()
2188 << exit(FatalError);
2191 createAndWriteRegion
2199 (overwrite ? oldInstance : runTime.timeName())
2202 else if (largestOnly)
2204 label regionI = findMax(regionSizes);
2207 << "Subsetting region " << regionI
2208 << " of size " << regionSizes[regionI] << endl;
2210 createAndWriteRegion
2218 (overwrite ? oldInstance : runTime.timeName())
2224 for (label regionI = 0; regionI < nCellRegions; regionI++)
2227 << "Region " << regionI << nl
2228 << "-------- " << endl;
2230 createAndWriteRegion
2238 (overwrite ? oldInstance : runTime.timeName())
2244 Info<< "End\n" << endl;
2250 // ************************************************************************* //