From de93be2b0dbfbe9db0d41fb79305dbc3fa62017c Mon Sep 17 00:00:00 2001 From: sbiri <sbiri@noc.ac.uk> Date: Tue, 21 Jul 2020 12:33:49 +0100 Subject: [PATCH] changed to contain only subroutines related to fluxes --- flux_subs.py | 354 ++------------------------------------------------- 1 file changed, 11 insertions(+), 343 deletions(-) diff --git a/flux_subs.py b/flux_subs.py index 0f8b959..1e17d5b 100755 --- a/flux_subs.py +++ b/flux_subs.py @@ -1,12 +1,6 @@ import numpy as np -import sys -from VaporPressure import VaporPressure +from util_subs import (CtoK, kappa, gc, visc_air) -CtoK = 273.16 # 273.15 -""" Conversion factor for $^\circ\,$C to K """ - -kappa = 0.4 # NOTE: 0.41 -""" von Karman's constant """ # --------------------------------------------------------------------- @@ -500,7 +494,8 @@ def psiu_26(zol, meth): f = np.power(zol, 2)/(1+np.power(zol, 2)) psi = np.where(zol < 0, (1-f)*psik+f*psic, psi) return psi -# --------------------------------------------------------------------- +#------------------------------------------------------------------------------ + def psim_conv(zol, meth): @@ -547,137 +542,6 @@ def psim_stab(zol, meth): # --------------------------------------------------------------------- -def get_init(spd, T, SST, lat, P, Rl, Rs, cskin, gust, L, tol, meth, qmeth): - """ - Checks initial input values and sets defaults if needed - - Parameters - ---------- - spd : float - relative wind speed in m/s (is assumed as magnitude difference - between wind and surface current vectors) - T : float - air temperature in K - SST : float - sea surface temperature in K - lat : float - latitude (deg), default 45deg - P : float - air pressure (hPa), default 1013hPa - Rl : float - downward longwave radiation (W/m^2) - Rs : float - downward shortwave radiation (W/m^2) - cskin : int - 0 switch cool skin adjustment off, else 1 - default is 1 - gust : int - 3x1 [x, beta, zi] x=1 to include the effect of gustiness, else 0 - beta gustiness parameter, beta=1 for UA, beta=1.2 for COARE - zi PBL height (m) 600 for COARE, 1000 for UA and ERA5, 800 default - default for COARE [1, 1.2, 600] - default for UA, ERA5 [1, 1, 1000] - default else [1, 1.2, 800] - L : int - Monin-Obukhov length definition options - 0 : default for S80, S88, LP82, YT96 and LY04 - 1 : following UA (Zeng et al., 1998), default for UA - 2 : following ERA5 (IFS Documentation cy46r1), default for ERA5 - 3 : COARE3.5 (Edson et al., 2013), default for C30, C35 and C40 - tol : float - 4x1 or 7x1 [option, lim1-3 or lim1-6] - option : 'flux' to set tolerance limits for fluxes only lim1-3 - option : 'ref' to set tolerance limits for height adjustment lim-1-3 - option : 'all' to set tolerance limits for both fluxes and height - adjustment lim1-6 ['all', 0.01, 0.01, 5e-05, 0.01, 1, 1] - meth : str - "S80","S88","LP82","YT96","UA","LY04","C30","C35","C40","ERA5" - qmeth : str - is the saturation evaporation method to use amongst - "HylandWexler","Hardy","Preining","Wexler","GoffGratch","CIMO", - "MagnusTetens","Buck","Buck2","WMO","WMO2000","Sonntag","Bolton", - "IAPWS","MurphyKoop"] - default is Buck2 - - Returns - ------- - lat : float - latitude - P : float - air pressure (hPa) - Rl : float - downward longwave radiation (W/m^2) - Rs : float - downward shortwave radiation (W/m^2) - cskin : int - cool skin adjustment switch - gust : int - gustiness switch - tol : float - tolerance limits - L : int - MO length switch - - """ - if ((type(spd) != np.ndarray) or (type(T) != np.ndarray) or - (type(SST) != np.ndarray)): - sys.exit("input type of spd, T and SST should be numpy.ndarray") - # if input values are nan break - if meth not in ["S80", "S88", "LP82", "YT96", "UA", "LY04", "C30", "C35", - "C40","ERA5"]: - sys.exit("unknown method") - if qmeth not in ["HylandWexler", "Hardy", "Preining", "Wexler", "CIMO", - "GoffGratch", "MagnusTetens", "Buck", "Buck2", "WMO", - "WMO2000", "Sonntag", "Bolton", "IAPWS", "MurphyKoop"]: - sys.exit("unknown q-method") - if (np.all(np.isnan(spd)) or np.all(np.isnan(T)) or np.all(np.isnan(SST))): - sys.exit("input wind, T or SST is empty") - if (np.all(lat == None)): # set latitude to 45deg if empty - lat = 45*np.ones(spd.shape) - elif ((np.all(lat != None)) and (np.size(lat) == 1)): - lat = np.ones(spd.shape)*np.copy(lat) - if ((np.all(P == None)) or np.all(np.isnan(P))): - P = np.ones(spd.shape)*1013 - elif (((np.all(P != None)) or np.all(~np.isnan(P))) and np.size(P) == 1): - P = np.ones(spd.shape)*np.copy(P) - if (np.all(Rl == None) or np.all(np.isnan(Rl))): - Rl = np.ones(spd.shape)*370 # set to default for COARE3.5 - if (np.all(Rs == None) or np.all(np.isnan(Rs))): - Rs = np.ones(spd.shape)*150 # set to default for COARE3.5 - if ((cskin == None) and (meth == "S80" or meth == "S88" or meth == "LP82" - or meth == "YT96")): - cskin = 0 - elif ((cskin == None) and (meth == "UA" or meth == "LY04" or meth == "C30" - or meth == "C35" or meth == "C40" - or meth == "ERA5")): - cskin = 1 - if ((gust == None) and (meth == "C30" or meth == "C35" or meth == "C40")): - gust = [1, 1.2, 600] - elif ((gust == None) and (meth == "UA" or meth == "ERA5")): - gust = [1, 1, 1000] - elif (gust == None): - gust = [1, 1.2, 800] - elif (np.size(gust) < 3): - sys.exit("gust input must be a 3x1 array") - if (L not in [None, 0, 1, 2, 3]): - sys.exit("L input must be either None, 0, 1, 2 or 3") - if ((L == None) and (meth == "S80" or meth == "S88" or meth == "LP82" - or meth == "YT96" or meth == "LY04")): - L = 0 - elif ((L == None) and (meth == "UA")): - L = 1 - elif ((L == None) and (meth == "ERA5")): - L = 2 - elif ((L == None) and (meth == "C30" or meth == "C35" or meth == "C40")): - L = 3 - if (tol == None): - tol = ['flux', 0.01, 1, 1] - elif (tol[0] not in ['flux', 'ref', 'all']): - sys.exit("unknown tolerance input") - return lat, P, Rl, Rs, cskin, gust, tol, L -# --------------------------------------------------------------------- - - def get_skin(sst, qsea, rho, Rl, Rs, Rnl, cp, lv, tkt, usr, tsr, qsr, lat): """ Computes cool skin @@ -779,7 +643,7 @@ def get_gust(beta, Ta, usr, tsrv, zi, lat): def get_L(L, lat, usr, tsr, qsr, t10n, tv10n, qair, h_in, T, Ta, th, tv, sst, - dt, dq, wind, monob, meth): + dt, dtv, dq, zo, wind, monob, meth): """ calculates Monin-Obukhov length and virtual star temperature @@ -843,8 +707,13 @@ def get_L(L, lat, usr, tsr, qsr, t10n, tv10n, qair, h_in, T, Ta, th, tv, sst, monob = ((tv10n*np.power(usr, 2))/(g*kappa*tsrv)) monob = np.where(np.fabs(monob) < 1, np.where(monob < 0, -1, 1), monob) elif (L == 1): + Rb = g*h_in[0]*dtv/(tv*np.power(wind, 2)) + zol = np.where(Rb >= 0, Rb*np.log(h_in[0]/zo) / + (1-5*np.where(Rb < 0.19, Rb, 0.19)), + Rb*np.log(h_in[0]/zo)) + monob = h_in[0]/zol tsrv = tsr*(1.+0.61*qair)+0.61*th*qsr - monob = ((tv*np.power(usr, 2))/(kappa*g*tsrv)) + # monob = ((tv*np.power(usr, 2))/(kappa*g*tsrv)) elif (L == 2): tsrv = tsr+0.61*t10n*qsr Rb = ((g*h_in[0]*((2*dt)/(Ta+sst-g*h_in[0])+0.61*dq)) / @@ -867,64 +736,6 @@ def get_L(L, lat, usr, tsr, qsr, t10n, tv10n, qair, h_in, T, Ta, th, tv, sst, #------------------------------------------------------------------------------ -def get_hum(hum, T, sst, P, qmeth): - """ - Get specific humidity output - - Parameters - ---------- - hum : array - humidity input switch 2x1 [x, values] default is relative humidity - x='rh' : relative humidity in % - x='q' : specific humidity (g/kg) - x='Td' : dew point temperature (K) - T : float - air temperature in K - sst : float - sea surface temperature in K - P : float - air pressure at sea level in hPa - qmeth : str - method to calculate specific humidity from vapor pressure - - Returns - ------- - qair : float - specific humidity of air - qsea : float - specific humidity over sea surface - - """ - if (hum == None): - RH = np.ones(sst.shape)*80 - qsea = qsat_sea(sst, P, qmeth)/1000 # surface water q (g/kg) - qair = qsat_air(T, P, RH, qmeth)/1000 # q of air (g/kg) - elif (hum[0] not in ['rh', 'q', 'Td']): - sys.exit("unknown humidity input") - qair, qsea = np.nan, np.nan - elif (hum[0] == 'rh'): - RH = hum[1] - if (np.all(RH < 1)): - sys.exit("input relative humidity units should be \%") - qair, qsea = np.nan, np.nan - qsea = qsat_sea(sst, P, qmeth)/1000 # surface water q (g/kg) - qair = qsat_air(T, P, RH, qmeth)/1000 # q of air (g/kg) - elif (hum[0] == 'q'): - qair = hum[1] - qsea = qsat_sea(sst, P, qmeth)/1000 # surface water q (g/kg) - elif (hum[0] == 'Td'): - Td = hum[1] # dew point temperature (K) - Td = np.where(Td < 200, np.copy(Td)+CtoK, np.copy(Td)) - T = np.where(T < 200, np.copy(T)+CtoK, np.copy(T)) - esd = 611.21*np.exp(17.502*((Td-273.16)/(Td-32.19))) - es = 611.21*np.exp(17.502*((T-273.16)/(T-32.19))) - RH = 100*esd/es - qair = qsat_air(T, P, RH, qmeth)/1000 # q of air (g/kg) - qsea = qsat_sea(sst, P, qmeth)/1000 # surface water q (g/kg) - return qair, qsea -#------------------------------------------------------------------------- - - def get_strs(h_in, monob, wind, zo, zot, zoq, dt, dq, dter, dqer, ct, cq, cskin, meth): """ @@ -951,7 +762,7 @@ def get_strs(h_in, monob, wind, zo, zot, zoq, dt, dq, dter, dqer, ct, cq, dter : float cskin temperature adjustment (K) dqer : float - cskin q adjustment (q/kg) + cskin q adjustment (g/kg) ct : float temperature exchange coefficient cq : float @@ -1033,147 +844,4 @@ def get_strs(h_in, monob, wind, zo, zot, zoq, dt, dq, dter, dqer, ct, cq, tsr = ct*wind*(dt+dter*cskin)/usr qsr = cq*wind*(dq+dqer*cskin)/usr return usr, tsr, qsr -#------------------------------------------------------------------------------ - - -def get_heights(h, dim_len): - """ Reads input heights for velocity, temperature and humidity - - Parameters - ---------- - h : float - input heights (m) - dim_len : int - length dimension - - Returns - ------- - hh : array - """ - hh = np.zeros((3, dim_len)) - if (type(h) == float or type(h) == int): - hh[0, :], hh[1, :], hh[2, :] = h, h, h - elif (len(h) == 2 and np.ndim(h) == 1): - hh[0, :], hh[1, :], hh[2, :] = h[0], h[1], h[1] - elif (len(h) == 3 and np.ndim(h) == 1): - hh[0, :], hh[1, :], hh[2, :] = h[0], h[1], h[2] - elif (len(h) == 1 and np.ndim(h) == 2): - hh = np.zeros((3, h.shape[1])) - hh[0, :], hh[1, :], hh[2, :] = h[0, :], h[0, :], h[0, :] - elif (len(h) == 2 and np.ndim(h) == 2): - hh = np.zeros((3, h.shape[1])) - hh[0, :], hh[1, :], hh[2, :] = h[0, :], h[1, :], h[1, :] - elif (len(h) == 3 and np.ndim(h) == 2): - hh = np.zeros((3, h.shape[1])) - hh = np.copy(h) - return hh -# --------------------------------------------------------------------- - - -def qsat_sea(T, P, qmeth): - """ Computes surface saturation specific humidity (g/kg) - - Parameters - ---------- - T : float - temperature ($^\\circ$\\,C) - P : float - pressure (mb) - qmeth : str - method to calculate vapor pressure - - Returns - ------- - qs : float - """ - T = np.asarray(T) - if (np.nanmin(T) > 200): # if Ta in Kelvin convert to Celsius - T = T-CtoK - ex = VaporPressure(T, P, 'liquid', qmeth) - es = 0.98*ex # reduction at sea surface - qs = 622*es/(P-0.378*es) - return qs -# ------------------------------------------------------------------------------ - - -def qsat_air(T, P, rh, qmeth): - """ Computes saturation specific humidity (g/kg) as in C35 - - Parameters - ---------- - T : float - temperature ($^\circ$\,C) - P : float - pressure (mb) - rh : float - relative humidity (%) - qmeth : str - method to calculate vapor pressure - - Returns - ------- - q : float - em : float - """ - T = np.asarray(T) - if (np.nanmin(T) > 200): # if Ta in Kelvin convert to Celsius - T = T-CtoK - es = VaporPressure(T, P, 'liquid', qmeth) - em = 0.01*rh*es - q = 622*em/(P-0.378*em) - return q -# --------------------------------------------------------------------- - - -def gc(lat, lon=None): - """ Computes gravity relative to latitude - - Parameters - ---------- - lat : float - latitude ($^\circ$) - lon : float - longitude ($^\circ$, optional) - - Returns - ------- - gc : float - gravity constant (m/s^2) - """ - gamma = 9.7803267715 - c1 = 0.0052790414 - c2 = 0.0000232718 - c3 = 0.0000001262 - c4 = 0.0000000007 - if lon is not None: - lon_m, lat_m = np.meshgrid(lon, lat) - else: - lat_m = lat - phi = lat_m*np.pi/180. - xx = np.sin(phi) - gc = (gamma*(1+c1*np.power(xx, 2)+c2*np.power(xx, 4)+c3*np.power(xx, 6) + - c4*np.power(xx, 8))) - return gc # --------------------------------------------------------------------- - - -def visc_air(T): - """ Computes the kinematic viscosity of dry air as a function of air temp. - following Andreas (1989), CRREL Report 89-11. - - Parameters - ---------- - Ta : float - air temperature ($^\circ$\,C) - - Returns - ------- - visa : float - kinematic viscosity (m^2/s) - """ - T = np.asarray(T) - if (np.nanmin(T) > 200): # if Ta in Kelvin convert to Celsius - T = T-273.16 - visa = 1.326e-5*(1+6.542e-3*T+8.301e-6*np.power(T, 2) - - 4.84e-9*np.power(T, 3)) - return visa -- GitLab