flux_subs.py 36.3 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

sbiri's avatar
sbiri committed
7
def cdn_calc(u10n, Ta, Tp, lat, meth="S80"):
sbiri's avatar
sbiri committed
8
    """
9
    Calculates 10m 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]
sbiri's avatar
sbiri committed
15
    Ta   : float
sbiri's avatar
sbiri committed
16
        air temperature        [K]
sbiri's avatar
sbiri committed
17 18
    Tp   : float
        wave period
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"):
sbiri's avatar
sbiri committed
29 30
        cdn = np.where(u10n <= 3, (0.61+0.567/u10n)*0.001,
                       (0.61+0.063*u10n)*0.001)
sbiri's avatar
sbiri committed
31
    elif (meth == "LP82"):
sbiri's avatar
sbiri committed
32 33 34 35
       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
36
          meth == "C35" or meth == "C40" or meth == "Beljaars"):
sbiri's avatar
sbiri committed
37 38
        cdn = cdn_from_roughness(u10n, Ta, None, lat, meth)
    elif (meth == "YT96"):
39
        # for u<3 YT96 convert usr in eq. 21 to cdn
sbiri's avatar
sbiri committed
40
        cdn = np.where((u10n < 6) & (u10n >= 3),
41 42 43 44 45
                        (0.29+3.1/u10n+7.7/np.power(u10n, 2))*0.001,
                        np.where((u10n >= 6),
                        (0.60 + 0.070*u10n)*0.001,
                        np.power((0.10038+u10n*2.17e-3 +
                                  np.power(u10n, 2)*2.78e-3 -
sbiri's avatar
sbiri committed
46
                                  np.power(u10n, 3)*4.4e-5)/u10n, 2)))
sbiri's avatar
sbiri committed
47
    elif (meth == "LY04"):
sbiri's avatar
sbiri committed
48
        cdn = np.where(u10n >= 0.5,
49 50
                       (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
51
    else:
sbiri's avatar
sbiri committed
52
        print("unknown method cdn: "+meth)
sbiri's avatar
sbiri committed
53
    return cdn
sbiri's avatar
sbiri committed
54 55 56
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
57
def cdn_from_roughness(u10n, Ta, Tp, lat, meth="S88"):
sbiri's avatar
sbiri committed
58
    """
59
    Calculates 10m neutral drag coefficient from roughness length
sbiri's avatar
sbiri committed
60

sbiri's avatar
sbiri committed
61 62 63
    Parameters
    ----------
    u10n : float
sbiri's avatar
sbiri committed
64
        neutral 10m wind speed [m/s]
sbiri's avatar
sbiri committed
65
    Ta   : float
sbiri's avatar
sbiri committed
66
        air temperature        [K]
sbiri's avatar
sbiri committed
67 68
    Tp   : float
        wave period
sbiri's avatar
sbiri committed
69
    lat : float                [degE]
70
        latitude
sbiri's avatar
sbiri committed
71 72
    meth : str

sbiri's avatar
sbiri committed
73 74 75 76
    Returns
    -------
    cdn : float
    """
sbiri's avatar
sbiri committed
77
    g, tol = gc(lat, None), 0.000001
sbiri's avatar
sbiri committed
78 79 80
    cdn, usr = np.zeros(Ta.shape), np.zeros(Ta.shape)
    cdnn = (0.61+0.063*u10n)*0.001
    zo, zc, zs = np.zeros(Ta.shape), np.zeros(Ta.shape), np.zeros(Ta.shape)
sbiri's avatar
sbiri committed
81 82
    for it in range(5):
        cdn = np.copy(cdnn)
sbiri's avatar
sbiri committed
83
        usr = np.sqrt(cdn*u10n**2)
sbiri's avatar
sbiri committed
84
        if (meth == "S88"):
85
            # Charnock roughness length (eq. 4 in Smith 88)
sbiri's avatar
sbiri committed
86
            zc = 0.011*np.power(usr, 2)/g
87
            #  smooth surface roughness length (eq. 6 in Smith 88)
sbiri's avatar
sbiri committed
88
            zs = 0.11*visc_air(Ta)/usr
89
            zo = zc + zs  #  eq. 7 & 8 in Smith 88
sbiri's avatar
sbiri committed
90
        elif (meth == "UA"):
sbiri's avatar
sbiri committed
91 92
            # 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
93 94
        elif (meth == "C30"):
            a = 0.011*np.ones(Ta.shape)
95
            a = np.where(u10n > 10, 0.011+(u10n-10)*(0.018-0.011)/(18-10),
sbiri's avatar
sbiri committed
96 97 98 99
                         np.where(u10n > 18, 0.018, a))
            zo = a*np.power(usr, 2)/g+0.11*visc_air(Ta)/usr
        elif (meth == "C35"):
            a = 0.011*np.ones(Ta.shape)
100
            a = np.where(u10n > 19, 0.0017*19-0.0050, 0.0017*u10n-0.0050)
sbiri's avatar
sbiri committed
101 102 103 104 105
            zo = 0.11*visc_air(Ta)/usr+a*np.power(usr, 2)/g
        elif (meth == "C40"):
            a = 0.011*np.ones(Ta.shape)
            a = np.where(u10n > 22, 0.0016*22-0.0035, 0.0016*u10n-0.0035)
            zo = a*np.power(usr, 2)/g+0.11*visc_air(Ta)/usr # surface roughness
sbiri's avatar
sbiri committed
106
        elif ((meth == "ecmwf" or meth == "Beljaars")):
107
            # eq. (3.26) p.38 over sea IFS Documentation cy46r1
108
            zo = 0.018*np.power(usr, 2)/g+0.11*visc_air(Ta)/usr
sbiri's avatar
sbiri committed
109
        else:
sbiri's avatar
sbiri committed
110
            print("unknown method for cdn_from_roughness "+meth)
sbiri's avatar
sbiri committed
111
        cdnn = (kappa/np.log(10/zo))**2
sbiri's avatar
sbiri committed
112
    cdn = np.where(np.abs(cdnn-cdn) < tol, cdnn, np.nan)
113 114 115 116
    return cdn
# ---------------------------------------------------------------------


117
def cd_calc(cdn, hin, hout, psim):
sbiri's avatar
sbiri committed
118 119
    """
    Calculates drag coefficient at reference height
