/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2021 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see .
\*---------------------------------------------------------------------------*/
#include "electrostaticDepositionFvPatchScalarField.H"
#include "addToRunTimeSelectionTable.H"
#include "fvPatchFieldMapper.H"
#include "volFields.H"
#include "surfaceFields.H"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::electrostaticDepositionFvPatchScalarField&
Foam::electrostaticDepositionFvPatchScalarField::eVPatch
(
const label patchi
) const
{
const auto& eV =
db().lookupObject(this->internalField().name());
const volScalarField::Boundary& bf = eV.boundaryField();
const auto& eVpf =
refCast(bf[patchi]);
return const_cast(eVpf);
}
void Foam::electrostaticDepositionFvPatchScalarField::setMaster() const
{
if (master_ != -1)
{
return;
}
const auto& eV =
db().lookupObject(this->internalField().name());
const volScalarField::Boundary& bf = eV.boundaryField();
label master = -1;
forAll(bf, patchi)
{
if (isA(bf[patchi]))
{
electrostaticDepositionFvPatchScalarField& eVpf = eVPatch(patchi);
if (master == -1)
{
master = patchi;
}
eVpf.master() = master;
}
}
}
void Foam::electrostaticDepositionFvPatchScalarField::round
(
scalarField& fld,
const scalar dcml
) const
{
for (auto& f : fld)
{
f = std::round(f*dcml)/dcml;
}
}
void Foam::electrostaticDepositionFvPatchScalarField::writeFilmFields() const
{
const auto& eV =
db().lookupObject(this->internalField().name());
const volScalarField::Boundary& bf = eV.boundaryField();
const fvMesh& mesh = eV.mesh();
volScalarField h
(
IOobject
(
IOobject::scopedName("electrostaticDeposition", "h"),
mesh.time().timeName(),
mesh,
IOobject::NO_READ,
IOobject::NO_WRITE,
IOobject::NO_REGISTER
),
mesh,
dimensionedScalar(dimLength)
);
forAll(bf, patchi)
{
if (isA(bf[patchi]))
{
electrostaticDepositionFvPatchScalarField& eVpf = eVPatch(patchi);
auto& hp = h.boundaryFieldRef()[patchi];
hp = eVpf.h();
}
}
h.write();
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::electrostaticDepositionFvPatchScalarField::
electrostaticDepositionFvPatchScalarField
(
const fvPatch& p,
const DimensionedField& iF
)
:
fixedValueFvPatchScalarField(p, iF),
h_(p.size(), 0),
qcum_(p.size(), 0),
Vfilm_(p.size(), 0),
Ceffptr_(nullptr),
rptr_(nullptr),
jMin_(0),
qMin_(0),
Rbody_(0),
Vi_(0),
Vanode_(GREAT),
phasesDict_(),
phaseNames_(),
phases_(),
sigmas_(),
sigma_(sqr(dimCurrent)*pow3(dimTime)/(dimMass*pow3(dimLength)), scalar(1)),
timei_(-1),
master_(-1)
{}
Foam::electrostaticDepositionFvPatchScalarField::
electrostaticDepositionFvPatchScalarField
(
const fvPatch& p,
const DimensionedField& iF,
const dictionary& dict
)
:
fixedValueFvPatchScalarField(p, iF, dict, IOobjectOption::NO_READ),
h_("h", dict, p.size()),
qcum_("qCumulative", dict, p.size(), IOobjectOption::LAZY_READ),
Vfilm_("Vfilm", dict, p.size(), IOobjectOption::LAZY_READ),
Ceffptr_
(
PatchFunction1::New(p.patch(), "CoulombicEfficiency", dict)
),
rptr_(PatchFunction1::New(p.patch(), "resistivity", dict)),
jMin_(dict.getCheckOrDefault("jMin", 0, scalarMinMax::ge(0))),
qMin_(dict.getCheckOrDefault("qMin", 0, scalarMinMax::ge(0))),
Rbody_(dict.getCheckOrDefault("Rbody", 0, scalarMinMax::ge(0))),
Vi_(dict.getOrDefault("Vi", 0)),
Vanode_(dict.getOrDefault("Vanode", GREAT)),
phasesDict_(dict.subOrEmptyDict("phases")),
phaseNames_(),
phases_(),
sigmas_(),
sigma_
(
dimensionedScalar
(
sqr(dimCurrent)*pow3(dimTime)/(dimMass*pow3(dimLength)),
dict.getCheckOrDefault
(
"sigma",
scalar(1),
scalarMinMax::ge(SMALL)
)
)
),
timei_(-1),
master_(-1)
{
if (!this->readValueEntry(dict))
{
this->extrapolateInternal(); // Zero-gradient patch values
}
// If flow is multiphase
if (!phasesDict_.empty())
{
phaseNames_.setSize(phasesDict_.size());
phases_.setSize(phasesDict_.size());
sigmas_.setSize(phasesDict_.size());
label phasei = 0;
for (const entry& dEntry : phasesDict_)
{
const word& key = dEntry.keyword();
if (!dEntry.isDict())
{
FatalIOErrorInFunction(phasesDict_)
<< "Entry " << key << " is not a dictionary" << nl
<< exit(FatalIOError);
}
const dictionary& subDict = dEntry.dict();
phaseNames_[phasei] = key;
sigmas_.set
(
phasei,
new dimensionedScalar
(
sqr(dimCurrent)*pow3(dimTime)/(dimMass*pow3(dimLength)),
subDict.getCheck
(
"sigma",
scalarMinMax::ge(SMALL)
)
)
);
++phasei;
}
forAll(phaseNames_, i)
{
phases_.set
(
i,
db().getObjectPtr(phaseNames_[i])
);
}
}
}
Foam::electrostaticDepositionFvPatchScalarField::
electrostaticDepositionFvPatchScalarField
(
const electrostaticDepositionFvPatchScalarField& ptf,
const fvPatch& p,
const DimensionedField& iF,
const fvPatchFieldMapper& mapper
)
:
fixedValueFvPatchScalarField(ptf, p, iF, mapper),
h_(ptf.h_, mapper),
qcum_(ptf.qcum_, mapper),
Vfilm_(ptf.Vfilm_, mapper),
Ceffptr_(ptf.Ceffptr_.clone(p.patch())),
rptr_(ptf.rptr_.clone(p.patch())),
jMin_(ptf.jMin_),
qMin_(ptf.qMin_),
Rbody_(ptf.Rbody_),
Vi_(ptf.Vi_),
Vanode_(ptf.Vanode_),
phasesDict_(ptf.phasesDict_),
phaseNames_(ptf.phaseNames_),
phases_(ptf.phases_),
sigmas_(),
sigma_(ptf.sigma_),
timei_(ptf.timei_),
master_(-1)
{}
Foam::electrostaticDepositionFvPatchScalarField::
electrostaticDepositionFvPatchScalarField
(
const electrostaticDepositionFvPatchScalarField& ptf
)
:
fixedValueFvPatchScalarField(ptf),
h_(ptf.h_),
qcum_(ptf.qcum_),
Vfilm_(ptf.Vfilm_),
Ceffptr_(ptf.Ceffptr_.clone(patch().patch())),
rptr_(ptf.rptr_.clone(patch().patch())),
jMin_(ptf.jMin_),
qMin_(ptf.qMin_),
Rbody_(ptf.Rbody_),
Vi_(ptf.Vi_),
Vanode_(ptf.Vanode_),
phasesDict_(ptf.phasesDict_),
phaseNames_(ptf.phaseNames_),
phases_(ptf.phases_),
sigmas_(),
sigma_(ptf.sigma_),
timei_(ptf.timei_),
master_(-1)
{}
Foam::electrostaticDepositionFvPatchScalarField::
electrostaticDepositionFvPatchScalarField
(
const electrostaticDepositionFvPatchScalarField& ptf,
const DimensionedField& iF
)
:
fixedValueFvPatchScalarField(ptf, iF),
h_(ptf.h_),
qcum_(ptf.qcum_),
Vfilm_(ptf.Vfilm_),
Ceffptr_(ptf.Ceffptr_.clone(patch().patch())),
rptr_(ptf.rptr_.clone(patch().patch())),
jMin_(ptf.jMin_),
qMin_(ptf.qMin_),
Rbody_(ptf.Rbody_),
Vi_(ptf.Vi_),
Vanode_(ptf.Vanode_),
phasesDict_(ptf.phasesDict_),
phaseNames_(ptf.phaseNames_),
phases_(ptf.phases_),
sigmas_(),
sigma_(ptf.sigma_),
timei_(ptf.timei_),
master_(-1)
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
void Foam::electrostaticDepositionFvPatchScalarField::autoMap
(
const fvPatchFieldMapper& m
)
{
fixedValueFvPatchScalarField::autoMap(m);
h_.autoMap(m);
qcum_.autoMap(m);
Vfilm_.autoMap(m);
if (Ceffptr_)
{
Ceffptr_->autoMap(m);
}
if (rptr_)
{
rptr_->autoMap(m);
}
}
void Foam::electrostaticDepositionFvPatchScalarField::rmap
(
const fvPatchScalarField& ptf,
const labelList& addr
)
{
fixedValueFvPatchScalarField::rmap(ptf, addr);
const auto& tiptf =
refCast(ptf);
h_.rmap(tiptf.h_, addr);
qcum_.rmap(tiptf.qcum_, addr);
Vfilm_.rmap(tiptf.Vfilm_, addr);
if (Ceffptr_)
{
Ceffptr_->rmap(tiptf.Ceffptr_(), addr);
}
if (rptr_)
{
rptr_->rmap(tiptf.rptr_(), addr);
}
}
Foam::tmp
Foam::electrostaticDepositionFvPatchScalarField::sigma() const
{
const label patchi = patch().index();
if (phases_.size())
{
tmp tsigma =
phases_[0].boundaryField()[patchi]*sigmas_[0].value();
for (label i = 1; i < phases_.size(); ++i)
{
tsigma.ref() +=
phases_[i].boundaryField()[patchi]*sigmas_[i].value();
}
return tsigma;
}
return tmp::New(patch().size(), sigma_.value());
}
void Foam::electrostaticDepositionFvPatchScalarField::updateCoeffs()
{
if (updated())
{
return;
}
if (timei_ == db().time().timeIndex())
{
return;
}
const scalar t = db().time().timeOutputValue();
const scalar dt = db().time().deltaTValue();
const label patchi = patch().index();
const auto& eV =
db().lookupObject(this->internalField().name());
// Current density on film interface
tmp tjnp = -this->sigma()*eV.boundaryField()[patchi].snGrad();
scalarField& jnp = tjnp.ref();
jnp = max(jnp, scalar(0)); // experimental - do not allow any negative jnp
// experimental - avoid micro/nano currents/volts
// to reduce snowballing effects of lateral gradients on the patch
round(jnp);
// Calculate film-thickness finite increments
tmp tCoulombicEfficiency = Ceffptr_->value(t);
tmp tdh = tCoulombicEfficiency*(jnp - jMin_)*dt;
scalarField& dh = tdh.ref();
// Do not allow any depletion or abrasion of deposition
dh = max(dh, scalar(0));
// Do not allow any deposition when accumulative specific
// charge is less than minimum accumulative specific charge
qcum_ += jnp*dt;
forAll(dh, i)
{
if (qcum_[i] < qMin_)
{
dh[i] = 0;
}
}
// Add finite increments of film thickness to total film thickness
h_ += dh;
// Calculate incremental electric potential due to film resistance
tmp tresistivity = rptr_->value(t);
tmp tRfilm = tresistivity*tdh;
tmp tdV = jnp*tRfilm;
Vfilm_ += tdV;
Vfilm_ = min(Vfilm_, Vanode_);
// Calculate electric potential due to body resistance
tmp tVbody = tjnp*Rbody_;
// Add all electric potential contributions
operator==(min(Vi_ + Vfilm_ + tVbody, Vanode_));
fixedValueFvPatchScalarField::updateCoeffs();
timei_ = db().time().timeIndex();
{
auto limits = gMinMax(h_);
auto avg = gAverage(h_);
if (UPstream::master())
{
Info<< " patch: " << patch().name()
<< ", h: min = " << limits.min()
<< ", max = " << limits.max()
<< ", average = " << avg << nl
<< endl;
}
}
// Write here to avoid any upset to redistributePar-decompose
if (db().time().writeTime())
{
// Write film thickness fields as patch fields of a volScalarField
setMaster();
if (patch().index() == master_)
{
writeFilmFields();
}
}
}
void Foam::electrostaticDepositionFvPatchScalarField::write(Ostream& os) const
{
fvPatchField::write(os);
h_.writeEntry("h", os);
if (Ceffptr_)
{
Ceffptr_->writeData(os);
}
if (rptr_)
{
rptr_->writeData(os);
}
if (!phasesDict_.empty())
{
phasesDict_.writeEntry(phasesDict_.dictName(), os);
}
else
{
sigma_.writeEntry("sigma", os);
}
os.writeEntryIfDifferent("jMin", 0, jMin_);
os.writeEntryIfDifferent("qMin", 0, qMin_);
os.writeEntryIfDifferent("Rbody", 0, Rbody_);
os.writeEntryIfDifferent("Vi", 0, Vi_);
os.writeEntryIfDifferent("Vanode", GREAT, Vanode_);
qcum_.writeEntry("qCumulative", os);
Vfilm_.writeEntry("Vfilm", os);
fvPatchField::writeValueEntry(os);
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
makePatchTypeField
(
fvPatchScalarField,
electrostaticDepositionFvPatchScalarField
);
}
// ************************************************************************* //