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
26 Splits mesh into multiple regions.
28 Each region is defined as a domain whose cells can all be reached by
29 cell-face-cell walking without crossing
31 - additional faces from faceset (-blockedFaces faceSet).
32 - any face inbetween differing cellZones (-cellZones)
35 - mesh with multiple regions or
36 - mesh with cells put into cellZones (-makeCellZones)
39 - Should work in parallel.
40 cellZones can differ on either side of processor boundaries in which case
41 the faces get moved from processor patch to directMapped patch. Not
43 - If a cell zone gets split into more than one region it can detect
44 the largest matching region (-sloppyCellZones). This will accept any
45 region that covers more than 50% of the zone. It has to be a subset
46 so cannot have any cells in any other zone.
47 - useCellZonesOnly does not do a walk and uses the cellZones only. Use
48 this if you don't mind having disconnected domains in a single region.
49 This option requires all cells to be in one (and one only) cellZone.
51 \*---------------------------------------------------------------------------*/
53 #include "SortableList.H"
55 #include "regionSplit.H"
56 #include "fvMeshSubset.H"
57 #include "IOobjectList.H"
58 #include "volFields.H"
61 #include "polyTopoChange.H"
62 #include "removeCells.H"
64 #include "syncTools.H"
65 #include "ReadFields.H"
66 #include "directMappedWallPolyPatch.H"
67 #include "zeroGradientFvPatchFields.H"
71 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
73 template<class GeoField>
74 void addPatchFields(fvMesh& mesh, const word& patchFieldType)
76 HashTable<const GeoField*> flds
78 mesh.objectRegistry::lookupClass<GeoField>()
83 typename HashTable<const GeoField*>::const_iterator iter = flds.begin();
88 const GeoField& fld = *iter();
90 typename GeoField::GeometricBoundaryField& bfld =
91 const_cast<typename GeoField::GeometricBoundaryField&>
96 label sz = bfld.size();
101 GeoField::PatchFieldType::New
105 fld.dimensionedInternalField()
112 // Remove last patch field
113 template<class GeoField>
114 void trimPatchFields(fvMesh& mesh, const label nPatches)
116 HashTable<const GeoField*> flds
118 mesh.objectRegistry::lookupClass<GeoField>()
123 typename HashTable<const GeoField*>::const_iterator iter = flds.begin();
128 const GeoField& fld = *iter();
130 const_cast<typename GeoField::GeometricBoundaryField&>
138 // Reorder patch field
139 template<class GeoField>
140 void reorderPatchFields(fvMesh& mesh, const labelList& oldToNew)
142 HashTable<const GeoField*> flds
144 mesh.objectRegistry::lookupClass<GeoField>()
149 typename HashTable<const GeoField*>::const_iterator iter = flds.begin();
154 const GeoField& fld = *iter();
156 typename GeoField::GeometricBoundaryField& bfld =
157 const_cast<typename GeoField::GeometricBoundaryField&>
162 bfld.reorder(oldToNew);
167 // Adds patch if not yet there. Returns patchID.
168 label addPatch(fvMesh& mesh, const polyPatch& patch)
170 polyBoundaryMesh& polyPatches =
171 const_cast<polyBoundaryMesh&>(mesh.boundaryMesh());
173 label patchI = polyPatches.findPatchID(patch.name());
176 if (polyPatches[patchI].type() == patch.type())
183 FatalErrorIn("addPatch(fvMesh&, const polyPatch*)")
184 << "Already have patch " << patch.name()
185 << " but of type " << patch.type()
191 label insertPatchI = polyPatches.size();
192 label startFaceI = mesh.nFaces();
194 forAll(polyPatches, patchI)
196 const polyPatch& pp = polyPatches[patchI];
198 if (isA<processorPolyPatch>(pp))
200 insertPatchI = patchI;
201 startFaceI = pp.start();
207 // Below is all quite a hack. Feel free to change once there is a better
208 // mechanism to insert and reorder patches.
210 // Clear local fields and e.g. polyMesh parallelInfo.
213 label sz = polyPatches.size();
215 fvBoundaryMesh& fvPatches = const_cast<fvBoundaryMesh&>(mesh.boundary());
217 // Add polyPatch at the end
218 polyPatches.setSize(sz+1);
225 insertPatchI, //index
230 fvPatches.setSize(sz+1);
236 polyPatches[sz], // point to newly added polyPatch
241 addPatchFields<volScalarField>
244 calculatedFvPatchField<scalar>::typeName
246 addPatchFields<volVectorField>
249 calculatedFvPatchField<vector>::typeName
251 addPatchFields<volSphericalTensorField>
254 calculatedFvPatchField<sphericalTensor>::typeName
256 addPatchFields<volSymmTensorField>
259 calculatedFvPatchField<symmTensor>::typeName
261 addPatchFields<volTensorField>
264 calculatedFvPatchField<tensor>::typeName
269 addPatchFields<surfaceScalarField>
272 calculatedFvPatchField<scalar>::typeName
274 addPatchFields<surfaceVectorField>
277 calculatedFvPatchField<vector>::typeName
279 addPatchFields<surfaceSphericalTensorField>
282 calculatedFvPatchField<sphericalTensor>::typeName
284 addPatchFields<surfaceSymmTensorField>
287 calculatedFvPatchField<symmTensor>::typeName
289 addPatchFields<surfaceTensorField>
292 calculatedFvPatchField<tensor>::typeName
295 // Create reordering list
296 // patches before insert position stay as is
297 labelList oldToNew(sz+1);
298 for (label i = 0; i < insertPatchI; i++)
302 // patches after insert position move one up
303 for (label i = insertPatchI; i < sz; i++)
307 // appended patch gets moved to insert position
308 oldToNew[sz] = insertPatchI;
310 // Shuffle into place
311 polyPatches.reorder(oldToNew);
312 fvPatches.reorder(oldToNew);
314 reorderPatchFields<volScalarField>(mesh, oldToNew);
315 reorderPatchFields<volVectorField>(mesh, oldToNew);
316 reorderPatchFields<volSphericalTensorField>(mesh, oldToNew);
317 reorderPatchFields<volSymmTensorField>(mesh, oldToNew);
318 reorderPatchFields<volTensorField>(mesh, oldToNew);
319 reorderPatchFields<surfaceScalarField>(mesh, oldToNew);
320 reorderPatchFields<surfaceVectorField>(mesh, oldToNew);
321 reorderPatchFields<surfaceSphericalTensorField>(mesh, oldToNew);
322 reorderPatchFields<surfaceSymmTensorField>(mesh, oldToNew);
323 reorderPatchFields<surfaceTensorField>(mesh, oldToNew);
329 // Reorder and delete patches.
333 const labelList& oldToNew,
334 const label nNewPatches
337 polyBoundaryMesh& polyPatches =
338 const_cast<polyBoundaryMesh&>(mesh.boundaryMesh());
339 fvBoundaryMesh& fvPatches = const_cast<fvBoundaryMesh&>(mesh.boundary());
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);
357 polyPatches.setSize(nNewPatches);
358 fvPatches.setSize(nNewPatches);
359 trimPatchFields<volScalarField>(mesh, nNewPatches);
360 trimPatchFields<volVectorField>(mesh, nNewPatches);
361 trimPatchFields<volSphericalTensorField>(mesh, nNewPatches);
362 trimPatchFields<volSymmTensorField>(mesh, nNewPatches);
363 trimPatchFields<volTensorField>(mesh, nNewPatches);
364 trimPatchFields<surfaceScalarField>(mesh, nNewPatches);
365 trimPatchFields<surfaceVectorField>(mesh, nNewPatches);
366 trimPatchFields<surfaceSphericalTensorField>(mesh, nNewPatches);
367 trimPatchFields<surfaceSymmTensorField>(mesh, nNewPatches);
368 trimPatchFields<surfaceTensorField>(mesh, nNewPatches);
372 template<class GeoField>
376 const fvMesh& subMesh,
377 const labelList& cellMap,
378 const labelList& faceMap
381 const labelList patchMap(identity(mesh.boundaryMesh().size()));
383 HashTable<const GeoField*> fields
385 mesh.objectRegistry::lookupClass<GeoField>()
387 forAllConstIter(typename HashTable<const GeoField*>, fields, iter)
389 const GeoField& fld = *iter();
391 Info<< "Mapping field " << fld.name() << endl;
393 tmp<GeoField> tSubFld
395 fvMeshSubset::interpolate
405 // Hack: set value to 0 for introduced patches (since don't
407 forAll(tSubFld().boundaryField(), patchI)
409 const fvPatchField<typename GeoField::value_type>& pfld =
410 tSubFld().boundaryField()[patchI];
414 isA<calculatedFvPatchField<typename GeoField::value_type> >
418 tSubFld().boundaryField()[patchI] ==
419 pTraits<typename GeoField::value_type>::zero;
424 GeoField* subFld = tSubFld.ptr();
425 subFld->rename(fld.name());
426 subFld->writeOpt() = IOobject::AUTO_WRITE;
432 template<class GeoField>
433 void subsetSurfaceFields
436 const fvMesh& subMesh,
437 const labelList& faceMap
440 const labelList patchMap(identity(mesh.boundaryMesh().size()));
442 HashTable<const GeoField*> fields
444 mesh.objectRegistry::lookupClass<GeoField>()
446 forAllConstIter(typename HashTable<const GeoField*>, fields, iter)
448 const GeoField& fld = *iter();
450 Info<< "Mapping field " << fld.name() << endl;
452 tmp<GeoField> tSubFld
454 fvMeshSubset::interpolate
463 // Hack: set value to 0 for introduced patches (since don't
465 forAll(tSubFld().boundaryField(), patchI)
467 const fvsPatchField<typename GeoField::value_type>& pfld =
468 tSubFld().boundaryField()[patchI];
472 isA<calculatedFvsPatchField<typename GeoField::value_type> >
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();
504 // Get per region-region interface the sizes. If sumParallel sums sizes.
505 // Returns interfaces as straight list for looping in identical order.
506 void getInterfaceSizes
508 const polyMesh& mesh,
509 const labelList& cellRegion,
510 const bool sumParallel,
512 edgeList& interfaces,
513 EdgeMap<label>& interfaceSizes
520 forAll(mesh.faceNeighbour(), faceI)
522 label ownRegion = cellRegion[mesh.faceOwner()[faceI]];
523 label neiRegion = cellRegion[mesh.faceNeighbour()[faceI]];
525 if (ownRegion != neiRegion)
529 min(ownRegion, neiRegion),
530 max(ownRegion, neiRegion)
533 EdgeMap<label>::iterator iter = interfaceSizes.find(interface);
535 if (iter != interfaceSizes.end())
541 interfaceSizes.insert(interface, 1);
549 // Neighbour cellRegion.
550 labelList coupledRegion(mesh.nFaces()-mesh.nInternalFaces());
552 forAll(coupledRegion, i)
554 label cellI = mesh.faceOwner()[i+mesh.nInternalFaces()];
555 coupledRegion[i] = cellRegion[cellI];
557 syncTools::swapBoundaryFaceList(mesh, coupledRegion, false);
559 forAll(coupledRegion, i)
561 label faceI = i+mesh.nInternalFaces();
562 label ownRegion = cellRegion[mesh.faceOwner()[faceI]];
563 label neiRegion = coupledRegion[i];
565 if (ownRegion != neiRegion)
569 min(ownRegion, neiRegion),
570 max(ownRegion, neiRegion)
573 EdgeMap<label>::iterator iter = interfaceSizes.find(interface);
575 if (iter != interfaceSizes.end())
581 interfaceSizes.insert(interface, 1);
587 if (sumParallel && Pstream::parRun())
589 if (Pstream::master())
591 // Receive and add to my sizes
594 int slave=Pstream::firstSlave();
595 slave<=Pstream::lastSlave();
599 IPstream fromSlave(Pstream::blocking, slave);
601 EdgeMap<label> slaveSizes(fromSlave);
603 forAllConstIter(EdgeMap<label>, slaveSizes, slaveIter)
605 EdgeMap<label>::iterator masterIter =
606 interfaceSizes.find(slaveIter.key());
608 if (masterIter != interfaceSizes.end())
610 masterIter() += slaveIter();
614 interfaceSizes.insert(slaveIter.key(), slaveIter());
622 int slave=Pstream::firstSlave();
623 slave<=Pstream::lastSlave();
627 // Receive the edges using shared points from the slave.
628 OPstream toSlave(Pstream::blocking, slave);
629 toSlave << interfaceSizes;
636 OPstream toMaster(Pstream::blocking, Pstream::masterNo());
637 toMaster << interfaceSizes;
639 // Receive from master
641 IPstream fromMaster(Pstream::blocking, Pstream::masterNo());
642 fromMaster >> interfaceSizes;
647 // Make sure all processors have interfaces in same order
648 interfaces = interfaceSizes.toc();
651 Pstream::scatter(interfaces);
656 // Create mesh for region.
657 autoPtr<mapPolyMesh> createRegionMesh
659 const labelList& cellRegion,
660 const EdgeMap<label>& interfaceToPatch,
663 const word& regionName,
664 autoPtr<fvMesh>& newMesh
667 // Create dummy system/fv*
672 mesh.time().system(),
680 Info<< "Testing:" << io.objectPath() << endl;
683 // if (!exists(io.objectPath()))
685 Info<< "Writing dummy " << regionName/io.name() << endl;
686 dictionary dummyDict;
688 dummyDict.add("divSchemes", divDict);
690 dummyDict.add("gradSchemes", gradDict);
692 dummyDict.add("laplacianSchemes", laplDict);
694 IOdictionary(io, dummyDict).regIOobject::write();
701 mesh.time().system(),
710 //if (!exists(io.objectPath()))
712 Info<< "Writing dummy " << regionName/io.name() << endl;
713 dictionary dummyDict;
714 IOdictionary(io, dummyDict).regIOobject::write();
719 // Neighbour cellRegion.
720 labelList coupledRegion(mesh.nFaces()-mesh.nInternalFaces());
722 forAll(coupledRegion, i)
724 label cellI = mesh.faceOwner()[i+mesh.nInternalFaces()];
725 coupledRegion[i] = cellRegion[cellI];
727 syncTools::swapBoundaryFaceList(mesh, coupledRegion, false);
730 // Topology change container. Start off from existing mesh.
731 polyTopoChange meshMod(mesh);
733 // Cell remover engine
734 removeCells cellRemover(mesh);
736 // Select all but region cells
737 labelList cellsToRemove(getNonRegionCells(cellRegion, regionI));
739 // Find out which faces will get exposed. Note that this
740 // gets faces in mesh face order. So both regions will get same
741 // face in same order (important!)
742 labelList exposedFaces = cellRemover.getExposedFaces(cellsToRemove);
744 labelList exposedPatchIDs(exposedFaces.size());
745 forAll(exposedFaces, i)
747 label faceI = exposedFaces[i];
749 label ownRegion = cellRegion[mesh.faceOwner()[faceI]];
750 label neiRegion = -1;
752 if (mesh.isInternalFace(faceI))
754 neiRegion = cellRegion[mesh.faceNeighbour()[faceI]];
758 neiRegion = coupledRegion[faceI-mesh.nInternalFaces()];
761 label otherRegion = -1;
763 if (ownRegion == regionI && neiRegion != regionI)
765 otherRegion = neiRegion;
767 else if (ownRegion != regionI && neiRegion == regionI)
769 otherRegion = ownRegion;
773 FatalErrorIn("createRegionMesh(..)")
774 << "Exposed face:" << faceI
775 << " fc:" << mesh.faceCentres()[faceI]
776 << " has owner region " << ownRegion
777 << " and neighbour region " << neiRegion
778 << " when handling region:" << regionI
782 if (otherRegion != -1)
784 edge interface(regionI, otherRegion);
787 if (regionI < otherRegion)
789 exposedPatchIDs[i] = interfaceToPatch[interface];
793 exposedPatchIDs[i] = interfaceToPatch[interface]+1;
799 cellRemover.setRefinement
807 autoPtr<mapPolyMesh> map = meshMod.makeMesh
813 mesh.time().timeName(),
825 void createAndWriteRegion
828 const labelList& cellRegion,
829 const wordList& regionNames,
830 const EdgeMap<label>& interfaceToPatch,
832 const word& newMeshInstance
835 Info<< "Creating mesh for region " << regionI
836 << ' ' << regionNames[regionI] << endl;
838 autoPtr<fvMesh> newMesh;
839 autoPtr<mapPolyMesh> map = createRegionMesh
845 regionNames[regionI],
849 Info<< "Mapping fields" << endl;
851 // Map existing fields
852 newMesh().updateMesh(map());
854 // Add subsetted fields
855 subsetVolFields<volScalarField>
862 subsetVolFields<volVectorField>
869 subsetVolFields<volSphericalTensorField>
876 subsetVolFields<volSymmTensorField>
883 subsetVolFields<volTensorField>
891 subsetSurfaceFields<surfaceScalarField>
897 subsetSurfaceFields<surfaceVectorField>
903 subsetSurfaceFields<surfaceSphericalTensorField>
909 subsetSurfaceFields<surfaceSymmTensorField>
915 subsetSurfaceFields<surfaceTensorField>
923 const polyBoundaryMesh& newPatches = newMesh().boundaryMesh();
924 newPatches.checkParallelSync(true);
926 // Delete empty patches
927 // ~~~~~~~~~~~~~~~~~~~~
929 // Create reordering list to move patches-to-be-deleted to end
930 labelList oldToNew(newPatches.size(), -1);
933 Info<< "Deleting empty patches" << endl;
935 // Assumes all non-proc boundaries are on all processors!
936 forAll(newPatches, patchI)
938 const polyPatch& pp = newPatches[patchI];
940 if (!isA<processorPolyPatch>(pp))
942 if (returnReduce(pp.size(), sumOp<label>()) > 0)
944 oldToNew[patchI] = newI++;
949 // Same for processor patches (but need no reduction)
950 forAll(newPatches, patchI)
952 const polyPatch& pp = newPatches[patchI];
954 if (isA<processorPolyPatch>(pp) && pp.size())
956 oldToNew[patchI] = newI++;
960 const label nNewPatches = newI;
962 // Move all deleteable patches to the end
963 forAll(oldToNew, patchI)
965 if (oldToNew[patchI] == -1)
967 oldToNew[patchI] = newI++;
971 reorderPatches(newMesh(), oldToNew, nNewPatches);
974 Info<< "Writing new mesh" << endl;
976 newMesh().setInstance(newMeshInstance);
979 // Write addressing files like decomposePar
980 Info<< "Writing addressing to base mesh" << endl;
982 labelIOList pointProcAddressing
986 "pointRegionAddressing",
987 newMesh().facesInstance(),
988 newMesh().meshSubDir,
996 Info<< "Writing map " << pointProcAddressing.name()
997 << " from region" << regionI
998 << " points back to base mesh." << endl;
999 pointProcAddressing.write();
1001 labelIOList faceProcAddressing
1005 "faceRegionAddressing",
1006 newMesh().facesInstance(),
1007 newMesh().meshSubDir,
1015 forAll(faceProcAddressing, faceI)
1017 // face + turning index. (see decomposePar)
1018 // Is the face pointing in the same direction?
1019 label oldFaceI = map().faceMap()[faceI];
1023 map().cellMap()[newMesh().faceOwner()[faceI]]
1024 == mesh.faceOwner()[oldFaceI]
1027 faceProcAddressing[faceI] = oldFaceI+1;
1031 faceProcAddressing[faceI] = -(oldFaceI+1);
1034 Info<< "Writing map " << faceProcAddressing.name()
1035 << " from region" << regionI
1036 << " faces back to base mesh." << endl;
1037 faceProcAddressing.write();
1039 labelIOList cellProcAddressing
1043 "cellRegionAddressing",
1044 newMesh().facesInstance(),
1045 newMesh().meshSubDir,
1053 Info<< "Writing map " <<cellProcAddressing.name()
1054 << " from region" << regionI
1055 << " cells back to base mesh." << endl;
1056 cellProcAddressing.write();
1060 // Create for every region-region interface with non-zero size two patches.
1061 // First one is for minimumregion to maximumregion.
1062 // Note that patches get created in same order on all processors (if parallel)
1063 // since looping over synchronised 'interfaces'.
1064 EdgeMap<label> addRegionPatches
1067 const labelList& cellRegion,
1068 const label nCellRegions,
1069 const edgeList& interfaces,
1070 const EdgeMap<label>& interfaceSizes,
1071 const wordList& regionNames
1074 // Check that all patches are present in same order.
1075 mesh.boundaryMesh().checkParallelSync(true);
1077 Info<< nl << "Adding patches" << nl << endl;
1079 EdgeMap<label> interfaceToPatch(nCellRegions);
1081 forAll(interfaces, interI)
1083 const edge& e = interfaces[interI];
1085 if (interfaceSizes[e] > 0)
1087 const word inter1 = regionNames[e[0]] + "_to_" + regionNames[e[1]];
1088 const word inter2 = regionNames[e[1]] + "_to_" + regionNames[e[0]];
1090 directMappedWallPolyPatch patch1
1096 regionNames[e[1]], // sampleRegion
1097 directMappedPatchBase::NEARESTPATCHFACE,
1098 inter2, // samplePatch
1099 point::zero, // offset
1103 label patchI = addPatch(mesh, patch1);
1105 directMappedWallPolyPatch patch2
1111 regionNames[e[0]], // sampleRegion
1112 directMappedPatchBase::NEARESTPATCHFACE,
1114 point::zero, // offset
1117 addPatch(mesh, patch2);
1119 Info<< "For interface between region " << e[0]
1120 << " and " << e[1] << " added patch " << patchI
1121 << " " << mesh.boundaryMesh()[patchI].name()
1124 interfaceToPatch.insert(e, patchI);
1127 return interfaceToPatch;
1131 //// Checks if regionI in cellRegion is subset of existing cellZone. Returns -1
1132 //// if no zone found, zone otherwise
1133 //label findCorrespondingSubZone
1135 // const cellZoneMesh& cellZones,
1136 // const labelList& existingZoneID,
1137 // const labelList& cellRegion,
1138 // const label regionI
1141 // // Zone corresponding to region. No corresponding zone.
1142 // label zoneI = labelMax;
1144 // labelList regionCells = findIndices(cellRegion, regionI);
1146 // if (regionCells.empty())
1148 // // My local portion is empty. Maps to any empty cellZone. Mark with
1149 // // special value which can get overwritten by other processors.
1154 // // Get zone for first element.
1155 // zoneI = existingZoneID[regionCells[0]];
1159 // zoneI = labelMax;
1163 // // 1. All regionCells in zoneI?
1164 // forAll(regionCells, i)
1166 // if (existingZoneID[regionCells[i]] != zoneI)
1168 // zoneI = labelMax;
1175 // // Determine same zone over all processors.
1176 // reduce(zoneI, maxOp<label>());
1178 // if (zoneI == labelMax)
1180 // // Cells in region that are not in zoneI
1188 // Find region that covers most of cell zone
1189 label findCorrespondingRegion
1191 const labelList& existingZoneID, // per cell the (unique) zoneID
1192 const labelList& cellRegion,
1193 const label nCellRegions,
1195 const label minOverlapSize
1198 // Per region the number of cells in zoneI
1199 labelList cellsInZone(nCellRegions, 0);
1201 forAll(cellRegion, cellI)
1203 if (existingZoneID[cellI] == zoneI)
1205 cellsInZone[cellRegion[cellI]]++;
1209 Pstream::listCombineGather(cellsInZone, plusEqOp<label>());
1210 Pstream::listCombineScatter(cellsInZone);
1212 // Pick region with largest overlap of zoneI
1213 label regionI = findMax(cellsInZone);
1216 if (cellsInZone[regionI] < minOverlapSize)
1218 // Region covers too little of zone. Not good enough.
1223 // Check that region contains no cells that aren't in cellZone.
1224 forAll(cellRegion, cellI)
1226 if (cellRegion[cellI] == regionI && existingZoneID[cellI] != zoneI)
1228 // cellI in regionI but not in zoneI
1233 // If one in error, all should be in error. Note that branch gets taken
1235 reduce(regionI, minOp<label>());
1242 //// Checks if cellZone has corresponding cellRegion.
1243 //label findCorrespondingRegion
1245 // const cellZoneMesh& cellZones,
1246 // const labelList& existingZoneID, // per cell the (unique) zoneID
1247 // const labelList& cellRegion,
1248 // const label nCellRegions,
1249 // const label zoneI
1252 // // Region corresponding to zone. Start off with special value: no
1253 // // corresponding region.
1254 // label regionI = labelMax;
1256 // const cellZone& cz = cellZones[zoneI];
1260 // // My local portion is empty. Maps to any empty cellZone. Mark with
1261 // // special value which can get overwritten by other processors.
1266 // regionI = cellRegion[cz[0]];
1270 // if (cellRegion[cz[i]] != regionI)
1272 // regionI = labelMax;
1278 // // Determine same zone over all processors.
1279 // reduce(regionI, maxOp<label>());
1282 // // 2. All of region present?
1284 // if (regionI == labelMax)
1288 // else if (regionI != -1)
1290 // forAll(cellRegion, cellI)
1292 // if (cellRegion[cellI] == regionI && existingZoneID[cellI] != zoneI)
1294 // // cellI in regionI but not in zoneI
1299 // // If one in error, all should be in error. Note that branch gets taken
1301 // reduce(regionI, minOp<label>());
1310 int main(int argc, char *argv[])
1312 argList::validOptions.insert("cellZones", "");
1313 argList::validOptions.insert("cellZonesOnly", "");
1314 argList::validOptions.insert("blockedFaces", "faceSet");
1315 argList::validOptions.insert("makeCellZones", "");
1316 argList::validOptions.insert("largestOnly", "");
1317 argList::validOptions.insert("insidePoint", "point");
1318 argList::validOptions.insert("overwrite", "");
1319 argList::validOptions.insert("detectOnly", "");
1320 argList::validOptions.insert("sloppyCellZones", "");
1322 # include "setRootCase.H"
1323 # include "createTime.H"
1324 runTime.functionObjects().off();
1325 # include "createMesh.H"
1326 const word oldInstance = mesh.pointsInstance();
1328 word blockedFacesName;
1329 if (args.optionFound("blockedFaces"))
1331 blockedFacesName = args.option("blockedFaces");
1332 Info<< "Reading blocked internal faces from faceSet "
1333 << blockedFacesName << nl << endl;
1336 bool makeCellZones = args.optionFound("makeCellZones");
1337 bool largestOnly = args.optionFound("largestOnly");
1338 bool insidePoint = args.optionFound("insidePoint");
1339 bool useCellZones = args.optionFound("cellZones");
1340 bool useCellZonesOnly = args.optionFound("cellZonesOnly");
1341 bool overwrite = args.optionFound("overwrite");
1342 bool detectOnly = args.optionFound("detectOnly");
1343 bool sloppyCellZones = args.optionFound("sloppyCellZones");
1345 if (insidePoint && largestOnly)
1347 FatalErrorIn(args.executable())
1348 << "You cannot specify both -largestOnly"
1349 << " (keep region with most cells)"
1350 << " and -insidePoint (keep region containing point)"
1351 << exit(FatalError);
1355 const cellZoneMesh& cellZones = mesh.cellZones();
1358 // Collect zone per cell
1359 // ~~~~~~~~~~~~~~~~~~~~~
1360 // - non-unique zoning
1364 labelList zoneID(mesh.nCells(), -1);
1366 forAll(cellZones, zoneI)
1368 const cellZone& cz = cellZones[zoneI];
1372 label cellI = cz[i];
1373 if (zoneID[cellI] == -1)
1375 zoneID[cellI] = zoneI;
1379 FatalErrorIn(args.executable())
1380 << "Cell " << cellI << " with cell centre "
1381 << mesh.cellCentres()[cellI]
1382 << " is multiple zones. This is not allowed." << endl
1383 << "It is in zone " << cellZones[zoneID[cellI]].name()
1384 << " and in zone " << cellZones[zoneI].name()
1385 << exit(FatalError);
1390 // Neighbour zoneID.
1391 labelList neiZoneID(mesh.nFaces()-mesh.nInternalFaces());
1393 forAll(neiZoneID, i)
1395 neiZoneID[i] = zoneID[mesh.faceOwner()[i+mesh.nInternalFaces()]];
1397 syncTools::swapBoundaryFaceList(mesh, neiZoneID, false);
1400 // Determine connected regions
1401 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
1403 // Mark additional faces that are blocked
1404 boolList blockedFace;
1406 // Read from faceSet
1407 if (blockedFacesName.size())
1409 faceSet blockedFaceSet(mesh, blockedFacesName);
1410 Info<< "Read " << returnReduce(blockedFaceSet.size(), sumOp<label>())
1411 << " blocked faces from set " << blockedFacesName << nl << endl;
1413 blockedFace.setSize(mesh.nFaces(), false);
1415 forAllConstIter(faceSet, blockedFaceSet, iter)
1417 blockedFace[iter.key()] = true;
1421 // Imply from differing cellZones
1424 blockedFace.setSize(mesh.nFaces(), false);
1426 for (label faceI = 0; faceI < mesh.nInternalFaces(); faceI++)
1428 label own = mesh.faceOwner()[faceI];
1429 label nei = mesh.faceNeighbour()[faceI];
1431 if (zoneID[own] != zoneID[nei])
1433 blockedFace[faceI] = true;
1437 // Different cellZones on either side of processor patch.
1438 forAll(neiZoneID, i)
1440 label faceI = i+mesh.nInternalFaces();
1442 if (zoneID[mesh.faceOwner()[faceI]] != neiZoneID[i])
1444 blockedFace[faceI] = true;
1450 // Determine per cell the region it belongs to
1451 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1453 // cellRegion is the labelList with the region per cell.
1454 labelList cellRegion;
1455 label nCellRegions = 0;
1456 if (useCellZonesOnly)
1458 label unzonedCellI = findIndex(zoneID, -1);
1459 if (unzonedCellI != -1)
1461 FatalErrorIn(args.executable())
1462 << "For the cellZonesOnly option all cells "
1463 << "have to be in a cellZone." << endl
1464 << "Cell " << unzonedCellI
1465 << " at" << mesh.cellCentres()[unzonedCellI]
1466 << " is not in a cellZone. There might be more unzoned cells."
1467 << exit(FatalError);
1469 cellRegion = zoneID;
1470 nCellRegions = gMax(cellRegion)+1;
1474 // Do a topological walk to determine regions
1475 regionSplit regions(mesh, blockedFace);
1476 nCellRegions = regions.nRegions();
1477 cellRegion.transfer(regions);
1480 Info<< endl << "Number of regions:" << nCellRegions << nl << endl;
1483 // Write to manual decomposition option
1485 labelIOList cellToRegion
1490 mesh.facesInstance(),
1498 cellToRegion.write();
1500 Info<< "Writing region per cell file (for manual decomposition) to "
1501 << cellToRegion.objectPath() << nl << endl;
1503 // Write for postprocessing
1505 volScalarField cellToRegion
1517 dimensionedScalar("zero", dimless, 0),
1518 zeroGradientFvPatchScalarField::typeName
1520 forAll(cellRegion, cellI)
1522 cellToRegion[cellI] = cellRegion[cellI];
1524 cellToRegion.write();
1526 Info<< "Writing region per cell as volScalarField to "
1527 << cellToRegion.objectPath() << nl << endl;
1534 labelList regionSizes(nCellRegions, 0);
1536 forAll(cellRegion, cellI)
1538 regionSizes[cellRegion[cellI]]++;
1540 forAll(regionSizes, regionI)
1542 reduce(regionSizes[regionI], sumOp<label>());
1545 Info<< "Region\tCells" << nl
1546 << "------\t-----" << endl;
1548 forAll(regionSizes, regionI)
1550 Info<< regionI << '\t' << regionSizes[regionI] << nl;
1555 // Sizes per cellzone
1556 // ~~~~~~~~~~~~~~~~~~
1558 labelList zoneSizes(cellZones.size(), 0);
1559 if (useCellZones || makeCellZones || sloppyCellZones)
1561 List<wordList> zoneNames(Pstream::nProcs());
1562 zoneNames[Pstream::myProcNo()] = cellZones.names();
1563 Pstream::gatherList(zoneNames);
1564 Pstream::scatterList(zoneNames);
1566 forAll(zoneNames, procI)
1568 if (zoneNames[procI] != zoneNames[0])
1570 FatalErrorIn(args.executable())
1571 << "cellZones not synchronised across processors." << endl
1572 << "Master has cellZones " << zoneNames[0] << endl
1573 << "Processor " << procI
1574 << " has cellZones " << zoneNames[procI]
1575 << exit(FatalError);
1579 forAll(cellZones, zoneI)
1581 zoneSizes[zoneI] = returnReduce
1583 cellZones[zoneI].size(),
1590 // Whether region corresponds to a cellzone
1591 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1594 labelList regionToZone(nCellRegions, -1);
1596 wordList regionNames(nCellRegions);
1598 labelList zoneToRegion(cellZones.size(), -1);
1600 if (sloppyCellZones)
1602 Info<< "Trying to match regions to existing cell zones;"
1603 << " region can be subset of cell zone." << nl << endl;
1605 forAll(cellZones, zoneI)
1607 label regionI = findCorrespondingRegion
1613 label(0.5*zoneSizes[zoneI]) // minimum overlap
1618 Info<< "Sloppily matched region " << regionI
1619 << " size " << regionSizes[regionI]
1620 << " to zone " << zoneI << " size " << zoneSizes[zoneI]
1622 zoneToRegion[zoneI] = regionI;
1623 regionToZone[regionI] = zoneI;
1624 regionNames[regionI] = cellZones[zoneI].name();
1630 Info<< "Trying to match regions to existing cell zones." << nl << endl;
1632 forAll(cellZones, zoneI)
1634 label regionI = findCorrespondingRegion
1640 1 // minimum overlap
1645 zoneToRegion[zoneI] = regionI;
1646 regionToZone[regionI] = zoneI;
1647 regionNames[regionI] = cellZones[zoneI].name();
1651 // Allocate region names for unmatched regions.
1652 forAll(regionToZone, regionI)
1654 if (regionToZone[regionI] == -1)
1656 regionNames[regionI] = "domain" + Foam::name(regionI);
1661 // Print region to zone
1662 Info<< "Region\tZone\tName" << nl
1663 << "------\t----\t----" << endl;
1664 forAll(regionToZone, regionI)
1666 Info<< regionI << '\t' << regionToZone[regionI] << '\t'
1667 << regionNames[regionI] << nl;
1671 //// Print zone to region
1672 //Info<< "Zone\tName\tRegion" << nl
1673 // << "----\t----\t------" << endl;
1674 //forAll(zoneToRegion, zoneI)
1676 // Info<< zoneI << '\t' << cellZones[zoneI].name() << '\t'
1677 // << zoneToRegion[zoneI] << nl;
1683 // Since we're going to mess with patches make sure all non-processor ones
1684 // are on all processors.
1685 mesh.boundaryMesh().checkParallelSync(true);
1688 // Sizes of interface between regions. From pair of regions to number of
1690 edgeList interfaces;
1691 EdgeMap<label> interfaceSizes;
1696 true, // sum in parallel?
1702 Info<< "Sizes inbetween regions:" << nl << nl
1703 << "Region\tRegion\tFaces" << nl
1704 << "------\t------\t-----" << endl;
1706 forAll(interfaces, interI)
1708 const edge& e = interfaces[interI];
1710 Info<< e[0] << '\t' << e[1] << '\t' << interfaceSizes[e] << nl;
1721 // Read objects in time directory
1722 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1724 IOobjectList objects(mesh, runTime.timeName());
1728 PtrList<volScalarField> vsFlds;
1729 ReadFields(mesh, objects, vsFlds);
1731 PtrList<volVectorField> vvFlds;
1732 ReadFields(mesh, objects, vvFlds);
1734 PtrList<volSphericalTensorField> vstFlds;
1735 ReadFields(mesh, objects, vstFlds);
1737 PtrList<volSymmTensorField> vsymtFlds;
1738 ReadFields(mesh, objects, vsymtFlds);
1740 PtrList<volTensorField> vtFlds;
1741 ReadFields(mesh, objects, vtFlds);
1743 // Read surface fields.
1745 PtrList<surfaceScalarField> ssFlds;
1746 ReadFields(mesh, objects, ssFlds);
1748 PtrList<surfaceVectorField> svFlds;
1749 ReadFields(mesh, objects, svFlds);
1751 PtrList<surfaceSphericalTensorField> sstFlds;
1752 ReadFields(mesh, objects, sstFlds);
1754 PtrList<surfaceSymmTensorField> ssymtFlds;
1755 ReadFields(mesh, objects, ssymtFlds);
1757 PtrList<surfaceTensorField> stFlds;
1758 ReadFields(mesh, objects, stFlds);
1763 // Remove any demand-driven fields ('S', 'V' etc)
1767 if (nCellRegions == 1)
1769 Info<< "Only one region. Doing nothing." << endl;
1771 else if (makeCellZones)
1773 Info<< "Putting cells into cellZones instead of splitting mesh."
1776 // Check if region overlaps with existing zone. If so keep.
1778 for (label regionI = 0; regionI < nCellRegions; regionI++)
1780 label zoneI = regionToZone[regionI];
1784 Info<< " Region " << regionI << " : corresponds to existing"
1786 << zoneI << ' ' << cellZones[zoneI].name() << endl;
1790 // Create new cellZone.
1791 labelList regionCells = findIndices(cellRegion, regionI);
1793 word zoneName = "region" + Foam::name(regionI);
1795 zoneI = cellZones.findZoneID(zoneName);
1799 zoneI = cellZones.size();
1800 mesh.cellZones().setSize(zoneI+1);
1801 mesh.cellZones().set
1807 regionCells, //addressing
1809 cellZones //cellZoneMesh
1815 mesh.cellZones()[zoneI].clearAddressing();
1816 mesh.cellZones()[zoneI] = regionCells;
1818 Info<< " Region " << regionI << " : created new cellZone "
1819 << zoneI << ' ' << cellZones[zoneI].name() << endl;
1822 mesh.cellZones().writeOpt() = IOobject::AUTO_WRITE;
1827 mesh.setInstance(runTime.timeName());
1831 mesh.setInstance(oldInstance);
1834 Info<< "Writing cellZones as new mesh to time " << runTime.timeName()
1840 // Write cellSets for convenience
1841 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1843 Info<< "Writing cellSets corresponding to cellZones." << nl << endl;
1845 forAll(cellZones, zoneI)
1847 const cellZone& cz = cellZones[zoneI];
1849 cellSet(mesh, cz.name(), cz).write();
1854 // Add patches for interfaces
1855 // ~~~~~~~~~~~~~~~~~~~~~~~~~~
1857 // Add all possible patches. Empty ones get filtered later on.
1858 Info<< nl << "Adding patches" << nl << endl;
1860 EdgeMap<label> interfaceToPatch
1885 point insidePoint(args.optionLookup("insidePoint")());
1889 label cellI = mesh.findCell(insidePoint);
1891 Info<< nl << "Found point " << insidePoint << " in cell " << cellI
1896 regionI = cellRegion[cellI];
1899 reduce(regionI, maxOp<label>());
1902 << "Subsetting region " << regionI
1903 << " containing point " << insidePoint << endl;
1907 FatalErrorIn(args.executable())
1908 << "Point " << insidePoint
1909 << " is not inside the mesh." << nl
1910 << "Bounding box of the mesh:" << mesh.bounds()
1911 << exit(FatalError);
1914 createAndWriteRegion
1921 (overwrite ? oldInstance : runTime.timeName())
1924 else if (largestOnly)
1926 label regionI = findMax(regionSizes);
1929 << "Subsetting region " << regionI
1930 << " of size " << regionSizes[regionI] << endl;
1932 createAndWriteRegion
1939 (overwrite ? oldInstance : runTime.timeName())
1945 for (label regionI = 0; regionI < nCellRegions; regionI++)
1948 << "Region " << regionI << nl
1949 << "-------- " << endl;
1951 createAndWriteRegion
1958 (overwrite ? oldInstance : runTime.timeName())
1964 Info<< "End\n" << endl;
1970 // ************************************************************************* //