1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | foam-extend: Open Source CFD
4 \\ / O peration | Version: 3.2
5 \\ / A nd | Web: http://www.foam-extend.org
6 \\/ M anipulation | For copyright notice see file Copyright
7 -------------------------------------------------------------------------------
9 This file is part of foam-extend.
11 foam-extend 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 3 of the License, or (at your
14 option) any later version.
16 foam-extend is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with foam-extend. 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 "mapPolyMesh.H"
47 #include "directTopoChange.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 directTopoChange& 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();
526 Foam::word meshRegionName = polyMesh::defaultRegion;
527 args.optionReadIfPresent("region", meshRegionName);
529 const bool overwrite = args.optionFound("overwrite");
531 Info<< "Reading createPatchDict." << nl << endl;
540 meshRegionName != polyMesh::defaultRegion
552 // Whether to synchronise points
553 const Switch pointSync(dict.lookup("pointSync"));
556 // Change tolerance in controlDict instead. HJ, 22/Oct/2008
558 // Set the matching tolerance so we can read illegal meshes
559 // scalar tol = readScalar(dict.lookup("matchTolerance"));
560 // Info<< "Using relative tolerance " << tol
561 // << " to match up faces and points" << nl << endl;
562 // polyPatch::matchTol_ = tol;
564 # include "createNamedPolyMesh.H"
566 const word oldInstance = mesh.pointsInstance();
568 const polyBoundaryMesh& patches = mesh.boundaryMesh();
570 // If running parallel check same patches everywhere
571 patches.checkParallelSync(true);
574 dumpCyclicMatch("initial_", mesh);
576 // Read patch construct info from dictionary
577 PtrList<dictionary> patchSources(dict.lookup("patchInfo"));
581 // 1. Add all new patches
582 // ~~~~~~~~~~~~~~~~~~~~~~
584 if (patchSources.size())
586 // Old and new patches.
587 DynamicList<polyPatch*> allPatches(patches.size()+patchSources.size());
589 label startFaceI = mesh.nInternalFaces();
592 forAll(patches, patchI)
594 const polyPatch& pp = patches[patchI];
596 if (!isA<processorPolyPatch>(pp))
608 startFaceI += pp.size();
612 forAll(patchSources, addedI)
614 const dictionary& dict = patchSources[addedI];
616 word patchName(dict.lookup("name"));
618 label destPatchI = patches.findPatchID(patchName);
620 if (destPatchI == -1)
622 dictionary patchDict(dict.subDict("dictionary"));
624 destPatchI = allPatches.size();
626 Info<< "Adding new patch " << patchName
627 << " as patch " << destPatchI
628 << " from " << patchDict << endl;
630 patchDict.set("nFaces", 0);
631 patchDict.set("startFace", startFaceI);
633 // Add an empty patch.
648 forAll(patches, patchI)
650 const polyPatch& pp = patches[patchI];
652 if (isA<processorPolyPatch>(pp))
664 startFaceI += pp.size();
669 mesh.removeBoundary();
670 mesh.addPatches(allPatches);
680 directTopoChange meshMod(mesh);
683 forAll(patchSources, addedI)
685 const dictionary& dict = patchSources[addedI];
687 word patchName(dict.lookup("name"));
689 label destPatchI = patches.findPatchID(patchName);
691 if (destPatchI == -1)
693 FatalErrorIn(args.executable()) << "patch " << patchName
694 << " not added. Problem." << abort(FatalError);
697 word sourceType(dict.lookup("constructFrom"));
699 if (sourceType == "patches")
701 labelHashSet patchSources(patches.patchSet(dict.lookup("patches")));
703 // Repatch faces of the patches.
704 forAllConstIter(labelHashSet, patchSources, iter)
706 const polyPatch& pp = patches[iter.key()];
708 Info<< "Moving faces from patch " << pp.name()
709 << " to patch " << destPatchI << endl;
723 else if (sourceType == "set")
725 word setName(dict.lookup("set"));
727 faceSet faces(mesh, setName);
729 Info<< "Read " << returnReduce(faces.size(), sumOp<label>())
730 << " faces from faceSet " << faces.name() << endl;
732 // Sort (since faceSet contains faces in arbitrary order)
733 labelList faceLabels(faces.toc());
735 SortableList<label> patchFaces(faceLabels);
737 forAll(patchFaces, i)
739 label faceI = patchFaces[i];
741 if (mesh.isInternalFace(faceI))
743 FatalErrorIn(args.executable())
744 << "Face " << faceI << " specified in set "
746 << " is not an external face of the mesh." << endl
747 << "This application can only repatch existing boundary"
748 << " faces." << exit(FatalError);
762 FatalErrorIn(args.executable())
763 << "Invalid source type " << sourceType << endl
764 << "Valid source types are 'patches' 'set'" << exit(FatalError);
770 // Change mesh, use inflation to reforce calculation of transformation
772 Info<< "Doing topology modification to order faces." << nl << endl;
773 autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh, true);
774 mesh.movePoints(map().preMotionPoints());
776 dumpCyclicMatch("coupled_", mesh);
778 // Synchronise points.
781 Info<< "Not synchronising points." << nl << endl;
785 Info<< "Synchronising points." << nl << endl;
787 // This is a bit tricky. Both normal and position might be out and
788 // current separation also includes the normal
789 // ( separation_ = (nf&(Cr - Cf))*nf ).
791 // For processor patches:
792 // - disallow multiple separation/transformation. This basically
793 // excludes decomposed cyclics. Use the (probably 0) separation
794 // to align the points.
795 // For cyclic patches:
796 // - for separated ones use our own recalculated offset vector
797 // - for rotational ones use current one.
799 forAll(mesh.boundaryMesh(), patchI)
801 const polyPatch& pp = mesh.boundaryMesh()[patchI];
803 if (pp.size() && isA<coupledPolyPatch>(pp))
805 const coupledPolyPatch& cpp =
806 refCast<const coupledPolyPatch>(pp);
810 Info<< "On coupled patch " << pp.name()
811 << " separation[0] was "
812 << cpp.separation()[0] << endl;
814 if (isA<cyclicPolyPatch>(pp))
816 const cyclicPolyPatch& cycpp =
817 refCast<const cyclicPolyPatch>(pp);
819 if (cycpp.transform() == cyclicPolyPatch::TRANSLATIONAL)
821 Info<< "On cyclic translation patch " << pp.name()
822 << " forcing uniform separation of "
823 << cycpp.separationVector() << endl;
824 const_cast<vectorField&>(cpp.separation()) =
825 pointField(1, cycpp.separationVector());
829 const_cast<vectorField&>(cpp.separation()) =
833 pp[pp.size()/2].centre(mesh.points())
834 - pp[0].centre(mesh.points())
840 const_cast<vectorField&>(cpp.separation())
843 Info<< "On coupled patch " << pp.name()
844 << " forcing uniform separation of "
845 << cpp.separation() << endl;
847 else if (!cpp.parallel())
849 Info<< "On coupled patch " << pp.name()
850 << " forcing uniform rotation of "
851 << cpp.forwardT()[0] << endl;
853 const_cast<tensorField&>
857 const_cast<tensorField&>
862 Info<< "On coupled patch " << pp.name()
863 << " forcing uniform rotation of "
864 << cpp.forwardT() << endl;
869 Info<< "Synchronising points." << endl;
871 pointField newPoints(mesh.points());
878 point(GREAT, GREAT, GREAT)
881 scalarField diff(mag(newPoints-mesh.points()));
882 Info<< "Points changed by average:" << gAverage(diff)
883 << " max:" << gMax(diff) << nl << endl;
885 mesh.movePoints(newPoints);
888 // 3. Remove zeros-sized patches
889 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
891 Info<< "Removing patches with no faces in them." << nl<< endl;
895 dumpCyclicMatch("final_", mesh);
898 // Set the precision of the points data to 10
899 IOstream::defaultPrecision(10);
907 mesh.setInstance(oldInstance);
910 // Write resulting mesh
911 Info<< "Writing repatched mesh to " << runTime.timeName() << nl << endl;
914 Info<< "End\n" << endl;
920 // ************************************************************************* //