mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
449 lines
11 KiB
C
449 lines
11 KiB
C
/*---------------------------------------------------------------------------*\
|
|
========= |
|
|
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
|
\\ / O peration |
|
|
\\ / A nd | Copyright (C) 2011-2017 OpenFOAM Foundation
|
|
\\/ M anipulation |
|
|
-------------------------------------------------------------------------------
|
|
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 <http://www.gnu.org/licenses/>.
|
|
|
|
\*---------------------------------------------------------------------------*/
|
|
|
|
#include "directions.H"
|
|
#include "polyMesh.H"
|
|
#include "twoDPointCorrector.H"
|
|
#include "directionInfo.H"
|
|
#include "MeshWave.H"
|
|
#include "OFstream.H"
|
|
#include "meshTools.H"
|
|
#include "hexMatcher.H"
|
|
#include "globalMeshData.H"
|
|
|
|
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
|
|
|
|
const Foam::Enum
|
|
<
|
|
Foam::directions::directionType
|
|
>
|
|
Foam::directions::directionTypeNames_
|
|
{
|
|
{ directionType::TAN1, "tan1" },
|
|
{ directionType::TAN2, "tan2" },
|
|
{ directionType::NORMAL, "normal" },
|
|
};
|
|
|
|
|
|
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
|
|
|
void Foam::directions::writeOBJ(Ostream& os, const point& pt)
|
|
{
|
|
os << "v " << pt.x() << ' ' << pt.y() << ' ' << pt.z() << endl;
|
|
}
|
|
|
|
|
|
void Foam::directions::writeOBJ
|
|
(
|
|
Ostream& os,
|
|
const point& pt0,
|
|
const point& pt1,
|
|
label& vertI
|
|
)
|
|
{
|
|
writeOBJ(os, pt0);
|
|
writeOBJ(os, pt1);
|
|
|
|
os << "l " << vertI + 1 << ' ' << vertI + 2 << endl;
|
|
|
|
vertI += 2;
|
|
}
|
|
|
|
|
|
void Foam::directions::writeOBJ
|
|
(
|
|
const fileName& fName,
|
|
const primitiveMesh& mesh,
|
|
const vectorField& dirs
|
|
)
|
|
{
|
|
Pout<< "Writing cell info to " << fName << " as vectors at the cellCentres"
|
|
<< endl << endl;
|
|
|
|
OFstream xDirStream(fName);
|
|
|
|
label vertI = 0;
|
|
|
|
forAll(dirs, celli)
|
|
{
|
|
const point& ctr = mesh.cellCentres()[celli];
|
|
|
|
// Calculate local length scale
|
|
scalar minDist = GREAT;
|
|
|
|
const labelList& nbrs = mesh.cellCells()[celli];
|
|
|
|
forAll(nbrs, nbrI)
|
|
{
|
|
minDist = min(minDist, mag(mesh.cellCentres()[nbrs[nbrI]] - ctr));
|
|
}
|
|
|
|
scalar scale = 0.5*minDist;
|
|
|
|
writeOBJ(xDirStream, ctr, ctr + scale*dirs[celli], vertI);
|
|
}
|
|
}
|
|
|
|
|
|
void Foam::directions::check2D
|
|
(
|
|
const twoDPointCorrector* correct2DPtr,
|
|
const vector& vec
|
|
)
|
|
{
|
|
if (correct2DPtr)
|
|
{
|
|
if (mag(correct2DPtr->planeNormal() & vec) > 1e-6)
|
|
{
|
|
FatalErrorInFunction
|
|
<< "is not normal to plane defined in dynamicMeshDict."
|
|
<< endl
|
|
<< "Either make case 3D or adjust vector."
|
|
<< exit(FatalError);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
Foam::vectorField Foam::directions::propagateDirection
|
|
(
|
|
const polyMesh& mesh,
|
|
const bool useTopo,
|
|
const polyPatch& pp,
|
|
const vectorField& ppField,
|
|
const vector& defaultDir
|
|
)
|
|
{
|
|
// Seed all faces on patch
|
|
labelList changedFaces(pp.size());
|
|
List<directionInfo> changedFacesInfo(pp.size());
|
|
|
|
if (useTopo)
|
|
{
|
|
forAll(pp, patchFacei)
|
|
{
|
|
label meshFacei = pp.start() + patchFacei;
|
|
|
|
label celli = mesh.faceOwner()[meshFacei];
|
|
|
|
if (!hexMatcher().isA(mesh, celli))
|
|
{
|
|
FatalErrorInFunction
|
|
<< "useHexTopology specified but cell " << celli
|
|
<< " on face " << patchFacei << " of patch " << pp.name()
|
|
<< " is not a hex" << exit(FatalError);
|
|
}
|
|
|
|
const vector& cutDir = ppField[patchFacei];
|
|
|
|
// Get edge(bundle) on cell most in direction of cutdir
|
|
label edgeI = meshTools::cutDirToEdge(mesh, celli, cutDir);
|
|
|
|
// Convert edge into index on face
|
|
label faceIndex =
|
|
directionInfo::edgeToFaceIndex
|
|
(
|
|
mesh,
|
|
celli,
|
|
meshFacei,
|
|
edgeI
|
|
);
|
|
|
|
// Set initial face and direction
|
|
changedFaces[patchFacei] = meshFacei;
|
|
changedFacesInfo[patchFacei] =
|
|
directionInfo
|
|
(
|
|
faceIndex,
|
|
cutDir
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
forAll(pp, patchFacei)
|
|
{
|
|
changedFaces[patchFacei] = pp.start() + patchFacei;
|
|
changedFacesInfo[patchFacei] =
|
|
directionInfo
|
|
(
|
|
-2, // Geometric information only
|
|
ppField[patchFacei]
|
|
);
|
|
}
|
|
}
|
|
|
|
MeshWave<directionInfo> directionCalc
|
|
(
|
|
mesh,
|
|
changedFaces,
|
|
changedFacesInfo,
|
|
mesh.globalData().nTotalCells()+1
|
|
);
|
|
|
|
const List<directionInfo>& cellInfo = directionCalc.allCellInfo();
|
|
|
|
vectorField dirField(cellInfo.size());
|
|
|
|
label nUnset = 0;
|
|
label nGeom = 0;
|
|
label nTopo = 0;
|
|
|
|
forAll(cellInfo, celli)
|
|
{
|
|
label index = cellInfo[celli].index();
|
|
|
|
if (index == -3)
|
|
{
|
|
// Never visited
|
|
WarningInFunction
|
|
<< "Cell " << celli << " never visited to determine "
|
|
<< "local coordinate system" << endl
|
|
<< "Using direction " << defaultDir << " instead" << endl;
|
|
|
|
dirField[celli] = defaultDir;
|
|
|
|
nUnset++;
|
|
}
|
|
else if (index == -2)
|
|
{
|
|
// Geometric direction
|
|
dirField[celli] = cellInfo[celli].n();
|
|
|
|
nGeom++;
|
|
}
|
|
else if (index == -1)
|
|
{
|
|
FatalErrorInFunction
|
|
<< "Illegal index " << index << endl
|
|
<< "Value is only allowed on faces" << abort(FatalError);
|
|
}
|
|
else
|
|
{
|
|
// Topological edge cut. Convert into average cut direction.
|
|
dirField[celli] = meshTools::edgeToCutDir(mesh, celli, index);
|
|
|
|
nTopo++;
|
|
}
|
|
}
|
|
|
|
reduce(nGeom, sumOp<label>());
|
|
reduce(nTopo, sumOp<label>());
|
|
reduce(nUnset, sumOp<label>());
|
|
|
|
Info<< "Calculated local coords for " << defaultDir
|
|
<< endl
|
|
<< " Geometric cut cells : " << nGeom << endl
|
|
<< " Topological cut cells : " << nTopo << endl
|
|
<< " Unset cells : " << nUnset << endl
|
|
<< endl;
|
|
|
|
return dirField;
|
|
}
|
|
|
|
|
|
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
|
|
|
|
Foam::directions::directions
|
|
(
|
|
const polyMesh& mesh,
|
|
const dictionary& dict,
|
|
const twoDPointCorrector* correct2DPtr
|
|
)
|
|
:
|
|
List<vectorField>(wordList(dict.lookup("directions")).size())
|
|
{
|
|
const wordList wantedDirs(dict.lookup("directions"));
|
|
const word coordSystem(dict.lookup("coordinateSystem"));
|
|
|
|
bool wantNormal = false;
|
|
bool wantTan1 = false;
|
|
bool wantTan2 = false;
|
|
label nDirs = 0;
|
|
|
|
if (coordSystem != "fieldBased")
|
|
{
|
|
for (const word& wantedName : wantedDirs)
|
|
{
|
|
directionType wantedDir = directionTypeNames_[wantedName];
|
|
|
|
if (wantedDir == NORMAL)
|
|
{
|
|
wantNormal = true;
|
|
}
|
|
else if (wantedDir == TAN1)
|
|
{
|
|
wantTan1 = true;
|
|
}
|
|
else if (wantedDir == TAN2)
|
|
{
|
|
wantTan2 = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (coordSystem == "global")
|
|
{
|
|
const dictionary& globalDict = dict.subDict("globalCoeffs");
|
|
|
|
vector tan1(globalDict.lookup("tan1"));
|
|
check2D(correct2DPtr, tan1);
|
|
|
|
vector tan2(globalDict.lookup("tan2"));
|
|
check2D(correct2DPtr, tan2);
|
|
|
|
const vector normal = normalised(tan1 ^ tan2);
|
|
|
|
Info<< "Global Coordinate system:" << endl
|
|
<< " normal : " << normal << endl
|
|
<< " tan1 : " << tan1 << endl
|
|
<< " tan2 : " << tan2
|
|
<< endl << endl;
|
|
|
|
if (wantNormal)
|
|
{
|
|
operator[](nDirs++) = vectorField(1, normal);
|
|
}
|
|
if (wantTan1)
|
|
{
|
|
operator[](nDirs++) = vectorField(1, tan1);
|
|
}
|
|
if (wantTan2)
|
|
{
|
|
operator[](nDirs++) = vectorField(1, tan2);
|
|
}
|
|
}
|
|
else if (coordSystem == "patchLocal")
|
|
{
|
|
const dictionary& patchDict = dict.subDict("patchLocalCoeffs");
|
|
|
|
const word patchName(patchDict.lookup("patch"));
|
|
|
|
const label patchi = mesh.boundaryMesh().findPatchID(patchName);
|
|
|
|
if (patchi == -1)
|
|
{
|
|
FatalErrorInFunction
|
|
<< "Cannot find patch "
|
|
<< patchName
|
|
<< exit(FatalError);
|
|
}
|
|
|
|
// Take zeroth face on patch
|
|
const polyPatch& pp = mesh.boundaryMesh()[patchi];
|
|
|
|
vector tan1(patchDict.lookup("tan1"));
|
|
|
|
const vector& n0 = pp.faceNormals()[0];
|
|
|
|
if (correct2DPtr)
|
|
{
|
|
tan1 = correct2DPtr->planeNormal() ^ n0;
|
|
|
|
WarningInFunction
|
|
<< "Discarding user specified tan1 since 2D case." << endl
|
|
<< "Recalculated tan1 from face normal and planeNormal as "
|
|
<< tan1 << endl << endl;
|
|
}
|
|
|
|
const bool useTopo(dict.get<bool>("useHexTopology"));
|
|
|
|
vectorField normalDirs;
|
|
vectorField tan1Dirs;
|
|
|
|
if (wantNormal || wantTan2)
|
|
{
|
|
normalDirs =
|
|
propagateDirection
|
|
(
|
|
mesh,
|
|
useTopo,
|
|
pp,
|
|
pp.faceNormals(),
|
|
n0
|
|
);
|
|
|
|
if (wantNormal)
|
|
{
|
|
this->operator[](nDirs++) = normalDirs;
|
|
}
|
|
}
|
|
|
|
if (wantTan1 || wantTan2)
|
|
{
|
|
tan1Dirs =
|
|
propagateDirection
|
|
(
|
|
mesh,
|
|
useTopo,
|
|
pp,
|
|
vectorField(pp.size(), tan1),
|
|
tan1
|
|
);
|
|
|
|
|
|
if (wantTan1)
|
|
{
|
|
this->operator[](nDirs++) = tan1Dirs;
|
|
}
|
|
}
|
|
if (wantTan2)
|
|
{
|
|
tmp<vectorField> tan2Dirs = normalDirs ^ tan1Dirs;
|
|
|
|
this->operator[](nDirs++) = tan2Dirs;
|
|
}
|
|
}
|
|
else if (coordSystem == "fieldBased")
|
|
{
|
|
forAll(wantedDirs, i)
|
|
{
|
|
operator[](nDirs++) =
|
|
vectorIOField
|
|
(
|
|
IOobject
|
|
(
|
|
mesh.instance()/wantedDirs[i],
|
|
mesh,
|
|
IOobject::MUST_READ,
|
|
IOobject::NO_WRITE
|
|
)
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FatalErrorInFunction
|
|
<< "Unknown coordinate system "
|
|
<< coordSystem << endl
|
|
<< "Known types are global, patchLocal and fieldBased"
|
|
<< exit(FatalError);
|
|
}
|
|
}
|
|
|
|
|
|
// ************************************************************************* //
|