/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2016 OpenFOAM Foundation Copyright (C) 2019 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. OpenFOAM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenFOAM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenFOAM. If not, see . \*---------------------------------------------------------------------------*/ #include "oldCyclicPolyPatch.H" #include "addToRunTimeSelectionTable.H" #include "polyBoundaryMesh.H" #include "polyMesh.H" #include "OFstream.H" #include "patchZones.H" #include "matchPoints.H" #include "Time.H" #include "transformList.H" #include "cyclicPolyPatch.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // namespace Foam { defineTypeNameAndDebug(oldCyclicPolyPatch, 0); addToRunTimeSelectionTable(polyPatch, oldCyclicPolyPatch, word); addToRunTimeSelectionTable(polyPatch, oldCyclicPolyPatch, dictionary); } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Foam::pointField Foam::oldCyclicPolyPatch::calcFaceCentres ( const UList& faces, const pointField& points ) { pointField ctrs(faces.size()); forAll(faces, facei) { ctrs[facei] = faces[facei].centre(points); } return ctrs; } Foam::pointField Foam::oldCyclicPolyPatch::getAnchorPoints ( const UList& faces, const pointField& points ) { pointField anchors(faces.size()); forAll(faces, facei) { anchors[facei] = points[faces[facei][0]]; } return anchors; } Foam::label Foam::oldCyclicPolyPatch::findMaxArea ( const UList& points, const UList& faces ) { label maxI = -1; scalar maxAreaSqr = -GREAT; forAll(faces, facei) { scalar areaSqr = faces[facei].magSqr(points); if (maxAreaSqr < areaSqr) { maxAreaSqr = areaSqr; maxI = facei; } } return maxI; } bool Foam::oldCyclicPolyPatch::getGeometricHalves ( const primitivePatch& pp, labelList& half0ToPatch, labelList& half1ToPatch ) const { // Get geometric zones of patch by looking at normals. // Method 1: any edge with sharpish angle is edge between two halves. // (this will handle e.g. wedge geometries). // Also two fully disconnected regions will be handled this way. // Method 2: sort faces into two halves based on face normal. // Calculate normals const vectorField& faceNormals = pp.faceNormals(); // Find edges with sharp angles. boolList regionEdge(pp.nEdges(), false); const labelListList& edgeFaces = pp.edgeFaces(); label nRegionEdges = 0; forAll(edgeFaces, edgeI) { const labelList& eFaces = edgeFaces[edgeI]; // Check manifold edges for sharp angle. // (Non-manifold already handled by patchZones) if (eFaces.size() == 2) { if ((faceNormals[eFaces[0]] & faceNormals[eFaces[1]])< featureCos_) { regionEdge[edgeI] = true; nRegionEdges++; } } } // For every face determine zone it is connected to (without crossing // any regionEdge) patchZones ppZones(pp, regionEdge); if (debug) { Pout<< "oldCyclicPolyPatch::getGeometricHalves : " << "Found " << nRegionEdges << " edges on patch " << name() << " where the cos of the angle between two connected faces" << " was less than " << featureCos_ << nl << "Patch divided by these and by single sides edges into " << ppZones.nZones() << " parts." << endl; // Dumping zones to obj files. labelList nZoneFaces(ppZones.nZones()); for (label zoneI = 0; zoneI < ppZones.nZones(); zoneI++) { OFstream stream ( boundaryMesh().mesh().time().path() /name()+"_zone_"+Foam::name(zoneI)+".obj" ); Pout<< "oldCyclicPolyPatch::getGeometricHalves : Writing zone " << zoneI << " face centres to OBJ file " << stream.name() << endl; labelList zoneFaces(findIndices(ppZones, zoneI)); forAll(zoneFaces, i) { writeOBJ(stream, pp[zoneFaces[i]].centre(pp.points())); } nZoneFaces[zoneI] = zoneFaces.size(); } } if (ppZones.nZones() == 2) { half0ToPatch = findIndices(ppZones, 0); half1ToPatch = findIndices(ppZones, 1); } else { if (debug) { Pout<< "oldCyclicPolyPatch::getGeometricHalves :" << " falling back to face-normal comparison" << endl; } label n0Faces = 0; half0ToPatch.setSize(pp.size()); label n1Faces = 0; half1ToPatch.setSize(pp.size()); // Compare to face 0 normal. forAll(faceNormals, facei) { if ((faceNormals[facei] & faceNormals[0]) > 0) { half0ToPatch[n0Faces++] = facei; } else { half1ToPatch[n1Faces++] = facei; } } half0ToPatch.setSize(n0Faces); half1ToPatch.setSize(n1Faces); if (debug) { Pout<< "oldCyclicPolyPatch::getGeometricHalves :" << " Number of faces per zone:(" << n0Faces << ' ' << n1Faces << ')' << endl; } } if (half0ToPatch.size() != half1ToPatch.size()) { fileName casePath(boundaryMesh().mesh().time().path()); // Dump halves { fileName nm0(casePath/name()+"_half0_faces.obj"); Pout<< "oldCyclicPolyPatch::getGeometricHalves : Writing half0" << " faces to OBJ file " << nm0 << endl; writeOBJ(nm0, UIndirectList(pp, half0ToPatch)(), pp.points()); fileName nm1(casePath/name()+"_half1_faces.obj"); Pout<< "oldCyclicPolyPatch::getGeometricHalves : Writing half1" << " faces to OBJ file " << nm1 << endl; writeOBJ(nm1, UIndirectList(pp, half1ToPatch)(), pp.points()); } // Dump face centres { OFstream str0(casePath/name()+"_half0.obj"); Pout<< "oldCyclicPolyPatch::getGeometricHalves : Writing half0" << " face centres to OBJ file " << str0.name() << endl; forAll(half0ToPatch, i) { writeOBJ(str0, pp[half0ToPatch[i]].centre(pp.points())); } OFstream str1(casePath/name()+"_half1.obj"); Pout<< "oldCyclicPolyPatch::getGeometricHalves : Writing half1" << " face centres to OBJ file " << str1.name() << endl; forAll(half1ToPatch, i) { writeOBJ(str1, pp[half1ToPatch[i]].centre(pp.points())); } } SeriousErrorInFunction << "Patch " << name() << " gets decomposed in two zones of" << "inequal size: " << half0ToPatch.size() << " and " << half1ToPatch.size() << endl << "This means that the patch is either not two separate regions" << " or one region where the angle between the different regions" << " is not sufficiently sharp." << endl << "Please adapt the featureCos setting." << endl << "Continuing with incorrect face ordering from now on!" << endl; return false; } return true; } void Foam::oldCyclicPolyPatch::getCentresAndAnchors ( const primitivePatch& pp, const faceList& half0Faces, const faceList& half1Faces, pointField& ppPoints, pointField& half0Ctrs, pointField& half1Ctrs, pointField& anchors0, scalarField& tols ) const { // Get geometric data on both halves. half0Ctrs = calcFaceCentres(half0Faces, pp.points()); anchors0 = getAnchorPoints(half0Faces, pp.points()); half1Ctrs = calcFaceCentres(half1Faces, pp.points()); switch (transform()) { case ROTATIONAL: { label face0 = getConsistentRotationFace(half0Ctrs); label face1 = getConsistentRotationFace(half1Ctrs); const vector n0 = normalised ( (half0Ctrs[face0] - rotationCentre_) ^ rotationAxis_ ); const vector n1 = normalised ( (half1Ctrs[face1] - rotationCentre_) ^ -rotationAxis_ ); if (debug) { Pout<< "oldCyclicPolyPatch::getCentresAndAnchors :" << " Specified rotation :" << " n0:" << n0 << " n1:" << n1 << endl; } // Rotation (around origin) const tensor reverseT(rotationTensor(n0, -n1)); // Rotation forAll(half0Ctrs, facei) { half0Ctrs[facei] = Foam::transform(reverseT, half0Ctrs[facei]); anchors0[facei] = Foam::transform(reverseT, anchors0[facei]); } ppPoints = Foam::transform(reverseT, pp.points()); break; } //- Problem: usually specified translation is not accurate enough //- To get proper match so keep automatic determination over here. //case TRANSLATIONAL: //{ // // Transform 0 points. // // if (debug) // { // Pout<< "oldCyclicPolyPatch::getCentresAndAnchors :" // << "Specified translation : " << separationVector_ // << endl; // } // // half0Ctrs += separationVector_; // anchors0 += separationVector_; // break; //} default: { // Assumes that cyclic is planar. This is also the initial // condition for patches without faces. // Determine the face with max area on both halves. These // two faces are used to determine the transformation tensors label max0I = findMaxArea(pp.points(), half0Faces); const vector n0 = half0Faces[max0I].unitNormal(pp.points()); label max1I = findMaxArea(pp.points(), half1Faces); const vector n1 = half1Faces[max1I].unitNormal(pp.points()); if (mag(n0 & n1) < 1-matchTolerance()) { if (debug) { Pout<< "oldCyclicPolyPatch::getCentresAndAnchors :" << " Detected rotation :" << " n0:" << n0 << " n1:" << n1 << endl; } // Rotation (around origin) const tensor reverseT(rotationTensor(n0, -n1)); // Rotation forAll(half0Ctrs, facei) { half0Ctrs[facei] = Foam::transform ( reverseT, half0Ctrs[facei] ); anchors0[facei] = Foam::transform ( reverseT, anchors0[facei] ); } ppPoints = Foam::transform(reverseT, pp.points()); } else { // Parallel translation. Get average of all used points. primitiveFacePatch half0(half0Faces, pp.points()); const pointField& half0Pts = half0.localPoints(); const point ctr0(sum(half0Pts)/half0Pts.size()); primitiveFacePatch half1(half1Faces, pp.points()); const pointField& half1Pts = half1.localPoints(); const point ctr1(sum(half1Pts)/half1Pts.size()); if (debug) { Pout<< "oldCyclicPolyPatch::getCentresAndAnchors :" << " Detected translation :" << " n0:" << n0 << " n1:" << n1 << " ctr0:" << ctr0 << " ctr1:" << ctr1 << endl; } half0Ctrs += ctr1 - ctr0; anchors0 += ctr1 - ctr0; ppPoints = pp.points() + ctr1 - ctr0; } break; } } // Calculate typical distance per face tols = matchTolerance()*calcFaceTol(half1Faces, pp.points(), half1Ctrs); } bool Foam::oldCyclicPolyPatch::matchAnchors ( const bool report, const primitivePatch& pp, const labelList& half0ToPatch, const pointField& anchors0, const labelList& half1ToPatch, const faceList& half1Faces, const labelList& from1To0, const scalarField& tols, labelList& faceMap, labelList& rotation ) const { // Set faceMap such that half0 faces get first and corresponding half1 // faces last. forAll(half0ToPatch, half0Facei) { // Label in original patch label patchFacei = half0ToPatch[half0Facei]; faceMap[patchFacei] = half0Facei; // No rotation rotation[patchFacei] = 0; } bool fullMatch = true; forAll(from1To0, half1Facei) { label patchFacei = half1ToPatch[half1Facei]; // This face has to match the corresponding one on half0. label half0Facei = from1To0[half1Facei]; label newFacei = half0Facei + pp.size()/2; faceMap[patchFacei] = newFacei; // Rotate patchFacei such that its f[0] aligns with that of // the corresponding face // (which after shuffling will be at position half0Facei) const point& wantedAnchor = anchors0[half0Facei]; rotation[newFacei] = getRotation ( pp.points(), half1Faces[half1Facei], wantedAnchor, tols[half1Facei] ); if (rotation[newFacei] == -1) { fullMatch = false; if (report) { const face& f = half1Faces[half1Facei]; SeriousErrorInFunction << "Patch:" << name() << " : " << "Cannot find point on face " << f << " with vertices:" << UIndirectList(pp.points(), f) << " that matches point " << wantedAnchor << " when matching the halves of cyclic patch " << name() << endl << "Continuing with incorrect face ordering from now on!" << endl; } } } return fullMatch; } Foam::label Foam::oldCyclicPolyPatch::getConsistentRotationFace ( const pointField& faceCentres ) const { const scalarField magRadSqr ( magSqr((faceCentres - rotationCentre_) ^ rotationAxis_) ); scalarField axisLen ( (faceCentres - rotationCentre_) & rotationAxis_ ); axisLen = axisLen - min(axisLen); const scalarField magLenSqr ( magRadSqr + axisLen*axisLen ); label rotFace = -1; scalar maxMagLenSqr = -GREAT; scalar maxMagRadSqr = -GREAT; forAll(faceCentres, i) { if (magLenSqr[i] >= maxMagLenSqr) { if (magRadSqr[i] > maxMagRadSqr) { rotFace = i; maxMagLenSqr = magLenSqr[i]; maxMagRadSqr = magRadSqr[i]; } } } if (debug) { Info<< "getConsistentRotationFace(const pointField&)" << nl << " rotFace = " << rotFace << nl << " point = " << faceCentres[rotFace] << endl; } return rotFace; } // * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * * * * * // Foam::oldCyclicPolyPatch::oldCyclicPolyPatch ( const word& name, const label size, const label start, const label index, const polyBoundaryMesh& bm, const word& patchType, const transformType transform ) : coupledPolyPatch(name, size, start, index, bm, patchType, transform), featureCos_(0.9), rotationAxis_(Zero), rotationCentre_(Zero), separationVector_(Zero) {} Foam::oldCyclicPolyPatch::oldCyclicPolyPatch ( const word& name, const dictionary& dict, const label index, const polyBoundaryMesh& bm, const word& patchType ) : coupledPolyPatch(name, dict, index, bm, patchType), featureCos_(0.9), rotationAxis_(Zero), rotationCentre_(Zero), separationVector_(Zero) { if (dict.found("neighbourPatch")) { FatalIOErrorInFunction(dict) << "Found \"neighbourPatch\" entry when reading cyclic patch " << name << endl << "Is this mesh already with split cyclics?" << endl << "If so run a newer version that supports it" << ", if not comment out the \"neighbourPatch\" entry and re-run" << exit(FatalIOError); } dict.readIfPresent("featureCos", featureCos_); switch (transform()) { case ROTATIONAL: { dict.readEntry("rotationAxis", rotationAxis_); dict.readEntry("rotationCentre", rotationCentre_); break; } case TRANSLATIONAL: { dict.readEntry("separationVector", separationVector_); break; } default: { // no additional info required } } } Foam::oldCyclicPolyPatch::oldCyclicPolyPatch ( const oldCyclicPolyPatch& pp, const polyBoundaryMesh& bm ) : coupledPolyPatch(pp, bm), featureCos_(pp.featureCos_), rotationAxis_(pp.rotationAxis_), rotationCentre_(pp.rotationCentre_), separationVector_(pp.separationVector_) {} Foam::oldCyclicPolyPatch::oldCyclicPolyPatch ( const oldCyclicPolyPatch& pp, const polyBoundaryMesh& bm, const label index, const label newSize, const label newStart ) : coupledPolyPatch(pp, bm, index, newSize, newStart), featureCos_(pp.featureCos_), rotationAxis_(pp.rotationAxis_), rotationCentre_(pp.rotationCentre_), separationVector_(pp.separationVector_) {} // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // Foam::oldCyclicPolyPatch::~oldCyclicPolyPatch() {} // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // void Foam::oldCyclicPolyPatch::initGeometry(PstreamBuffers& pBufs) { polyPatch::initGeometry(pBufs); } void Foam::oldCyclicPolyPatch::calcGeometry ( const primitivePatch& referPatch, const pointField& thisCtrs, const vectorField& thisAreas, const pointField& thisCc, const pointField& nbrCtrs, const vectorField& nbrAreas, const pointField& nbrCc ) {} void Foam::oldCyclicPolyPatch::calcGeometry(PstreamBuffers& pBufs) {} void Foam::oldCyclicPolyPatch::initMovePoints ( PstreamBuffers& pBufs, const pointField& p ) { polyPatch::initMovePoints(pBufs, p); } void Foam::oldCyclicPolyPatch::movePoints ( PstreamBuffers& pBufs, const pointField& p ) { polyPatch::movePoints(pBufs, p); } void Foam::oldCyclicPolyPatch::initUpdateMesh(PstreamBuffers& pBufs) { polyPatch::initUpdateMesh(pBufs); } void Foam::oldCyclicPolyPatch::updateMesh(PstreamBuffers& pBufs) { polyPatch::updateMesh(pBufs); } void Foam::oldCyclicPolyPatch::initOrder ( PstreamBuffers&, const primitivePatch& pp ) const {} // Return new ordering. Ordering is -faceMap: for every face index // the new face -rotation:for every new face the clockwise shift // of the original face. Return false if nothing changes (faceMap // is identity, rotation is 0) bool Foam::oldCyclicPolyPatch::order ( PstreamBuffers&, const primitivePatch& pp, labelList& faceMap, labelList& rotation ) const { faceMap.setSize(pp.size()); faceMap = -1; rotation.setSize(pp.size()); rotation = 0; if (pp.empty()) { // No faces, nothing to change. return false; } if (pp.size()&1) { FatalErrorInFunction << "Size of cyclic " << name() << " should be a multiple of 2" << ". It is " << pp.size() << abort(FatalError); } label halfSize = pp.size()/2; // Supplied primitivePatch already with new points. // Cyclics are limited to one transformation tensor // currently anyway (i.e. straight plane) so should not be too big a // problem. // Indices of faces on half0 labelList half0ToPatch; // Indices of faces on half1 labelList half1ToPatch; // 1. Test if already correctly oriented by starting from trivial ordering. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ half0ToPatch = identity(halfSize); half1ToPatch = half0ToPatch + halfSize; // Get faces faceList half0Faces(UIndirectList(pp, half0ToPatch)); faceList half1Faces(UIndirectList(pp, half1ToPatch)); // Get geometric quantities pointField half0Ctrs, half1Ctrs, anchors0, ppPoints; scalarField tols; getCentresAndAnchors ( pp, half0Faces, half1Faces, ppPoints, half0Ctrs, half1Ctrs, anchors0, tols ); // Geometric match of face centre vectors labelList from1To0; bool matchedAll = matchPoints ( half1Ctrs, half0Ctrs, tols, false, from1To0 ); if (debug) { Pout<< "oldCyclicPolyPatch::order : test if already ordered:" << matchedAll << endl; // Dump halves fileName nm0("match1_"+name()+"_half0_faces.obj"); Pout<< "oldCyclicPolyPatch::order : Writing half0" << " faces to OBJ file " << nm0 << endl; writeOBJ(nm0, half0Faces, ppPoints); fileName nm1("match1_"+name()+"_half1_faces.obj"); Pout<< "oldCyclicPolyPatch::order : Writing half1" << " faces to OBJ file " << nm1 << endl; writeOBJ(nm1, half1Faces, pp.points()); OFstream ccStr ( boundaryMesh().mesh().time().path() /"match1_"+ name() + "_faceCentres.obj" ); Pout<< "oldCyclicPolyPatch::order : " << "Dumping currently found cyclic match as lines between" << " corresponding face centres to file " << ccStr.name() << endl; // Recalculate untransformed face centres //pointField rawHalf0Ctrs = calcFaceCentres(half0Faces, pp.points()); label vertI = 0; forAll(half1Ctrs, i) { //if (from1To0[i] != -1) { // Write edge between c1 and c0 //const point& c0 = rawHalf0Ctrs[from1To0[i]]; //const point& c0 = half0Ctrs[from1To0[i]]; const point& c0 = half0Ctrs[i]; const point& c1 = half1Ctrs[i]; writeOBJ(ccStr, c0, c1, vertI); } } } // 2. Ordered in pairs (so 0,1 coupled and 2,3 etc.) // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ if (!matchedAll) { label facei = 0; for (label i = 0; i < halfSize; i++) { half0ToPatch[i] = facei++; half1ToPatch[i] = facei++; } // And redo all matching half0Faces = UIndirectList(pp, half0ToPatch); half1Faces = UIndirectList(pp, half1ToPatch); getCentresAndAnchors ( pp, half0Faces, half1Faces, ppPoints, half0Ctrs, half1Ctrs, anchors0, tols ); // Geometric match of face centre vectors matchedAll = matchPoints ( half1Ctrs, half0Ctrs, tols, false, from1To0 ); if (debug) { Pout<< "oldCyclicPolyPatch::order : test if pairwise ordered:" << matchedAll << endl; // Dump halves fileName nm0("match2_"+name()+"_half0_faces.obj"); Pout<< "oldCyclicPolyPatch::order : Writing half0" << " faces to OBJ file " << nm0 << endl; writeOBJ(nm0, half0Faces, ppPoints); fileName nm1("match2_"+name()+"_half1_faces.obj"); Pout<< "oldCyclicPolyPatch::order : Writing half1" << " faces to OBJ file " << nm1 << endl; writeOBJ(nm1, half1Faces, pp.points()); OFstream ccStr ( boundaryMesh().mesh().time().path() /"match2_"+name()+"_faceCentres.obj" ); Pout<< "oldCyclicPolyPatch::order : " << "Dumping currently found cyclic match as lines between" << " corresponding face centres to file " << ccStr.name() << endl; // Recalculate untransformed face centres label vertI = 0; forAll(half1Ctrs, i) { if (from1To0[i] != -1) { // Write edge between c1 and c0 const point& c0 = half0Ctrs[from1To0[i]]; const point& c1 = half1Ctrs[i]; writeOBJ(ccStr, c0, c1, vertI); } } } } // 3. Baffles(coincident faces) converted into cyclics (e.g. jump) // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ if (!matchedAll) { label baffleI = 0; forAll(pp, facei) { const face& f = pp.localFaces()[facei]; const labelList& pFaces = pp.pointFaces()[f[0]]; label matchedFacei = -1; forAll(pFaces, i) { label otherFacei = pFaces[i]; if (otherFacei > facei) { const face& otherF = pp.localFaces()[otherFacei]; // Note: might pick up two similar oriented faces // (but that is illegal anyway) if (f == otherF) { matchedFacei = otherFacei; break; } } } if (matchedFacei != -1) { half0ToPatch[baffleI] = facei; half1ToPatch[baffleI] = matchedFacei; baffleI++; } } if (baffleI == halfSize) { // And redo all matching half0Faces = UIndirectList(pp, half0ToPatch); half1Faces = UIndirectList(pp, half1ToPatch); getCentresAndAnchors ( pp, half0Faces, half1Faces, ppPoints, half0Ctrs, half1Ctrs, anchors0, tols ); // Geometric match of face centre vectors matchedAll = matchPoints ( half1Ctrs, half0Ctrs, tols, false, from1To0 ); if (debug) { Pout<< "oldCyclicPolyPatch::order : test if baffles:" << matchedAll << endl; // Dump halves fileName nm0("match3_"+name()+"_half0_faces.obj"); Pout<< "oldCyclicPolyPatch::order : Writing half0" << " faces to OBJ file " << nm0 << endl; writeOBJ(nm0, half0Faces, ppPoints); fileName nm1("match3_"+name()+"_half1_faces.obj"); Pout<< "oldCyclicPolyPatch::order : Writing half1" << " faces to OBJ file " << nm1 << endl; writeOBJ(nm1, half1Faces, pp.points()); OFstream ccStr ( boundaryMesh().mesh().time().path() /"match3_"+ name() + "_faceCentres.obj" ); Pout<< "oldCyclicPolyPatch::order : " << "Dumping currently found cyclic match as lines between" << " corresponding face centres to file " << ccStr.name() << endl; // Recalculate untransformed face centres label vertI = 0; forAll(half1Ctrs, i) { if (from1To0[i] != -1) { // Write edge between c1 and c0 const point& c0 = half0Ctrs[from1To0[i]]; const point& c1 = half1Ctrs[i]; writeOBJ(ccStr, c0, c1, vertI); } } } } } // 4. Automatic geometric ordering // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ if (!matchedAll) { // Split faces according to feature angle or topology bool okSplit = getGeometricHalves(pp, half0ToPatch, half1ToPatch); if (!okSplit) { // Did not split into two equal parts. return false; } // And redo all matching half0Faces = UIndirectList(pp, half0ToPatch); half1Faces = UIndirectList(pp, half1ToPatch); getCentresAndAnchors ( pp, half0Faces, half1Faces, ppPoints, half0Ctrs, half1Ctrs, anchors0, tols ); // Geometric match of face centre vectors matchedAll = matchPoints ( half1Ctrs, half0Ctrs, tols, false, from1To0 ); if (debug) { Pout<< "oldCyclicPolyPatch::order : automatic ordering result:" << matchedAll << endl; // Dump halves fileName nm0("match4_"+name()+"_half0_faces.obj"); Pout<< "oldCyclicPolyPatch::order : Writing half0" << " faces to OBJ file " << nm0 << endl; writeOBJ(nm0, half0Faces, ppPoints); fileName nm1("match4_"+name()+"_half1_faces.obj"); Pout<< "oldCyclicPolyPatch::order : Writing half1" << " faces to OBJ file " << nm1 << endl; writeOBJ(nm1, half1Faces, pp.points()); OFstream ccStr ( boundaryMesh().mesh().time().path() /"match4_"+ name() + "_faceCentres.obj" ); Pout<< "oldCyclicPolyPatch::order : " << "Dumping currently found cyclic match as lines between" << " corresponding face centres to file " << ccStr.name() << endl; // Recalculate untransformed face centres label vertI = 0; forAll(half1Ctrs, i) { if (from1To0[i] != -1) { // Write edge between c1 and c0 const point& c0 = half0Ctrs[from1To0[i]]; const point& c1 = half1Ctrs[i]; writeOBJ(ccStr, c0, c1, vertI); } } } } if (!matchedAll || debug) { // Dump halves fileName nm0(name()+"_half0_faces.obj"); Pout<< "oldCyclicPolyPatch::order : Writing half0" << " faces to OBJ file " << nm0 << endl; writeOBJ(nm0, half0Faces, pp.points()); fileName nm1(name()+"_half1_faces.obj"); Pout<< "oldCyclicPolyPatch::order : Writing half1" << " faces to OBJ file " << nm1 << endl; writeOBJ(nm1, half1Faces, pp.points()); OFstream ccStr ( boundaryMesh().mesh().time().path() /name() + "_faceCentres.obj" ); Pout<< "oldCyclicPolyPatch::order : " << "Dumping currently found cyclic match as lines between" << " corresponding face centres to file " << ccStr.name() << endl; // Recalculate untransformed face centres //pointField rawHalf0Ctrs = calcFaceCentres(half0Faces, pp.points()); label vertI = 0; forAll(half1Ctrs, i) { if (from1To0[i] != -1) { // Write edge between c1 and c0 //const point& c0 = rawHalf0Ctrs[from1To0[i]]; const point& c0 = half0Ctrs[from1To0[i]]; const point& c1 = half1Ctrs[i]; writeOBJ(ccStr, c0, c1, vertI); } } } if (!matchedAll) { SeriousErrorInFunction << "Patch:" << name() << " : " << "Cannot match vectors to faces on both sides of patch" << endl << " Perhaps your faces do not match?" << " The obj files written contain the current match." << endl << " Continuing with incorrect face ordering from now on!" << endl; return false; } // Set faceMap such that half0 faces get first and corresponding half1 // faces last. matchAnchors ( true, // report if anchor matching error pp, half0ToPatch, anchors0, half1ToPatch, half1Faces, from1To0, tols, faceMap, rotation ); // Return false if no change necessary, true otherwise. forAll(faceMap, facei) { if (faceMap[facei] != facei || rotation[facei] != 0) { return true; } } return false; } void Foam::oldCyclicPolyPatch::write(Ostream& os) const { // Replacement of polyPatch::write to write 'cyclic' instead of type(): os.writeEntry("type", cyclicPolyPatch::typeName); patchIdentifier::write(os); os.writeEntry("nFaces", size()); os.writeEntry("startFace", start()); os.writeEntry("featureCos", featureCos_); switch (transform()) { case ROTATIONAL: { os.writeEntry("rotationAxis", rotationAxis_); os.writeEntry("rotationCentre", rotationCentre_); break; } case TRANSLATIONAL: { os.writeEntry("separationVector", separationVector_); break; } default: { // no additional info to write } } WarningInFunction << "Please run foamUpgradeCyclics to convert these old-style" << " cyclics into two separate cyclics patches." << endl; } // ************************************************************************* //