diff --git a/src/atmosphericModels/Make/files b/src/atmosphericModels/Make/files index 62808d2debd74b05c7fe23be758d18ba691df0a0..0b61ec6284890e6b1788ecda7e31f9cee02f152c 100644 --- a/src/atmosphericModels/Make/files +++ b/src/atmosphericModels/Make/files @@ -35,6 +35,13 @@ 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 diff --git a/src/atmosphericModels/Make/options b/src/atmosphericModels/Make/options index d2af502b1a05d1f68710f13284c2247eb8283f05..033971d12007a7133181d53bf5b718aba68c6dc5 100644 --- a/src/atmosphericModels/Make/options +++ b/src/atmosphericModels/Make/options @@ -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 \ diff --git a/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransfer.C b/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransfer.C new file mode 100644 index 0000000000000000000000000000000000000000..3c76ebe6ac45d471ebff668e317f133caef6ce79 --- /dev/null +++ b/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransfer.C @@ -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; +} + + +// ************************************************************************* // diff --git a/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransfer.H b/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransfer.H new file mode 100644 index 0000000000000000000000000000000000000000..95740bb74b7341974b93ed9cc264b8c3fbf8df56 --- /dev/null +++ b/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransfer.H @@ -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 + +// ************************************************************************* // diff --git a/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransferModels/evapotranspirationHeatTransferModel/evapotranspirationHeatTransferModel.C b/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransferModels/evapotranspirationHeatTransferModel/evapotranspirationHeatTransferModel.C new file mode 100644 index 0000000000000000000000000000000000000000..5051e01f80e82b15ef61f8e34b8c3f3a2bc8546a --- /dev/null +++ b/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransferModels/evapotranspirationHeatTransferModel/evapotranspirationHeatTransferModel.C @@ -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; +} + + +// ************************************************************************* // diff --git a/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransferModels/evapotranspirationHeatTransferModel/evapotranspirationHeatTransferModel.H b/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransferModels/evapotranspirationHeatTransferModel/evapotranspirationHeatTransferModel.H new file mode 100644 index 0000000000000000000000000000000000000000..1f34f91c945575132ff7943f088988a90e6b5f50 --- /dev/null +++ b/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransferModels/evapotranspirationHeatTransferModel/evapotranspirationHeatTransferModel.H @@ -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 + +// ************************************************************************* // diff --git a/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransferModels/evapotranspirationHeatTransferModel/evapotranspirationHeatTransferModelNew.C b/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransferModels/evapotranspirationHeatTransferModel/evapotranspirationHeatTransferModelNew.C new file mode 100644 index 0000000000000000000000000000000000000000..cf8f876d9e68412914413fd64f202d4b96a280d5 --- /dev/null +++ b/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransferModels/evapotranspirationHeatTransferModel/evapotranspirationHeatTransferModelNew.C @@ -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) + ); +} + + +// ************************************************************************* // diff --git a/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransferModels/grass/grass.C b/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransferModels/grass/grass.C new file mode 100644 index 0000000000000000000000000000000000000000..c22c1d12978144c9559cea6260541bd4381eb905 --- /dev/null +++ b/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransferModels/grass/grass.C @@ -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; +} + + +// ************************************************************************* // diff --git a/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransferModels/grass/grass.H b/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransferModels/grass/grass.H new file mode 100644 index 0000000000000000000000000000000000000000..b679f4e3ae2b2f48bfb7c25b6d3f143e6134613b --- /dev/null +++ b/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransferModels/grass/grass.H @@ -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 + +// ************************************************************************* // diff --git a/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransferModels/tree/tree.C b/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransferModels/tree/tree.C new file mode 100644 index 0000000000000000000000000000000000000000..9c5cb5daee5683dd5187ef0d34dbcc9e6a0b615e --- /dev/null +++ b/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransferModels/tree/tree.C @@ -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; +} + + +// ************************************************************************* // diff --git a/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransferModels/tree/tree.H b/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransferModels/tree/tree.H new file mode 100644 index 0000000000000000000000000000000000000000..c8704a2cb300d8c4d31c8727b6595dce12a8f807 --- /dev/null +++ b/src/atmosphericModels/fvOptions/evapotranspirationHeatTransfer/evapotranspirationHeatTransferModels/tree/tree.H @@ -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 + +// ************************************************************************* //