/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2015-2023 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 .
\*---------------------------------------------------------------------------*/
#include "ThermalPhaseChangePhaseSystem.H"
#include "heatTransferModel.H"
#include "alphatPhaseChangeWallFunctionBase.H"
#include "fvcVolumeIntegrate.H"
#include "fvmSup.H"
#include "rhoFluidMulticomponentThermo.H"
#include "wallBoilingHeatTransfer.H"
// * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * //
template
void Foam::ThermalPhaseChangePhaseSystem::addDmdts
(
PtrList& dmdts
) const
{
forAllConstIter(phaseSystem::dmdtfTable, dmdtfs_, dmdtfIter)
{
const phaseInterface interface(*this, dmdtfIter.key());
addField(interface.phase1(), "dmdt", *dmdtfIter(), dmdts);
addField(interface.phase2(), "dmdt", - *dmdtfIter(), dmdts);
}
forAllConstIter(phaseSystem::dmdtfTable, nDmdtfs_, nDmdtfIter)
{
const phaseInterface interface(*this, nDmdtfIter.key());
addField(interface.phase1(), "dmdt", *nDmdtfIter(), dmdts);
addField(interface.phase2(), "dmdt", - *nDmdtfIter(), dmdts);
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template
Foam::ThermalPhaseChangePhaseSystem::
ThermalPhaseChangePhaseSystem
(
const fvMesh& mesh
)
:
BasePhaseSystem(mesh),
volatile_(this->template lookupOrDefault("volatile", "none")),
dmdt0s_(this->phases().size()),
pressureImplicit_
(
this->template lookupOrDefault("pressureImplicit", true)
)
{
this->generateInterfacialModels(saturationModels_);
// Check that models have been specified in the correct combinations
forAllConstIter
(
saturationModelTable,
saturationModels_,
saturationModelIter
)
{
const phaseInterface& interface = saturationModelIter()->interface();
const phaseModel& phase1 = interface.phase1();
const phaseModel& phase2 = interface.phase2();
this->template validateMassTransfer
<
interfaceSaturationTemperatureModel
>(interface);
if
(
!this->heatTransferModels_.found(interface)
|| !this->heatTransferModels_[interface]->haveModelInThe(phase1)
|| !this->heatTransferModels_[interface]->haveModelInThe(phase2)
)
{
FatalErrorInFunction
<< "A heat transfer model for both sides of the "
<< interface.name() << " interface is not specified. This is "
<< "required by the corresponding saturation model"
<< exit(FatalError);
}
}
// Generate interfacial mass transfer fields, initially assumed to be zero
forAllConstIter
(
saturationModelTable,
saturationModels_,
saturationModelIter
)
{
const phaseInterface& interface = saturationModelIter()->interface();
dmdtfs_.insert
(
interface,
new volScalarField
(
IOobject
(
IOobject::groupName
(
"thermalPhaseChange:dmdtf",
interface.name()
),
this->mesh().time().name(),
this->mesh(),
IOobject::READ_IF_PRESENT,
IOobject::AUTO_WRITE
),
this->mesh(),
dimensionedScalar(dimDensity/dimTime, 0)
)
);
d2mdtdpfs_.insert
(
interface,
new volScalarField
(
IOobject
(
IOobject::groupName
(
"thermalPhaseChange:d2mdtdpf",
interface.name()
),
this->mesh().time().name(),
this->mesh(),
IOobject::NO_READ,
IOobject::AUTO_WRITE
),
this->mesh(),
dimensionedScalar((dimDensity/dimTime)/dimPressure, 0)
)
);
Tfs_.insert
(
interface,
new volScalarField
(
IOobject
(
IOobject::groupName
(
"thermalPhaseChange:Tf",
interface.name()
),
this->mesh().time().name(),
this->mesh(),
IOobject::READ_IF_PRESENT,
IOobject::AUTO_WRITE
),
(
interface.phase1().fluidThermo().T()
+ interface.phase2().fluidThermo().T()
)/2
)
);
Tsats_.insert
(
interface,
new volScalarField
(
IOobject
(
IOobject::groupName
(
"thermalPhaseChange:Tsat",
interface.name()
),
this->mesh().time().name(),
this->mesh(),
IOobject::READ_IF_PRESENT,
IOobject::AUTO_WRITE
),
saturationModels_[interface]->Tsat
(
interface.phase1().fluidThermo().p()
)
)
);
nDmdtfs_.insert
(
interface,
new volScalarField
(
IOobject
(
IOobject::groupName
(
"thermalPhaseChange:nucleation:dmdtf",
interface.name()
),
this->mesh().time().name(),
this->mesh(),
IOobject::READ_IF_PRESENT,
IOobject::AUTO_WRITE
),
this->mesh(),
dimensionedScalar(dimDensity/dimTime, 0)
)
);
}
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
template
Foam::ThermalPhaseChangePhaseSystem::
~ThermalPhaseChangePhaseSystem()
{}
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
template
const Foam::interfaceSaturationTemperatureModel&
Foam::ThermalPhaseChangePhaseSystem::saturation
(
const phaseInterfaceKey& key
) const
{
return saturationModels_[key];
}
template
Foam::tmp
Foam::ThermalPhaseChangePhaseSystem::dmdtf
(
const phaseInterfaceKey& key
) const
{
tmp tDmdtf = BasePhaseSystem::dmdtf(key);
if (dmdtfs_.found(key))
{
tDmdtf.ref() += *dmdtfs_[key];
}
if (nDmdtfs_.found(key))
{
tDmdtf.ref() += *nDmdtfs_[key];
}
return tDmdtf;
}
template
Foam::PtrList
Foam::ThermalPhaseChangePhaseSystem::dmdts() const
{
PtrList dmdts(BasePhaseSystem::dmdts());
addDmdts(dmdts);
return dmdts;
}
template
Foam::PtrList
Foam::ThermalPhaseChangePhaseSystem::d2mdtdps() const
{
PtrList d2mdtdps(BasePhaseSystem::d2mdtdps());
forAllConstIter(phaseSystem::dmdtfTable, d2mdtdpfs_, d2mdtdpfIter)
{
const phaseInterface interface(*this, d2mdtdpfIter.key());
addField(interface.phase1(), "d2mdtdp", *d2mdtdpfIter(), d2mdtdps);
addField(interface.phase2(), "d2mdtdp", - *d2mdtdpfIter(), d2mdtdps);
}
return d2mdtdps;
}
template
Foam::autoPtr
Foam::ThermalPhaseChangePhaseSystem::
momentumTransfer()
{
autoPtr eqnsPtr =
BasePhaseSystem::momentumTransfer();
phaseSystem::momentumTransferTable& eqns = eqnsPtr();
this->addDmdtUfs(dmdtfs_, eqns);
this->addDmdtUfs(nDmdtfs_, eqns);
return eqnsPtr;
}
template
Foam::autoPtr
Foam::ThermalPhaseChangePhaseSystem::
momentumTransferf()
{
autoPtr eqnsPtr =
BasePhaseSystem::momentumTransferf();
phaseSystem::momentumTransferTable& eqns = eqnsPtr();
this->addDmdtUfs(dmdtfs_, eqns);
this->addDmdtUfs(nDmdtfs_, eqns);
return eqnsPtr;
}
template
Foam::autoPtr
Foam::ThermalPhaseChangePhaseSystem::heatTransfer() const
{
autoPtr eqnsPtr =
BasePhaseSystem::heatTransfer();
phaseSystem::heatTransferTable& eqns = eqnsPtr();
// Create temperatures at which to evaluate nucleation mass transfers
phaseSystem::dmdtfTable Tns;
forAllConstIter(phaseSystem::dmdtfTable, nDmdtfs_, nDmdtfIter)
{
const phaseInterface interface(*this, nDmdtfIter.key());
const interfaceSaturationTemperatureModel& satModel =
this->saturation(nDmdtfIter.key());
Tns.insert
(
interface,
satModel.Tsat(interface.phase1().fluidThermo().p()).ptr()
);
}
// Mass transfer terms
if (volatile_ != "none")
{
{
phaseSystem::dmidtfTable dmidtfs;
forAllConstIter(phaseSystem::dmdtfTable, dmdtfs_, dmdtfIter)
{
const phaseInterface interface(*this, dmdtfIter.key());
dmidtfs.insert(interface, new HashPtrTable());
dmidtfs[interface]->insert
(
volatile_,
new volScalarField(*dmdtfIter())
);
}
this->addDmidtHefs
(
dmidtfs,
//Tfs_,
Tsats_,
latentHeatScheme::upwind,
latentHeatTransfer::mass,
eqns
);
}
{
phaseSystem::dmidtfTable nDmidtfs;
forAllConstIter(phaseSystem::dmdtfTable, nDmdtfs_, nDmdtfIter)
{
const phaseInterface interface(*this, nDmdtfIter.key());
nDmidtfs.insert(interface, new HashPtrTable());
nDmidtfs[interface]->insert
(
volatile_,
new volScalarField(*nDmdtfIter())
);
}
this->addDmidtHefs
(
nDmidtfs,
Tns,
0,
latentHeatScheme::upwind,
eqns
);
}
}
else
{
this->addDmdtHefs
(
dmdtfs_,
//Tfs_,
Tsats_,
latentHeatScheme::upwind,
latentHeatTransfer::mass,
eqns
);
this->addDmdtHefs
(
nDmdtfs_,
Tns,
0,
latentHeatScheme::upwind,
eqns
);
}
// Lagging
{
PtrList dmdts(this->phases().size());
addDmdts(dmdts);
forAll(this->phases(), phasei)
{
const phaseModel& phase = this->phases()[phasei];
if (dmdt0s_.set(phase.index()))
{
*eqns[phase.name()] +=
fvm::Sp
(
dmdt0s_[phase.index()] - dmdts[phase.index()],
phase.fluidThermo().he()
);
}
}
}
return eqnsPtr;
}
template
Foam::autoPtr
Foam::ThermalPhaseChangePhaseSystem::specieTransfer() const
{
autoPtr eqnsPtr =
BasePhaseSystem::specieTransfer();
phaseSystem::specieTransferTable& eqns = eqnsPtr();
if (volatile_ != "none")
{
{
phaseSystem::dmidtfTable dmidtfs;
forAllConstIter(phaseSystem::dmdtfTable, dmdtfs_, dmdtfIter)
{
const phaseInterface interface(*this, dmdtfIter.key());
dmidtfs.insert(interface, new HashPtrTable());
dmidtfs[interface]->insert
(
volatile_,
new volScalarField(*dmdtfIter())
);
}
this->addDmidtYf(dmidtfs, eqns);
}
{
phaseSystem::dmidtfTable nDmidtfs;
forAllConstIter(phaseSystem::dmdtfTable, nDmdtfs_, nDmdtfIter)
{
const phaseInterface interface(*this, nDmdtfIter.key());
nDmidtfs.insert(interface, new HashPtrTable());
nDmidtfs[interface]->insert
(
volatile_,
new volScalarField(*nDmdtfIter())
);
}
this->addDmidtYf(nDmidtfs, eqns);
}
}
else
{
this->addDmdtYfs(dmdtfs_, eqns);
this->addDmdtYfs(nDmdtfs_, eqns);
}
return eqnsPtr;
}
template
void Foam::ThermalPhaseChangePhaseSystem::
correctContinuityError()
{
dmdt0s_ = PtrList(this->phases().size());
addDmdts(dmdt0s_);
BasePhaseSystem::correctContinuityError();
}
template
void
Foam::ThermalPhaseChangePhaseSystem::correctInterfaceThermo()
{
typedef
Foam::heatTransferModels::wallBoilingHeatTransfer
wallBoilingHeatTransferModel;
HashTable
wallBoilingHeatTransferModels =
this->mesh().template lookupClass();
typedef
compressible::alphatPhaseChangeWallFunctionBase
alphatPhaseChangeWallFunction;
forAllConstIter
(
saturationModelTable,
saturationModels_,
saturationModelIter
)
{
const phaseInterface& interface = saturationModelIter()->interface();
const phaseModel& phase1 = interface.phase1();
const phaseModel& phase2 = interface.phase2();
const rhoFluidThermo& thermo1 = phase1.fluidThermo();
const rhoFluidThermo& thermo2 = phase2.fluidThermo();
const volScalarField& T1(thermo1.T());
const volScalarField& T2(thermo2.T());
const sidedBlendedHeatTransferModel& heatTransferModel =
this->heatTransferModels_[interface];
// Interfacial mass transfer update
{
volScalarField& dmdtf(*this->dmdtfs_[interface]);
volScalarField& Tf(*this->Tfs_[interface]);
volScalarField& Tsat(*this->Tsats_[interface]);
Tsat = saturationModelIter()->Tsat(thermo1.p());
const volScalarField L
(
volatile_ != "none"
? this->Li
(
interface,
volatile_,
dmdtf,
Tsat,
latentHeatScheme::symmetric
)
: this->L
(
interface,
dmdtf,
Tsat,
latentHeatScheme::symmetric
)
);
volScalarField H1(heatTransferModel.modelInThe(phase1).K(0));
volScalarField H2(heatTransferModel.modelInThe(phase2).K(0));
volScalarField dmdtfNew((H1*(Tsat - T1) + H2*(Tsat - T2))/L);
if (volatile_ != "none")
{
dmdtfNew *=
neg0(dmdtfNew)*phase1.Y(volatile_)
+ pos(dmdtfNew)*phase2.Y(volatile_);
}
if (pressureImplicit_)
{
volScalarField& d2mdtdpf(*this->d2mdtdpfs_[interface]);
const dimensionedScalar dp(rootSmall*thermo1.p().average());
const volScalarField dTsatdp
(
(
saturationModelIter()->Tsat(thermo1.p() + dp/2)
- saturationModelIter()->Tsat(thermo1.p() - dp/2)
)/dp
);
d2mdtdpf = (H1 + H2)*dTsatdp/L;
if (volatile_ != "none")
{
d2mdtdpf *=
neg0(dmdtfNew)*phase1.Y(volatile_)
+ pos(dmdtfNew)*phase2.Y(volatile_);
}
}
H1 = heatTransferModel.modelInThe(phase1).K();
H2 = heatTransferModel.modelInThe(phase2).K();
// Limit the H[12] to avoid /0
H1.max(small);
H2.max(small);
Tf = (H1*T1 + H2*T2 + dmdtfNew*L)/(H1 + H2);
Info<< Tsat.name()
<< ": min = " << gMin(Tsat.primitiveField())
<< ", mean = " << gAverage(Tsat.primitiveField())
<< ", max = " << gMax(Tsat.primitiveField())
<< endl;
Info<< Tf.name()
<< ": min = " << gMin(Tf.primitiveField())
<< ", mean = " << gAverage(Tf.primitiveField())
<< ", max = " << gMax(Tf.primitiveField())
<< endl;
const scalar dmdtfRelax =
this->mesh().solution().fieldRelaxationFactor(dmdtf.member());
dmdtf = (1 - dmdtfRelax)*dmdtf + dmdtfRelax*dmdtfNew;
Info<< dmdtf.name()
<< ": min = " << gMin(dmdtf.primitiveField())
<< ", mean = " << gAverage(dmdtf.primitiveField())
<< ", max = " << gMax(dmdtf.primitiveField())
<< ", integral = " << fvc::domainIntegrate(dmdtf).value()
<< endl;
}
// Nucleation mass transfer update
{
volScalarField& nDmdtf(*this->nDmdtfs_[interface]);
nDmdtf = Zero;
bool wallBoilingActive = false;
forAllConstIter
(
HashTable,
wallBoilingHeatTransferModels,
wallBoilingHeatTransferModelIter
)
{
const wallBoilingHeatTransferModel& wbht =
*wallBoilingHeatTransferModelIter();
if (!wbht.activePhaseInterface(interface)) continue;
wallBoilingActive = true;
nDmdtf +=
(interface == wbht.activePhaseInterface() ? +1 : -1)
*wbht.dmdtf();
}
forAllConstIter(phaseInterface, interface, interfaceIter)
{
const phaseModel& phase = interfaceIter();
const word alphatName =
IOobject::groupName("alphat", phase.name());
if (!phase.mesh().foundObject(alphatName))
continue;
const volScalarField& alphat =
phase.mesh().lookupObject(alphatName);
forAll(alphat.boundaryField(), patchi)
{
const fvPatchScalarField& alphatp =
alphat.boundaryField()[patchi];
if (!isA(alphatp)) continue;
const alphatPhaseChangeWallFunction& alphatw =
refCast(alphatp);
if (!alphatw.activeInterface(interface)) continue;
wallBoilingActive = true;
UIndirectList nDmdtfp
(
nDmdtf.primitiveFieldRef(),
alphatp.patch().faceCells()
);
nDmdtfp =
scalarField(nDmdtfp)
- (interfaceIter.index() == 0 ? +1 : -1)
*alphatw.dmdtf();
}
}
if (wallBoilingActive)
{
Info<< nDmdtf.name()
<< ": min = " << gMin(nDmdtf.primitiveField())
<< ", mean = " << gAverage(nDmdtf.primitiveField())
<< ", max = " << gMax(nDmdtf.primitiveField())
<< ", integral = " << fvc::domainIntegrate(nDmdtf).value()
<< endl;
}
}
}
}
template
bool Foam::ThermalPhaseChangePhaseSystem::read()
{
if (BasePhaseSystem::read())
{
bool readOK = true;
// Models ...
return readOK;
}
else
{
return false;
}
}
// ************************************************************************* //