1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
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
13 the Free Software Foundation, either version 3 of the License, or
14 (at your 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, see <http://www.gnu.org/licenses/>.
28 Reconstructs a mesh using geometric information only.
30 Writes point/face/cell procAddressing so afterwards reconstructPar can be
31 used to reconstruct fields.
34 - uses geometric matching tolerance (set with -mergeTol (at your option)
36 If the parallel case does not have correct procBoundaries use the
37 -fullMatch option which will check all boundary faces (bit slower).
39 \*---------------------------------------------------------------------------*/
43 #include "IOobjectList.H"
44 #include "labelIOList.H"
45 #include "processorPolyPatch.H"
46 #include "mapAddedPolyMesh.H"
47 #include "polyMeshAdder.H"
48 #include "faceCoupleInfo.H"
49 #include "fvMeshAdder.H"
50 #include "polyTopoChange.H"
54 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
56 // Tolerance (as fraction of the bounding box). Needs to be fairly lax since
57 // usually meshes get written with limited precision (6 digits)
58 static const scalar defaultMergeTol = 1E-7;
71 elems[i] = map[elems[i]];
77 // Determine which faces are coupled. Uses geometric merge distance.
78 // Looks either at all boundaryFaces (fullMatch) or only at the
79 // procBoundaries for procI. Assumes that masterMesh contains already merged
80 // all the processors < procI.
81 autoPtr<faceCoupleInfo> determineCoupledFaces
85 const polyMesh& masterMesh,
86 const polyMesh& meshToAdd,
87 const scalar mergeDist
90 if (fullMatch || masterMesh.nCells() == 0)
92 return autoPtr<faceCoupleInfo>
98 mergeDist, // absolute merging distance
99 true // matching faces identical
105 // Pick up all patches on masterMesh ending in "toDDD" where DDD is
106 // the processor number procI.
108 const polyBoundaryMesh& masterPatches = masterMesh.boundaryMesh();
110 const string toProcString("to" + name(procI));
112 DynamicList<label> masterFaces
115 - masterMesh.nInternalFaces()
118 forAll(masterPatches, patchI)
120 const polyPatch& pp = masterPatches[patchI];
124 isA<processorPolyPatch>(pp)
126 pp.name().rfind(toProcString)
127 == (pp.name().size()-toProcString.size())
131 label meshFaceI = pp.start();
134 masterFaces.append(meshFaceI++);
138 masterFaces.shrink();
141 // Pick up all patches on meshToAdd ending in "procBoundaryDDDtoYYY"
142 // where DDD is the processor number procI and YYY is < procI.
144 const polyBoundaryMesh& addPatches = meshToAdd.boundaryMesh();
146 DynamicList<label> addFaces
149 - meshToAdd.nInternalFaces()
152 forAll(addPatches, patchI)
154 const polyPatch& pp = addPatches[patchI];
156 if (isA<processorPolyPatch>(pp))
158 bool isConnected = false;
160 for (label mergedProcI = 0; mergedProcI < procI; mergedProcI++)
162 const string fromProcString
170 if (pp.name() == fromProcString)
179 label meshFaceI = pp.start();
182 addFaces.append(meshFaceI++);
189 return autoPtr<faceCoupleInfo>
197 mergeDist, // absolute merging distance
198 true, // matching faces identical?
199 false, // if perfectmatch are faces already ordered
200 // (e.g. processor patches)
201 false // are faces each on separate patch?
208 autoPtr<mapPolyMesh> mergeSharedPoints
210 const scalar mergeDist,
212 labelListList& pointProcAddressing
215 // Find out which sets of points get merged and create a map from
216 // mesh point to unique point.
217 Map<label> pointToMaster
219 fvMeshAdder::findSharedPoints
226 Info<< "mergeSharedPoints : detected " << pointToMaster.size()
227 << " points that are to be merged." << endl;
229 if (returnReduce(pointToMaster.size(), sumOp<label>()) == 0)
231 return autoPtr<mapPolyMesh>(NULL);
234 polyTopoChange meshMod(mesh);
236 fvMeshAdder::mergePoints(mesh, pointToMaster, meshMod);
238 // Change the mesh (no inflation). Note: parallel comms allowed.
239 autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh, false, true);
241 // Update fields. No inflation, parallel sync.
242 mesh.updateMesh(map);
244 // pointProcAddressing give indices into the master mesh so adapt them
245 // for changed point numbering.
247 // Adapt constructMaps for merged points.
248 forAll(pointProcAddressing, procI)
250 labelList& constructMap = pointProcAddressing[procI];
252 forAll(constructMap, i)
254 label oldPointI = constructMap[i];
256 // New label of point after changeMesh.
257 label newPointI = map().reversePointMap()[oldPointI];
261 constructMap[i] = -newPointI-2;
263 else if (newPointI >= 0)
265 constructMap[i] = newPointI;
269 FatalErrorIn("fvMeshDistribute::mergeSharedPoints()")
270 << "Problem. oldPointI:" << oldPointI
271 << " newPointI:" << newPointI << abort(FatalError);
280 int main(int argc, char *argv[])
284 "reconstruct a mesh using geometric information only"
287 argList::noParallel();
292 "specify the merge distance relative to the bounding box size "
295 argList::addBoolOption
298 "do (slower) geometric matching on all boundary faces"
301 #include "addTimeOptions.H"
302 #include "addRegionOption.H"
303 #include "setRootCase.H"
304 #include "createTime.H"
306 Info<< "This is an experimental tool which tries to merge"
307 << " individual processor" << nl
308 << "meshes back into one master mesh. Use it if the original"
309 << " master mesh has" << nl
310 << "been deleted or if the processor meshes have been modified"
311 << " (topology change)." << nl
312 << "This tool will write the resulting mesh to a new time step"
313 << " and construct" << nl
314 << "xxxxProcAddressing files in the processor meshes so"
315 << " reconstructPar can be" << nl
316 << "used to regenerate the fields on the master mesh." << nl
318 << "Not well tested & use at your own risk!" << nl
322 word regionName = polyMesh::defaultRegion;
323 word regionDir = word::null;
325 if (args.optionReadIfPresent("region", regionName))
327 regionDir = regionName;
328 Info<< "Operating on region " << regionName << nl << endl;
331 scalar mergeTol = defaultMergeTol;
332 args.optionReadIfPresent("mergeTol", mergeTol);
334 scalar writeTol = Foam::pow(10.0, -scalar(IOstream::defaultPrecision()));
336 Info<< "Merge tolerance : " << mergeTol << nl
337 << "Write tolerance : " << writeTol << endl;
339 if (runTime.writeFormat() == IOstream::ASCII && mergeTol < writeTol)
341 FatalErrorIn(args.executable())
342 << "Your current settings specify ASCII writing with "
343 << IOstream::defaultPrecision() << " digits precision." << endl
344 << "Your merging tolerance (" << mergeTol << ") is finer than this."
346 << "Please change your writeFormat to binary"
347 << " or increase the writePrecision" << endl
348 << "or adjust the merge tolerance (-mergeTol)."
353 const bool fullMatch = args.optionFound("fullMatch");
357 Info<< "Doing geometric matching on all boundary faces." << nl << endl;
361 Info<< "Doing geometric matching on correct procBoundaries only."
362 << nl << "This assumes a correct decomposition." << endl;
375 / fileName(word("processor") + name(nProcs))
382 Info<< "Found " << nProcs << " processor directories" << nl << endl;
385 // Read all databases.
386 PtrList<Time> databases(nProcs);
388 forAll(databases, procI)
390 Info<< "Reading database "
391 << args.caseName()/fileName(word("processor") + name(procI))
399 Time::controlDictName,
401 args.caseName()/fileName(word("processor") + name(procI))
405 Time& procTime = databases[procI];
407 instantList Times = procTime.times();
409 // set startTime and endTime depending on -time and -latestTime options
410 # include "checkTimeOptions.H"
412 procTime.setTime(Times[startTime], startTime);
414 if (procI > 0 && databases[procI-1].value() != procTime.value())
416 FatalErrorIn(args.executable())
417 << "Time not equal on processors." << nl
418 << "Processor:" << procI-1
419 << " time:" << databases[procI-1].value() << nl
420 << "Processor:" << procI
421 << " time:" << procTime.value()
427 Info<< "Setting master time to " << databases[0].timeName() << nl << endl;
428 runTime.setTime(databases[0]);
431 // Read point on individual processors to determine merge tolerance
432 // (otherwise single cell domains might give problems)
434 boundBox bb = boundBox::invertedBox;
436 for (label procI = 0; procI < nProcs; procI++)
438 fileName pointsInstance
440 databases[procI].findInstance
442 regionDir/polyMesh::meshSubDir,
447 if (pointsInstance != databases[procI].timeName())
449 FatalErrorIn(args.executable())
450 << "Your time was specified as " << databases[procI].timeName()
451 << " but there is no polyMesh/points in that time." << endl
452 << "(there is a points file in " << pointsInstance
454 << "Please rerun with the correct time specified"
455 << " (through the -constant, -time or -latestTime "
456 << "(at your option)."
457 << endl << exit(FatalError);
460 Info<< "Reading points from "
461 << databases[procI].caseName()
462 << " for time = " << databases[procI].timeName()
470 databases[procI].findInstance
472 regionDir/polyMesh::meshSubDir,
475 regionDir/polyMesh::meshSubDir,
483 boundBox domainBb(points, false);
485 bb.min() = min(bb.min(), domainBb.min());
486 bb.max() = max(bb.max(), domainBb.max());
488 const scalar mergeDist = mergeTol * bb.mag();
490 Info<< "Overall mesh bounding box : " << bb << nl
491 << "Relative tolerance : " << mergeTol << nl
492 << "Absolute matching distance : " << mergeDist << nl
496 // Addressing from processor to reconstructed case
497 labelListList cellProcAddressing(nProcs);
498 labelListList faceProcAddressing(nProcs);
499 labelListList pointProcAddressing(nProcs);
500 labelListList boundaryProcAddressing(nProcs);
502 // Internal faces on the final reconstructed mesh
503 label masterInternalFaces;
504 // Owner addressing on the final reconstructed mesh
505 labelList masterOwner;
508 // Construct empty mesh.
509 Info<< "Constructing empty mesh to add to." << nl << endl;
519 xferCopy(pointField()),
520 xferCopy(faceList()),
524 for (label procI = 0; procI < nProcs; procI++)
526 Info<< "Reading mesh to add from "
527 << databases[procI].caseName()
528 << " for time = " << databases[procI].timeName()
536 databases[procI].timeName(),
541 // Initialize its addressing
542 cellProcAddressing[procI] = identity(meshToAdd.nCells());
543 faceProcAddressing[procI] = identity(meshToAdd.nFaces());
544 pointProcAddressing[procI] = identity(meshToAdd.nPoints());
545 boundaryProcAddressing[procI] =
546 identity(meshToAdd.boundaryMesh().size());
549 // Find geometrically shared points/faces.
550 autoPtr<faceCoupleInfo> couples = determineCoupledFaces
560 // Add elements to mesh
561 Info<< "Adding to master mesh" << nl << endl;
563 autoPtr<mapAddedPolyMesh> map = polyMeshAdder::add
570 // Update all addressing so xxProcAddressing points to correct item
573 // Processors that were already in masterMesh
574 for (label mergedI = 0; mergedI < procI; mergedI++)
576 renumber(map().oldCellMap(), cellProcAddressing[mergedI]);
577 renumber(map().oldFaceMap(), faceProcAddressing[mergedI]);
578 renumber(map().oldPointMap(), pointProcAddressing[mergedI]);
579 // Note: boundary is special since can contain -1.
580 renumber(map().oldPatchMap(), boundaryProcAddressing[mergedI]);
584 renumber(map().addedCellMap(), cellProcAddressing[procI]);
585 renumber(map().addedFaceMap(), faceProcAddressing[procI]);
586 renumber(map().addedPointMap(), pointProcAddressing[procI]);
587 renumber(map().addedPatchMap(), boundaryProcAddressing[procI]);
592 // See if any points on the mastermesh have become connected
593 // because of connections through processor meshes.
594 mergeSharedPoints(mergeDist, masterMesh, pointProcAddressing);
596 // Save some properties on the reconstructed mesh
597 masterInternalFaces = masterMesh.nInternalFaces();
598 masterOwner = masterMesh.faceOwner();
601 Info<< "\nWriting merged mesh to "
602 << runTime.path()/runTime.timeName()
605 if (!masterMesh.write())
607 FatalErrorIn(args.executable())
608 << "Failed writing polyMesh."
614 // Write the addressing
616 Info<< "Reconstructing the addressing from the processor meshes"
617 << " to the newly reconstructed mesh" << nl << endl;
619 forAll(databases, procI)
621 Info<< "Reading processor " << procI << " mesh from "
622 << databases[procI].caseName() << endl;
629 databases[procI].timeName(),
635 // From processor point to reconstructed mesh point
637 Info<< "Writing pointProcAddressing to "
638 << databases[procI].caseName()
639 /procMesh.facesInstance()
640 /polyMesh::meshSubDir
647 "pointProcAddressing",
648 procMesh.facesInstance(),
649 polyMesh::meshSubDir,
653 false // do not register
655 pointProcAddressing[procI]
659 // From processor face to reconstructed mesh face
661 Info<< "Writing faceProcAddressing to "
662 << databases[procI].caseName()
663 /procMesh.facesInstance()
664 /polyMesh::meshSubDir
667 labelIOList faceProcAddr
671 "faceProcAddressing",
672 procMesh.facesInstance(),
673 polyMesh::meshSubDir,
677 false // do not register
679 faceProcAddressing[procI]
682 // Now add turning index to faceProcAddressing.
683 // See reconstrurPar for meaning of turning index.
684 forAll(faceProcAddr, procFaceI)
686 label masterFaceI = faceProcAddr[procFaceI];
690 !procMesh.isInternalFace(procFaceI)
691 && masterFaceI < masterInternalFaces
694 // proc face is now external but used to be internal face.
695 // Check if we have owner or neighbour.
697 label procOwn = procMesh.faceOwner()[procFaceI];
698 label masterOwn = masterOwner[masterFaceI];
700 if (cellProcAddressing[procI][procOwn] == masterOwn)
702 // No turning. Offset by 1.
703 faceProcAddr[procFaceI]++;
708 faceProcAddr[procFaceI] =
709 -1 - faceProcAddr[procFaceI];
714 // No turning. Offset by 1.
715 faceProcAddr[procFaceI]++;
719 faceProcAddr.write();
722 // From processor cell to reconstructed mesh cell
724 Info<< "Writing cellProcAddressing to "
725 << databases[procI].caseName()
726 /procMesh.facesInstance()
727 /polyMesh::meshSubDir
734 "cellProcAddressing",
735 procMesh.facesInstance(),
736 polyMesh::meshSubDir,
740 false // do not register
742 cellProcAddressing[procI]
747 // From processor patch to reconstructed mesh patch
749 Info<< "Writing boundaryProcAddressing to "
750 << databases[procI].caseName()
751 /procMesh.facesInstance()
752 /polyMesh::meshSubDir
759 "boundaryProcAddressing",
760 procMesh.facesInstance(),
761 polyMesh::meshSubDir,
765 false // do not register
767 boundaryProcAddressing[procI]
773 Info<< "End.\n" << endl;
779 // ************************************************************************* //