120 121 122 123 124

    Parameters
    ----------
    cdn : float
        neutral drag coefficient
125
    hin : float
sbiri's avatar
sbiri committed
126
        original sensor height  [m]
127
    hout : float
sbiri's avatar
sbiri committed
128
        reference height        [m]
129 130 131 132 133 134 135
    psim : float
        momentum stability function

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

sbiri's avatar
sbiri committed
140

sbiri's avatar
sbiri committed
141
def ctcqn_calc(zol, cdn, u10n, zo, Ta, meth="S80"):
sbiri's avatar
sbiri committed
142
    """
143
    Calculates 10m neutral heat and moisture exchange coefficients
sbiri's avatar
sbiri committed
144

sbiri's avatar
sbiri committed
145 146 147 148 149
    Parameters
    ----------
    zol  : float
        height over MO length
    cdn  : float
150
        neutral drag coefficient
sbiri's avatar
sbiri committed
151
    u10n : float
sbiri's avatar
sbiri committed
152
        neutral 10m wind speed  [m/s]
sbiri's avatar
sbiri committed
153
    zo   : float
sbiri's avatar
sbiri committed
154
        surface roughness       [m]
sbiri's avatar
sbiri committed
155
    Ta   : float
sbiri's avatar
sbiri committed
156
        air temperature         [K]
sbiri's avatar
sbiri committed
157 158
    meth : str

sbiri's avatar
sbiri committed
159 160 161 162 163 164 165
    Returns
    -------
    ctn : float
        neutral heat exchange coefficient
    cqn : float
        neutral moisture exchange coefficient
    """
sbiri's avatar
sbiri committed
166
    if (meth == "S80" or meth == "S88" or meth == "YT96"):
sbiri's avatar
sbiri committed
167
        cqn = np.ones(Ta.shape)*1.20*0.001  # from S88
sbiri's avatar
sbiri committed
168
        ctn = np.ones(Ta.shape)*1.00*0.001
sbiri's avatar
sbiri committed
169
    elif (meth == "LP82"):
sbiri's avatar
sbiri committed
170 171
        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
172 173 174 175 176
    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"):
        usr = np.sqrt(cdn*np.power(u10n, 2))
sbiri's avatar
sbiri committed
177
        # Zeng et al. 1998 (25)
sbiri's avatar
sbiri committed
178 179
        re=usr*zo/visc_air(Ta)
        zoq = zo/np.exp(2.67*np.power(re, 1/4)-2.57)
sbiri's avatar
sbiri committed
180 181 182 183 184
        zot = zoq
        cqn = np.where((u10n > 0.5) & (u10n < 18), np.power(kappa, 2) /
                       (np.log(10/zo)*np.log(10/zoq)), np.nan)
        ctn = np.where((u10n > 0.5) & (u10n < 18), np.power(kappa, 2) /
                       (np.log(10/zo)*np.log(10/zoq)), np.nan)
