/*---------------------------------------------------------------------------*\
========= |
\\ / 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();
}
// ************************************************************************* //