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
25 \*---------------------------------------------------------------------------*/
27 #include "cyclicPolyPatch.H"
28 #include "addToRunTimeSelectionTable.H"
29 #include "polyBoundaryMesh.H"
31 #include "demandDrivenData.H"
33 #include "patchZones.H"
34 #include "matchPoints.H"
37 #include "transformList.H"
39 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
43 defineTypeNameAndDebug(cyclicPolyPatch, 0);
45 addToRunTimeSelectionTable(polyPatch, cyclicPolyPatch, word);
46 addToRunTimeSelectionTable(polyPatch, cyclicPolyPatch, dictionary);
50 const char* NamedEnum<cyclicPolyPatch::transformType, 3>::names[] =
57 const NamedEnum<cyclicPolyPatch::transformType, 3>
58 cyclicPolyPatch::transformTypeNames;
62 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
64 Foam::label Foam::cyclicPolyPatch::findMaxArea
66 const pointField& points,
71 scalar maxAreaSqr = -GREAT;
75 scalar areaSqr = magSqr(faces[faceI].normal(points));
77 if (areaSqr > maxAreaSqr)
87 void Foam::cyclicPolyPatch::calcTransforms()
91 const pointField& points = this->points();
93 // Determine geometric quantities on the two halves
94 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
106 pointField half0Ctrs(calcFaceCentres(half0, half0.points()));
108 scalarField half0Tols(calcFaceTol(half0, half0.points(), half0Ctrs));
120 pointField half1Ctrs(calcFaceCentres(half1, half1.points()));
125 fileName casePath(boundaryMesh().mesh().time().path());
127 fileName nm0(casePath/name()+"_half0_faces.obj");
128 Pout<< "cyclicPolyPatch::calcTransforms : Writing half0"
129 << " faces to OBJ file " << nm0 << endl;
130 writeOBJ(nm0, half0, half0.points());
132 fileName nm1(casePath/name()+"_half1_faces.obj");
133 Pout<< "cyclicPolyPatch::calcTransforms : Writing half1"
134 << " faces to OBJ file " << nm1 << endl;
135 writeOBJ(nm1, half1, half1.points());
137 OFstream str(casePath/name()+"_half0_to_half1.obj");
139 Pout<< "cyclicPolyPatch::calcTransforms :"
140 << " Writing coupled face centres as lines to " << str.name()
144 const point& p0 = half0Ctrs[i];
145 str << "v " << p0.x() << ' ' << p0.y() << ' ' << p0.z() << nl;
147 const point& p1 = half1Ctrs[i];
148 str << "v " << p1.x() << ' ' << p1.y() << ' ' << p1.z() << nl;
150 str << "l " << vertI-1 << ' ' << vertI << nl;
154 vectorField half0Normals(half0.size());
155 vectorField half1Normals(half1.size());
157 for (label facei = 0; facei < size()/2; facei++)
159 half0Normals[facei] = operator[](facei).normal(points);
160 label nbrFacei = facei+size()/2;
161 half1Normals[facei] = operator[](nbrFacei).normal(points);
163 scalar magSf = mag(half0Normals[facei]);
164 scalar nbrMagSf = mag(half1Normals[facei]);
165 scalar avSf = (magSf + nbrMagSf)/2.0;
167 if (magSf < ROOTVSMALL && nbrMagSf < ROOTVSMALL)
169 // Undetermined normal. Use dummy normal to force separation
170 // check. (note use of sqrt(VSMALL) since that is how mag
172 half0Normals[facei] = point(1, 0, 0);
173 half1Normals[facei] = half0Normals[facei];
175 else if (mag(magSf - nbrMagSf)/avSf > coupledPolyPatch::matchTol)
179 "cyclicPolyPatch::calcTransforms()"
180 ) << "face " << facei << " area does not match neighbour "
181 << nbrFacei << " by "
182 << 100*mag(magSf - nbrMagSf)/avSf
183 << "% -- possible face ordering problem." << endl
184 << "patch:" << name()
185 << " my area:" << magSf
186 << " neighbour area:" << nbrMagSf
187 << " matching tolerance:" << coupledPolyPatch::matchTol
189 << "Mesh face:" << start()+facei
191 << UIndirectList<point>(points, operator[](facei))()
193 << "Neighbour face:" << start()+nbrFacei
195 << UIndirectList<point>(points, operator[](nbrFacei))()
197 << "Rerun with cyclic debug flag set"
198 << " for more information." << exit(FatalError);
202 half0Normals[facei] /= magSf;
203 half1Normals[facei] /= nbrMagSf;
208 // See if transformation is prescribed
209 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
215 // Specified single rotation tensor.
217 // Get best fitting face and its opposite number
218 label face0 = getConsistentRotationFace(half0Ctrs);
223 (half0Ctrs[face0] - rotationCentre_)
228 (half1Ctrs[face1] - rotationCentre_)
231 n0 /= mag(n0) + VSMALL;
232 n1 /= mag(n1) + VSMALL;
236 Pout<< "cyclicPolyPatch::calcTransforms :"
237 << " Specified rotation :"
238 << " n0:" << n0 << " n1:" << n1 << endl;
241 // Calculate transformation tensors from face0,1 only.
242 // Note: can use tight tolerance now.
245 pointField(1, half0Ctrs[face0]),
246 pointField(1, half1Ctrs[face1]),
249 scalarField(1, half0Tols[face0]),
258 // Calculate transformation tensors from all faces.
275 // Get geometric zones of patch by looking at normals.
276 // Method 1: any edge with sharpish angle is edge between two halves.
277 // (this will handle e.g. wedge geometries).
278 // Also two fully disconnected regions will be handled this way.
279 // Method 2: sort faces into two halves based on face normal.
280 bool Foam::cyclicPolyPatch::getGeometricHalves
282 const primitivePatch& pp,
283 labelList& half0ToPatch,
284 labelList& half1ToPatch
288 const vectorField& faceNormals = pp.faceNormals();
290 // Find edges with sharp angles.
291 boolList regionEdge(pp.nEdges(), false);
293 const labelListList& edgeFaces = pp.edgeFaces();
295 label nRegionEdges = 0;
297 forAll(edgeFaces, edgeI)
299 const labelList& eFaces = edgeFaces[edgeI];
301 // Check manifold edges for sharp angle.
302 // (Non-manifold already handled by patchZones)
303 if (eFaces.size() == 2)
305 if ((faceNormals[eFaces[0]] & faceNormals[eFaces[1]])< featureCos_)
307 regionEdge[edgeI] = true;
315 // For every face determine zone it is connected to (without crossing
317 patchZones ppZones(pp, regionEdge);
321 Pout<< "cyclicPolyPatch::getGeometricHalves : "
322 << "Found " << nRegionEdges << " edges on patch " << name()
323 << " where the cos of the angle between two connected faces"
324 << " was less than " << featureCos_ << nl
325 << "Patch divided by these and by single sides edges into "
326 << ppZones.nZones() << " parts." << endl;
329 // Dumping zones to obj files.
331 labelList nZoneFaces(ppZones.nZones());
333 for (label zoneI = 0; zoneI < ppZones.nZones(); zoneI++)
337 boundaryMesh().mesh().time().path()
338 /name()+"_zone_"+Foam::name(zoneI)+".obj"
340 Pout<< "cyclicPolyPatch::getGeometricHalves : Writing zone "
341 << zoneI << " face centres to OBJ file " << stream.name()
344 labelList zoneFaces(findIndices(ppZones, zoneI));
348 writeOBJ(stream, pp[zoneFaces[i]].centre(pp.points()));
351 nZoneFaces[zoneI] = zoneFaces.size();
356 if (ppZones.nZones() == 2)
358 half0ToPatch = findIndices(ppZones, 0);
359 half1ToPatch = findIndices(ppZones, 1);
365 Pout<< "cyclicPolyPatch::getGeometricHalves :"
366 << " falling back to face-normal comparison" << endl;
369 half0ToPatch.setSize(pp.size());
372 half1ToPatch.setSize(pp.size());
374 // Compare to face 0 normal.
375 forAll(faceNormals, faceI)
377 if ((faceNormals[faceI] & faceNormals[0]) > 0)
379 half0ToPatch[n0Faces++] = faceI;
383 half1ToPatch[n1Faces++] = faceI;
386 half0ToPatch.setSize(n0Faces);
387 half1ToPatch.setSize(n1Faces);
391 Pout<< "cyclicPolyPatch::getGeometricHalves :"
392 << " Number of faces per zone:("
393 << n0Faces << ' ' << n1Faces << ')' << endl;
397 if (half0ToPatch.size() != half1ToPatch.size())
399 fileName casePath(boundaryMesh().mesh().time().path());
403 fileName nm0(casePath/name()+"_half0_faces.obj");
404 Pout<< "cyclicPolyPatch::getGeometricHalves : Writing half0"
405 << " faces to OBJ file " << nm0 << endl;
406 writeOBJ(nm0, UIndirectList<face>(pp, half0ToPatch)(), pp.points());
408 fileName nm1(casePath/name()+"_half1_faces.obj");
409 Pout<< "cyclicPolyPatch::getGeometricHalves : Writing half1"
410 << " faces to OBJ file " << nm1 << endl;
411 writeOBJ(nm1, UIndirectList<face>(pp, half1ToPatch)(), pp.points());
416 OFstream str0(casePath/name()+"_half0.obj");
417 Pout<< "cyclicPolyPatch::getGeometricHalves : Writing half0"
418 << " face centres to OBJ file " << str0.name() << endl;
420 forAll(half0ToPatch, i)
422 writeOBJ(str0, pp[half0ToPatch[i]].centre(pp.points()));
425 OFstream str1(casePath/name()+"_half1.obj");
426 Pout<< "cyclicPolyPatch::getGeometricHalves : Writing half1"
427 << " face centres to OBJ file " << str1.name() << endl;
428 forAll(half1ToPatch, i)
430 writeOBJ(str1, pp[half1ToPatch[i]].centre(pp.points()));
436 "cyclicPolyPatch::getGeometricHalves"
437 "(const primitivePatch&, labelList&, labelList&) const"
438 ) << "Patch " << name() << " gets decomposed in two zones of"
439 << "inequal size: " << half0ToPatch.size()
440 << " and " << half1ToPatch.size() << endl
441 << "This means that the patch is either not two separate regions"
442 << " or one region where the angle between the different regions"
443 << " is not sufficiently sharp." << endl
444 << "Please adapt the featureCos setting." << endl
445 << "Continuing with incorrect face ordering from now on!" << endl;
456 // Given a split of faces into left and right half calculate the centres
457 // and anchor points. Transform the left points so they align with the
459 void Foam::cyclicPolyPatch::getCentresAndAnchors
461 const primitivePatch& pp,
462 const faceList& half0Faces,
463 const faceList& half1Faces,
465 pointField& ppPoints,
466 pointField& half0Ctrs,
467 pointField& half1Ctrs,
468 pointField& anchors0,
472 // Get geometric data on both halves.
473 half0Ctrs = calcFaceCentres(half0Faces, pp.points());
474 anchors0 = getAnchorPoints(half0Faces, pp.points());
475 half1Ctrs = calcFaceCentres(half1Faces, pp.points());
481 label face0 = getConsistentRotationFace(half0Ctrs);
482 label face1 = getConsistentRotationFace(half1Ctrs);
484 vector n0 = ((half0Ctrs[face0] - rotationCentre_) ^ rotationAxis_);
485 vector n1 = ((half1Ctrs[face1] - rotationCentre_) ^ -rotationAxis_);
486 n0 /= mag(n0) + VSMALL;
487 n1 /= mag(n1) + VSMALL;
491 Pout<< "cyclicPolyPatch::getCentresAndAnchors :"
492 << " Specified rotation :"
493 << " n0:" << n0 << " n1:" << n1 << endl;
496 // Rotation (around origin)
497 const tensor reverseT(rotationTensor(n0, -n1));
500 forAll(half0Ctrs, faceI)
502 half0Ctrs[faceI] = Foam::transform(reverseT, half0Ctrs[faceI]);
503 anchors0[faceI] = Foam::transform(reverseT, anchors0[faceI]);
506 ppPoints = Foam::transform(reverseT, pp.points());
510 //- Problem: usually specified translation is not accurate enough
511 //- to get proper match so keep automatic determination over here.
512 //case TRANSLATIONAL:
514 // // Transform 0 points.
518 // Pout<< "cyclicPolyPatch::getCentresAndAnchors :"
519 // << "Specified translation : " << separationVector_ << endl;
522 // half0Ctrs += separationVector_;
523 // anchors0 += separationVector_;
528 // Assumes that cyclic is planar. This is also the initial
529 // condition for patches without faces.
531 // Determine the face with max area on both halves. These
532 // two faces are used to determine the transformation tensors
533 label max0I = findMaxArea(pp.points(), half0Faces);
534 vector n0 = half0Faces[max0I].normal(pp.points());
535 n0 /= mag(n0) + VSMALL;
537 label max1I = findMaxArea(pp.points(), half1Faces);
538 vector n1 = half1Faces[max1I].normal(pp.points());
539 n1 /= mag(n1) + VSMALL;
541 if (mag(n0 & n1) < 1-coupledPolyPatch::matchTol)
545 Pout<< "cyclicPolyPatch::getCentresAndAnchors :"
546 << " Detected rotation :"
547 << " n0:" << n0 << " n1:" << n1 << endl;
550 // Rotation (around origin)
551 const tensor reverseT(rotationTensor(n0, -n1));
554 forAll(half0Ctrs, faceI)
556 half0Ctrs[faceI] = Foam::transform
561 anchors0[faceI] = Foam::transform
567 ppPoints = Foam::transform(reverseT, pp.points());
571 // Parallel translation. Get average of all used points.
573 primitiveFacePatch half0(half0Faces, pp.points());
574 const pointField& half0Pts = half0.localPoints();
575 const point ctr0(sum(half0Pts)/half0Pts.size());
577 primitiveFacePatch half1(half1Faces, pp.points());
578 const pointField& half1Pts = half1.localPoints();
579 const point ctr1(sum(half1Pts)/half1Pts.size());
583 Pout<< "cyclicPolyPatch::getCentresAndAnchors :"
584 << " Detected translation :"
585 << " n0:" << n0 << " n1:" << n1
586 << " ctr0:" << ctr0 << " ctr1:" << ctr1 << endl;
589 half0Ctrs += ctr1 - ctr0;
590 anchors0 += ctr1 - ctr0;
591 ppPoints = pp.points() + ctr1 - ctr0;
598 // Calculate typical distance per face
599 tols = calcFaceTol(half1Faces, pp.points(), half1Ctrs);
603 // Calculates faceMap and rotation. Returns true if all ok.
604 bool Foam::cyclicPolyPatch::matchAnchors
607 const primitivePatch& pp,
608 const labelList& half0ToPatch,
609 const pointField& anchors0,
611 const labelList& half1ToPatch,
612 const faceList& half1Faces,
613 const labelList& from1To0,
615 const scalarField& tols,
621 // Set faceMap such that half0 faces get first and corresponding half1
624 forAll(half0ToPatch, half0FaceI)
626 // Label in original patch
627 label patchFaceI = half0ToPatch[half0FaceI];
629 faceMap[patchFaceI] = half0FaceI;
632 rotation[patchFaceI] = 0;
635 bool fullMatch = true;
637 forAll(from1To0, half1FaceI)
639 label patchFaceI = half1ToPatch[half1FaceI];
641 // This face has to match the corresponding one on half0.
642 label half0FaceI = from1To0[half1FaceI];
644 label newFaceI = half0FaceI + pp.size()/2;
646 faceMap[patchFaceI] = newFaceI;
648 // Rotate patchFaceI such that its f[0] aligns with that of
649 // the corresponding face
650 // (which after shuffling will be at position half0FaceI)
652 const point& wantedAnchor = anchors0[half0FaceI];
654 rotation[newFaceI] = getRotation
657 half1Faces[half1FaceI],
662 if (rotation[newFaceI] == -1)
668 const face& f = half1Faces[half1FaceI];
671 "cyclicPolyPatch::matchAnchors(..)"
672 ) << "Patch:" << name() << " : "
673 << "Cannot find point on face " << f
675 << UIndirectList<point>(pp.points(), f)()
676 << " that matches point " << wantedAnchor
677 << " when matching the halves of cyclic patch " << name()
679 << "Continuing with incorrect face ordering from now on!"
688 Foam::label Foam::cyclicPolyPatch::getConsistentRotationFace
690 const pointField& faceCentres
693 const scalarField magRadSqr =
694 magSqr((faceCentres - rotationCentre_) ^ rotationAxis_);
695 scalarField axisLen = (faceCentres - rotationCentre_) & rotationAxis_;
696 axisLen = axisLen - min(axisLen);
697 const scalarField magLenSqr = magRadSqr + axisLen*axisLen;
700 scalar maxMagLenSqr = -GREAT;
701 scalar maxMagRadSqr = -GREAT;
702 forAll(faceCentres, i)
704 if (magLenSqr[i] >= maxMagLenSqr)
706 if (magRadSqr[i] > maxMagRadSqr)
709 maxMagLenSqr = magLenSqr[i];
710 maxMagRadSqr = magRadSqr[i];
717 Info<< "getConsistentRotationFace(const pointField&)" << nl
718 << " rotFace = " << rotFace << nl
719 << " point = " << faceCentres[rotFace] << endl;
726 // * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * * * * * //
728 Foam::cyclicPolyPatch::cyclicPolyPatch
734 const polyBoundaryMesh& bm
737 coupledPolyPatch(name, size, start, index, bm),
738 coupledPointsPtr_(NULL),
739 coupledEdgesPtr_(NULL),
742 rotationAxis_(vector::zero),
743 rotationCentre_(point::zero),
744 separationVector_(vector::zero)
748 Foam::cyclicPolyPatch::cyclicPolyPatch
751 const dictionary& dict,
753 const polyBoundaryMesh& bm
756 coupledPolyPatch(name, dict, index, bm),
757 coupledPointsPtr_(NULL),
758 coupledEdgesPtr_(NULL),
761 rotationAxis_(vector::zero),
762 rotationCentre_(point::zero),
763 separationVector_(vector::zero)
765 dict.readIfPresent("featureCos", featureCos_);
767 if (dict.found("transform"))
769 transform_ = transformTypeNames.read(dict.lookup("transform"));
774 dict.lookup("rotationAxis") >> rotationAxis_;
775 dict.lookup("rotationCentre") >> rotationCentre_;
780 dict.lookup("separationVector") >> separationVector_;
785 // no additional info required
792 Foam::cyclicPolyPatch::cyclicPolyPatch
794 const cyclicPolyPatch& pp,
795 const polyBoundaryMesh& bm
798 coupledPolyPatch(pp, bm),
799 coupledPointsPtr_(NULL),
800 coupledEdgesPtr_(NULL),
801 featureCos_(pp.featureCos_),
802 transform_(pp.transform_),
803 rotationAxis_(pp.rotationAxis_),
804 rotationCentre_(pp.rotationCentre_),
805 separationVector_(pp.separationVector_)
809 Foam::cyclicPolyPatch::cyclicPolyPatch
811 const cyclicPolyPatch& pp,
812 const polyBoundaryMesh& bm,
818 coupledPolyPatch(pp, bm, index, newSize, newStart),
819 coupledPointsPtr_(NULL),
820 coupledEdgesPtr_(NULL),
821 featureCos_(pp.featureCos_),
822 transform_(pp.transform_),
823 rotationAxis_(pp.rotationAxis_),
824 rotationCentre_(pp.rotationCentre_),
825 separationVector_(pp.separationVector_)
829 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
831 Foam::cyclicPolyPatch::~cyclicPolyPatch()
833 deleteDemandDrivenData(coupledPointsPtr_);
834 deleteDemandDrivenData(coupledEdgesPtr_);
839 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
841 void Foam::cyclicPolyPatch::initGeometry()
843 polyPatch::initGeometry();
846 void Foam::cyclicPolyPatch::calcGeometry()
848 polyPatch::calcGeometry();
852 void Foam::cyclicPolyPatch::initMovePoints(const pointField& p)
854 polyPatch::initMovePoints(p);
857 void Foam::cyclicPolyPatch::movePoints(const pointField& p)
859 polyPatch::movePoints(p);
863 void Foam::cyclicPolyPatch::initUpdateMesh()
865 polyPatch::initUpdateMesh();
868 void Foam::cyclicPolyPatch::updateMesh()
870 polyPatch::updateMesh();
871 deleteDemandDrivenData(coupledPointsPtr_);
872 deleteDemandDrivenData(coupledEdgesPtr_);
876 const Foam::edgeList& Foam::cyclicPolyPatch::coupledPoints() const
878 if (!coupledPointsPtr_)
880 // Look at cyclic patch as two halves, A and B.
881 // Now all we know is that relative face index in halfA is same
882 // as coupled face in halfB and also that the 0th vertex
885 // From halfA point to halfB or -1.
886 labelList coupledPoint(nPoints(), -1);
888 for (label patchFaceA = 0; patchFaceA < size()/2; patchFaceA++)
890 const face& fA = localFaces()[patchFaceA];
894 label patchPointA = fA[indexA];
896 if (coupledPoint[patchPointA] == -1)
898 const face& fB = localFaces()[patchFaceA + size()/2];
900 label indexB = (fB.size() - indexA) % fB.size();
902 // Filter out points on wedge axis
903 if (patchPointA != fB[indexB])
905 coupledPoint[patchPointA] = fB[indexB];
911 coupledPointsPtr_ = new edgeList(nPoints());
912 edgeList& connected = *coupledPointsPtr_;
914 // Extract coupled points.
915 label connectedI = 0;
917 forAll(coupledPoint, i)
919 if (coupledPoint[i] != -1)
921 connected[connectedI++] = edge(i, coupledPoint[i]);
925 connected.setSize(connectedI);
931 boundaryMesh().mesh().time().path()
936 Pout<< "Writing file " << str.name() << " with coordinates of "
937 << "coupled points" << endl;
941 const point& a = points()[meshPoints()[connected[i][0]]];
942 const point& b = points()[meshPoints()[connected[i][1]]];
944 str<< "v " << a.x() << ' ' << a.y() << ' ' << a.z() << nl;
945 str<< "v " << b.x() << ' ' << b.y() << ' ' << b.z() << nl;
948 str<< "l " << vertI-1 << ' ' << vertI << nl;
952 // Remove any addressing calculated for the coupled edges calculation
953 const_cast<primitivePatch&>(static_cast<const primitivePatch&>(*this))
956 return *coupledPointsPtr_;
960 const Foam::edgeList& Foam::cyclicPolyPatch::coupledEdges() const
962 if (!coupledEdgesPtr_)
964 // Build map from points on halfA to points on halfB.
965 const edgeList& pointCouples = coupledPoints();
967 Map<label> aToB(2*pointCouples.size());
969 forAll(pointCouples, i)
971 const edge& e = pointCouples[i];
973 aToB.insert(e[0], e[1]);
976 // Map from edge on half A to points (in halfB indices)
977 EdgeMap<label> edgeMap(nEdges());
979 for (label patchFaceA = 0; patchFaceA < size()/2; patchFaceA++)
981 const labelList& fEdges = faceEdges()[patchFaceA];
985 label edgeI = fEdges[i];
987 const edge& e = edges()[edgeI];
989 // Convert edge end points to corresponding points on halfB
991 Map<label>::const_iterator fnd0 = aToB.find(e[0]);
992 if (fnd0 != aToB.end())
994 Map<label>::const_iterator fnd1 = aToB.find(e[1]);
995 if (fnd1 != aToB.end())
997 edgeMap.insert(edge(fnd0(), fnd1()), edgeI);
1003 coupledEdgesPtr_ = new edgeList(nEdges()/2);
1004 edgeList& coupledEdges = *coupledEdgesPtr_;
1007 for (label patchFaceB = size()/2; patchFaceB < size(); patchFaceB++)
1009 const labelList& fEdges = faceEdges()[patchFaceB];
1013 label edgeI = fEdges[i];
1015 const edge& e = edges()[edgeI];
1017 // Look up halfA edge from HashTable.
1018 EdgeMap<label>::iterator iter = edgeMap.find(e);
1020 if (iter != edgeMap.end())
1022 label halfAEdgeI = iter();
1024 // Store correspondence. Filter out edges on wedge axis.
1025 if (halfAEdgeI != edgeI)
1027 coupledEdges[coupleI++] = edge(halfAEdgeI, edgeI);
1030 // Remove so we build unique list
1031 edgeMap.erase(iter);
1035 coupledEdges.setSize(coupleI);
1040 forAll(coupledEdges, i)
1042 const edge& e = coupledEdges[i];
1044 if (e[0] == e[1] || e[0] < 0 || e[1] < 0)
1046 FatalErrorIn("cyclicPolyPatch::coupledEdges() const")
1047 << "Problem : at position " << i
1048 << " illegal couple:" << e
1049 << abort(FatalError);
1057 boundaryMesh().mesh().time().path()
1062 Pout<< "Writing file " << str.name() << " with centres of "
1063 << "coupled edges" << endl;
1065 forAll(coupledEdges, i)
1067 const edge& e = coupledEdges[i];
1069 const point& a = edges()[e[0]].centre(localPoints());
1070 const point& b = edges()[e[1]].centre(localPoints());
1072 str<< "v " << a.x() << ' ' << a.y() << ' ' << a.z() << nl;
1073 str<< "v " << b.x() << ' ' << b.y() << ' ' << b.z() << nl;
1076 str<< "l " << vertI-1 << ' ' << vertI << nl;
1080 // Remove any addressing calculated for the coupled edges calculation
1081 const_cast<primitivePatch&>(static_cast<const primitivePatch&>(*this))
1084 return *coupledEdgesPtr_;
1088 void Foam::cyclicPolyPatch::initOrder(const primitivePatch& pp) const
1092 // Return new ordering. Ordering is -faceMap: for every face index
1093 // the new face -rotation:for every new face the clockwise shift
1094 // of the original face. Return false if nothing changes (faceMap
1095 // is identity, rotation is 0)
1096 bool Foam::cyclicPolyPatch::order
1098 const primitivePatch& pp,
1103 faceMap.setSize(pp.size());
1106 rotation.setSize(pp.size());
1111 // No faces, nothing to change.
1117 FatalErrorIn("cyclicPolyPatch::order(..)")
1118 << "Size of cyclic " << name() << " should be a multiple of 2"
1119 << ". It is " << pp.size() << abort(FatalError);
1122 label halfSize = pp.size()/2;
1124 // Supplied primitivePatch already with new points.
1125 // Cyclics are limited to one transformation tensor
1126 // currently anyway (i.e. straight plane) so should not be too big a
1130 // Indices of faces on half0
1131 labelList half0ToPatch;
1132 // Indices of faces on half1
1133 labelList half1ToPatch;
1136 // 1. Test if already correctly oriented by starting from trivial ordering.
1137 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1139 half0ToPatch = identity(halfSize);
1140 half1ToPatch = half0ToPatch + halfSize;
1143 faceList half0Faces(UIndirectList<face>(pp, half0ToPatch));
1144 faceList half1Faces(UIndirectList<face>(pp, half1ToPatch));
1146 // Get geometric quantities
1147 pointField half0Ctrs, half1Ctrs, anchors0, ppPoints;
1149 getCentresAndAnchors
1162 // Geometric match of face centre vectors
1164 bool matchedAll = matchPoints
1175 Pout<< "cyclicPolyPatch::order : test if already ordered:"
1176 << matchedAll << endl;
1179 fileName nm0("match1_"+name()+"_half0_faces.obj");
1180 Pout<< "cyclicPolyPatch::order : Writing half0"
1181 << " faces to OBJ file " << nm0 << endl;
1182 writeOBJ(nm0, half0Faces, ppPoints);
1184 fileName nm1("match1_"+name()+"_half1_faces.obj");
1185 Pout<< "cyclicPolyPatch::order : Writing half1"
1186 << " faces to OBJ file " << nm1 << endl;
1187 writeOBJ(nm1, half1Faces, pp.points());
1191 boundaryMesh().mesh().time().path()
1192 /"match1_"+ name() + "_faceCentres.obj"
1194 Pout<< "cyclicPolyPatch::order : "
1195 << "Dumping currently found cyclic match as lines between"
1196 << " corresponding face centres to file " << ccStr.name()
1199 // Recalculate untransformed face centres
1200 //pointField rawHalf0Ctrs = calcFaceCentres(half0Faces, pp.points());
1203 forAll(half1Ctrs, i)
1205 //if (from1To0[i] != -1)
1207 // Write edge between c1 and c0
1208 //const point& c0 = rawHalf0Ctrs[from1To0[i]];
1209 //const point& c0 = half0Ctrs[from1To0[i]];
1210 const point& c0 = half0Ctrs[i];
1211 const point& c1 = half1Ctrs[i];
1212 writeOBJ(ccStr, c0, c1, vertI);
1218 // 2. Ordered in pairs (so 0,1 coupled and 2,3 etc.)
1219 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1224 for (label i = 0; i < halfSize; i++)
1226 half0ToPatch[i] = faceI++;
1227 half1ToPatch[i] = faceI++;
1230 // And redo all matching
1231 half0Faces = UIndirectList<face>(pp, half0ToPatch);
1232 half1Faces = UIndirectList<face>(pp, half1ToPatch);
1234 getCentresAndAnchors
1247 // Geometric match of face centre vectors
1248 matchedAll = matchPoints
1259 Pout<< "cyclicPolyPatch::order : test if pairwise ordered:"
1260 << matchedAll << endl;
1262 fileName nm0("match2_"+name()+"_half0_faces.obj");
1263 Pout<< "cyclicPolyPatch::order : Writing half0"
1264 << " faces to OBJ file " << nm0 << endl;
1265 writeOBJ(nm0, half0Faces, ppPoints);
1267 fileName nm1("match2_"+name()+"_half1_faces.obj");
1268 Pout<< "cyclicPolyPatch::order : Writing half1"
1269 << " faces to OBJ file " << nm1 << endl;
1270 writeOBJ(nm1, half1Faces, pp.points());
1274 boundaryMesh().mesh().time().path()
1275 /"match2_"+name()+"_faceCentres.obj"
1277 Pout<< "cyclicPolyPatch::order : "
1278 << "Dumping currently found cyclic match as lines between"
1279 << " corresponding face centres to file " << ccStr.name()
1282 // Recalculate untransformed face centres
1283 //pointField rawHalf0Ctrs = calcFaceCentres(half0Faces, pp.points());
1286 forAll(half1Ctrs, i)
1288 if (from1To0[i] != -1)
1290 // Write edge between c1 and c0
1291 //const point& c0 = rawHalf0Ctrs[from1To0[i]];
1292 const point& c0 = half0Ctrs[from1To0[i]];
1293 const point& c1 = half1Ctrs[i];
1294 writeOBJ(ccStr, c0, c1, vertI);
1301 // 3. Baffles(coincident faces) converted into cyclics (e.g. jump)
1302 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1308 forAll(*this, faceI)
1310 const face& f = pp.localFaces()[faceI];
1311 const labelList& pFaces = pp.pointFaces()[f[0]];
1313 label matchedFaceI = -1;
1317 label otherFaceI = pFaces[i];
1319 if (otherFaceI > faceI)
1321 const face& otherF = pp.localFaces()[otherFaceI];
1323 // Note: might pick up two similar oriented faces
1324 // (but that is illegal anyway)
1327 matchedFaceI = otherFaceI;
1333 if (matchedFaceI != -1)
1335 half0ToPatch[baffleI] = faceI;
1336 half1ToPatch[baffleI] = matchedFaceI;
1341 if (baffleI == halfSize)
1343 // And redo all matching
1344 half0Faces = UIndirectList<face>(pp, half0ToPatch);
1345 half1Faces = UIndirectList<face>(pp, half1ToPatch);
1347 getCentresAndAnchors
1360 // Geometric match of face centre vectors
1361 matchedAll = matchPoints
1372 Pout<< "cyclicPolyPatch::order : test if baffles:"
1373 << matchedAll << endl;
1375 fileName nm0("match3_"+name()+"_half0_faces.obj");
1376 Pout<< "cyclicPolyPatch::order : Writing half0"
1377 << " faces to OBJ file " << nm0 << endl;
1378 writeOBJ(nm0, half0Faces, ppPoints);
1380 fileName nm1("match3_"+name()+"_half1_faces.obj");
1381 Pout<< "cyclicPolyPatch::order : Writing half1"
1382 << " faces to OBJ file " << nm1 << endl;
1383 writeOBJ(nm1, half1Faces, pp.points());
1387 boundaryMesh().mesh().time().path()
1388 /"match3_"+ name() + "_faceCentres.obj"
1390 Pout<< "cyclicPolyPatch::order : "
1391 << "Dumping currently found cyclic match as lines between"
1392 << " corresponding face centres to file " << ccStr.name()
1395 // Recalculate untransformed face centres
1396 //pointField rawHalf0Ctrs = calcFaceCentres(half0Faces, pp.points());
1399 forAll(half1Ctrs, i)
1401 if (from1To0[i] != -1)
1403 // Write edge between c1 and c0
1404 //const point& c0 = rawHalf0Ctrs[from1To0[i]];
1405 const point& c0 = half0Ctrs[from1To0[i]];
1406 const point& c1 = half1Ctrs[i];
1407 writeOBJ(ccStr, c0, c1, vertI);
1415 // 4. Automatic geometric ordering
1416 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1420 // Split faces according to feature angle or topology
1421 bool okSplit = getGeometricHalves(pp, half0ToPatch, half1ToPatch);
1425 // Did not split into two equal parts.
1429 // And redo all matching
1430 half0Faces = UIndirectList<face>(pp, half0ToPatch);
1431 half1Faces = UIndirectList<face>(pp, half1ToPatch);
1433 getCentresAndAnchors
1446 // Geometric match of face centre vectors
1447 matchedAll = matchPoints
1458 Pout<< "cyclicPolyPatch::order : automatic ordering result:"
1459 << matchedAll << endl;
1461 fileName nm0("match4_"+name()+"_half0_faces.obj");
1462 Pout<< "cyclicPolyPatch::order : Writing half0"
1463 << " faces to OBJ file " << nm0 << endl;
1464 writeOBJ(nm0, half0Faces, ppPoints);
1466 fileName nm1("match4_"+name()+"_half1_faces.obj");
1467 Pout<< "cyclicPolyPatch::order : Writing half1"
1468 << " faces to OBJ file " << nm1 << endl;
1469 writeOBJ(nm1, half1Faces, pp.points());
1473 boundaryMesh().mesh().time().path()
1474 /"match4_"+ name() + "_faceCentres.obj"
1476 Pout<< "cyclicPolyPatch::order : "
1477 << "Dumping currently found cyclic match as lines between"
1478 << " corresponding face centres to file " << ccStr.name()
1481 // Recalculate untransformed face centres
1482 //pointField rawHalf0Ctrs = calcFaceCentres(half0Faces, pp.points());
1485 forAll(half1Ctrs, i)
1487 if (from1To0[i] != -1)
1489 // Write edge between c1 and c0
1490 //const point& c0 = rawHalf0Ctrs[from1To0[i]];
1491 const point& c0 = half0Ctrs[from1To0[i]];
1492 const point& c1 = half1Ctrs[i];
1493 writeOBJ(ccStr, c0, c1, vertI);
1500 if (!matchedAll || debug)
1503 fileName nm0(name()+"_half0_faces.obj");
1504 Pout<< "cyclicPolyPatch::order : Writing half0"
1505 << " faces to OBJ file " << nm0 << endl;
1506 writeOBJ(nm0, half0Faces, pp.points());
1508 fileName nm1(name()+"_half1_faces.obj");
1509 Pout<< "cyclicPolyPatch::order : Writing half1"
1510 << " faces to OBJ file " << nm1 << endl;
1511 writeOBJ(nm1, half1Faces, pp.points());
1515 boundaryMesh().mesh().time().path()
1516 /name() + "_faceCentres.obj"
1518 Pout<< "cyclicPolyPatch::order : "
1519 << "Dumping currently found cyclic match as lines between"
1520 << " corresponding face centres to file " << ccStr.name()
1523 // Recalculate untransformed face centres
1524 //pointField rawHalf0Ctrs = calcFaceCentres(half0Faces, pp.points());
1527 forAll(half1Ctrs, i)
1529 if (from1To0[i] != -1)
1531 // Write edge between c1 and c0
1532 //const point& c0 = rawHalf0Ctrs[from1To0[i]];
1533 const point& c0 = half0Ctrs[from1To0[i]];
1534 const point& c1 = half1Ctrs[i];
1535 writeOBJ(ccStr, c0, c1, vertI);
1545 "cyclicPolyPatch::order"
1546 "(const primitivePatch&, labelList&, labelList&) const"
1547 ) << "Patch:" << name() << " : "
1548 << "Cannot match vectors to faces on both sides of patch" << endl
1549 << " Perhaps your faces do not match?"
1550 << " The obj files written contain the current match." << endl
1551 << " Continuing with incorrect face ordering from now on!"
1558 // Set faceMap such that half0 faces get first and corresponding half1
1562 true, // report if anchor matching error
1574 // Return false if no change neccesary, true otherwise.
1576 forAll(faceMap, faceI)
1578 if (faceMap[faceI] != faceI || rotation[faceI] != 0)
1588 void Foam::cyclicPolyPatch::write(Ostream& os) const
1590 polyPatch::write(os);
1591 os.writeKeyword("featureCos") << featureCos_ << token::END_STATEMENT << nl;
1596 os.writeKeyword("transform") << transformTypeNames[ROTATIONAL]
1597 << token::END_STATEMENT << nl;
1598 os.writeKeyword("rotationAxis") << rotationAxis_
1599 << token::END_STATEMENT << nl;
1600 os.writeKeyword("rotationCentre") << rotationCentre_
1601 << token::END_STATEMENT << nl;
1606 os.writeKeyword("transform") << transformTypeNames[TRANSLATIONAL]
1607 << token::END_STATEMENT << nl;
1608 os.writeKeyword("separationVector") << separationVector_
1609 << token::END_STATEMENT << nl;
1614 // no additional info to write
1620 // ************************************************************************* //