sbiri's avatar
sbiri committed
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
    elif (meth == "C30"):
        usr = np.sqrt(cdn*np.power(u10n, 2))
        rr = zo*usr/visc_air(Ta)
        zoq = np.where(5e-5/np.power(rr, 0.6) > 1.15e-4, 1.15e-4,
                       5e-5/np.power(rr, 0.6))  # moisture roughness
        zot=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)
    elif (meth == "C35"):
        usr = np.sqrt(cdn*np.power(u10n, 2))
        rr = zo*usr/visc_air(Ta)
        zoq = np.where(5.8e-5/np.power(rr, 0.72) > 1.6e-4, 1.6e-4,
                       5.8e-5/np.power(rr, 0.72))  # moisture roughness
        zot=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)
    elif (meth == "C40"):
        usr = np.sqrt(cdn*np.power(u10n, 2))
        rr = zo*usr/visc_air(Ta)
        zot = np.where(1.0e-4/np.power(rr, 0.55) > 2.4e-4/np.power(rr, 1.2),
                       2.4e-4/np.power(rr, 1.2),
                       1.0e-4/np.power(rr, 0.55)) # temperature roughness
        zoq = np.where(2.0e-5/np.power(rr,0.22) > 1.1e-4/np.power(rr,0.9),
                       1.1e-4/np.power(rr,0.9), 2.0e-5/np.power(rr,0.22))
        cqn = kappa**2/np.log(10/zo)/np.log(10/zoq)
        ctn = kappa**2/np.log(10/zo)/np.log(10/zot)
sbiri's avatar
sbiri committed
211
    elif (meth == "ecmwf" or meth == "Beljaars"):
212
        # eq. (3.26) p.38 over sea IFS Documentation cy46r1
sbiri's avatar
sbiri committed
213 214 215 216 217
        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)
sbiri's avatar
sbiri committed
218
    else:
sbiri's avatar
sbiri committed
219
        print("unknown method ctcqn: "+meth)
sbiri's avatar
sbiri committed
220
    return ctn, cqn
sbiri's avatar
sbiri committed
221 222 223
# ---------------------------------------------------------------------


224
def ctcq_calc(cdn, cd, ctn, cqn, ht, hq, hout, psit, psiq):
sbiri's avatar
sbiri committed
225 226
    """
    Calculates heat and moisture exchange coefficients at reference height
sbiri's avatar
sbiri committed
227

sbiri's avatar
sbiri committed
228 229 230 231 232 233 234 235 236 237
    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
sbiri's avatar
sbiri committed
238 239 240 241
    ht : float
        original temperature sensor height [m]
    hq : float
        original moisture sensor height    [m]
242 243
    hout : float
        output height                   [m]
sbiri's avatar
sbiri committed
244 245 246 247
    psit : float
        heat stability function
    psiq : float
        moisture stability function
sbiri's avatar
sbiri committed
248

sbiri's avatar
sbiri committed
249 250 251
    Returns
    -------
    ct : float
252
       heat exchange coefficient
sbiri's avatar
sbiri committed
253
    cq : float
254
       moisture exchange coefficient
sbiri's avatar
sbiri committed
255
    """
256
    ct = (ctn*np.sqrt(cd/cdn) /
257
          (1+ctn*((np.log(ht/hout)-psit)/(kappa*np.sqrt(cdn)))))
258
    cq = (cqn*np.sqrt(cd/cdn) /
259
          (1+cqn*((np.log(hq/hout)-psiq)/(kappa*np.sqrt(cdn)))))
sbiri's avatar
sbiri committed
260
    return ct, cq
sbiri's avatar
sbiri committed
261 262 263
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
264
def get_stabco(meth="S80"):
sbiri's avatar
sbiri committed
265 266
    """
    Gives the coefficients \\alpha, \\beta, \\gamma for stability functions
sbiri's avatar
sbiri committed
267 268 269 270 271 272 273 274 275 276 277

    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
278
        meth == "UA" or meth == "ecmwf" or meth == "C30" or
279
        meth == "C35" or meth == "C40" or meth == "Beljaars"):
sbiri's avatar
sbiri committed
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
        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
295
def psim_calc(zol, meth="S80"):
sbiri's avatar
sbiri committed
296 297
    """
    Calculates momentum stability function
sbiri's avatar
sbiri committed
298

sbiri's avatar
sbiri committed
299 300 301 302
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
303 304
    meth : str

