flux_subs.py 33.9 KB
Newer Older
sbiri's avatar
sbiri committed
1
import numpy as np
2
from util_subs import (CtoK, kappa, gc, visc_air)
sbiri's avatar
sbiri committed
3

sbiri's avatar
sbiri committed
4
# ---------------------------------------------------------------------
sbiri's avatar
sbiri committed
5

sbiri's avatar
sbiri committed
6

7
def cdn_calc(u10n, usr, Ta, lat, meth="S80"):
sbiri's avatar
sbiri committed
8
    """
sbiri's avatar
sbiri committed
9
    Calculates neutral drag coefficient
sbiri's avatar
sbiri committed
10

sbiri's avatar
sbiri committed
11 12 13
    Parameters
    ----------
    u10n : float
sbiri's avatar
sbiri committed
14
        neutral 10m wind speed [m/s]
15 16
    usr : float
        friction velocity      [m/s]
sbiri's avatar
sbiri committed
17
    Ta   : float
sbiri's avatar
sbiri committed
18
        air temperature        [K]
19
    lat : float
sbiri's avatar
sbiri committed
20
        latitude               [degE]
sbiri's avatar
sbiri committed
21 22
    meth : str

sbiri's avatar
sbiri committed
23 24 25 26
    Returns
    -------
    cdn : float
    """
27
    cdn = np.zeros(Ta.shape)*np.nan
sbiri's avatar
sbiri committed
28
    if (meth == "S80"):
29
        cdn = (0.61+0.063*u10n)*0.001
sbiri's avatar
sbiri committed
30
    elif (meth == "LP82"):
sbiri's avatar
sbiri committed
31 32 33 34
       cdn = np.where(u10n < 4, 1.14*0.001,
                       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
35 36
          meth == "C35" or meth == "Beljaars"): #  or meth == "C40"
        cdn = cdn_from_roughness(u10n, usr, Ta, lat, meth)
sbiri's avatar
sbiri committed
37
    elif (meth == "YT96"):
38 39 40
        # 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 -
                        np.power(u10n, 3)*4.4e-5)/u10n, 2)
sbiri's avatar
sbiri committed
41
    elif (meth == "LY04"):
sbiri's avatar
sbiri committed
42
        cdn = np.where(u10n >= 0.5,
43 44
                       (0.142+(2.7/u10n)+(u10n/13.09))*0.001,
                       (0.142+(2.7/0.5)+(0.5/13.09))*0.001)
sbiri's avatar
sbiri committed
45
    else:
sbiri's avatar
sbiri committed
46
        print("unknown method cdn: "+meth)
sbiri's avatar
sbiri committed
47
    return cdn
sbiri's avatar
sbiri committed
48 49 50
# ---------------------------------------------------------------------


51
def cdn_from_roughness(u10n, usr, Ta, lat, meth="S88"):
sbiri's avatar
sbiri committed
52
    """
sbiri's avatar
sbiri committed
53
    Calculates neutral drag coefficient from roughness length
sbiri's avatar
sbiri committed
54

sbiri's avatar
sbiri committed
55 56 57
    Parameters
    ----------
    u10n : float
sbiri's avatar
sbiri committed
58
        neutral 10m wind speed [m/s]
59 60
    usr : float
        friction velocity      [m/s]
sbiri's avatar
sbiri committed
61
    Ta   : float
sbiri's avatar
sbiri committed
62 63
        air temperature        [K]
    lat : float                [degE]
64
        latitude
sbiri's avatar
sbiri committed
65 66
    meth : str

sbiri's avatar
sbiri committed
67 68 69 70
    Returns
    -------
    cdn : float
    """
71 72
    g = gc(lat, None)
    cdn = (0.61+0.063*u10n)*0.001
sbiri's avatar
sbiri committed
73
    zo, zc, zs = np.zeros(Ta.shape), np.zeros(Ta.shape), np.zeros(Ta.shape)
sbiri's avatar
sbiri committed
74
    for it in range(5):
sbiri's avatar
sbiri committed
75
        if (meth == "S88"):
76
            # Charnock roughness length (eq. 4 in Smith 88)
sbiri's avatar
sbiri committed
77
            zc = 0.011*np.power(usr, 2)/g
78
            #  smooth surface roughness length (eq. 6 in Smith 88)
sbiri's avatar
sbiri committed
79
            zs = 0.11*visc_air(Ta)/usr
80
            zo = zc + zs  #  eq. 7 & 8 in Smith 88
sbiri's avatar
sbiri committed
81
        elif (meth == "UA"):
sbiri's avatar
sbiri committed
82 83
            # valid for 0<u<18m/s # Zeng et al. 1998 (24)
            zo = 0.013*np.power(usr, 2)/g+0.11*visc_air(Ta)/usr
sbiri's avatar
sbiri committed
84 85
        elif (meth == "C30"):
            a = 0.011*np.ones(Ta.shape)
86
            a = np.where(u10n > 10, 0.011+(u10n-10)*(0.018-0.011)/(18-10),
sbiri's avatar
sbiri committed
87 88 89
                         np.where(u10n > 18, 0.018, a))
            zo = a*np.power(usr, 2)/g+0.11*visc_air(Ta)/usr
        elif (meth == "C35"):
90 91 92
            zo = (0.11*visc_air(Ta)/usr +
                  np.minimum(0.0017*19-0.0050, 0.0017*u10n-0.0050) *
                  np.power(usr, 2)/g)
sbiri's avatar
sbiri committed
93
        elif ((meth == "ecmwf" or meth == "Beljaars")):
94
            # eq. (3.26) p.38 over sea IFS Documentation cy46r1
95
            zo = 0.018*np.power(usr, 2)/g+0.11*visc_air(Ta)/usr
sbiri's avatar
sbiri committed
96
        else:
sbiri's avatar
sbiri committed
97
            print("unknown method for cdn_from_roughness "+meth)
98
        cdn = np.power(kappa/np.log(10/zo), 2)
99 100 101 102
    return cdn
# ---------------------------------------------------------------------


103
def cd_calc(cdn, hin, hout, psim):
sbiri's avatar
sbiri committed
104 105
    """
    Calculates drag coefficient at reference height
106 107 108 109 110

    Parameters
    ----------
    cdn : float
        neutral drag coefficient
111
    hin : float
112
        wind speed height       [m]
113
    hout : float
sbiri's avatar
sbiri committed
114
        reference height        [m]
115 116 117 118 119 120 121
    psim : float
        momentum stability function

    Returns
    -------
    cd : float
    """
