1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 1991-2010 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
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 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[])
282 argList::noParallel();
283 argList::validOptions.insert("mergeTol", "relative merge distance");
284 argList::validOptions.insert("fullMatch", "");
286 # include "addTimeOptions.H"
287 # include "addRegionOption.H"
288 # include "setRootCase.H"
289 # include "createTime.H"
291 Info<< "This is an experimental tool which tries to merge"
292 << " individual processor" << nl
293 << "meshes back into one master mesh. Use it if the original"
294 << " master mesh has" << nl
295 << "been deleted or if the processor meshes have been modified"
296 << " (topology change)." << nl
297 << "This tool will write the resulting mesh to a new time step"
298 << " and construct" << nl
299 << "xxxxProcAddressing files in the processor meshes so"
300 << " reconstructPar can be" << nl
301 << "used to regenerate the fields on the master mesh." << nl
303 << "Not well tested & use at your own risk!" << nl
307 word regionName = polyMesh::defaultRegion;
308 fileName regionPrefix = "";
309 if (args.optionFound("region"))
311 regionName = args.option("region");
312 regionPrefix = regionName;
313 Info<< "Operating on region " << regionName << nl << endl;
316 scalar mergeTol = defaultMergeTol;
317 args.optionReadIfPresent("mergeTol", mergeTol);
319 scalar writeTol = Foam::pow(10.0, -scalar(IOstream::defaultPrecision()));
321 Info<< "Merge tolerance : " << mergeTol << nl
322 << "Write tolerance : " << writeTol << endl;
324 if (runTime.writeFormat() == IOstream::ASCII && mergeTol < writeTol)
326 FatalErrorIn(args.executable())
327 << "Your current settings specify ASCII writing with "
328 << IOstream::defaultPrecision() << " digits precision." << endl
329 << "Your merging tolerance (" << mergeTol << ") is finer than this."
331 << "Please change your writeFormat to binary"
332 << " or increase the writePrecision" << endl
333 << "or adjust the merge tolerance (-mergeTol)."
338 const bool fullMatch = args.optionFound("fullMatch");
342 Info<< "Doing geometric matching on all boundary faces." << nl << endl;
346 Info<< "Doing geometric matching on correct procBoundaries only."
347 << nl << "This assumes a correct decomposition." << endl;
360 / fileName(word("processor") + name(nProcs))
367 Info<< "Found " << nProcs << " processor directories" << nl << endl;
370 // Read all databases.
371 PtrList<Time> databases(nProcs);
373 forAll (databases, procI)
375 Info<< "Reading database "
376 << args.caseName()/fileName(word("processor") + name(procI))
384 Time::controlDictName,
386 args.caseName()/fileName(word("processor") + name(procI))
390 Time& procTime = databases[procI];
392 instantList Times = procTime.times();
394 // set startTime and endTime depending on -time and -latestTime options
395 # include "checkTimeOptions.H"
397 procTime.setTime(Times[startTime], startTime);
399 if (procI > 0 && databases[procI-1].value() != procTime.value())
401 FatalErrorIn(args.executable())
402 << "Time not equal on processors." << nl
403 << "Processor:" << procI-1
404 << " time:" << databases[procI-1].value() << nl
405 << "Processor:" << procI
406 << " time:" << procTime.value()
412 Info<< "Setting master time to " << databases[0].timeName() << nl << endl;
413 runTime.setTime(databases[0]);
416 // Read point on individual processors to determine merge tolerance
417 // (otherwise single cell domains might give problems)
419 boundBox bb = boundBox::invertedBox;
421 for (label procI = 0; procI < nProcs; procI++)
423 fileName pointsInstance
425 databases[procI].findInstance
427 regionPrefix/polyMesh::meshSubDir,
432 if (pointsInstance != databases[procI].timeName())
434 FatalErrorIn(args.executable())
435 << "Your time was specified as " << databases[procI].timeName()
436 << " but there is no polyMesh/points in that time." << endl
437 << "(there is a points file in " << pointsInstance
439 << "Please rerun with the correct time specified"
440 << " (through the -constant, -time or -latestTime option)."
441 << endl << exit(FatalError);
444 Info<< "Reading points from "
445 << databases[procI].caseName()
446 << " for time = " << databases[procI].timeName()
454 databases[procI].findInstance
456 regionPrefix/polyMesh::meshSubDir,
459 regionPrefix/polyMesh::meshSubDir,
467 boundBox domainBb(points, false);
469 bb.min() = min(bb.min(), domainBb.min());
470 bb.max() = max(bb.max(), domainBb.max());
472 const scalar mergeDist = mergeTol * bb.mag();
474 Info<< "Overall mesh bounding box : " << bb << nl
475 << "Relative tolerance : " << mergeTol << nl
476 << "Absolute matching distance : " << mergeDist << nl
480 // Addressing from processor to reconstructed case
481 labelListList cellProcAddressing(nProcs);
482 labelListList faceProcAddressing(nProcs);
483 labelListList pointProcAddressing(nProcs);
484 labelListList boundaryProcAddressing(nProcs);
486 // Internal faces on the final reconstructed mesh
487 label masterInternalFaces;
488 // Owner addressing on the final reconstructed mesh
489 labelList masterOwner;
492 // Construct empty mesh.
493 Info<< "Constructing empty mesh to add to." << nl << endl;
503 xferCopy(pointField()),
504 xferCopy(faceList()),
508 for (label procI = 0; procI < nProcs; procI++)
510 Info<< "Reading mesh to add from "
511 << databases[procI].caseName()
512 << " for time = " << databases[procI].timeName()
520 databases[procI].timeName(),
525 // Initialize its addressing
526 cellProcAddressing[procI] = identity(meshToAdd.nCells());
527 faceProcAddressing[procI] = identity(meshToAdd.nFaces());
528 pointProcAddressing[procI] = identity(meshToAdd.nPoints());
529 boundaryProcAddressing[procI] =
530 identity(meshToAdd.boundaryMesh().size());
533 // Find geometrically shared points/faces.
534 autoPtr<faceCoupleInfo> couples = determineCoupledFaces
544 // Add elements to mesh
545 Info<< "Adding to master mesh" << nl << endl;
547 autoPtr<mapAddedPolyMesh> map = polyMeshAdder::add
554 // Update all addressing so xxProcAddressing points to correct item
557 // Processors that were already in masterMesh
558 for (label mergedI = 0; mergedI < procI; mergedI++)
560 renumber(map().oldCellMap(), cellProcAddressing[mergedI]);
561 renumber(map().oldFaceMap(), faceProcAddressing[mergedI]);
562 renumber(map().oldPointMap(), pointProcAddressing[mergedI]);
563 // Note: boundary is special since can contain -1.
564 renumber(map().oldPatchMap(), boundaryProcAddressing[mergedI]);
568 renumber(map().addedCellMap(), cellProcAddressing[procI]);
569 renumber(map().addedFaceMap(), faceProcAddressing[procI]);
570 renumber(map().addedPointMap(), pointProcAddressing[procI]);
571 renumber(map().addedPatchMap(), boundaryProcAddressing[procI]);
576 // See if any points on the mastermesh have become connected
577 // because of connections through processor meshes.
578 mergeSharedPoints(mergeDist, masterMesh, pointProcAddressing);
580 // Save some properties on the reconstructed mesh
581 masterInternalFaces = masterMesh.nInternalFaces();
582 masterOwner = masterMesh.faceOwner();
585 Info<< "\nWriting merged mesh to "
586 << runTime.path()/runTime.timeName()
589 if (!masterMesh.write())
591 FatalErrorIn(args.executable())
592 << "Failed writing polyMesh."
598 // Write the addressing
600 Info<< "Reconstructing the addressing from the processor meshes"
601 << " to the newly reconstructed mesh" << nl << endl;
603 forAll(databases, procI)
605 Info<< "Reading processor " << procI << " mesh from "
606 << databases[procI].caseName() << endl;
613 databases[procI].timeName(),
619 // From processor point to reconstructed mesh point
621 Info<< "Writing pointProcAddressing to "
622 << databases[procI].caseName()
623 /procMesh.facesInstance()
624 /polyMesh::meshSubDir
631 "pointProcAddressing",
632 procMesh.facesInstance(),
633 polyMesh::meshSubDir,
637 false // do not register
639 pointProcAddressing[procI]
643 // From processor face to reconstructed mesh face
645 Info<< "Writing faceProcAddressing to "
646 << databases[procI].caseName()
647 /procMesh.facesInstance()
648 /polyMesh::meshSubDir
651 labelIOList faceProcAddr
655 "faceProcAddressing",
656 procMesh.facesInstance(),
657 polyMesh::meshSubDir,
661 false // do not register
663 faceProcAddressing[procI]
666 // Now add turning index to faceProcAddressing.
667 // See reconstrurPar for meaning of turning index.
668 forAll(faceProcAddr, procFaceI)
670 label masterFaceI = faceProcAddr[procFaceI];
674 !procMesh.isInternalFace(procFaceI)
675 && masterFaceI < masterInternalFaces
678 // proc face is now external but used to be internal face.
679 // Check if we have owner or neighbour.
681 label procOwn = procMesh.faceOwner()[procFaceI];
682 label masterOwn = masterOwner[masterFaceI];
684 if (cellProcAddressing[procI][procOwn] == masterOwn)
686 // No turning. Offset by 1.
687 faceProcAddr[procFaceI]++;
692 faceProcAddr[procFaceI] =
693 -1 - faceProcAddr[procFaceI];
698 // No turning. Offset by 1.
699 faceProcAddr[procFaceI]++;
703 faceProcAddr.write();
706 // From processor cell to reconstructed mesh cell
708 Info<< "Writing cellProcAddressing to "
709 << databases[procI].caseName()
710 /procMesh.facesInstance()
711 /polyMesh::meshSubDir
718 "cellProcAddressing",
719 procMesh.facesInstance(),
720 polyMesh::meshSubDir,
724 false // do not register
726 cellProcAddressing[procI]
731 // From processor patch to reconstructed mesh patch
733 Info<< "Writing boundaryProcAddressing to "
734 << databases[procI].caseName()
735 /procMesh.facesInstance()
736 /polyMesh::meshSubDir
743 "boundaryProcAddressing",
744 procMesh.facesInstance(),
745 polyMesh::meshSubDir,
749 false // do not register
751 boundaryProcAddressing[procI]
757 Info<< "End.\n" << endl;
763 // ************************************************************************* //