sbiri's avatar
sbiri committed
305 306 307 308
    Returns
    -------
    psim : float
    """
sbiri's avatar
sbiri committed
309 310
    if (meth == "ecmwf"):
        psim = psim_ecmwf(zol)
sbiri's avatar
sbiri committed
311 312
    elif (meth == "C30" or meth == "C35" or meth == "C40"):
        psim = psiu_26(zol, meth)
313 314
    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
315
    else:
316 317
        psim = np.where(zol < 0, psim_conv(zol, meth),
                        psim_stab(zol, meth))
sbiri's avatar
sbiri committed
318
    return psim
sbiri's avatar
sbiri committed
319 320 321
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
322
def psit_calc(zol, meth="S80"):
sbiri's avatar
sbiri committed
323 324
    """
    Calculates heat stability function
sbiri's avatar
sbiri committed
325

sbiri's avatar
sbiri committed
326 327 328 329
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
330
    meth : str
331
        parameterisation method
sbiri's avatar
sbiri committed
332

sbiri's avatar
sbiri committed
333 334 335 336
    Returns
    -------
    psit : float
    """
sbiri's avatar
sbiri committed
337
    if (meth == "ecmwf"):
338
        psit = np.where(zol < 0, psi_conv(zol, meth),
sbiri's avatar
sbiri committed
339
                        psi_ecmwf(zol))
sbiri's avatar
sbiri committed
340 341
    elif (meth == "C30" or meth == "C35" or meth == "C40"):
        psit = psit_26(zol)
342 343
    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
344
    else:
345 346
        psit = np.where(zol < 0, psi_conv(zol, meth),
                        psi_stab(zol, meth))
sbiri's avatar
sbiri committed
347
    return psit
sbiri's avatar
sbiri committed
348 349 350
# ---------------------------------------------------------------------


351
def psi_Bel(zol):
sbiri's avatar
sbiri committed
352 353
    """
    Calculates momentum/heat stability function
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371

    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
372 373 374 375
def psi_ecmwf(zol):
    """
    Calculates heat stability function for stable conditions
    for method ecmwf
sbiri's avatar
sbiri committed
376

sbiri's avatar
sbiri committed
377 378 379 380
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
381

sbiri's avatar
sbiri committed
382 383 384 385
    Returns
    -------
    psit : float
    """
386
    # eq (3.22) p. 37 IFS Documentation cy46r1
sbiri's avatar
sbiri committed
387 388
    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
389
    return psit
sbiri's avatar
sbiri committed
390 391
# ---------------------------------------------------------------------

sbiri's avatar
sbiri committed
392 393

def psit_26(zol):
sbiri's avatar
sbiri committed
394 395
    """
    Computes temperature structure function as in C35
sbiri's avatar
sbiri committed
396 397 398 399 400 401 402 403 404 405 406

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

    Returns
    -------
    psi : float
    """
    b, d = 2/3, 0.35
407 408 409 410 411 412 413 414
    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)
415 416
    f = np.power(zol, 2)/(1+np.power(zol, 2))
    psi = np.where(zol < 0, (1-f)*psik+f*psic, psi)
sbiri's avatar
sbiri committed
417 418 419 420
    return psi
# ---------------------------------------------------------------------


421
def psi_conv(zol, meth):
sbiri's avatar
sbiri committed
422 423
    """
    Calculates heat stability function for unstable conditions
sbiri's avatar
sbiri committed
424

sbiri's avatar
sbiri committed
425 426 427 428
    Parameters
    ----------
    zol : float
        height over MO length
429 430
    meth : str
        parameterisation method
sbiri's avatar
sbiri committed
431

sbiri's avatar
sbiri committed
432 433 434 435
    Returns
    -------
    psit : float
    """
436 437
    coeffs = get_stabco(meth)
    alpha, beta = coeffs[0], coeffs[1]
438 439
    xtmp = np.power(1-alpha*zol, beta)
    psit = 2*np.log((1+np.power(xtmp, 2))*0.5)
sbiri's avatar
sbiri committed
440
    return psit
sbiri's avatar
sbiri committed
441 442 443
# ---------------------------------------------------------------------


444
def psi_stab(zol, meth):
sbiri's avatar
sbiri committed
445 446
    """
    Calculates heat stability function for stable conditions
sbiri's avatar
sbiri committed
447

sbiri's avatar
sbiri committed
448 449 450 451
    Parameters
    ----------
    zol : float
        height over MO length
452 453
    meth : str
        parameterisation method
sbiri's avatar
sbiri committed
454

sbiri's avatar
sbiri committed
455 456 457 458
    Returns
    -------
    psit : float
    """
459 460
    coeffs = get_stabco(meth)
    gamma = coeffs[2]
sbiri's avatar
sbiri committed
461
    psit = -gamma*zol
sbiri's avatar
sbiri committed
462
    return psit