122
    cd = (cdn/np.power(1+(np.sqrt(cdn)*(np.log(hin/hout)-psim))/kappa, 2))
123
    return cd
sbiri's avatar
sbiri committed
124
# ---------------------------------------------------------------------
sbiri's avatar
sbiri committed
125

sbiri's avatar
sbiri committed
126

127
def ctcqn_calc(zol, cdn, usr, zo, Ta, meth="S80"):
sbiri's avatar
sbiri committed
128
    """
sbiri's avatar
sbiri committed
129
    Calculates neutral heat and moisture exchange coefficients
sbiri's avatar
sbiri committed
130

sbiri's avatar
sbiri committed
131 132 133 134 135
    Parameters
    ----------
    zol  : float
        height over MO length
    cdn  : float
136
        neutral drag coefficient
137 138
    usr : float
        friction velocity      [m/s]
sbiri's avatar
sbiri committed
139
    zo   : float
sbiri's avatar
sbiri committed
140
        surface roughness       [m]
sbiri's avatar
sbiri committed
141
    Ta   : float
sbiri's avatar
sbiri committed
142
        air temperature         [K]
sbiri's avatar
sbiri committed
143 144
    meth : str

sbiri's avatar
sbiri committed
145 146 147 148 149 150 151
    Returns
    -------
    ctn : float
        neutral heat exchange coefficient
    cqn : float
        neutral moisture exchange coefficient
    """
sbiri's avatar
sbiri committed
152
    if (meth == "S80" or meth == "S88" or meth == "YT96"):
sbiri's avatar
sbiri committed
153
        cqn = np.ones(Ta.shape)*1.20*0.001  # from S88
sbiri's avatar
sbiri committed
154
        ctn = np.ones(Ta.shape)*1.00*0.001
sbiri's avatar
sbiri committed
155
    elif (meth == "LP82"):
sbiri's avatar
sbiri committed
156 157
        cqn = np.where((zol <= 0), 1.15*0.001, 1*0.001)
        ctn = np.where((zol <= 0), 1.13*0.001, 0.66*0.001)
sbiri's avatar
sbiri committed
158 159 160 161
    elif (meth == "LY04"):
        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"):
sbiri's avatar
sbiri committed
162
        # Zeng et al. 1998 (25)
163 164
        rr = usr*zo/visc_air(Ta)
        zoq = zo/np.exp(2.67*np.power(rr, 1/4)-2.57)
165 166 167
        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))
sbiri's avatar
sbiri committed
168 169
    elif (meth == "C30"):
        rr = zo*usr/visc_air(Ta)
170 171
        zoq = np.minimum(5e-5/np.power(rr, 0.6), 1.15e-4)  # moisture roughness
        zot = np.copy(zoq)  # temperature roughness
172 173
        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)
sbiri's avatar
sbiri committed
174 175
    elif (meth == "C35"):
        rr = zo*usr/visc_air(Ta)
176
        zoq = np.minimum(5.8e-5/np.power(rr, 0.72), 1.6e-4) # moisture roughness
177
        zot = np.copy(zoq)  # temperature roughness
178 179
        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)
sbiri's avatar
sbiri committed
180
    elif (meth == "ecmwf" or meth == "Beljaars"):
181
        # eq. (3.26) p.38 over sea IFS Documentation cy46r1
sbiri's avatar
sbiri committed
182 183
        zot = 0.40*visc_air(Ta)/usr
        zoq = 0.62*visc_air(Ta)/usr
184 185
        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)
sbiri's avatar
sbiri committed
186
    else:
sbiri's avatar
sbiri committed
187
        print("unknown method ctcqn: "+meth)
sbiri's avatar
sbiri committed
188
    return ctn, cqn
sbiri's avatar
sbiri committed
189 190 191
# ---------------------------------------------------------------------


192
def ctcq_calc(cdn, cd, ctn, cqn, hin, hout, psit, psiq):
sbiri's avatar
sbiri committed
193 194
    """
    Calculates heat and moisture exchange coefficients at reference height
sbiri's avatar
sbiri committed
195

sbiri's avatar
sbiri committed
196 197 198 199 200 201 202 203 204 205
    Parameters
    ----------
    cdn : float
        neutral drag coefficient
    cd  : float
        drag coefficient at reference height
    ctn : float
        neutral heat exchange coefficient
    cqn : float
        neutral moisture exchange coefficient
206 207
    hin : float
        original temperature/humidity sensor height [m]
208
    hout : float
209
        reference height                            [m]
sbiri's avatar
sbiri committed
210 211 212 213
    psit : float
        heat stability function
    psiq : float
        moisture stability function
sbiri's avatar
sbiri committed
214

sbiri's avatar
sbiri committed
215 216 217
    Returns
    -------
    ct : float
218
       heat exchange coefficient
sbiri's avatar
sbiri committed
219
    cq : float
220
       moisture exchange coefficient
sbiri's avatar
sbiri committed
221
    """
222
    ct = (ctn*np.sqrt(cd/cdn) /
223
          (1+ctn*((np.log(hin[1]/hout[1])-psit)/(kappa*np.sqrt(cdn)))))
224
    cq = (cqn*np.sqrt(cd/cdn) /
225
          (1+cqn*((np.log(hin[2]/hout[2])-psiq)/(kappa*np.sqrt(cdn)))))
sbiri's avatar
sbiri committed
226
    return ct, cq
sbiri's avatar
sbiri committed
227 228 229
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
230
def get_stabco(meth="S80"):
sbiri's avatar
sbiri committed
231 232
    """
    Gives the coefficients \\alpha, \\beta, \\gamma for stability functions
sbiri's avatar
sbiri committed
233 234 235 236 237 238 239 240 241 242 243

    Parameters
    ----------
    meth : str

    Returns
    -------
    coeffs : float
    """
    alpha, beta, gamma = 0, 0, 0
    if (meth == "S80" or meth == "S88" or meth == "LY04" or
sbiri's avatar
sbiri committed
244
        meth == "UA" or meth == "ecmwf" or meth == "C30" or
sbiri's avatar
sbiri committed
245
        meth == "C35" or meth == "Beljaars"):
