1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 1991-2009 OpenCFD Ltd.
7 -------------------------------------------------------------------------------
9 This file is part of OpenFOAM.
11 OpenFOAM is free software; you can redistribute it and/or modify it
12 under the terms of the GNU General Public License as published by the
13 Free Software Foundation; either version 2 of the License, or (at your
14 option) any later version.
16 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 You should have received a copy of the GNU General Public License
22 along with OpenFOAM; if not, write to the Free Software Foundation,
23 Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 \*---------------------------------------------------------------------------*/
28 #include "volFields.H"
29 #include "dictionary.H"
32 #include "incompressible/singlePhaseTransportModel/singlePhaseTransportModel.H"
33 #include "incompressible/RAS/RASModel/RASModel.H"
34 #include "incompressible/LES/LESModel/LESModel.H"
36 #include "basicThermo.H"
37 #include "compressible/RAS/RASModel/RASModel.H"
38 #include "compressible/LES/LESModel/LESModel.H"
41 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
45 defineTypeNameAndDebug(forces, 0);
48 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
50 Foam::tmp<Foam::volSymmTensorField> Foam::forces::devRhoReff() const
52 if (obr_.foundObject<compressible::RASModel>("RASProperties"))
54 const compressible::RASModel& ras
55 = obr_.lookupObject<compressible::RASModel>("RASProperties");
57 return ras.devRhoReff();
59 else if (obr_.foundObject<incompressible::RASModel>("RASProperties"))
61 const incompressible::RASModel& ras
62 = obr_.lookupObject<incompressible::RASModel>("RASProperties");
64 return rho()*ras.devReff();
66 else if (obr_.foundObject<compressible::LESModel>("LESProperties"))
68 const compressible::LESModel& les =
69 obr_.lookupObject<compressible::LESModel>("LESProperties");
71 return les.devRhoBeff();
73 else if (obr_.foundObject<incompressible::LESModel>("LESProperties"))
75 const incompressible::LESModel& les
76 = obr_.lookupObject<incompressible::LESModel>("LESProperties");
78 return rho()*les.devBeff();
80 else if (obr_.foundObject<basicThermo>("thermophysicalProperties"))
82 const basicThermo& thermo =
83 obr_.lookupObject<basicThermo>("thermophysicalProperties");
85 const volVectorField& U = obr_.lookupObject<volVectorField>(UName_);
87 return -thermo.mu()*dev(twoSymm(fvc::grad(U)));
91 obr_.foundObject<singlePhaseTransportModel>("transportProperties")
94 const singlePhaseTransportModel& laminarT =
95 obr_.lookupObject<singlePhaseTransportModel>
96 ("transportProperties");
98 const volVectorField& U = obr_.lookupObject<volVectorField>(UName_);
100 return -rho()*laminarT.nu()*dev(twoSymm(fvc::grad(U)));
102 else if (obr_.foundObject<dictionary>("transportProperties"))
104 const dictionary& transportProperties =
105 obr_.lookupObject<dictionary>("transportProperties");
107 dimensionedScalar nu(transportProperties.lookup("nu"));
109 const volVectorField& U = obr_.lookupObject<volVectorField>(UName_);
111 return -rho()*nu*dev(twoSymm(fvc::grad(U)));
115 FatalErrorIn("forces::devRhoReff()")
116 << "No valid model for viscous stress calculation."
119 return volSymmTensorField::null();
124 Foam::tmp<Foam::volScalarField> Foam::forces::rho() const
126 if (rhoName_ == "rhoInf")
128 const fvMesh& mesh = refCast<const fvMesh>(obr_);
130 return tmp<volScalarField>
137 mesh.time().timeName(),
141 dimensionedScalar("rho", dimDensity, rhoRef_)
147 return(obr_.lookupObject<volScalarField>(rhoName_));
152 Foam::scalar Foam::forces::rho(const volScalarField& p) const
154 if (p.dimensions() == dimPressure)
160 if (rhoName_ != "rhoInf")
162 FatalErrorIn("forces::rho(const volScalarField& p)")
163 << "Dynamic pressure is expected but kinematic is provided."
172 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
177 const objectRegistry& obr,
178 const dictionary& dict,
179 const bool loadFromFiles
189 rhoName_(word::null),
190 directForceDensity_(false),
197 // Check if the available mesh is an fvMesh otherise deactivate
198 if (!isA<fvMesh>(obr_))
203 "Foam::forces::forces"
206 "const objectRegistry&, "
207 "const dictionary&, "
210 ) << "No fvMesh available, deactivating."
218 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
220 Foam::forces::~forces()
224 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
226 void Foam::forces::read(const dictionary& dict)
230 log_ = dict.lookupOrDefault<Switch>("log", false);
232 const fvMesh& mesh = refCast<const fvMesh>(obr_);
235 mesh.boundaryMesh().patchSet(wordList(dict.lookup("patches")));
237 dict.readIfPresent("directForceDensity", directForceDensity_);
239 if (directForceDensity_)
241 // Optional entry for fDName
242 fDName_ = dict.lookupOrDefault<word>("fDName", "fD");
244 // Check whether fDName exists, if not deactivate forces
247 !obr_.foundObject<volVectorField>(fDName_)
251 WarningIn("void forces::read(const dictionary& dict)")
252 << "Could not find " << fDName_ << " in database." << nl
253 << " De-activating forces."
259 // Optional entries U and p
260 pName_ = dict.lookupOrDefault<word>("pName", "p");
261 UName_ = dict.lookupOrDefault<word>("UName", "U");
262 rhoName_ = dict.lookupOrDefault<word>("rhoName", "rho");
264 // Check whether UName, pName and rhoName exists,
265 // if not deactivate forces
268 !obr_.foundObject<volVectorField>(UName_)
269 || !obr_.foundObject<volScalarField>(pName_)
272 && !obr_.foundObject<volScalarField>(rhoName_)
278 WarningIn("void forces::read(const dictionary& dict)")
279 << "Could not find " << UName_ << ", " << pName_;
281 if (rhoName_ != "rhoInf")
283 Info<< " or " << rhoName_;
286 Info<< " in database." << nl << " De-activating forces."
290 // Reference density needed for incompressible calculations
291 rhoRef_ = readScalar(dict.lookup("rhoInf"));
293 // Reference pressure, 0 by default
294 pRef_ = dict.lookupOrDefault<scalar>("pRef", 0.0);
297 // Centre of rotation for moment calculations
298 CofR_ = dict.lookup("CofR");
303 void Foam::forces::makeFile()
305 // Create the forces file if not already created
306 if (forcesFilePtr_.empty())
310 Info<< "Creating forces file." << endl;
314 if (Pstream::master())
318 obr_.time().timeName(obr_.time().startTime().value());
320 if (Pstream::parRun())
322 // Put in undecomposed case (Note: gives problems for
323 // distributed data running)
324 forcesDir = obr_.time().path()/".."/name_/startTimeName;
328 forcesDir = obr_.time().path()/name_/startTimeName;
331 // Create directory if does not exist.
334 // Open new file at start up
335 forcesFilePtr_.reset(new OFstream(forcesDir/(type() + ".dat")));
337 // Add headers to output data
344 void Foam::forces::writeFileHeader()
346 if (forcesFilePtr_.valid())
350 << "forces(pressure, viscous) moment(pressure, viscous)"
356 void Foam::forces::execute()
358 // Do nothing - only valid on write
362 void Foam::forces::end()
364 // Do nothing - only valid on write
368 void Foam::forces::write()
372 // Create the forces file if not already created
375 forcesMoments fm = calcForcesMoment();
377 if (Pstream::master())
379 forcesFilePtr_() << obr_.time().value() << tab << fm << endl;
383 Info<< "forces output:" << nl
384 << " forces(pressure, viscous)" << fm.first() << nl
385 << " moment(pressure, viscous)" << fm.second() << nl
393 Foam::forces::forcesMoments Foam::forces::calcForcesMoment() const
397 pressureViscous(vector::zero, vector::zero),
398 pressureViscous(vector::zero, vector::zero)
401 if (directForceDensity_)
403 const volVectorField& fD = obr_.lookupObject<volVectorField>(fDName_);
405 const fvMesh& mesh = fD.mesh();
407 const surfaceVectorField::GeometricBoundaryField& Sfb =
408 mesh.Sf().boundaryField();
410 forAllConstIter(labelHashSet, patchSet_, iter)
412 label patchi = iter.key();
414 vectorField Md = mesh.C().boundaryField()[patchi] - CofR_;
416 scalarField sA = mag(Sfb[patchi]);
418 // Normal force = surfaceUnitNormal * (surfaceNormal & forceDensity)
422 Sfb[patchi] & fD.boundaryField()[patchi]
425 fm.first().first() += sum(fN);
426 fm.second().first() += sum(Md ^ fN);
428 // Tangential force (total force minus normal fN)
429 vectorField fT = sA*fD.boundaryField()[patchi] - fN;
431 fm.first().second() += sum(fT);
432 fm.second().second() += sum(Md ^ fT);
437 const volVectorField& U = obr_.lookupObject<volVectorField>(UName_);
438 const volScalarField& p = obr_.lookupObject<volScalarField>(pName_);
440 const fvMesh& mesh = U.mesh();
442 const surfaceVectorField::GeometricBoundaryField& Sfb =
443 mesh.Sf().boundaryField();
445 tmp<volSymmTensorField> tdevRhoReff = devRhoReff();
446 const volSymmTensorField::GeometricBoundaryField& devRhoReffb
447 = tdevRhoReff().boundaryField();
449 // Scale pRef by density for incompressible simulations
450 scalar pRef = pRef_/rho(p);
452 forAllConstIter(labelHashSet, patchSet_, iter)
454 label patchi = iter.key();
456 vectorField Md = mesh.C().boundaryField()[patchi] - CofR_;
458 vectorField pf = Sfb[patchi]*(p.boundaryField()[patchi] - pRef);
460 fm.first().first() += rho(p)*sum(pf);
461 fm.second().first() += rho(p)*sum(Md ^ pf);
463 vectorField vf = Sfb[patchi] & devRhoReffb[patchi];
465 fm.first().second() += sum(vf);
466 fm.second().second() += sum(Md ^ vf);
476 // ************************************************************************* //