sbiri's avatar
sbiri committed
463 464 465
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
466 467 468
def psim_ecmwf(zol):
    """
    Calculates momentum stability function for method ecmwf
sbiri's avatar
sbiri committed
469

sbiri's avatar
sbiri committed
470 471 472 473
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
474

sbiri's avatar
sbiri committed
475 476 477 478
    Returns
    -------
    psim : float
    """
479
    # eq (3.20, 3.22) p. 37 IFS Documentation cy46r1
sbiri's avatar
sbiri committed
480
    coeffs = get_stabco("ecmwf")
481 482
    alpha, beta = coeffs[0], coeffs[1]
    xtmp = np.power(1-alpha*zol, beta)
sbiri's avatar
sbiri committed
483
    a, b, c, d = 1, 2/3, 5, 0.35
484 485 486
    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
487 488 489 490
    return psim
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
491
def psiu_26(zol, meth):
sbiri's avatar
sbiri committed
492 493
    """
    Computes velocity structure function C35
sbiri's avatar
sbiri committed
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531

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

    Returns
    -------
    psi : float
    """
    if (meth == "C30"):
        dzol = np.where(0.35*zol > 50, 50, 0.35*zol) # 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)
    elif (meth == "C35" or meth == "C40"):
        dzol = np.where(0.35*zol > 50, 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)
    return psi
532 533
#------------------------------------------------------------------------------

sbiri's avatar
sbiri committed
534 535


536
def psim_conv(zol, meth):
sbiri's avatar
sbiri committed
537 538
    """
    Calculates momentum stability function for unstable conditions
sbiri's avatar
sbiri committed
539

sbiri's avatar
sbiri committed
540 541 542 543
    Parameters
    ----------
    zol : float
        height over MO length
544 545
    meth : str
        parameterisation method
sbiri's avatar
sbiri committed
546

sbiri's avatar
sbiri committed
547 548 549 550
    Returns
    -------
    psim : float
    """
551 552
    coeffs = get_stabco(meth)
    alpha, beta = coeffs[0], coeffs[1]
553 554
    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
555
            2*np.arctan(xtmp)+np.pi/2)
sbiri's avatar
sbiri committed
556
    return psim
sbiri's avatar
sbiri committed
557 558 559
# ---------------------------------------------------------------------


560
def psim_stab(zol, meth):
sbiri's avatar
sbiri committed
561 562
    """
    Calculates momentum stability function for stable conditions
sbiri's avatar
sbiri committed
563

sbiri's avatar
sbiri committed
564 565 566 567
    Parameters
    ----------
    zol : float
        height over MO length
568 569
    meth : str
        parameterisation method
sbiri's avatar
sbiri committed
570

sbiri's avatar
sbiri committed
571 572 573 574
    Returns
    -------
    psim : float
    """
575 576
    coeffs = get_stabco(meth)
    gamma = coeffs[2]
sbiri's avatar
sbiri committed
577 578 579 580 581
    psim = -gamma*zol
    return psim
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
582 583 584 585
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
586

sbiri's avatar
sbiri committed
587 588 589
    Parameters
    ----------
    sst : float
sbiri's avatar
sbiri committed
590
        sea surface temperature      [K]
sbiri's avatar
sbiri committed
591
    qsea : float
sbiri's avatar
sbiri committed
592
        specific humidity over sea   [g/kg]
sbiri's avatar
sbiri committed
593
    rho : float
sbiri's avatar
sbiri committed
594
        density of air               [kg/m^3]
sbiri's avatar
sbiri committed
595
    Rs : float
sbiri's avatar
sbiri committed
596
        downward shortwave radiation [Wm-2]
sbiri's avatar
sbiri committed
597
    Rnl : float
sbiri's avatar
sbiri committed
598
        upwelling IR radiation       [Wm-2]
sbiri's avatar
sbiri committed
599
    cp : float
sbiri's avatar
sbiri committed
600
       specific heat of air at constant pressure [J/K/kg]
sbiri's avatar
sbiri committed
601
    lv : float
sbiri's avatar
sbiri committed
602 603 604
       latent heat of vaporization   [J/kg]
    delta : float
       cool skin thickness           [m]
sbiri's avatar
sbiri committed
605
    usr : float
sbiri's avatar
sbiri committed
606
       friction velocity             [m/s]
sbiri's avatar
sbiri committed
607
    tsr : float
sbiri's avatar
sbiri committed
608
       star temperature              [K]
sbiri's avatar
sbiri committed
609
    qsr : float
sbiri's avatar
sbiri committed
610
       star humidity                 [g/kg]
sbiri's avatar
sbiri committed
611
    lat : float
sbiri's avatar
sbiri committed
612
       latitude                      [degE]
sbiri's avatar
sbiri committed
613