sbiri's avatar
sbiri committed
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
        alpha, beta, gamma = 16, 0.25, 5  # Smith 1980, from Dyer (1974)
    elif (meth == "LP82"):
        alpha, beta, gamma = 16, 0.25, 7
    elif (meth == "YT96"):
        alpha, beta, gamma = 20, 0.25, 5
    else:
        print("unknown method stabco: "+meth)
    coeffs = np.zeros(3)
    coeffs[0] = alpha
    coeffs[1] = beta
    coeffs[2] = gamma
    return coeffs
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
261
def psim_calc(zol, meth="S80"):
sbiri's avatar
sbiri committed
262 263
    """
    Calculates momentum stability function
sbiri's avatar
sbiri committed
264

sbiri's avatar
sbiri committed
265 266 267 268
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
269 270
    meth : str

sbiri's avatar
sbiri committed
271 272 273 274
    Returns
    -------
    psim : float
    """
sbiri's avatar
sbiri committed
275 276
    if (meth == "ecmwf"):
        psim = psim_ecmwf(zol)
sbiri's avatar
sbiri committed
277
    elif (meth == "C30" or meth == "C35"):  # or meth == "C40"
sbiri's avatar
sbiri committed
278
        psim = psiu_26(zol, meth)
279 280
    elif (meth == "Beljaars"): # Beljaars (1997) eq. 16, 17
        psim = np.where(zol < 0, psim_conv(zol, meth), psi_Bel(zol))
sbiri's avatar
sbiri committed
281
    else:
282 283
        psim = np.where(zol < 0, psim_conv(zol, meth),
                        psim_stab(zol, meth))
sbiri's avatar
sbiri committed
284
    return psim
sbiri's avatar
sbiri committed
285 286 287
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
288
def psit_calc(zol, meth="S80"):
sbiri's avatar
sbiri committed
289 290
    """
    Calculates heat stability function
sbiri's avatar
sbiri committed
291

sbiri's avatar
sbiri committed
292 293 294 295
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
296
    meth : str
297
        parameterisation method
sbiri's avatar
sbiri committed
298

sbiri's avatar
sbiri committed
299 300 301 302
    Returns
    -------
    psit : float
    """
sbiri's avatar
sbiri committed
303
    if (meth == "ecmwf"):
304
        psit = np.where(zol < 0, psi_conv(zol, meth),
sbiri's avatar
sbiri committed
305
                        psi_ecmwf(zol))
sbiri's avatar
sbiri committed
306
    elif (meth == "C30" or meth == "C35"):
sbiri's avatar
sbiri committed
307
        psit = psit_26(zol)
308 309
    elif (meth == "Beljaars"): # Beljaars (1997) eq. 16, 17
        psit = np.where(zol < 0, psi_conv(zol, meth), psi_Bel(zol))
sbiri's avatar
sbiri committed
310
    else:
311 312
        psit = np.where(zol < 0, psi_conv(zol, meth),
                        psi_stab(zol, meth))
sbiri's avatar
sbiri committed
313
    return psit
sbiri's avatar
sbiri committed
314 315 316
# ---------------------------------------------------------------------


317
def psi_Bel(zol):
sbiri's avatar
sbiri committed
318 319
    """
    Calculates momentum/heat stability function
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337

    Parameters
    ----------
    zol : float
        height over MO length
    meth : str
        parameterisation method

    Returns
    -------
    psit : float
    """
    a, b, c, d = 0.7, 0.75, 5, 0.35
    psi = -(a*zol+b*(zol-c/d)*np.exp(-d*zol)+b*c/d)
    return psi
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
338 339 340 341
def psi_ecmwf(zol):
    """
    Calculates heat stability function for stable conditions
    for method ecmwf
sbiri's avatar
sbiri committed
342

sbiri's avatar
sbiri committed
343 344 345 346
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
347

sbiri's avatar
sbiri committed
348 349 350 351
    Returns
    -------
    psit : float
    """
352
    # eq (3.22) p. 37 IFS Documentation cy46r1
sbiri's avatar
sbiri committed
353 354
    a, b, c, d = 1, 2/3, 5, 0.35
    psit = -b*(zol-c/d)*np.exp(-d*zol)-np.power(1+(2/3)*a*zol, 1.5)-(b*c)/d+1
sbiri's avatar
sbiri committed
355
    return psit
sbiri's avatar
sbiri committed
356 357
# ---------------------------------------------------------------------

sbiri's avatar
sbiri committed
358 359

def psit_26(zol):
sbiri's avatar
sbiri committed
360 361
    """
    Computes temperature structure function as in C35
sbiri's avatar
sbiri committed
362 363 364 365 366 367 368 369 370 371 372

    Parameters
    ----------
    zol : float
        height over MO length

    Returns
    -------
    psi : float
    """
    b, d = 2/3, 0.35
373 374 375 376 377 378 379 380 381 382
    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
sbiri's avatar
sbiri committed
383 384 385 386
    return psi
# ---------------------------------------------------------------------


387
def psi_conv(zol, meth):
sbiri's avatar
sbiri committed
388 389
    """
    Calculates heat stability function for unstable conditions
sbiri's avatar
sbiri committed
390

sbiri's avatar
sbiri committed
391 392 393 394
    Parameters
    ----------
    zol : float
        height over MO length
395 396
    meth : str
        parameterisation method
sbiri's avatar
sbiri committed
397

sbiri's avatar
sbiri committed
398 399 400 401
    Returns
    -------
    psit : float
    """
402 403
    coeffs = get_stabco(meth)
    alpha, beta = coeffs[0], coeffs[1]
404 405
    xtmp = np.power(1-alpha*zol, beta)
    psit = 2*np.log((1+np.power(xtmp, 2))*0.5)
sbiri's avatar
sbiri committed
406
    return psit
sbiri's avatar
sbiri committed
407 408 409
# ---------------------------------------------------------------------


410
def psi_stab(zol, meth):
sbiri's avatar
sbiri committed
411 412
    """
    Calculates heat stability function for stable conditions
sbiri's avatar
sbiri committed
413

sbiri's avatar
sbiri committed
414 415 416 417
    Parameters
    ----------
    zol : float
        height over MO length
418 419
    meth : str
        parameterisation method
sbiri's avatar
sbiri committed
420

sbiri's avatar
sbiri committed
421 422 423 424
    Returns
    -------
    psit : float
    """
425 426
    coeffs = get_stabco(meth)
    gamma = coeffs[2]
sbiri's avatar
sbiri committed
427
    psit = -gamma*zol
sbiri's avatar
sbiri committed
428
    return psit
