Compare commits

...

2 Commits

Author SHA1 Message Date
413c91d873 WIP: ENH: evapotranspirationHeatTransfer: New atmospheric-model fvOption
WIP: The units of the main governing eq are inconsistent - see EP1950

Applies sources on temperature ('T' - incompressible)
or energy ('h'/'e' - compressible) equation to incorporate
evapotranspiration heat-transfer effects from the specified
plant canopy. Heat transfer is usually calculated based on
empirical relations between plants and solar radiation.

Two submodels to incorporate heat transfer effects
- 'tree': specified tree canopy - uses empirical relations between solar
radiation and evapotranspiration.
- 'grass': specified grass canopy - uses Pemnan-Monteith Equation model.
2024-04-05 13:56:20 +01:00
f6539e4d52 ENH: treeTurbulence: new atmospheric-model fvOption
Applies sources on 'k' and either 'epsilon' or 'omega'
to incorporate effects of trees on atmospheric boundary layer.
2024-03-22 10:25:11 +00:00
14 changed files with 2541 additions and 0 deletions

View File

@ -33,7 +33,15 @@ fvOptions/atmPlantCanopyTurbSource/atmPlantCanopyTurbSource.C
fvOptions/atmPlantCanopyUSource/atmPlantCanopyUSource.C
fvOptions/atmPlantCanopyTSource/atmPlantCanopyTSource.C
fvOptions/atmNutSource/atmNutSource.C
fvOptions/treeTurbulence/treeTurbulence.C
etht = fvOptions/evapotranspirationHeatTransfer
ethtModels = $(etht)/evapotranspirationHeatTransferModels
$(etht)/evapotranspirationHeatTransfer.C
$(ethtModels)/evapotranspirationHeatTransferModel/evapotranspirationHeatTransferModel.C
$(ethtModels)/evapotranspirationHeatTransferModel/evapotranspirationHeatTransferModelNew.C
$(ethtModels)/tree/tree.C
$(ethtModels)/grass/grass.C
/* functionObjects */
functionObjects/ObukhovLength/ObukhovLength.C

View File

@ -8,6 +8,7 @@ EXE_INC = \
-I$(LIB_SRC)/thermophysicalModels/basic/lnInclude \
-I$(LIB_SRC)/thermophysicalModels/specie/lnInclude \
-I$(LIB_SRC)/thermophysicalModels/solidThermo/lnInclude \
-I$(LIB_SRC)/thermophysicalModels/radiation/lnInclude \
-I$(LIB_SRC)/TurbulenceModels/turbulenceModels/lnInclude \
-I$(LIB_SRC)/TurbulenceModels/compressible/lnInclude \
-I$(LIB_SRC)/TurbulenceModels/incompressible/lnInclude \
@ -18,6 +19,7 @@ LIB_LIBS = \
-lfvOptions \
-lmeshTools \
-lsurfMesh \
-lradiationModels \
-lfluidThermophysicalModels \
-lsolidThermo \
-lturbulenceModels \

View File

