diff --git a/src/functionObjects/lagrangian/particles/particles.H b/src/functionObjects/lagrangian/particles/particles.H index 606b55de28..42e32faf51 100644 --- a/src/functionObjects/lagrangian/particles/particles.H +++ b/src/functionObjects/lagrangian/particles/particles.H @@ -25,8 +25,8 @@ Class Foam::functionObjects::particles Description - This functionObject tracks a uncoupled kinematic particle cloud in the - specified velocity field of an incompressible flow (laminar, RANS or LES). + This functionObject tracks a particle cloud in the specified velocity field + of an incompressible flow (laminar, RANS or LES). It may be used in conjunction with any transient single-phase incompressible flow solver such as pisoFoam or pimpleFoam and tracks the particles or diff --git a/src/lagrangian/parcel/fvModels/clouds.C b/src/lagrangian/parcel/fvModels/clouds.C index b595cd64b8..0a007725c5 100644 --- a/src/lagrangian/parcel/fvModels/clouds.C +++ b/src/lagrangian/parcel/fvModels/clouds.C @@ -57,16 +57,87 @@ Foam::fv::clouds::clouds ) : fvModel(sourceName, modelType, dict, mesh), - carrierThermo_ + g_ ( - mesh.lookupObject(physicalProperties::typeName) + IOobject + ( + "g", + mesh.time().constant(), + mesh, + IOobject::READ_IF_PRESENT, + IOobject::NO_WRITE + ), + dimensionedVector(dimAcceleration, Zero) ), - clouds_ + carrierHasThermo_ ( - mesh.lookupObject("rho"), - mesh.lookupObject("U"), - mesh.lookupObject("g"), - carrierThermo_ + mesh.foundObject(physicalProperties::typeName) + ), + tCarrierThermo_ + ( + carrierHasThermo_ + ? tmpNrc + ( + mesh.lookupObject(physicalProperties::typeName) + ) + : tmpNrc(nullptr) + ), + tCarrierViscosity_ + ( + carrierHasThermo_ + ? tmpNrc(nullptr) + : tmpNrc + ( + mesh.lookupObject(physicalProperties::typeName) + ) + ), + tRho_ + ( + carrierHasThermo_ + ? tmp(nullptr) + : tmp + ( + new volScalarField + ( + IOobject + ( + "rho", + mesh.time().timeName(), + mesh + ), + mesh, + dimensionedScalar("rho", dimDensity, tCarrierViscosity_()) + ) + ) + ), + tMu_ + ( + carrierHasThermo_ + ? tmp(nullptr) + : tmp + ( + new volScalarField("mu", tRho_()*tCarrierViscosity_().nu()) + ) + ), + rhoName_(dict.lookupOrDefault("rho", "rho")), + UName_(dict.lookupOrDefault("U", "U")), + cloudsPtr_ + ( + carrierHasThermo_ + ? new parcelCloudList + ( + mesh.lookupObject(rhoName_), + mesh.lookupObject(UName_), + g_, + tCarrierThermo_() + ) + : new parcelCloudList + ( + tRho_(), + mesh.lookupObject(UName_), + tMu_(), + g_ + ) ), curTimeIndex_(-1) {} @@ -76,20 +147,29 @@ Foam::fv::clouds::clouds Foam::wordList Foam::fv::clouds::addSupFields() const { - wordList fieldNames({"rho", "U", carrierThermo_.he().name()}); + wordList fieldNames(1, UName_); - if (isA(carrierThermo_)) + if (carrierHasThermo_) { - const basicSpecieMixture& composition = - refCast(carrierThermo_); + const fluidThermo& carrierThermo = tCarrierThermo_(); - const PtrList& Y = composition.Y(); + fieldNames.append(rhoName_); - forAll(Y, i) + fieldNames.append(carrierThermo.he().name()); + + if (isA(carrierThermo)) { - if (composition.solve(i)) + const basicSpecieMixture& composition = + refCast(carrierThermo); + + const PtrList& Y = composition.Y(); + + forAll(Y, i) { - fieldNames.append(Y[i].name()); + if (composition.solve(i)) + { + fieldNames.append(Y[i].name()); + } } } } @@ -105,7 +185,12 @@ void Foam::fv::clouds::correct() return; } - clouds_.evolve(); + if (!carrierHasThermo_) + { + tMu_.ref() = tRho_()*tCarrierViscosity_().nu(); + } + + cloudsPtr_().evolve(); curTimeIndex_ = mesh().time().timeIndex(); } @@ -122,16 +207,18 @@ void Foam::fv::clouds::addSup Info<< type() << ": applying source to " << eqn.psi().name() << endl; } - if (fieldName == "rho") - { - eqn += clouds_.Srho(eqn.psi()); - } - else + if (!carrierHasThermo_) { FatalErrorInFunction - << "Support for field " << fieldName << " is not implemented" + << "Applying source to compressible equation when carrier thermo " + << "is not available" << exit(FatalError); } + + if (fieldName == rhoName_) + { + eqn += cloudsPtr_().Srho(eqn.psi()); + } } @@ -147,35 +234,65 @@ void Foam::fv::clouds::addSup Info<< type() << ": applying source to " << eqn.psi().name() << endl; } - if (fieldName == "rho") + if (!carrierHasThermo_) { - eqn += clouds_.Srho(eqn.psi()); + FatalErrorInFunction + << "Applying source to compressible equation when carrier thermo " + << "is not available" + << exit(FatalError); } - else if (fieldName == carrierThermo_.he().name()) + + const fluidThermo& carrierThermo = tCarrierThermo_(); + + if (fieldName == rhoName_) { - eqn += clouds_.Sh(eqn.psi()); + eqn += cloudsPtr_().Srho(eqn.psi()); + } + else if (fieldName == carrierThermo.he().name()) + { + eqn += cloudsPtr_().Sh(eqn.psi()); } else if ( - isA(carrierThermo_) - && refCast(carrierThermo_).contains + isA(carrierThermo) + && refCast(carrierThermo).contains ( eqn.psi().name() ) ) { - eqn += clouds_.SYi + eqn += cloudsPtr_().SYi ( - refCast(carrierThermo_).index(eqn.psi()), + refCast(carrierThermo).index(eqn.psi()), eqn.psi() ); } - else +} + + +void Foam::fv::clouds::addSup +( + fvMatrix& eqn, + const word& fieldName +) const +{ + if (debug) + { + Info<< type() << ": applying source to " << eqn.psi().name() << endl; + } + + if (carrierHasThermo_) { FatalErrorInFunction - << "Support for field " << fieldName << " is not implemented" + << "Applying source to incompressible equation when carrier thermo " + << "is available" << exit(FatalError); } + + if (fieldName == UName_) + { + eqn += cloudsPtr_().SU(eqn.psi())/tRho_(); + } } @@ -191,23 +308,25 @@ void Foam::fv::clouds::addSup Info<< type() << ": applying source to " << eqn.psi().name() << endl; } - if (fieldName == "U") - { - eqn += clouds_.SU(eqn.psi()); - } - else + if (!carrierHasThermo_) { FatalErrorInFunction - << "Support for field " << fieldName << " is not implemented" + << "Applying source to compressible equation when carrier thermo " + << "is not available" << exit(FatalError); } + + if (fieldName == UName_) + { + eqn += cloudsPtr_().SU(eqn.psi()); + } } void Foam::fv::clouds::preUpdateMesh() { // Store the particle positions - clouds_.storeGlobalPositions(); + cloudsPtr_().storeGlobalPositions(); } diff --git a/src/lagrangian/parcel/fvModels/clouds.H b/src/lagrangian/parcel/fvModels/clouds.H index b72ab32fb2..b17c672783 100644 --- a/src/lagrangian/parcel/fvModels/clouds.H +++ b/src/lagrangian/parcel/fvModels/clouds.H @@ -25,17 +25,46 @@ Class Foam::fv::clouds Description - Lagrangian clouds fvModel + This fvModel adds any number of Lagrangian clouds to any single-phase + solver. The particles are tracked through, and exchange sources with, the + Eulerian flow field. + + As well as the fvModel controls, properties must be specified for each + cloud. For a single cloud, these should be provided in the + constant/cloudProperties file. For multiple clouds, the list of cloud names + must first be provided in the constant/clouds file. Then, each named cloud + has its properties specified in its constant/Properties file. + + The application of sources to the Eulerian fields is controlled by the + solution/coupled switch in each cloud's properties file. If set to "true" + then the Eulerian phase will have forces, and heat and mass sources applied + to it by the Lagrangian phase. If set to "false" then these will be omitted, + and the Lagrangian phase will not affect the Eulerian phase. + + If this model is used with an incompressible solver, then the density of + the Eulerian phase must be specified in the constant/phaseProperties + dictionary. + + Gravity will be read from the constant/g file if present, otherwise it will + default to zero. Usage Example usage: \verbatim clouds { - type clouds; + libs ("liblagrangianParcel.so"); + type clouds; } \endverbatim + \table + Property | Description | Required | Default value + type | Type name: clouds | yes | + rho | Name of the density field | no | rho + U | Name of the velocity field | no | U + \endtable + SourceFiles clouds.C @@ -46,6 +75,7 @@ SourceFiles #include "fvModel.H" #include "fluidThermo.H" +#include "viscosityModel.H" #include "uniformDimensionedFields.H" #include "parcelCloudList.H" @@ -66,11 +96,35 @@ class clouds { // Private Data - //- Reference to the carrier phase thermo - const fluidThermo& carrierThermo_; + //- Optional acceleration due to gravity + const uniformDimensionedVectorField g_; + + //- Flag to indicate whether a thermo model is available for the + // carrier + const bool carrierHasThermo_; + + //- Reference to the carrier phase thermo. Valid only if the carrier + // has thermo. + const tmpNrc tCarrierThermo_; + + //- Reference to the carrier viscosity model. Valid only if the carrier + // does not have thermo. + const tmpNrc tCarrierViscosity_; + + //- Density field. Valid only if the carrier does not have thermo. + const tmp tRho_; + + //- Viscosity field. Valid only if the carrier does not have thermo. + tmp tMu_; + + //- Name of the density field + const word rhoName_; + + //- Name of the velocity field + const word UName_; //- The Lagrangian cloud list - mutable parcelCloudList clouds_; + mutable autoPtr cloudsPtr_; //- Current time index (used for updating) mutable label curTimeIndex_; @@ -127,7 +181,7 @@ public: const word& fieldName ) const; - //- Add source to pressure or enthalpy equation + //- Add source to enthalpy or species equation virtual void addSup ( const volScalarField& rho, @@ -135,7 +189,14 @@ public: const word& fieldName ) const; - //- Add source to momentum equation + //- Add source to incompressible momentum equation + virtual void addSup + ( + fvMatrix& eqn, + const word& fieldName + ) const; + + //- Add source to compressible momentum equation virtual void addSup ( const volScalarField& rho,