sbiri's avatar
sbiri committed
429 430 431
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
432 433 434
def psim_ecmwf(zol):
    """
    Calculates momentum stability function for method ecmwf
sbiri's avatar
sbiri committed
435

sbiri's avatar
sbiri committed
436 437 438 439
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
440

sbiri's avatar
sbiri committed
441 442 443 444
    Returns
    -------
    psim : float
    """
445
    # eq (3.20, 3.22) p. 37 IFS Documentation cy46r1
sbiri's avatar
sbiri committed
446
    coeffs = get_stabco("ecmwf")
447 448
    alpha, beta = coeffs[0], coeffs[1]
    xtmp = np.power(1-alpha*zol, beta)
sbiri's avatar
sbiri committed
449
    a, b, c, d = 1, 2/3, 5, 0.35
450 451 452
    psim = np.where(zol < 0, np.pi/2-2*np.arctan(xtmp) +
                    np.log((np.power(1+xtmp, 2)*(1+np.power(xtmp, 2)))/8),
                    -b*(zol-c/d)*np.exp(-d*zol)-a*zol-(b*c)/d)
sbiri's avatar
sbiri committed
453 454 455 456
    return psim
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
457
def psiu_26(zol, meth):
sbiri's avatar
sbiri committed
458 459
    """
    Computes velocity structure function C35
sbiri's avatar
sbiri committed
460 461 462 463 464 465 466 467 468 469 470

    Parameters
    ----------
    zol : float
        height over MO length

    Returns
    -------
    psi : float
    """
    if (meth == "C30"):
471
        dzol = np.minimum(0.35*zol, 50) # stable
472 473 474 475 476 477 478 479 480 481 482
        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
483
    elif (meth == "C35"): #  or meth == "C40"
484
        dzol = np.minimum(50, 0.35*zol)  # stable
sbiri's avatar
sbiri committed
485
        a, b, c, d = 0.7, 3/4, 5, 0.35
486 487 488 489 490 491 492 493 494
        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
sbiri's avatar
sbiri committed
495
    return psi
496 497
#------------------------------------------------------------------------------

sbiri's avatar
sbiri committed
498 499


500
def psim_conv(zol, meth):
sbiri's avatar
sbiri committed
501 502
    """
    Calculates momentum stability function for unstable conditions
sbiri's avatar
sbiri committed
503

sbiri's avatar
sbiri committed
504 505 506 507
    Parameters
    ----------
    zol : float
        height over MO length
508 509
    meth : str
        parameterisation method
sbiri's avatar
sbiri committed
510

sbiri's avatar
sbiri committed
511 512 513 514
    Returns
    -------
    psim : float
    """
515 516
    coeffs = get_stabco(meth)
    alpha, beta = coeffs[0], coeffs[1]
517 518
    xtmp = np.power(1-alpha*zol, beta)
    psim = (2*np.log((1+xtmp)*0.5)+np.log((1+np.power(xtmp, 2))*0.5) -
sbiri's avatar
sbiri committed
519
            2*np.arctan(xtmp)+np.pi/2)
sbiri's avatar
sbiri committed
520
    return psim
sbiri's avatar
sbiri committed
521 522 523
# ---------------------------------------------------------------------


524
def psim_stab(zol, meth):
sbiri's avatar
sbiri committed
525 526
    """
    Calculates momentum stability function for stable conditions
sbiri's avatar
sbiri committed
527

sbiri's avatar
sbiri committed
528 529 530 531
    Parameters
    ----------
    zol : float
        height over MO length
532 533
    meth : str
        parameterisation method
sbiri's avatar
sbiri committed
534

sbiri's avatar
sbiri committed
535 536 537 538
    Returns
    -------
    psim : float
    """
539 540
    coeffs = get_stabco(meth)
    gamma = coeffs[2]
sbiri's avatar
sbiri committed
541 542 543 544 545
    psim = -gamma*zol
    return psim
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
546 547 548 549
def cs_C35(sst, qsea, rho, Rs, Rnl, cp, lv, delta, usr, tsr, qsr, lat):
    """
    Computes cool skin
    following COARE3.5 (Fairall et al. 1996, Edson et al. 2013)
sbiri's avatar
sbiri committed
550

sbiri's avatar
sbiri committed
551 552 553
    Parameters
    ----------
    sst : float
sbiri's avatar
sbiri committed
554
        sea surface temperature      [K]
sbiri's avatar
sbiri committed
555
    qsea : float
sbiri's avatar
sbiri committed
556
        specific humidity over sea   [g/kg]
sbiri's avatar
sbiri committed
557
    rho : float
sbiri's avatar
sbiri committed
558
        density of air               [kg/m^3]
sbiri's avatar
sbiri committed
559
    Rs : float
sbiri's avatar
sbiri committed
560
        downward shortwave radiation [Wm-2]
sbiri's avatar
sbiri committed
561
    Rnl : float
sbiri's avatar
sbiri committed
562
        upwelling IR radiation       [Wm-2]
sbiri's avatar
sbiri committed
563
    cp : float
sbiri's avatar
sbiri committed
564
       specific heat of air at constant pressure [J/K/kg]
sbiri's avatar
sbiri committed
565
    lv : float
sbiri's avatar
sbiri committed
566 567 568
       latent heat of vaporization   [J/kg]
    delta : float
       cool skin thickness           [m]
sbiri's avatar
sbiri committed
569
    usr : float
sbiri's avatar
sbiri committed
570
       friction velocity             [m/s]
sbiri's avatar
sbiri committed
571
    tsr : float
sbiri's avatar
sbiri committed
572
       star temperature              [K]
sbiri's avatar
sbiri committed
573
    qsr : float
sbiri's avatar
sbiri committed
574
       star humidity                 [g/kg]
sbiri's avatar
sbiri committed
575
    lat : float
sbiri's avatar
sbiri committed
576
       latitude                      [degE]
sbiri's avatar
sbiri committed
577

sbiri's avatar
sbiri committed
578 579 580
    Returns
    -------
    dter : float
sbiri's avatar
sbiri committed
581
        cool skin correction         [K]
sbiri's avatar
sbiri committed
582
    dqer : float
sbiri's avatar
sbiri committed
583 584 585
       humidity corrction            [g/kg]
    delta : float
       cool skin thickness           [m]
sbiri's avatar
sbiri committed
586
    """
sbiri's avatar
sbiri committed
587 588
    # coded following Saunders (1967) with lambda = 6
    g = gc(lat, None)
