"""Function for surface heat flux calculations."""
from __future__ import division
try:
range = xrange
except NameError:
pass
from .infracalcs import infracalcs
from math import log
[docs]def urbflux(UCM, UBL, BEM, forc, parameter, simTime, RSM):
"""Calculate the surface heat fluxes.
Args:
UCM: UCMDef object.
UBL: UBLDef object.
BEM: List of BEMDef objects.
forc: Forcing object.
parameter: Param object.
simTime: SimParam object.
RSM: RSMDef object.
Returns:
Tuple with updated UCMDef, UBLDef, and BEM objects.
"""
T_can = UCM.canTemp
Cp = parameter.cp
UCM.Q_roof = 0.
sigma = 5.67e-8 # Stephan-Boltzman constant
UCM.roofTemp = 0. # Average urban roof temperature
UCM.wallTemp = 0. # Average urban wall temperature
for j in range(len(BEM)):
# Building energy model
BEM[j].building.BEMCalc(UCM, BEM[j], forc, parameter, simTime)
BEM[j].ElecTotal = BEM[j].building.ElecTotal * BEM[j].fl_area # W m-2
# Update roof infra calc
e_roof = BEM[j].roof.emissivity
T_roof = BEM[j].roof.layerTemp[0]
BEM[j].roof.infra = e_roof * (forc.infra - sigma * T_roof ** 4.)
# update wall infra calc (road done later)
e_wall = BEM[j].wall.emissivity
T_wall = BEM[j].wall.layerTemp[0]
# calculates the infrared radiation for wall, taking into account radiation
# exchange from road
_infra_road_, BEM[j].wall.infra = \
infracalcs(UCM, forc, UCM.road.emissivity, e_wall, UCM.roadTemp, T_wall)
# Update element temperatures
BEM[j].mass.layerTemp = \
BEM[j].mass.Conduction(
simTime.dt, BEM[j].building.fluxMass, 1., 0., BEM[j].building.fluxMass)
BEM[j].roof.SurfFlux(
forc, parameter, simTime, UCM.canHum, T_can, max(forc.wind, UCM.canWind), 1.,
BEM[j].building.fluxRoof)
BEM[j].wall.SurfFlux(
forc, parameter, simTime, UCM.canHum, T_can, UCM.canWind, 1.,
BEM[j].building.fluxWall)
# Note the average wall & roof temperature
UCM.wallTemp = UCM.wallTemp + BEM[j].frac * BEM[j].wall.layerTemp[0]
UCM.roofTemp = UCM.roofTemp + BEM[j].frac * BEM[j].roof.layerTemp[0]
# Update road infra calc (assume walls have similar emissivity, so use the last one)
UCM.road.infra, _wall_infra = \
infracalcs(UCM, forc, UCM.road.emissivity, e_wall, UCM.roadTemp, UCM.wallTemp)
UCM.road.SurfFlux(forc, parameter, simTime, UCM.canHum, T_can, UCM.canWind, 2., 0.)
UCM.roadTemp = UCM.road.layerTemp[0]
# Sensible & latent heat flux (total)
try:
UCM.latHeat += \
UCM.latAnthrop + UCM.treeLatHeat + UCM.road.lat * (1. - UCM.bldDensity)
except TypeError:
pass # latheat is None
# ---------------------------------------------------------------------
# Advective heat flux to UBL from VDM
#
# Note: UWG_Matlab code here is modified to compensate for rounding errors
# that occur when recursively adding forDens, intAdv1, and intAdv2.
# This causes issues in the UBL.advHeat calculation when large (1e5)
# numbers are subtracted to produce small numbers (1e-10) that can
# differ from equivalent matlab calculations by a factor of 2.
# Values this small are ~ 0, but for consistency's sake Kahan Summation
# algorithm is applied to keep margin of difference from UWG_Matlab low.
# ---------------------------------------------------------------------
forDens = 0.0
intAdv1 = 0.0
intAdv2 = 0.0
# c1 & c2 stores values truncated by floating point rounding for values < 10^-16
c1 = 0.0
c2 = 0.0
c3 = 0.0
for iz in range(RSM.nzfor):
# At c loss of precision at at low order of magnitude, that we need in
# UBL.advHeat calc. Algebraically t is 0, but with floating pt numbers
# c will accumulate truncated values
y = (RSM.densityProfC[iz] * RSM.dz[iz] / (RSM.z[RSM.nzfor - 1] +
RSM.dz[RSM.nzfor-1] / 2.))
t = forDens + y
c1 += (t - forDens) - y
forDens = t
y = RSM.windProf[iz] * RSM.tempProf[iz] * RSM.dz[iz]
t = intAdv1 + y
c2 += (t - intAdv1) - y
intAdv1 = t
y = RSM.windProf[iz] * RSM.dz[iz]
t = intAdv2 + y
c3 += (t - intAdv2) - y
intAdv2 = t
# Add the truncated values back
forDens -= c1
intAdv1 -= c2
intAdv2 -= c3
UBL.advHeat = (
UBL.paralLength * Cp * forDens * (intAdv1 - (UBL.ublTemp * intAdv2)) /
UBL.urbArea)
# ---------------------------------------------------------------------
# Convective heat flux to UBL from UCM (see Appendix - Bueno (2014))
# ---------------------------------------------------------------------
zrUrb = 2 * UCM.bldHeight
zref = RSM.z[RSM.nzref-1] # Reference height
# Reference wind speed & canyon air density
windUrb = (
forc.wind*log(zref / RSM.z0r) / log(parameter.windHeight / RSM.z0r) *
log(zrUrb / UCM.z0u) / log(zref / UCM.z0u))
dens = forc.pres / (1000 * 0.287042 * T_can * (1. + 1.607858 * UCM.canHum))
# Friction velocity
UCM.ustar = parameter.vk * windUrb / log((zrUrb - UCM.l_disp) / UCM.z0u)
# Convective scaling velocity
wstar = (parameter.g * max(UCM.sensHeat, 0.0) * zref / dens / Cp / T_can) ** (1 / 3.)
UCM.ustarMod = max(UCM.ustar, wstar) # Modified friction velocity
UCM.uExch = parameter.exCoeff * UCM.ustarMod # Exchange velocity
# Canyon wind speed, Eq. 27 Chp. 3 Hanna and Britter, 2002
# assuming CD = 1 and lambda_f = verToHor/4
UCM.canWind = UCM.ustarMod * (UCM.verToHor / 8.) ** (-1 / 2.)
# Canyon turbulent velocities
UCM.turbU = 2.4 * UCM.ustarMod
UCM.turbV = 1.9 * UCM.ustarMod
UCM.turbW = 1.3 * UCM.ustarMod
# Urban wind profile
for iz in range(RSM.nzref):
UCM.windProf.append(
UCM.ustar / parameter.vk * log((RSM.z[iz] + UCM.bldHeight - UCM.l_disp) /
UCM.z0u))
return UCM, UBL, BEM