sbiri's avatar
sbiri committed
614 615 616
    Returns
    -------
    dter : float
sbiri's avatar
sbiri committed
617
        cool skin correction         [K]
sbiri's avatar
sbiri committed
618
    dqer : float
sbiri's avatar
sbiri committed
619 620 621
       humidity corrction            [g/kg]
    delta : float
       cool skin thickness           [m]
sbiri's avatar
sbiri committed
622
    """
sbiri's avatar
sbiri committed
623 624
    # coded following Saunders (1967) with lambda = 6
    g = gc(lat, None)
sbiri's avatar
sbiri committed
625 626
    if (np.nanmin(sst) > 200):  # if sst in Kelvin convert to Celsius
        sst = sst-CtoK
sbiri's avatar
sbiri committed
627 628 629 630
    # ************  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
631
    aw = 2.1e-5*np.power(np.maximum(sst+3.2, 0), 0.79)
sbiri's avatar
sbiri committed
632 633
    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
634
    Rns = 0.945*Rs  # albedo correction
sbiri's avatar
sbiri committed
635 636 637 638 639 640
    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
641
    xlamx = 6*np.ones(sst.shape)
sbiri's avatar
sbiri committed
642
    xlamx = np.where(alq > 0, 6/(1+(bigc*alq/usr**4)**0.75)**0.333, 6)
sbiri's avatar
sbiri committed
643
    delta = np.where(alq > 0, xlamx*visw/(np.sqrt(rho/rhow)*usr),
sbiri's avatar
sbiri committed
644 645
                   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
646
    dter = qcol*delta/tcw
sbiri's avatar
sbiri committed
647
    dqer = wetc*dter
sbiri's avatar
sbiri committed
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 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736
    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
737 738 739
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
740 741 742 743 744 745 746 747 748 749 750
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
751
    Rnl : float
sbiri's avatar
sbiri committed
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782
        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
783
    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
784 785 786 787 788 789 790 791 792 793 794
    # *** 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
795
    for jwl in range(10): # itteration to solve implicitely equation for warm layer
sbiri's avatar
sbiri committed
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 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879
        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
880
def get_gust(beta, Ta, usr, tsrv, zi, lat):
sbiri's avatar
sbiri committed
881 882
    """
    Computes gustiness
sbiri's avatar
sbiri committed
883

sbiri's avatar
sbiri committed
884 885 886 887 888
    Parameters
    ----------
    beta : float
        constant
    Ta : float
sbiri's avatar
sbiri committed
889
        air temperature   [K]
sbiri's avatar
sbiri committed
890
    usr : float
sbiri's avatar
sbiri committed
891
        friction velocity [m/s]
sbiri's avatar
sbiri committed
892
    tsrv : float
sbiri's avatar
sbiri committed
893
        star virtual temperature of air [K]
sbiri's avatar
sbiri committed
894
    zi : int
sbiri's avatar
sbiri committed
895
        scale height of the boundary layer depth [m]
sbiri's avatar
sbiri committed
896 897
    lat : float
        latitude
sbiri's avatar
sbiri committed
898

sbiri's avatar
sbiri committed
899 900
    Returns
    -------
sbiri's avatar
sbiri committed
901
    ug : float        [m/s]
sbiri's avatar
sbiri committed
902
    """
903
    if (np.nanmax(Ta) < 200):  # convert to K if in Celsius
sbiri's avatar
sbiri committed
904 905
        Ta = Ta+273.16
    g = gc(lat, None)
sbiri's avatar
sbiri committed
906
    Bf = (-g/Ta)*usr*tsrv
sbiri's avatar
sbiri committed
907 908 909 910 911 912
    ug = np.ones(np.shape(Ta))*0.2
    ug = np.where(Bf > 0, beta*np.power(Bf*zi, 1/3), 0.2)
    return ug
# ---------------------------------------------------------------------


913 914
def get_L(L, lat, usr, tsr, qsr, hin, Ta, sst, qair, qsea, wind, monob, psim,
          meth):
sbiri's avatar
sbiri committed
915 916 917 918 919 920 921
    """
    calculates Monin-Obukhov length and virtual star temperature

    Parameters
    ----------
    L : int
        Monin-Obukhov length definition options
sbiri's avatar
sbiri committed
922 923
        "S80"  : default for S80, S88, LP82, YT96 and LY04
        "ecmwf" : following ecmwf (IFS Documentation cy46r1), default for ecmwf
sbiri's avatar
sbiri committed
924 925 926 927 928 929 930 931 932 933
    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
934
    hin : float
sbiri's avatar
sbiri committed
935 936 937 938 939
        sensor heights (m)
    Ta : float
        air temperature (K)
    sst : float
        sea surface temperature (K)