sbiri's avatar
sbiri committed
589 590
    if (np.nanmin(sst) > 200):  # if sst in Kelvin convert to Celsius
        sst = sst-CtoK
sbiri's avatar
sbiri committed
591 592 593 594
    # ************  cool skin constants  *******
    # density of water, specific heat capacity of water, water viscosity,
    # thermal conductivity of water
    rhow, cpw, visw, tcw = 1022, 4000, 1e-6, 0.6
sbiri's avatar
sbiri committed
595
    aw = 2.1e-5*np.power(np.maximum(sst+3.2, 0), 0.79)
sbiri's avatar
sbiri committed
596 597
    bigc = 16*g*cpw*np.power(rhow*visw, 3)/(np.power(tcw, 2)*np.power(rho, 2))
    wetc = 0.622*lv*qsea/(287.1*np.power(sst+273.16, 2))
sbiri's avatar
sbiri committed
598
    Rns = 0.945*Rs  # albedo correction
sbiri's avatar
sbiri committed
599 600 601 602 603 604
    shf = -rho*cp*usr*tsr
    lhf = -rho*lv*usr*qsr
    Qnsol = Rnl+shf+lhf
    fs = 0.065+11*delta-6.6e-5/delta*(1-np.exp(-delta/8.0e-4))
    qcol = Qnsol-Rns*fs
    alq = aw*qcol+0.026*lhf*cpw/lv
sbiri's avatar
sbiri committed
605
    xlamx = 6*np.ones(sst.shape)
sbiri's avatar
sbiri committed
606
    xlamx = np.where(alq > 0, 6/(1+(bigc*alq/usr**4)**0.75)**0.333, 6)
sbiri's avatar
sbiri committed
607
    delta = np.where(alq > 0, xlamx*visw/(np.sqrt(rho/rhow)*usr),
sbiri's avatar
sbiri committed
608 609
                   np.where(xlamx*visw/(np.sqrt(rho/rhow)*usr) > 0.01, 0.01,
                   xlamx*visw/(np.sqrt(rho/rhow)*usr)))
sbiri's avatar
sbiri committed
610
    dter = qcol*delta/tcw
sbiri's avatar
sbiri committed
611
    dqer = wetc*dter
sbiri's avatar
sbiri committed
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
    return dter, dqer, delta
# ---------------------------------------------------------------------


def delta(aw, Qnsol, usr, lat):
    """
    Computes the thickness (m) of the viscous skin layer.
    Based on Fairall et al., 1996 and cited in IFS Documentation Cy46r1
    eq. 8.155 p. 164

    Parameters
    ----------
    aw : float
        thermal expansion coefficient of sea-water  [1/K]
    Qnsol : float
        part of the net heat flux actually absorbed in the warm layer [W/m^2]
    usr : float
        friction velocity in the air (u*) [m/s]
    lat : float
       latitude                      [degE]

    Returns
    -------
    delta : float
        the thickness (m) of the viscous skin layer

    """
    rhow, visw, tcw = 1025, 1e-6, 0.6
    # u* in the water
    usr_w = np.maximum(usr, 1e-4)*np.sqrt(1.2/rhow) # rhoa=1.2
    rcst_cs = 16*gc(lat)*np.power(visw, 3)/np.power(tcw, 2)
    lm = 6/np.power(1+np.power(np.maximum(Qnsol*aw*rcst_cs /
                                          np.power(usr_w, 4), 0), 3/4),
                    1/3)
    ztmp = visw/usr_w
    delta = np.where(Qnsol > 0, np.minimum(6*ztmp, 0.007), lm*ztmp)
    return delta
# ---------------------------------------------------------------------


def cs_ecmwf(rho, Rs, Rnl, cp, lv, usr, tsr, qsr, sst, lat):
    """
    cool skin adjustment based on IFS Documentation cy46r1

    Parameters
    ----------
    rho : float
        density of air               [kg/m^3]
    Rs : float
        downward solar radiation [Wm-2]
    Rnl : float
        net thermal radiaion     [Wm-2]
    cp : float
       specific heat of air at constant pressure [J/K/kg]
    lv : float
       latent heat of vaporization   [J/kg]
    usr : float
       friction velocity         [m/s]
    tsr : float
       star temperature              [K]
    qsr : float
       star humidity                 [g/kg]
    sst : float
        sea surface temperature  [K]
    lat : float
       latitude                  [degE]

    Returns
    -------
    dtc : float
        cool skin temperature correction [K]

    """
    if (np.nanmin(sst) < 200):  # if sst in Celsius convert to Kelvin
        sst = sst+CtoK
    aw = np.maximum(1e-5, 1e-5*(sst-CtoK))
    Rns = 0.945*Rs  # (1-0.066)*Rs net solar radiation (albedo correction)
    shf = -rho*cp*usr*tsr
    lhf = -rho*lv*usr*qsr
    Qnsol = shf+lhf+Rnl  # eq. 8.152
    d = delta(aw, Qnsol, usr, lat)
    for jc in range(10): # because implicit in terms of delta...
        # # fraction of the solar radiation absorbed in layer delta eq. 8.153
        # and Eq.(5) Zeng & Beljaars, 2005
        fs = 0.065+11*d-6.6e-5/d*(1-np.exp(-d/8e-4))
        Q = Qnsol-fs*Rns
        d = delta(aw, Q, usr, lat)
    dtc = Q*d/0.6  # (rhow*cw*kw)eq. 8.151
    return dtc
sbiri's avatar
sbiri committed
701 702 703
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
704 705 706 707 708 709 710 711 712 713 714
def wl_ecmwf(rho, Rs, Rnl, cp, lv, usr, tsr, qsr, sst, skt, dtc, lat):
    """
    warm layer correction following IFS Documentation cy46r1
    and aerobulk (Brodeau et al., 2016)

    Parameters
    ----------
    rho : float
        density of air               [kg/m^3]
    Rs : float
        downward solar radiation    [Wm-2]
sbiri's avatar
sbiri committed
715
    Rnl : float
sbiri's avatar
sbiri committed
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746
        net thermal radiation  [Wm-2]
    cp : float
       specific heat of air at constant pressure [J/K/kg]
    lv : float
       latent heat of vaporization   [J/kg]
    usr : float
        friction velocity           [m/s]
    tsr : float
       star temperature              [K]
    qsr : float
       star humidity                 [g/kg]
    sst : float
        bulk sst                    [K]
    skt : float
        skin sst from previous step [K]
    dtc : float
        cool skin correction        [K]
    lat : float
        latitude                    [degE]

    Returns
    -------
    dtwl : float
        warm layer correction       [K]

    """
    if (np.nanmin(sst) < 200):  # if sst in Celsius convert to Kelvin
        sst = sst+CtoK
    rhow, cpw, visw, rd0 = 1025, 4190, 1e-6, 3
    Rns = 0.945*Rs
    ## Previous value of dT / warm-layer, adapted to depth:
