BUGFIX: Uninitialised member variables
[foam-extend-3.2.git] / applications / utilities / parallelProcessing / redistributeMeshPar / redistributeMeshPar.C
blob505e604908f0d1c3451841d863f5acd0702947ae
1 /*---------------------------------------------------------------------------*\
2   =========                 |
3   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
4    \\    /   O peration     |
5     \\  /    A nd           | Copyright held by original author
6      \\/     M anipulation  |
7 -------------------------------------------------------------------------------
8 License
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
19     for more details.
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
25 Application
26     redistributeMeshPar
28 Description
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
38         mkdir processor0
39                 ..
40         mkdir processorN
42         # Copy undecomposed polyMesh
43         cp -r constant processor0
45         # Distribute
46         mpirun -np ddd redistributeMeshPar -parallel
48 \*---------------------------------------------------------------------------*/
50 #include "fvMesh.H"
51 #include "decompositionMethod.H"
52 #include "PstreamReduceOps.H"
53 #include "fvCFD.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
70     const Time& runTime,
71     const word& regionName,
72     const fileName& instDir,
73     const bool haveMesh
76     Pout<< "Create mesh for time = "
77         << runTime.timeName() << nl << endl;
79     IOobject io
80     (
81         regionName,
82         instDir,
83         runTime,
84         IOobject::MUST_READ
85     );
87     if (!haveMesh)
88     {
89         // Create dummy mesh. Only used on procs that don't have mesh.
90         fvMesh dummyMesh
91         (
92             io,
93             xferCopy(pointField()),
94             xferCopy(faceList()),
95             xferCopy(labelList()),
96             xferCopy(labelList()),
97             false
98         );
99         Pout<< "Writing dummy mesh to " << dummyMesh.polyMesh::objectPath()
100             << endl;
101         dummyMesh.write();
102     }
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())
111     {
112         // Send patches
113         for
114         (
115             int slave=Pstream::firstSlave();
116             slave<=Pstream::lastSlave();
117             slave++
118         )
119         {
120             OPstream toSlave(Pstream::blocking, slave);
121             toSlave << mesh.boundaryMesh();
122         }
123     }
124     else
125     {
126         // Receive patches
127         IPstream fromMaster(Pstream::blocking, Pstream::masterNo());
128         PtrList<entry> patchEntries(fromMaster);
130         if (haveMesh)
131         {
132             // Check master names against mine
134             const polyBoundaryMesh& patches = mesh.boundaryMesh();
136             forAll(patchEntries, patchI)
137             {
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)
143                 {
144                     break;
145                 }
147                 if (patchI >= patches.size())
148                 {
149                     FatalErrorIn
150                     (
151                         "createMesh(const Time&, const fileName&, const bool)"
152                     )   << "Non-processor patches not synchronised."
153                         << endl
154                         << "Processor " << Pstream::myProcNo()
155                         << " has only " << patches.size()
156                         << " patches, master has "
157                         << patchI
158                         << exit(FatalError);
159                 }
161                 if
162                 (
163                     type != patches[patchI].type()
164                  || name != patches[patchI].name()
165                 )
166                 {
167                     FatalErrorIn
168                     (
169                         "createMesh(const Time&, const fileName&, const bool)"
170                     )   << "Non-processor patches not synchronised."
171                         << endl
172                         << "Master patch " << patchI
173                         << " name:" << type
174                         << " type:" << type << endl
175                         << "Processor " << Pstream::myProcNo()
176                         << " patch " << patchI
177                         << " has name:" << patches[patchI].name()
178                         << " type:" << patches[patchI].type()
179                         << exit(FatalError);
180                 }
181             }
182         }
183         else
184         {
185             // Add patch
186             List<polyPatch*> patches(patchEntries.size());
187             label nPatches = 0;
189             forAll(patchEntries, patchI)
190             {
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)
196                 {
197                     break;
198                 }
200                 Pout<< "Adding patch:" << nPatches
201                     << " name:" << name
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
211                 (
212                     name,
213                     patchDict,
214                     nPatches++,
215                     mesh.boundaryMesh()
216                 ).ptr();
217             }
218             patches.setSize(nPatches);
219             mesh.addFvPatches(patches, false);  // no parallel comms
221             //// Write empty mesh now we have correct patches
222             //meshPtr().write();
223         }
224     }
226     if (!haveMesh)
227     {
228         // We created a dummy mesh file above. Delete it.
229         Pout<< "Removing dummy mesh " << io.objectPath()
230             << endl;
231         rmDir(io.objectPath());
232     }
234     // Force recreation of globalMeshData.
235     mesh.clearOut();
236     mesh.globalData();
238     return meshPtr;
242 // Get merging distance when matching face centres
243 scalar getMergeDistance
245     const argList& args,
246     const Time& runTime,
247     const boundBox& bb
250     scalar mergeTol = defaultMergeTol;
251     args.optionReadIfPresent("mergeTol", mergeTol);
253     scalar writeTol =
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)
260     {
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)."
269             << exit(FatalError);
270     }
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
277         << endl;
279     return mergeDist;
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
299     const word& name,
300     const fvMesh& mesh,
301     const labelList& decomp
304     Info<< "Writing wanted cell distribution to volScalarField " << name
305         << " for post-processing purposes." << nl << endl;
307     volScalarField procCells
308     (
309         IOobject
310         (
311             name,
312             mesh.time().timeName(),
313             mesh,
314             IOobject::NO_READ,
315             IOobject::AUTO_WRITE,
316             false                   // do not register
317         ),
318         mesh,
319         dimensionedScalar(name, dimless, -1),
320         zeroGradientFvPatchScalarField::typeName
321     );
323     forAll(procCells, cI)
324     {
325         procCells[cI] = decomp[cI];
326     }
327     procCells.write();
331 // Read vol or surface fields
332 //template<class T, class Mesh>
333 template<class GeoField>
334 void readFields
336     const boolList& haveMesh,
337     const fvMesh& mesh,
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();
349     // Get master names
350     wordList masterNames(objectNames);
351     Pstream::scatter(masterNames);
353     if (haveMesh[Pstream::myProcNo()] && objectNames != masterNames)
354     {
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);
361     }
363     fields.setSize(masterNames.size());
365     // Have master send all fields to processors that don't have a mesh
366     if (Pstream::master())
367     {
368         forAll(masterNames, i)
369         {
370             const word& name = masterNames[i];
371             IOobject& io = *objects[name];
372             io.writeOpt() = IOobject::AUTO_WRITE;
374             // Load field
375             fields.set(i, new GeoField(io, mesh));
377             // Create zero sized field and send
378             if (subsetterPtr.valid())
379             {
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++)
384                 {
385                     if (!haveMesh[procI])
386                     {
387                         OPstream toProc(Pstream::blocking, procI);
388                         toProc<< tsubfld();
389                     }
390                 }
391             }
392         }
393     }
394     else if (!haveMesh[Pstream::myProcNo()])
395     {
396         // Don't have mesh (nor fields). Receive empty field from master.
398         forAll(masterNames, i)
399         {
400             const word& name = masterNames[i];
402             // Receive field
403             IPstream fromMaster(Pstream::blocking, Pstream::masterNo());
405             fields.set
406             (
407                 i,
408                 new GeoField
409                 (
410                     IOobject
411                     (
412                         name,
413                         mesh.time().timeName(),
414                         mesh,
415                         IOobject::NO_READ,
416                         IOobject::AUTO_WRITE
417                     ),
418                     mesh,
419                     fromMaster
420                 )
421             );
423             //// Write it for next time round (since mesh gets written as well)
424             //fields[i].write();
425         }
426     }
427     else
428     {
429         // Have mesh so just try to load
430         forAll(masterNames, i)
431         {
432             const word& name = masterNames[i];
433             IOobject& io = *objects[name];
434             io.writeOpt() = IOobject::AUTO_WRITE;
436             // Load field
437             fields.set(i, new GeoField(io, mesh));
438         }
439     }
443 // Debugging: compare two fields.
444 void compareFields
446     const scalar tolDim,
447     const volVectorField& a,
448     const volVectorField& b
451     forAll(a, cellI)
452     {
453         if (mag(b[cellI] - a[cellI]) > tolDim)
454         {
455             FatalErrorIn
456             (
457                 "compareFields"
458                 "(const scalar, const volVectorField&, const volVectorField&)"
459             )   << "Did not map volVectorField correctly:" << nl
460                 << "cell:" << cellI
461                 << " transfer b:" << b[cellI]
462                 << " real cc:" << a[cellI]
463                 << abort(FatalError);
464         }
465     }
466     forAll(a.boundaryField(), patchI)
467     {
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())
478         {
479             forAll(aBoundary, i)
480             {
481                 if (mag(aBoundary[i] - bBoundary[i]) > tolDim)
482                 {
483                     FatalErrorIn
484                     (
485                         "compareFields"
486                         "(const scalar, const volVectorField&"
487                         ", const volVectorField&)"
488                     )   << "Did not map volVectorField correctly:"
489                         << endl
490                         << "patch:" << patchI << " patchFace:" << i
491                         << " cc:" << endl
492                         << "    real    :" << aBoundary[i] << endl
493                         << "    mapped  :" << bBoundary[i] << endl
494                         << abort(FatalError);
495                 }
496             }
497         }
498     }
502 // Main program:
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()))
515     //{
516     //    Pout<< "Creating case directory " << args.path() << endl;
517     //    mkDir(args.path());
518     //}
520 #   include "createTime.H"
522     word regionName = polyMesh::defaultRegion;
523     fileName meshSubDir;
525     if (args.optionReadIfPresent("region", regionName))
526     {
527         meshSubDir = regionName/polyMesh::meshSubDir;
528     }
529     else
530     {
531         meshSubDir = polyMesh::meshSubDir;
532     }
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())
541     {
542         masterInstDir = runTime.findInstance(meshSubDir, "points");
543     }
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);
559     // Create mesh
560     autoPtr<fvMesh> meshPtr = createMesh
561     (
562         runTime,
563         regionName,
564         masterInstDir,
565         haveMesh[Pstream::myProcNo()]
566     );
567     fvMesh& mesh = meshPtr();
569     Pout<< "Read mesh:" << endl;
570     printMeshData(Pout, mesh);
571     Pout<< endl;
573     IOdictionary decompositionDict
574     (
575         IOobject
576         (
577             "decomposeParDict",
578             runTime.system(),
579             mesh,
580             IOobject::MUST_READ,
581             IOobject::NO_WRITE
582         )
583     );
585     labelList finalDecomp;
587     // Create decompositionMethod and new decomposition
588     {
589         autoPtr<decompositionMethod> decomposer
590         (
591             decompositionMethod::New
592             (
593                 decompositionDict,
594                 mesh
595             )
596         );
598         if (!decomposer().parallelAware())
599         {
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."
608                 << endl;
609         }
611         finalDecomp = decomposer().decompose(mesh.cellCentres());
612     }
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
622     // processors
623     autoPtr<fvMeshSubset> subsetterPtr;
625     if (!allHaveMesh)
626     {
627         // Find last non-processor patch.
628         const polyBoundaryMesh& patches = mesh.boundaryMesh();
630         label nonProcI = -1;
632         forAll(patches, patchI)
633         {
634             if (isA<processorPolyPatch>(patches[patchI]))
635             {
636                 break;
637             }
638             nonProcI++;
639         }
641         if (nonProcI == -1)
642         {
643             FatalErrorIn(args.executable())
644                 << "Cannot find non-processor patch on processor "
645                 << Pstream::myProcNo() << endl
646                 << " Current patches:" << patches.names() << abort(FatalError);
647         }
649         // Subset 0 cells, no parallel comms. This is used to create zero-sized
650         // fields.
651         subsetterPtr.reset
652         (
653             new fvMeshSubset
654             (
655                 IOobject
656                 (
657                     "set",
658                     runTime.timeName(),
659                     mesh,
660                     IOobject::NO_READ,
661                     IOobject::NO_WRITE
662                 ),
663                 mesh
664             )
665         );
666         subsetterPtr().setLargeCellSubset(labelHashSet(0), nonProcI, false);
667     }
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())
676     {
677         objects.erase(iter);
678     }
680     PtrList<volScalarField> volScalarFields;
681     readFields
682     (
683         haveMesh,
684         mesh,
685         subsetterPtr,
686         objects,
687         volScalarFields
688     );
690     PtrList<volVectorField> volVectorFields;
691     readFields
692     (
693         haveMesh,
694         mesh,
695         subsetterPtr,
696         objects,
697         volVectorFields
698     );
700     PtrList<volSphericalTensorField> volSphereTensorFields;
701     readFields
702     (
703         haveMesh,
704         mesh,
705         subsetterPtr,
706         objects,
707         volSphereTensorFields
708     );
710     PtrList<volSymmTensorField> volSymmTensorFields;
711     readFields
712     (
713         haveMesh,
714         mesh,
715         subsetterPtr,
716         objects,
717         volSymmTensorFields
718     );
720     PtrList<volTensorField> volTensorFields;
721     readFields
722     (
723         haveMesh,
724         mesh,
725         subsetterPtr,
726         objects,
727         volTensorFields
728     );
730     PtrList<surfaceScalarField> surfScalarFields;
731     readFields
732     (
733         haveMesh,
734         mesh,
735         subsetterPtr,
736         objects,
737         surfScalarFields
738     );
740     PtrList<surfaceVectorField> surfVectorFields;
741     readFields
742     (
743         haveMesh,
744         mesh,
745         subsetterPtr,
746         objects,
747         surfVectorFields
748     );
750     PtrList<surfaceSphericalTensorField> surfSphereTensorFields;
751     readFields
752     (
753         haveMesh,
754         mesh,
755         subsetterPtr,
756         objects,
757         surfSphereTensorFields
758     );
760     PtrList<surfaceSymmTensorField> surfSymmTensorFields;
761     readFields
762     (
763         haveMesh,
764         mesh,
765         subsetterPtr,
766         objects,
767         surfSymmTensorFields
768     );
770     PtrList<surfaceTensorField> surfTensorFields;
771     readFields
772     (
773         haveMesh,
774         mesh,
775         subsetterPtr,
776         objects,
777         surfTensorFields
778     );
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
787     (
788         args,
789         runTime,
790         mesh.globalData().bb()
791     );
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);
806     // Print a bit
807     Pout<< "After distribution mesh:" << endl;
808     printMeshData(Pout, mesh);
809     Pout<< endl;
811     runTime++;
812     Pout<< "Writing redistributed mesh to " << runTime.timeName()
813         << nl << endl;
814     mesh.write();
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);
829     Info<< nl
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"
833         << nl
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)
840     {
841         fileName procDir = "processor" + name(procI);
843         if (nFaces[procI] == 0)
844         {
845             Info<< "    rm -r " << procDir.c_str() << nl;
846         }
847         else
848         {
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;
855         }
856     }
857     Info<< endl;
860     Pout<< "End\n" << endl;
862     return 0;
866 // ************************************************************************* //