1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright held by original author
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 Redistributes existing decomposed mesh and fields according to the current
30 settings in the decomposeParDict file.
32 Must be run on maximum number of source and destination processors.
33 Balances mesh and writes new mesh to new time directory.
35 Can also work like decomposePar:
37 # Create empty processors
42 # Copy undecomposed polyMesh
43 cp -r constant processor0
46 mpirun -np ddd redistributeMeshPar -parallel
48 \*---------------------------------------------------------------------------*/
51 #include "decompositionMethod.H"
52 #include "PstreamReduceOps.H"
54 #include "fvMeshDistribute.H"
55 #include "mapDistributePolyMesh.H"
56 #include "IOobjectList.H"
58 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
60 // Tolerance (as fraction of the bounding box). Needs to be fairly lax since
61 // usually meshes get written with limited precision (6 digits)
62 static const scalar defaultMergeTol = 1E-6;
65 // Read mesh if available. Otherwise create empty mesh with same non-proc
66 // patches as proc0 mesh. Requires all processors to have all patches
67 // (and in same order).
68 autoPtr<fvMesh> createMesh
71 const word& regionName,
72 const fileName& instDir,
76 Pout<< "Create mesh for time = "
77 << runTime.timeName() << nl << endl;
89 // Create dummy mesh. Only used on procs that don't have mesh.
93 xferCopy(pointField()),
95 xferCopy(labelList()),
96 xferCopy(labelList()),
99 Pout<< "Writing dummy mesh to " << dummyMesh.polyMesh::objectPath()
104 Pout<< "Reading mesh from " << io.objectPath() << endl;
105 autoPtr<fvMesh> meshPtr(new fvMesh(io));
106 fvMesh& mesh = meshPtr();
109 // Determine patches.
110 if (Pstream::master())
115 int slave=Pstream::firstSlave();
116 slave<=Pstream::lastSlave();
120 OPstream toSlave(Pstream::blocking, slave);
121 toSlave << mesh.boundaryMesh();
127 IPstream fromMaster(Pstream::blocking, Pstream::masterNo());
128 PtrList<entry> patchEntries(fromMaster);
132 // Check master names against mine
134 const polyBoundaryMesh& patches = mesh.boundaryMesh();
136 forAll(patchEntries, patchI)
138 const entry& e = patchEntries[patchI];
139 const word type(e.dict().lookup("type"));
140 const word& name = e.keyword();
142 if (type == processorPolyPatch::typeName)
147 if (patchI >= patches.size())
151 "createMesh(const Time&, const fileName&, const bool)"
152 ) << "Non-processor patches not synchronised."
154 << "Processor " << Pstream::myProcNo()
155 << " has only " << patches.size()
156 << " patches, master has "
163 type != patches[patchI].type()
164 || name != patches[patchI].name()
169 "createMesh(const Time&, const fileName&, const bool)"
170 ) << "Non-processor patches not synchronised."
172 << "Master patch " << patchI
174 << " type:" << type << endl
175 << "Processor " << Pstream::myProcNo()
176 << " patch " << patchI
177 << " has name:" << patches[patchI].name()
178 << " type:" << patches[patchI].type()
186 List<polyPatch*> patches(patchEntries.size());
189 forAll(patchEntries, patchI)
191 const entry& e = patchEntries[patchI];
192 const word type(e.dict().lookup("type"));
193 const word& name = e.keyword();
195 if (type == processorPolyPatch::typeName)
200 Pout<< "Adding patch:" << nPatches
202 << " type:" << type << endl;
204 dictionary patchDict(e.dict());
205 patchDict.remove("nFaces");
206 patchDict.add("nFaces", 0);
207 patchDict.remove("startFace");
208 patchDict.add("startFace", 0);
210 patches[patchI] = polyPatch::New
218 patches.setSize(nPatches);
219 mesh.addFvPatches(patches, false); // no parallel comms
221 //// Write empty mesh now we have correct patches
228 // We created a dummy mesh file above. Delete it.
229 Pout<< "Removing dummy mesh " << io.objectPath()
231 rmDir(io.objectPath());
234 // Force recreation of globalMeshData.
242 // Get merging distance when matching face centres
243 scalar getMergeDistance
250 scalar mergeTol = defaultMergeTol;
251 args.optionReadIfPresent("mergeTol", mergeTol);
254 Foam::pow(scalar(10.0), -scalar(IOstream::defaultPrecision()));
256 Info<< "Merge tolerance : " << mergeTol << nl
257 << "Write tolerance : " << writeTol << endl;
259 if (runTime.writeFormat() == IOstream::ASCII && mergeTol < writeTol)
261 FatalErrorIn("getMergeDistance")
262 << "Your current settings specify ASCII writing with "
263 << IOstream::defaultPrecision() << " digits precision." << endl
264 << "Your merging tolerance (" << mergeTol
265 << ") is finer than this." << endl
266 << "Please change your writeFormat to binary"
267 << " or increase the writePrecision" << endl
268 << "or adjust the merge tolerance (-mergeTol)."
272 scalar mergeDist = mergeTol * bb.mag();
274 Info<< "Overall meshes bounding box : " << bb << nl
275 << "Relative tolerance : " << mergeTol << nl
276 << "Absolute matching distance : " << mergeDist << nl
283 void printMeshData(Ostream& os, const polyMesh& mesh)
285 os << "Number of points: " << mesh.points().size() << nl
286 << " faces: " << mesh.faces().size() << nl
287 << " internal faces: " << mesh.faceNeighbour().size() << nl
288 << " cells: " << mesh.cells().size() << nl
289 << " boundary patches: " << mesh.boundaryMesh().size() << nl
290 << " point zones: " << mesh.pointZones().size() << nl
291 << " face zones: " << mesh.faceZones().size() << nl
292 << " cell zones: " << mesh.cellZones().size() << nl;
296 // Debugging: write volScalarField with decomposition for post-processing.
297 void writeDecomposition
301 const labelList& decomp
304 Info<< "Writing wanted cell distribution to volScalarField " << name
305 << " for post-processing purposes." << nl << endl;
307 volScalarField procCells
312 mesh.time().timeName(),
315 IOobject::AUTO_WRITE,
316 false // do not register
319 dimensionedScalar(name, dimless, -1),
320 zeroGradientFvPatchScalarField::typeName
323 forAll(procCells, cI)
325 procCells[cI] = decomp[cI];
331 // Read vol or surface fields
332 //template<class T, class Mesh>
333 template<class GeoField>
336 const boolList& haveMesh,
338 const autoPtr<fvMeshSubset>& subsetterPtr,
339 IOobjectList& allObjects,
340 PtrList<GeoField>& fields
343 //typedef GeometricField<T, fvPatchField, Mesh> fldType;
345 // Get my objects of type
346 IOobjectList objects(allObjects.lookupClass(GeoField::typeName));
347 // Check that we all have all objects
348 wordList objectNames = objects.toc();
350 wordList masterNames(objectNames);
351 Pstream::scatter(masterNames);
353 if (haveMesh[Pstream::myProcNo()] && objectNames != masterNames)
355 FatalErrorIn("readFields()")
356 << "differing fields of type " << GeoField::typeName
357 << " on processors." << endl
358 << "Master has:" << masterNames << endl
359 << Pstream::myProcNo() << " has:" << objectNames
360 << abort(FatalError);
363 fields.setSize(masterNames.size());
365 // Have master send all fields to processors that don't have a mesh
366 if (Pstream::master())
368 forAll(masterNames, i)
370 const word& name = masterNames[i];
371 IOobject& io = *objects[name];
372 io.writeOpt() = IOobject::AUTO_WRITE;
375 fields.set(i, new GeoField(io, mesh));
377 // Create zero sized field and send
378 if (subsetterPtr.valid())
380 tmp<GeoField> tsubfld = subsetterPtr().interpolate(fields[i]);
382 // Send to all processors that don't have a mesh
383 for (label procI = 1; procI < Pstream::nProcs(); procI++)
385 if (!haveMesh[procI])
387 OPstream toProc(Pstream::blocking, procI);
394 else if (!haveMesh[Pstream::myProcNo()])
396 // Don't have mesh (nor fields). Receive empty field from master.
398 forAll(masterNames, i)
400 const word& name = masterNames[i];
403 IPstream fromMaster(Pstream::blocking, Pstream::masterNo());
413 mesh.time().timeName(),
423 //// Write it for next time round (since mesh gets written as well)
429 // Have mesh so just try to load
430 forAll(masterNames, i)
432 const word& name = masterNames[i];
433 IOobject& io = *objects[name];
434 io.writeOpt() = IOobject::AUTO_WRITE;
437 fields.set(i, new GeoField(io, mesh));
443 // Debugging: compare two fields.
447 const volVectorField& a,
448 const volVectorField& b
453 if (mag(b[cellI] - a[cellI]) > tolDim)
458 "(const scalar, const volVectorField&, const volVectorField&)"
459 ) << "Did not map volVectorField correctly:" << nl
461 << " transfer b:" << b[cellI]
462 << " real cc:" << a[cellI]
463 << abort(FatalError);
466 forAll(a.boundaryField(), patchI)
468 // We have real mesh cellcentre and
469 // mapped original cell centre.
471 const fvPatchVectorField& aBoundary =
472 a.boundaryField()[patchI];
474 const fvPatchVectorField& bBoundary =
475 b.boundaryField()[patchI];
477 if (!bBoundary.coupled())
481 if (mag(aBoundary[i] - bBoundary[i]) > tolDim)
486 "(const scalar, const volVectorField&"
487 ", const volVectorField&)"
488 ) << "Did not map volVectorField correctly:"
490 << "patch:" << patchI << " patchFace:" << i
492 << " real :" << aBoundary[i] << endl
493 << " mapped :" << bBoundary[i] << endl
494 << abort(FatalError);
504 int main(int argc, char *argv[])
506 # include "addRegionOption.H"
507 argList::validOptions.insert("mergeTol", "relative merge distance");
509 // Create argList. This will check for non-existing processor dirs.
510 # include "setRootCase.H"
512 //- Not useful anymore. See above.
513 //// Create processor directory if non-existing
514 //if (!Pstream::master() && !isDir(args.path()))
516 // Pout<< "Creating case directory " << args.path() << endl;
517 // mkDir(args.path());
520 # include "createTime.H"
522 word regionName = polyMesh::defaultRegion;
525 if (args.optionReadIfPresent("region", regionName))
527 meshSubDir = regionName/polyMesh::meshSubDir;
531 meshSubDir = polyMesh::meshSubDir;
533 Info<< "Using mesh subdirectory " << meshSubDir << nl << endl;
536 // Get time instance directory. Since not all processors have meshes
537 // just use the master one everywhere.
539 fileName masterInstDir;
540 if (Pstream::master())
542 masterInstDir = runTime.findInstance(meshSubDir, "points");
544 Pstream::scatter(masterInstDir);
546 // Check who has a mesh
547 const fileName meshPath = runTime.path()/masterInstDir/meshSubDir;
549 Info<< "Found points in " << meshPath << nl << endl;
552 boolList haveMesh(Pstream::nProcs(), false);
553 haveMesh[Pstream::myProcNo()] = isDir(meshPath);
554 Pstream::gatherList(haveMesh);
555 Pstream::scatterList(haveMesh);
556 Info<< "Per processor mesh availability : " << haveMesh << endl;
557 const bool allHaveMesh = (findIndex(haveMesh, false) == -1);
560 autoPtr<fvMesh> meshPtr = createMesh
565 haveMesh[Pstream::myProcNo()]
567 fvMesh& mesh = meshPtr();
569 Pout<< "Read mesh:" << endl;
570 printMeshData(Pout, mesh);
573 IOdictionary decompositionDict
585 labelList finalDecomp;
587 // Create decompositionMethod and new decomposition
589 autoPtr<decompositionMethod> decomposer
591 decompositionMethod::New
598 if (!decomposer().parallelAware())
600 WarningIn(args.executable())
601 << "You have selected decomposition method "
602 << decomposer().typeName
603 << " which does" << endl
604 << "not synchronise the decomposition across"
605 << " processor patches." << endl
606 << " You might want to select a decomposition method which"
607 << " is aware of this. Continuing."
611 finalDecomp = decomposer().decompose(mesh.cellCentres());
614 // Dump decomposition to volScalarField
615 writeDecomposition("decomposition", mesh, finalDecomp);
618 // Create 0 sized mesh to do all the generation of zero sized
619 // fields on processors that have zero sized meshes. Note that this is
620 // only nessecary on master but since polyMesh construction with
621 // Pstream::parRun does parallel comms we have to do it on all
623 autoPtr<fvMeshSubset> subsetterPtr;
627 // Find last non-processor patch.
628 const polyBoundaryMesh& patches = mesh.boundaryMesh();
632 forAll(patches, patchI)
634 if (isA<processorPolyPatch>(patches[patchI]))
643 FatalErrorIn(args.executable())
644 << "Cannot find non-processor patch on processor "
645 << Pstream::myProcNo() << endl
646 << " Current patches:" << patches.names() << abort(FatalError);
649 // Subset 0 cells, no parallel comms. This is used to create zero-sized
666 subsetterPtr().setLargeCellSubset(labelHashSet(0), nonProcI, false);
670 // Get original objects (before incrementing time!)
671 IOobjectList objects(mesh, runTime.timeName());
672 // We don't want to map the decomposition (mapping already tested when
673 // mapping the cell centre field)
674 IOobjectList::iterator iter = objects.find("decomposition");
675 if (iter != objects.end())
680 PtrList<volScalarField> volScalarFields;
690 PtrList<volVectorField> volVectorFields;
700 PtrList<volSphericalTensorField> volSphereTensorFields;
707 volSphereTensorFields
710 PtrList<volSymmTensorField> volSymmTensorFields;
720 PtrList<volTensorField> volTensorFields;
730 PtrList<surfaceScalarField> surfScalarFields;
740 PtrList<surfaceVectorField> surfVectorFields;
750 PtrList<surfaceSphericalTensorField> surfSphereTensorFields;
757 surfSphereTensorFields
760 PtrList<surfaceSymmTensorField> surfSymmTensorFields;
770 PtrList<surfaceTensorField> surfTensorFields;
781 // Debugging: Create additional volField that will be mapped.
782 // Used to test correctness of mapping
783 volVectorField mapCc("mapCc", 1*mesh.C());
785 // Global matching tolerance
786 const scalar tolDim = getMergeDistance
790 mesh.globalData().bb()
793 // Mesh distribution engine
794 fvMeshDistribute distributor(mesh, tolDim);
796 Pout<< "Wanted distribution:"
797 << distributor.countCells(finalDecomp) << nl << endl;
799 // Do actual sending/receiving of mesh
800 autoPtr<mapDistributePolyMesh> map = distributor.distribute(finalDecomp);
802 //// Distribute any non-registered data accordingly
803 //map().distributeFaceData(faceCc);
807 Pout<< "After distribution mesh:" << endl;
808 printMeshData(Pout, mesh);
812 Pout<< "Writing redistributed mesh to " << runTime.timeName()
817 // Debugging: test mapped cellcentre field.
818 compareFields(tolDim, mesh.C(), mapCc);
820 // Print nice message
821 // ~~~~~~~~~~~~~~~~~~
823 // Determine which processors remain so we can print nice final message.
824 labelList nFaces(Pstream::nProcs());
825 nFaces[Pstream::myProcNo()] = mesh.nFaces();
826 Pstream::gatherList(nFaces);
827 Pstream::scatterList(nFaces);
830 << "You can pick up the redecomposed mesh from the polyMesh directory"
831 << " in " << runTime.timeName() << "." << nl
832 << "If you redecomposed the mesh to less processors you can delete"
834 << "the processor directories with 0 sized meshes in them." << nl
835 << "Below is a sample set of commands to do this."
836 << " Take care when issuing these" << nl
837 << "commands." << nl << endl;
839 forAll(nFaces, procI)
841 fileName procDir = "processor" + name(procI);
843 if (nFaces[procI] == 0)
845 Info<< " rm -r " << procDir.c_str() << nl;
849 fileName timeDir = procDir/runTime.timeName()/meshSubDir;
850 fileName constDir = procDir/runTime.constant()/meshSubDir;
852 Info<< " rm -r " << constDir.c_str() << nl
853 << " mv " << timeDir.c_str()
854 << ' ' << constDir.c_str() << nl;
860 Pout<< "End\n" << endl;
866 // ************************************************************************* //