sbiri's avatar
sbiri committed
747
    aw = 2.1e-5*np.power(np.maximum(sst-CtoK+3.2, 0), 0.79) # thermal expansion coefficient of sea-water (SST accurate enough#)
sbiri's avatar
sbiri committed
748 749 750 751 752 753 754 755 756 757 758
    # *** Rd = Fraction of solar radiation absorbed in warm layer (-)
    a1, a2, a3 = 0.28, 0.27, 0.45
    b1, b2, b3 = -71.5, -2.8, -0.06 # [m-1]
    Rd = 1-(a1*np.exp(b1*rd0)+a2*np.exp(b2*rd0)+a3*np.exp(b3*rd0))
    shf = -rho*cp*usr*tsr
    lhf = -rho*lv*usr*qsr
    Qnsol = shf+lhf+Rnl
    usrw  = np.maximum(usr, 1e-4 )*np.sqrt(1.2/rhow)   # u* in the water
    zc3 = rd0*kappa*gc(lat)/np.power(1.2/rhow, 3/2)
    zc4 = (0.3+1)*kappa/rd0
    zc5 = (0.3+1)/(0.3*rd0)
sbiri's avatar
sbiri committed
759
    for jwl in range(10): # itteration to solve implicitely equation for warm layer
sbiri's avatar
sbiri committed
760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843
        dsst = skt-sst+dtc
        ## Buoyancy flux and stability parameter (zdl = -z/L) in water
        ZSRD = (Qnsol-Rns*Rd)/(rhow*cpw)
        ztmp = np.maximum(dsst, 0)
        zdl = np.where(ZSRD > 0, 2*(np.power(usrw, 2) *
                                    np.sqrt(ztmp/(5*rd0*gc(lat)*aw/visw)))+ZSRD,
                       np.power(usrw, 2)*np.sqrt(ztmp/(5*rd0*gc(lat)*aw/visw)))
        usr = np.maximum(usr, 1e-4 )
        zdL = zc3*aw*zdl/np.power(usr, 3)
        ## Stability function Phi_t(-z/L) (zdL is -z/L) :
        zphi = np.where(zdL > 0, (1+(5*zdL+4*np.power(zdL, 2)) /
                                  (1+3*zdL+0.25*np.power(zdL, 2)) +
                                  2/np.sqrt(1-16*(-np.abs(zdL)))),
                        1/np.sqrt(1-16*(-np.abs(zdL))))
        zz = zc4*(usrw)/zphi
        zz = np.maximum(np.abs(zz) , 1e-4)*np.sign(zz)
        dtwl = np.maximum(0, (zc5*ZSRD)/zz)
    return dtwl
#----------------------------------------------------------------------


def cs_Beljaars(rho, Rs, Rnl, cp, lv, usr, tsr, qsr, lat, Qs):
    """
    cool skin adjustment based on Beljaars (1997)
    air-sea interaction in the ECMWF model

    Parameters
    ----------
    rho : float
        density of air           [kg/m^3]
    Rs : float
        downward solar radiation [Wm-2]
    Rnl : float
        net thermal radiaion     [Wm-2]
    cp : float
       specific heat of air at constant pressure [J/K/kg]
    lv : float
       latent heat of vaporization   [J/kg]
    usr : float
       friction velocity         [m/s]
    tsr : float
       star temperature              [K]
    qsr : float
       star humidity                 [g/kg]
    lat : float
       latitude                  [degE]
    Qs : float
      radiation balance

    Returns
    -------
    Qs : float
      radiation balance
    dtc : float
        cool skin temperature correction [K]

    """
    g = gc(lat, None)
    tcw = 0.6       # thermal conductivity of water (at 20C) [W/m/K]
    visw = 1e-6     # kinetic viscosity of water [m^2/s]
    rhow = 1025     # Density of sea-water [kg/m^3]
    cpw = 4190      # specific heat capacity of water
    aw = 3e-4       # thermal expansion coefficient [K-1]
    Rns = 0.945*Rs  # net solar radiation (albedo correction)
    shf = -rho*cp*usr*tsr
    lhf = -rho*lv*usr*qsr
    Q = Rnl+shf+lhf+Qs
    xt = (16*Q*g*aw*cpw*np.power(rhow*visw, 3))/(np.power(usr, 4) *
                                                np.power(rho*tcw, 2))
    xt1 = 1+np.power(xt, 3/4)
    # Saunders const  eq. 22
    ls = np.where(Q > 0, 6/np.power(xt1, 1/3), 6)
    delta = np.where(Q > 0 , (ls*visw)/(np.sqrt(rho/rhow)*usr),
                     np.where((ls*visw)/(np.sqrt(rho/rhow)*usr) > 0.01, 0.01,
                              (ls*visw)/(np.sqrt(rho/rhow)*usr)))  # eq. 21
    # fraction of the solar radiation absorbed in layer delta
    fc = 0.065+11*delta-6.6e-5*(1-np.exp(-delta/0.0008))/delta
    Qs = fc*Rns
    Q = Rnl+shf+lhf+Qs
    dtc = Q*delta/tcw
    return Qs, dtc
#----------------------------------------------------------------------


sbiri's avatar
sbiri committed
844
def get_gust(beta, Ta, usr, tsrv, zi, lat):
sbiri's avatar
sbiri committed
845 846
    """
    Computes gustiness
sbiri's avatar
sbiri committed
847

sbiri's avatar
sbiri committed
848 849 850 851 852
    Parameters
    ----------
    beta : float
        constant
    Ta : float
sbiri's avatar
sbiri committed
853
        air temperature   [K]
sbiri's avatar
sbiri committed
854
    usr : float
sbiri's avatar
sbiri committed
855
        friction velocity [m/s]
sbiri's avatar
sbiri committed
856
    tsrv : float
sbiri's avatar
sbiri committed
857
        star virtual temperature of air [K]
sbiri's avatar
sbiri committed
858
    zi : int
sbiri's avatar
sbiri committed
859
        scale height of the boundary layer depth [m]
sbiri's avatar
sbiri committed
860 861
    lat : float
        latitude
sbiri's avatar
sbiri committed
862

sbiri's avatar
sbiri committed
863 864
    Returns
    -------
sbiri's avatar
sbiri committed
865
    ug : float        [m/s]
sbiri's avatar
sbiri committed
866
    """
