From 91b4cd7d27f3f046085630c0f810143b663d06f5 Mon Sep 17 00:00:00 2001 From: sbiri <sbiri@noc.ac.uk> Date: Fri, 19 Mar 2021 10:08:04 +0000 Subject: [PATCH] cdn_from_roughness fixed not to set non-converged values to NaN. fixed issue Cdn C35 --- AirSeaFluxCode.py | 12 ++--- flux_subs.py | 132 +++++++++++++++++++++------------------------- 2 files changed, 66 insertions(+), 78 deletions(-) diff --git a/AirSeaFluxCode.py b/AirSeaFluxCode.py index ba60c83..38222e1 100644 --- a/AirSeaFluxCode.py +++ b/AirSeaFluxCode.py @@ -182,7 +182,8 @@ def AirSeaFluxCode(spd, T, SST, lat=None, hum=None, P=None, hin=18, hout=10, lv = (2.501-0.00237*(sst-CtoK))*1e6 cp = 1004.67*(1 + 0.00084*qsea) u10n = np.copy(spd) - cd10n = cdn_calc(u10n, Ta, None, lat, meth) + usr = 0.035*u10n + cd10n = cdn_calc(u10n, usr, Ta, lat, meth) ct10n, ct, cq10n, cq = (np.zeros(spd.shape)*np.nan, np.zeros(spd.shape)*np.nan, np.zeros(spd.shape)*np.nan, np.zeros(spd.shape)*np.nan) psim, psit, psiq = (np.zeros(spd.shape), np.zeros(spd.shape), @@ -233,7 +234,7 @@ def AirSeaFluxCode(spd, T, SST, lat=None, hum=None, P=None, hin=18, hout=10, elif (tol[0] == 'all'): old = np.array([np.copy(u10n), np.copy(t10n), np.copy(q10n), np.copy(tau), np.copy(sensible), np.copy(latent)]) - cd10n[ind] = cdn_calc(u10n[ind], Ta[ind], None, lat[ind], meth) + cd10n[ind] = cdn_calc(u10n[ind], usr[ind], Ta[ind], lat[ind], meth) if (np.all(np.isnan(cd10n))): break logging.info('break %s at iteration %s cd10n<0', meth, it) @@ -241,7 +242,7 @@ def AirSeaFluxCode(spd, T, SST, lat=None, hum=None, P=None, hin=18, hout=10, psim[ind] = psim_calc(h_in[0, ind]/monob[ind], meth) cd[ind] = cd_calc(cd10n[ind], h_in[0, ind], ref_ht, psim[ind]) ct10n[ind], cq10n[ind] = ctcqn_calc(h_in[1, ind]/monob[ind], - cd10n[ind], u10n[ind], zo[ind], + cd10n[ind], usr[ind], zo[ind], Ta[ind], meth) zot[ind] = ref_ht/(np.exp(np.power(kappa, 2) / (ct10n[ind]*np.log(ref_ht/zo[ind])))) @@ -250,7 +251,7 @@ def AirSeaFluxCode(spd, T, SST, lat=None, hum=None, P=None, hin=18, hout=10, psit[ind] = psit_calc(h_in[1, ind]/monob[ind], meth) psiq[ind] = psit_calc(h_in[2, ind]/monob[ind], meth) ct[ind], cq[ind] = ctcq_calc(cd10n[ind], cd[ind], ct10n[ind], cq10n[ind], - h_in[1, ind], h_in[2, ind], ref_ht, + h_in[:, ind], [ref_ht, ref_ht, ref_ht], psit[ind], psiq[ind]) usr[ind], tsr[ind], qsr[ind] = get_strs(h_in[:, ind], monob[ind], wind[ind], zo[ind], zot[ind], @@ -413,8 +414,7 @@ def AirSeaFluxCode(spd, T, SST, lat=None, hum=None, P=None, hin=18, hout=10, # adjust neutral ctn, cqn at any output height ctn =np.power(kappa, 2)/(np.log(h_out[0]/zo)*np.log(h_out[1]/zot)) cqn =np.power(kappa, 2)/(np.log(h_out[0]/zo)*np.log(h_out[2]/zoq)) - ct, cq = ctcq_calc(cdn, cd, ctn, cqn, h_out[1], h_out[2], h_out[1], - psit, psiq) + ct, cq = ctcq_calc(cdn, cd, ctn, cqn, h_out, h_out, psit, psiq) uref = (spd-usr/kappa*(np.log(h_in[0]/h_out[0])-psim + psim_calc(h_out[0]/monob, meth))) tref = (Ta-tsr/kappa*(np.log(h_in[1]/h_out[1])-psit + diff --git a/flux_subs.py b/flux_subs.py index 2500897..c83f71d 100755 --- a/flux_subs.py +++ b/flux_subs.py @@ -4,7 +4,7 @@ from util_subs import (CtoK, kappa, gc, visc_air) # --------------------------------------------------------------------- -def cdn_calc(u10n, Ta, Tp, lat, meth="S80"): +def cdn_calc(u10n, usr, Ta, lat, meth="S80"): """ Calculates neutral drag coefficient @@ -12,10 +12,10 @@ def cdn_calc(u10n, Ta, Tp, lat, meth="S80"): ---------- u10n : float neutral 10m wind speed [m/s] + usr : float + friction velocity [m/s] Ta : float air temperature [K] - Tp : float - wave period lat : float latitude [degE] meth : str @@ -32,8 +32,8 @@ def cdn_calc(u10n, Ta, Tp, lat, meth="S80"): np.where((u10n < 11) & (u10n >= 4), 1.2*0.001, (0.49+0.065*u10n)*0.001)) elif (meth == "S88" or meth == "UA" or meth == "ecmwf" or meth == "C30" or - meth == "C35" or meth == "Beljaars"): - cdn = cdn_from_roughness(u10n, Ta, None, lat, meth) + meth == "C35" or meth == "Beljaars"): # or meth == "C40" + cdn = cdn_from_roughness(u10n, usr, Ta, lat, meth) elif (meth == "YT96"): # convert usr in eq. 21 to cdn to expand for low wind speeds cdn = np.power((0.10038+u10n*2.17e-3+np.power(u10n, 2)*2.78e-3 - @@ -48,7 +48,7 @@ def cdn_calc(u10n, Ta, Tp, lat, meth="S80"): # --------------------------------------------------------------------- -def cdn_from_roughness(u10n, Ta, Tp, lat, meth="S88"): +def cdn_from_roughness(u10n, usr, Ta, lat, meth="S88"): """ Calculates neutral drag coefficient from roughness length @@ -56,10 +56,10 @@ def cdn_from_roughness(u10n, Ta, Tp, lat, meth="S88"): ---------- u10n : float neutral 10m wind speed [m/s] + usr : float + friction velocity [m/s] Ta : float air temperature [K] - Tp : float - wave period lat : float [degE] latitude meth : str @@ -68,13 +68,10 @@ def cdn_from_roughness(u10n, Ta, Tp, lat, meth="S88"): ------- cdn : float """ - g, tol = gc(lat, None), 0.000001 - cdn, usr = np.zeros(Ta.shape), np.zeros(Ta.shape) - cdnn = (0.61+0.063*u10n)*0.001 + g = gc(lat, None) + cdn = (0.61+0.063*u10n)*0.001 zo, zc, zs = np.zeros(Ta.shape), np.zeros(Ta.shape), np.zeros(Ta.shape) for it in range(5): - cdn = np.copy(cdnn) - usr = np.sqrt(cdn*u10n**2) if (meth == "S88"): # Charnock roughness length (eq. 4 in Smith 88) zc = 0.011*np.power(usr, 2)/g @@ -98,8 +95,7 @@ def cdn_from_roughness(u10n, Ta, Tp, lat, meth="S88"): zo = 0.018*np.power(usr, 2)/g+0.11*visc_air(Ta)/usr else: print("unknown method for cdn_from_roughness "+meth) - cdnn = (kappa/np.log(10/zo))**2 - cdn = np.where(np.abs(cdnn-cdn) < tol, cdnn, np.nan) + cdn = np.power(kappa/np.log(10/zo), 2) return cdn # --------------------------------------------------------------------- @@ -128,7 +124,7 @@ def cd_calc(cdn, hin, hout, psim): # --------------------------------------------------------------------- -def ctcqn_calc(zol, cdn, u10n, zo, Ta, meth="S80"): +def ctcqn_calc(zol, cdn, usr, zo, Ta, meth="S80"): """ Calculates neutral heat and moisture exchange coefficients @@ -138,8 +134,8 @@ def ctcqn_calc(zol, cdn, u10n, zo, Ta, meth="S80"): height over MO length cdn : float neutral drag coefficient - u10n : float - neutral 10m wind speed [m/s] + usr : float + friction velocity [m/s] zo : float surface roughness [m] Ta : float @@ -163,41 +159,37 @@ def ctcqn_calc(zol, cdn, u10n, zo, Ta, meth="S80"): cqn = 34.6*0.001*np.sqrt(cdn) ctn = np.where(zol <= 0, 32.7*0.001*np.sqrt(cdn), 18*0.001*np.sqrt(cdn)) elif (meth == "UA"): - usr = np.sqrt(cdn*np.power(u10n, 2)) # Zeng et al. 1998 (25) - re=usr*zo/visc_air(Ta) - zoq = zo/np.exp(2.67*np.power(re, 1/4)-2.57) + rr = usr*zo/visc_air(Ta) + zoq = zo/np.exp(2.67*np.power(rr, 1/4)-2.57) zot = np.copy(zoq) cqn = np.power(kappa, 2)/(np.log(10/zo)*np.log(10/zoq)) ctn = np.power(kappa, 2)/(np.log(10/zo)*np.log(10/zoq)) elif (meth == "C30"): - usr = np.sqrt(cdn*np.power(u10n, 2)) rr = zo*usr/visc_air(Ta) zoq = np.minimum(5e-5/np.power(rr, 0.6), 1.15e-4) # moisture roughness zot = np.copy(zoq) # temperature roughness - cqn = kappa**2/np.log(10/zo)/np.log(10/zoq) - ctn = kappa**2/np.log(10/zo)/np.log(10/zot) + cqn = np.power(kappa, 2)/np.log(10/zo)/np.log(10/zoq) + ctn = np.power(kappa, 2)/np.log(10/zo)/np.log(10/zot) elif (meth == "C35"): - usr = np.sqrt(cdn*np.power(u10n, 2)) rr = zo*usr/visc_air(Ta) - zoq = np.minimum(5.8e-5/np.power(rr, 0.72), 1.6e-4) # moisture roughness + zoq = np.minimum(5.8e-5/np.power(rr, 0.72), 1.6e-4) # moisture roughness zot = np.copy(zoq) # temperature roughness - cqn = kappa**2/np.log(10/zo)/np.log(10/zoq) - ctn = kappa**2/np.log(10/zo)/np.log(10/zot) + cqn = np.power(kappa, 2)/np.log(10/zo)/np.log(10/zoq) + ctn = np.power(kappa, 2)/np.log(10/zo)/np.log(10/zot) elif (meth == "ecmwf" or meth == "Beljaars"): # eq. (3.26) p.38 over sea IFS Documentation cy46r1 - usr = np.sqrt(cdn*np.power(u10n, 2)) zot = 0.40*visc_air(Ta)/usr zoq = 0.62*visc_air(Ta)/usr - cqn = kappa**2/np.log(10/zo)/np.log(10/zoq) - ctn = kappa**2/np.log(10/zo)/np.log(10/zot) + cqn = np.power(kappa, 2)/np.log(10/zo)/np.log(10/zoq) + ctn = np.power(kappa, 2)/np.log(10/zo)/np.log(10/zot) else: print("unknown method ctcqn: "+meth) return ctn, cqn # --------------------------------------------------------------------- -def ctcq_calc(cdn, cd, ctn, cqn, ht, hq, hout, psit, psiq): +def ctcq_calc(cdn, cd, ctn, cqn, hin, hout, psit, psiq): """ Calculates heat and moisture exchange coefficients at reference height @@ -211,12 +203,10 @@ def ctcq_calc(cdn, cd, ctn, cqn, ht, hq, hout, psit, psiq): neutral heat exchange coefficient cqn : float neutral moisture exchange coefficient - ht : float - original temperature sensor height [m] - hq : float - original moisture sensor height [m] + hin : float + original temperature/humidity sensor height [m] hout : float - reference height [m] + reference height [m] psit : float heat stability function psiq : float @@ -230,9 +220,9 @@ def ctcq_calc(cdn, cd, ctn, cqn, ht, hq, hout, psit, psiq): moisture exchange coefficient """ ct = (ctn*np.sqrt(cd/cdn) / - (1+ctn*((np.log(ht/hout)-psit)/(kappa*np.sqrt(cdn))))) + (1+ctn*((np.log(hin[1]/hout[1])-psit)/(kappa*np.sqrt(cdn))))) cq = (cqn*np.sqrt(cd/cdn) / - (1+cqn*((np.log(hq/hout)-psiq)/(kappa*np.sqrt(cdn))))) + (1+cqn*((np.log(hin[2]/hout[2])-psiq)/(kappa*np.sqrt(cdn))))) return ct, cq # --------------------------------------------------------------------- @@ -380,16 +370,16 @@ def psit_26(zol): psi : float """ b, d = 2/3, 0.35 - dzol = np.where(d*zol > 50, 50, d*zol) - psi = np.where(zol > 0,-(np.power(1+b*zol, 1.5)+b*(zol-14.28) * - np.exp(-dzol)+8.525), np.nan) - psik = np.where(zol < 0, 2*np.log((1+np.sqrt(1-15*zol))/2), np.nan) - psic = np.where(zol < 0, 1.5*np.log((1+np.power(1-34.15*zol, 1/3) + - np.power(1-34.15*zol, 2/3))/3)-np.sqrt(3) * - np.arctan(1+2*np.power(1-34.15*zol, 1/3))/np.sqrt(3) + - 4*np.arctan(1)/np.sqrt(3), np.nan) - f = np.power(zol, 2)/(1+np.power(zol, 2)) - psi = np.where(zol < 0, (1-f)*psik+f*psic, psi) + dzol = np.minimum(d*zol, 50) + psi = -1*((1+b*zol)**1.5+b*(zol-14.28)*np.exp(-dzol)+8.525) + k = np.where(zol < 0) + x = np.sqrt(1-15*zol[k]) + psik = 2*np.log((1+x)/2) + x = np.power(1-34.15*zol[k], 1/3) + psic = (1.5*np.log((1+x+np.power(x, 2))/3)-np.sqrt(3) * + np.arctan((1+2*x)/np.sqrt(3))+4*np.arctan(1)/np.sqrt(3)) + f = np.power(zol[k], 2)/(1+np.power(zol[k], 2)) + psi[k] = (1-f)*psik+f*psic return psi # --------------------------------------------------------------------- @@ -479,31 +469,29 @@ def psiu_26(zol, meth): """ if (meth == "C30"): dzol = np.minimum(0.35*zol, 50) # stable - psi = np.where(zol >= 0, -((1+zol)+0.6667*(zol-14.28)*np.exp(-dzol) + - 8.525), np.nan) - x = np.where(zol < 0, np.power(1-15*zol, 0.25), np.nan) - psik = np.where(zol < 0, 2*np.log((1+x)/2)+np.log((1+np.power(x, 2)) / - 2)-2*np.arctan(x)+2*np.arctan(1), np.nan) - x = np.where(zol < 0, np.power(1-10.15*zol, 0.3333), np.nan) - psic = np.where(zol < 0, 1.5*np.log((1+x+np.power(x, 2))/3) - - np.sqrt(3)*np.arctan((1+2*x)/np.sqrt(3)) + - 4*np.arctan(1)/np.sqrt(3), np.nan) - f = np.power(zol, 2)/(1+np.power(zol, 2)) - psi = np.where(zol < 0, (1-f)*psik+f*psic, psi) + psi = -1*((1+zol)+0.6667*(zol-14.28)*np.exp(-dzol)+8.525) + k = np.where(zol < 0) # unstable + x = (1-15*zol[k])**0.25 + psik = (2*np.log((1+x)/2)+np.log((1+x*x)/2)-2*np.arctan(x) + + 2*np.arctan(1)) + x = (1-10.15*zol[k])**(1/3) + psic = (1.5*np.log((1+x+x*x)/3) - + np.sqrt(3)*np.arctan((1+2*x)/np.sqrt(3)) + + 4*np.arctan(1)/np.sqrt(3)) + f = zol[k]**2/(1+zol[k]**2) + psi[k] = (1-f)*psik+f*psic elif (meth == "C35"): # or meth == "C40" - dzol = np.minimum(0.35*zol, 50) # stable + dzol = np.minimum(50, 0.35*zol) # stable a, b, c, d = 0.7, 3/4, 5, 0.35 - psi = np.where(zol >= 0, -(a*zol+b*(zol-c/d)*np.exp(-dzol)+b*c/d), - np.nan) - x = np.where(zol < 0, np.power(1-15*zol, 0.25), np.nan) - psik = np.where(zol < 0, 2*np.log((1+x)/2)+np.log((1+x**2)/2) - - 2*np.arctan(x)+2*np.arctan(1), np.nan) - x = np.where(zol < 0, np.power(1-10.15*zol, 0.3333), np.nan) - psic = np.where(zol < 0, 1.5*np.log((1+x+np.power(x, 2))/3) - - np.sqrt(3)*np.arctan((1+2*x)/np.sqrt(3)) + - 4*np.arctan(1)/np.sqrt(3), np.nan) - f = np.power(zol, 2)/(1+np.power(zol, 2)) - psi = np.where(zol < 0, (1-f)*psik+f*psic, psi) + psi = -1*(a*zol+b*(zol-c/d)*np.exp(-dzol)+b*c/d) + k = np.where(zol < 0) # unstable + x = np.power(1-15*zol[k], 1/4) + psik = 2*np.log((1+x)/2)+np.log((1+x*x)/2)-2*np.arctan(x)+2*np.arctan(1) + x = np.power(1-10.15*zol[k], 1/3) + psic = (1.5*np.log((1+x+np.power(x, 2))/3)-np.sqrt(3) * + np.arctan((1+2*x)/np.sqrt(3))+4*np.arctan(1)/np.sqrt(3)) + f = np.power(zol[k], 2)/(1+np.power(zol[k], 2)) + psi[k] = (1-f)*psik+f*psic return psi #------------------------------------------------------------------------------ -- GitLab