/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2018-2019 OpenCFD Ltd.
\\/ 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 .
\*---------------------------------------------------------------------------*/
#include "momentum.H"
#include "fvMesh.H"
#include "volFields.H"
#include "cellSet.H"
#include "cylindricalRotation.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
namespace functionObjects
{
defineTypeNameAndDebug(momentum, 0);
addToRunTimeSelectionTable(functionObject, momentum, dictionary);
}
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * //
void Foam::functionObjects::momentum::purgeFields()
{
obr_.checkOut(scopedName("momentum"));
obr_.checkOut(scopedName("angularMomentum"));
obr_.checkOut(scopedName("angularVelocity"));
}
template
Foam::autoPtr
Foam::functionObjects::momentum::newField
(
const word& baseName,
const dimensionSet& dims,
bool registerObject
) const
{
return
autoPtr::New
(
IOobject
(
scopedName(baseName),
time_.timeName(),
mesh_,
IOobject::NO_READ,
IOobject::NO_WRITE,
registerObject
),
mesh_,
dimensioned(dims, Zero)
);
}
void Foam::functionObjects::momentum::calc()
{
initialise();
// Ensure volRegion is properly up-to-date.
// Purge old fields if anything has changed (eg, mesh size etc)
if (volRegion::update())
{
purgeFields();
}
// When field writing is not enabled we need our local storage
// for the momentum and angular velocity fields
autoPtr tmomentum, tAngularMom, tAngularVel;
// The base fields required
const auto& U = lookupObject(UName_);
const auto* rhoPtr = findObject(rhoName_);
const dimensionedScalar rhoRef("rho", dimDensity, rhoRef_);
// For quantities such as the mass-averaged angular velocity,
// we would calculate the mass per-cell here.
// tmp tmass =
// (
// rhoPtr
// ? (mesh_.V() * (*rhoPtr))
// : (mesh_.V() * rhoRef)
// );
// Linear momentum
// ~~~~~~~~~~~~~~~
auto* pmomentum = getObjectPtr(scopedName("momentum"));
if (!pmomentum)
{
tmomentum = newField("momentum", dimVelocity*dimMass);
pmomentum = tmomentum.get(); // get(), not release()
}
auto& momentum = *pmomentum;
if (rhoPtr)
{
momentum.ref() = (U * mesh_.V() * (*rhoPtr));
}
else
{
momentum.ref() = (U * mesh_.V() * rhoRef);
}
momentum.correctBoundaryConditions();
// Angular momentum
// ~~~~~~~~~~~~~~~~
auto* pAngularMom =
getObjectPtr(scopedName("angularMomentum"));
if (hasCsys_ && !pAngularMom)
{
tAngularMom =
newField("angularMomentum", dimVelocity*dimMass);
pAngularMom = tAngularMom.get(); // get(), not release()
}
else if (!pAngularMom)
{
// Angular momentum not requested, but alias to normal momentum
// to simplify logic when calculating the summations
pAngularMom = pmomentum;
}
auto& angularMom = *pAngularMom;
// Angular velocity
// ~~~~~~~~~~~~~~~~
auto* pAngularVel =
getObjectPtr(scopedName("angularVelocity"));
if (hasCsys_)
{
if (!pAngularVel)
{
tAngularVel =
newField("angularVelocity", dimVelocity);
pAngularVel = tAngularVel.get(); // get(), not release()
}
auto& angularVel = *pAngularVel;
// Global to local
angularVel.primitiveFieldRef() =
csys_.invTransform(mesh_.cellCentres(), U.internalField());
angularVel.correctBoundaryConditions();
if (rhoPtr)
{
angularMom.ref() = (angularVel * mesh_.V() * (*rhoPtr));
}
else
{
angularMom.ref() = (angularVel * mesh_.V() * rhoRef);
}
angularMom.correctBoundaryConditions();
}
// Integrate the selection
sumMomentum_ = Zero;
sumAngularMom_ = Zero;
switch (regionType_)
{
case vrtCellSet:
case vrtCellZone:
{
for (const label celli : cellIDs())
{
sumMomentum_ += momentum[celli];
sumAngularMom_ += angularMom[celli];
}
break;
}
case vrtAll:
{
for (label celli=0; celli < mesh_.nCells(); ++celli)
{
sumMomentum_ += momentum[celli];
sumAngularMom_ += angularMom[celli];
}
break;
}
}
reduce(sumMomentum_, sumOp());
reduce(sumAngularMom_, sumOp());
}
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
void Foam::functionObjects::momentum::writeFileHeader(Ostream& os)
{
if (!writeToFile() || writtenHeader_)
{
return;
}
if (hasCsys_)
{
writeHeader(os, "Momentum, Angular Momentum");
writeHeaderValue(os, "origin", csys_.origin());
writeHeaderValue(os, "axis", csys_.e3());
}
else
{
writeHeader(os, "Momentum");
}
if (regionType_ != vrtAll)
{
writeHeader
(
os,
"Selection " + regionTypeNames_[regionType_]
+ " = " + regionName_
);
}
writeHeader(os, "");
writeCommented(os, "Time");
writeTabbed(os, "(momentum_x momentum_y momentum_z)");
if (hasCsys_)
{
writeTabbed(os, "(momentum_r momentum_rtheta momentum_axis)");
}
writeTabbed(os, "volume");
os << endl;
writtenHeader_ = true;
}
void Foam::functionObjects::momentum::initialise()
{
if (initialised_)
{
return;
}
if (!foundObject(UName_))
{
FatalErrorInFunction
<< "Could not find U: " << UName_ << " in database"
<< exit(FatalError);
}
const auto* pPtr = cfindObject(pName_);
if (pPtr && pPtr->dimensions() == dimPressure)
{
// Compressible - rho is mandatory
if (!foundObject(rhoName_))
{
FatalErrorInFunction
<< "Could not find rho:" << rhoName_
<< exit(FatalError);
}
}
initialised_ = true;
}
void Foam::functionObjects::momentum::writeValues(Ostream& os)
{
if (log)
{
Info<< type() << " " << name() << " write:" << nl;
Info<< " Sum of Momentum";
if (regionType_ != vrtAll)
{
Info<< ' ' << regionTypeNames_[regionType_]
<< ' ' << regionName_;
}
Info<< " (volume " << volRegion::V() << ')' << nl
<< " linear : " << sumMomentum_ << nl;
if (hasCsys_)
{
Info<< " angular : " << sumAngularMom_ << nl;
}
Info<< endl;
}
if (writeToFile())
{
writeTime(os);
os << tab << sumMomentum_;
if (hasCsys_)
{
os << tab << sumAngularMom_;
}
os << tab << volRegion::V() << endl;
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::functionObjects::momentum::momentum
(
const word& name,
const Time& runTime,
const dictionary& dict,
bool readFields
)
:
fvMeshFunctionObject(name, runTime, dict),
volRegion(fvMeshFunctionObject::mesh_, dict),
writeFile(mesh_, name, typeName, dict),
sumMomentum_(Zero),
sumAngularMom_(Zero),
UName_(),
pName_(),
rhoName_(),
rhoRef_(1.0),
csys_(),
hasCsys_(false),
writeMomentum_(false),
writeVelocity_(false),
writePosition_(false),
initialised_(false)
{
if (readFields)
{
read(dict);
Log << endl;
}
}
Foam::functionObjects::momentum::momentum
(
const word& name,
const objectRegistry& obr,
const dictionary& dict,
bool readFields
)
:
fvMeshFunctionObject(name, obr, dict),
volRegion(fvMeshFunctionObject::mesh_, dict),
writeFile(mesh_, name, typeName, dict),
sumMomentum_(Zero),
sumAngularMom_(Zero),
UName_(),
pName_(),
rhoName_(),
rhoRef_(1.0),
csys_(),
hasCsys_(false),
writeMomentum_(false),
writeVelocity_(false),
writePosition_(false),
initialised_(false)
{
if (readFields)
{
read(dict);
Log << endl;
}
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::functionObjects::momentum::read(const dictionary& dict)
{
fvMeshFunctionObject::read(dict);
volRegion::read(dict);
writeFile::read(dict);
initialised_ = false;
Info<< type() << " " << name() << ":" << nl;
// Optional entries U and p
UName_ = dict.lookupOrDefault("U", "U");
pName_ = dict.lookupOrDefault("p", "p");
rhoName_ = dict.lookupOrDefault("rho", "rho");
rhoRef_ = dict.lookupOrDefault("rhoRef", 1.0);
hasCsys_ = dict.lookupOrDefault("cylindrical", false);
if (hasCsys_)
{
csys_ = coordSystem::cylindrical(dict);
}
writeMomentum_ = dict.lookupOrDefault("writeMomentum", false);
writeVelocity_ = dict.lookupOrDefault("writeVelocity", false);
writePosition_ = dict.lookupOrDefault("writePosition", false);
Info<<"Integrating for selection: "
<< regionTypeNames_[regionType_]
<< " (" << regionName_ << ")" << nl;
if (writeMomentum_)
{
Info<< " Momentum fields will be written" << endl;
mesh_.objectRegistry::store
(
newField("momentum", dimVelocity*dimMass)
);
if (hasCsys_)
{
mesh_.objectRegistry::store
(
newField("angularMomentum", dimVelocity*dimMass)
);
}
}
if (hasCsys_)
{
if (writeVelocity_)
{
Info<< " Angular velocity will be written" << endl;
mesh_.objectRegistry::store
(
newField("angularVelocity", dimVelocity)
);
}
if (writePosition_)
{
Info<< " Angular position will be written" << endl;
}
}
return true;
}
bool Foam::functionObjects::momentum::execute()
{
calc();
if (Pstream::master())
{
writeFileHeader(file());
writeValues(file());
Log << endl;
}
// Write state/results information
setResult("momentum_x", sumMomentum_[0]);
setResult("momentum_y", sumMomentum_[1]);
setResult("momentum_z", sumMomentum_[2]);
setResult("momentum_r", sumAngularMom_[0]);
setResult("momentum_rtheta", sumAngularMom_[1]);
setResult("momentum_axis", sumAngularMom_[2]);
return true;
}
bool Foam::functionObjects::momentum::write()
{
if (writeMomentum_ || (hasCsys_ && (writeVelocity_ || writePosition_)))
{
Log << "Writing fields" << nl;
const volVectorField* fieldPtr;
fieldPtr = findObject(scopedName("momentum"));
if (fieldPtr) fieldPtr->write();
fieldPtr = findObject(scopedName("angularMomentum"));
if (fieldPtr) fieldPtr->write();
fieldPtr = findObject(scopedName("angularVelocity"));
if (fieldPtr) fieldPtr->write();
if (hasCsys_ && writePosition_)
{
// Clunky, but currently no simple means of handling
// component-wise conversion and output
auto cyl_r = newField("cyl_r", dimLength);
auto cyl_t = newField("cyl_theta", dimless);
auto cyl_z = newField("cyl_z", dimLength);
// Internal
{
const auto& pts = mesh_.cellCentres();
const label len = pts.size();
UList& r = cyl_r->primitiveFieldRef(false);
UList& t = cyl_t->primitiveFieldRef(false);
UList& z = cyl_z->primitiveFieldRef(false);
for (label i=0; i < len; ++i)
{
point p(csys_.localPosition(pts[i]));
r[i] = p.x();
t[i] = p.y();
z[i] = p.z();
}
}
// Boundary
const polyBoundaryMesh& pbm = mesh_.boundaryMesh();
forAll(pbm, patchi)
{
const auto& pts = pbm[patchi].faceCentres();
const label len = pts.size();
UList& r = cyl_r->boundaryFieldRef(false)[patchi];
UList& t = cyl_t->boundaryFieldRef(false)[patchi];
UList& z = cyl_z->boundaryFieldRef(false)[patchi];
for (label i=0; i < len; ++i)
{
point p(csys_.localPosition(pts[i]));
r[i] = p.x();
t[i] = p.y();
z[i] = p.z();
}
}
cyl_r->write();
cyl_t->write();
cyl_z->write();
}
}
return true;
}
void Foam::functionObjects::momentum::updateMesh(const mapPolyMesh& mpm)
{
volRegion::updateMesh(mpm);
purgeFields(); // Mesh motion makes calculated fields dubious
}
void Foam::functionObjects::momentum::movePoints(const polyMesh& pm)
{
volRegion::movePoints(pm);
purgeFields(); // Mesh motion makes calculated fields dubious
}
// ************************************************************************* //