867
    if (np.nanmax(Ta) < 200):  # convert to K if in Celsius
sbiri's avatar
sbiri committed
868 869
        Ta = Ta+273.16
    g = gc(lat, None)
sbiri's avatar
sbiri committed
870
    Bf = (-g/Ta)*usr*tsrv
sbiri's avatar
sbiri committed
871 872 873 874 875 876
    ug = np.ones(np.shape(Ta))*0.2
    ug = np.where(Bf > 0, beta*np.power(Bf*zi, 1/3), 0.2)
    return ug
# ---------------------------------------------------------------------


877 878
def get_L(L, lat, usr, tsr, qsr, hin, Ta, sst, qair, qsea, wind, monob, psim,
          meth):
sbiri's avatar
sbiri committed
879 880 881 882 883 884 885
    """
    calculates Monin-Obukhov length and virtual star temperature

    Parameters
    ----------
    L : int
        Monin-Obukhov length definition options
sbiri's avatar
sbiri committed
886 887
        "S80"  : default for S80, S88, LP82, YT96 and LY04
        "ecmwf" : following ecmwf (IFS Documentation cy46r1), default for ecmwf
sbiri's avatar
sbiri committed
888 889 890 891 892 893 894 895 896 897
    lat : float
        latitude
    usr : float
        friction wind speed (m/s)
    tsr : float
        star temperature (K)
    qsr : float
        star specific humidity (g/kg)
    t10n : float
        neutral temperature at 10m (K)
sbiri's avatar
sbiri committed
898
    hin : float
sbiri's avatar
sbiri committed
899 900 901 902 903
        sensor heights (m)
    Ta : float
        air temperature (K)
    sst : float
        sea surface temperature (K)
sbiri's avatar
sbiri committed
904 905 906
    qair : float
        air specific humidity (g/kg)
    qsea : float
sbiri's avatar
sbiri committed
907
        specific humidity at sea surface (g/kg)
sbiri's avatar
sbiri committed
908 909
    q10n : float
        neutral specific humidity at 10m (g/kg)
sbiri's avatar
sbiri committed
910 911 912 913 914
    wind : float
        wind speed (m/s)
    monob : float
        Monin-Obukhov length from previous iteration step (m)
    meth : str
915
        bulk parameterisation method option: "S80", "S88", "LP82", "YT96",
sbiri's avatar
sbiri committed
916
        "UA", "LY04", "C30", "C35", "ecmwf", "Beljaars"
sbiri's avatar
sbiri committed
917 918 919

    Returns
    -------
920 921 922 923
    tsrv : float
        virtual star temperature (K)
    monob : float
        M-O length (m)
sbiri's avatar
sbiri committed
924 925

    """
sbiri's avatar
sbiri committed
926
    g = gc(lat)
927
    Rb = np.empty(sst.shape)
928 929 930 931 932 933 934 935 936 937
    # as in NCAR, LY04
    tsrv = tsr*(1+0.6077*qair)+0.6077*Ta*qsr
    # from eq. 3.24 ifs Cy46r1 pp. 37
    thvs = sst*(1+0.6077*qsea) # virtual SST
    dthv = (Ta-sst)*(1+0.6077*qair)+0.6077*Ta*(qair-qsea)
    tv = 0.5*(thvs+Ta*(1+0.6077*qair)) # estimate tv within surface layer
    # adjust wind to T sensor's height
    uz = (wind-usr/kappa*(np.log(hin[0]/hin[1])-psim +
                            psim_calc(hin[1]/monob, meth)))
    Rb = g*dthv*hin[1]/(tv*uz*uz)
938
    if (L == "S80"):
sbiri's avatar
sbiri committed
939 940 941
        temp = (g*kappa*tsrv /
                np.maximum(np.power(usr, 2)*Ta*(1+0.6077*qair), 1e-9))
        temp = np.minimum(np.abs(temp), 200)*np.sign(temp)
sbiri's avatar
sbiri committed
942
        temp = np.minimum(np.abs(temp), 10/hin[0])*np.sign(temp)
sbiri's avatar
sbiri committed
943 944
        monob = 1/np.copy(temp)
    elif (L == "ecmwf"):
sbiri's avatar
sbiri committed
945 946
        zo = (0.11*visc_air(Ta)/usr+0.018*np.power(usr, 2)/g)
        zot = 0.40*visc_air(Ta)/usr
947
        zol = (Rb*(np.power(np.log((hin[1]+zo)/zo)-psim_calc((hin[1]+zo) /
sbiri's avatar
sbiri committed
948 949
                                                              monob, meth) +
                            psim_calc(zo/monob, meth), 2) /
950 951
                   (np.log((hin[1]+zo)/zot) -
                    psit_calc((hin[1]+zo)/monob, meth) +
sbiri's avatar
sbiri committed
952
                    psit_calc(zot/monob, meth))))
sbiri's avatar
sbiri committed
953
        monob = hin[1]/zol
954
    return tsrv, monob, Rb
sbiri's avatar
sbiri committed
955 956 957
#------------------------------------------------------------------------------


sbiri's avatar
sbiri committed
958
def get_strs(hin, monob, wind, zo, zot, zoq, dt, dq, dter, dqer, dtwl, ct, cq,
sbiri's avatar
sbiri committed
959
             cskin, wl, meth):
