1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 1991-2009 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 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
26 Utility to create patches out of selected boundary faces. Faces come either
27 from existing patches or from a faceSet.
30 - creates new patches (from selected boundary faces). Synchronise faces
32 - synchronises points on coupled boundaries
33 - remove patches with 0 faces in them
35 \*---------------------------------------------------------------------------*/
37 #include "cyclicPolyPatch.H"
38 #include "syncTools.H"
42 #include "SortableList.H"
44 #include "meshTools.H"
46 #include "IOPtrList.H"
47 #include "polyTopoChange.H"
48 #include "polyModifyFace.H"
52 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
56 defineTemplateTypeNameAndDebug(IOPtrList<dictionary>, 0);
59 // Combine operator to synchronise points. We choose point nearest to origin so
60 // we can use e.g. great,great,great as null value.
66 void operator()(vector& x, const vector& y) const
68 if (magSqr(y) < magSqr(x))
81 polyTopoChange& meshMod
84 const label zoneID = mesh.faceZones().whichZone(faceID);
86 bool zoneFlip = false;
90 const faceZone& fZone = mesh.faceZones()[zoneID];
92 zoneFlip = fZone.flipMap()[fZone.whichFace(faceID)];
99 mesh.faces()[faceID], // face
101 mesh.faceOwner()[faceID], // owner
105 false, // remove from zone
107 zoneFlip // zone flip
113 // Filter out the empty patches.
114 void filterPatches(polyMesh& mesh)
116 const polyBoundaryMesh& patches = mesh.boundaryMesh();
119 DynamicList<polyPatch*> allPatches(patches.size());
121 label nOldPatches = returnReduce(patches.size(), sumOp<label>());
124 forAll(patches, patchI)
126 const polyPatch& pp = patches[patchI];
128 // Note: reduce possible since non-proc patches guaranteed in same order
129 if (!isA<processorPolyPatch>(pp))
131 if (returnReduce(pp.size(), sumOp<label>()) > 0)
146 Info<< "Removing empty patch " << pp.name() << " at position "
151 // Copy non-empty processor patches
152 forAll(patches, patchI)
154 const polyPatch& pp = patches[patchI];
156 if (isA<processorPolyPatch>(pp))
173 Info<< "Removing empty processor patch " << pp.name()
174 << " at position " << patchI << endl;
179 label nAllPatches = returnReduce(allPatches.size(), sumOp<label>());
180 if (nAllPatches != nOldPatches)
182 Info<< "Removing patches." << endl;
184 mesh.removeBoundary();
185 mesh.addPatches(allPatches);
189 Info<< "No patches removed." << endl;
194 // Dump for all patches the current match
195 void dumpCyclicMatch(const fileName& prefix, const polyMesh& mesh)
197 const polyBoundaryMesh& patches = mesh.boundaryMesh();
199 forAll(patches, patchI)
201 if (isA<cyclicPolyPatch>(patches[patchI]))
203 const cyclicPolyPatch& cycPatch =
204 refCast<const cyclicPolyPatch>(patches[patchI]);
206 label halfSize = cycPatch.size()/2;
210 OFstream str(prefix+cycPatch.name()+"_half0.obj");
211 Pout<< "Dumping " << cycPatch.name()
212 << " half0 faces to " << str.name() << endl;
216 static_cast<faceList>
228 OFstream str(prefix+cycPatch.name()+"_half1.obj");
229 Pout<< "Dumping " << cycPatch.name()
230 << " half1 faces to " << str.name() << endl;
234 static_cast<faceList>
248 // Lines between corresponding face centres
249 OFstream str(prefix+cycPatch.name()+"_match.obj");
252 Pout<< "Dumping cyclic match as lines between face centres to "
253 << str.name() << endl;
255 for (label faceI = 0; faceI < halfSize; faceI++)
257 const point& fc0 = mesh.faceCentres()[cycPatch.start()+faceI];
258 meshTools::writeOBJ(str, fc0);
261 label nbrFaceI = halfSize + faceI;
263 mesh.faceCentres()[cycPatch.start()+nbrFaceI];
264 meshTools::writeOBJ(str, fc1);
267 str<< "l " << vertI-1 << ' ' << vertI << nl;
276 const vectorField& separation,
280 if (separation.size() == 1)
282 // Single value for all.
286 field[i] += separation[0];
289 else if (separation.size() == field.size())
293 field[i] += separation[i];
300 "separateList(const vectorField&, UList<vector>&)"
301 ) << "Sizes of field and transformation not equal. field:"
302 << field.size() << " transformation:" << separation.size()
303 << abort(FatalError);
308 // Synchronise points on both sides of coupled boundaries.
309 template <class CombineOp>
312 const polyMesh& mesh,
314 const CombineOp& cop,
315 const point& nullValue
318 if (points.size() != mesh.nPoints())
323 "(const polyMesh&, pointField&, const CombineOp&, const point&)"
324 ) << "Number of values " << points.size()
325 << " is not equal to the number of points in the mesh "
326 << mesh.nPoints() << abort(FatalError);
329 const polyBoundaryMesh& patches = mesh.boundaryMesh();
331 // Is there any coupled patch with transformation?
332 bool hasTransformation = false;
334 if (Pstream::parRun())
338 forAll(patches, patchI)
340 const polyPatch& pp = patches[patchI];
344 isA<processorPolyPatch>(pp)
346 && refCast<const processorPolyPatch>(pp).owner()
349 const processorPolyPatch& procPatch =
350 refCast<const processorPolyPatch>(pp);
352 // Get data per patchPoint in neighbouring point numbers.
353 pointField patchInfo(procPatch.nPoints(), nullValue);
355 const labelList& meshPts = procPatch.meshPoints();
356 const labelList& nbrPts = procPatch.neighbPoints();
358 forAll(nbrPts, pointI)
360 label nbrPointI = nbrPts[pointI];
361 if (nbrPointI >= 0 && nbrPointI < patchInfo.size())
363 patchInfo[nbrPointI] = points[meshPts[pointI]];
367 OPstream toNbr(Pstream::blocking, procPatch.neighbProcNo());
375 forAll(patches, patchI)
377 const polyPatch& pp = patches[patchI];
381 isA<processorPolyPatch>(pp)
383 && !refCast<const processorPolyPatch>(pp).owner()
386 const processorPolyPatch& procPatch =
387 refCast<const processorPolyPatch>(pp);
389 pointField nbrPatchInfo(procPatch.nPoints());
391 // We do not know the number of points on the other side
392 // so cannot use Pstream::read.
396 procPatch.neighbProcNo()
398 fromNbr >> nbrPatchInfo;
400 // Null any value which is not on neighbouring processor
401 nbrPatchInfo.setSize(procPatch.nPoints(), nullValue);
403 if (!procPatch.parallel())
405 hasTransformation = true;
406 transformList(procPatch.forwardT(), nbrPatchInfo);
408 else if (procPatch.separated())
410 hasTransformation = true;
411 separateList(-procPatch.separation(), nbrPatchInfo);
414 const labelList& meshPts = procPatch.meshPoints();
416 forAll(meshPts, pointI)
418 label meshPointI = meshPts[pointI];
419 points[meshPointI] = nbrPatchInfo[pointI];
426 forAll(patches, patchI)
428 const polyPatch& pp = patches[patchI];
430 if (isA<cyclicPolyPatch>(pp))
432 const cyclicPolyPatch& cycPatch =
433 refCast<const cyclicPolyPatch>(pp);
435 const edgeList& coupledPoints = cycPatch.coupledPoints();
436 const labelList& meshPts = cycPatch.meshPoints();
438 pointField half0Values(coupledPoints.size());
440 forAll(coupledPoints, i)
442 const edge& e = coupledPoints[i];
443 label point0 = meshPts[e[0]];
444 half0Values[i] = points[point0];
447 if (!cycPatch.parallel())
449 hasTransformation = true;
450 transformList(cycPatch.reverseT(), half0Values);
452 else if (cycPatch.separated())
454 hasTransformation = true;
455 const vectorField& v = cycPatch.coupledPolyPatch::separation();
456 separateList(v, half0Values);
459 forAll(coupledPoints, i)
461 const edge& e = coupledPoints[i];
462 label point1 = meshPts[e[1]];
463 points[point1] = half0Values[i];
468 //- Note: hasTransformation is only used for warning messages so
469 // reduction not strictly nessecary.
470 //reduce(hasTransformation, orOp<bool>());
472 // Synchronize multiple shared points.
473 const globalMeshData& pd = mesh.globalData();
475 if (pd.nGlobalPoints() > 0)
477 if (hasTransformation)
482 "(const polyMesh&, pointField&, const CombineOp&, const point&)"
483 ) << "There are decomposed cyclics in this mesh with"
484 << " transformations." << endl
485 << "This is not supported. The result will be incorrect"
490 // Values on shared points.
491 pointField sharedPts(pd.nGlobalPoints(), nullValue);
493 forAll(pd.sharedPointLabels(), i)
495 label meshPointI = pd.sharedPointLabels()[i];
496 // Fill my entries in the shared points
497 sharedPts[pd.sharedPointAddr()[i]] = points[meshPointI];
500 // Combine on master.
501 Pstream::listCombineGather(sharedPts, cop);
502 Pstream::listCombineScatter(sharedPts);
504 // Now we will all have the same information. Merge it back with
505 // my local information.
506 forAll(pd.sharedPointLabels(), i)
508 label meshPointI = pd.sharedPointLabels()[i];
509 points[meshPointI] = sharedPts[pd.sharedPointAddr()[i]];
517 int main(int argc, char *argv[])
519 # include "addRegionOption.H"
520 argList::validOptions.insert("overwrite", "");
522 # include "setRootCase.H"
523 # include "createTime.H"
524 runTime.functionObjects().off();
525 # include "createNamedPolyMesh.H"
527 const bool overwrite = args.optionFound("overwrite");
529 Info<< "Reading createPatchDict." << nl << endl;
538 regionName != polyMesh::defaultRegion
550 // Whether to synchronise points
551 const Switch pointSync(dict.lookup("pointSync"));
554 // Set the matching tolerance so we can read illegal meshes
555 scalar tol = readScalar(dict.lookup("matchTolerance"));
556 Info<< "Using relative tolerance " << tol
557 << " to match up faces and points" << nl << endl;
558 coupledPolyPatch::matchTol = tol;
561 const word oldInstance = mesh.pointsInstance();
563 const polyBoundaryMesh& patches = mesh.boundaryMesh();
565 // If running parallel check same patches everywhere
566 patches.checkParallelSync(true);
569 dumpCyclicMatch("initial_", mesh);
571 // Read patch construct info from dictionary
572 PtrList<dictionary> patchSources(dict.lookup("patchInfo"));
576 // 1. Add all new patches
577 // ~~~~~~~~~~~~~~~~~~~~~~
579 if (patchSources.size())
581 // Old and new patches.
582 DynamicList<polyPatch*> allPatches(patches.size()+patchSources.size());
584 label startFaceI = mesh.nInternalFaces();
587 forAll(patches, patchI)
589 const polyPatch& pp = patches[patchI];
591 if (!isA<processorPolyPatch>(pp))
603 startFaceI += pp.size();
607 forAll(patchSources, addedI)
609 const dictionary& dict = patchSources[addedI];
611 word patchName(dict.lookup("name"));
613 label destPatchI = patches.findPatchID(patchName);
615 if (destPatchI == -1)
617 dictionary patchDict(dict.subDict("dictionary"));
619 destPatchI = allPatches.size();
621 Info<< "Adding new patch " << patchName
622 << " as patch " << destPatchI
623 << " from " << patchDict << endl;
625 patchDict.set("nFaces", 0);
626 patchDict.set("startFace", startFaceI);
628 // Add an empty patch.
643 forAll(patches, patchI)
645 const polyPatch& pp = patches[patchI];
647 if (isA<processorPolyPatch>(pp))
659 startFaceI += pp.size();
664 mesh.removeBoundary();
665 mesh.addPatches(allPatches);
675 polyTopoChange meshMod(mesh);
678 forAll(patchSources, addedI)
680 const dictionary& dict = patchSources[addedI];
682 word patchName(dict.lookup("name"));
684 label destPatchI = patches.findPatchID(patchName);
686 if (destPatchI == -1)
688 FatalErrorIn(args.executable()) << "patch " << patchName
689 << " not added. Problem." << abort(FatalError);
692 word sourceType(dict.lookup("constructFrom"));
694 if (sourceType == "patches")
696 labelHashSet patchSources(patches.patchSet(dict.lookup("patches")));
698 // Repatch faces of the patches.
699 forAllConstIter(labelHashSet, patchSources, iter)
701 const polyPatch& pp = patches[iter.key()];
703 Info<< "Moving faces from patch " << pp.name()
704 << " to patch " << destPatchI << endl;
718 else if (sourceType == "set")
720 word setName(dict.lookup("set"));
722 faceSet faces(mesh, setName);
724 Info<< "Read " << returnReduce(faces.size(), sumOp<label>())
725 << " faces from faceSet " << faces.name() << endl;
727 // Sort (since faceSet contains faces in arbitrary order)
728 labelList faceLabels(faces.toc());
730 SortableList<label> patchFaces(faceLabels);
732 forAll(patchFaces, i)
734 label faceI = patchFaces[i];
736 if (mesh.isInternalFace(faceI))
738 FatalErrorIn(args.executable())
739 << "Face " << faceI << " specified in set "
741 << " is not an external face of the mesh." << endl
742 << "This application can only repatch existing boundary"
743 << " faces." << exit(FatalError);
757 FatalErrorIn(args.executable())
758 << "Invalid source type " << sourceType << endl
759 << "Valid source types are 'patches' 'set'" << exit(FatalError);
765 // Change mesh, use inflation to reforce calculation of transformation
767 Info<< "Doing topology modification to order faces." << nl << endl;
768 autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh, true);
769 mesh.movePoints(map().preMotionPoints());
771 dumpCyclicMatch("coupled_", mesh);
773 // Synchronise points.
776 Info<< "Not synchronising points." << nl << endl;
780 Info<< "Synchronising points." << nl << endl;
782 // This is a bit tricky. Both normal and position might be out and
783 // current separation also includes the normal
784 // ( separation_ = (nf&(Cr - Cf))*nf ).
786 // For processor patches:
787 // - disallow multiple separation/transformation. This basically
788 // excludes decomposed cyclics. Use the (probably 0) separation
789 // to align the points.
790 // For cyclic patches:
791 // - for separated ones use our own recalculated offset vector
792 // - for rotational ones use current one.
794 forAll(mesh.boundaryMesh(), patchI)
796 const polyPatch& pp = mesh.boundaryMesh()[patchI];
798 if (pp.size() && isA<coupledPolyPatch>(pp))
800 const coupledPolyPatch& cpp =
801 refCast<const coupledPolyPatch>(pp);
805 Info<< "On coupled patch " << pp.name()
806 << " separation[0] was "
807 << cpp.separation()[0] << endl;
809 if (isA<cyclicPolyPatch>(pp))
811 const cyclicPolyPatch& cycpp =
812 refCast<const cyclicPolyPatch>(pp);
814 if (cycpp.transform() == cyclicPolyPatch::TRANSLATIONAL)
816 Info<< "On cyclic translation patch " << pp.name()
817 << " forcing uniform separation of "
818 << cycpp.separationVector() << endl;
819 const_cast<vectorField&>(cpp.separation()) =
820 pointField(1, cycpp.separationVector());
824 const_cast<vectorField&>(cpp.separation()) =
828 pp[pp.size()/2].centre(mesh.points())
829 - pp[0].centre(mesh.points())
835 const_cast<vectorField&>(cpp.separation())
838 Info<< "On coupled patch " << pp.name()
839 << " forcing uniform separation of "
840 << cpp.separation() << endl;
842 else if (!cpp.parallel())
844 Info<< "On coupled patch " << pp.name()
845 << " forcing uniform rotation of "
846 << cpp.forwardT()[0] << endl;
848 const_cast<tensorField&>
852 const_cast<tensorField&>
857 Info<< "On coupled patch " << pp.name()
858 << " forcing uniform rotation of "
859 << cpp.forwardT() << endl;
864 Info<< "Synchronising points." << endl;
866 pointField newPoints(mesh.points());
873 point(GREAT, GREAT, GREAT)
876 scalarField diff(mag(newPoints-mesh.points()));
877 Info<< "Points changed by average:" << gAverage(diff)
878 << " max:" << gMax(diff) << nl << endl;
880 mesh.movePoints(newPoints);
883 // 3. Remove zeros-sized patches
884 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
886 Info<< "Removing patches with no faces in them." << nl<< endl;
890 dumpCyclicMatch("final_", mesh);
893 // Set the precision of the points data to 10
894 IOstream::defaultPrecision(10);
902 mesh.setInstance(oldInstance);
905 // Write resulting mesh
906 Info<< "Writing repatched mesh to " << runTime.timeName() << nl << endl;
909 Info<< "End\n" << endl;
915 // ************************************************************************* //