1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright held by original author
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 "mapPolyMesh.H"
48 #include "directTopoChange.H"
49 #include "polyModifyFace.H"
53 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
57 defineTemplateTypeNameAndDebug(IOPtrList<dictionary>, 0);
60 // Combine operator to synchronise points. We choose point nearest to origin so
61 // we can use e.g. great,great,great as null value.
67 void operator()(vector& x, const vector& y) const
69 if (magSqr(y) < magSqr(x))
82 directTopoChange& meshMod
85 const label zoneID = mesh.faceZones().whichZone(faceID);
87 bool zoneFlip = false;
91 const faceZone& fZone = mesh.faceZones()[zoneID];
93 zoneFlip = fZone.flipMap()[fZone.whichFace(faceID)];
100 mesh.faces()[faceID], // face
102 mesh.faceOwner()[faceID], // owner
106 false, // remove from zone
108 zoneFlip // zone flip
114 // Filter out the empty patches.
115 void filterPatches(polyMesh& mesh)
117 const polyBoundaryMesh& patches = mesh.boundaryMesh();
120 DynamicList<polyPatch*> allPatches(patches.size());
122 label nOldPatches = returnReduce(patches.size(), sumOp<label>());
125 forAll(patches, patchI)
127 const polyPatch& pp = patches[patchI];
129 // Note: reduce possible since non-proc patches guaranteed in same order
130 if (!isA<processorPolyPatch>(pp))
132 if (returnReduce(pp.size(), sumOp<label>()) > 0)
147 Info<< "Removing empty patch " << pp.name() << " at position "
152 // Copy non-empty processor patches
153 forAll(patches, patchI)
155 const polyPatch& pp = patches[patchI];
157 if (isA<processorPolyPatch>(pp))
174 Info<< "Removing empty processor patch " << pp.name()
175 << " at position " << patchI << endl;
180 label nAllPatches = returnReduce(allPatches.size(), sumOp<label>());
181 if (nAllPatches != nOldPatches)
183 Info<< "Removing patches." << endl;
185 mesh.removeBoundary();
186 mesh.addPatches(allPatches);
190 Info<< "No patches removed." << endl;
195 // Dump for all patches the current match
196 void dumpCyclicMatch(const fileName& prefix, const polyMesh& mesh)
198 const polyBoundaryMesh& patches = mesh.boundaryMesh();
200 forAll(patches, patchI)
202 if (isA<cyclicPolyPatch>(patches[patchI]))
204 const cyclicPolyPatch& cycPatch =
205 refCast<const cyclicPolyPatch>(patches[patchI]);
207 label halfSize = cycPatch.size()/2;
211 OFstream str(prefix+cycPatch.name()+"_half0.obj");
212 Pout<< "Dumping " << cycPatch.name()
213 << " half0 faces to " << str.name() << endl;
217 static_cast<faceList>
229 OFstream str(prefix+cycPatch.name()+"_half1.obj");
230 Pout<< "Dumping " << cycPatch.name()
231 << " half1 faces to " << str.name() << endl;
235 static_cast<faceList>
249 // Lines between corresponding face centres
250 OFstream str(prefix+cycPatch.name()+"_match.obj");
253 Pout<< "Dumping cyclic match as lines between face centres to "
254 << str.name() << endl;
256 for (label faceI = 0; faceI < halfSize; faceI++)
258 const point& fc0 = mesh.faceCentres()[cycPatch.start()+faceI];
259 meshTools::writeOBJ(str, fc0);
262 label nbrFaceI = halfSize + faceI;
264 mesh.faceCentres()[cycPatch.start()+nbrFaceI];
265 meshTools::writeOBJ(str, fc1);
268 str<< "l " << vertI-1 << ' ' << vertI << nl;
277 const vectorField& separation,
281 if (separation.size() == 1)
283 // Single value for all.
287 field[i] += separation[0];
290 else if (separation.size() == field.size())
294 field[i] += separation[i];
301 "separateList(const vectorField&, UList<vector>&)"
302 ) << "Sizes of field and transformation not equal. field:"
303 << field.size() << " transformation:" << separation.size()
304 << abort(FatalError);
309 // Synchronise points on both sides of coupled boundaries.
310 template <class CombineOp>
313 const polyMesh& mesh,
315 const CombineOp& cop,
316 const point& nullValue
319 if (points.size() != mesh.nPoints())
324 "(const polyMesh&, pointField&, const CombineOp&, const point&)"
325 ) << "Number of values " << points.size()
326 << " is not equal to the number of points in the mesh "
327 << mesh.nPoints() << abort(FatalError);
330 const polyBoundaryMesh& patches = mesh.boundaryMesh();
332 // Is there any coupled patch with transformation?
333 bool hasTransformation = false;
335 if (Pstream::parRun())
339 forAll(patches, patchI)
341 const polyPatch& pp = patches[patchI];
345 isA<processorPolyPatch>(pp)
347 && refCast<const processorPolyPatch>(pp).owner()
350 const processorPolyPatch& procPatch =
351 refCast<const processorPolyPatch>(pp);
353 // Get data per patchPoint in neighbouring point numbers.
354 pointField patchInfo(procPatch.nPoints(), nullValue);
356 const labelList& meshPts = procPatch.meshPoints();
357 const labelList& nbrPts = procPatch.neighbPoints();
359 forAll(nbrPts, pointI)
361 label nbrPointI = nbrPts[pointI];
362 if (nbrPointI >= 0 && nbrPointI < patchInfo.size())
364 patchInfo[nbrPointI] = points[meshPts[pointI]];
368 OPstream toNbr(Pstream::blocking, procPatch.neighbProcNo());
376 forAll(patches, patchI)
378 const polyPatch& pp = patches[patchI];
382 isA<processorPolyPatch>(pp)
384 && !refCast<const processorPolyPatch>(pp).owner()
387 const processorPolyPatch& procPatch =
388 refCast<const processorPolyPatch>(pp);
390 pointField nbrPatchInfo(procPatch.nPoints());
392 // We do not know the number of points on the other side
393 // so cannot use Pstream::read.
397 procPatch.neighbProcNo()
399 fromNbr >> nbrPatchInfo;
401 // Null any value which is not on neighbouring processor
402 nbrPatchInfo.setSize(procPatch.nPoints(), nullValue);
404 if (!procPatch.parallel())
406 hasTransformation = true;
407 transformList(procPatch.forwardT(), nbrPatchInfo);
409 else if (procPatch.separated())
411 hasTransformation = true;
412 separateList(-procPatch.separation(), nbrPatchInfo);
415 const labelList& meshPts = procPatch.meshPoints();
417 forAll(meshPts, pointI)
419 label meshPointI = meshPts[pointI];
420 points[meshPointI] = nbrPatchInfo[pointI];
427 forAll(patches, patchI)
429 const polyPatch& pp = patches[patchI];
431 if (isA<cyclicPolyPatch>(pp))
433 const cyclicPolyPatch& cycPatch =
434 refCast<const cyclicPolyPatch>(pp);
436 const edgeList& coupledPoints = cycPatch.coupledPoints();
437 const labelList& meshPts = cycPatch.meshPoints();
439 pointField half0Values(coupledPoints.size());
441 forAll(coupledPoints, i)
443 const edge& e = coupledPoints[i];
444 label point0 = meshPts[e[0]];
445 half0Values[i] = points[point0];
448 if (!cycPatch.parallel())
450 hasTransformation = true;
451 transformList(cycPatch.reverseT(), half0Values);
453 else if (cycPatch.separated())
455 hasTransformation = true;
456 const vectorField& v = cycPatch.coupledPolyPatch::separation();
457 separateList(v, half0Values);
460 forAll(coupledPoints, i)
462 const edge& e = coupledPoints[i];
463 label point1 = meshPts[e[1]];
464 points[point1] = half0Values[i];
469 //- Note: hasTransformation is only used for warning messages so
470 // reduction not strictly nessecary.
471 //reduce(hasTransformation, orOp<bool>());
473 // Synchronize multiple shared points.
474 const globalMeshData& pd = mesh.globalData();
476 if (pd.nGlobalPoints() > 0)
478 if (hasTransformation)
483 "(const polyMesh&, pointField&, const CombineOp&, const point&)"
484 ) << "There are decomposed cyclics in this mesh with"
485 << " transformations." << endl
486 << "This is not supported. The result will be incorrect"
491 // Values on shared points.
492 pointField sharedPts(pd.nGlobalPoints(), nullValue);
494 forAll(pd.sharedPointLabels(), i)
496 label meshPointI = pd.sharedPointLabels()[i];
497 // Fill my entries in the shared points
498 sharedPts[pd.sharedPointAddr()[i]] = points[meshPointI];
501 // Combine on master.
502 Pstream::listCombineGather(sharedPts, cop);
503 Pstream::listCombineScatter(sharedPts);
505 // Now we will all have the same information. Merge it back with
506 // my local information.
507 forAll(pd.sharedPointLabels(), i)
509 label meshPointI = pd.sharedPointLabels()[i];
510 points[meshPointI] = sharedPts[pd.sharedPointAddr()[i]];
518 int main(int argc, char *argv[])
520 # include "addRegionOption.H"
521 argList::validOptions.insert("overwrite", "");
523 # include "setRootCase.H"
524 # include "createTime.H"
525 runTime.functionObjects().off();
527 Foam::word meshRegionName = polyMesh::defaultRegion;
528 args.optionReadIfPresent("region", meshRegionName);
530 const bool overwrite = args.optionFound("overwrite");
532 Info<< "Reading createPatchDict." << nl << endl;
541 meshRegionName != polyMesh::defaultRegion
553 // Whether to synchronise points
554 const Switch pointSync(dict.lookup("pointSync"));
557 // Change tolerance in controlDict instead. HJ, 22/Oct/2008
559 // Set the matching tolerance so we can read illegal meshes
560 // scalar tol = readScalar(dict.lookup("matchTolerance"));
561 // Info<< "Using relative tolerance " << tol
562 // << " to match up faces and points" << nl << endl;
563 // polyPatch::matchTol_ = tol;
565 # include "createNamedPolyMesh.H"
567 const word oldInstance = mesh.pointsInstance();
569 const polyBoundaryMesh& patches = mesh.boundaryMesh();
571 // If running parallel check same patches everywhere
572 patches.checkParallelSync(true);
575 dumpCyclicMatch("initial_", mesh);
577 // Read patch construct info from dictionary
578 PtrList<dictionary> patchSources(dict.lookup("patchInfo"));
582 // 1. Add all new patches
583 // ~~~~~~~~~~~~~~~~~~~~~~
585 if (patchSources.size())
587 // Old and new patches.
588 DynamicList<polyPatch*> allPatches(patches.size()+patchSources.size());
590 label startFaceI = mesh.nInternalFaces();
593 forAll(patches, patchI)
595 const polyPatch& pp = patches[patchI];
597 if (!isA<processorPolyPatch>(pp))
609 startFaceI += pp.size();
613 forAll(patchSources, addedI)
615 const dictionary& dict = patchSources[addedI];
617 word patchName(dict.lookup("name"));
619 label destPatchI = patches.findPatchID(patchName);
621 if (destPatchI == -1)
623 dictionary patchDict(dict.subDict("dictionary"));
625 destPatchI = allPatches.size();
627 Info<< "Adding new patch " << patchName
628 << " as patch " << destPatchI
629 << " from " << patchDict << endl;
631 patchDict.set("nFaces", 0);
632 patchDict.set("startFace", startFaceI);
634 // Add an empty patch.
649 forAll(patches, patchI)
651 const polyPatch& pp = patches[patchI];
653 if (isA<processorPolyPatch>(pp))
665 startFaceI += pp.size();
670 mesh.removeBoundary();
671 mesh.addPatches(allPatches);
681 directTopoChange meshMod(mesh);
684 forAll(patchSources, addedI)
686 const dictionary& dict = patchSources[addedI];
688 word patchName(dict.lookup("name"));
690 label destPatchI = patches.findPatchID(patchName);
692 if (destPatchI == -1)
694 FatalErrorIn(args.executable()) << "patch " << patchName
695 << " not added. Problem." << abort(FatalError);
698 word sourceType(dict.lookup("constructFrom"));
700 if (sourceType == "patches")
702 labelHashSet patchSources(patches.patchSet(dict.lookup("patches")));
704 // Repatch faces of the patches.
705 forAllConstIter(labelHashSet, patchSources, iter)
707 const polyPatch& pp = patches[iter.key()];
709 Info<< "Moving faces from patch " << pp.name()
710 << " to patch " << destPatchI << endl;
724 else if (sourceType == "set")
726 word setName(dict.lookup("set"));
728 faceSet faces(mesh, setName);
730 Info<< "Read " << returnReduce(faces.size(), sumOp<label>())
731 << " faces from faceSet " << faces.name() << endl;
733 // Sort (since faceSet contains faces in arbitrary order)
734 labelList faceLabels(faces.toc());
736 SortableList<label> patchFaces(faceLabels);
738 forAll(patchFaces, i)
740 label faceI = patchFaces[i];
742 if (mesh.isInternalFace(faceI))
744 FatalErrorIn(args.executable())
745 << "Face " << faceI << " specified in set "
747 << " is not an external face of the mesh." << endl
748 << "This application can only repatch existing boundary"
749 << " faces." << exit(FatalError);
763 FatalErrorIn(args.executable())
764 << "Invalid source type " << sourceType << endl
765 << "Valid source types are 'patches' 'set'" << exit(FatalError);
771 // Change mesh, use inflation to reforce calculation of transformation
773 Info<< "Doing topology modification to order faces." << nl << endl;
774 autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh, true);
775 mesh.movePoints(map().preMotionPoints());
777 dumpCyclicMatch("coupled_", mesh);
779 // Synchronise points.
782 Info<< "Not synchronising points." << nl << endl;
786 Info<< "Synchronising points." << nl << endl;
788 // This is a bit tricky. Both normal and position might be out and
789 // current separation also includes the normal
790 // ( separation_ = (nf&(Cr - Cf))*nf ).
792 // For processor patches:
793 // - disallow multiple separation/transformation. This basically
794 // excludes decomposed cyclics. Use the (probably 0) separation
795 // to align the points.
796 // For cyclic patches:
797 // - for separated ones use our own recalculated offset vector
798 // - for rotational ones use current one.
800 forAll(mesh.boundaryMesh(), patchI)
802 const polyPatch& pp = mesh.boundaryMesh()[patchI];
804 if (pp.size() && isA<coupledPolyPatch>(pp))
806 const coupledPolyPatch& cpp =
807 refCast<const coupledPolyPatch>(pp);
811 Info<< "On coupled patch " << pp.name()
812 << " separation[0] was "
813 << cpp.separation()[0] << endl;
815 if (isA<cyclicPolyPatch>(pp))
817 const cyclicPolyPatch& cycpp =
818 refCast<const cyclicPolyPatch>(pp);
820 if (cycpp.transform() == cyclicPolyPatch::TRANSLATIONAL)
822 Info<< "On cyclic translation patch " << pp.name()
823 << " forcing uniform separation of "
824 << cycpp.separationVector() << endl;
825 const_cast<vectorField&>(cpp.separation()) =
826 pointField(1, cycpp.separationVector());
830 const_cast<vectorField&>(cpp.separation()) =
834 pp[pp.size()/2].centre(mesh.points())
835 - pp[0].centre(mesh.points())
841 const_cast<vectorField&>(cpp.separation())
844 Info<< "On coupled patch " << pp.name()
845 << " forcing uniform separation of "
846 << cpp.separation() << endl;
848 else if (!cpp.parallel())
850 Info<< "On coupled patch " << pp.name()
851 << " forcing uniform rotation of "
852 << cpp.forwardT()[0] << endl;
854 const_cast<tensorField&>
858 const_cast<tensorField&>
863 Info<< "On coupled patch " << pp.name()
864 << " forcing uniform rotation of "
865 << cpp.forwardT() << endl;
870 Info<< "Synchronising points." << endl;
872 pointField newPoints(mesh.points());
879 point(GREAT, GREAT, GREAT)
882 scalarField diff(mag(newPoints-mesh.points()));
883 Info<< "Points changed by average:" << gAverage(diff)
884 << " max:" << gMax(diff) << nl << endl;
886 mesh.movePoints(newPoints);
889 // 3. Remove zeros-sized patches
890 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
892 Info<< "Removing patches with no faces in them." << nl<< endl;
896 dumpCyclicMatch("final_", mesh);
899 // Set the precision of the points data to 10
900 IOstream::defaultPrecision(10);
908 mesh.setInstance(oldInstance);
911 // Write resulting mesh
912 Info<< "Writing repatched mesh to " << runTime.timeName() << nl << endl;
915 Info<< "End\n" << endl;
921 // ************************************************************************* //