In foam-extend-3.2, calculation of force and moment by porous effect is not yet supported.
Therefore I implemented it by refering to OpenFOAM-2.2.
The calculation of porous effect is activated by porosity=true.
4 files were changed.
src/postProcessing/functionObjects/forces/forces/forces.H
\*---------------------------------------------------------------------------*/
#ifndef forces_H
#define forces_H
#include "primitiveFieldsFwd.H"
#include "volFieldsFwd.H"
#include "HashSet.H"
#include "Tuple2.H"
#include "OFstream.H"
#include "Switch.H"
#include "pointFieldFwd.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
// Forward declaration of classes
class objectRegistry;
class dictionary;
class mapPolyMesh;
/*---------------------------------------------------------------------------*\
Class forces Declaration
\*---------------------------------------------------------------------------*/
class forces
{
public:
// Tuple which holds the pressure (.first()) and viscous (.second) forces
typedef Tuple2<vector, vector> pressureViscous;
// Tuple which holds the forces (.first()) and moment (.second)
// pressure/viscous forces Tuples.
typedef Tuple2<pressureViscous, pressureViscous> forcesMoments;
//- Sum operation class to accumulate the pressure, viscous forces
// and moments
class sumOp
{
public:
forcesMoments operator()
(
const forcesMoments& fm1,
const forcesMoments& fm2
) const
{
return forcesMoments
(
pressureViscous
(
fm1.first().first() + fm2.first().first(),
fm1.first().second() + fm2.first().second()
),
pressureViscous
(
fm1.second().first() + fm2.second().first(),
fm1.second().second() + fm2.second().second()
)
);
}
};
protected:
// Private data
//- Name of this set of forces,
// Also used as the name of the probes directory.
word name_;
const objectRegistry& obr_;
//- on/off switch
bool active_;
//- Switch to send output to Info as well as to file
Switch log_;
// Read from dictionary
//- Patches to integrate forces over
labelHashSet patchSet_;
//- Name of pressure field
word pName_;
//- Name of velocity field
word UName_;
//- Name of density field (optional)
word rhoName_;
//- Is the force density being supplied directly?
Switch directForceDensity_;
//- The name of the force density (fD) field
word fDName_;
//- Reference density needed for incompressible calculations
scalar rhoRef_;
//- Reference pressure
scalar pRef_;
//- Centre of rotation
vector CofR_;
//- Switch for porous effect calculation
Switch porosity_;
//- Forces/moment file ptr
autoPtr<OFstream> forcesFilePtr_;
// Private Member Functions
//- If the forces file has not been created create it
void makeFile();
//- Output file header information
virtual void writeFileHeader();
//- Return the effective viscous stress (laminar + turbulent).
tmp<volSymmTensorField> devRhoReff() const;
//- Return mu
tmp<volScalarField> mu() const;
//- Return rho if rhoName is specified otherwise rhoRef
tmp<volScalarField> rho() const;
//- Return rhoRef if the pressure field is dynamic, i.e. p/rho
// otherwise return 1
scalar rho(const volScalarField& p) const;
//- Disallow default bitwise copy construct
forces(const forces&);
//- Disallow default bitwise assignment
void operator=(const forces&);
public:
//- Runtime type information
TypeName("forces");
// Constructors
//- Construct for given objectRegistry and dictionary.
// Allow the possibility to load fields from files
forces
(
const word& name,
const objectRegistry&,
const dictionary&,
const bool loadFromFiles = false
);
//- Destructor
virtual ~forces();
// Member Functions
//- Return name of the set of forces
virtual const word& name() const
{
return name_;
}
//- Read the forces data
virtual void read(const dictionary&);
//- Execute, currently does nothing
virtual void execute();
//- Execute at the final time-loop, currently does nothing
virtual void end();
//- Write the forces
virtual void write();
//- Calculate and return porous effect
virtual forcesMoments calcPorousEffect() const;
//- Calculate and return forces and moment
virtual forcesMoments calcForcesMoment() const;
//- Update for changes of mesh
virtual void updateMesh(const mapPolyMesh&)
{}
//- Update for changes of mesh
virtual void movePoints(const pointField&)
{}
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //
src/postProcessing/functionObjects/forces/forces/forces.C
\*---------------------------------------------------------------------------*/
#include "forces.H"
#include "volFields.H"
#include "dictionary.H"
#include "foamTime.H"
#include "incompressible/singlePhaseTransportModel/singlePhaseTransportModel.H"
#include "incompressible/RAS/RASModel/RASModel.H"
#include "incompressible/LES/LESModel/LESModel.H"
#include "basicThermo.H"
#include "compressible/RAS/RASModel/RASModel.H"
#include "compressible/LES/LESModel/LESModel.H"
#include "porousZones.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(forces, 0);
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::tmp<Foam::volSymmTensorField> Foam::forces::devRhoReff() const
{
if (obr_.foundObject<compressible::RASModel>("RASProperties"))
{
const compressible::RASModel& ras
= obr_.lookupObject<compressible::RASModel>("RASProperties");
return ras.devRhoReff();
}
else if (obr_.foundObject<incompressible::RASModel>("RASProperties"))
{
const incompressible::RASModel& ras
= obr_.lookupObject<incompressible::RASModel>("RASProperties");
return rho()*ras.devReff();
}
else if (obr_.foundObject<compressible::LESModel>("LESProperties"))
{
const compressible::LESModel& les =
obr_.lookupObject<compressible::LESModel>("LESProperties");
return les.devRhoBeff();
}
else if (obr_.foundObject<incompressible::LESModel>("LESProperties"))
{
const incompressible::LESModel& les
= obr_.lookupObject<incompressible::LESModel>("LESProperties");
return rho()*les.devBeff();
}
else if (obr_.foundObject<basicThermo>("thermophysicalProperties"))
{
const basicThermo& thermo =
obr_.lookupObject<basicThermo>("thermophysicalProperties");
const volVectorField& U = obr_.lookupObject<volVectorField>(UName_);
return -thermo.mu()*dev(twoSymm(fvc::grad(U)));
}
else if
(
obr_.foundObject<singlePhaseTransportModel>("transportProperties")
)
{
const singlePhaseTransportModel& laminarT =
obr_.lookupObject<singlePhaseTransportModel>
("transportProperties");
const volVectorField& U = obr_.lookupObject<volVectorField>(UName_);
return -rho()*laminarT.nu()*dev(twoSymm(fvc::grad(U)));
}
else if (obr_.foundObject<dictionary>("transportProperties"))
{
const dictionary& transportProperties =
obr_.lookupObject<dictionary>("transportProperties");
dimensionedScalar nu(transportProperties.lookup("nu"));
const volVectorField& U = obr_.lookupObject<volVectorField>(UName_);
return -rho()*nu*dev(twoSymm(fvc::grad(U)));
}
else
{
FatalErrorIn("forces::devRhoReff()")
<< "No valid model for viscous stress calculation."
<< exit(FatalError);
return volSymmTensorField::null();
}
}
Foam::tmp<Foam::volScalarField> Foam::forces::mu() const
{
if (obr_.foundObject<basicThermo>("thermophysicalProperties"))
{
const basicThermo& thermo =
obr_.lookupObject<basicThermo>("thermophysicalProperties");
return thermo.mu();
}
else if
(
obr_.foundObject<transportModel>("transportProperties")
)
{
const transportModel& laminarT =
obr_.lookupObject<transportModel>("transportProperties");
return rho()*laminarT.nu();
}
else if (obr_.foundObject<dictionary>("transportProperties"))
{
const dictionary& transportProperties =
obr_.lookupObject<dictionary>("transportProperties");
dimensionedScalar nu(transportProperties.lookup("nu"));
return rho()*nu;
}
else
{
FatalErrorIn("forces::mu()")
<< "No valid model for dynamic viscosity calculation"
<< exit(FatalError);
return volScalarField::null();
}
}
Foam::tmp<Foam::volScalarField> Foam::forces::rho() const
{
if (rhoName_ == "rhoInf")
{
const fvMesh& mesh = refCast<const fvMesh>(obr_);
return tmp<volScalarField>
(
new volScalarField
(
IOobject
(
"rho",
mesh.time().timeName(),
mesh
),
mesh,
dimensionedScalar("rho", dimDensity, rhoRef_)
)
);
}
else
{
return(obr_.lookupObject<volScalarField>(rhoName_));
}
}
Foam::scalar Foam::forces::rho(const volScalarField& p) const
{
if (p.dimensions() == dimPressure)
{
return 1.0;
}
else
{
if (rhoName_ != "rhoInf")
{
FatalErrorIn("forces::rho(const volScalarField& p)")
<< "Dynamic pressure is expected but kinematic is provided."
<< exit(FatalError);
}
return rhoRef_;
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::forces::forces
(
const word& name,
const objectRegistry& obr,
const dictionary& dict,
const bool loadFromFiles
)
:
name_(name),
obr_(obr),
active_(true),
log_(false),
patchSet_(),
pName_(word::null),
UName_(word::null),
rhoName_(word::null),
directForceDensity_(false),
fDName_(""),
rhoRef_(VGREAT),
pRef_(0),
CofR_(vector::zero),
porosity_(false),
forcesFilePtr_(NULL)
{
// Check if the available mesh is an fvMesh otherise deactivate
if (!isA<fvMesh>(obr_))
{
active_ = false;
WarningIn
(
"Foam::forces::forces"
"("
"const word&, "
"const objectRegistry&, "
"const dictionary&, "
"const bool"
")"
) << "No fvMesh available, deactivating."
<< endl;
}
read(dict);
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::forces::~forces()
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
void Foam::forces::read(const dictionary& dict)
{
if (active_)
{
log_ = dict.lookupOrDefault<Switch>("log", false);
const fvMesh& mesh = refCast<const fvMesh>(obr_);
patchSet_ =
mesh.boundaryMesh().patchSet(wordList(dict.lookup("patches")));
dict.readIfPresent("directForceDensity", directForceDensity_);
if (directForceDensity_)
{
// Optional entry for fDName
fDName_ = dict.lookupOrDefault<word>("fDName", "fD");
// Check whether fDName exists, if not deactivate forces
if
(
!obr_.foundObject<volVectorField>(fDName_)
)
{
active_ = false;
WarningIn("void forces::read(const dictionary& dict)")
<< "Could not find " << fDName_ << " in database." << nl
<< " De-activating forces."
<< endl;
}
}
else
{
// Optional entries U and p
pName_ = dict.lookupOrDefault<word>("pName", "p");
UName_ = dict.lookupOrDefault<word>("UName", "U");
rhoName_ = dict.lookupOrDefault<word>("rhoName", "rho");
// Check whether UName, pName and rhoName exists,
// if not deactivate forces
if
(
!obr_.foundObject<volVectorField>(UName_)
|| !obr_.foundObject<volScalarField>(pName_)
|| (
rhoName_ != "rhoInf"
&& !obr_.foundObject<volScalarField>(rhoName_)
)
)
{
active_ = false;
WarningIn("void forces::read(const dictionary& dict)")
<< "Could not find " << UName_ << ", " << pName_;
if (rhoName_ != "rhoInf")
{
Info<< " or " << rhoName_;
}
Info<< " in database." << nl << " De-activating forces."
<< endl;
}
// Reference density needed for incompressible calculations
rhoRef_ = readScalar(dict.lookup("rhoInf"));
// Reference pressure, 0 by default
pRef_ = dict.lookupOrDefault<scalar>("pRef", 0.0);
}
// Centre of rotation for moment calculations
CofR_ = dict.lookup("CofR");
porosity_ = dict.lookupOrDefault<Switch>("porosity", false);
}
}
void Foam::forces::makeFile()
{
// Create the forces file if not already created
if (forcesFilePtr_.empty())
{
if (debug)
{
Info<< "Creating forces file." << endl;
}
// File update
if (Pstream::master())
{
fileName forcesDir;
word startTimeName =
obr_.time().timeName(obr_.time().startTime().value());
if (Pstream::parRun())
{
// Put in undecomposed case (Note: gives problems for
// distributed data running)
forcesDir = obr_.time().path()/".."/name_/startTimeName;
}
else
{
forcesDir = obr_.time().path()/name_/startTimeName;
}
// Create directory if does not exist.
mkDir(forcesDir);
// Open new file at start up
forcesFilePtr_.reset(new OFstream(forcesDir/(type() + ".dat")));
// Add headers to output data
writeFileHeader();
}
}
}
void Foam::forces::writeFileHeader()
{
if (forcesFilePtr_.valid())
{
forcesFilePtr_()
<< "# Time" << tab
<< "forces(pressure, viscous) moment(pressure, viscous) porous(force, moment)"
<< endl;
}
}
void Foam::forces::execute()
{
// Do nothing - only valid on write
}
void Foam::forces::end()
{
// Do nothing - only valid on write
}
void Foam::forces::write()
{
if (active_)
{
// Create the forces file if not already created
makeFile();
forcesMoments fm = calcForcesMoment();
porousEffect pe = calcPorousEffect();
if (Pstream::master())
{
forcesFilePtr_() << obr_.time().value() << tab << fm
<< "(" << pe.first().first()
<< " " << pe.second().first() << ")" << endl;
if (log_)
{
Info<< "forces output:" << nl
<< " forces(pressure, viscous)" << fm.first() << nl
<< " moment(pressure, viscous)" << fm.second() << nl
<< " porous( force, moment)" << "(" << pe.first().first() << " "
<< pe.second().first() << ")" << nl
<< endl;
}
}
}
}
Foam::forces::forcesMoments Foam::forces::calcPorousEffect() const
{
forcesMoments pe
(
pressureViscous(vector::zero, vector::zero),
pressureViscous(vector::zero, vector::zero)
);
if (porosity_)
{
// calculate force and moment by porous effect.
{
const volVectorField& U = obr_.lookupObject<volVectorField>(UName_);
const volScalarField& rho(this->rho());
const volScalarField& mu(this->mu());
const fvMesh& mesh = U.mesh();
porousZones pZones(mesh);
if (pZones.size() == 0)
{
WarningIn("void Foam::forces::calcPorousEffect()")
<< "Porosity effects requested, but no porosity models found "
<< "in the database"
<< endl;
}
forAll(pZones, i)
{
const porousZone& pm = pZones[i];
vectorField fPTot(pm.force(U, rho, mu));
label zoneI = pm.zoneId();
const cellZone& cZone = mesh.cellZones()[zoneI];
const vectorField d(mesh.C(), cZone);
const vectorField fP(fPTot, cZone);
const vectorField Md(d - CofR_);
pe.first().first() += sum(fP);
pe.second().first() += sum(Md ^ fP);
}
}
}
reduce(pe, sumOp());
return pe;
}
Foam::forces::forcesMoments Foam::forces::calcForcesMoment() const
{
forcesMoments fm
(
pressureViscous(vector::zero, vector::zero),
pressureViscous(vector::zero, vector::zero)
);
if (directForceDensity_)
{
const volVectorField& fD = obr_.lookupObject<volVectorField>(fDName_);
const fvMesh& mesh = fD.mesh();
const surfaceVectorField::GeometricBoundaryField& Sfb =
mesh.Sf().boundaryField();
forAllConstIter(labelHashSet, patchSet_, iter)
{
label patchi = iter.key();
vectorField Md = mesh.C().boundaryField()[patchi] - CofR_;
scalarField sA = mag(Sfb[patchi]);
// Normal force = surfaceUnitNormal*(surfaceNormal & forceDensity)
vectorField fN =
Sfb[patchi]/sA
*(
Sfb[patchi] & fD.boundaryField()[patchi]
);
fm.first().first() += sum(fN);
fm.second().first() += sum(Md ^ fN);
// Tangential force (total force minus normal fN)
vectorField fT = sA*fD.boundaryField()[patchi] - fN;
fm.first().second() += sum(fT);
fm.second().second() += sum(Md ^ fT);
}
}
else
{
const volVectorField& U = obr_.lookupObject<volVectorField>(UName_);
const volScalarField& p = obr_.lookupObject<volScalarField>(pName_);
const fvMesh& mesh = U.mesh();
const surfaceVectorField::GeometricBoundaryField& Sfb =
mesh.Sf().boundaryField();
tmp<volSymmTensorField> tdevRhoReff = devRhoReff();
const volSymmTensorField::GeometricBoundaryField& devRhoReffb
= tdevRhoReff().boundaryField();
// Scale pRef by density for incompressible simulations
scalar pRef = pRef_/rho(p);
forAllConstIter(labelHashSet, patchSet_, iter)
{
label patchi = iter.key();
vectorField Md = mesh.C().boundaryField()[patchi] - CofR_;
vectorField pf = Sfb[patchi]*(p.boundaryField()[patchi] - pRef);
fm.first().first() += rho(p)*sum(pf);
fm.second().first() += rho(p)*sum(Md ^ pf);
vectorField vf = Sfb[patchi] & devRhoReffb[patchi];
fm.first().second() += sum(vf);
fm.second().second() += sum(Md ^ vf);
}
}
reduce(fm, sumOp());
return fm;
}
// ************************************************************************* //
src/finiteVolume/cfdTools/general/porousMedia/porousZone.H
\*---------------------------------------------------------------------------*/
#ifndef porousZone_H
#define porousZone_H
#include "dictionary.H"
#include "coordinateSystem.H"
#include "coordinateSystems.H"
#include "wordList.H"
#include "labelList.H"
#include "dimensionedScalar.H"
#include "dimensionedTensor.H"
#include "primitiveFieldsFwd.H"
#include "volFieldsFwd.H"
#include "fvMatricesFwd.H"
#include "fvMesh.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
class fvMesh;
/*---------------------------------------------------------------------------*\
Class porousZone Declaration
\*---------------------------------------------------------------------------*/
class porousZone
{
// Private data
//- Name of this zone
word name_;
//- Reference to the finite volume mesh this zone is part of
const fvMesh& mesh_;
//- Dictionary containing the parameters
dictionary dict_;
//- Cell zone ID
label cellZoneID_;
//- Coordinate system used for the zone (Cartesian)
coordinateSystem coordSys_;
//- porosity of the zone (0 < porosity <= 1)
// Placeholder for treatment of temporal terms.
// Currently unused.
scalar porosity_;
//- powerLaw coefficient C0
scalar C0_;
//- powerLaw coefficient C1
scalar C1_;
//- Darcy coefficient
dimensionedTensor D_;
//- Forchheimer coefficient
dimensionedTensor F_;
// Private Member Functions
//- adjust negative resistance values to be multiplier of max value
static void adjustNegativeResistance(dimensionedVector& resist);
//- Power-law resistance
template<class RhoFieldType>
void addPowerLawResistance
(
scalarField& Udiag,
const labelList& cells,
const scalarField& V,
const RhoFieldType& rho,
const vectorField& U
) const;
//- Viscous and inertial resistance
template<class RhoFieldType>
void addViscousInertialResistance
(
scalarField& Udiag,
vectorField& Usource,
const labelList& cells,
const scalarField& V,
const RhoFieldType& rho,
const scalarField& mu,
const vectorField& U
) const;
//- Power-law resistance
template<class RhoFieldType>
void addPowerLawResistance
(
tensorField& AU,
const labelList& cells,
const RhoFieldType& rho,
const vectorField& U
) const;
//- Viscous and inertial resistance
template<class RhoFieldType>
void addViscousInertialResistance
(
tensorField& AU,
const labelList& cells,
const RhoFieldType& rho,
const scalarField& mu,
const vectorField& U
) const;
//- Disallow default bitwise copy construct
porousZone(const porousZone&);
//- Disallow default bitwise assignment
void operator=(const porousZone&);
public:
// Constructors
//- Construct from components
porousZone(const word& name, const fvMesh&, const dictionary&);
//- Return clone
autoPtr<porousZone> clone() const
{
notImplemented("autoPtr<porousZone> clone() const");
return autoPtr<porousZone>(NULL);
}
//- Return pointer to new porousZone created on freestore from Istream
class iNew
{
//- Reference to the finite volume mesh this zone is part of
const fvMesh& mesh_;
public:
iNew(const fvMesh& mesh)
:
mesh_(mesh)
{}
autoPtr<porousZone> operator()(Istream& is) const
{
word name(is);
dictionary dict(is);
return autoPtr<porousZone>(new porousZone(name, mesh_, dict));
}
};
//- Destructor
virtual ~porousZone()
{}
// Member Functions
// Access
//- cellZone name
const word& zoneName() const
{
return name_;
}
//- Return mesh
const fvMesh& mesh() const
{
return mesh_;
}
//- cellZone number
label zoneId() const
{
return cellZoneID_;
}
//- dictionary values used for the porousZone
const dictionary& dict() const
{
return dict_;
}
//- Return coordinate system
const coordinateSystem& coordSys() const
{
return coordSys_;
}
//- Return origin
const point& origin() const
{
return coordSys_.origin();
}
//- Return axis
vector axis() const
{
return coordSys_.axis();
}
//- Return porosity
scalar porosity() const
{
return porosity_;
}
//- Edit access to porosity
scalar& porosity()
{
return porosity_;
}
//- Modify time derivative elements according to porosity
template<class Type>
void modifyDdt(fvMatrix<Type>&) const;
//-
//
Foam::tmp<Foam::vectorField> force
(
const volVectorField& U,
const volScalarField& rho,
const volScalarField& mu
) const;
//- Add the viscous and inertial resistance force contribution
// to the momentum equation
void addResistance(fvVectorMatrix& UEqn) const;
//- Add the viscous and inertial resistance force contribution
// to the tensorial diagonal.
// Optionally correct the processor BCs of AU.
void addResistance
(
const fvVectorMatrix& UEqn,
volTensorField& AU,
bool correctAUprocBC = true
) const;
//- Write the porousZone dictionary
virtual void writeDict(Ostream&, bool subDict = true) const;
// Ostream Operator
friend Ostream& operator<<(Ostream&, const porousZone&);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#ifdef NoRepository
# include "porousZoneTemplates.C"
#endif
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //
src/finiteVolume/cfdTools/general/porousMedia/porousZone.C
\*---------------------------------------------------------------------------*/
#include "porousZone.H"
#include "fvMesh.H"
#include "fvMatrices.H"
#include "geometricOneField.H"
// * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * * //
// adjust negative resistance values to be multiplier of max value
void Foam::porousZone::adjustNegativeResistance(dimensionedVector& resist)
{
scalar maxCmpt = max(0, cmptMax(resist.value()));
if (maxCmpt < 0)
{
FatalErrorIn
(
"Foam::porousZone::porousZone::adjustNegativeResistance"
"(dimensionedVector&)"
) << "negative resistances! " << resist
<< exit(FatalError);
}
else
{
vector& val = resist.value();
for (label cmpt=0; cmpt < vector::nComponents; ++cmpt)
{
if (val[cmpt] < 0)
{
val[cmpt] *= -maxCmpt;
}
}
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::porousZone::porousZone
(
const word& name,
const fvMesh& mesh,
const dictionary& dict
)
:
name_(name),
mesh_(mesh),
dict_(dict),
cellZoneID_(mesh_.cellZones().findZoneID(name)),
coordSys_(dict, mesh),
porosity_(1),
C0_(0),
C1_(0),
D_("D", dimensionSet(0, -2, 0, 0, 0), tensor::zero),
F_("F", dimensionSet(0, -1, 0, 0, 0), tensor::zero)
{
Info<< "Creating porous zone: " << name_ << endl;
bool foundZone = (cellZoneID_ != -1);
reduce(foundZone, orOp<bool>());
if (!foundZone && Pstream::master())
{
FatalErrorIn
(
"Foam::porousZone::porousZone"
"(const fvMesh&, const word&, const dictionary&)"
) << "cannot find porous cellZone " << name_
<< exit(FatalError);
}
// porosity
if (dict_.readIfPresent("porosity", porosity_))
{
if (porosity_ <= 0.0 || porosity_ > 1.0)
{
FatalIOErrorIn
(
"Foam::porousZone::porousZone"
"(const fvMesh&, const word&, const dictionary&)",
dict_
)
<< "out-of-range porosity value " << porosity_
<< exit(FatalIOError);
}
}
// powerLaw coefficients
if (const dictionary* dictPtr = dict_.subDictPtr("powerLaw"))
{
dictPtr->readIfPresent("C0", C0_);
dictPtr->readIfPresent("C1", C1_);
}
// Darcy-Forchheimer coefficients
if (const dictionary* dictPtr = dict_.subDictPtr("Darcy"))
{
// local-to-global transformation tensor
const tensor& E = coordSys_.R();
dimensionedVector d(vector::zero);
if (dictPtr->readIfPresent("d", d))
{
if (D_.dimensions() != d.dimensions())
{
FatalIOErrorIn
(
"Foam::porousZone::porousZone"
"(const fvMesh&, const word&, const dictionary&)",
dict_
) << "incorrect dimensions for d: " << d.dimensions()
<< " should be " << D_.dimensions()
<< exit(FatalIOError);
}
adjustNegativeResistance(d);
D_.value().xx() = d.value().x();
D_.value().yy() = d.value().y();
D_.value().zz() = d.value().z();
D_.value() = (E & D_ & E.T()).value();
}
dimensionedVector f(vector::zero);
if (dictPtr->readIfPresent("f", f))
{
if (F_.dimensions() != f.dimensions())
{
FatalIOErrorIn
(
"Foam::porousZone::porousZone"
"(const fvMesh&, const word&, const dictionary&)",
dict_
) << "incorrect dimensions for f: " << f.dimensions()
<< " should be " << F_.dimensions()
<< exit(FatalIOError);
}
adjustNegativeResistance(f);
// leading 0.5 is from 1/2 * rho
F_.value().xx() = 0.5*f.value().x();
F_.value().yy() = 0.5*f.value().y();
F_.value().zz() = 0.5*f.value().z();
F_.value() = (E & F_ & E.T()).value();
}
}
// provide some feedback for the user
// writeDict(Info, false);
// it is an error not to define anything
if
(
C0_ <= VSMALL
&& magSqr(D_.value()) <= VSMALL
&& magSqr(F_.value()) <= VSMALL
)
{
FatalIOErrorIn
(
"Foam::porousZone::porousZone"
"(const fvMesh&, const word&, const dictionary&)",
dict_
) << "neither powerLaw (C0/C1) "
"nor Darcy-Forchheimer law (d/f) specified"
<< exit(FatalIOError);
}
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::tmp<Foam::vectorField> Foam::porousZone::force
(
const volVectorField& U,
const volScalarField& rho,
const volScalarField& mu
) const
{
scalarField Udiag(U.size(), 0.0);
vectorField Usource(U.size(), vector::zero);
const scalarField& V = mesh_.V();
const labelList& cells = mesh_.cellZones()[cellZoneID_];
if (C0_ > VSMALL)
{
addPowerLawResistance(Udiag, cells, V, rho, U);
return Udiag*U;
}
// else
addViscousInertialResistance(Udiag, Usource, cells, V, rho, mu, U);
return Udiag*U - Usource;
}
void Foam::porousZone::addResistance(fvVectorMatrix& UEqn) const
{
if (cellZoneID_ == -1)
{
return;
}
bool compressible = false;
if (UEqn.dimensions() == dimensionSet(1, 1, -2, 0, 0))
{
compressible = true;
}
const labelList& cells = mesh_.cellZones()[cellZoneID_];
const scalarField& V = mesh_.V();
scalarField& Udiag = UEqn.diag();
vectorField& Usource = UEqn.source();
const vectorField& U = UEqn.psi();
if (C0_ > VSMALL)
{
if (compressible)
{
addPowerLawResistance
(
Udiag,
cells,
V,
mesh_.lookupObject<volScalarField>("rho"),
U
);
}
else
{
addPowerLawResistance
(
Udiag,
cells,
V,
geometricOneField(),
U
);
}
}
const tensor& D = D_.value();
const tensor& F = F_.value();
if (magSqr(D) > VSMALL || magSqr(F) > VSMALL)
{
if (compressible)
{
addViscousInertialResistance
(
Udiag,
Usource,
cells,
V,
mesh_.lookupObject<volScalarField>("rho"),
mesh_.lookupObject<volScalarField>("mu"),
U
);
}
else
{
addViscousInertialResistance
(
Udiag,
Usource,
cells,
V,
geometricOneField(),
mesh_.lookupObject<volScalarField>("nu"),
U
);
}
}
}
void Foam::porousZone::addResistance
(
const fvVectorMatrix& UEqn,
volTensorField& AU,
bool correctAUprocBC
) const
{
if (cellZoneID_ == -1)
{
return;
}
bool compressible = false;
if (UEqn.dimensions() == dimensionSet(1, 1, -2, 0, 0))
{
compressible = true;
}
const labelList& cells = mesh_.cellZones()[cellZoneID_];
const vectorField& U = UEqn.psi();
if (C0_ > VSMALL)
{
if (compressible)
{
addPowerLawResistance
(
AU,
cells,
mesh_.lookupObject<volScalarField>("rho"),
U
);
}
else
{
addPowerLawResistance
(
AU,
cells,
geometricOneField(),
U
);
}
}
const tensor& D = D_.value();
const tensor& F = F_.value();
if (magSqr(D) > VSMALL || magSqr(F) > VSMALL)
{
if (compressible)
{
addViscousInertialResistance
(
AU,
cells,
mesh_.lookupObject<volScalarField>("rho"),
mesh_.lookupObject<volScalarField>("mu"),
U
);
}
else
{
addViscousInertialResistance
(
AU,
cells,
geometricOneField(),
mesh_.lookupObject<volScalarField>("nu"),
U
);
}
}
if (correctAUprocBC)
{
// Correct the boundary conditions of the tensorial diagonal to ensure
// processor boundaries are correctly handled when AU^-1 is interpolated
// for the pressure equation.
AU.correctBoundaryConditions();
}
}
void Foam::porousZone::writeDict(Ostream& os, bool subDict) const
{
if (subDict)
{
os << indent << token::BEGIN_BLOCK << incrIndent << nl;
os.writeKeyword("name") << zoneName() << token::END_STATEMENT << nl;
}
else
{
os << indent << zoneName() << nl
<< indent << token::BEGIN_BLOCK << incrIndent << nl;
}
if (dict_.found("note"))
{
os.writeKeyword("note") << string(dict_.lookup("note"))
<< token::END_STATEMENT << nl;
}
coordSys_.writeDict(os, true);
if (dict_.found("porosity"))
{
os.writeKeyword("porosity") << porosity() << token::END_STATEMENT << nl;
}
// powerLaw coefficients
if (const dictionary* dictPtr = dict_.subDictPtr("powerLaw"))
{
os << indent << "powerLaw";
dictPtr->write(os);
}
// Darcy-Forchheimer coefficients
if (const dictionary* dictPtr = dict_.subDictPtr("Darcy"))
{
os << indent << "Darcy";
dictPtr->write(os);
}
os << decrIndent << indent << token::END_BLOCK << endl;
}
// * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
Foam::Ostream& Foam::operator<<(Ostream& os, const porousZone& pZone)
{
pZone.writeDict(os);
return os;
}
// ************************************************************************* //
Therefore I implemented it by refering to OpenFOAM-2.2.
The calculation of porous effect is activated by porosity=true.
4 files were changed.
src/postProcessing/functionObjects/forces/forces/forces.H
\*---------------------------------------------------------------------------*/
#ifndef forces_H
#define forces_H
#include "primitiveFieldsFwd.H"
#include "volFieldsFwd.H"
#include "HashSet.H"
#include "Tuple2.H"
#include "OFstream.H"
#include "Switch.H"
#include "pointFieldFwd.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
// Forward declaration of classes
class objectRegistry;
class dictionary;
class mapPolyMesh;
/*---------------------------------------------------------------------------*\
Class forces Declaration
\*---------------------------------------------------------------------------*/
class forces
{
public:
// Tuple which holds the pressure (.first()) and viscous (.second) forces
typedef Tuple2<vector, vector> pressureViscous;
// Tuple which holds the forces (.first()) and moment (.second)
// pressure/viscous forces Tuples.
typedef Tuple2<pressureViscous, pressureViscous> forcesMoments;
//- Sum operation class to accumulate the pressure, viscous forces
// and moments
class sumOp
{
public:
forcesMoments operator()
(
const forcesMoments& fm1,
const forcesMoments& fm2
) const
{
return forcesMoments
(
pressureViscous
(
fm1.first().first() + fm2.first().first(),
fm1.first().second() + fm2.first().second()
),
pressureViscous
(
fm1.second().first() + fm2.second().first(),
fm1.second().second() + fm2.second().second()
)
);
}
};
protected:
// Private data
//- Name of this set of forces,
// Also used as the name of the probes directory.
word name_;
const objectRegistry& obr_;
//- on/off switch
bool active_;
//- Switch to send output to Info as well as to file
Switch log_;
// Read from dictionary
//- Patches to integrate forces over
labelHashSet patchSet_;
//- Name of pressure field
word pName_;
//- Name of velocity field
word UName_;
//- Name of density field (optional)
word rhoName_;
//- Is the force density being supplied directly?
Switch directForceDensity_;
//- The name of the force density (fD) field
word fDName_;
//- Reference density needed for incompressible calculations
scalar rhoRef_;
//- Reference pressure
scalar pRef_;
//- Centre of rotation
vector CofR_;
//- Switch for porous effect calculation
Switch porosity_;
//- Forces/moment file ptr
autoPtr<OFstream> forcesFilePtr_;
// Private Member Functions
//- If the forces file has not been created create it
void makeFile();
//- Output file header information
virtual void writeFileHeader();
//- Return the effective viscous stress (laminar + turbulent).
tmp<volSymmTensorField> devRhoReff() const;
//- Return mu
tmp<volScalarField> mu() const;
//- Return rho if rhoName is specified otherwise rhoRef
tmp<volScalarField> rho() const;
//- Return rhoRef if the pressure field is dynamic, i.e. p/rho
// otherwise return 1
scalar rho(const volScalarField& p) const;
//- Disallow default bitwise copy construct
forces(const forces&);
//- Disallow default bitwise assignment
void operator=(const forces&);
public:
//- Runtime type information
TypeName("forces");
// Constructors
//- Construct for given objectRegistry and dictionary.
// Allow the possibility to load fields from files
forces
(
const word& name,
const objectRegistry&,
const dictionary&,
const bool loadFromFiles = false
);
//- Destructor
virtual ~forces();
// Member Functions
//- Return name of the set of forces
virtual const word& name() const
{
return name_;
}
//- Read the forces data
virtual void read(const dictionary&);
//- Execute, currently does nothing
virtual void execute();
//- Execute at the final time-loop, currently does nothing
virtual void end();
//- Write the forces
virtual void write();
//- Calculate and return porous effect
virtual forcesMoments calcPorousEffect() const;
//- Calculate and return forces and moment
virtual forcesMoments calcForcesMoment() const;
//- Update for changes of mesh
virtual void updateMesh(const mapPolyMesh&)
{}
//- Update for changes of mesh
virtual void movePoints(const pointField&)
{}
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //
src/postProcessing/functionObjects/forces/forces/forces.C
\*---------------------------------------------------------------------------*/
#include "forces.H"
#include "volFields.H"
#include "dictionary.H"
#include "foamTime.H"
#include "incompressible/singlePhaseTransportModel/singlePhaseTransportModel.H"
#include "incompressible/RAS/RASModel/RASModel.H"
#include "incompressible/LES/LESModel/LESModel.H"
#include "basicThermo.H"
#include "compressible/RAS/RASModel/RASModel.H"
#include "compressible/LES/LESModel/LESModel.H"
#include "porousZones.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(forces, 0);
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::tmp<Foam::volSymmTensorField> Foam::forces::devRhoReff() const
{
if (obr_.foundObject<compressible::RASModel>("RASProperties"))
{
const compressible::RASModel& ras
= obr_.lookupObject<compressible::RASModel>("RASProperties");
return ras.devRhoReff();
}
else if (obr_.foundObject<incompressible::RASModel>("RASProperties"))
{
const incompressible::RASModel& ras
= obr_.lookupObject<incompressible::RASModel>("RASProperties");
return rho()*ras.devReff();
}
else if (obr_.foundObject<compressible::LESModel>("LESProperties"))
{
const compressible::LESModel& les =
obr_.lookupObject<compressible::LESModel>("LESProperties");
return les.devRhoBeff();
}
else if (obr_.foundObject<incompressible::LESModel>("LESProperties"))
{
const incompressible::LESModel& les
= obr_.lookupObject<incompressible::LESModel>("LESProperties");
return rho()*les.devBeff();
}
else if (obr_.foundObject<basicThermo>("thermophysicalProperties"))
{
const basicThermo& thermo =
obr_.lookupObject<basicThermo>("thermophysicalProperties");
const volVectorField& U = obr_.lookupObject<volVectorField>(UName_);
return -thermo.mu()*dev(twoSymm(fvc::grad(U)));
}
else if
(
obr_.foundObject<singlePhaseTransportModel>("transportProperties")
)
{
const singlePhaseTransportModel& laminarT =
obr_.lookupObject<singlePhaseTransportModel>
("transportProperties");
const volVectorField& U = obr_.lookupObject<volVectorField>(UName_);
return -rho()*laminarT.nu()*dev(twoSymm(fvc::grad(U)));
}
else if (obr_.foundObject<dictionary>("transportProperties"))
{
const dictionary& transportProperties =
obr_.lookupObject<dictionary>("transportProperties");
dimensionedScalar nu(transportProperties.lookup("nu"));
const volVectorField& U = obr_.lookupObject<volVectorField>(UName_);
return -rho()*nu*dev(twoSymm(fvc::grad(U)));
}
else
{
FatalErrorIn("forces::devRhoReff()")
<< "No valid model for viscous stress calculation."
<< exit(FatalError);
return volSymmTensorField::null();
}
}
Foam::tmp<Foam::volScalarField> Foam::forces::mu() const
{
if (obr_.foundObject<basicThermo>("thermophysicalProperties"))
{
const basicThermo& thermo =
obr_.lookupObject<basicThermo>("thermophysicalProperties");
return thermo.mu();
}
else if
(
obr_.foundObject<transportModel>("transportProperties")
)
{
const transportModel& laminarT =
obr_.lookupObject<transportModel>("transportProperties");
return rho()*laminarT.nu();
}
else if (obr_.foundObject<dictionary>("transportProperties"))
{
const dictionary& transportProperties =
obr_.lookupObject<dictionary>("transportProperties");
dimensionedScalar nu(transportProperties.lookup("nu"));
return rho()*nu;
}
else
{
FatalErrorIn("forces::mu()")
<< "No valid model for dynamic viscosity calculation"
<< exit(FatalError);
return volScalarField::null();
}
}
Foam::tmp<Foam::volScalarField> Foam::forces::rho() const
{
if (rhoName_ == "rhoInf")
{
const fvMesh& mesh = refCast<const fvMesh>(obr_);
return tmp<volScalarField>
(
new volScalarField
(
IOobject
(
"rho",
mesh.time().timeName(),
mesh
),
mesh,
dimensionedScalar("rho", dimDensity, rhoRef_)
)
);
}
else
{
return(obr_.lookupObject<volScalarField>(rhoName_));
}
}
Foam::scalar Foam::forces::rho(const volScalarField& p) const
{
if (p.dimensions() == dimPressure)
{
return 1.0;
}
else
{
if (rhoName_ != "rhoInf")
{
FatalErrorIn("forces::rho(const volScalarField& p)")
<< "Dynamic pressure is expected but kinematic is provided."
<< exit(FatalError);
}
return rhoRef_;
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::forces::forces
(
const word& name,
const objectRegistry& obr,
const dictionary& dict,
const bool loadFromFiles
)
:
name_(name),
obr_(obr),
active_(true),
log_(false),
patchSet_(),
pName_(word::null),
UName_(word::null),
rhoName_(word::null),
directForceDensity_(false),
fDName_(""),
rhoRef_(VGREAT),
pRef_(0),
CofR_(vector::zero),
porosity_(false),
forcesFilePtr_(NULL)
{
// Check if the available mesh is an fvMesh otherise deactivate
if (!isA<fvMesh>(obr_))
{
active_ = false;
WarningIn
(
"Foam::forces::forces"
"("
"const word&, "
"const objectRegistry&, "
"const dictionary&, "
"const bool"
")"
) << "No fvMesh available, deactivating."
<< endl;
}
read(dict);
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::forces::~forces()
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
void Foam::forces::read(const dictionary& dict)
{
if (active_)
{
log_ = dict.lookupOrDefault<Switch>("log", false);
const fvMesh& mesh = refCast<const fvMesh>(obr_);
patchSet_ =
mesh.boundaryMesh().patchSet(wordList(dict.lookup("patches")));
dict.readIfPresent("directForceDensity", directForceDensity_);
if (directForceDensity_)
{
// Optional entry for fDName
fDName_ = dict.lookupOrDefault<word>("fDName", "fD");
// Check whether fDName exists, if not deactivate forces
if
(
!obr_.foundObject<volVectorField>(fDName_)
)
{
active_ = false;
WarningIn("void forces::read(const dictionary& dict)")
<< "Could not find " << fDName_ << " in database." << nl
<< " De-activating forces."
<< endl;
}
}
else
{
// Optional entries U and p
pName_ = dict.lookupOrDefault<word>("pName", "p");
UName_ = dict.lookupOrDefault<word>("UName", "U");
rhoName_ = dict.lookupOrDefault<word>("rhoName", "rho");
// Check whether UName, pName and rhoName exists,
// if not deactivate forces
if
(
!obr_.foundObject<volVectorField>(UName_)
|| !obr_.foundObject<volScalarField>(pName_)
|| (
rhoName_ != "rhoInf"
&& !obr_.foundObject<volScalarField>(rhoName_)
)
)
{
active_ = false;
WarningIn("void forces::read(const dictionary& dict)")
<< "Could not find " << UName_ << ", " << pName_;
if (rhoName_ != "rhoInf")
{
Info<< " or " << rhoName_;
}
Info<< " in database." << nl << " De-activating forces."
<< endl;
}
// Reference density needed for incompressible calculations
rhoRef_ = readScalar(dict.lookup("rhoInf"));
// Reference pressure, 0 by default
pRef_ = dict.lookupOrDefault<scalar>("pRef", 0.0);
}
// Centre of rotation for moment calculations
CofR_ = dict.lookup("CofR");
porosity_ = dict.lookupOrDefault<Switch>("porosity", false);
}
}
void Foam::forces::makeFile()
{
// Create the forces file if not already created
if (forcesFilePtr_.empty())
{
if (debug)
{
Info<< "Creating forces file." << endl;
}
// File update
if (Pstream::master())
{
fileName forcesDir;
word startTimeName =
obr_.time().timeName(obr_.time().startTime().value());
if (Pstream::parRun())
{
// Put in undecomposed case (Note: gives problems for
// distributed data running)
forcesDir = obr_.time().path()/".."/name_/startTimeName;
}
else
{
forcesDir = obr_.time().path()/name_/startTimeName;
}
// Create directory if does not exist.
mkDir(forcesDir);
// Open new file at start up
forcesFilePtr_.reset(new OFstream(forcesDir/(type() + ".dat")));
// Add headers to output data
writeFileHeader();
}
}
}
void Foam::forces::writeFileHeader()
{
if (forcesFilePtr_.valid())
{
forcesFilePtr_()
<< "# Time" << tab
<< "forces(pressure, viscous) moment(pressure, viscous) porous(force, moment)"
<< endl;
}
}
void Foam::forces::execute()
{
// Do nothing - only valid on write
}
void Foam::forces::end()
{
// Do nothing - only valid on write
}
void Foam::forces::write()
{
if (active_)
{
// Create the forces file if not already created
makeFile();
forcesMoments fm = calcForcesMoment();
porousEffect pe = calcPorousEffect();
if (Pstream::master())
{
forcesFilePtr_() << obr_.time().value() << tab << fm
<< "(" << pe.first().first()
<< " " << pe.second().first() << ")" << endl;
if (log_)
{
Info<< "forces output:" << nl
<< " forces(pressure, viscous)" << fm.first() << nl
<< " moment(pressure, viscous)" << fm.second() << nl
<< " porous( force, moment)" << "(" << pe.first().first() << " "
<< pe.second().first() << ")" << nl
<< endl;
}
}
}
}
Foam::forces::forcesMoments Foam::forces::calcPorousEffect() const
{
forcesMoments pe
(
pressureViscous(vector::zero, vector::zero),
pressureViscous(vector::zero, vector::zero)
);
{
// calculate force and moment by porous effect.
{
const volVectorField& U = obr_.lookupObject<volVectorField>(UName_);
const volScalarField& rho(this->rho());
const volScalarField& mu(this->mu());
const fvMesh& mesh = U.mesh();
porousZones pZones(mesh);
if (pZones.size() == 0)
{
WarningIn("void Foam::forces::calcPorousEffect()")
<< "Porosity effects requested, but no porosity models found "
<< "in the database"
<< endl;
}
forAll(pZones, i)
{
const porousZone& pm = pZones[i];
vectorField fPTot(pm.force(U, rho, mu));
label zoneI = pm.zoneId();
const cellZone& cZone = mesh.cellZones()[zoneI];
const vectorField d(mesh.C(), cZone);
const vectorField fP(fPTot, cZone);
const vectorField Md(d - CofR_);
pe.first().first() += sum(fP);
pe.second().first() += sum(Md ^ fP);
}
}
}
reduce(pe, sumOp());
}
Foam::forces::forcesMoments Foam::forces::calcForcesMoment() const
{
forcesMoments fm
(
pressureViscous(vector::zero, vector::zero),
pressureViscous(vector::zero, vector::zero)
);
if (directForceDensity_)
{
const volVectorField& fD = obr_.lookupObject<volVectorField>(fDName_);
const fvMesh& mesh = fD.mesh();
const surfaceVectorField::GeometricBoundaryField& Sfb =
mesh.Sf().boundaryField();
forAllConstIter(labelHashSet, patchSet_, iter)
{
label patchi = iter.key();
vectorField Md = mesh.C().boundaryField()[patchi] - CofR_;
scalarField sA = mag(Sfb[patchi]);
// Normal force = surfaceUnitNormal*(surfaceNormal & forceDensity)
vectorField fN =
Sfb[patchi]/sA
*(
Sfb[patchi] & fD.boundaryField()[patchi]
);
fm.first().first() += sum(fN);
fm.second().first() += sum(Md ^ fN);
// Tangential force (total force minus normal fN)
vectorField fT = sA*fD.boundaryField()[patchi] - fN;
fm.first().second() += sum(fT);
fm.second().second() += sum(Md ^ fT);
}
}
else
{
const volVectorField& U = obr_.lookupObject<volVectorField>(UName_);
const volScalarField& p = obr_.lookupObject<volScalarField>(pName_);
const fvMesh& mesh = U.mesh();
const surfaceVectorField::GeometricBoundaryField& Sfb =
mesh.Sf().boundaryField();
tmp<volSymmTensorField> tdevRhoReff = devRhoReff();
const volSymmTensorField::GeometricBoundaryField& devRhoReffb
= tdevRhoReff().boundaryField();
// Scale pRef by density for incompressible simulations
scalar pRef = pRef_/rho(p);
forAllConstIter(labelHashSet, patchSet_, iter)
{
label patchi = iter.key();
vectorField Md = mesh.C().boundaryField()[patchi] - CofR_;
vectorField pf = Sfb[patchi]*(p.boundaryField()[patchi] - pRef);
fm.first().first() += rho(p)*sum(pf);
fm.second().first() += rho(p)*sum(Md ^ pf);
vectorField vf = Sfb[patchi] & devRhoReffb[patchi];
fm.first().second() += sum(vf);
fm.second().second() += sum(Md ^ vf);
}
}
reduce(fm, sumOp());
return fm;
}
// ************************************************************************* //
src/finiteVolume/cfdTools/general/porousMedia/porousZone.H
\*---------------------------------------------------------------------------*/
#ifndef porousZone_H
#define porousZone_H
#include "dictionary.H"
#include "coordinateSystem.H"
#include "coordinateSystems.H"
#include "wordList.H"
#include "labelList.H"
#include "dimensionedScalar.H"
#include "dimensionedTensor.H"
#include "primitiveFieldsFwd.H"
#include "volFieldsFwd.H"
#include "fvMatricesFwd.H"
#include "fvMesh.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
class fvMesh;
/*---------------------------------------------------------------------------*\
Class porousZone Declaration
\*---------------------------------------------------------------------------*/
class porousZone
{
// Private data
//- Name of this zone
word name_;
//- Reference to the finite volume mesh this zone is part of
const fvMesh& mesh_;
//- Dictionary containing the parameters
dictionary dict_;
//- Cell zone ID
label cellZoneID_;
//- Coordinate system used for the zone (Cartesian)
coordinateSystem coordSys_;
//- porosity of the zone (0 < porosity <= 1)
// Placeholder for treatment of temporal terms.
// Currently unused.
scalar porosity_;
//- powerLaw coefficient C0
scalar C0_;
//- powerLaw coefficient C1
scalar C1_;
//- Darcy coefficient
dimensionedTensor D_;
//- Forchheimer coefficient
dimensionedTensor F_;
// Private Member Functions
//- adjust negative resistance values to be multiplier of max value
static void adjustNegativeResistance(dimensionedVector& resist);
//- Power-law resistance
template<class RhoFieldType>
void addPowerLawResistance
(
scalarField& Udiag,
const labelList& cells,
const scalarField& V,
const RhoFieldType& rho,
const vectorField& U
) const;
//- Viscous and inertial resistance
template<class RhoFieldType>
void addViscousInertialResistance
(
scalarField& Udiag,
vectorField& Usource,
const labelList& cells,
const scalarField& V,
const RhoFieldType& rho,
const scalarField& mu,
const vectorField& U
) const;
//- Power-law resistance
template<class RhoFieldType>
void addPowerLawResistance
(
tensorField& AU,
const labelList& cells,
const RhoFieldType& rho,
const vectorField& U
) const;
//- Viscous and inertial resistance
template<class RhoFieldType>
void addViscousInertialResistance
(
tensorField& AU,
const labelList& cells,
const RhoFieldType& rho,
const scalarField& mu,
const vectorField& U
) const;
//- Disallow default bitwise copy construct
porousZone(const porousZone&);
//- Disallow default bitwise assignment
void operator=(const porousZone&);
public:
// Constructors
//- Construct from components
porousZone(const word& name, const fvMesh&, const dictionary&);
//- Return clone
autoPtr<porousZone> clone() const
{
notImplemented("autoPtr<porousZone> clone() const");
return autoPtr<porousZone>(NULL);
}
//- Return pointer to new porousZone created on freestore from Istream
class iNew
{
//- Reference to the finite volume mesh this zone is part of
const fvMesh& mesh_;
public:
iNew(const fvMesh& mesh)
:
mesh_(mesh)
{}
autoPtr<porousZone> operator()(Istream& is) const
{
word name(is);
dictionary dict(is);
return autoPtr<porousZone>(new porousZone(name, mesh_, dict));
}
};
//- Destructor
virtual ~porousZone()
{}
// Member Functions
// Access
//- cellZone name
const word& zoneName() const
{
return name_;
}
//- Return mesh
const fvMesh& mesh() const
{
return mesh_;
}
//- cellZone number
label zoneId() const
{
return cellZoneID_;
}
//- dictionary values used for the porousZone
const dictionary& dict() const
{
return dict_;
}
//- Return coordinate system
const coordinateSystem& coordSys() const
{
return coordSys_;
}
//- Return origin
const point& origin() const
{
return coordSys_.origin();
}
//- Return axis
vector axis() const
{
return coordSys_.axis();
}
//- Return porosity
scalar porosity() const
{
return porosity_;
}
//- Edit access to porosity
scalar& porosity()
{
return porosity_;
}
//- Modify time derivative elements according to porosity
template<class Type>
void modifyDdt(fvMatrix<Type>&) const;
//-
//
Foam::tmp<Foam::vectorField> force
(
const volVectorField& U,
const volScalarField& rho,
const volScalarField& mu
) const;
//- Add the viscous and inertial resistance force contribution
// to the momentum equation
void addResistance(fvVectorMatrix& UEqn) const;
//- Add the viscous and inertial resistance force contribution
// to the tensorial diagonal.
// Optionally correct the processor BCs of AU.
void addResistance
(
const fvVectorMatrix& UEqn,
volTensorField& AU,
bool correctAUprocBC = true
) const;
//- Write the porousZone dictionary
virtual void writeDict(Ostream&, bool subDict = true) const;
// Ostream Operator
friend Ostream& operator<<(Ostream&, const porousZone&);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#ifdef NoRepository
# include "porousZoneTemplates.C"
#endif
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //
src/finiteVolume/cfdTools/general/porousMedia/porousZone.C
\*---------------------------------------------------------------------------*/
#include "porousZone.H"
#include "fvMesh.H"
#include "fvMatrices.H"
#include "geometricOneField.H"
// * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * * //
// adjust negative resistance values to be multiplier of max value
void Foam::porousZone::adjustNegativeResistance(dimensionedVector& resist)
{
scalar maxCmpt = max(0, cmptMax(resist.value()));
if (maxCmpt < 0)
{
FatalErrorIn
(
"Foam::porousZone::porousZone::adjustNegativeResistance"
"(dimensionedVector&)"
) << "negative resistances! " << resist
<< exit(FatalError);
}
else
{
vector& val = resist.value();
for (label cmpt=0; cmpt < vector::nComponents; ++cmpt)
{
if (val[cmpt] < 0)
{
val[cmpt] *= -maxCmpt;
}
}
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::porousZone::porousZone
(
const word& name,
const fvMesh& mesh,
const dictionary& dict
)
:
name_(name),
mesh_(mesh),
dict_(dict),
cellZoneID_(mesh_.cellZones().findZoneID(name)),
coordSys_(dict, mesh),
porosity_(1),
C0_(0),
C1_(0),
D_("D", dimensionSet(0, -2, 0, 0, 0), tensor::zero),
F_("F", dimensionSet(0, -1, 0, 0, 0), tensor::zero)
{
Info<< "Creating porous zone: " << name_ << endl;
bool foundZone = (cellZoneID_ != -1);
reduce(foundZone, orOp<bool>());
if (!foundZone && Pstream::master())
{
FatalErrorIn
(
"Foam::porousZone::porousZone"
"(const fvMesh&, const word&, const dictionary&)"
) << "cannot find porous cellZone " << name_
<< exit(FatalError);
}
// porosity
if (dict_.readIfPresent("porosity", porosity_))
{
if (porosity_ <= 0.0 || porosity_ > 1.0)
{
FatalIOErrorIn
(
"Foam::porousZone::porousZone"
"(const fvMesh&, const word&, const dictionary&)",
dict_
)
<< "out-of-range porosity value " << porosity_
<< exit(FatalIOError);
}
}
// powerLaw coefficients
if (const dictionary* dictPtr = dict_.subDictPtr("powerLaw"))
{
dictPtr->readIfPresent("C0", C0_);
dictPtr->readIfPresent("C1", C1_);
}
// Darcy-Forchheimer coefficients
if (const dictionary* dictPtr = dict_.subDictPtr("Darcy"))
{
// local-to-global transformation tensor
const tensor& E = coordSys_.R();
dimensionedVector d(vector::zero);
if (dictPtr->readIfPresent("d", d))
{
if (D_.dimensions() != d.dimensions())
{
FatalIOErrorIn
(
"Foam::porousZone::porousZone"
"(const fvMesh&, const word&, const dictionary&)",
dict_
) << "incorrect dimensions for d: " << d.dimensions()
<< " should be " << D_.dimensions()
<< exit(FatalIOError);
}
adjustNegativeResistance(d);
D_.value().xx() = d.value().x();
D_.value().yy() = d.value().y();
D_.value().zz() = d.value().z();
D_.value() = (E & D_ & E.T()).value();
}
dimensionedVector f(vector::zero);
if (dictPtr->readIfPresent("f", f))
{
if (F_.dimensions() != f.dimensions())
{
FatalIOErrorIn
(
"Foam::porousZone::porousZone"
"(const fvMesh&, const word&, const dictionary&)",
dict_
) << "incorrect dimensions for f: " << f.dimensions()
<< " should be " << F_.dimensions()
<< exit(FatalIOError);
}
adjustNegativeResistance(f);
// leading 0.5 is from 1/2 * rho
F_.value().xx() = 0.5*f.value().x();
F_.value().yy() = 0.5*f.value().y();
F_.value().zz() = 0.5*f.value().z();
F_.value() = (E & F_ & E.T()).value();
}
}
// provide some feedback for the user
// writeDict(Info, false);
// it is an error not to define anything
if
(
C0_ <= VSMALL
&& magSqr(D_.value()) <= VSMALL
&& magSqr(F_.value()) <= VSMALL
)
{
FatalIOErrorIn
(
"Foam::porousZone::porousZone"
"(const fvMesh&, const word&, const dictionary&)",
dict_
) << "neither powerLaw (C0/C1) "
"nor Darcy-Forchheimer law (d/f) specified"
<< exit(FatalIOError);
}
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::tmp<Foam::vectorField> Foam::porousZone::force
(
const volVectorField& U,
const volScalarField& rho,
const volScalarField& mu
) const
{
scalarField Udiag(U.size(), 0.0);
vectorField Usource(U.size(), vector::zero);
const scalarField& V = mesh_.V();
const labelList& cells = mesh_.cellZones()[cellZoneID_];
if (C0_ > VSMALL)
{
addPowerLawResistance(Udiag, cells, V, rho, U);
return Udiag*U;
}
// else
addViscousInertialResistance(Udiag, Usource, cells, V, rho, mu, U);
return Udiag*U - Usource;
}
void Foam::porousZone::addResistance(fvVectorMatrix& UEqn) const
{
if (cellZoneID_ == -1)
{
return;
}
bool compressible = false;
if (UEqn.dimensions() == dimensionSet(1, 1, -2, 0, 0))
{
compressible = true;
}
const labelList& cells = mesh_.cellZones()[cellZoneID_];
const scalarField& V = mesh_.V();
scalarField& Udiag = UEqn.diag();
vectorField& Usource = UEqn.source();
const vectorField& U = UEqn.psi();
if (C0_ > VSMALL)
{
if (compressible)
{
addPowerLawResistance
(
Udiag,
cells,
V,
mesh_.lookupObject<volScalarField>("rho"),
U
);
}
else
{
addPowerLawResistance
(
Udiag,
cells,
V,
geometricOneField(),
U
);
}
}
const tensor& D = D_.value();
const tensor& F = F_.value();
if (magSqr(D) > VSMALL || magSqr(F) > VSMALL)
{
if (compressible)
{
addViscousInertialResistance
(
Udiag,
Usource,
cells,
V,
mesh_.lookupObject<volScalarField>("rho"),
mesh_.lookupObject<volScalarField>("mu"),
U
);
}
else
{
addViscousInertialResistance
(
Udiag,
Usource,
cells,
V,
geometricOneField(),
mesh_.lookupObject<volScalarField>("nu"),
U
);
}
}
}
void Foam::porousZone::addResistance
(
const fvVectorMatrix& UEqn,
volTensorField& AU,
bool correctAUprocBC
) const
{
if (cellZoneID_ == -1)
{
return;
}
bool compressible = false;
if (UEqn.dimensions() == dimensionSet(1, 1, -2, 0, 0))
{
compressible = true;
}
const labelList& cells = mesh_.cellZones()[cellZoneID_];
const vectorField& U = UEqn.psi();
if (C0_ > VSMALL)
{
if (compressible)
{
addPowerLawResistance
(
AU,
cells,
mesh_.lookupObject<volScalarField>("rho"),
U
);
}
else
{
addPowerLawResistance
(
AU,
cells,
geometricOneField(),
U
);
}
}
const tensor& D = D_.value();
const tensor& F = F_.value();
if (magSqr(D) > VSMALL || magSqr(F) > VSMALL)
{
if (compressible)
{
addViscousInertialResistance
(
AU,
cells,
mesh_.lookupObject<volScalarField>("rho"),
mesh_.lookupObject<volScalarField>("mu"),
U
);
}
else
{
addViscousInertialResistance
(
AU,
cells,
geometricOneField(),
mesh_.lookupObject<volScalarField>("nu"),
U
);
}
}
if (correctAUprocBC)
{
// Correct the boundary conditions of the tensorial diagonal to ensure
// processor boundaries are correctly handled when AU^-1 is interpolated
// for the pressure equation.
AU.correctBoundaryConditions();
}
}
void Foam::porousZone::writeDict(Ostream& os, bool subDict) const
{
if (subDict)
{
os << indent << token::BEGIN_BLOCK << incrIndent << nl;
os.writeKeyword("name") << zoneName() << token::END_STATEMENT << nl;
}
else
{
os << indent << zoneName() << nl
<< indent << token::BEGIN_BLOCK << incrIndent << nl;
}
if (dict_.found("note"))
{
os.writeKeyword("note") << string(dict_.lookup("note"))
<< token::END_STATEMENT << nl;
}
coordSys_.writeDict(os, true);
if (dict_.found("porosity"))
{
os.writeKeyword("porosity") << porosity() << token::END_STATEMENT << nl;
}
// powerLaw coefficients
if (const dictionary* dictPtr = dict_.subDictPtr("powerLaw"))
{
os << indent << "powerLaw";
dictPtr->write(os);
}
// Darcy-Forchheimer coefficients
if (const dictionary* dictPtr = dict_.subDictPtr("Darcy"))
{
os << indent << "Darcy";
dictPtr->write(os);
}
os << decrIndent << indent << token::END_BLOCK << endl;
}
// * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
Foam::Ostream& Foam::operator<<(Ostream& os, const porousZone& pZone)
{
pZone.writeDict(os);
return os;
}
// ************************************************************************* //
Hi
ReplyDeleteI want to do this for viscoelastic fluid. but unfortunately I don't know about this code! so I learned OOP in C++. but still I can't find out about the commands in this code! could you please help me?
Thanks a lot
These are only a small part of my code which give additional features to foam-extend-3.2 (fe32). If you want to find out what the code does more precisely, please visit the fe32 GitHub repository.
Deletehttps://github.com/Unofficial-Extend-Project-Mirror/foam-extend-foam-extend-3.2
My main problem is finding out how this commands work! I want to make some changes on forces code to calculate Cd and Cl for flow of viscoelastic fluid around a cylinder.
DeleteI don't understand what you mean. What are the commands you are talking about?
DeleteThese are just calculating the porous forces at all cells included in the cellSet with porosity and summing them all.
for example:
ReplyDeleteFoam::tmp Foam::forces::mu() const
{
if (obr_.foundObject("thermophysicalProperties"))
{
const basicThermo& thermo =
obr_.lookupObject("thermophysicalProperties");
return thermo.mu();
}
else if ,.......
How could you write this code?
I can't understand the code! so I can't write the extra code for calculating forces in viscoelastic fluid.
The instance of basicThermo class which contains thermophysical properties is registered to obr_ with the name of thermophysicalProperties at the initialization stage of the solver.
DeleteTherefore if an instance registered as thermophysicalProperties is found, you can get the instance and pull mu from it.
obr_ is an object registry which contains global instances.
Hi again
ReplyDeleteI have other question:
I found out that for my purpose (adding calculating forces for viscoelastic fluids to force.C) I should just change devRhoReff() function (is it correct??)
To do that, we don't need to calculate stress, because stress is calculated as a flow parameter (like U , P ,...) . just I should return "tau" as an outout of devRhoReff() function. but when I do this, it can't read tau data!! it tells me erroe: "tau was not declared in this scope".
However, I want to know how you access to flow data like U and P? this will help me to access stress data.
Thank you
Are you sure tau is calculated as a flow parameter? I don't think it's usual.
DeleteNormally shear stress is calculated only when necessary and is not stored.
yes, if you see "viscoelasticFluidFoam" you can find that tau is a volSymmTensorField and it is calculated in every timeStep like p and U.
DeleteNow I put "tau" in return of devRhoReff() function. the Cd is calculated.
I have the other question:
why did they put minus in calculating friction force? :
-rho()*nu*dev(twoSymm(fvc::grad(U)))
It is because of the direction of Sf.
DeletevectorField vf = Sfb[patchi] & devRhoReffb[patchi];
Thank you for your short answers
ReplyDeleteBut sorry! I didnt get your statement. could you please explain it more?
I know that:
for calculating tangential force (friction force): fT = tau(ij) . Am(e_m)
where e_m is the unit normal vector to surface of cell. now, why should we put a minus in this relation?
for example why don't we use minus for calculation of devRhoReff in "icoTurbModel"?
thanks
In icoTurbModel, devRhoReff is the shear stress which fluid in the cell receives.
DeleteOn the other hand, devRhoReff in force.C is the shear stress which surface receives from fluid in the cell. That's why minus is put.
Hi
ReplyDeleteI have 2 questions:
1- what does sum(fT) function do?
2- what are "bin" or "binI" and applyBins function in forces.C code?
Thanks