960 961 962 963 964
    """
    calculates star wind speed, temperature and specific humidity

    Parameters
    ----------
sbiri's avatar
sbiri committed
965
    hin : float
sbiri's avatar
sbiri committed
966
        sensor heights [m]
967
    monob : float
sbiri's avatar
sbiri committed
968
        M-O length     [m]
969
    wind : float
sbiri's avatar
sbiri committed
970
        wind speed     [m/s]
971
    zo : float
sbiri's avatar
sbiri committed
972
        momentum roughness length    [m]
973
    zot : float
sbiri's avatar
sbiri committed
974
        temperature roughness length [m]
975
    zoq : float
sbiri's avatar
sbiri committed
976
        moisture roughness length    [m]
977
    dt : float
sbiri's avatar
sbiri committed
978
        temperature difference       [K]
979
    dq : float
sbiri's avatar
sbiri committed
980
        specific humidity difference [g/kg]
981
    dter : float
sbiri's avatar
sbiri committed
982
        cskin temperature adjustment [K]
983
    dqer : float
sbiri's avatar
sbiri committed
984 985 986
        cskin q adjustment           [g/kg]
    dtwl : float
        warm layer temperature adjustment [K]
987 988 989 990 991 992
    ct : float
        temperature exchange coefficient
    cq : float
        moisture exchange coefficient
    cskin : int
        cool skin adjustment switch
sbiri's avatar
sbiri committed
993 994
    wl : int
        warm layer correction switch
995 996
    meth : str
        bulk parameterisation method option: "S80", "S88", "LP82", "YT96", "UA",
sbiri's avatar
sbiri committed
997
        "LY04", "C30", "C35", "ecmwf", "Beljaars"
998 999 1000 1001

    Returns
    -------
    usr : float
sbiri's avatar
sbiri committed
1002
        friction wind speed [m/s]
1003
    tsr : float
sbiri's avatar
sbiri committed
1004
        star temperature    [K]
1005
    qsr : float
sbiri's avatar
sbiri committed
1006
        star specific humidity [g/kg]
1007 1008 1009

    """
    if (meth == "UA"):
sbiri's avatar
sbiri committed
1010
        usr = np.where(hin[0]/monob <= -1.574, kappa*wind /
1011 1012
                       (np.log(-1.574*monob/zo)-psim_calc(-1.574, meth) +
                        psim_calc(zo/monob, meth) +
sbiri's avatar
sbiri committed
1013
                        1.14*(np.power(-hin[0]/monob, 1/3) -
1014
                        np.power(1.574, 1/3))),
sbiri's avatar
sbiri committed
1015 1016 1017
                       np.where(hin[0]/monob < 0, kappa*wind /
                                (np.log(hin[0]/zo) -
                                 psim_calc(hin[0]/monob, meth) +
1018
                                 psim_calc(zo/monob, meth)),
sbiri's avatar
sbiri committed
1019 1020 1021
                                np.where(hin[0]/monob <= 1, kappa*wind /
                                         (np.log(hin[0]/zo) +
                                          5*hin[0]/monob-5*zo/monob),
1022 1023
                                         kappa*wind/(np.log(monob/zo)+5 -
                                                     5*zo/monob +
sbiri's avatar
sbiri committed
1024 1025
                                                     5*np.log(hin[0]/monob) +
                                                     hin[0]/monob-1))))
1026
                                # Zeng et al. 1998 (7-10)
sbiri's avatar
sbiri committed
1027
        tsr = np.where(hin[1]/monob < -0.465, kappa*(dt+dter*cskin-dtwl*wl) /
1028 1029
                       (np.log((-0.465*monob)/zot) -
                        psit_calc(-0.465, meth)+0.8*(np.power(0.465, -1/3) -
sbiri's avatar
sbiri committed
1030 1031 1032 1033
                        np.power(-hin[1]/monob, -1/3))),
                       np.where(hin[1]/monob < 0, kappa*(dt+dter*cskin-dtwl*wl) /
                                (np.log(hin[1]/zot) -
                                 psit_calc(hin[1]/monob, meth) +
1034
                                 psit_calc(zot/monob, meth)),
sbiri's avatar
sbiri committed
1035
                                np.where(hin[1]/monob <= 1,
sbiri's avatar
sbiri committed
1036
                                         kappa*(dt+dter*cskin-dtwl*wl) /
sbiri's avatar
sbiri committed
1037 1038
                                         (np.log(hin[1]/zot) +
                                          5*hin[1]/monob-5*zot/monob),
sbiri's avatar
sbiri committed
1039
                                         kappa*(dt+dter*cskin-dtwl*wl) /
1040
                                         (np.log(monob/zot)+5 -
sbiri's avatar
sbiri committed
1041 1042
                                          5*zot/monob+5*np.log(hin[1]/monob) +
                                          hin[1]/monob-1))))
1043
                                # Zeng et al. 1998 (11-14)
sbiri's avatar
sbiri committed
1044
        qsr = np.where(hin[2]/monob < -0.465, kappa*(dq+dqer*cskin) /
1045 1046 1047
                       (np.log((-0.465*monob)/zoq) -
                        psit_calc(-0.465, meth)+psit_calc(zoq/monob, meth) +
                        0.8*(np.power(0.465, -1/3) -
sbiri's avatar
sbiri committed
1048 1049 1050 1051
                             np.power(-hin[2]/monob, -1/3))),
                       np.where(hin[2]/monob < 0,
                                kappa*(dq+dqer*cskin)/(np.log(hin[1]/zot) -
                                psit_calc(hin[2]/monob, meth) +
1052
                                psit_calc(zoq/monob, meth)),
sbiri's avatar
sbiri committed
1053
                                np.where(hin[2]/monob <= 1,
1054
                                         kappa*(dq+dqer*cskin) /
sbiri's avatar
sbiri committed
1055
                                         (np.log(hin[1]/zoq)+5*hin[2]/monob -
1056 1057 1058
                                          5*zoq/monob),
                                         kappa*(dq+dqer*cskin)/
                                         (np.log(monob/zoq)+5-5*zoq/monob +
sbiri's avatar
sbiri committed
1059 1060
                                          5*np.log(hin[2]/monob) +
                                          hin[2]/monob-1))))
sbiri's avatar
sbiri committed
1061
    elif (meth == "C30" or meth == "C35"):
sbiri's avatar
sbiri committed
1062 1063 1064 1065 1066
        usr = (wind*kappa/(np.log(hin[0]/zo)-psiu_26(hin[0]/monob, meth)))
        tsr = ((dt+dter*cskin-dtwl*wl)*(kappa/(np.log(hin[1]/zot) -
                                       psit_26(hin[1]/monob))))
        qsr = ((dq+dqer*cskin)*(kappa/(np.log(hin[2]/zoq) -
                                       psit_26(hin[2]/monob))))
1067
    else:
sbiri's avatar
sbiri committed
1068
        usr = (wind*kappa/(np.log(hin[0]/zo)-psim_calc(hin[0]/monob, meth)))
sbiri's avatar
sbiri committed
1069
        tsr = ct*wind*(dt+dter*cskin-dtwl*wl)/usr
1070 1071
        qsr = cq*wind*(dq+dqer*cskin)/usr
    return usr, tsr, qsr
sbiri's avatar
sbiri committed
1072
# ---------------------------------------------------------------------