/*---------------------------------------------------------------------------*\ ========= | \\ / 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) 2018-2020 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 . Description Best thing is probably to look at attachDetach which does almost exactly the same but for the geometric matching of points and face centres. \*---------------------------------------------------------------------------*/ #include "perfectInterface.H" #include "polyTopoChanger.H" #include "polyMesh.H" #include "polyTopoChange.H" #include "addToRunTimeSelectionTable.H" #include "mapPolyMesh.H" #include "matchPoints.H" #include "polyModifyFace.H" #include "polyRemovePoint.H" #include "polyRemoveFace.H" #include "indirectPrimitivePatch.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // namespace Foam { defineTypeNameAndDebug(perfectInterface, 0); addToRunTimeSelectionTable ( polyMeshModifier, perfectInterface, dictionary ); } // Tolerance used as fraction of minimum edge length. const Foam::scalar Foam::perfectInterface::tol_ = 1e-3; // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // Foam::pointField Foam::perfectInterface::calcFaceCentres ( const indirectPrimitivePatch& pp ) { const pointField& points = pp.points(); pointField ctrs(pp.size()); forAll(ctrs, patchFacei) { ctrs[patchFacei] = pp[patchFacei].centre(points); } return ctrs; } // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::perfectInterface::perfectInterface ( const word& name, const label index, const polyTopoChanger& mme, const word& faceZoneName, const word& masterPatchName, const word& slavePatchName ) : polyMeshModifier(name, index, mme, true), faceZoneID_(faceZoneName, mme.mesh().faceZones()), masterPatchID_(masterPatchName, mme.mesh().boundaryMesh()), slavePatchID_(slavePatchName, mme.mesh().boundaryMesh()) {} Foam::perfectInterface::perfectInterface ( const word& name, const dictionary& dict, const label index, const polyTopoChanger& mme ) : polyMeshModifier(name, index, mme, dict.get("active")), faceZoneID_ ( dict.get("faceZoneName"), mme.mesh().faceZones() ), masterPatchID_ ( dict.get("masterPatchName"), mme.mesh().boundaryMesh() ), slavePatchID_ ( dict.get("slavePatchName"), mme.mesh().boundaryMesh() ) {} // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // bool Foam::perfectInterface::changeTopology() const { // If modifier is inactive, skip change if (!active()) { if (debug) { Pout<< "bool perfectInterface::changeTopology() const " << "for object " << name() << " : " << "Inactive" << endl; } return false; } else { // I want topo change every time step. return true; } } void Foam::perfectInterface::setRefinement ( const indirectPrimitivePatch& pp0, const indirectPrimitivePatch& pp1, polyTopoChange& ref ) const { const polyMesh& mesh = topoChanger().mesh(); const polyBoundaryMesh& patches = mesh.boundaryMesh(); // Some aliases const edgeList& edges0 = pp0.edges(); const pointField& pts0 = pp0.localPoints(); const pointField& pts1 = pp1.localPoints(); const labelList& meshPts0 = pp0.meshPoints(); const labelList& meshPts1 = pp1.meshPoints(); // Get local dimension as fraction of minimum edge length scalar minLen = GREAT; forAll(edges0, edgeI) { minLen = min(minLen, edges0[edgeI].mag(pts0)); } scalar typDim = tol_*minLen; if (debug) { Pout<< "typDim:" << typDim << " edges0:" << edges0.size() << " pts0:" << pts0.size() << " pts1:" << pts1.size() << " pp0:" << pp0.size() << " pp1:" << pp1.size() << endl; } // Determine pointMapping in mesh point labels. Uses geometric // comparison to find correspondence between patch points. labelList renumberPoints(mesh.points().size()); forAll(renumberPoints, i) { renumberPoints[i] = i; } { labelList from1To0Points(pts1.size()); bool matchOk = matchPoints ( pts1, pts0, scalarField(pts1.size(), typDim), // tolerance true, // verbose from1To0Points ); if (!matchOk) { FatalErrorInFunction << "Points on patch sides do not match to within tolerance " << typDim << exit(FatalError); } forAll(pts1, i) { renumberPoints[meshPts1[i]] = meshPts0[from1To0Points[i]]; } } // Calculate correspondence between patch faces labelList from0To1Faces(pp1.size()); bool matchOk = matchPoints ( calcFaceCentres(pp0), calcFaceCentres(pp1), scalarField(pp0.size(), typDim), // tolerance true, // verbose from0To1Faces ); if (!matchOk) { FatalErrorInFunction << "Face centres of patch sides do not match to within tolerance " << typDim << exit(FatalError); } // Now // - renumber faces using pts1 (except patch1 faces) // - remove patch1 faces. Remember cell label on owner side. // - modify patch0 faces to be internal. // 1. Get faces to be renumbered labelHashSet affectedFaces(2*pp1.size()); forAll(meshPts1, i) { label meshPointi = meshPts1[i]; if (meshPointi != renumberPoints[meshPointi]) { const labelList& pFaces = mesh.pointFaces()[meshPointi]; affectedFaces.insert(pFaces); } } forAll(pp1, i) { affectedFaces.erase(pp1.addressing()[i]); } // Remove patch0 from renumbered faces. Should not be necessary since // patch0 and 1 should not share any point (if created by mergeMeshing) // so affectedFaces should not contain any patch0 faces but you can // never be sure what the user is doing. forAll(pp0, i) { label facei = pp0.addressing()[i]; if (affectedFaces.erase(facei)) { WarningInFunction << "Found face " << facei << " vertices " << mesh.faces()[facei] << " whose points are" << " used both by master patch and slave patch" << endl; } } // 2. Renumber (non patch0/1) faces. for (const label facei : affectedFaces) { const face& f = mesh.faces()[facei]; face newFace(f.size()); forAll(newFace, fp) { newFace[fp] = renumberPoints[f[fp]]; } label nbr = -1; label patchi = -1; if (mesh.isInternalFace(facei)) { nbr = mesh.faceNeighbour()[facei]; } else { patchi = patches.whichPatch(facei); } label zoneID = mesh.faceZones().whichZone(facei); bool zoneFlip = false; if (zoneID >= 0) { const faceZone& fZone = mesh.faceZones()[zoneID]; zoneFlip = fZone.flipMap()[fZone.whichFace(facei)]; } ref.setAction ( polyModifyFace ( newFace, // modified face facei, // label of face being modified mesh.faceOwner()[facei], // owner nbr, // neighbour false, // face flip patchi, // patch for face false, // remove from zone zoneID, // zone for face zoneFlip // face flip in zone ) ); } // 3. Remove patch1 points forAll(meshPts1, i) { label meshPointi = meshPts1[i]; if (meshPointi != renumberPoints[meshPointi]) { ref.setAction(polyRemovePoint(meshPointi)); } } // 4. Remove patch1 faces forAll(pp1, i) { label facei = pp1.addressing()[i]; ref.setAction(polyRemoveFace(facei)); } // 5. Modify patch0 faces for new points (not really necessary; see // comment above about patch1 and patch0 never sharing points) and // becoming internal. const boolList& mfFlip = mesh.faceZones()[faceZoneID_.index()].flipMap(); forAll(pp0, i) { label facei = pp0.addressing()[i]; const face& f = mesh.faces()[facei]; face newFace(f.size()); forAll(newFace, fp) { newFace[fp] = renumberPoints[f[fp]]; } label own = mesh.faceOwner()[facei]; label pp1Facei = pp1.addressing()[from0To1Faces[i]]; label nbr = mesh.faceOwner()[pp1Facei]; if (own < nbr) { ref.setAction ( polyModifyFace ( newFace, // modified face facei, // label of face being modified own, // owner nbr, // neighbour false, // face flip -1, // patch for face false, // remove from zone faceZoneID_.index(), // zone for face mfFlip[i] // face flip in zone ) ); } else { ref.setAction ( polyModifyFace ( newFace.reverseFace(), // modified face facei, // label of face being modified nbr, // owner own, // neighbour true, // face flip -1, // patch for face false, // remove from zone faceZoneID_.index(), // zone for face !mfFlip[i] // face flip in zone ) ); } } } void Foam::perfectInterface::setRefinement(polyTopoChange& ref) const { if (debug) { Pout<< "bool perfectInterface::setRefinement(polyTopoChange&) const : " << "for object " << name() << " : " << "masterPatchID_:" << masterPatchID_ << " slavePatchID_:" << slavePatchID_ << " faceZoneID_:" << faceZoneID_ << endl; } if ( masterPatchID_.active() && slavePatchID_.active() && faceZoneID_.active() ) { const polyMesh& mesh = topoChanger().mesh(); const polyBoundaryMesh& patches = mesh.boundaryMesh(); const polyPatch& patch0 = patches[masterPatchID_.index()]; const polyPatch& patch1 = patches[slavePatchID_.index()]; indirectPrimitivePatch pp0 ( IndirectList(mesh.faces(), identity(patch0.range())), mesh.points() ); indirectPrimitivePatch pp1 ( IndirectList(mesh.faces(), identity(patch1.range())), mesh.points() ); setRefinement(pp0, pp1, ref); } } void Foam::perfectInterface::modifyMotionPoints(pointField& motionPoints) const { // Update only my points. Nothing to be done here as points already // shared by now. } void Foam::perfectInterface::updateMesh(const mapPolyMesh& morphMap) { // Mesh has changed topologically. Update local topological data const polyMesh& mesh = topoChanger().mesh(); faceZoneID_.update(mesh.faceZones()); masterPatchID_.update(mesh.boundaryMesh()); slavePatchID_.update(mesh.boundaryMesh()); } void Foam::perfectInterface::write(Ostream& os) const { os << nl << type() << nl << name()<< nl << faceZoneID_.name() << nl << masterPatchID_.name() << nl << slavePatchID_.name() << endl; } void Foam::perfectInterface::writeDict(Ostream& os) const { os << nl; os.beginBlock(name()); os.writeEntry("type", type()); os.writeEntry("active", active()); os.writeEntry("faceZoneName", faceZoneID_.name()); os.writeEntry("masterPatchName", masterPatchID_.name()); os.writeEntry("slavePatchName", slavePatchID_.name()); os.endBlock(); } // ************************************************************************* //