1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | foam-extend: Open Source CFD
4 \\ / O peration | Version: 3.2
5 \\ / A nd | Web: http://www.foam-extend.org
6 \\/ M anipulation | For copyright notice see file Copyright
7 -------------------------------------------------------------------------------
9 This file is part of foam-extend.
11 foam-extend 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 3 of the License, or (at your
14 option) any later version.
16 foam-extend is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with foam-extend. 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 in between differing cellZones (-cellZones)
37 - volScalarField with regions as different scalars (-detectOnly) or
38 - mesh with multiple regions or
39 - mesh with cells put into cellZones (-makeCellZones)
42 - cellZonesOnly does not do a walk and uses the cellZones only. Use
43 this if you don't mind having disconnected domains in a single region.
44 This option requires all cells to be in one (and one only) cellZone.
46 - cellZonesFileOnly behaves like -cellZonesOnly but reads the cellZones
47 from the specified file. This allows one to explicitly specify the region
48 distribution and still have multiple cellZones per region.
50 - useCellZonesOnly does not do a walk and uses the cellZones only. Use
51 this if you don't mind having disconnected domains in a single region.
52 This option requires all cells to be in one (and one only) cellZone.
55 - Should work in parallel.
56 cellZones can differ on either side of processor boundaries in which case
57 the faces get moved from processor patch to directMapped patch. Not
60 - If a cell zone gets split into more than one region it can detect
61 the largest matching region (-sloppyCellZones). This will accept any
62 region that covers more than 50% of the zone. It has to be a subset
63 so cannot have any cells in any other zone.
65 - writes maps like decomposePar back to original mesh:
66 - pointRegionAddressing : for every point in this region the point in
68 - cellRegionAddressing : ,, cell ,, cell ,,
69 - faceRegionAddressing : ,, face ,, face in
70 the original mesh + 'turning index'. For a face in the same orientation
71 this is the original facelabel+1, for a turned face
72 this is -facelabel - 1
74 \*---------------------------------------------------------------------------*/
76 #include "SortableList.H"
78 #include "regionSplit.H"
79 #include "fvMeshSubset.H"
80 #include "IOobjectList.H"
81 #include "volFields.H"
84 #include "directTopoChange.H"
85 #include "mapPolyMesh.H"
86 #include "removeCells.H"
88 #include "syncTools.H"
89 #include "ReadFields.H"
90 #include "directMappedWallPolyPatch.H"
91 #include "zeroGradientFvPatchFields.H"
95 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
97 template<class GeoField>
98 void addPatchFields(fvMesh& mesh, const word& patchFieldType)
100 HashTable<const GeoField*> flds
102 mesh.objectRegistry::lookupClass<GeoField>()
107 typename HashTable<const GeoField*>::const_iterator iter =
113 const GeoField& fld = *iter();
115 typename GeoField::GeometricBoundaryField& bfld =
116 const_cast<typename GeoField::GeometricBoundaryField&>
121 label sz = bfld.size();
122 bfld.setSize(sz + 1);
126 GeoField::PatchFieldType::New
130 fld.dimensionedInternalField()
137 // Remove last patch field
138 template<class GeoField>
139 void trimPatchFields(fvMesh& mesh, const label nPatches)
141 HashTable<const GeoField*> flds
143 mesh.objectRegistry::lookupClass<GeoField>()
148 typename HashTable<const GeoField*>::const_iterator iter =
154 const GeoField& fld = *iter();
156 const_cast<typename GeoField::GeometricBoundaryField&>
164 // Reorder patch field
165 template<class GeoField>
166 void reorderPatchFields(fvMesh& mesh, const labelList& oldToNew)
168 HashTable<const GeoField*> flds
170 mesh.objectRegistry::lookupClass<GeoField>()
175 typename HashTable<const GeoField*>::const_iterator iter =
181 const GeoField& fld = *iter();
183 typename GeoField::GeometricBoundaryField& bfld =
184 const_cast<typename GeoField::GeometricBoundaryField&>
189 bfld.reorder(oldToNew);
194 // Adds patch if not yet there. Returns patchID.
195 label addPatch(fvMesh& mesh, const polyPatch& patch)
197 polyBoundaryMesh& polyPatches =
198 const_cast<polyBoundaryMesh&>(mesh.boundaryMesh());
200 label patchI = polyPatches.findPatchID(patch.name());
204 if (polyPatches[patchI].type() == patch.type())
211 FatalErrorIn("addPatch(fvMesh&, const polyPatch*)")
212 << "Already have patch " << patch.name()
213 << " but of type " << patch.type()
219 label insertPatchI = polyPatches.size();
220 label startFaceI = mesh.nFaces();
222 forAll (polyPatches, patchI)
224 const polyPatch& pp = polyPatches[patchI];
226 if (isA<processorPolyPatch>(pp))
228 insertPatchI = patchI;
229 startFaceI = pp.start();
235 // Below is all quite a hack. Feel free to change once there is a better
236 // mechanism to insert and reorder patches.
238 // Clear local fields and e.g. polyMesh parallelInfo.
241 label sz = polyPatches.size();
243 fvBoundaryMesh& fvPatches = const_cast<fvBoundaryMesh&>(mesh.boundary());
245 Info<< "Inserting patch " << patch.name() << " to slot " << insertPatchI
246 << " out of " << polyPatches.size() << endl;
248 // Add polyPatch at the end
249 polyPatches.setSize(sz + 1);
256 insertPatchI, // index
261 fvPatches.setSize(sz + 1);
267 polyPatches[sz], // point to newly added polyPatch
272 addPatchFields<volScalarField>
275 calculatedFvPatchField<scalar>::typeName
277 addPatchFields<volVectorField>
280 calculatedFvPatchField<vector>::typeName
282 addPatchFields<volSphericalTensorField>
285 calculatedFvPatchField<sphericalTensor>::typeName
287 addPatchFields<volSymmTensorField>
290 calculatedFvPatchField<symmTensor>::typeName
292 addPatchFields<volTensorField>
295 calculatedFvPatchField<tensor>::typeName
300 addPatchFields<surfaceScalarField>
303 calculatedFvPatchField<scalar>::typeName
305 addPatchFields<surfaceVectorField>
308 calculatedFvPatchField<vector>::typeName
310 addPatchFields<surfaceSphericalTensorField>
313 calculatedFvPatchField<sphericalTensor>::typeName
315 addPatchFields<surfaceSymmTensorField>
318 calculatedFvPatchField<symmTensor>::typeName
320 addPatchFields<surfaceTensorField>
323 calculatedFvPatchField<tensor>::typeName
326 // Create reordering list
327 // patches before insert position stay as is
328 labelList oldToNew(sz + 1);
329 for (label i = 0; i < insertPatchI; i++)
333 // patches after insert position move one up
334 for (label i = insertPatchI; i < sz; i++)
338 // appended patch gets moved to insert position
339 oldToNew[sz] = insertPatchI;
341 // Shuffle into place
342 polyPatches.reorder(oldToNew);
343 fvPatches.reorder(oldToNew);
345 reorderPatchFields<volScalarField>(mesh, oldToNew);
346 reorderPatchFields<volVectorField>(mesh, oldToNew);
347 reorderPatchFields<volSphericalTensorField>(mesh, oldToNew);
348 reorderPatchFields<volSymmTensorField>(mesh, oldToNew);
349 reorderPatchFields<volTensorField>(mesh, oldToNew);
350 reorderPatchFields<surfaceScalarField>(mesh, oldToNew);
351 reorderPatchFields<surfaceVectorField>(mesh, oldToNew);
352 reorderPatchFields<surfaceSphericalTensorField>(mesh, oldToNew);
353 reorderPatchFields<surfaceSymmTensorField>(mesh, oldToNew);
354 reorderPatchFields<surfaceTensorField>(mesh, oldToNew);
360 // Reorder and delete patches. Deleted. HJ, 22/May/2011
363 template<class GeoField>
367 const fvMesh& subMesh,
368 const labelList& cellMap,
369 const labelList& faceMap,
370 const labelList& patchMap
373 // Real patch map passed by argument
376 HashTable<const GeoField*> fields
378 mesh.objectRegistry::lookupClass<GeoField>()
380 forAllConstIter(typename HashTable<const GeoField*>, fields, iter)
382 const GeoField& fld = *iter();
384 Info<< "Mapping field " << fld.name() << endl;
386 tmp<GeoField> tSubFld
388 fvMeshSubset::meshToMesh
398 // Hack: set value to 0 for introduced patches (since don't
400 forAll (tSubFld().boundaryField(), patchI)
402 const fvPatchField<typename GeoField::value_type>& pfld =
403 tSubFld().boundaryField()[patchI];
407 isA<calculatedFvPatchField<typename GeoField::value_type> >
411 tSubFld().boundaryField()[patchI] ==
412 pTraits<typename GeoField::value_type>::zero;
417 GeoField* subFld = tSubFld.ptr();
418 subFld->rename(fld.name());
419 subFld->writeOpt() = IOobject::AUTO_WRITE;
425 template<class GeoField>
426 void subsetSurfaceFields
429 const fvMesh& subMesh,
430 const labelList& faceMap,
431 const labelList& patchMap
434 // Real patch map passed by argument
437 HashTable<const GeoField*> fields
439 mesh.objectRegistry::lookupClass<GeoField>()
442 forAllConstIter(typename HashTable<const GeoField*>, fields, iter)
444 const GeoField& fld = *iter();
446 Info<< "Mapping field " << fld.name() << endl;
448 tmp<GeoField> tSubFld
450 fvMeshSubset::meshToMesh
459 // Hack: set value to 0 for introduced patches (since don't
461 forAll (tSubFld().boundaryField(), patchI)
463 const fvsPatchField<typename GeoField::value_type>& pfld =
464 tSubFld().boundaryField()[patchI];
468 isA<calculatedFvsPatchField<typename GeoField::value_type> >
472 tSubFld().boundaryField()[patchI] ==
473 pTraits<typename GeoField::value_type>::zero;
478 GeoField* subFld = tSubFld.ptr();
479 subFld->rename(fld.name());
480 subFld->writeOpt() = IOobject::AUTO_WRITE;
486 // Select all cells not in the region
487 labelList getNonRegionCells(const labelList& cellRegion, const label regionI)
489 DynamicList<label> nonRegionCells(cellRegion.size());
490 forAll (cellRegion, cellI)
492 if (cellRegion[cellI] != regionI)
494 nonRegionCells.append(cellI);
497 return nonRegionCells.shrink();
501 // Get per region-region interface the sizes. If sumParallel sums sizes.
502 // Returns interfaces as straight list for looping in identical order.
503 void getInterfaceSizes
505 const polyMesh& mesh,
506 const labelList& cellRegion,
507 const bool sumParallel,
509 edgeList& interfaces,
510 EdgeMap<label>& interfaceSizes
517 forAll (mesh.faceNeighbour(), faceI)
519 label ownRegion = cellRegion[mesh.faceOwner()[faceI]];
520 label neiRegion = cellRegion[mesh.faceNeighbour()[faceI]];
522 if (ownRegion != neiRegion)
526 min(ownRegion, neiRegion),
527 max(ownRegion, neiRegion)
530 EdgeMap<label>::iterator iter = interfaceSizes.find(interface);
532 if (iter != interfaceSizes.end())
538 interfaceSizes.insert(interface, 1);
546 // Neighbour cellRegion.
547 labelList coupledRegion(mesh.nFaces()-mesh.nInternalFaces());
549 forAll (coupledRegion, i)
551 label cellI = mesh.faceOwner()[i+mesh.nInternalFaces()];
552 coupledRegion[i] = cellRegion[cellI];
554 syncTools::swapBoundaryFaceList(mesh, coupledRegion, false);
556 forAll (coupledRegion, i)
558 label faceI = i+mesh.nInternalFaces();
559 label ownRegion = cellRegion[mesh.faceOwner()[faceI]];
560 label neiRegion = coupledRegion[i];
562 if (ownRegion != neiRegion)
566 min(ownRegion, neiRegion),
567 max(ownRegion, neiRegion)
570 EdgeMap<label>::iterator iter = interfaceSizes.find(interface);
572 if (iter != interfaceSizes.end())
578 interfaceSizes.insert(interface, 1);
584 if (sumParallel && Pstream::parRun())
586 if (Pstream::master())
588 // Receive and add to my sizes
591 int slave = Pstream::firstSlave();
592 slave <= Pstream::lastSlave();
596 IPstream fromSlave(Pstream::blocking, slave);
598 EdgeMap<label> slaveSizes(fromSlave);
600 forAllConstIter(EdgeMap<label>, slaveSizes, slaveIter)
602 EdgeMap<label>::iterator masterIter =
603 interfaceSizes.find(slaveIter.key());
605 if (masterIter != interfaceSizes.end())
607 masterIter() += slaveIter();
611 interfaceSizes.insert(slaveIter.key(), slaveIter());
619 int slave = Pstream::firstSlave();
620 slave <= Pstream::lastSlave();
624 // Receive the edges using shared points from the slave.
625 OPstream toSlave(Pstream::blocking, slave);
626 toSlave << interfaceSizes;
633 OPstream toMaster(Pstream::blocking, Pstream::masterNo());
634 toMaster << interfaceSizes;
636 // Receive from master
638 IPstream fromMaster(Pstream::blocking, Pstream::masterNo());
639 fromMaster >> interfaceSizes;
644 // Make sure all processors have interfaces in same order
645 interfaces = interfaceSizes.toc();
648 Pstream::scatter(interfaces);
653 // Create mesh for region. Mesh pointer not passed in: instead, mesh is
654 // written out (as polyMesh) and read in (as fvMesh)
655 // to allow for field mapping.
657 autoPtr<mapPolyMesh> createRegionMesh
659 const labelList& cellRegion,
660 const EdgeMap<label>& interfaceToPatch,
663 const word& regionName
666 // Neighbour cellRegion.
667 labelList coupledRegion(mesh.nFaces() - mesh.nInternalFaces());
669 forAll (coupledRegion, i)
671 label cellI = mesh.faceOwner()[i+mesh.nInternalFaces()];
672 coupledRegion[i] = cellRegion[cellI];
674 syncTools::swapBoundaryFaceList(mesh, coupledRegion, false);
676 // Topology change container. Start off from existing mesh.
677 directTopoChange meshMod(mesh);
679 // Cell remover engine
680 removeCells cellRemover(mesh);
682 // Select all but region cells
683 labelList cellsToRemove(getNonRegionCells(cellRegion, regionI));
685 // Find out which faces will get exposed. Note that this
686 // gets faces in mesh face order. So both regions will get same
687 // face in same order (important!)
688 labelList exposedFaces = cellRemover.getExposedFaces(cellsToRemove);
690 labelList exposedPatchIDs(exposedFaces.size());
691 forAll (exposedFaces, i)
693 label faceI = exposedFaces[i];
695 label ownRegion = cellRegion[mesh.faceOwner()[faceI]];
696 label neiRegion = -1;
698 if (mesh.isInternalFace(faceI))
700 neiRegion = cellRegion[mesh.faceNeighbour()[faceI]];
704 neiRegion = coupledRegion[faceI-mesh.nInternalFaces()];
707 label otherRegion = -1;
709 if (ownRegion == regionI && neiRegion != regionI)
711 otherRegion = neiRegion;
713 else if (ownRegion != regionI && neiRegion == regionI)
715 otherRegion = ownRegion;
719 FatalErrorIn("createRegionMesh(..)")
720 << "Exposed face:" << faceI
721 << " fc:" << mesh.faceCentres()[faceI]
722 << " has owner region " << ownRegion
723 << " and neighbour region " << neiRegion
724 << " when handling region:" << regionI
728 if (otherRegion != -1)
730 edge interface(regionI, otherRegion);
733 if (regionI < otherRegion)
735 exposedPatchIDs[i] = interfaceToPatch[interface];
739 exposedPatchIDs[i] = interfaceToPatch[interface] + 1;
745 cellRemover.setRefinement
753 autoPtr<polyMesh> newMesh;
755 autoPtr<mapPolyMesh> map = meshMod.makeMesh
761 mesh.time().timeName(),
769 polyBoundaryMesh& newPatches =
770 const_cast<polyBoundaryMesh&>(newMesh().boundaryMesh());
771 newPatches.checkParallelSync(true);
773 // Delete empty patches
774 // ~~~~~~~~~~~~~~~~~~~~
776 // Create reordering list to move patches-to-be-deleted to end
777 labelList oldToNew(newPatches.size(), -1);
780 Info<< "Deleting empty patches" << endl;
782 // Assumes all non-proc boundaries are on all processors!
783 forAll (newPatches, patchI)
785 const polyPatch& pp = newPatches[patchI];
787 if (!isA<processorPolyPatch>(pp))
789 if (returnReduce(pp.size(), sumOp<label>()) > 0)
791 oldToNew[patchI] = newI++;
796 // Same for processor patches (but need no reduction)
797 forAll (newPatches, patchI)
799 const polyPatch& pp = newPatches[patchI];
801 if (isA<processorPolyPatch>(pp) && pp.size())
803 oldToNew[patchI] = newI++;
807 const label nNewPatches = newI;
809 // Move all deleteable patches to the end
810 forAll (oldToNew, patchI)
812 if (oldToNew[patchI] == -1)
814 oldToNew[patchI] = newI++;
818 // Reorder polyPatches and trim empty patches from the end
820 newPatches.reorder(oldToNew);
821 newPatches.setSize(nNewPatches);
824 Info<< "Writing new mesh" << endl;
827 // Write addressing files like decomposePar
828 Info<< "Writing addressing to base mesh" << endl;
830 labelIOList pointRegionAddressing
834 "pointRegionAddressing",
835 newMesh().facesInstance(),
836 newMesh().meshSubDir,
845 Info<< "Writing map " << pointRegionAddressing.name()
846 << " from region" << regionI
847 << " points back to base mesh." << endl;
848 pointRegionAddressing.write();
850 labelIOList faceRegionAddressing
854 "faceRegionAddressing",
855 newMesh().facesInstance(),
856 newMesh().meshSubDir,
865 forAll (faceRegionAddressing, faceI)
867 // face + turning index. (see decomposePar)
868 // Is the face pointing in the same direction?
869 label oldFaceI = map().faceMap()[faceI];
873 map().cellMap()[newMesh().faceOwner()[faceI]]
874 == mesh.faceOwner()[oldFaceI]
877 faceRegionAddressing[faceI] = oldFaceI + 1;
881 faceRegionAddressing[faceI] = -(oldFaceI + 1);
885 Info<< "Writing map " << faceRegionAddressing.name()
886 << " from region" << regionI
887 << " faces back to base mesh." << endl;
888 faceRegionAddressing.write();
890 labelIOList cellRegionAddressing
894 "cellRegionAddressing",
895 newMesh().facesInstance(),
896 newMesh().meshSubDir,
905 Info<< "Writing map " << cellRegionAddressing.name()
906 << " from region" << regionI
907 << " cells back to base mesh." << endl;
908 cellRegionAddressing.write();
910 // Calculate and write boundary addressing
912 const polyBoundaryMesh& oldPatches = mesh.boundaryMesh();
913 const polyBoundaryMesh& newPatches = newMesh().boundaryMesh();
915 labelIOList boundaryRegionAddressing
919 "boundaryRegionAddressing",
920 newMesh().facesInstance(),
921 newMesh().meshSubDir,
929 newMesh().boundaryMesh().size(),
934 Info<<"Number of new patches: " << nNewPatches <<endl;
936 forAll (boundaryRegionAddressing, patchID)
938 const word& curPatchName = newPatches[patchID].name();
940 forAll (oldPatches, oldPatchI)
942 if (oldPatches[oldPatchI].name() == curPatchName)
944 boundaryRegionAddressing[patchID] = oldPatchI;
949 Info<< "Writing map " << boundaryRegionAddressing.name()
950 << " from region" << regionI
951 << " faces back to base mesh." << endl;
952 boundaryRegionAddressing.write();
959 void createAndWriteRegion
962 const labelList& cellRegion,
963 const wordList& regionNames,
964 const EdgeMap<label>& interfaceToPatch,
966 const word& newMeshInstance
969 Info<< "Creating mesh for region " << regionI
970 << ' ' << regionNames[regionI] << endl;
972 // Create region mesh will write the mesh
974 autoPtr<mapPolyMesh> map = createRegionMesh
983 // Read in mesh as fvMesh for mapping. HJ, 19/May/2011
988 regionNames[regionI],
996 // Read in patch map. HJ, 22/May/2011
997 labelIOList boundaryRegionAddressing
1001 "boundaryRegionAddressing",
1002 newMesh.facesInstance(),
1005 IOobject::MUST_READ,
1010 Info<< "Mapping fields" << endl;
1012 // Map existing fields: not needed
1014 // Add subset fields
1015 // Note: real patch map provided in place of identity map hack
1017 subsetVolFields<volScalarField>
1023 boundaryRegionAddressing
1025 subsetVolFields<volVectorField>
1031 boundaryRegionAddressing
1033 subsetVolFields<volSphericalTensorField>
1039 boundaryRegionAddressing
1041 subsetVolFields<volSymmTensorField>
1047 boundaryRegionAddressing
1049 subsetVolFields<volTensorField>
1055 boundaryRegionAddressing
1058 subsetSurfaceFields<surfaceScalarField>
1063 boundaryRegionAddressing
1065 subsetSurfaceFields<surfaceVectorField>
1070 boundaryRegionAddressing
1072 subsetSurfaceFields<surfaceSphericalTensorField>
1077 boundaryRegionAddressing
1079 subsetSurfaceFields<surfaceSymmTensorField>
1084 boundaryRegionAddressing
1086 subsetSurfaceFields<surfaceTensorField>
1091 boundaryRegionAddressing
1094 // Moved map writing into createRegionMesh. HJ, 20/May/2011
1095 newMesh.objectRegistry::write();
1099 // Create for every region-region interface with non-zero size two patches.
1100 // First one is for minimum region to maximum region.
1101 // Note that patches get created in same order on all processors (if parallel)
1102 // since looping over synchronised 'interfaces'.
1103 EdgeMap<label> addRegionPatches
1106 const labelList& cellRegion,
1107 const label nCellRegions,
1108 const edgeList& interfaces,
1109 const EdgeMap<label>& interfaceSizes,
1110 const wordList& regionNames
1113 // Check that all patches are present in same order.
1114 mesh.boundaryMesh().checkParallelSync(true);
1116 Info<< nl << "Adding patches" << nl << endl;
1118 EdgeMap<label> interfaceToPatch(nCellRegions);
1120 forAll (interfaces, interI)
1122 const edge& e = interfaces[interI];
1124 if (interfaceSizes[e] > 0)
1126 const word inter1 = regionNames[e[0]] + "_to_" + regionNames[e[1]];
1127 const word inter2 = regionNames[e[1]] + "_to_" + regionNames[e[0]];
1129 directMappedWallPolyPatch patch1
1135 regionNames[e[1]], // sampleRegion
1136 directMappedPatchBase::NEARESTPATCHFACE,
1137 inter2, // samplePatch
1138 point::zero, // offset
1142 label patchI = addPatch(mesh, patch1);
1144 directMappedWallPolyPatch patch2
1150 regionNames[e[0]], // sampleRegion
1151 directMappedPatchBase::NEARESTPATCHFACE,
1153 point::zero, // offset
1156 addPatch(mesh, patch2);
1158 Info<< "For interface between region " << e[0]
1159 << " and " << e[1] << " added patch " << patchI
1160 << " " << mesh.boundaryMesh()[patchI].name()
1163 interfaceToPatch.insert(e, patchI);
1167 return interfaceToPatch;
1171 // Find region that covers most of cell zone
1172 label findCorrespondingRegion
1174 const labelList& existingZoneID, // per cell the (unique) zoneID
1175 const labelList& cellRegion,
1176 const label nCellRegions,
1178 const label minOverlapSize
1181 // Per region the number of cells in zoneI
1182 labelList cellsInZone(nCellRegions, 0);
1184 forAll (cellRegion, cellI)
1186 if (existingZoneID[cellI] == zoneI)
1188 cellsInZone[cellRegion[cellI]]++;
1192 Pstream::listCombineGather(cellsInZone, plusEqOp<label>());
1193 Pstream::listCombineScatter(cellsInZone);
1195 // Pick region with largest overlap of zoneI
1196 label regionI = findMax(cellsInZone);
1199 if (cellsInZone[regionI] < minOverlapSize)
1201 // Region covers too little of zone. Not good enough.
1206 // Check that region contains no cells that aren't in cellZone.
1207 forAll (cellRegion, cellI)
1209 if (cellRegion[cellI] == regionI && existingZoneID[cellI] != zoneI)
1211 // cellI in regionI but not in zoneI
1216 // If one in error, all should be in error. Note that branch gets taken
1218 reduce(regionI, minOp<label>());
1225 // Get zone per cell
1226 // - non-unique zoning
1230 const polyMesh& mesh,
1231 const cellZoneMesh& cellZones,
1233 labelList& neiZoneID
1237 zoneID.setSize(mesh.nCells());
1240 forAll (cellZones, zoneI)
1242 const cellZone& cz = cellZones[zoneI];
1246 label cellI = cz[i];
1247 if (zoneID[cellI] == -1)
1249 zoneID[cellI] = zoneI;
1253 FatalErrorIn("getZoneID(..)")
1254 << "Cell " << cellI << " with cell centre "
1255 << mesh.cellCentres()[cellI]
1256 << " is multiple zones. This is not allowed." << endl
1257 << "It is in zone " << cellZones[zoneID[cellI]].name()
1258 << " and in zone " << cellZones[zoneI].name()
1259 << exit(FatalError);
1264 // Neighbour zoneID.
1265 neiZoneID.setSize(mesh.nFaces()-mesh.nInternalFaces());
1267 forAll (neiZoneID, i)
1269 neiZoneID[i] = zoneID[mesh.faceOwner()[i+mesh.nInternalFaces()]];
1271 syncTools::swapBoundaryFaceList(mesh, neiZoneID, false);
1277 const bool sloppyCellZones,
1278 const polyMesh& mesh,
1280 const label nCellRegions,
1281 const labelList& cellRegion,
1283 labelList& regionToZone,
1284 wordList& regionNames,
1285 labelList& zoneToRegion
1288 const cellZoneMesh& cellZones = mesh.cellZones();
1290 regionToZone.setSize(nCellRegions, -1);
1291 regionNames.setSize(nCellRegions);
1292 zoneToRegion.setSize(cellZones.size(), -1);
1294 // Get current per cell zoneID
1295 labelList zoneID(mesh.nCells(), -1);
1296 labelList neiZoneID(mesh.nFaces()-mesh.nInternalFaces());
1297 getZoneID(mesh, cellZones, zoneID, neiZoneID);
1299 // Sizes per cellzone
1300 labelList zoneSizes(cellZones.size(), 0);
1302 List<wordList> zoneNames(Pstream::nProcs());
1303 zoneNames[Pstream::myProcNo()] = cellZones.names();
1304 Pstream::gatherList(zoneNames);
1305 Pstream::scatterList(zoneNames);
1307 forAll (zoneNames, procI)
1309 if (zoneNames[procI] != zoneNames[0])
1311 FatalErrorIn("matchRegions(..)")
1312 << "cellZones not synchronised across processors." << endl
1313 << "Master has cellZones " << zoneNames[0] << endl
1314 << "Processor " << procI
1315 << " has cellZones " << zoneNames[procI]
1316 << exit(FatalError);
1320 forAll (cellZones, zoneI)
1322 zoneSizes[zoneI] = returnReduce
1324 cellZones[zoneI].size(),
1331 if (sloppyCellZones)
1333 Info<< "Trying to match regions to existing cell zones;"
1334 << " region can be subset of cell zone." << nl << endl;
1336 forAll (cellZones, zoneI)
1338 label regionI = findCorrespondingRegion
1344 label(0.5*zoneSizes[zoneI]) // minimum overlap
1349 Info<< "Sloppily matched region " << regionI
1350 //<< " size " << regionSizes[regionI]
1351 << " to zone " << zoneI << " size " << zoneSizes[zoneI]
1353 zoneToRegion[zoneI] = regionI;
1354 regionToZone[regionI] = zoneI;
1355 regionNames[regionI] = cellZones[zoneI].name();
1361 Info<< "Trying to match regions to existing cell zones." << nl << endl;
1363 forAll (cellZones, zoneI)
1365 label regionI = findCorrespondingRegion
1371 1 // minimum overlap
1376 zoneToRegion[zoneI] = regionI;
1377 regionToZone[regionI] = zoneI;
1378 regionNames[regionI] = cellZones[zoneI].name();
1382 // Allocate region names for unmatched regions.
1383 forAll (regionToZone, regionI)
1385 if (regionToZone[regionI] == -1)
1387 regionNames[regionI] = "domain" + Foam::name(regionI);
1393 void writeCellToRegion(const fvMesh& mesh, const labelList& cellRegion)
1395 // Write to manual decomposition option
1397 labelIOList cellToRegion
1402 mesh.facesInstance(),
1410 cellToRegion.write();
1412 Info<< "Writing region per cell file (for manual decomposition) to "
1413 << cellToRegion.objectPath() << nl << endl;
1416 // Write for postprocessing
1418 volScalarField cellToRegion
1423 mesh.time().timeName(),
1430 dimensionedScalar("zero", dimless, 0),
1431 zeroGradientFvPatchScalarField::typeName
1433 forAll (cellRegion, cellI)
1435 cellToRegion[cellI] = cellRegion[cellI];
1437 cellToRegion.write();
1439 Info<< "Writing region per cell as volScalarField to "
1440 << cellToRegion.objectPath() << nl << endl;
1448 int main(int argc, char *argv[])
1450 argList::validOptions.insert("cellZones", "");
1451 argList::validOptions.insert("cellZonesOnly", "");
1452 argList::validOptions.insert("cellZonesFileOnly", "cellZonesName");
1453 argList::validOptions.insert("blockedFaces", "faceSet");
1454 argList::validOptions.insert("makeCellZones", "");
1455 argList::validOptions.insert("largestOnly", "");
1456 argList::validOptions.insert("insidePoint", "point");
1457 argList::validOptions.insert("overwrite", "");
1458 argList::validOptions.insert("detectOnly", "");
1459 argList::validOptions.insert("sloppyCellZones", "");
1461 # include "setRootCase.H"
1462 # include "createTime.H"
1463 runTime.functionObjects().off();
1464 # include "createMesh.H"
1465 const word oldInstance = mesh.pointsInstance();
1467 word blockedFacesName;
1468 if (args.optionFound("blockedFaces"))
1470 blockedFacesName = args.option("blockedFaces");
1471 Info<< "Reading blocked internal faces from faceSet "
1472 << blockedFacesName << nl << endl;
1475 bool makeCellZones = args.optionFound("makeCellZones");
1476 bool largestOnly = args.optionFound("largestOnly");
1477 bool insidePoint = args.optionFound("insidePoint");
1478 bool useCellZones = args.optionFound("cellZones");
1479 bool useCellZonesOnly = args.optionFound("cellZonesOnly");
1480 bool useCellZonesFile = args.optionFound("cellZonesFileOnly");
1481 bool overwrite = args.optionFound("overwrite");
1482 bool detectOnly = args.optionFound("detectOnly");
1483 bool sloppyCellZones = args.optionFound("sloppyCellZones");
1487 (useCellZonesOnly || useCellZonesFile)
1489 blockedFacesName != word::null
1494 FatalErrorIn(args.executable())
1495 << "You cannot specify both -cellZonesOnly or -cellZonesFileOnly"
1496 << " (which specify complete split)"
1497 << " in combination with -blockedFaces or -cellZones"
1498 << " (which imply a split based on topology)"
1499 << exit(FatalError);
1502 if (insidePoint && largestOnly)
1504 FatalErrorIn(args.executable())
1505 << "You cannot specify both -largestOnly"
1506 << " (keep region with most cells)"
1507 << " and -insidePoint (keep region containing point)"
1508 << exit(FatalError);
1512 const cellZoneMesh& cellZones = mesh.cellZones();
1515 labelList zoneID(mesh.nCells(), -1);
1518 labelList neiZoneID(mesh.nFaces() - mesh.nInternalFaces());
1520 getZoneID(mesh, cellZones, zoneID, neiZoneID);
1524 // Determine per cell the region it belongs to
1525 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1527 // cellRegion is the labelList with the region per cell.
1528 labelList cellRegion;
1531 labelList regionToZone;
1534 wordList regionNames;
1537 labelList zoneToRegion;
1539 label nCellRegions = 0;
1540 if (useCellZonesOnly)
1542 Info<< "Using current cellZones to split mesh into regions."
1543 << " This requires all"
1544 << " cells to be in one and only one cellZone." << nl << endl;
1546 label unzonedCellI = findIndex(zoneID, -1);
1548 if (unzonedCellI != -1)
1550 FatalErrorIn(args.executable())
1551 << "For the cellZonesOnly option all cells "
1552 << "have to be in a cellZone." << endl
1553 << "Cell " << unzonedCellI
1554 << " at" << mesh.cellCentres()[unzonedCellI]
1555 << " is not in a cellZone. There might be more unzoned cells."
1556 << exit(FatalError);
1559 cellRegion = zoneID;
1560 nCellRegions = gMax(cellRegion) + 1;
1561 regionToZone.setSize(nCellRegions);
1562 regionNames.setSize(nCellRegions);
1563 zoneToRegion.setSize(cellZones.size(), -1);
1565 for (label regionI = 0; regionI < nCellRegions; regionI++)
1567 regionToZone[regionI] = regionI;
1568 zoneToRegion[regionI] = regionI;
1569 regionNames[regionI] = cellZones[regionI].name();
1572 else if (useCellZonesFile)
1574 const word zoneFile = args.option("cellZonesFileOnly");
1575 Info<< "Reading split from cellZones file " << zoneFile << endl
1576 << "This requires all"
1577 << " cells to be in one and only one cellZone." << nl << endl;
1579 cellZoneMesh newCellZones
1584 mesh.facesInstance(),
1585 polyMesh::meshSubDir,
1587 IOobject::MUST_READ,
1594 labelList newZoneID(mesh.nCells(), -1);
1595 labelList newNeiZoneID(mesh.nFaces()-mesh.nInternalFaces());
1596 getZoneID(mesh, newCellZones, newZoneID, newNeiZoneID);
1598 label unzonedCellI = findIndex(newZoneID, -1);
1600 if (unzonedCellI != -1)
1602 FatalErrorIn(args.executable())
1603 << "For the cellZonesFileOnly option all cells "
1604 << "have to be in a cellZone." << endl
1605 << "Cell " << unzonedCellI
1606 << " at" << mesh.cellCentres()[unzonedCellI]
1607 << " is not in a cellZone. There might be more unzoned cells."
1608 << exit(FatalError);
1611 cellRegion = newZoneID;
1612 nCellRegions = gMax(cellRegion) + 1;
1613 zoneToRegion.setSize(newCellZones.size(), -1);
1614 regionToZone.setSize(nCellRegions);
1615 regionNames.setSize(nCellRegions);
1617 for (label regionI = 0; regionI < nCellRegions; regionI++)
1619 regionToZone[regionI] = regionI;
1620 zoneToRegion[regionI] = regionI;
1621 regionNames[regionI] = newCellZones[regionI].name();
1626 // Determine connected regions
1627 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
1629 // Mark additional faces that are blocked
1630 boolList blockedFace;
1632 // Read from faceSet
1633 if (blockedFacesName.size())
1635 faceSet blockedFaceSet(mesh, blockedFacesName);
1637 << returnReduce(blockedFaceSet.size(), sumOp<label>())
1638 << " blocked faces from set " << blockedFacesName
1641 blockedFace.setSize(mesh.nFaces(), false);
1643 forAllConstIter(faceSet, blockedFaceSet, iter)
1645 blockedFace[iter.key()] = true;
1649 // Imply from differing cellZones
1652 blockedFace.setSize(mesh.nFaces(), false);
1654 for (label faceI = 0; faceI < mesh.nInternalFaces(); faceI++)
1656 label own = mesh.faceOwner()[faceI];
1657 label nei = mesh.faceNeighbour()[faceI];
1659 if (zoneID[own] != zoneID[nei])
1661 blockedFace[faceI] = true;
1665 // Different cellZones on either side of processor patch.
1666 forAll (neiZoneID, i)
1668 label faceI = i+mesh.nInternalFaces();
1670 if (zoneID[mesh.faceOwner()[faceI]] != neiZoneID[i])
1672 blockedFace[faceI] = true;
1677 // Do a topological walk to determine regions
1678 regionSplit regions(mesh, blockedFace);
1680 nCellRegions = regions.nRegions();
1681 cellRegion.transfer(regions);
1683 // Make up region names. If possible match them to existing zones.
1697 Info<< endl << "Number of regions:" << nCellRegions << nl << endl;
1700 // Write decomposition to file
1701 writeCellToRegion(mesh, cellRegion);
1707 labelList regionSizes(nCellRegions, 0);
1709 forAll (cellRegion, cellI)
1711 regionSizes[cellRegion[cellI]]++;
1713 forAll (regionSizes, regionI)
1715 reduce(regionSizes[regionI], sumOp<label>());
1718 Info<< "Region\tCells" << nl
1719 << "------\t-----" << endl;
1721 forAll (regionSizes, regionI)
1723 Info<< regionI << '\t' << regionSizes[regionI] << nl;
1729 // Print region to zone
1730 Info<< "Region\tZone\tName" << nl
1731 << "------\t----\t----" << endl;
1732 forAll (regionToZone, regionI)
1734 Info<< regionI << '\t' << regionToZone[regionI] << '\t'
1735 << regionNames[regionI] << nl;
1741 // Since we're going to mess with patches make sure all non-processor ones
1742 // are on all processors.
1743 mesh.boundaryMesh().checkParallelSync(true);
1746 // Sizes of interface between regions. From pair of regions to number of
1748 edgeList interfaces;
1749 EdgeMap<label> interfaceSizes;
1754 true, // sum in parallel?
1760 Info<< "Sizes inbetween regions:" << nl << nl
1761 << "Region\tRegion\tFaces" << nl
1762 << "------\t------\t-----" << endl;
1764 forAll (interfaces, interI)
1766 const edge& e = interfaces[interI];
1768 Info<< e[0] << '\t' << e[1] << '\t' << interfaceSizes[e] << nl;
1779 // Read objects in time directory
1780 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1782 IOobjectList objects(mesh, runTime.timeName());
1786 PtrList<volScalarField> vsFlds;
1787 ReadFields(mesh, objects, vsFlds);
1789 PtrList<volVectorField> vvFlds;
1790 ReadFields(mesh, objects, vvFlds);
1792 PtrList<volSphericalTensorField> vstFlds;
1793 ReadFields(mesh, objects, vstFlds);
1795 PtrList<volSymmTensorField> vsymtFlds;
1796 ReadFields(mesh, objects, vsymtFlds);
1798 PtrList<volTensorField> vtFlds;
1799 ReadFields(mesh, objects, vtFlds);
1801 // Read surface fields.
1803 PtrList<surfaceScalarField> ssFlds;
1804 ReadFields(mesh, objects, ssFlds);
1806 PtrList<surfaceVectorField> svFlds;
1807 ReadFields(mesh, objects, svFlds);
1809 PtrList<surfaceSphericalTensorField> sstFlds;
1810 ReadFields(mesh, objects, sstFlds);
1812 PtrList<surfaceSymmTensorField> ssymtFlds;
1813 ReadFields(mesh, objects, ssymtFlds);
1815 PtrList<surfaceTensorField> stFlds;
1816 ReadFields(mesh, objects, stFlds);
1821 // Remove any demand-driven fields ('S', 'V' etc)
1825 if (nCellRegions == 1)
1827 Info<< "Only one region. Doing nothing." << endl;
1829 else if (makeCellZones)
1831 Info<< "Putting cells into cellZones instead of splitting mesh."
1834 // Check if region overlaps with existing zone. If so keep.
1836 for (label regionI = 0; regionI < nCellRegions; regionI++)
1838 label zoneI = regionToZone[regionI];
1842 Info<< " Region " << regionI << " : corresponds to existing"
1844 << zoneI << ' ' << cellZones[zoneI].name() << endl;
1848 // Create new cellZone.
1849 labelList regionCells = findIndices(cellRegion, regionI);
1851 word zoneName = "region" + Foam::name(regionI);
1853 zoneI = cellZones.findZoneID(zoneName);
1857 zoneI = cellZones.size();
1858 mesh.cellZones().setSize(zoneI+1);
1859 mesh.cellZones().set
1865 regionCells, // addressing
1867 cellZones // cellZoneMesh
1873 mesh.cellZones()[zoneI].clearAddressing();
1874 mesh.cellZones()[zoneI] = regionCells;
1876 Info<< " Region " << regionI << " : created new cellZone "
1877 << zoneI << ' ' << cellZones[zoneI].name() << endl;
1880 mesh.cellZones().writeOpt() = IOobject::AUTO_WRITE;
1885 mesh.setInstance(runTime.timeName());
1889 mesh.setInstance(oldInstance);
1892 Info<< "Writing cellZones as new mesh to time " << runTime.timeName()
1898 // Write cellSets for convenience
1899 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1901 Info<< "Writing cellSets corresponding to cellZones." << nl << endl;
1903 forAll (cellZones, zoneI)
1905 const cellZone& cz = cellZones[zoneI];
1907 cellSet(mesh, cz.name(), labelHashSet(cz)).write();
1912 // Add patches for interfaces
1913 // ~~~~~~~~~~~~~~~~~~~~~~~~~~
1915 // Add all possible patches. Empty ones get filtered later on.
1916 Info<< nl << "Adding patches" << nl << endl;
1918 EdgeMap<label> interfaceToPatch
1943 point insidePoint(args.optionLookup("insidePoint")());
1947 label cellI = mesh.findCell(insidePoint);
1949 Info<< nl << "Found point " << insidePoint << " in cell " << cellI
1954 regionI = cellRegion[cellI];
1957 reduce(regionI, maxOp<label>());
1960 << "Subsetting region " << regionI
1961 << " containing point " << insidePoint << endl;
1965 FatalErrorIn(args.executable())
1966 << "Point " << insidePoint
1967 << " is not inside the mesh." << nl
1968 << "Bounding box of the mesh:" << mesh.bounds()
1969 << exit(FatalError);
1972 createAndWriteRegion
1979 (overwrite ? oldInstance : runTime.timeName())
1982 else if (largestOnly)
1984 label regionI = findMax(regionSizes);
1987 << "Subsetting region " << regionI
1988 << " of size " << regionSizes[regionI] << endl;
1990 createAndWriteRegion
1997 (overwrite ? oldInstance : runTime.timeName())
2003 for (label regionI = 0; regionI < nCellRegions; regionI++)
2006 << "Region " << regionI << nl
2007 << "-------- " << endl;
2009 createAndWriteRegion
2016 (overwrite ? oldInstance : runTime.timeName())
2022 Info<< "End\n" << endl;
2028 // ************************************************************************* //