/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022-2023 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 "effectivenessTable.H"
#include "basicThermo.H"
#include "surfaceInterpolate.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
namespace heatExchangerModels
{
defineTypeNameAndDebug(effectivenessTable, 0);
addToRunTimeSelectionTable
(
heatExchangerModel,
effectivenessTable,
dictionary
);
}
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::heatExchangerModels::effectivenessTable::writeFileHeader
(
Ostream& os
) const
{
writeFile::writeHeader(os, "Heat exchanger source");
writeFile::writeCommented(os, "Time");
writeFile::writeTabbed(os, "Net mass flux [kg/s]");
writeFile::writeTabbed(os, "Total heat exchange [W]");
writeFile::writeTabbed(os, "Secondary inlet T [K]");
writeFile::writeTabbed(os, "Reference T [K]");
writeFile::writeTabbed(os, "Effectiveness [-]");
if (secondaryCpPtr_)
{
writeFile::writeTabbed(os, "Secondary outlet T [K]");
}
os << endl;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::heatExchangerModels::effectivenessTable::effectivenessTable
(
const fvMesh& mesh,
const word& name,
const dictionary& coeffs
)
:
heatExchangerModel(mesh, name, coeffs),
userPrimaryInletT_(false),
targetQdotActive_(false),
secondaryCpPtr_
(
Function1::NewIfPresent
(
"secondaryCp",
coeffs,
word::null,
&mesh
)
),
eTable_(),
targetQdotCalcInterval_(5),
secondaryMassFlowRate_(0),
secondaryInletT_(0),
primaryInletT_(0),
targetQdot_(0),
targetQdotRelax_(0.5),
sumPhi_(0),
Qt_(0),
Tref_(0),
effectiveness_(0)
{
writeFileHeader(file());
}
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
void Foam::heatExchangerModels::effectivenessTable::initialise()
{
eTable_.reset(new interpolation2DTable(coeffs_));
heatExchangerModel::initialise();
}
Foam::tmp
Foam::heatExchangerModels::effectivenessTable::energyDensity
(
const labelList& cells
)
{
const auto& thermo = mesh_.lookupObject(basicThermo::dictName);
const auto& phi = mesh_.lookupObject(phiName_);
const auto& T = mesh_.lookupObject(TName_);
const surfaceScalarField Cpf(fvc::interpolate(thermo.Cp()));
const surfaceScalarField Tf(fvc::interpolate(T));
sumPhi_ = 0;
scalar sumMagPhi = 0;
scalar CpfMean = 0;
scalar primaryInletTfMean = 0;
forAll(faceId_, i)
{
const label facei = faceId_[i];
if (facePatchId_[i] != -1)
{
const label patchi = facePatchId_[i];
const scalar phii = phi.boundaryField()[patchi][facei]*faceSign_[i];
const scalar magPhii = mag(phii);
sumPhi_ += phii;
sumMagPhi += magPhii;
const scalar Cpfi = Cpf.boundaryField()[patchi][facei];
const scalar Tfi = Tf.boundaryField()[patchi][facei];
CpfMean += Cpfi*magPhii;
primaryInletTfMean += Tfi*magPhii;
}
else
{
const scalar phii = phi[facei]*faceSign_[i];
const scalar magPhii = mag(phii);
sumPhi_ += phii;
sumMagPhi += magPhii;
CpfMean += Cpf[facei]*magPhii;
primaryInletTfMean += Tf[facei]*magPhii;
}
}
reduce(CpfMean, sumOp());
reduce(sumPhi_, sumOp());
reduce(sumMagPhi, sumOp());
CpfMean /= sumMagPhi + ROOTVSMALL;
scalar primaryInletT = primaryInletT_;
if (!userPrimaryInletT_)
{
reduce(primaryInletTfMean, sumOp());
primaryInletT = primaryInletTfMean/(sumMagPhi + ROOTVSMALL);
}
effectiveness_ = eTable_()(mag(sumPhi_), secondaryMassFlowRate_);
const scalar alpha = effectiveness_*CpfMean*mag(sumPhi_);
Qt_ = alpha*(secondaryInletT_ - primaryInletT);
if
(
targetQdotActive_
&& (mesh_.time().timeIndex() % targetQdotCalcInterval_ == 0)
)
{
const scalar dT = (targetQdot_ - Qt_)/(alpha + ROOTVSMALL);
secondaryInletT_ += targetQdotRelax_*dT;
}
// start with a copy
scalarField deltaTCells(T, cells);
Tref_ = 0;
if (Qt_ > 0)
{
Tref_ = gMax(deltaTCells);
for (scalar& delta : deltaTCells)
{
delta = Foam::max(Tref_ - delta, scalar(0));
}
}
else
{
Tref_ = gMin(deltaTCells);
for (scalar& delta : deltaTCells)
{
delta = Foam::max(delta - Tref_, scalar(0));
}
}
const auto& U = mesh_.lookupObject(UName_);
const scalarField& V = mesh_.V();
scalar sumWeight = 0;
forAll(cells, i)
{
const label celli = cells[i];
sumWeight += V[celli]*mag(U[celli])*deltaTCells[i];
}
reduce(sumWeight, sumOp());
return Qt_*deltaTCells/(sumWeight + ROOTVSMALL);
}
bool Foam::heatExchangerModels::effectivenessTable::read(const dictionary& dict)
{
if (!writeFile::read(dict))
{
return false;
}
Info<< incrIndent << indent << "- using model: " << type() << endl;
coeffs_.readEntry("secondaryMassFlowRate", secondaryMassFlowRate_);
coeffs_.readEntry("secondaryInletT", secondaryInletT_);
if (coeffs_.readIfPresent("primaryInletT", primaryInletT_))
{
userPrimaryInletT_ = true;
Info<< indent
<< "- using user-specified primary flow inlet temperature: "
<< primaryInletT_ << endl;
}
else
{
Info<< indent
<< "- using flux-weighted primary flow inlet temperature"
<< endl;
}
if (coeffs_.readIfPresent("targetQdot", targetQdot_))
{
targetQdotActive_ = true;
Info<< indent << "- using target heat rejection: " << targetQdot_ << nl;
coeffs_.readIfPresent
(
"targetQdotCalcInterval",
targetQdotCalcInterval_
);
Info<< indent << "- updating secondary inlet temperature every "
<< targetQdotCalcInterval_ << " iterations" << nl;
coeffs_.readIfPresent("targetQdotRelax", targetQdotRelax_);
Info<< indent << "- temperature relaxation: "
<< targetQdotRelax_ << endl;
}
UName_ = coeffs_.getOrDefault("U", "U");
TName_ = coeffs_.getOrDefault("T", "T");
phiName_ = coeffs_.getOrDefault("phi", "phi");
coeffs_.readEntry("faceZone", faceZoneName_);
Info<< decrIndent;
return true;
}
void Foam::heatExchangerModels::effectivenessTable::write
(
const bool log
)
{
if (log)
{
Info<< nl
<< type() << ": " << name_ << nl << incrIndent
<< indent << "Net mass flux [kg/s] : " << sumPhi_ << nl
<< indent << "Total heat exchange [W] : " << Qt_ << nl
<< indent << "Secondary inlet T [K] : " << secondaryInletT_<< nl
<< indent << "Reference T [K] : " << Tref_ << nl
<< indent << "Effectiveness [-] : " << effectiveness_
<< decrIndent;
}
if (Pstream::master())
{
Ostream& os = file();
writeCurrentTime(os);
os << tab << sumPhi_
<< tab << Qt_
<< tab << secondaryInletT_
<< tab << Tref_
<< tab << effectiveness_;
if (secondaryCpPtr_)
{
// Secondary Cp as a function of the starting secondary temperature
const scalar secondaryCp = secondaryCpPtr_->value(secondaryInletT_);
const scalar secondaryOutletT =
secondaryInletT_ - Qt_/(secondaryMassFlowRate_*secondaryCp);
if (log)
{
Info << nl << incrIndent << indent
<< "Secondary outlet T [K] : " << secondaryOutletT
<< decrIndent;
}
os << tab << secondaryOutletT;
}
os << endl;
}
Info<< nl << endl;
}
// ************************************************************************* //