@ -0,0 +1,172 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "evapotranspirationHeatTransfer.H"
#include "evapotranspirationHeatTransferModel.H"
#include "fvMesh.H"
#include "fvMatrix.H"
#include "basicThermo.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
namespace fv
{
defineTypeNameAndDebug(evapotranspirationHeatTransfer, 0);
addToRunTimeSelectionTable
(
option,
evapotranspirationHeatTransfer,
dictionary
);
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::fv::evapotranspirationHeatTransfer::evapotranspirationHeatTransfer
(
const word& sourceName,
const word& modelType,
const dictionary& dict,
const fvMesh& mesh
)
:
fv::cellSetOption(sourceName, modelType, dict, mesh),
ethtModelPtr_()
{
// Set the field name to that of the energy
// field from which the temperature is obtained
const auto& thermo = mesh_.lookupObject<basicThermo>(basicThermo::dictName);
fieldNames_.resize(1, thermo.he().name());
fv::option::resetApplied();
Info<< " Applying evapotranspirationHeatTransfer to: " << fieldNames_[0]
<< endl;
read(dict);
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::fv::evapotranspirationHeatTransfer::~evapotranspirationHeatTransfer()
{} // evapotranspirationHeatTransferModel was forward declared
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
void Foam::fv::evapotranspirationHeatTransfer::addSup
(
fvScalarMatrix& eqn,
const label fieldi
)
{
if (this->V() < VSMALL)
{
return;
}
const scalar V = this->V();
const scalarField& Vcells = mesh_.V();
const volScalarField& he = eqn.psi();
scalarField& heSource = eqn.source();
// Calculate evapotranspiration heat transfer rate per volume [J/s/m^3]
const scalarField Q(ethtModelPtr_->Q(cells_)/V);
if (he.dimensions() == dimEnergy/dimMass)
{
forAll(cells_, i)
{
const label celli = cells_[i];
heSource[celli] += Q[i]*Vcells[celli];
}
}
else if (he.dimensions() == dimTemperature)
{
const auto& thermo =
mesh_.lookupObject<basicThermo>(basicThermo::dictName);
// Calculate density*heat capacity at constant pressure/volume
const volScalarField rhoCpv(thermo.rho()*thermo.Cpv());
// heSource [K m^3/s] = [J/s/m^3] * m^3 / [kg/m^3] / [J/kg/K]
forAll(cells_, i)
{
const label celli = cells_[i];
heSource[celli] += Q[i]*Vcells[celli]*rhoCpv[i];
}
}
}
void Foam::fv::evapotranspirationHeatTransfer::addSup
(
const volScalarField& rho,
fvScalarMatrix& eqn,
const label fieldi
)
{
addSup(eqn, fieldi);
}
bool Foam::fv::evapotranspirationHeatTransfer::read(const dictionary& dict)
{
if (!fv::cellSetOption::read(dict))
{
return false;
}
if (selectionMode_ != selectionModeType::smCellZone)
{
FatalIOErrorInFunction(dict)
<< "evapotranspirationHeatTransfer requires "
<< selectionModeTypeNames_[selectionModeType::smCellZone]
<< exit(FatalIOError);
}
ethtModelPtr_.reset
(
evapotranspirationHeatTransferModel::New(dict, mesh_)
);
return true;
}
// ************************************************************************* //

View File

@ -0,0 +1,183 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
Class
Foam::fv::evapotranspirationHeatTransfer
Description
Applies sources on temperature (\c T - incompressible)
or energy (\c h/e - compressible) equation to incorporate
evapotranspiration heat-transfer effects from the specified
plant canopy. Heat transfer is usually calculated based on
empirical relations between plants and solar radiation.
Usage
Example by using \c constant/fvOptions:
\verbatim
evapotranspirationHeatTransfer1
{
// Mandatory entries
type evapotranspirationHeatTransfer;
model <word>;
// Model-specific entries
...
// Inherited entries
...
}
\endverbatim
where the entries mean:
\table
Property | Description | Type | Reqd | Deflt
type | Type name: evapotranspirationHeatTransfer | word | yes | -
model | Name of heat-transfer model | word | yes | -
\endtable
Options for the \c model entry:
\verbatim
tree | Regression-based heat-transfer model for trees
grass | Heat-transfer model for grass
\endverbatim
The inherited entries are elaborated in:
- \link fvOption.H \endlink
- \link cellSetOption.H \endlink
Note
- Evapotranspiration mass transfer is not modelled.
SourceFiles
evapotranspirationHeatTransfer.C
\*---------------------------------------------------------------------------*/
#ifndef Foam_fv_evapotranspirationHeatTransfer_H
#define Foam_fv_evapotranspirationHeatTransfer_H
#include "cellSetOption.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
// Forward Declarations
class evapotranspirationHeatTransferModel;
namespace fv
{
/*---------------------------------------------------------------------------*\
Class evapotranspirationHeatTransfer Declaration
\*---------------------------------------------------------------------------*/
class evapotranspirationHeatTransfer
:
public fv::cellSetOption
{
// Private Data
//- Runtime-selectable evapotranspiration heat transfer model
autoPtr<evapotranspirationHeatTransferModel> ethtModelPtr_;
public:
//- Runtime type information
TypeName("evapotranspirationHeatTransfer");
// Constructors
//- Construct from explicit source name and mesh
evapotranspirationHeatTransfer
(
const word& sourceName,
const word& modelType,
const dictionary& dict,
const fvMesh& mesh
);
//- No copy construct
evapotranspirationHeatTransfer(const evapotranspirationHeatTransfer&) =
delete;
//- No copy assignment
void operator=(const evapotranspirationHeatTransfer&) = delete;
//- Destructor
virtual ~evapotranspirationHeatTransfer();
// Member Functions
//- Add explicit contribution to energy equation
//- for incompressible flow computations
virtual void addSup
(
fvScalarMatrix& eqn,
const label fieldi
);
//- Add explicit contribution to energy equation
//- for compressible flow computations
virtual void addSup
(
const volScalarField& rho,
fvScalarMatrix& eqn,
const label fieldi
);
//- Add explicit contribution to energy equation
//- for multiphase flow computations
virtual void addSup
(
const volScalarField& alpha,
const volScalarField& rho,
fvScalarMatrix& eqn,
const label fieldi
)
{
NotImplemented;
}
//- Read dictionary
virtual bool read(const dictionary& dict);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace fv
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -0,0 +1,196 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "evapotranspirationHeatTransferModel.H"
#include "radiationModel.H"
#include "solarLoadBase.H"
#include "fvMesh.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(evapotranspirationHeatTransferModel, 0);
defineRunTimeSelectionTable
(
evapotranspirationHeatTransferModel,
dictionary
);
}
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
Foam::volScalarField& Foam::evapotranspirationHeatTransferModel::getOrReadField
(
const word& fieldName
) const
{
auto* ptr = mesh_.getObjectPtr<volScalarField>(fieldName);
if (!ptr)
{
ptr = new volScalarField
(
IOobject
(
fieldName,
mesh_.time().timeName(),
mesh_,
IOobject::MUST_READ,
IOobject::AUTO_WRITE
),
mesh_
);
mesh_.objectRegistry::store(ptr);
}
return *ptr;
}
Foam::scalar Foam::evapotranspirationHeatTransferModel::q() const
{
// Retrieve solar-load information
const auto& base =
mesh().lookupObject<radiation::solarLoadBase>("solarLoadBase");
const solarCalculator& solar = base.solarCalculatorRef();
// Retrieve internal & boundary faces directly hit by solar rays
const faceShading& shade = base.faceShadingRef();
const labelList& hitFacesId = shade.rayStartFaces();
// Retrieve face zone information
const faceZone& zone = mesh_.faceZones()[faceZoneId_];
const vectorField& faceNormals = zone().faceNormals();
const scalarField& faceAreas = zone().magFaceAreas();
// Retrieve direct solar load [W/m^2]
const vector directSolarRad(solar.directSolarRad()*solar.direction());
// Identify zone faces within mesh
bitSet isZoneFace(mesh_.nFaces());
isZoneFace.set(zone);
// Calculate area-averaged incident solar load
// Assuming hit faces are updated by solarLoad
scalar q = 0;
scalar totalFaceArea = 0;
for (const label facei : hitFacesId)
{
if (isZoneFace[facei])
{
const label localFacei = zone.whichFace(facei);
const vector& faceNormal = faceNormals[localFacei];
const scalar faceArea = faceAreas[localFacei];
const scalar qIncident = directSolarRad & faceNormal;
q += qIncident*faceArea;
totalFaceArea += faceArea;
}
}
reduce(q, sumOp<scalar>());
reduce(totalFaceArea, sumOp<scalar>());
q /= totalFaceArea;
// Sum diffusive solar loads
switch(solar.sunLoadModel())
{
case solarCalculator::mSunLoadFairWeatherConditions:
case solarCalculator::mSunLoadTheoreticalMaximum:
{
// Calculate diffusive radiance
// contribution from sky and ground
tmp<scalarField> tdiffuseSolarRad =
solar.diffuseSolarRad(faceNormals);
const scalarField& diffuseSolarRad = tdiffuseSolarRad.cref();
// Calculate area-averaged diffusive solar load
scalar meanDiffuseSolarRad = 0;
forAll(faceAreas, i)
{
meanDiffuseSolarRad += diffuseSolarRad[i]*faceAreas[i];
}
meanDiffuseSolarRad /= totalFaceArea;
q += meanDiffuseSolarRad;
}
break;
case solarCalculator::mSunLoadConstant:
case solarCalculator::mSunLoadTimeDependent:
{
q += solar.diffuseSolarRad();
}
break;
}
return q;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::evapotranspirationHeatTransferModel::evapotranspirationHeatTransferModel
(
const dictionary& dict,
const fvMesh& mesh
)
:
mesh_(mesh)
{}
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
bool Foam::evapotranspirationHeatTransferModel::read(const dictionary& dict)
{
faceZoneId_ = mesh_.faceZones().findZoneID(dict.get<word>("faceZone"));
if (faceZoneId_ < 0)
{
const word faceZoneName(mesh_.faceZones()[faceZoneId_].name());
FatalIOErrorInFunction(dict)
<< type() << ' ' << typeName << ": "
<< " No matching face zone: " << faceZoneName << nl
<< " Known face zones: "
<< flatOutput(mesh_.faceZones().names()) << nl
<< exit(FatalIOError);
return false;
}
return true;
}
// ************************************************************************* //

View File

@ -0,0 +1,180 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
Class
Foam::evapotranspirationHeatTransferModel
Description
Base class for evapotranspiration models
to handle general evapotranspiration characteristics.
SourceFiles
evapotranspirationHeatTransferModel.C
evapotranspirationHeatTransferModelNew.C
evapotranspirationHeatTransferModelTemplates.C
\*---------------------------------------------------------------------------*/
#ifndef Foam_evapotranspirationHeatTransferModel_H
#define Foam_evapotranspirationHeatTransferModel_H
#include "dictionary.H"
#include "HashSet.H"
#include "volFields.H"
#include "runTimeSelectionTables.H"
#include "OFstream.H"
#include "coordinateSystem.H"
#include "writeFile.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
// Forward Declarations
class fvMesh;
/*---------------------------------------------------------------------------*\
Class evapotranspirationHeatTransferModel Declaration
\*---------------------------------------------------------------------------*/
class evapotranspirationHeatTransferModel
{
// Private Data
//- Reference to the mesh
const fvMesh& mesh_;
//- Identifier of specified face zone
label faceZoneId_;
protected:
// Protected Member Functions
//- Return requested field from the object registry
//- or read+register the field to the object registry
volScalarField& getOrReadField(const word& fieldName) const;
//- Area-averaged heat flux due to short-wave
//- solar rays on face zone [W/m^2]
// Short-wave: direct and diffusive solar rays
scalar q() const;
public:
//- Runtime type information
TypeName("evapotranspirationHeatTransferModel");
// Declare runtime constructor selection table
declareRunTimeSelectionTable
(
autoPtr,
evapotranspirationHeatTransferModel,
dictionary,
(
const dictionary& dict,
const fvMesh& mesh
),
(dict, mesh)
);
// Selectors
//- Return a reference to the selected evapotranspiration model
static autoPtr<evapotranspirationHeatTransferModel> New
(
const dictionary& dict,
const fvMesh& mesh
);
// Constructors
//- Construct from components
evapotranspirationHeatTransferModel
(
const dictionary& dict,
const fvMesh& mesh
);
//- No copy construct
evapotranspirationHeatTransferModel
(
const evapotranspirationHeatTransferModel&
) = delete;
//- No copy assignment
void operator=(const evapotranspirationHeatTransferModel&) = delete;
//- Destructor
virtual ~evapotranspirationHeatTransferModel() = default;
// Member Functions
// Access
//- Return const reference to the mesh
const fvMesh& mesh() const noexcept
{
return mesh_;
}
//- Return the face-zone identifier
label faceZoneId() const noexcept
{
return faceZoneId_;
}
// Evaluation
//- Return heat-transfer rate for speficied cells [J/s]
virtual tmp<scalarField> Q(const labelList& cells) const = 0;
// I-O
//- Read the dictionary
virtual bool read(const dictionary& dict);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -0,0 +1,62 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "evapotranspirationHeatTransferModel.H"
#include "fvMesh.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
Foam::autoPtr<Foam::evapotranspirationHeatTransferModel>
Foam::evapotranspirationHeatTransferModel::New
(
const dictionary& dict,
const fvMesh& mesh
)
{
word modelType(dict.get<word>("model"));
auto cstrIter = dictionaryConstructorTablePtr_->cfind(modelType);
if (!cstrIter.found())
{
FatalIOErrorInLookup
(
dict,
"model",
modelType,
*dictionaryConstructorTablePtr_
) << exit(FatalIOError);
}
return autoPtr<evapotranspirationHeatTransferModel>
(
cstrIter()(dict, mesh)
);
}
// ************************************************************************* //

View File

@ -0,0 +1,418 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "grass.H"
#include "basicThermo.H"
#include "fluidThermo.H"
#include "turbulentTransportModel.H"
#include "turbulentFluidThermoModel.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
namespace evapotranspirationHeatTransferModels
{
defineTypeNameAndDebug(grass, 0);
addToRunTimeSelectionTable
(
evapotranspirationHeatTransferModel,
grass,
dictionary
);
}
}
const Foam::Enum
<
Foam::evapotranspirationHeatTransferModels::grass::soilHeatFluxType
>
Foam::evapotranspirationHeatTransferModels::grass::soilHeatFluxTypeNames
({
{
soilHeatFluxType::PROPORTIONAL_TO_SOLAR_RADIATION,
"proportionalToSolarRadiation"
},
{ soilHeatFluxType::BOUNDARY, "boundary" }
});
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::tmp<Foam::scalarField>
Foam::evapotranspirationHeatTransferModels::grass::E(const labelList& cells)
const
{
const scalar q = this->q();
const scalar G = this->G(cells);
const scalar Delta = this->Delta();
const scalar D = this->D();
const scalar ra = this->ra();
const scalar rs = this->rs();
const scalar gamma = this->gamma();
// (BSG:Eq. 11)
const scalar E =
(Delta*(q - G) + rho_*Cp_*D/ra)/(Delta + gamma*(1 + rs/ra));
return tmp<scalarField>::New(cells.size(), E);
}
Foam::scalar Foam::evapotranspirationHeatTransferModels::grass::Delta() const
{
// (BSG:Eq. 15), (APR:Eq. 13) - note 0.6108 -> 610.8 due to kPa -> Pa
const scalar Ta = Tref_ + 237.3;
return 4098*(610.8*exp(17.27*Tref_/Ta))/sqr(Ta);
}
Foam::scalar Foam::evapotranspirationHeatTransferModels::grass::D() const
{
// (BSG:Eq. 16)
const scalar RHref = RHptr_->value(mesh().time().timeOutputValue());
return (1 - RHref)*pSat();
}
Foam::scalar Foam::evapotranspirationHeatTransferModels::grass::pSat() const
{
// (BSG:Eq. 17; Arden Buck equation)
const scalar p1 = (1.0007 + 3.46e-8*pAtm_)*611.21;
return p1*exp((18.678 - Tref_/234.5)*Tref_/(Tref_ + 257.14));
}
Foam::scalar Foam::evapotranspirationHeatTransferModels::grass::gamma() const
{
// (APR:Eq. 8)
return Cp_*pAtm_/(epsilon_*lambda());
}
Foam::scalar Foam::evapotranspirationHeatTransferModels::grass::lambda() const
{
// (BSG:Eq. 12)
const scalar T1 = Tref_ + 273.15;
const scalar T2 = Tref_ + 239.24;
return 1.91846e6*sqr(T1/T2);
}
Foam::scalar Foam::evapotranspirationHeatTransferModels::grass::ra() const
{
// (APR:Eq. 4), (BSG:Eq. 14)
const scalar log1 = log((zRefU_ - d_*h_)/(zom_*h_));
const scalar log2 = log((zRefH_ - d_*h_)/(zoh_*h_));
return log1*log2/(sqr(kappa_)*uRef_);
}
Foam::scalar Foam::evapotranspirationHeatTransferModels::grass::rs() const
{
// (BSG:Eq. 13), (APR:Eq. 5; reduced form in p. 4-5)
return ri_/(scalar(12)*h_);
}
Foam::scalar Foam::evapotranspirationHeatTransferModels::grass::S
(
const labelList& cells
) const
{
// Mark fvOption cells within mesh
bitSet isZoneCell(mesh().nCells());
isZoneCell.set(cells);
scalar S = 0;
// Select cells next to specified patches
// Sum patch area that is covered by fvOption cells
for (const label patchi : patchSet_)
{
const scalarField& s = mesh().magSf().boundaryField()[patchi];
const polyPatch& pp = mesh().boundaryMesh()[patchi];
const labelList& faceCells = pp.faceCells();
forAll(faceCells, i)
{
const bool isCovered = isZoneCell[faceCells[i]];
if (isCovered)
{
S += s[i];
}
}
}
reduce(S, sumOp<scalar>());
if (mag(S) < SMALL)
{
FatalErrorInFunction
<< "Area coverage of grass cannot be zero. "
<< "Check whether cellZone has any boundary faces."
<< exit(FatalError);
}
return S;
}
Foam::scalar Foam::evapotranspirationHeatTransferModels::grass::G
(
const labelList& cells
) const
{
switch (soilHeatFluxMethod_)
{
case soilHeatFluxType::PROPORTIONAL_TO_SOLAR_RADIATION:
{
return Csoil_*q();
}
case soilHeatFluxType::BOUNDARY:
{
// Retrieve heat flux through patches
tmp<FieldField<Field, scalar>> tqBf = this->qBf();
const FieldField<Field, scalar>& qBf = tqBf.cref();
// Mark fvOption cells within mesh
bitSet isZoneCell(mesh().nCells());
isZoneCell.set(cells);
scalar G = 0;
for (const label patchi : patchSet_)
{
const scalarField& s = mesh().magSf().boundaryField()[patchi];
const scalarField& qfld = qBf[patchi];
const polyPatch& pp = mesh().boundaryMesh()[patchi];
const labelList& faceCells = pp.faceCells();
forAll(faceCells, i)
{
const bool isCovered = isZoneCell[faceCells[i]];
if (isCovered)
{
G += qfld[i]*s[i];
}
}
}
reduce(G, sumOp<scalar>());
return G/area_;
}
}
return -1;
}
Foam::tmp<Foam::FieldField<Foam::Field, Foam::scalar>>
Foam::evapotranspirationHeatTransferModels::grass::qBf() const
{
const auto& T = mesh().lookupObject<volScalarField>(TName_);
const volScalarField::Boundary& Tbf = T.boundaryField();
auto tq = tmp<FieldField<Field, scalar>>::New(Tbf.size());
auto& q = tq.ref();
forAll(q, patchi)
{
q.set(patchi, new Field<scalar>(Tbf[patchi].size(), Zero));
}
typedef compressible::turbulenceModel cmpTurbModel;
if (mesh().foundObject<cmpTurbModel>(cmpTurbModel::propertiesName))
{
const auto& turb =
mesh().lookupObject<cmpTurbModel>(cmpTurbModel::propertiesName);
// Note: calling he(p,T) instead of he()
const volScalarField he(turb.transport().he(turb.transport().p(), T));
const volScalarField::Boundary& hebf = he.boundaryField();
const volScalarField alphaEff(turb.alphaEff());
const volScalarField::Boundary& alphaEffbf = alphaEff.boundaryField();
for (const label patchi : patchSet_)
{
q[patchi] = alphaEffbf[patchi]*hebf[patchi].snGrad();
}
}
else if (mesh().foundObject<fluidThermo>(fluidThermo::dictName))
{
const auto& thermo =
mesh().lookupObject<fluidThermo>(fluidThermo::dictName);
// Note: calling he(p,T) instead of he()
const volScalarField he(thermo.he(thermo.p(), T));
const volScalarField::Boundary& hebf = he.boundaryField();
const volScalarField& alpha(thermo.alpha());
const volScalarField::Boundary& alphabf = alpha.boundaryField();
for (const label patchi : patchSet_)
{
q[patchi] = alphabf[patchi]*hebf[patchi].snGrad();
}
}
else
{
FatalErrorInFunction
<< "Unable to find a valid thermo model to evaluate q. " << nl
<< "Database contents are: " << mesh().objectRegistry::sortedToc()
<< exit(FatalError);
}
// No radiative heat flux contribution is present
return tq;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::evapotranspirationHeatTransferModels::grass::grass
(
const dictionary& dict,
const fvMesh& mesh
)
:
evapotranspirationHeatTransferModel(dict, mesh),
soilHeatFluxMethod_
(
soilHeatFluxTypeNames.getOrDefault
(
"soilHeatFluxMethod",
dict,
soilHeatFluxType::PROPORTIONAL_TO_SOLAR_RADIATION
)
),
Tptr_(nullptr),
RHptr_(nullptr),
area_(-1),
Csoil_(),
rho_(),
Cp_(),
epsilon_(),
h_(),
kappa_(),
uRef_(),
zRefU_(),
zRefH_(),
zom_(),
zoh_(),
d_(),
ri_(),
pAtm_(),
Tref_(0),
TName_(),
patchSet_()
{
Info<< " Activating evapotranspiration heat transfer model: "
<< typeName << endl;
read(dict);
}
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
Foam::tmp<Foam::scalarField>
Foam::evapotranspirationHeatTransferModels::grass::Q
(
const labelList& cells
) const
{
if (area_ == -1)
{
area_ = S(cells);
}
Tref_ = Tptr_->value(mesh().time().timeOutputValue());
return E(cells)*area_;
}
bool Foam::evapotranspirationHeatTransferModels::grass::read
(
const dictionary& dict
)
{
if (!evapotranspirationHeatTransferModel::read(dict))
{
return false;
}
Tptr_.reset(Function1<scalar>::New("Tref", dict, &mesh()));
RHptr_.reset(Function1<scalar>::New("RHref", dict, &mesh()));
area_ = -1;
const auto range = scalarMinMax::ge(SMALL);
Csoil_ = dict.getCheckOrDefault<scalar>("Csoil", 0.1, range);
rho_ = dict.getCheckOrDefault<scalar>("rho", 1.225, range);
Cp_ = dict.getCheckOrDefault<scalar>("Cp", 1013.0, range);
epsilon_ = dict.getCheckOrDefault<scalar>("epsilon", 0.622, range);
h_ = dict.getCheckOrDefault<scalar>("h", 0.1, range);
kappa_ = dict.getCheckOrDefault<scalar>("kappa", 0.41, range);
uRef_ = dict.getCheckOrDefault<scalar>("uRef", 2, range);
zRefU_ = dict.getCheckOrDefault<scalar>("zRefU", 10, range);
zRefH_ = dict.getCheckOrDefault<scalar>("zRefH", 10, range);
zom_ = dict.getCheckOrDefault<scalar>("zom", 0.123, range);
zoh_ = dict.getCheckOrDefault<scalar>("zoh", 0.0123, range);
d_ = dict.getCheckOrDefault<scalar>("d", 2.0/3.0, range);
ri_ = dict.getCheckOrDefault<scalar>("ri", 100, range);
pAtm_ = dict.getCheckOrDefault<scalar>("pAtm", 101.325, range);
TName_ = dict.getOrDefault<word>("T", "T");
if (soilHeatFluxMethod_ == soilHeatFluxType::BOUNDARY)
{
patchSet_ =
mesh().boundaryMesh().patchSet(dict.get<wordRes>("patches"));
}
return true;
}
// ************************************************************************* //

View File

@ -0,0 +1,342 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
Class
Foam::evapotranspirationHeatTransferModels::grass
Description
Applies sources on temperature (\c T - incompressible) or energy
(\c h/e - compressible) equation to incorporate evapotranspiration
heat-transfer effects from the specified grass canopy.
The model is based on Pemnan-Monteith Equation model.
Sources applied to either of the below, if exist:
\verbatim
T | Temperature [K]
e | Internal energy [m^2/s^2]
h | Enthalphy [m^2/s^2]
\endverbatim
Required fields:
\verbatim
T | Temperature [K]
e | Internal energy [m^2/s^2]
h | Enthalphy [m^2/s^2]
LAD | Leaf area density [m^2/m^3]
\endverbatim
References:
\verbatim
Governing equations (tag:BSG):
Brozovsky, J., Simonsen, A., & Gaitani, N. (2021).
Validation of a CFD model for the evaluation of urban
microclimate at high latitudes: A case study in Trondheim, Norway.
Building and Environment, 205, 108175.
DOI:10.1016/j.buildenv.2021.108175
Governing equations (tag:APR):
Allen, R. G., Pereira, L. S., Raes, D., & Smith, M. (1998).
Crop evapotranspiration-Guidelines for computing crop
water requirements-FAO Irrigation and drainage paper 56.
Fao, Rome, 300(9), D05109.
\endverbatim
Usage
Example by using \c constant/fvOptions:
\verbatim
evapotranspirationHeatTransfer1
{
// Inherited entries
...
// Mandatory entries
model grass;
Tref <Function1<scalar>>;
RHref <Function1<scalar>>;
// Conditional entries
// when soilHeatFluxMethod == boundary
patches <wordRes>;
// Optional entries
soilHeatFluxMethod <word>;
Csoil <scalar>;
rho <scalar>;
Cp <scalar>;
epsilon <scalar>;
h <scalar>;
kappa <scalar>;
uRef <scalar>;
zRefU <scalar>;
zRefH <scalar>;
zom <scalar>;
zoh <scalar>;
d <scalar>;
ri <scalar>;
pAtm <scalar>;
T <word>;
}
\endverbatim
where the entries mean:
\table
Property | Description | Type | Reqd | Deflt
model | Model name: grass | word | yes | -
Tref | Reference weather station air temperature [Celsius] <!--
--> | Function1\<scalar\> | yes | -
RHref | Reference weather station relative humidity [%] <!--
--> | Function1\<scalar\> | yes | -
patches | Names of ground patches | wordRes | yes | -
soilHeatFluxMethod | Method to calculate soil heat flux - see below <!--
--> | word | no | -
Csoil | Proportionality constant of soil heat-flux wrt <!--
--> solar radiation | scalar | no | 0.1
rho | Mean air density at constant pressure [kg/m^3] <!--
--> | scalar | no | 1.225
Cp | Specific heat at constant pressure [J/kg/C] <!--
--> | scalar | no | 1013.0
epsilon | Molecular-weight ratio of water vapour/dry air [-] <!--
--> | scalar | no | 0.622
h | Height of grass layer [m] | scalar | no | 0.1
kappa | Von Karman constant [-] | scalar | no | 0.41
uRef | Reference velocity magnitude [m/s] | scalar | no | 2.0
zRefU | Height of wind speed measurements [m] | scalar | no | 10.0
zRefH | Height of humidity measurements [m] | scalar | no | 10.0
zom | Roughness length governing momentum transfer coefficient <!--
--> [-] | scalar | no | 0.123
zoh | Roughness length governing transfer of heat and vapour <!--
--> coefficient [-] | scalar | no | 0.0123
d | Zero plane displacement height coefficient [-] <!--
--> | scalar | no | 0.666
ri | Bulk stomata resistance of leaf [s/m] | scalar | no | 100.0
pAtm | Atmospheric pressure [kPa] | scalar | no | 101.325
T | Name of temperature field | word | no | T
\endtable
Options for the \c soilHeatFluxMethod entry:
\verbatim
proportionalToSolarRadiation | Estimate from solar load
boundary | Obtain soil heat flux from boundary
\endverbatim
The inherited entries are elaborated in:
- \link evapotranspirationHeatTransfer.H \endlink
- \link evapotranspirationHeatTransferModel.H \endlink
SourceFiles
grass.C
grassTemplates.C
\*---------------------------------------------------------------------------*/
#ifndef Foam_evapotranspirationHeatTransferModels_grass_H
#define Foam_evapotranspirationHeatTransferModels_grass_H
#include "evapotranspirationHeatTransferModel.H"
#include "Function1.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
namespace evapotranspirationHeatTransferModels
{
/*---------------------------------------------------------------------------*\
Class grass Declaration
\*---------------------------------------------------------------------------*/
class grass
:
public evapotranspirationHeatTransferModel
{
// Private Enumerations
//- Options for the soil heat flux
enum soilHeatFluxType : char
{
PROPORTIONAL_TO_SOLAR_RADIATION = 0, //!< "Estimate from solar load"
BOUNDARY, //!< "Obtain soil heat flux from boundary"
};
//- Names for soilHeatFluxType
static const Enum<soilHeatFluxType> soilHeatFluxTypeNames;
// Private Data
//- Soil heat-flux calculation method
enum soilHeatFluxType soilHeatFluxMethod_;
//- Reference weather station air temperature [Celsius]
autoPtr<Function1<scalar>> Tptr_;
//- Reference weather station relative humidity [%]
autoPtr<Function1<scalar>> RHptr_;
//- Area coverage of grass [m^2]
mutable scalar area_;
//- Proportionality constant of soil heat-flux wrt solar radiation [-]
scalar Csoil_;
//- Mean air density at constant pressure [kg/m^3]
scalar rho_;
//- Specific heat at constant pressure [J/kg/C]
scalar Cp_;
//- Molecular-weight ratio of water vapour/dry air [-]
scalar epsilon_;
//- Height of grass layer [m]
scalar h_;
//- Von Karman constant [-]
scalar kappa_;
//- Reference velocity magnitude [m/s]
scalar uRef_;
//- Height of wind speed measurements [m]
scalar zRefU_;
//- Height of humidity measurements [m]
scalar zRefH_;
//- Roughness length governing momentum transfer coefficient [-]
scalar zom_;
//- Roughness length governing transfer of heat and vapour coeff [-]
scalar zoh_;
//- Zero plane displacement height coefficient [-]
scalar d_;
//- Bulk stomata resistance of leaf [s/m]
scalar ri_;
//- Atmospheric pressure [kPa]
scalar pAtm_;
//- Cached reference weather station air temperature [Celsius]
mutable scalar Tref_;
//- Name of temperature field
word TName_;
//- List of patches to calculate boundary heat flux
labelHashSet patchSet_;
// Private Member Functions
//- Return evapotranspiration heat flux through grass layer [W/m^2]
tmp<scalarField> E(const labelList& cells) const;
//- Return slope of the relation between
//- vapour pressure-temperature [Pa/K]
scalar Delta() const;
//- Return atmospheric vapour pressure deficit [Pa]
scalar D() const;
//- Return saturated vapour pressure over water [Pa]
scalar pSat() const;
//- Return psychrometric constant [kPa/K]
scalar gamma() const;
//- Return specific latent heat of vaporisation [MJ/kg]
scalar lambda() const;
//- Return bulk aerodynamic resistance [Pa]
scalar ra() const;
//- Return bulk surface resistance [Pa]
scalar rs() const;
//- Return area coverage of grass [m^2]
scalar S(const labelList& cells) const;
//- Return area-averaged heat flux through ground [W/m^2]
scalar G(const labelList& cells) const;
//- Return heat-flux boundary fields
tmp<FieldField<Field, scalar>> qBf() const;
public:
//- Runtime type information
TypeName("grass");
// Constructors
//- Construct from components
grass
(
const dictionary& dict,
const fvMesh& mesh
);
//- No copy construct
grass(const grass&) = delete;
//- No copy assignment
void operator=(const grass&) = delete;
//- Destructor
virtual ~grass() = default;
// Member Functions
// Evaluation
//- Return heat-transfer rate for speficied cells [J/s]
virtual tmp<scalarField> Q(const labelList& cells) const;
// I-O
//- Read the dictionary
virtual bool read(const dictionary& dict);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace evapotranspirationHeatTransferModels
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -0,0 +1,139 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "tree.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
namespace evapotranspirationHeatTransferModels
{
defineTypeNameAndDebug(tree, 0);
addToRunTimeSelectionTable
(
evapotranspirationHeatTransferModel,
tree,
dictionary
);
}
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::scalar Foam::evapotranspirationHeatTransferModels::tree::Et() const
{
return a_*q() + b_;
}
Foam::tmp<Foam::scalarField>
Foam::evapotranspirationHeatTransferModels::tree::leafArea
(
const labelList& cells
) const
{
const volScalarField& LAD = getOrReadField(LADname_);
const scalarField& V = mesh().V();
auto tleafArea = tmp<scalarField>::New(cells.size(), Zero);
auto& leafArea = tleafArea.ref();
forAll(cells, i)
{
const label celli = cells[i];
leafArea[i] = LAD[celli]*V[celli];
}
return tleafArea;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::evapotranspirationHeatTransferModels::tree::tree
(
const dictionary& dict,
const fvMesh& mesh
)
:
evapotranspirationHeatTransferModel(dict, mesh),
a_(),
b_(),
lambda_(),
LADname_()
{
Info<< " Activating evapotranspiration heat transfer model: "
<< typeName << endl;
read(dict);
}
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
Foam::tmp<Foam::scalarField>
Foam::evapotranspirationHeatTransferModels::tree::Q
(
const labelList& cells
) const
{
// Convert units from [MJ g/hr] to [J kg/s]
static const scalar unitConverter = scalar(1000)/scalar(3600);
return unitConverter*lambda_*Et()*leafArea(cells);
}
bool Foam::evapotranspirationHeatTransferModels::tree::read
(
const dictionary& dict
)
{
if (!evapotranspirationHeatTransferModel::read(dict))
{
return false;
}
const auto range = scalarMinMax::ge(SMALL);
a_ = dict.getOrDefault<scalar>("a", 0.3622);
b_ = dict.getOrDefault<scalar>("b", 60.758);
lambda_ = dict.getCheckOrDefault<scalar>("lambda", 2.44, range);
LADname_ = dict.getOrDefault<word>("LAD", "LAD");
(void) getOrReadField(LADname_);
return true;
}
// ************************************************************************* //

View File

@ -0,0 +1,219 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
Class
Foam::evapotranspirationHeatTransferModels::tree
Description
Applies sources on temperature (\c T - incompressible) or energy
(\c h/e - compressible) equation to incorporate evapotranspiration
heat-transfer effects from the specified tree canopy.
The heat-transfer is calculated based on the following relations.
The heat transfer is given by the first equation and the
evapotranspiration is calculated linearly proportional to absorbed
solar radiation (direct and diffusive) with the empirical relation
shown in the second equation below.
\f[
Q = \lambda E_t \frac{1000}{3600} \int_{cellZone} LAD \, dV
\f]
where
\vartable
Q | Heat transfer from tree crown [W]
\lambda | Specific latent heat of vaporisation [MJ/kg]
E_t | Area-averaged evapotranspiration [g/m^2/hr]
LAD | Leaf area density [m^2/m^3]
V | Volume [m^3]
\endvartable
with
\f[
E_t = a R_n + b
\f]
where
\vartable
R_n | Heat flux through tree crown [W/m^2]
a | Linear-regression coefficient - slope parameter [g/W/hr]
b | Linear-regression coefficient - intercept parameter [g/m^2/hr]
\endvartable
Sources applied to either of the below, if exist:
\verbatim
T | Temperature [K]
e | Internal energy [m^2/s^2]
h | Enthalphy [m^2/s^2]
\endverbatim
Required fields:
\verbatim
T | Temperature [K]
e | Internal energy [m^2/s^2]
h | Enthalphy [m^2/s^2]
LAD | Leaf area density [m^2/m^3]
\endverbatim
Usage
Example by using \c constant/fvOptions:
\verbatim
evapotranspirationHeatTransfer1
{
// Inherited entries
...
// Mandatory entries
model tree;
// Optional entries
a <scalar>;
b <scalar>;
lambda <scalar>;
LAD <word>;
}
\endverbatim
where the entries mean:
\table
Property | Description | Type | Reqd | Deflt
model | Model name: tree | word | yes | -
a | Linear-regression coefficient - slope parameter [g/W/hr] | scalar <!--
--> | no | 0.3622
b | Linear-regression coefficient - intercept parameter [g/m^2/hr] <!--
--> | scalar | no | 60.758
lambda | Specific latent heat of vaporisation [MJ/kg] | scalar <!--
--> | no | 2.44
LAD | Name of leaf-area density field | word | no | LAD
\endtable
The inherited entries are elaborated in:
- \link evapotranspirationHeatTransfer.H \endlink
- \link evapotranspirationHeatTransferModel.H \endlink
SourceFiles
tree.C
treeTemplates.C
\*---------------------------------------------------------------------------*/
#ifndef Foam_evapotranspirationHeatTransferModels_tree_H
#define Foam_evapotranspirationHeatTransferModels_tree_H
#include "evapotranspirationHeatTransferModel.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
namespace evapotranspirationHeatTransferModels
{
/*---------------------------------------------------------------------------*\
Class tree Declaration
\*---------------------------------------------------------------------------*/
class tree
:
public evapotranspirationHeatTransferModel
{
// Private Data
//- Linear-regression coefficient - slope parameter [g/W/hr]
scalar a_;
//- Linear-regression coefficient - intercept parameter [g/m^2/hr]
scalar b_;
//- Specific latent heat of vaporisation [MJ/kg]
scalar lambda_;
//- Name of leaf-area density field
word LADname_;
// Private Member Functions
//- Return area-averaged transpiration [g/m^2/hr]
// Calculated from empirical regressions with heat flux
scalar Et() const;
//- Return volume weighted leaf area density (LAD) [m^2]
// LAD [m^2/m^3]: one-sided area of leaves per unit volume
tmp<scalarField> leafArea(const labelList& cells) const;
public:
//- Runtime type information
TypeName("tree");
// Constructors
//- Construct from components
tree
(
const dictionary& dict,
const fvMesh& mesh
);
//- No copy construct
tree(const tree&) = delete;
//- No copy assignment
void operator=(const tree&) = delete;
//- Destructor
virtual ~tree() = default;
// Member Functions
// Evaluation
//- Return heat-transfer rate for speficied cells [J/s]
virtual tmp<scalarField> Q(const labelList& cells) const;
// I-O
//- Read the dictionary
virtual bool read(const dictionary& dict);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace evapotranspirationHeatTransferModels
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -0,0 +1,234 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "treeTurbulence.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
namespace Foam
{
namespace fv
{
defineTypeNameAndDebug(treeTurbulence, 0);
addToRunTimeSelectionTable(option, treeTurbulence, dictionary);
}
}
// * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * //
Foam::volScalarField& Foam::fv::treeTurbulence::getOrReadField
(
const word& fieldName
) const
{
auto* ptr = mesh_.getObjectPtr<volScalarField>(fieldName);
if (!ptr)
{
ptr = new volScalarField
(
IOobject
(
fieldName,
mesh_.time().timeName(),
mesh_,
IOobject::MUST_READ,
IOobject::AUTO_WRITE
),
mesh_
);
mesh_.objectRegistry::store(ptr);
}
return *ptr;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::fv::treeTurbulence::treeTurbulence
(
const word& sourceName,
const word& modelType,
const dictionary& dict,
const fvMesh& mesh
)
:
fv::cellSetOption(sourceName, modelType, dict, mesh),
isEpsilon_(true),
betaP_(),
betaD_(),
Ceps1_(),
Ceps2_(),
betaStar_(),
CdName_(),
LADname_()
{
read(dict);
const auto* turbPtr = mesh_.findObject<turbulenceModel>
(
turbulenceModel::propertiesName
);
if (!turbPtr)
{
FatalErrorInFunction
<< "Unable to find a turbulence model."
<< abort(FatalError);
}
fieldNames_.resize(2);
tmp<volScalarField> tepsilon = turbPtr->epsilon();
tmp<volScalarField> tomega = turbPtr->omega();
if (!tepsilon.isTmp())
{
fieldNames_[0] = tepsilon().name();
}
else if (!tomega.isTmp())
{
isEpsilon_ = false;
fieldNames_[0] = tomega().name();
}
else
{
FatalErrorInFunction
<< "Unable to find epsilon or omega field." << nl
<< abort(FatalError);
}
fieldNames_[1] = turbPtr->k()().name();
fv::option::resetApplied();
Log << " Applying treeTurbulence to: "
<< fieldNames_[0] << " and " << fieldNames_[1]
<< endl;
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
void Foam::fv::treeTurbulence::addSup
(
fvScalarMatrix& eqn,
const label fieldi
)
{
if (fieldi == 1)
{
kSource(geometricOneField(), geometricOneField(), eqn, fieldi);
return;
}
if (isEpsilon_)
{
epsilonSource(geometricOneField(), geometricOneField(), eqn, fieldi);
}
else
{
omegaSource(geometricOneField(), geometricOneField(), eqn, fieldi);
}
}
void Foam::fv::treeTurbulence::addSup
(
const volScalarField& rho,
fvScalarMatrix& eqn,
const label fieldi
)
{
if (fieldi == 1)
{
kSource(geometricOneField(), rho, eqn, fieldi);
return;
}
if (isEpsilon_)
{
epsilonSource(geometricOneField(), rho, eqn, fieldi);
}
else
{
omegaSource(geometricOneField(), rho, eqn, fieldi);
}
}
void Foam::fv::treeTurbulence::addSup
(
const volScalarField& alpha,
const volScalarField& rho,
fvScalarMatrix& eqn,
const label fieldi
)
{
if (fieldi == 1)
{
kSource(alpha, rho, eqn, fieldi);
return;
}
if (isEpsilon_)
{
epsilonSource(alpha, rho, eqn, fieldi);
}
else
{
omegaSource(alpha, rho, eqn, fieldi);
}
}
bool Foam::fv::treeTurbulence::read(const dictionary& dict)
{
if (!fv::cellSetOption::read(dict))
{
return false;
}
betaP_ = dict.getOrDefault<scalar>("betaP", 1.0);
betaD_ = dict.getOrDefault<scalar>("betaD", 4.0);
Ceps1_ = dict.getOrDefault<scalar>("Ceps1", 0.9);
Ceps2_ = dict.getOrDefault<scalar>("Ceps2", 0.9);
betaStar_ = dict.getOrDefault<scalar>("betaStar", 0.09);
CdName_ = dict.getOrDefault<word>("Cd", "Cd");
LADname_ = dict.getOrDefault<word>("LAD", "LAD");
(void) getOrReadField(CdName_);
(void) getOrReadField(LADname_);
return true;
}
// ************************************************************************* //

View File

@ -0,0 +1,262 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
Class
Foam::fv::treeTurbulence
Group
grpFvOptionsSources
Description
Applies sources on \c k and either \c epsilon or \c omega
to incorporate effects of trees on atmospheric boundary layer.
Corrections applied to:
\verbatim
k | Turbulent kinetic energy [m^2/s^2]
\endverbatim
Corrections applied to either of the below, if exist:
\verbatim
epsilon | Turbulent kinetic energy dissipation rate [m^2/s^3]
omega | Specific dissipation rate [1/s]
\endverbatim
Required fields:
\verbatim
k | Turbulent kinetic energy [m^2/s^2]
Cd | Canopy drag coefficient [-]
LAD | Leaf area density [m^2/m^3]
epsilon | Turbulent kinetic energy dissipation rate [m^2/s^3]
omega | Specific dissipation rate [1/s]
\endverbatim
References:
\verbatim
Governing equations (tag:BSG):
Brozovsky, J., Simonsen, A., & Gaitani, N. (2021).
Validation of a CFD model for the evaluation of urban microclimate
at high latitudes: A case study in Trondheim, Norway.
Building and Environment, 205, 108175.
DOI:10.1016/j.buildenv.2021.108175
\endverbatim
Usage
Example by using \c constant/fvOptions:
\verbatim
treeTurbulence1
{
// Mandatory entries
type treeTurbulence;
// Optional entries
Cd <word>;
LAD <word>;
betaP <scalar>;
betaD <scalar>;
Ceps1 <scalar>;
Ceps2 <scalar>;
betaStar <scalar>;
// Inherited entries
...
}
\endverbatim
where the entries mean:
\table
Property | Description | Type | Reqd | Deflt
type | Type name: treeTurbulence | word | yes | -
Cd | Name of operand canopy drag coefficient field | word | no | Cd
LAD | Name of operand leaf area density field | word | no | LAD
betaP | Model coefficient | scalar | no | 1.0
betaD | Model coefficient | scalar | no | 4.0
Ceps1 | Model coefficient | scalar | no | 0.9
Ceps2 | Model coefficient | scalar | no | 0.9
betaStar | Model coefficient | scalar | no | 0.09
\endtable
The inherited entries are elaborated in:
- \link fvOption.H \endlink
- \link cellSetOption.H \endlink
SourceFiles
treeTurbulence.C
treeTurbulenceTemplates.C
\*---------------------------------------------------------------------------*/
#ifndef fv_treeTurbulence_H
#define fv_treeTurbulence_H
#include "cellSetOption.H"
#include "turbulentTransportModel.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
namespace fv
{
/*---------------------------------------------------------------------------*\
Class treeTurbulence Declaration
\*---------------------------------------------------------------------------*/
class treeTurbulence
:
public fv::cellSetOption
{
// Private Data
//- Internal flag to determine the working field is epsilon or omega
bool isEpsilon_;
// Model coefficients
scalar betaP_;
scalar betaD_;
scalar Ceps1_;
scalar Ceps2_;
scalar betaStar_;
//- Name of operand canopy drag coefficient field
word CdName_;
//- Name of operand leaf area density field
word LADname_;
// Private Member Functions
//- Return requested field from the object registry
//- or read+register the field to the object registry
volScalarField& getOrReadField(const word& fieldName) const;
//- Apply sources to turbulent kinetic energy
template<class AlphaFieldType, class RhoFieldType>
void kSource
(
const AlphaFieldType& alpha,
const RhoFieldType& rho,
fvScalarMatrix& eqn,
const label fieldi
) const;
//- Apply sources to turbulent kinetic energy dissipation rate
template<class AlphaFieldType, class RhoFieldType>
void epsilonSource
(
const AlphaFieldType& alpha,
const RhoFieldType& rho,
fvScalarMatrix& eqn,
const label fieldi
) const;
//- Apply sources to specific dissipation rate
template<class AlphaFieldType, class RhoFieldType>
void omegaSource
(
const AlphaFieldType& alpha,
const RhoFieldType& rho,
fvScalarMatrix& eqn,
const label fieldi
) const;
public:
//- Runtime type information
TypeName("treeTurbulence");
// Constructors
//- Construct from explicit source name and mesh
treeTurbulence
(
const word& sourceName,
const word& modelType,
const dictionary& dict,
const fvMesh& mesh
);
//- No copy construct
treeTurbulence(const treeTurbulence&) = delete;
//- No copy assignment
void operator=(const treeTurbulence&) = delete;
// Member Functions
//- Add explicit contribution to epsilon or omega equation
//- for incompressible flow computations
virtual void addSup
(
fvScalarMatrix& eqn,
const label fieldi
);
//- Add explicit contribution to epsilon or omega equation
//- for compressible flow computations
virtual void addSup
(
const volScalarField& rho,
fvScalarMatrix& eqn,
const label fieldi
);
//- Add explicit contribution to epsilon or omega equation
//- for multiphase flow computations
virtual void addSup
(
const volScalarField& alpha,
const volScalarField& rho,
fvScalarMatrix& eqn,
const label fieldi
);
//- Read source dictionary
virtual bool read(const dictionary& dict);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace fv
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#ifdef NoRepository
#include "treeTurbulenceTemplates.C"
#endif
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -0,0 +1,124 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "treeTurbulence.H"
#include "volFields.H"
#include "fvmSup.H"
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
template<class AlphaFieldType, class RhoFieldType>
void Foam::fv::treeTurbulence::kSource
(
const AlphaFieldType& alpha,
const RhoFieldType& rho,
fvScalarMatrix& eqn,
const label fieldi
) const
{
const auto* turbPtr = mesh_.findObject<turbulenceModel>
(
turbulenceModel::propertiesName
);
const volScalarField& k = turbPtr->k();
const volVectorField& U = turbPtr->U();
const volScalarField magU(mag(U));
const volScalarField& Cd = getOrReadField(CdName_);
const volScalarField& LAD = getOrReadField(LADname_);
// (BSG:Eq. 8)
eqn +=
alpha*rho*LAD*Cd*betaP_*pow3(magU)
- fvm::Sp(alpha*rho*LAD*Cd*betaD_*magU, k);
}
template<class AlphaFieldType, class RhoFieldType>
void Foam::fv::treeTurbulence::epsilonSource
(
const AlphaFieldType& alpha,
const RhoFieldType& rho,
fvScalarMatrix& eqn,
const label fieldi
) const
{
const auto* turbPtr = mesh_.findObject<turbulenceModel>
(
turbulenceModel::propertiesName
);
const volScalarField& epsilon = turbPtr->epsilon();
const volScalarField& k = turbPtr->k();
const volVectorField& U = turbPtr->U();
const volScalarField magU(mag(U));
const volScalarField& Cd = getOrReadField(CdName_);
const volScalarField& LAD = getOrReadField(LADname_);
// (BSG:Eq. 9)
eqn +=
fvm::Sp
(
alpha*rho*LAD*Cd*(Ceps1_*betaP_/k*pow3(magU) - Ceps2_*betaD_*magU),
epsilon
);
}
template<class AlphaFieldType, class RhoFieldType>
void Foam::fv::treeTurbulence::omegaSource
(
const AlphaFieldType& alpha,
const RhoFieldType& rho,
fvScalarMatrix& eqn,
const label fieldi
) const
{
const auto* turbPtr = mesh_.findObject<turbulenceModel>
(
turbulenceModel::propertiesName
);
const volScalarField& omega = turbPtr->omega();
const volScalarField& k = turbPtr->k();
const volVectorField& U = turbPtr->U();
const volScalarField magU(mag(U));
const volScalarField& Cd = getOrReadField(CdName_);
const volScalarField& LAD = getOrReadField(LADname_);
// (derived from BSG:Eq. 9 by assuming epsilon = betaStar*omega*k)
eqn +=
fvm::Sp
(
alpha*rho*LAD*Cd*betaStar_
*(Ceps1_*betaP_*pow3(magU) - Ceps2_*betaD_*k*magU),
omega
);
}
// ************************************************************************* //