我在几年前做了个一拉格朗日粒子直径变化的代码。我现在已经记不起来太多了。能想起来的是:
粒子直径跟流场的湍流动能耗散率有关系
植入的是一个跟PBM有关的粒径变化方程,考虑了coalescense和破碎
代码可以编译并且测试过可以体现粒径变化
可以把下面的MomentumCloud.C:替换到OpenFOAM原来的代码然后编译。搞明白之后看看思路然后适配到你们的算法上。
MomentumCloud.C:
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2011-2021 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 "MomentumCloud.H"
#include "integrationScheme.H"
#include "interpolation.H"
#include "subCycleTime.H"
#include "InjectionModelList.H"
#include "DispersionModel.H"
#include "PatchInteractionModel.H"
#include "StochasticCollisionModel.H"
#include "SurfaceFilmModel.H"
// * * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * //
template<class CloudType>
void Foam::MomentumCloud<CloudType>::setModels()
{
dispersionModel_.reset
(
DispersionModel<MomentumCloud<CloudType>>::New
(
subModelProperties_,
*this
).ptr()
);
patchInteractionModel_.reset
(
PatchInteractionModel<MomentumCloud<CloudType>>::New
(
subModelProperties_,
*this
).ptr()
);
stochasticCollisionModel_.reset
(
StochasticCollisionModel<MomentumCloud<CloudType>>::New
(
subModelProperties_,
*this
).ptr()
);
surfaceFilmModel_.reset
(
SurfaceFilmModel<MomentumCloud<CloudType>>::New
(
subModelProperties_,
*this
).ptr()
);
UIntegrator_.reset
(
integrationScheme::New
(
"U",
solution_.integrationSchemes()
).ptr()
);
}
template<class CloudType>
template<class TrackCloudType>
void Foam::MomentumCloud<CloudType>::solve
(
TrackCloudType& cloud,
typename parcelType::trackingData& td
)
{
if (solution_.steadyState())
{
cloud.storeState();
cloud.preEvolve();
evolveCloud(cloud, td);
if (solution_.coupled())
{
cloud.relaxSources(cloud.cloudCopy());
}
}
else
{
/////////////////////////////////
Info<< "Dyfluid: Evolve function in MomentumCloud.C" << nl;
List<DynamicList<label>> PartLabelPre(U_.size());
List<DynamicList<label>> PartLabelPost(U_.size());
DynamicList<label> cellWithPartPre;
DynamicList<label> cellWithPartPost;
pNumber_.primitiveFieldRef() = 0.0;
forAllIter(typename MomentumCloud<CloudType>, *this, iter)
{
parcelType& p = iter();
pNumber_[p.cell()] += p.nParticle();
PartLabelPre[p.cell()].append(p.origId());
}
forAll(U_, cell)
{
if (PartLabelPre[cell].size() != 0)
{
cellWithPartPre.append(cell);
}
}
//Info<< cellWithPartPre << nl;
/////////////////////////////////
cloud.preEvolve();
evolveCloud(cloud, td);
if (solution_.coupled())
{
cloud.scaleSources();
}
/////////////////////////////////
forAllIter(typename MomentumCloud<CloudType>, *this, iter)
{
parcelType& p = iter();
PartLabelPost[p.cell()].append(p.origId());
}
const volScalarField& epsi = U_.mesh().lookupObject<volScalarField>("epsilon.water");
scalar C1 = 0.00481;
scalar C2 = 0.08;
scalar sigma = 0.072;
scalar rhoc = 998.0;
scalar D1 = 0.88;
scalar D2 = 9e6;
scalar muc = 1e-3;
const scalar dt = this->mesh().time().deltaTValue();
forAll(U_, cell)
{
// if there are particles in some cell
if (PartLabelPost[cell].size() != 0)
{
cellWithPartPost.append(cell);
scalar allVolume = 0.0;
scalar allnParticle = 0.0;
scalar allnParticleNew = 0.0;
scalar diamAve = 0.0;
//Info<< "Cell [" << cell << "] has " << PartLabelPost[cell].size()
// << " particles" << nl;
// loop all over particles
forAllIter(typename MomentumCloud<CloudType>, *this, iter)
{
parcelType& p = iter();
// loop particles label in this cell
forAll(PartLabelPost[cell], i)
{
// if this particle belongs to this cell
if (p.origId() == PartLabelPost[cell](i))
{
allVolume += p.nParticle()*M_PI/6.0*pow3(p.d());
allnParticle += p.nParticle();
diamAve = pow(allVolume/allnParticle*6.0/M_PI, 1.0/3.0);
//Info<< " Labels are " << PartLabelPost[cell](i)
// << ", its nParticle is " << p.nParticle()
// << ", volume is " << allVolume << nl;
}
}
}
const scalar d = diamAve;
// cell manipulation, calculate average diameter
if (d > SMALL)
{
const scalar epsilon = epsi[cell];
scalar gN =
allnParticle*C1*pow(epsilon, 1.0/3.0)/pow(d, 2.0/3.0)
*exp(-C2*sigma/(rhoc*pow(epsilon, 2.0/3.0)*pow(d, 5.0/3.0)));
scalar aSqrN =
sqr(allnParticle)*D1*pow(epsilon, 1.0/3.0)*4.0*sqrt(2.0)*pow(d, 7.0/6.0)
*exp(-D2*muc*rhoc*epsilon/sqr(sigma)*pow4(d)/16.0);
allnParticleNew = allnParticle + (gN - aSqrN)*dt;
diamAve = diamAve*pow(allnParticle/allnParticleNew, 1.0/3.0);
//Info<< "gN = " << gN << nl;
//Info<< "aSqrN = " << aSqrN << nl;
//Info<< "Particle increased by " << allnParticleNew - allnParticle << nl;
//Info<< "diamAve is " << diamAve << nl;
}
scalar newVolume = 0.0;
forAllIter(typename MomentumCloud<CloudType>, *this, iter)
{
parcelType& p = iter();
forAll(PartLabelPost[cell], i)
{
if (p.origId() == PartLabelPost[cell](i))
{
p.nParticle() = allnParticleNew/PartLabelPost[cell].size();
p.d() = diamAve;
newVolume += p.nParticle()*M_PI/6.0*pow3(p.d());
//Info<< " after brecoa, volume is " << newVolume
// << "p.nParticle is " << p.nParticle() << nl;
}
}
}
}
}
/////////////////////////////////
}
cloud.info();
cloud.postEvolve();
if (solution_.steadyState())
{
cloud.restoreState();
}
}
template<class CloudType>
void Foam::MomentumCloud<CloudType>::buildCellOccupancy()
{
if (cellOccupancyPtr_.empty())
{
cellOccupancyPtr_.reset
(
new List<DynamicList<parcelType*>>(this->mesh().nCells())
);
}
else if (cellOccupancyPtr_().size() != this->mesh().nCells())
{
// If the size of the mesh has changed, reset the
// cellOccupancy size
cellOccupancyPtr_().setSize(this->mesh().nCells());
}
List<DynamicList<parcelType*>>& cellOccupancy = cellOccupancyPtr_();
forAll(cellOccupancy, cO)
{
cellOccupancy[cO].clear();
}
forAllIter(typename MomentumCloud<CloudType>, *this, iter)
{
cellOccupancy[iter().cell()].append(&iter());
}
}
template<class CloudType>
void Foam::MomentumCloud<CloudType>::updateCellOccupancy()
{
// Only build the cellOccupancy if the pointer is set, i.e. it has
// been requested before.
if (cellOccupancyPtr_.valid())
{
buildCellOccupancy();
}
}
template<class CloudType>
template<class TrackCloudType>
void Foam::MomentumCloud<CloudType>::evolveCloud
(
TrackCloudType& cloud,
typename parcelType::trackingData& td
)
{
if (solution_.coupled())
{
cloud.resetSourceTerms();
}
if (solution_.transient())
{
label preInjectionSize = this->size();
this->surfaceFilm().inject(cloud);
// Update the cellOccupancy if the size of the cloud has changed
// during the injection.
if (preInjectionSize != this->size())
{
updateCellOccupancy();
preInjectionSize = this->size();
}
injectors_.inject(cloud, td);
// Assume that motion will update the cellOccupancy as necessary
// before it is required.
cloud.motion(cloud, td);
stochasticCollision().update(td, solution_.trackTime());
}
else
{
// this->surfaceFilm().injectSteadyState(cloud);
injectors_.injectSteadyState(cloud, td, solution_.trackTime());
CloudType::move(cloud, td, solution_.trackTime());
}
}
template<class CloudType>
void Foam::MomentumCloud<CloudType>::postEvolve()
{
Info<< endl;
if (debug)
{
this->writePositions();
}
this->dispersion().cacheFields(false);
forces_.cacheFields(false);
functions_.postEvolve();
solution_.nextIter();
if (this->db().time().writeTime())
{
outputProperties_.writeObject
(
IOstream::ASCII,
IOstream::currentVersion,
this->db().time().writeCompression(),
true
);
}
}
template<class CloudType>
void Foam::MomentumCloud<CloudType>::cloudReset(MomentumCloud<CloudType>& c)
{
CloudType::cloudReset(c);
rndGen_ = c.rndGen_;
forces_.transfer(c.forces_);
functions_.transfer(c.functions_);
injectors_.transfer(c.injectors_);
dispersionModel_.reset(c.dispersionModel_.ptr());
patchInteractionModel_.reset(c.patchInteractionModel_.ptr());
stochasticCollisionModel_.reset(c.stochasticCollisionModel_.ptr());
surfaceFilmModel_.reset(c.surfaceFilmModel_.ptr());
UIntegrator_.reset(c.UIntegrator_.ptr());
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template<class CloudType>
Foam::MomentumCloud<CloudType>::MomentumCloud
(
const word& cloudName,
const volScalarField& rho,
const volVectorField& U,
const volScalarField& mu,
const dimensionedVector& g,
const bool readFields
)
:
CloudType(cloudName, rho, U, mu, g, false),
cloudCopyPtr_(nullptr),
particleProperties_
(
IOobject
(
cloudName + "Properties",
this->mesh().time().constant(),
this->mesh(),
IOobject::MUST_READ_IF_MODIFIED,
IOobject::NO_WRITE
)
),
outputProperties_
(
IOobject
(
cloudName + "OutputProperties",
this->mesh().time().timeName(),
"uniform"/cloud::prefix/cloudName,
this->mesh(),
IOobject::READ_IF_PRESENT,
IOobject::NO_WRITE
)
),
solution_(this->mesh(), particleProperties_.subDict("solution")),
constProps_(particleProperties_),
subModelProperties_
(
particleProperties_.subOrEmptyDict("subModels", true)
),
rndGen_(0),
cellOccupancyPtr_(),
cellLengthScale_(mag(cbrt(this->mesh().V()))),
pNumber_
(
IOobject
(
"pNumbers",
this->mesh().time().timeName(),
this->mesh(),
IOobject::NO_READ,
IOobject::AUTO_WRITE
),
this->mesh(),
dimensionedScalar(dimless, 0.0)
),
rho_(rho),
U_(U),
mu_(mu),
g_(g),
pAmbient_(0.0),
forces_
(
*this,
this->mesh(),
subModelProperties_.subOrEmptyDict
(
"particleForces",
true
),
true
),
functions_
(
*this,
particleProperties_.subOrEmptyDict("cloudFunctions"),
true
),
injectors_
(
subModelProperties_.subOrEmptyDict("injectionModels"),
*this
),
dispersionModel_(nullptr),
patchInteractionModel_(nullptr),
stochasticCollisionModel_(nullptr),
surfaceFilmModel_(nullptr),
UIntegrator_(nullptr),
UTrans_
(
new volVectorField::Internal
(
IOobject
(
this->name() + ":UTrans",
this->db().time().timeName(),
this->db(),
IOobject::READ_IF_PRESENT,
IOobject::AUTO_WRITE
),
this->mesh(),
dimensionedVector(dimMass*dimVelocity, Zero)
)
),
UCoeff_
(
new volScalarField::Internal
(
IOobject
(
this->name() + ":UCoeff",
this->db().time().timeName(),
this->db(),
IOobject::READ_IF_PRESENT,
IOobject::AUTO_WRITE
),
this->mesh(),
dimensionedScalar( dimMass, 0)
)
)
{
setModels();
if (readFields)
{
parcelType::readFields(*this);
this->deleteLostParticles();
}
if (solution_.resetSourcesOnStartup())
{
resetSourceTerms();
}
}
template<class CloudType>
Foam::MomentumCloud<CloudType>::MomentumCloud
(
const word& cloudName,
const volScalarField& rho,
const volVectorField& U,
const dimensionedVector& g,
const fluidThermo& carrierThermo,
const bool readFields
)
:
MomentumCloud(cloudName, rho, U, carrierThermo.mu(), g, readFields)
{}
template<class CloudType>
Foam::MomentumCloud<CloudType>::MomentumCloud
(
MomentumCloud<CloudType>& c,
const word& name
)
:
CloudType(c, name),
cloudCopyPtr_(nullptr),
particleProperties_(c.particleProperties_),
outputProperties_(c.outputProperties_),
solution_(c.solution_),
constProps_(c.constProps_),
subModelProperties_(c.subModelProperties_),
rndGen_(c.rndGen_),
cellOccupancyPtr_(nullptr),
cellLengthScale_(c.cellLengthScale_),
pNumber_(c.pNumber_),
rho_(c.rho_),
U_(c.U_),
mu_(c.mu_),
g_(c.g_),
pAmbient_(c.pAmbient_),
forces_(c.forces_),
functions_(c.functions_),
injectors_(c.injectors_),
dispersionModel_(c.dispersionModel_->clone()),
patchInteractionModel_(c.patchInteractionModel_->clone()),
stochasticCollisionModel_(c.stochasticCollisionModel_->clone()),
surfaceFilmModel_(c.surfaceFilmModel_->clone()),
UIntegrator_(c.UIntegrator_->clone()),
UTrans_
(
new volVectorField::Internal
(
IOobject
(
this->name() + ":UTrans",
this->db().time().timeName(),
this->db(),
IOobject::NO_READ,
IOobject::NO_WRITE,
false
),
c.UTrans_()
)
),
UCoeff_
(
new volScalarField::Internal
(
IOobject
(
name + ":UCoeff",
this->db().time().timeName(),
this->db(),
IOobject::NO_READ,
IOobject::NO_WRITE,
false
),
c.UCoeff_()
)
)
{}
template<class CloudType>
Foam::MomentumCloud<CloudType>::MomentumCloud
(
const fvMesh& mesh,
const word& name,
const MomentumCloud<CloudType>& c
)
:
CloudType(mesh, name, c),
cloudCopyPtr_(nullptr),
particleProperties_
(
IOobject
(
name + "Properties",
mesh.time().constant(),
mesh,
IOobject::NO_READ,
IOobject::NO_WRITE,
false
)
),
outputProperties_
(
IOobject
(
name + "OutputProperties",
this->mesh().time().timeName(),
"uniform"/cloud::prefix/name,
this->mesh(),
IOobject::NO_READ,
IOobject::NO_WRITE,
false
)
),
solution_(mesh),
constProps_(),
subModelProperties_(dictionary::null),
rndGen_(0),
cellOccupancyPtr_(nullptr),
cellLengthScale_(c.cellLengthScale_),
pNumber_(c.pNumber_),
rho_(c.rho_),
U_(c.U_),
mu_(c.mu_),
g_(c.g_),
pAmbient_(c.pAmbient_),
forces_(*this, mesh),
functions_(*this),
injectors_(*this),
dispersionModel_(nullptr),
patchInteractionModel_(nullptr),
stochasticCollisionModel_(nullptr),
surfaceFilmModel_(nullptr),
UIntegrator_(nullptr),
UTrans_(nullptr),
UCoeff_(nullptr)
{}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
template<class CloudType>
Foam::MomentumCloud<CloudType>::~MomentumCloud()
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
template<class CloudType>
void Foam::MomentumCloud<CloudType>::setParcelThermoProperties
(
parcelType& parcel,
const scalar lagrangianDt
)
{
parcel.rho() = constProps_.rho0();
}
template<class CloudType>
void Foam::MomentumCloud<CloudType>::checkParcelProperties
(
parcelType& parcel,
const scalar lagrangianDt,
const bool fullyDescribed
)
{
const scalar carrierDt = this->mesh().time().deltaTValue();
parcel.stepFraction() = (carrierDt - lagrangianDt)/carrierDt;
if (parcel.typeId() == -1)
{
parcel.typeId() = constProps_.parcelTypeId();
}
}
template<class CloudType>
void Foam::MomentumCloud<CloudType>::storeState()
{
cloudCopyPtr_.reset
(
static_cast<MomentumCloud<CloudType>*>
(
clone(this->name() + "Copy").ptr()
)
);
}
template<class CloudType>
void Foam::MomentumCloud<CloudType>::restoreState()
{
cloudReset(cloudCopyPtr_());
cloudCopyPtr_.clear();
}
template<class CloudType>
void Foam::MomentumCloud<CloudType>::resetSourceTerms()
{
UTransRef().field() = Zero;
UCoeffRef().field() = 0.0;
}
template<class CloudType>
template<class Type>
void Foam::MomentumCloud<CloudType>::relax
(
DimensionedField<Type, volMesh>& field,
const DimensionedField<Type, volMesh>& field0,
const word& name
) const
{
const scalar coeff = solution_.relaxCoeff(name);
field = field0 + coeff*(field - field0);
}
template<class CloudType>
template<class Type>
void Foam::MomentumCloud<CloudType>::scale
(
DimensionedField<Type, volMesh>& field,
const word& name
) const
{
const scalar coeff = solution_.relaxCoeff(name);
field *= coeff;
}
template<class CloudType>
void Foam::MomentumCloud<CloudType>::relaxSources
(
const MomentumCloud<CloudType>& cloudOldTime
)
{
this->relax(UTrans_(), cloudOldTime.UTrans_(), "U");
this->relax(UCoeff_(), cloudOldTime.UCoeff_(), "U");
}
template<class CloudType>
void Foam::MomentumCloud<CloudType>::scaleSources()
{
this->scale(UTrans_(), "U");
this->scale(UCoeff_(), "U");
}
template<class CloudType>
void Foam::MomentumCloud<CloudType>::preEvolve()
{
// force calculation of mesh dimensions - needed for parallel runs
// with topology change due to lazy evaluation of valid mesh dimensions
label nGeometricD = this->mesh().nGeometricD();
Info<< "\nSolving " << nGeometricD << "-D cloud " << this->name() << endl;
this->dispersion().cacheFields(true);
forces_.cacheFields(true);
updateCellOccupancy();
pAmbient_ = constProps_.dict().template
lookupOrDefault<scalar>("pAmbient", pAmbient_);
functions_.preEvolve();
}
template<class CloudType>
void Foam::MomentumCloud<CloudType>::evolve()
{
if (solution_.canEvolve())
{
typename parcelType::trackingData td(*this);
solve(*this, td);
}
}
template<class CloudType>
template<class TrackCloudType>
void Foam::MomentumCloud<CloudType>::motion
(
TrackCloudType& cloud,
typename parcelType::trackingData& td
)
{
CloudType::move(cloud, td, solution_.trackTime());
updateCellOccupancy();
}
template<class CloudType>
void Foam::MomentumCloud<CloudType>::patchData
(
const parcelType& p,
const polyPatch& pp,
vector& nw,
vector& Up
) const
{
p.patchData(nw, Up);
Up /= p.mesh().time().deltaTValue();
// If this is a wall patch, then there may be a non-zero tangential velocity
// component; the lid velocity in a lid-driven cavity case, for example. We
// want the particle to interact with this velocity, so we look it up in the
// velocity field and use it to set the wall-tangential component.
if (!this->mesh().moving() && isA<wallPolyPatch>(pp))
{
const label patchi = pp.index();
const label patchFacei = pp.whichFace(p.face());
// We only want to use the boundary condition value only if it is set
// by the boundary condition. If the boundary values are extrapolated
// (e.g., slip conditions) then they represent the motion of the fluid
// just inside the domain rather than that of the wall itself.
if (U_.boundaryField()[patchi].fixesValue())
{
const vector Uw1 = U_.boundaryField()[patchi][patchFacei];
const vector& Uw0 =
U_.oldTime().boundaryField()[patchi][patchFacei];
const scalar f = p.currentTimeFraction();
const vector Uw = Uw0 + f*(Uw1 - Uw0);
const tensor nnw = nw*nw;
Up = (nnw & Up) + Uw - (nnw & Uw);
}
}
}
template<class CloudType>
void Foam::MomentumCloud<CloudType>::updateMesh()
{
updateCellOccupancy();
injectors_.updateMesh();
cellLengthScale_ = mag(cbrt(this->mesh().V()));
}
template<class CloudType>
void Foam::MomentumCloud<CloudType>::autoMap(const mapPolyMesh& mapper)
{
Cloud<parcelType>::autoMap(mapper);
updateMesh();
}
template<class CloudType>
void Foam::MomentumCloud<CloudType>::info()
{
vector linearMomentum = linearMomentumOfSystem();
reduce(linearMomentum, sumOp<vector>());
scalar linearKineticEnergy = linearKineticEnergyOfSystem();
reduce(linearKineticEnergy, sumOp<scalar>());
Info<< "Cloud: " << this->name() << nl
<< " Current number of parcels = "
<< returnReduce(this->size(), sumOp<label>()) << nl
<< " Current mass in system = "
<< returnReduce(massInSystem(), sumOp<scalar>()) << nl
<< " Linear momentum = "
<< linearMomentum << nl
<< " |Linear momentum| = "
<< mag(linearMomentum) << nl
<< " Linear kinetic energy = "
<< linearKineticEnergy << nl;
injectors_.info(Info);
this->surfaceFilm().info(Info);
this->patchInteraction().info(Info);
}
// ************************************************************************* //