1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 1991-2009 OpenCFD Ltd.
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 the
13 Free Software Foundation; either version 2 of the License, or (at your
14 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, write to the Free Software Foundation,
23 Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
29 Splits mesh into multiple regions.
31 Each region is defined as a domain whose cells can all be reached by
32 cell-face-cell walking without crossing
34 - additional faces from faceset (-blockedFaces faceSet).
35 - any face inbetween differing cellZones (-cellZones)
38 - volScalarField with regions as different scalars (-detectOnly) or
39 - mesh with multiple regions or
40 - mesh with cells put into cellZones (-makeCellZones)
43 - cellZonesOnly does not do a walk and uses the cellZones only. Use
44 this if you don't mind having disconnected domains in a single region.
45 This option requires all cells to be in one (and one only) cellZone.
47 - cellZonesFileOnly behaves like -cellZonesOnly but reads the cellZones
48 from the specified file. This allows one to explicitly specify the region
49 distribution and still have multiple cellZones per region.
51 - useCellZonesOnly does not do a walk and uses the cellZones only. Use
52 this if you don't mind having disconnected domains in a single region.
53 This option requires all cells to be in one (and one only) cellZone.
56 - Should work in parallel.
57 cellZones can differ on either side of processor boundaries in which case
58 the faces get moved from processor patch to directMapped patch. Not
61 - If a cell zone gets split into more than one region it can detect
62 the largest matching region (-sloppyCellZones). This will accept any
63 region that covers more than 50% of the zone. It has to be a subset
64 so cannot have any cells in any other zone.
66 - writes maps like decomposePar back to original mesh:
67 - pointRegionAddressing : for every point in this region the point in
69 - cellRegionAddressing : ,, cell ,, cell ,,
70 - faceRegionAddressing : ,, face ,, face in
71 the original mesh + 'turning index'. For a face in the same orientation
72 this is the original facelabel+1, for a turned face this is -facelabel-1
73 \*---------------------------------------------------------------------------*/
75 #include "SortableList.H"
77 #include "regionSplit.H"
78 #include "fvMeshSubset.H"
79 #include "IOobjectList.H"
80 #include "volFields.H"
83 #include "polyTopoChange.H"
84 #include "removeCells.H"
86 #include "syncTools.H"
87 #include "ReadFields.H"
88 #include "directMappedWallPolyPatch.H"
89 #include "zeroGradientFvPatchFields.H"
93 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
95 template<class GeoField>
96 void addPatchFields(fvMesh& mesh, const word& patchFieldType)
98 HashTable<const GeoField*> flds
100 mesh.objectRegistry::lookupClass<GeoField>()
105 typename HashTable<const GeoField*>::const_iterator iter = flds.begin();
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>()
145 typename HashTable<const GeoField*>::const_iterator iter = flds.begin();
150 const GeoField& fld = *iter();
152 const_cast<typename GeoField::GeometricBoundaryField&>
160 // Reorder patch field
161 template<class GeoField>
162 void reorderPatchFields(fvMesh& mesh, const labelList& oldToNew)
164 HashTable<const GeoField*> flds
166 mesh.objectRegistry::lookupClass<GeoField>()
171 typename HashTable<const GeoField*>::const_iterator iter = flds.begin();
176 const GeoField& fld = *iter();
178 typename GeoField::GeometricBoundaryField& bfld =
179 const_cast<typename GeoField::GeometricBoundaryField&>
184 bfld.reorder(oldToNew);
189 // Adds patch if not yet there. Returns patchID.
190 label addPatch(fvMesh& mesh, const polyPatch& patch)
192 polyBoundaryMesh& polyPatches =
193 const_cast<polyBoundaryMesh&>(mesh.boundaryMesh());
195 label patchI = polyPatches.findPatchID(patch.name());
198 if (polyPatches[patchI].type() == patch.type())
205 FatalErrorIn("addPatch(fvMesh&, const polyPatch*)")
206 << "Already have patch " << patch.name()
207 << " but of type " << patch.type()
213 label insertPatchI = polyPatches.size();
214 label startFaceI = mesh.nFaces();
216 forAll(polyPatches, patchI)
218 const polyPatch& pp = polyPatches[patchI];
220 if (isA<processorPolyPatch>(pp))
222 insertPatchI = patchI;
223 startFaceI = pp.start();
229 // Below is all quite a hack. Feel free to change once there is a better
230 // mechanism to insert and reorder patches.
232 // Clear local fields and e.g. polyMesh parallelInfo.
235 label sz = polyPatches.size();
237 fvBoundaryMesh& fvPatches = const_cast<fvBoundaryMesh&>(mesh.boundary());
239 // Add polyPatch at the end
240 polyPatches.setSize(sz+1);
247 insertPatchI, //index
252 fvPatches.setSize(sz+1);
258 polyPatches[sz], // point to newly added polyPatch
263 addPatchFields<volScalarField>
266 calculatedFvPatchField<scalar>::typeName
268 addPatchFields<volVectorField>
271 calculatedFvPatchField<vector>::typeName
273 addPatchFields<volSphericalTensorField>
276 calculatedFvPatchField<sphericalTensor>::typeName
278 addPatchFields<volSymmTensorField>
281 calculatedFvPatchField<symmTensor>::typeName
283 addPatchFields<volTensorField>
286 calculatedFvPatchField<tensor>::typeName
291 addPatchFields<surfaceScalarField>
294 calculatedFvPatchField<scalar>::typeName
296 addPatchFields<surfaceVectorField>
299 calculatedFvPatchField<vector>::typeName
301 addPatchFields<surfaceSphericalTensorField>
304 calculatedFvPatchField<sphericalTensor>::typeName
306 addPatchFields<surfaceSymmTensorField>
309 calculatedFvPatchField<symmTensor>::typeName
311 addPatchFields<surfaceTensorField>
314 calculatedFvPatchField<tensor>::typeName
317 // Create reordering list
318 // patches before insert position stay as is
319 labelList oldToNew(sz+1);
320 for (label i = 0; i < insertPatchI; i++)
324 // patches after insert position move one up
325 for (label i = insertPatchI; i < sz; i++)
329 // appended patch gets moved to insert position
330 oldToNew[sz] = insertPatchI;
332 // Shuffle into place
333 polyPatches.reorder(oldToNew);
334 fvPatches.reorder(oldToNew);
336 reorderPatchFields<volScalarField>(mesh, oldToNew);
337 reorderPatchFields<volVectorField>(mesh, oldToNew);
338 reorderPatchFields<volSphericalTensorField>(mesh, oldToNew);
339 reorderPatchFields<volSymmTensorField>(mesh, oldToNew);
340 reorderPatchFields<volTensorField>(mesh, oldToNew);
341 reorderPatchFields<surfaceScalarField>(mesh, oldToNew);
342 reorderPatchFields<surfaceVectorField>(mesh, oldToNew);
343 reorderPatchFields<surfaceSphericalTensorField>(mesh, oldToNew);
344 reorderPatchFields<surfaceSymmTensorField>(mesh, oldToNew);
345 reorderPatchFields<surfaceTensorField>(mesh, oldToNew);
351 // Reorder and delete patches.
355 const labelList& oldToNew,
356 const label nNewPatches
359 polyBoundaryMesh& polyPatches =
360 const_cast<polyBoundaryMesh&>(mesh.boundaryMesh());
361 fvBoundaryMesh& fvPatches = const_cast<fvBoundaryMesh&>(mesh.boundary());
363 // Shuffle into place
364 polyPatches.reorder(oldToNew);
365 fvPatches.reorder(oldToNew);
367 reorderPatchFields<volScalarField>(mesh, oldToNew);
368 reorderPatchFields<volVectorField>(mesh, oldToNew);
369 reorderPatchFields<volSphericalTensorField>(mesh, oldToNew);
370 reorderPatchFields<volSymmTensorField>(mesh, oldToNew);
371 reorderPatchFields<volTensorField>(mesh, oldToNew);
372 reorderPatchFields<surfaceScalarField>(mesh, oldToNew);
373 reorderPatchFields<surfaceVectorField>(mesh, oldToNew);
374 reorderPatchFields<surfaceSphericalTensorField>(mesh, oldToNew);
375 reorderPatchFields<surfaceSymmTensorField>(mesh, oldToNew);
376 reorderPatchFields<surfaceTensorField>(mesh, oldToNew);
379 polyPatches.setSize(nNewPatches);
380 fvPatches.setSize(nNewPatches);
381 trimPatchFields<volScalarField>(mesh, nNewPatches);
382 trimPatchFields<volVectorField>(mesh, nNewPatches);
383 trimPatchFields<volSphericalTensorField>(mesh, nNewPatches);
384 trimPatchFields<volSymmTensorField>(mesh, nNewPatches);
385 trimPatchFields<volTensorField>(mesh, nNewPatches);
386 trimPatchFields<surfaceScalarField>(mesh, nNewPatches);
387 trimPatchFields<surfaceVectorField>(mesh, nNewPatches);
388 trimPatchFields<surfaceSphericalTensorField>(mesh, nNewPatches);
389 trimPatchFields<surfaceSymmTensorField>(mesh, nNewPatches);
390 trimPatchFields<surfaceTensorField>(mesh, nNewPatches);
394 template<class GeoField>
398 const fvMesh& subMesh,
399 const labelList& cellMap,
400 const labelList& faceMap
403 const labelList patchMap(identity(mesh.boundaryMesh().size()));
405 HashTable<const GeoField*> fields
407 mesh.objectRegistry::lookupClass<GeoField>()
409 forAllConstIter(typename HashTable<const GeoField*>, fields, iter)
411 const GeoField& fld = *iter();
413 Info<< "Mapping field " << fld.name() << endl;
415 tmp<GeoField> tSubFld
417 fvMeshSubset::interpolate
427 // Hack: set value to 0 for introduced patches (since don't
429 forAll(tSubFld().boundaryField(), patchI)
431 const fvPatchField<typename GeoField::value_type>& pfld =
432 tSubFld().boundaryField()[patchI];
436 isA<calculatedFvPatchField<typename GeoField::value_type> >
440 tSubFld().boundaryField()[patchI] ==
441 pTraits<typename GeoField::value_type>::zero;
446 GeoField* subFld = tSubFld.ptr();
447 subFld->rename(fld.name());
448 subFld->writeOpt() = IOobject::AUTO_WRITE;
454 template<class GeoField>
455 void subsetSurfaceFields
458 const fvMesh& subMesh,
459 const labelList& faceMap
462 const labelList patchMap(identity(mesh.boundaryMesh().size()));
464 HashTable<const GeoField*> fields
466 mesh.objectRegistry::lookupClass<GeoField>()
468 forAllConstIter(typename HashTable<const GeoField*>, fields, iter)
470 const GeoField& fld = *iter();
472 Info<< "Mapping field " << fld.name() << endl;
474 tmp<GeoField> tSubFld
476 fvMeshSubset::interpolate
485 // Hack: set value to 0 for introduced patches (since don't
487 forAll(tSubFld().boundaryField(), patchI)
489 const fvsPatchField<typename GeoField::value_type>& pfld =
490 tSubFld().boundaryField()[patchI];
494 isA<calculatedFvsPatchField<typename GeoField::value_type> >
498 tSubFld().boundaryField()[patchI] ==
499 pTraits<typename GeoField::value_type>::zero;
504 GeoField* subFld = tSubFld.ptr();
505 subFld->rename(fld.name());
506 subFld->writeOpt() = IOobject::AUTO_WRITE;
511 // Select all cells not in the region
512 labelList getNonRegionCells(const labelList& cellRegion, const label regionI)
514 DynamicList<label> nonRegionCells(cellRegion.size());
515 forAll(cellRegion, cellI)
517 if (cellRegion[cellI] != regionI)
519 nonRegionCells.append(cellI);
522 return nonRegionCells.shrink();
526 // Get per region-region interface the sizes. If sumParallel sums sizes.
527 // Returns interfaces as straight list for looping in identical order.
528 void getInterfaceSizes
530 const polyMesh& mesh,
531 const labelList& cellRegion,
532 const bool sumParallel,
534 edgeList& interfaces,
535 EdgeMap<label>& interfaceSizes
542 forAll(mesh.faceNeighbour(), faceI)
544 label ownRegion = cellRegion[mesh.faceOwner()[faceI]];
545 label neiRegion = cellRegion[mesh.faceNeighbour()[faceI]];
547 if (ownRegion != neiRegion)
551 min(ownRegion, neiRegion),
552 max(ownRegion, neiRegion)
555 EdgeMap<label>::iterator iter = interfaceSizes.find(interface);
557 if (iter != interfaceSizes.end())
563 interfaceSizes.insert(interface, 1);
571 // Neighbour cellRegion.
572 labelList coupledRegion(mesh.nFaces()-mesh.nInternalFaces());
574 forAll(coupledRegion, i)
576 label cellI = mesh.faceOwner()[i+mesh.nInternalFaces()];
577 coupledRegion[i] = cellRegion[cellI];
579 syncTools::swapBoundaryFaceList(mesh, coupledRegion, false);
581 forAll(coupledRegion, i)
583 label faceI = i+mesh.nInternalFaces();
584 label ownRegion = cellRegion[mesh.faceOwner()[faceI]];
585 label neiRegion = coupledRegion[i];
587 if (ownRegion != neiRegion)
591 min(ownRegion, neiRegion),
592 max(ownRegion, neiRegion)
595 EdgeMap<label>::iterator iter = interfaceSizes.find(interface);
597 if (iter != interfaceSizes.end())
603 interfaceSizes.insert(interface, 1);
609 if (sumParallel && Pstream::parRun())
611 if (Pstream::master())
613 // Receive and add to my sizes
616 int slave=Pstream::firstSlave();
617 slave<=Pstream::lastSlave();
621 IPstream fromSlave(Pstream::blocking, slave);
623 EdgeMap<label> slaveSizes(fromSlave);
625 forAllConstIter(EdgeMap<label>, slaveSizes, slaveIter)
627 EdgeMap<label>::iterator masterIter =
628 interfaceSizes.find(slaveIter.key());
630 if (masterIter != interfaceSizes.end())
632 masterIter() += slaveIter();
636 interfaceSizes.insert(slaveIter.key(), slaveIter());
644 int slave=Pstream::firstSlave();
645 slave<=Pstream::lastSlave();
649 // Receive the edges using shared points from the slave.
650 OPstream toSlave(Pstream::blocking, slave);
651 toSlave << interfaceSizes;
658 OPstream toMaster(Pstream::blocking, Pstream::masterNo());
659 toMaster << interfaceSizes;
661 // Receive from master
663 IPstream fromMaster(Pstream::blocking, Pstream::masterNo());
664 fromMaster >> interfaceSizes;
669 // Make sure all processors have interfaces in same order
670 interfaces = interfaceSizes.toc();
673 Pstream::scatter(interfaces);
678 // Create mesh for region.
679 autoPtr<mapPolyMesh> createRegionMesh
681 const labelList& cellRegion,
682 const EdgeMap<label>& interfaceToPatch,
685 const word& regionName,
686 autoPtr<fvMesh>& newMesh
689 // Create dummy system/fv*
694 mesh.time().system(),
702 Info<< "Testing:" << io.objectPath() << endl;
705 // if (!exists(io.objectPath()))
707 Info<< "Writing dummy " << regionName/io.name() << endl;
708 dictionary dummyDict;
710 dummyDict.add("divSchemes", divDict);
712 dummyDict.add("gradSchemes", gradDict);
714 dummyDict.add("laplacianSchemes", laplDict);
716 IOdictionary(io, dummyDict).regIOobject::write();
723 mesh.time().system(),
732 //if (!exists(io.objectPath()))
734 Info<< "Writing dummy " << regionName/io.name() << endl;
735 dictionary dummyDict;
736 IOdictionary(io, dummyDict).regIOobject::write();
741 // Neighbour cellRegion.
742 labelList coupledRegion(mesh.nFaces()-mesh.nInternalFaces());
744 forAll(coupledRegion, i)
746 label cellI = mesh.faceOwner()[i+mesh.nInternalFaces()];
747 coupledRegion[i] = cellRegion[cellI];
749 syncTools::swapBoundaryFaceList(mesh, coupledRegion, false);
752 // Topology change container. Start off from existing mesh.
753 polyTopoChange meshMod(mesh);
755 // Cell remover engine
756 removeCells cellRemover(mesh);
758 // Select all but region cells
759 labelList cellsToRemove(getNonRegionCells(cellRegion, regionI));
761 // Find out which faces will get exposed. Note that this
762 // gets faces in mesh face order. So both regions will get same
763 // face in same order (important!)
764 labelList exposedFaces = cellRemover.getExposedFaces(cellsToRemove);
766 labelList exposedPatchIDs(exposedFaces.size());
767 forAll(exposedFaces, i)
769 label faceI = exposedFaces[i];
771 label ownRegion = cellRegion[mesh.faceOwner()[faceI]];
772 label neiRegion = -1;
774 if (mesh.isInternalFace(faceI))
776 neiRegion = cellRegion[mesh.faceNeighbour()[faceI]];
780 neiRegion = coupledRegion[faceI-mesh.nInternalFaces()];
783 label otherRegion = -1;
785 if (ownRegion == regionI && neiRegion != regionI)
787 otherRegion = neiRegion;
789 else if (ownRegion != regionI && neiRegion == regionI)
791 otherRegion = ownRegion;
795 FatalErrorIn("createRegionMesh(..)")
796 << "Exposed face:" << faceI
797 << " fc:" << mesh.faceCentres()[faceI]
798 << " has owner region " << ownRegion
799 << " and neighbour region " << neiRegion
800 << " when handling region:" << regionI
804 if (otherRegion != -1)
806 edge interface(regionI, otherRegion);
809 if (regionI < otherRegion)
811 exposedPatchIDs[i] = interfaceToPatch[interface];
815 exposedPatchIDs[i] = interfaceToPatch[interface]+1;
821 cellRemover.setRefinement
829 autoPtr<mapPolyMesh> map = meshMod.makeMesh
835 mesh.time().timeName(),
847 void createAndWriteRegion
850 const labelList& cellRegion,
851 const wordList& regionNames,
852 const EdgeMap<label>& interfaceToPatch,
854 const word& newMeshInstance
857 Info<< "Creating mesh for region " << regionI
858 << ' ' << regionNames[regionI] << endl;
860 autoPtr<fvMesh> newMesh;
861 autoPtr<mapPolyMesh> map = createRegionMesh
867 regionNames[regionI],
871 Info<< "Mapping fields" << endl;
873 // Map existing fields
874 newMesh().updateMesh(map());
876 // Add subsetted fields
877 subsetVolFields<volScalarField>
884 subsetVolFields<volVectorField>
891 subsetVolFields<volSphericalTensorField>
898 subsetVolFields<volSymmTensorField>
905 subsetVolFields<volTensorField>
913 subsetSurfaceFields<surfaceScalarField>
919 subsetSurfaceFields<surfaceVectorField>
925 subsetSurfaceFields<surfaceSphericalTensorField>
931 subsetSurfaceFields<surfaceSymmTensorField>
937 subsetSurfaceFields<surfaceTensorField>
945 const polyBoundaryMesh& newPatches = newMesh().boundaryMesh();
946 newPatches.checkParallelSync(true);
948 // Delete empty patches
949 // ~~~~~~~~~~~~~~~~~~~~
951 // Create reordering list to move patches-to-be-deleted to end
952 labelList oldToNew(newPatches.size(), -1);
955 Info<< "Deleting empty patches" << endl;
957 // Assumes all non-proc boundaries are on all processors!
958 forAll(newPatches, patchI)
960 const polyPatch& pp = newPatches[patchI];
962 if (!isA<processorPolyPatch>(pp))
964 if (returnReduce(pp.size(), sumOp<label>()) > 0)
966 oldToNew[patchI] = newI++;
971 // Same for processor patches (but need no reduction)
972 forAll(newPatches, patchI)
974 const polyPatch& pp = newPatches[patchI];
976 if (isA<processorPolyPatch>(pp) && pp.size())
978 oldToNew[patchI] = newI++;
982 const label nNewPatches = newI;
984 // Move all deleteable patches to the end
985 forAll(oldToNew, patchI)
987 if (oldToNew[patchI] == -1)
989 oldToNew[patchI] = newI++;
993 reorderPatches(newMesh(), oldToNew, nNewPatches);
996 Info<< "Writing new mesh" << endl;
998 newMesh().setInstance(newMeshInstance);
1001 // Write addressing files like decomposePar
1002 Info<< "Writing addressing to base mesh" << endl;
1004 labelIOList pointProcAddressing
1008 "pointRegionAddressing",
1009 newMesh().facesInstance(),
1010 newMesh().meshSubDir,
1018 Info<< "Writing map " << pointProcAddressing.name()
1019 << " from region" << regionI
1020 << " points back to base mesh." << endl;
1021 pointProcAddressing.write();
1023 labelIOList faceProcAddressing
1027 "faceRegionAddressing",
1028 newMesh().facesInstance(),
1029 newMesh().meshSubDir,
1037 forAll(faceProcAddressing, faceI)
1039 // face + turning index. (see decomposePar)
1040 // Is the face pointing in the same direction?
1041 label oldFaceI = map().faceMap()[faceI];
1045 map().cellMap()[newMesh().faceOwner()[faceI]]
1046 == mesh.faceOwner()[oldFaceI]
1049 faceProcAddressing[faceI] = oldFaceI+1;
1053 faceProcAddressing[faceI] = -(oldFaceI+1);
1056 Info<< "Writing map " << faceProcAddressing.name()
1057 << " from region" << regionI
1058 << " faces back to base mesh." << endl;
1059 faceProcAddressing.write();
1061 labelIOList cellProcAddressing
1065 "cellRegionAddressing",
1066 newMesh().facesInstance(),
1067 newMesh().meshSubDir,
1075 Info<< "Writing map " <<cellProcAddressing.name()
1076 << " from region" << regionI
1077 << " cells back to base mesh." << endl;
1078 cellProcAddressing.write();
1082 // Create for every region-region interface with non-zero size two patches.
1083 // First one is for minimumregion to maximumregion.
1084 // Note that patches get created in same order on all processors (if parallel)
1085 // since looping over synchronised 'interfaces'.
1086 EdgeMap<label> addRegionPatches
1089 const labelList& cellRegion,
1090 const label nCellRegions,
1091 const edgeList& interfaces,
1092 const EdgeMap<label>& interfaceSizes,
1093 const wordList& regionNames
1096 // Check that all patches are present in same order.
1097 mesh.boundaryMesh().checkParallelSync(true);
1099 Info<< nl << "Adding patches" << nl << endl;
1101 EdgeMap<label> interfaceToPatch(nCellRegions);
1103 forAll(interfaces, interI)
1105 const edge& e = interfaces[interI];
1107 if (interfaceSizes[e] > 0)
1109 const word inter1 = regionNames[e[0]] + "_to_" + regionNames[e[1]];
1110 const word inter2 = regionNames[e[1]] + "_to_" + regionNames[e[0]];
1112 directMappedWallPolyPatch patch1
1118 regionNames[e[1]], // sampleRegion
1119 directMappedPatchBase::NEARESTPATCHFACE,
1120 inter2, // samplePatch
1121 point::zero, // offset
1125 label patchI = addPatch(mesh, patch1);
1127 directMappedWallPolyPatch patch2
1133 regionNames[e[0]], // sampleRegion
1134 directMappedPatchBase::NEARESTPATCHFACE,
1136 point::zero, // offset
1139 addPatch(mesh, patch2);
1141 Info<< "For interface between region " << e[0]
1142 << " and " << e[1] << " added patch " << patchI
1143 << " " << mesh.boundaryMesh()[patchI].name()
1146 interfaceToPatch.insert(e, patchI);
1149 return interfaceToPatch;
1153 // Find region that covers most of cell zone
1154 label findCorrespondingRegion
1156 const labelList& existingZoneID, // per cell the (unique) zoneID
1157 const labelList& cellRegion,
1158 const label nCellRegions,
1160 const label minOverlapSize
1163 // Per region the number of cells in zoneI
1164 labelList cellsInZone(nCellRegions, 0);
1166 forAll(cellRegion, cellI)
1168 if (existingZoneID[cellI] == zoneI)
1170 cellsInZone[cellRegion[cellI]]++;
1174 Pstream::listCombineGather(cellsInZone, plusEqOp<label>());
1175 Pstream::listCombineScatter(cellsInZone);
1177 // Pick region with largest overlap of zoneI
1178 label regionI = findMax(cellsInZone);
1181 if (cellsInZone[regionI] < minOverlapSize)
1183 // Region covers too little of zone. Not good enough.
1188 // Check that region contains no cells that aren't in cellZone.
1189 forAll(cellRegion, cellI)
1191 if (cellRegion[cellI] == regionI && existingZoneID[cellI] != zoneI)
1193 // cellI in regionI but not in zoneI
1198 // If one in error, all should be in error. Note that branch gets taken
1200 reduce(regionI, minOp<label>());
1207 //// Checks if cellZone has corresponding cellRegion.
1208 //label findCorrespondingRegion
1210 // const cellZoneMesh& cellZones,
1211 // const labelList& existingZoneID, // per cell the (unique) zoneID
1212 // const labelList& cellRegion,
1213 // const label nCellRegions,
1214 // const label zoneI
1217 // // Region corresponding to zone. Start off with special value: no
1218 // // corresponding region.
1219 // label regionI = labelMax;
1221 // const cellZone& cz = cellZones[zoneI];
1225 // // My local portion is empty. Maps to any empty cellZone. Mark with
1226 // // special value which can get overwritten by other processors.
1231 // regionI = cellRegion[cz[0]];
1235 // if (cellRegion[cz[i]] != regionI)
1237 // regionI = labelMax;
1243 // // Determine same zone over all processors.
1244 // reduce(regionI, maxOp<label>());
1247 // // 2. All of region present?
1249 // if (regionI == labelMax)
1253 // else if (regionI != -1)
1255 // forAll(cellRegion, cellI)
1257 // if (cellRegion[cellI] == regionI && existingZoneID[cellI] != zoneI)
1259 // // cellI in regionI but not in zoneI
1264 // // If one in error, all should be in error. Note that branch gets taken
1266 // reduce(regionI, minOp<label>());
1273 // Get zone per cell
1274 // - non-unique zoning
1278 const polyMesh& mesh,
1279 const cellZoneMesh& cellZones,
1281 labelList& neiZoneID
1285 zoneID.setSize(mesh.nCells());
1288 forAll(cellZones, zoneI)
1290 const cellZone& cz = cellZones[zoneI];
1294 label cellI = cz[i];
1295 if (zoneID[cellI] == -1)
1297 zoneID[cellI] = zoneI;
1301 FatalErrorIn("getZoneID(..)")
1302 << "Cell " << cellI << " with cell centre "
1303 << mesh.cellCentres()[cellI]
1304 << " is multiple zones. This is not allowed." << endl
1305 << "It is in zone " << cellZones[zoneID[cellI]].name()
1306 << " and in zone " << cellZones[zoneI].name()
1307 << exit(FatalError);
1312 // Neighbour zoneID.
1313 neiZoneID.setSize(mesh.nFaces()-mesh.nInternalFaces());
1315 forAll(neiZoneID, i)
1317 neiZoneID[i] = zoneID[mesh.faceOwner()[i+mesh.nInternalFaces()]];
1319 syncTools::swapBoundaryFaceList(mesh, neiZoneID, false);
1325 const bool sloppyCellZones,
1326 const polyMesh& mesh,
1328 const label nCellRegions,
1329 const labelList& cellRegion,
1331 labelList& regionToZone,
1332 wordList& regionNames,
1333 labelList& zoneToRegion
1336 const cellZoneMesh& cellZones = mesh.cellZones();
1338 regionToZone.setSize(nCellRegions, -1);
1339 regionNames.setSize(nCellRegions);
1340 zoneToRegion.setSize(cellZones.size(), -1);
1342 // Get current per cell zoneID
1343 labelList zoneID(mesh.nCells(), -1);
1344 labelList neiZoneID(mesh.nFaces()-mesh.nInternalFaces());
1345 getZoneID(mesh, cellZones, zoneID, neiZoneID);
1347 // Sizes per cellzone
1348 labelList zoneSizes(cellZones.size(), 0);
1350 List<wordList> zoneNames(Pstream::nProcs());
1351 zoneNames[Pstream::myProcNo()] = cellZones.names();
1352 Pstream::gatherList(zoneNames);
1353 Pstream::scatterList(zoneNames);
1355 forAll(zoneNames, procI)
1357 if (zoneNames[procI] != zoneNames[0])
1359 FatalErrorIn("matchRegions(..)")
1360 << "cellZones not synchronised across processors." << endl
1361 << "Master has cellZones " << zoneNames[0] << endl
1362 << "Processor " << procI
1363 << " has cellZones " << zoneNames[procI]
1364 << exit(FatalError);
1368 forAll(cellZones, zoneI)
1370 zoneSizes[zoneI] = returnReduce
1372 cellZones[zoneI].size(),
1379 if (sloppyCellZones)
1381 Info<< "Trying to match regions to existing cell zones;"
1382 << " region can be subset of cell zone." << nl << endl;
1384 forAll(cellZones, zoneI)
1386 label regionI = findCorrespondingRegion
1392 label(0.5*zoneSizes[zoneI]) // minimum overlap
1397 Info<< "Sloppily matched region " << regionI
1398 //<< " size " << regionSizes[regionI]
1399 << " to zone " << zoneI << " size " << zoneSizes[zoneI]
1401 zoneToRegion[zoneI] = regionI;
1402 regionToZone[regionI] = zoneI;
1403 regionNames[regionI] = cellZones[zoneI].name();
1409 Info<< "Trying to match regions to existing cell zones." << nl << endl;
1411 forAll(cellZones, zoneI)
1413 label regionI = findCorrespondingRegion
1419 1 // minimum overlap
1424 zoneToRegion[zoneI] = regionI;
1425 regionToZone[regionI] = zoneI;
1426 regionNames[regionI] = cellZones[zoneI].name();
1430 // Allocate region names for unmatched regions.
1431 forAll(regionToZone, regionI)
1433 if (regionToZone[regionI] == -1)
1435 regionNames[regionI] = "domain" + Foam::name(regionI);
1441 void writeCellToRegion(const fvMesh& mesh, const labelList& cellRegion)
1443 // Write to manual decomposition option
1445 labelIOList cellToRegion
1450 mesh.facesInstance(),
1458 cellToRegion.write();
1460 Info<< "Writing region per cell file (for manual decomposition) to "
1461 << cellToRegion.objectPath() << nl << endl;
1463 // Write for postprocessing
1465 volScalarField cellToRegion
1470 mesh.time().timeName(),
1477 dimensionedScalar("zero", dimless, 0),
1478 zeroGradientFvPatchScalarField::typeName
1480 forAll(cellRegion, cellI)
1482 cellToRegion[cellI] = cellRegion[cellI];
1484 cellToRegion.write();
1486 Info<< "Writing region per cell as volScalarField to "
1487 << cellToRegion.objectPath() << nl << endl;
1495 int main(int argc, char *argv[])
1497 argList::validOptions.insert("cellZones", "");
1498 argList::validOptions.insert("cellZonesOnly", "");
1499 argList::validOptions.insert("cellZonesFileOnly", "cellZonesName");
1500 argList::validOptions.insert("blockedFaces", "faceSet");
1501 argList::validOptions.insert("makeCellZones", "");
1502 argList::validOptions.insert("largestOnly", "");
1503 argList::validOptions.insert("insidePoint", "point");
1504 argList::validOptions.insert("overwrite", "");
1505 argList::validOptions.insert("detectOnly", "");
1506 argList::validOptions.insert("sloppyCellZones", "");
1508 # include "setRootCase.H"
1509 # include "createTime.H"
1510 runTime.functionObjects().off();
1511 # include "createMesh.H"
1512 const word oldInstance = mesh.pointsInstance();
1514 word blockedFacesName;
1515 if (args.optionFound("blockedFaces"))
1517 blockedFacesName = args.option("blockedFaces");
1518 Info<< "Reading blocked internal faces from faceSet "
1519 << blockedFacesName << nl << endl;
1522 bool makeCellZones = args.optionFound("makeCellZones");
1523 bool largestOnly = args.optionFound("largestOnly");
1524 bool insidePoint = args.optionFound("insidePoint");
1525 bool useCellZones = args.optionFound("cellZones");
1526 bool useCellZonesOnly = args.optionFound("cellZonesOnly");
1527 bool useCellZonesFile = args.optionFound("cellZonesFileOnly");
1528 bool overwrite = args.optionFound("overwrite");
1529 bool detectOnly = args.optionFound("detectOnly");
1530 bool sloppyCellZones = args.optionFound("sloppyCellZones");
1534 (useCellZonesOnly || useCellZonesFile)
1536 blockedFacesName != word::null
1541 FatalErrorIn(args.executable())
1542 << "You cannot specify both -cellZonesOnly or -cellZonesFileOnly"
1543 << " (which specify complete split)"
1544 << " in combination with -blockedFaces or -cellZones"
1545 << " (which imply a split based on topology)"
1546 << exit(FatalError);
1551 if (insidePoint && largestOnly)
1553 FatalErrorIn(args.executable())
1554 << "You cannot specify both -largestOnly"
1555 << " (keep region with most cells)"
1556 << " and -insidePoint (keep region containing point)"
1557 << exit(FatalError);
1561 const cellZoneMesh& cellZones = mesh.cellZones();
1564 labelList zoneID(mesh.nCells(), -1);
1565 // Neighbour zoneID.
1566 labelList neiZoneID(mesh.nFaces()-mesh.nInternalFaces());
1567 getZoneID(mesh, cellZones, zoneID, neiZoneID);
1571 // Determine per cell the region it belongs to
1572 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1574 // cellRegion is the labelList with the region per cell.
1575 labelList cellRegion;
1577 labelList regionToZone;
1579 wordList regionNames;
1581 labelList zoneToRegion;
1583 label nCellRegions = 0;
1584 if (useCellZonesOnly)
1586 Info<< "Using current cellZones to split mesh into regions."
1587 << " This requires all"
1588 << " cells to be in one and only one cellZone." << nl << endl;
1590 label unzonedCellI = findIndex(zoneID, -1);
1591 if (unzonedCellI != -1)
1593 FatalErrorIn(args.executable())
1594 << "For the cellZonesOnly option all cells "
1595 << "have to be in a cellZone." << endl
1596 << "Cell " << unzonedCellI
1597 << " at" << mesh.cellCentres()[unzonedCellI]
1598 << " is not in a cellZone. There might be more unzoned cells."
1599 << exit(FatalError);
1601 cellRegion = zoneID;
1602 nCellRegions = gMax(cellRegion)+1;
1603 regionToZone.setSize(nCellRegions);
1604 regionNames.setSize(nCellRegions);
1605 zoneToRegion.setSize(cellZones.size(), -1);
1606 for (label regionI = 0; regionI < nCellRegions; regionI++)
1608 regionToZone[regionI] = regionI;
1609 zoneToRegion[regionI] = regionI;
1610 regionNames[regionI] = cellZones[regionI].name();
1613 else if (useCellZonesFile)
1615 const word zoneFile = args.option("cellZonesFileOnly");
1616 Info<< "Reading split from cellZones file " << zoneFile << endl
1617 << "This requires all"
1618 << " cells to be in one and only one cellZone." << nl << endl;
1620 cellZoneMesh newCellZones
1625 mesh.facesInstance(),
1626 polyMesh::meshSubDir,
1628 IOobject::MUST_READ,
1635 labelList newZoneID(mesh.nCells(), -1);
1636 labelList newNeiZoneID(mesh.nFaces()-mesh.nInternalFaces());
1637 getZoneID(mesh, newCellZones, newZoneID, newNeiZoneID);
1639 label unzonedCellI = findIndex(newZoneID, -1);
1640 if (unzonedCellI != -1)
1642 FatalErrorIn(args.executable())
1643 << "For the cellZonesFileOnly option all cells "
1644 << "have to be in a cellZone." << endl
1645 << "Cell " << unzonedCellI
1646 << " at" << mesh.cellCentres()[unzonedCellI]
1647 << " is not in a cellZone. There might be more unzoned cells."
1648 << exit(FatalError);
1650 cellRegion = newZoneID;
1651 nCellRegions = gMax(cellRegion)+1;
1652 zoneToRegion.setSize(newCellZones.size(), -1);
1653 regionToZone.setSize(nCellRegions);
1654 regionNames.setSize(nCellRegions);
1655 for (label regionI = 0; regionI < nCellRegions; regionI++)
1657 regionToZone[regionI] = regionI;
1658 zoneToRegion[regionI] = regionI;
1659 regionNames[regionI] = newCellZones[regionI].name();
1664 // Determine connected regions
1665 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
1667 // Mark additional faces that are blocked
1668 boolList blockedFace;
1670 // Read from faceSet
1671 if (blockedFacesName.size())
1673 faceSet blockedFaceSet(mesh, blockedFacesName);
1675 << returnReduce(blockedFaceSet.size(), sumOp<label>())
1676 << " blocked faces from set " << blockedFacesName << nl << endl;
1678 blockedFace.setSize(mesh.nFaces(), false);
1680 forAllConstIter(faceSet, blockedFaceSet, iter)
1682 blockedFace[iter.key()] = true;
1686 // Imply from differing cellZones
1689 blockedFace.setSize(mesh.nFaces(), false);
1691 for (label faceI = 0; faceI < mesh.nInternalFaces(); faceI++)
1693 label own = mesh.faceOwner()[faceI];
1694 label nei = mesh.faceNeighbour()[faceI];
1696 if (zoneID[own] != zoneID[nei])
1698 blockedFace[faceI] = true;
1702 // Different cellZones on either side of processor patch.
1703 forAll(neiZoneID, i)
1705 label faceI = i+mesh.nInternalFaces();
1707 if (zoneID[mesh.faceOwner()[faceI]] != neiZoneID[i])
1709 blockedFace[faceI] = true;
1714 // Do a topological walk to determine regions
1715 regionSplit regions(mesh, blockedFace);
1716 nCellRegions = regions.nRegions();
1717 cellRegion.transfer(regions);
1719 // Make up region names. If possible match them to existing zones.
1733 Info<< endl << "Number of regions:" << nCellRegions << nl << endl;
1736 // Write decomposition to file
1737 writeCellToRegion(mesh, cellRegion);
1744 labelList regionSizes(nCellRegions, 0);
1746 forAll(cellRegion, cellI)
1748 regionSizes[cellRegion[cellI]]++;
1750 forAll(regionSizes, regionI)
1752 reduce(regionSizes[regionI], sumOp<label>());
1755 Info<< "Region\tCells" << nl
1756 << "------\t-----" << endl;
1758 forAll(regionSizes, regionI)
1760 Info<< regionI << '\t' << regionSizes[regionI] << nl;
1766 // Print region to zone
1767 Info<< "Region\tZone\tName" << nl
1768 << "------\t----\t----" << endl;
1769 forAll(regionToZone, regionI)
1771 Info<< regionI << '\t' << regionToZone[regionI] << '\t'
1772 << regionNames[regionI] << nl;
1778 // Since we're going to mess with patches make sure all non-processor ones
1779 // are on all processors.
1780 mesh.boundaryMesh().checkParallelSync(true);
1783 // Sizes of interface between regions. From pair of regions to number of
1785 edgeList interfaces;
1786 EdgeMap<label> interfaceSizes;
1791 true, // sum in parallel?
1797 Info<< "Sizes inbetween regions:" << nl << nl
1798 << "Region\tRegion\tFaces" << nl
1799 << "------\t------\t-----" << endl;
1801 forAll(interfaces, interI)
1803 const edge& e = interfaces[interI];
1805 Info<< e[0] << '\t' << e[1] << '\t' << interfaceSizes[e] << nl;
1816 // Read objects in time directory
1817 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1819 IOobjectList objects(mesh, runTime.timeName());
1823 PtrList<volScalarField> vsFlds;
1824 ReadFields(mesh, objects, vsFlds);
1826 PtrList<volVectorField> vvFlds;
1827 ReadFields(mesh, objects, vvFlds);
1829 PtrList<volSphericalTensorField> vstFlds;
1830 ReadFields(mesh, objects, vstFlds);
1832 PtrList<volSymmTensorField> vsymtFlds;
1833 ReadFields(mesh, objects, vsymtFlds);
1835 PtrList<volTensorField> vtFlds;
1836 ReadFields(mesh, objects, vtFlds);
1838 // Read surface fields.
1840 PtrList<surfaceScalarField> ssFlds;
1841 ReadFields(mesh, objects, ssFlds);
1843 PtrList<surfaceVectorField> svFlds;
1844 ReadFields(mesh, objects, svFlds);
1846 PtrList<surfaceSphericalTensorField> sstFlds;
1847 ReadFields(mesh, objects, sstFlds);
1849 PtrList<surfaceSymmTensorField> ssymtFlds;
1850 ReadFields(mesh, objects, ssymtFlds);
1852 PtrList<surfaceTensorField> stFlds;
1853 ReadFields(mesh, objects, stFlds);
1858 // Remove any demand-driven fields ('S', 'V' etc)
1862 if (nCellRegions == 1)
1864 Info<< "Only one region. Doing nothing." << endl;
1866 else if (makeCellZones)
1868 Info<< "Putting cells into cellZones instead of splitting mesh."
1871 // Check if region overlaps with existing zone. If so keep.
1873 for (label regionI = 0; regionI < nCellRegions; regionI++)
1875 label zoneI = regionToZone[regionI];
1879 Info<< " Region " << regionI << " : corresponds to existing"
1881 << zoneI << ' ' << cellZones[zoneI].name() << endl;
1885 // Create new cellZone.
1886 labelList regionCells = findIndices(cellRegion, regionI);
1888 word zoneName = "region" + Foam::name(regionI);
1890 zoneI = cellZones.findZoneID(zoneName);
1894 zoneI = cellZones.size();
1895 mesh.cellZones().setSize(zoneI+1);
1896 mesh.cellZones().set
1902 regionCells, //addressing
1904 cellZones //cellZoneMesh
1910 mesh.cellZones()[zoneI].clearAddressing();
1911 mesh.cellZones()[zoneI] = regionCells;
1913 Info<< " Region " << regionI << " : created new cellZone "
1914 << zoneI << ' ' << cellZones[zoneI].name() << endl;
1917 mesh.cellZones().writeOpt() = IOobject::AUTO_WRITE;
1922 mesh.setInstance(runTime.timeName());
1926 mesh.setInstance(oldInstance);
1929 Info<< "Writing cellZones as new mesh to time " << runTime.timeName()
1935 // Write cellSets for convenience
1936 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1938 Info<< "Writing cellSets corresponding to cellZones." << nl << endl;
1940 forAll(cellZones, zoneI)
1942 const cellZone& cz = cellZones[zoneI];
1944 cellSet(mesh, cz.name(), cz).write();
1949 // Add patches for interfaces
1950 // ~~~~~~~~~~~~~~~~~~~~~~~~~~
1952 // Add all possible patches. Empty ones get filtered later on.
1953 Info<< nl << "Adding patches" << nl << endl;
1955 EdgeMap<label> interfaceToPatch
1980 point insidePoint(args.optionLookup("insidePoint")());
1984 label cellI = mesh.findCell(insidePoint);
1986 Info<< nl << "Found point " << insidePoint << " in cell " << cellI
1991 regionI = cellRegion[cellI];
1994 reduce(regionI, maxOp<label>());
1997 << "Subsetting region " << regionI
1998 << " containing point " << insidePoint << endl;
2002 FatalErrorIn(args.executable())
2003 << "Point " << insidePoint
2004 << " is not inside the mesh." << nl
2005 << "Bounding box of the mesh:" << mesh.bounds()
2006 << exit(FatalError);
2009 createAndWriteRegion
2016 (overwrite ? oldInstance : runTime.timeName())
2019 else if (largestOnly)
2021 label regionI = findMax(regionSizes);
2024 << "Subsetting region " << regionI
2025 << " of size " << regionSizes[regionI] << endl;
2027 createAndWriteRegion
2034 (overwrite ? oldInstance : runTime.timeName())
2040 for (label regionI = 0; regionI < nCellRegions; regionI++)
2043 << "Region " << regionI << nl
2044 << "-------- " << endl;
2046 createAndWriteRegion
2053 (overwrite ? oldInstance : runTime.timeName())
2059 Info<< "End\n" << endl;
2065 // ************************************************************************* //