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/>.
25 Utility to create patches out of selected boundary faces. Faces come either
26 from existing patches or from a faceSet.
29 - creates new patches (from selected boundary faces). Synchronise faces
31 - synchronises points on coupled boundaries
32 - remove patches with 0 faces in them
34 \*---------------------------------------------------------------------------*/
36 #include "cyclicPolyPatch.H"
37 #include "syncTools.H"
41 #include "SortableList.H"
43 #include "meshTools.H"
45 #include "IOPtrList.H"
46 #include "polyTopoChange.H"
47 #include "polyModifyFace.H"
48 #include "wordReList.H"
52 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
56 defineTemplateTypeNameAndDebug(IOPtrList<dictionary>, 0);
64 polyTopoChange& meshMod
67 const label zoneID = mesh.faceZones().whichZone(faceID);
69 bool zoneFlip = false;
73 const faceZone& fZone = mesh.faceZones()[zoneID];
75 zoneFlip = fZone.flipMap()[fZone.whichFace(faceID)];
82 mesh.faces()[faceID], // face
84 mesh.faceOwner()[faceID], // owner
88 false, // remove from zone
96 // Filter out the empty patches.
97 void filterPatches(polyMesh& mesh)
99 const polyBoundaryMesh& patches = mesh.boundaryMesh();
102 DynamicList<polyPatch*> allPatches(patches.size());
104 label nOldPatches = returnReduce(patches.size(), sumOp<label>());
107 forAll(patches, patchI)
109 const polyPatch& pp = patches[patchI];
111 // Note: reduce possible since non-proc patches guaranteed in same order
112 if (!isA<processorPolyPatch>(pp))
114 if (returnReduce(pp.size(), sumOp<label>()) > 0)
129 Info<< "Removing empty patch " << pp.name() << " at position "
134 // Copy non-empty processor patches
135 forAll(patches, patchI)
137 const polyPatch& pp = patches[patchI];
139 if (isA<processorPolyPatch>(pp))
156 Info<< "Removing empty processor patch " << pp.name()
157 << " at position " << patchI << endl;
162 label nAllPatches = returnReduce(allPatches.size(), sumOp<label>());
163 if (nAllPatches != nOldPatches)
165 Info<< "Removing patches." << endl;
167 mesh.removeBoundary();
168 mesh.addPatches(allPatches);
172 Info<< "No patches removed." << endl;
177 // Dump for all patches the current match
178 void dumpCyclicMatch(const fileName& prefix, const polyMesh& mesh)
180 const polyBoundaryMesh& patches = mesh.boundaryMesh();
182 forAll(patches, patchI)
186 isA<cyclicPolyPatch>(patches[patchI])
187 && refCast<const cyclicPolyPatch>(patches[patchI]).owner()
190 const cyclicPolyPatch& cycPatch =
191 refCast<const cyclicPolyPatch>(patches[patchI]);
195 OFstream str(prefix+cycPatch.name()+".obj");
196 Pout<< "Dumping " << cycPatch.name()
197 << " faces to " << str.name() << endl;
206 const cyclicPolyPatch& nbrPatch = cycPatch.neighbPatch();
208 OFstream str(prefix+nbrPatch.name()+".obj");
209 Pout<< "Dumping " << nbrPatch.name()
210 << " faces to " << str.name() << endl;
220 // Lines between corresponding face centres
221 OFstream str(prefix+cycPatch.name()+nbrPatch.name()+"_match.obj");
224 Pout<< "Dumping cyclic match as lines between face centres to "
225 << str.name() << endl;
227 forAll(cycPatch, faceI)
229 const point& fc0 = mesh.faceCentres()[cycPatch.start()+faceI];
230 meshTools::writeOBJ(str, fc0);
232 const point& fc1 = mesh.faceCentres()[nbrPatch.start()+faceI];
233 meshTools::writeOBJ(str, fc1);
236 str<< "l " << vertI-1 << ' ' << vertI << nl;
245 const vectorField& separation,
249 if (separation.size() == 1)
251 // Single value for all.
255 field[i] += separation[0];
258 else if (separation.size() == field.size())
262 field[i] += separation[i];
269 "separateList(const vectorField&, UList<vector>&)"
270 ) << "Sizes of field and transformation not equal. field:"
271 << field.size() << " transformation:" << separation.size()
272 << abort(FatalError);
277 // Synchronise points on both sides of coupled boundaries.
278 template <class CombineOp>
281 const polyMesh& mesh,
283 const CombineOp& cop,
284 const point& nullValue
287 if (points.size() != mesh.nPoints())
292 "(const polyMesh&, pointField&, const CombineOp&, const point&)"
293 ) << "Number of values " << points.size()
294 << " is not equal to the number of points in the mesh "
295 << mesh.nPoints() << abort(FatalError);
298 const polyBoundaryMesh& patches = mesh.boundaryMesh();
300 // Is there any coupled patch with transformation?
301 bool hasTransformation = false;
303 if (Pstream::parRun())
307 forAll(patches, patchI)
309 const polyPatch& pp = patches[patchI];
313 isA<processorPolyPatch>(pp)
315 && refCast<const processorPolyPatch>(pp).owner()
318 const processorPolyPatch& procPatch =
319 refCast<const processorPolyPatch>(pp);
321 // Get data per patchPoint in neighbouring point numbers.
322 pointField patchInfo(procPatch.nPoints(), nullValue);
324 const labelList& meshPts = procPatch.meshPoints();
325 const labelList& nbrPts = procPatch.neighbPoints();
327 forAll(nbrPts, pointI)
329 label nbrPointI = nbrPts[pointI];
330 if (nbrPointI >= 0 && nbrPointI < patchInfo.size())
332 patchInfo[nbrPointI] = points[meshPts[pointI]];
336 OPstream toNbr(Pstream::blocking, procPatch.neighbProcNo());
344 forAll(patches, patchI)
346 const polyPatch& pp = patches[patchI];
350 isA<processorPolyPatch>(pp)
352 && !refCast<const processorPolyPatch>(pp).owner()
355 const processorPolyPatch& procPatch =
356 refCast<const processorPolyPatch>(pp);
358 pointField nbrPatchInfo(procPatch.nPoints());
360 // We do not know the number of points on the other side
361 // so cannot use Pstream::read.
365 procPatch.neighbProcNo()
367 fromNbr >> nbrPatchInfo;
369 // Null any value which is not on neighbouring processor
370 nbrPatchInfo.setSize(procPatch.nPoints(), nullValue);
372 if (!procPatch.parallel())
374 hasTransformation = true;
375 transformList(procPatch.forwardT(), nbrPatchInfo);
377 else if (procPatch.separated())
379 hasTransformation = true;
380 separateList(-procPatch.separation(), nbrPatchInfo);
383 const labelList& meshPts = procPatch.meshPoints();
385 forAll(meshPts, pointI)
387 label meshPointI = meshPts[pointI];
388 points[meshPointI] = nbrPatchInfo[pointI];
395 forAll(patches, patchI)
397 const polyPatch& pp = patches[patchI];
401 isA<cyclicPolyPatch>(pp)
402 && refCast<const cyclicPolyPatch>(pp).owner()
405 const cyclicPolyPatch& cycPatch =
406 refCast<const cyclicPolyPatch>(pp);
408 const edgeList& coupledPoints = cycPatch.coupledPoints();
409 const labelList& meshPts = cycPatch.meshPoints();
410 const cyclicPolyPatch& nbrPatch = cycPatch.neighbPatch();
411 const labelList& nbrMeshPts = nbrPatch.meshPoints();
413 pointField half0Values(coupledPoints.size());
415 forAll(coupledPoints, i)
417 const edge& e = coupledPoints[i];
418 label point0 = meshPts[e[0]];
419 half0Values[i] = points[point0];
422 if (!cycPatch.parallel())
424 hasTransformation = true;
425 transformList(cycPatch.reverseT(), half0Values);
427 else if (cycPatch.separated())
429 hasTransformation = true;
430 separateList(cycPatch.separation(), half0Values);
433 forAll(coupledPoints, i)
435 const edge& e = coupledPoints[i];
436 label point1 = nbrMeshPts[e[1]];
437 points[point1] = half0Values[i];
442 //- Note: hasTransformation is only used for warning messages so
443 // reduction not strictly nessecary.
444 //reduce(hasTransformation, orOp<bool>());
446 // Synchronize multiple shared points.
447 const globalMeshData& pd = mesh.globalData();
449 if (pd.nGlobalPoints() > 0)
451 if (hasTransformation)
456 "(const polyMesh&, pointField&, const CombineOp&, const point&)"
457 ) << "There are decomposed cyclics in this mesh with"
458 << " transformations." << endl
459 << "This is not supported. The result will be incorrect"
464 // Values on shared points.
465 pointField sharedPts(pd.nGlobalPoints(), nullValue);
467 forAll(pd.sharedPointLabels(), i)
469 label meshPointI = pd.sharedPointLabels()[i];
470 // Fill my entries in the shared points
471 sharedPts[pd.sharedPointAddr()[i]] = points[meshPointI];
474 // Combine on master.
475 Pstream::listCombineGather(sharedPts, cop);
476 Pstream::listCombineScatter(sharedPts);
478 // Now we will all have the same information. Merge it back with
479 // my local information.
480 forAll(pd.sharedPointLabels(), i)
482 label meshPointI = pd.sharedPointLabels()[i];
483 points[meshPointI] = sharedPts[pd.sharedPointAddr()[i]];
491 int main(int argc, char *argv[])
493 # include "addOverwriteOption.H"
494 # include "addRegionOption.H"
496 # include "setRootCase.H"
497 # include "createTime.H"
498 runTime.functionObjects().off();
500 Foam::word meshRegionName = polyMesh::defaultRegion;
501 args.optionReadIfPresent("region", meshRegionName);
503 const bool overwrite = args.optionFound("overwrite");
505 Info<< "Reading createPatchDict." << nl << endl;
514 meshRegionName != polyMesh::defaultRegion
519 IOobject::MUST_READ_IF_MODIFIED,
526 // Whether to synchronise points
527 const Switch pointSync(dict.lookup("pointSync"));
529 # include "createNamedPolyMesh.H"
531 const word oldInstance = mesh.pointsInstance();
533 const polyBoundaryMesh& patches = mesh.boundaryMesh();
535 // If running parallel check same patches everywhere
536 patches.checkParallelSync(true);
539 dumpCyclicMatch("initial_", mesh);
541 // Read patch construct info from dictionary
542 PtrList<dictionary> patchSources(dict.lookup("patches"));
546 // 1. Add all new patches
547 // ~~~~~~~~~~~~~~~~~~~~~~
549 if (patchSources.size())
551 // Old and new patches.
552 DynamicList<polyPatch*> allPatches(patches.size()+patchSources.size());
554 label startFaceI = mesh.nInternalFaces();
557 forAll(patches, patchI)
559 const polyPatch& pp = patches[patchI];
561 if (!isA<processorPolyPatch>(pp))
573 startFaceI += pp.size();
577 forAll(patchSources, addedI)
579 const dictionary& dict = patchSources[addedI];
581 word patchName(dict.lookup("name"));
583 label destPatchI = patches.findPatchID(patchName);
585 if (destPatchI == -1)
587 dictionary patchDict(dict.subDict("patchInfo"));
589 destPatchI = allPatches.size();
591 Info<< "Adding new patch " << patchName
592 << " as patch " << destPatchI
593 << " from " << patchDict << endl;
595 patchDict.set("nFaces", 0);
596 patchDict.set("startFace", startFaceI);
598 // Add an empty patch.
613 forAll(patches, patchI)
615 const polyPatch& pp = patches[patchI];
617 if (isA<processorPolyPatch>(pp))
629 startFaceI += pp.size();
634 mesh.removeBoundary();
635 mesh.addPatches(allPatches);
645 polyTopoChange meshMod(mesh);
648 forAll(patchSources, addedI)
650 const dictionary& dict = patchSources[addedI];
652 const word patchName(dict.lookup("name"));
653 label destPatchI = patches.findPatchID(patchName);
655 if (destPatchI == -1)
657 FatalErrorIn(args.executable())
658 << "patch " << patchName << " not added. Problem."
659 << abort(FatalError);
662 const word sourceType(dict.lookup("constructFrom"));
664 if (sourceType == "patches")
666 labelHashSet patchSources
670 wordReList(dict.lookup("patches"))
674 // Repatch faces of the patches.
675 forAllConstIter(labelHashSet, patchSources, iter)
677 const polyPatch& pp = patches[iter.key()];
679 Info<< "Moving faces from patch " << pp.name()
680 << " to patch " << destPatchI << endl;
694 else if (sourceType == "set")
696 const word setName(dict.lookup("set"));
698 faceSet faces(mesh, setName);
700 Info<< "Read " << returnReduce(faces.size(), sumOp<label>())
701 << " faces from faceSet " << faces.name() << endl;
703 // Sort (since faceSet contains faces in arbitrary order)
704 labelList faceLabels(faces.toc());
706 SortableList<label> patchFaces(faceLabels);
708 forAll(patchFaces, i)
710 label faceI = patchFaces[i];
712 if (mesh.isInternalFace(faceI))
714 FatalErrorIn(args.executable())
715 << "Face " << faceI << " specified in set "
717 << " is not an external face of the mesh." << endl
718 << "This application can only repatch existing boundary"
719 << " faces." << exit(FatalError);
733 FatalErrorIn(args.executable())
734 << "Invalid source type " << sourceType << endl
735 << "Valid source types are 'patches' 'set'" << exit(FatalError);
741 // Change mesh, use inflation to reforce calculation of transformation
743 Info<< "Doing topology modification to order faces." << nl << endl;
744 autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh, true);
745 mesh.movePoints(map().preMotionPoints());
747 dumpCyclicMatch("coupled_", mesh);
749 // Synchronise points.
752 Info<< "Not synchronising points." << nl << endl;
756 Info<< "Synchronising points." << nl << endl;
758 // This is a bit tricky. Both normal and position might be out and
759 // current separation also includes the normal
760 // ( separation_ = (nf&(Cr - Cf))*nf ).
762 // For cyclic patches:
763 // - for separated ones use user specified offset vector
765 forAll(mesh.boundaryMesh(), patchI)
767 const polyPatch& pp = mesh.boundaryMesh()[patchI];
769 if (pp.size() && isA<coupledPolyPatch>(pp))
771 const coupledPolyPatch& cpp =
772 refCast<const coupledPolyPatch>(pp);
776 Info<< "On coupled patch " << pp.name()
777 << " separation[0] was "
778 << cpp.separation()[0] << endl;
780 if (isA<cyclicPolyPatch>(pp) && pp.size())
782 const cyclicPolyPatch& cycpp =
783 refCast<const cyclicPolyPatch>(pp);
785 if (cycpp.transform() == cyclicPolyPatch::TRANSLATIONAL)
787 // Force to wanted separation
788 Info<< "On cyclic translation patch " << pp.name()
789 << " forcing uniform separation of "
790 << cycpp.separationVector() << endl;
791 const_cast<vectorField&>(cpp.separation()) =
792 pointField(1, cycpp.separationVector());
796 const cyclicPolyPatch& nbr = cycpp.neighbPatch();
797 const_cast<vectorField&>(cpp.separation()) =
801 nbr[0].centre(mesh.points())
802 - cycpp[0].centre(mesh.points())
806 Info<< "On coupled patch " << pp.name()
807 << " forcing uniform separation of "
808 << cpp.separation() << endl;
810 else if (!cpp.parallel())
812 Info<< "On coupled patch " << pp.name()
813 << " forcing uniform rotation of "
814 << cpp.forwardT()[0] << endl;
816 const_cast<tensorField&>
820 const_cast<tensorField&>
825 Info<< "On coupled patch " << pp.name()
826 << " forcing uniform rotation of "
827 << cpp.forwardT() << endl;
832 Info<< "Synchronising points." << endl;
834 pointField newPoints(mesh.points());
840 minMagSqrEqOp<vector>(),
841 point(GREAT, GREAT, GREAT)
844 scalarField diff(mag(newPoints-mesh.points()));
845 Info<< "Points changed by average:" << gAverage(diff)
846 << " max:" << gMax(diff) << nl << endl;
848 mesh.movePoints(newPoints);
851 // 3. Remove zeros-sized patches
852 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
854 Info<< "Removing patches with no faces in them." << nl<< endl;
858 dumpCyclicMatch("final_", mesh);
861 // Set the precision of the points data to 10
862 IOstream::defaultPrecision(10);
870 mesh.setInstance(oldInstance);
873 // Write resulting mesh
874 Info<< "Writing repatched mesh to " << runTime.timeName() << nl << endl;
877 Info<< "End\n" << endl;
883 // ************************************************************************* //