sbiri's avatar
sbiri committed
940 941 942
    qair : float
        air specific humidity (g/kg)
    qsea : float
sbiri's avatar
sbiri committed
943
        specific humidity at sea surface (g/kg)
sbiri's avatar
sbiri committed
944 945
    q10n : float
        neutral specific humidity at 10m (g/kg)
sbiri's avatar
sbiri committed
946 947 948 949 950
    wind : float
        wind speed (m/s)
    monob : float
        Monin-Obukhov length from previous iteration step (m)
    meth : str
951
        bulk parameterisation method option: "S80", "S88", "LP82", "YT96",
sbiri's avatar
sbiri committed
952
        "UA", "LY04", "C30", "C35", "C40", "ecmwf", "Beljaars"
sbiri's avatar
sbiri committed
953 954 955

    Returns
    -------
956 957 958 959
    tsrv : float
        virtual star temperature (K)
    monob : float
        M-O length (m)
sbiri's avatar
sbiri committed
960 961

    """
sbiri's avatar
sbiri committed
962
    g = gc(lat)
963
    Rb = np.empty(sst.shape)
964
    if (L == "S80"):
sbiri's avatar
sbiri committed
965 966 967 968 969
        # as in NCAR, LY04
        tsrv = tsr*(1+0.6077*qair)+0.6077*Ta*qsr
        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
970
        temp = np.minimum(np.abs(temp), 10/hin[0])*np.sign(temp)
sbiri's avatar
sbiri committed
971 972
        monob = 1/np.copy(temp)
    elif (L == "ecmwf"):
973
        Rb = np.empty(sst.shape)
sbiri's avatar
sbiri committed
974
        tsrv = tsr*(1+0.6077*qair)+0.6077*Ta*qsr
975 976 977 978 979 980 981 982
        # 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)
sbiri's avatar
sbiri committed
983 984
        zo = (0.11*visc_air(Ta)/usr+0.018*np.power(usr, 2)/g)
        zot = 0.40*visc_air(Ta)/usr
985
        zol = (Rb*(np.power(np.log((hin[1]+zo)/zo)-psim_calc((hin[1]+zo) /
sbiri's avatar
sbiri committed
986 987
                                                              monob, meth) +
                            psim_calc(zo/monob, meth), 2) /
988 989
                   (np.log((hin[1]+zo)/zot) -
                    psit_calc((hin[1]+zo)/monob, meth) +
sbiri's avatar
sbiri committed
990
                    psit_calc(zot/monob, meth))))
991 992
        monob = hin[1]/zol 
    return tsrv, monob, Rb
sbiri's avatar
sbiri committed
993 994 995
#------------------------------------------------------------------------------


sbiri's avatar
sbiri committed
996
def get_strs(hin, monob, wind, zo, zot, zoq, dt, dq, dter, dqer, dtwl, ct, cq,
sbiri's avatar
sbiri committed
997
             cskin, wl, meth):
998 999 1000 1001 1002
    """
    calculates star wind speed, temperature and specific humidity

    Parameters
    ----------
sbiri's avatar
sbiri committed
1003
    hin : float
sbiri's avatar
sbiri committed
1004
        sensor heights [m]
1005
    monob : float
sbiri's avatar
sbiri committed
1006
        M-O length     [m]
1007
    wind : float
sbiri's avatar
sbiri committed
1008
        wind speed     [m/s]
1009
    zo : float
sbiri's avatar
sbiri committed
1010
        momentum roughness length    [m]
1011
    zot : float
sbiri's avatar
sbiri committed
1012
        temperature roughness length [m]
1013
    zoq : float
sbiri's avatar
sbiri committed
1014
        moisture roughness length    [m]
1015
    dt : float
sbiri's avatar
sbiri committed
1016
        temperature difference       [K]
1017
    dq : float
sbiri's avatar
sbiri committed
1018
        specific humidity difference [g/kg]
1019
    dter : float
sbiri's avatar
sbiri committed
1020
        cskin temperature adjustment [K]
1021
    dqer : float
sbiri's avatar
sbiri committed
1022 1023 1024
        cskin q adjustment           [g/kg]
    dtwl : float
        warm layer temperature adjustment [K]
1025 1026 1027 1028 1029 1030
    ct : float
        temperature exchange coefficient
    cq : float
        moisture exchange coefficient
    cskin : int
        cool skin adjustment switch
sbiri's avatar
sbiri committed
1031 1032
    wl : int
        warm layer correction switch
1033 1034
    meth : str
        bulk parameterisation method option: "S80", "S88", "LP82", "YT96", "UA",
sbiri's avatar
sbiri committed
1035
        "LY04", "C30", "C35", "C40", "ecmwf", "Beljaars"
1036 1037 1038 1039

    Returns
    -------
    usr : float
sbiri's avatar
sbiri committed
1040
        friction wind speed [m/s]
1041
    tsr : float
sbiri's avatar
sbiri committed
1042
        star temperature    [K]
1043
    qsr : float
sbiri's avatar
sbiri committed
1044
        star specific humidity [g/kg]
1045 1046 1047

    """
    if (meth == "UA"):
sbiri's avatar
sbiri committed
1048
        usr = np.where(hin[0]/monob <= -1.574, kappa*wind /
1049 1050
                       (np.log(-1.574*monob/zo)-psim_calc(-1.574, meth) +
                        psim_calc(zo/monob, meth) +
sbiri's avatar
sbiri committed
1051
                        1.14*(np.power(-hin[0]/monob, 1/3) -
1052
                        np.power(1.574, 1/3))),
sbiri's avatar
sbiri committed
1053 1054 1055
                       np.where(hin[0]/monob < 0, kappa*wind /
                                (np.log(hin[0]/zo) -
                                 psim_calc(hin[0]/monob, meth) +
1056
                                 psim_calc(zo/monob, meth)),
sbiri's avatar
sbiri committed
1057 1058 1059
                                np.where(hin[0]/monob <= 1, kappa*wind /
                                         (np.log(hin[0]/zo) +
                                          5*hin[0]/monob-5*zo/monob),
1060 1061
                                         kappa*wind/(np.log(monob/zo)+5 -
                                                     5*zo/monob +
sbiri's avatar
sbiri committed
1062 1063
                                                     5*np.log(hin[0]/monob) +
                                                     hin[0]/monob-1))))
1064
                                # Zeng et al. 1998 (7-10)
sbiri's avatar
sbiri committed
1065
        tsr = np.where(hin[1]/monob < -0.465, kappa*(dt+dter*cskin-dtwl*wl) /
1066 1067
                       (np.log((-0.465*monob)/zot) -
                        psit_calc(-0.465, meth)+0.8*(np.power(0.465, -1/3) -
sbiri's avatar
sbiri committed
1068 1069 1070 1071
                        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) +
1072
                                 psit_calc(zot/monob, meth)),
sbiri's avatar
sbiri committed
1073
                                np.where(hin[1]/monob <= 1,
sbiri's avatar
sbiri committed
1074
                                         kappa*(dt+dter*cskin-dtwl*wl) /
sbiri's avatar
sbiri committed
1075 1076
                                         (np.log(hin[1]/zot) +
                                          5*hin[1]/monob-5*zot/monob),
sbiri's avatar
sbiri committed
1077
                                         kappa*(dt+dter*cskin-dtwl*wl) /
1078
                                         (np.log(monob/zot)+5 -
sbiri's avatar
sbiri committed
1079 1080
                                          5*zot/monob+5*np.log(hin[1]/monob) +
                                          hin[1]/monob-1))))
1081
                                # Zeng et al. 1998 (11-14)
sbiri's avatar
sbiri committed
1082
        qsr = np.where(hin[2]/monob < -0.465, kappa*(dq+dqer*cskin) /
1083 1084 1085
                       (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
1086 1087 1088 1089
                             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) +
1090
                                psit_calc(zoq/monob, meth)),
sbiri's avatar
sbiri committed
1091
                                np.where(hin[2]/monob <= 1,
1092
                                         kappa*(dq+dqer*cskin) /
sbiri's avatar
sbiri committed
1093
                                         (np.log(hin[1]/zoq)+5*hin[2]/monob -
1094 1095 1096
                                          5*zoq/monob),
                                         kappa*(dq+dqer*cskin)/
                                         (np.log(monob/zoq)+5-5*zoq/monob +
sbiri's avatar
sbiri committed
1097 1098
                                          5*np.log(hin[2]/monob) +
                                          hin[2]/monob-1))))
1099
    elif (meth == "C30" or meth == "C35" or meth == "C40"):
sbiri's avatar
sbiri committed
1100 1101 1102 1103 1104
        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))))
1105
    else:
sbiri's avatar
sbiri committed
1106
        usr = (wind*kappa/(np.log(hin[0]/zo)-psim_calc(hin[0]/monob, meth)))
sbiri's avatar
sbiri committed
1107
        tsr = ct*wind*(dt+dter*cskin-dtwl*wl)/usr
1108 1109
        qsr = cq*wind*(dq+dqer*cskin)/usr
    return usr, tsr, qsr
sbiri's avatar
sbiri committed
1110
# ---------------------------------------------------------------------