flux_subs.py 35.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
    """
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]
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 == "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
    """
sbiri's avatar
sbiri committed
59
    Calculates 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
                         np.where(u10n > 18, 0.018, a))
            zo = a*np.power(usr, 2)/g+0.11*visc_air(Ta)/usr
        elif (meth == "C35"):
99 100 101
            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
102
        elif ((meth == "ecmwf" or meth == "Beljaars")):
103
            # eq. (3.26) p.38 over sea IFS Documentation cy46r1
104
            zo = 0.018*np.power(usr, 2)/g+0.11*visc_air(Ta)/usr
sbiri's avatar
sbiri committed
105
        else:
sbiri's avatar
sbiri committed
106
            print("unknown method for cdn_from_roughness "+meth)
sbiri's avatar
sbiri committed
107
        cdnn = (kappa/np.log(10/zo))**2
sbiri's avatar
sbiri committed
108
    cdn = np.where(np.abs(cdnn-cdn) < tol, cdnn, np.nan)
109 110 111 112
    return cdn
# ---------------------------------------------------------------------


113
def cd_calc(cdn, hin, hout, psim):
sbiri's avatar
sbiri committed
114 115
    """
    Calculates drag coefficient at reference height
116 117 118 119 120

    Parameters
    ----------
    cdn : float
        neutral drag coefficient
121
    hin : float
122
        wind speed height       [m]
123
    hout : float
sbiri's avatar
sbiri committed
124
        reference height        [m]
125 126 127 128 129 130 131
    psim : float
        momentum stability function

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

sbiri's avatar
sbiri committed
136

sbiri's avatar
sbiri committed
137
def ctcqn_calc(zol, cdn, u10n, zo, Ta, meth="S80"):
sbiri's avatar
sbiri committed
138
    """
sbiri's avatar
sbiri committed
139
    Calculates neutral heat and moisture exchange coefficients
sbiri's avatar
sbiri committed
140

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

sbiri's avatar
sbiri committed
155 156 157 158 159 160 161
    Returns
    -------
    ctn : float
        neutral heat exchange coefficient
    cqn : float
        neutral moisture exchange coefficient
    """
sbiri's avatar
sbiri committed
162
    if (meth == "S80" or meth == "S88" or meth == "YT96"):
sbiri's avatar
sbiri committed
163
        cqn = np.ones(Ta.shape)*1.20*0.001  # from S88
sbiri's avatar
sbiri committed
164
        ctn = np.ones(Ta.shape)*1.00*0.001
sbiri's avatar
sbiri committed
165
    elif (meth == "LP82"):
sbiri's avatar
sbiri committed
166 167
        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
168 169 170 171 172
    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
173
        # Zeng et al. 1998 (25)
sbiri's avatar
sbiri committed
174 175
        re=usr*zo/visc_air(Ta)
        zoq = zo/np.exp(2.67*np.power(re, 1/4)-2.57)
sbiri's avatar
sbiri committed
176
        zot = zoq
177
        cqn = np.where(u10n < 18, np.power(kappa, 2) /
sbiri's avatar
sbiri committed
178
                       (np.log(10/zo)*np.log(10/zoq)), np.nan)
179
        ctn = np.where(u10n < 18, np.power(kappa, 2) /
sbiri's avatar
sbiri committed
180
                       (np.log(10/zo)*np.log(10/zoq)), np.nan)
sbiri's avatar
sbiri committed
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
    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)
sbiri's avatar
sbiri committed
197
    elif (meth == "ecmwf" or meth == "Beljaars"):
198
        # eq. (3.26) p.38 over sea IFS Documentation cy46r1
sbiri's avatar
sbiri committed
199 200 201 202 203
        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
204
    else:
sbiri's avatar
sbiri committed
205
        print("unknown method ctcqn: "+meth)
sbiri's avatar
sbiri committed
206
    return ctn, cqn
sbiri's avatar
sbiri committed
207 208 209
# ---------------------------------------------------------------------


210
def ctcq_calc(cdn, cd, ctn, cqn, ht, hq, hout, psit, psiq):
sbiri's avatar
sbiri committed
211 212
    """
    Calculates heat and moisture exchange coefficients at reference height
sbiri's avatar
sbiri committed
213

sbiri's avatar
sbiri committed
214 215 216 217 218 219 220 221 222 223
    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
224
    ht : float
sbiri's avatar
sbiri committed
225
        original temperature sensor height [m]
sbiri's avatar
sbiri committed
226
    hq : float
sbiri's avatar
sbiri committed
227
        original moisture sensor height    [m]
228
    hout : float
sbiri's avatar
sbiri committed
229
        reference height                   [m]
sbiri's avatar
sbiri committed
230 231 232 233
    psit : float
        heat stability function
    psiq : float
        moisture stability function
sbiri's avatar
sbiri committed
234

sbiri's avatar
sbiri committed
235 236 237
    Returns
    -------
    ct : float
238
       heat exchange coefficient
sbiri's avatar
sbiri committed
239
    cq : float
240
       moisture exchange coefficient
sbiri's avatar
sbiri committed
241
    """
242
    ct = (ctn*np.sqrt(cd/cdn) /
243
          (1+ctn*((np.log(ht/hout)-psit)/(kappa*np.sqrt(cdn)))))
244
    cq = (cqn*np.sqrt(cd/cdn) /
245
          (1+cqn*((np.log(hq/hout)-psiq)/(kappa*np.sqrt(cdn)))))
sbiri's avatar
sbiri committed
246
    return ct, cq
sbiri's avatar
sbiri committed
247 248 249
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
250
def get_stabco(meth="S80"):
sbiri's avatar
sbiri committed
251 252
    """
    Gives the coefficients \\alpha, \\beta, \\gamma for stability functions
sbiri's avatar
sbiri committed
253 254 255 256 257 258 259 260 261 262 263

    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
264
        meth == "UA" or meth == "ecmwf" or meth == "C30" or
sbiri's avatar
sbiri committed
265
        meth == "C35" or meth == "Beljaars"):
sbiri's avatar
sbiri committed
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
        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
281
def psim_calc(zol, meth="S80"):
sbiri's avatar
sbiri committed
282 283
    """
    Calculates momentum stability function
sbiri's avatar
sbiri committed
284

sbiri's avatar
sbiri committed
285 286 287 288
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
289 290
    meth : str

sbiri's avatar
sbiri committed
291 292 293 294
    Returns
    -------
    psim : float
    """
sbiri's avatar
sbiri committed
295 296
    if (meth == "ecmwf"):
        psim = psim_ecmwf(zol)
sbiri's avatar
sbiri committed
297
    elif (meth == "C30" or meth == "C35"):  # or meth == "C40"
sbiri's avatar
sbiri committed
298
        psim = psiu_26(zol, meth)
299 300
    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
301
    else:
302 303
        psim = np.where(zol < 0, psim_conv(zol, meth),
                        psim_stab(zol, meth))
sbiri's avatar
sbiri committed
304
    return psim
sbiri's avatar
sbiri committed
305 306 307
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
308
def psit_calc(zol, meth="S80"):
sbiri's avatar
sbiri committed
309 310
    """
    Calculates heat stability function
sbiri's avatar
sbiri committed
311

sbiri's avatar
sbiri committed
312 313 314 315
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
316
    meth : str
317
        parameterisation method
sbiri's avatar
sbiri committed
318

sbiri's avatar
sbiri committed
319 320 321 322
    Returns
    -------
    psit : float
    """
sbiri's avatar
sbiri committed
323
    if (meth == "ecmwf"):
324
        psit = np.where(zol < 0, psi_conv(zol, meth),
sbiri's avatar
sbiri committed
325
                        psi_ecmwf(zol))
sbiri's avatar
sbiri committed
326
    elif (meth == "C30" or meth == "C35"):
sbiri's avatar
sbiri committed
327
        psit = psit_26(zol)
328 329
    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
330
    else:
331 332
        psit = np.where(zol < 0, psi_conv(zol, meth),
                        psi_stab(zol, meth))
sbiri's avatar
sbiri committed
333
    return psit
sbiri's avatar
sbiri committed
334 335 336
# ---------------------------------------------------------------------


337
def psi_Bel(zol):
sbiri's avatar
sbiri committed
338 339
    """
    Calculates momentum/heat stability function
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357

    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
358 359 360 361
def psi_ecmwf(zol):
    """
    Calculates heat stability function for stable conditions
    for method ecmwf
sbiri's avatar
sbiri committed
362

sbiri's avatar
sbiri committed
363 364 365 366
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
367

sbiri's avatar
sbiri committed
368 369 370 371
    Returns
    -------
    psit : float
    """
372
    # eq (3.22) p. 37 IFS Documentation cy46r1
sbiri's avatar
sbiri committed
373 374
    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
375
    return psit
sbiri's avatar
sbiri committed
376 377
# ---------------------------------------------------------------------

sbiri's avatar
sbiri committed
378 379

def psit_26(zol):
sbiri's avatar
sbiri committed
380 381
    """
    Computes temperature structure function as in C35
sbiri's avatar
sbiri committed
382 383 384 385 386 387 388 389 390 391 392

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

    Returns
    -------
    psi : float
    """
    b, d = 2/3, 0.35
393 394 395 396 397 398 399 400
    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)
401 402
    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
403 404 405 406
    return psi
# ---------------------------------------------------------------------


407
def psi_conv(zol, meth):
sbiri's avatar
sbiri committed
408 409
    """
    Calculates heat stability function for unstable conditions
sbiri's avatar
sbiri committed
410

sbiri's avatar
sbiri committed
411 412 413 414
    Parameters
    ----------
    zol : float
        height over MO length
415 416
    meth : str
        parameterisation method
sbiri's avatar
sbiri committed
417

sbiri's avatar
sbiri committed
418 419 420 421
    Returns
    -------
    psit : float
    """
422 423
    coeffs = get_stabco(meth)
    alpha, beta = coeffs[0], coeffs[1]
424 425
    xtmp = np.power(1-alpha*zol, beta)
    psit = 2*np.log((1+np.power(xtmp, 2))*0.5)
sbiri's avatar
sbiri committed
426
    return psit
sbiri's avatar
sbiri committed
427 428 429
# ---------------------------------------------------------------------


430
def psi_stab(zol, meth):
sbiri's avatar
sbiri committed
431 432
    """
    Calculates heat stability function for stable conditions
sbiri's avatar
sbiri committed
433

sbiri's avatar
sbiri committed
434 435 436 437
    Parameters
    ----------
    zol : float
        height over MO length
438 439
    meth : str
        parameterisation method
sbiri's avatar
sbiri committed
440

sbiri's avatar
sbiri committed
441 442 443 444
    Returns
    -------
    psit : float
    """
445 446
    coeffs = get_stabco(meth)
    gamma = coeffs[2]
sbiri's avatar
sbiri committed
447
    psit = -gamma*zol
sbiri's avatar
sbiri committed
448
    return psit
sbiri's avatar
sbiri committed
449 450 451
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
452 453 454
def psim_ecmwf(zol):
    """
    Calculates momentum stability function for method ecmwf
sbiri's avatar
sbiri committed
455

sbiri's avatar
sbiri committed
456 457 458 459
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
460

sbiri's avatar
sbiri committed
461 462 463 464
    Returns
    -------
    psim : float
    """
465
    # eq (3.20, 3.22) p. 37 IFS Documentation cy46r1
sbiri's avatar
sbiri committed
466
    coeffs = get_stabco("ecmwf")
467 468
    alpha, beta = coeffs[0], coeffs[1]
    xtmp = np.power(1-alpha*zol, beta)
sbiri's avatar
sbiri committed
469
    a, b, c, d = 1, 2/3, 5, 0.35
470 471 472
    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
473 474 475 476
    return psim
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
477
def psiu_26(zol, meth):
sbiri's avatar
sbiri committed
478 479
    """
    Computes velocity structure function C35
sbiri's avatar
sbiri committed
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502

    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)
sbiri's avatar
sbiri committed
503
    elif (meth == "C35"):
sbiri's avatar
sbiri committed
504 505 506 507 508 509 510 511 512 513 514 515 516 517
        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
518 519
#------------------------------------------------------------------------------

sbiri's avatar
sbiri committed
520 521


522
def psim_conv(zol, meth):
sbiri's avatar
sbiri committed
523 524
    """
    Calculates momentum stability function for unstable conditions
sbiri's avatar
sbiri committed
525

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

sbiri's avatar
sbiri committed
533 534 535 536
    Returns
    -------
    psim : float
    """
537 538
    coeffs = get_stabco(meth)
    alpha, beta = coeffs[0], coeffs[1]
539 540
    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
541
            2*np.arctan(xtmp)+np.pi/2)
sbiri's avatar
sbiri committed
542
    return psim
sbiri's avatar
sbiri committed
543 544 545
# ---------------------------------------------------------------------


546
def psim_stab(zol, meth):
sbiri's avatar
sbiri committed
547 548
    """
    Calculates momentum stability function for stable conditions
sbiri's avatar
sbiri committed
549

sbiri's avatar
sbiri committed
550 551 552 553
    Parameters
    ----------
    zol : float
        height over MO length
554 555
    meth : str
        parameterisation method
sbiri's avatar
sbiri committed
556

sbiri's avatar
sbiri committed
557 558 559 560
    Returns
    -------
    psim : float
    """
561 562
    coeffs = get_stabco(meth)
    gamma = coeffs[2]
sbiri's avatar
sbiri committed
563 564 565 566 567
    psim = -gamma*zol
    return psim
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
568 569 570 571
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
572

sbiri's avatar
sbiri committed
573 574 575
    Parameters
    ----------
    sst : float
sbiri's avatar
sbiri committed
576
        sea surface temperature      [K]
sbiri's avatar
sbiri committed
577
    qsea : float
sbiri's avatar
sbiri committed
578
        specific humidity over sea   [g/kg]
sbiri's avatar
sbiri committed
579
    rho : float
sbiri's avatar
sbiri committed
580
        density of air               [kg/m^3]
sbiri's avatar
sbiri committed
581
    Rs : float
sbiri's avatar
sbiri committed
582
        downward shortwave radiation [Wm-2]
sbiri's avatar
sbiri committed
583
    Rnl : float
sbiri's avatar
sbiri committed
584
        upwelling IR radiation       [Wm-2]
sbiri's avatar
sbiri committed
585
    cp : float
sbiri's avatar
sbiri committed
586
       specific heat of air at constant pressure [J/K/kg]
sbiri's avatar
sbiri committed
587
    lv : float
sbiri's avatar
sbiri committed
588 589 590
       latent heat of vaporization   [J/kg]
    delta : float
       cool skin thickness           [m]
sbiri's avatar
sbiri committed
591
    usr : float
sbiri's avatar
sbiri committed
592
       friction velocity             [m/s]
sbiri's avatar
sbiri committed
593
    tsr : float
sbiri's avatar
sbiri committed
594
       star temperature              [K]
sbiri's avatar
sbiri committed
595
    qsr : float
sbiri's avatar
sbiri committed
596
       star humidity                 [g/kg]
sbiri's avatar
sbiri committed
597
    lat : float
sbiri's avatar
sbiri committed
598
       latitude                      [degE]
sbiri's avatar
sbiri committed
599

sbiri's avatar
sbiri committed
600 601 602
    Returns
    -------
    dter : float
sbiri's avatar
sbiri committed
603
        cool skin correction         [K]
sbiri's avatar
sbiri committed
604
    dqer : float
sbiri's avatar
sbiri committed
605 606 607
       humidity corrction            [g/kg]
    delta : float
       cool skin thickness           [m]
sbiri's avatar
sbiri committed
608
    """
sbiri's avatar
sbiri committed
609 610
    # coded following Saunders (1967) with lambda = 6
    g = gc(lat, None)
sbiri's avatar
sbiri committed
611 612
    if (np.nanmin(sst) > 200):  # if sst in Kelvin convert to Celsius
        sst = sst-CtoK
sbiri's avatar
sbiri committed
613 614 615 616
    # ************  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
617
    aw = 2.1e-5*np.power(np.maximum(sst+3.2, 0), 0.79)
sbiri's avatar
sbiri committed
618 619
    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
620
    Rns = 0.945*Rs  # albedo correction
sbiri's avatar
sbiri committed
621 622 623 624 625 626
    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
627
    xlamx = 6*np.ones(sst.shape)
sbiri's avatar
sbiri committed
628
    xlamx = np.where(alq > 0, 6/(1+(bigc*alq/usr**4)**0.75)**0.333, 6)
sbiri's avatar
sbiri committed
629
    delta = np.where(alq > 0, xlamx*visw/(np.sqrt(rho/rhow)*usr),
sbiri's avatar
sbiri committed
630 631
                   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
632
    dter = qcol*delta/tcw
sbiri's avatar
sbiri committed
633
    dqer = wetc*dter
sbiri's avatar
sbiri committed
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 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
    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
723 724 725
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
726 727 728 729 730 731 732 733 734 735 736
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
737
    Rnl : float
sbiri's avatar
sbiri committed
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768
        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
769
    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
770 771 772 773 774 775 776 777 778 779 780
    # *** 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
781
    for jwl in range(10): # itteration to solve implicitely equation for warm layer
sbiri's avatar
sbiri committed
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 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865
        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
866
def get_gust(beta, Ta, usr, tsrv, zi, lat):
sbiri's avatar
sbiri committed
867 868
    """
    Computes gustiness
sbiri's avatar
sbiri committed
869

sbiri's avatar
sbiri committed
870 871 872 873 874
    Parameters
    ----------
    beta : float
        constant
    Ta : float
sbiri's avatar
sbiri committed
875
        air temperature   [K]
sbiri's avatar
sbiri committed
876
    usr : float
sbiri's avatar
sbiri committed
877
        friction velocity [m/s]
sbiri's avatar
sbiri committed
878
    tsrv : float
sbiri's avatar
sbiri committed
879
        star virtual temperature of air [K]
sbiri's avatar
sbiri committed
880
    zi : int
sbiri's avatar
sbiri committed
881
        scale height of the boundary layer depth [m]
sbiri's avatar
sbiri committed
882 883
    lat : float
        latitude
sbiri's avatar
sbiri committed
884

sbiri's avatar
sbiri committed
885 886
    Returns
    -------
sbiri's avatar
sbiri committed
887
    ug : float        [m/s]
sbiri's avatar
sbiri committed
888
    """
889
    if (np.nanmax(Ta) < 200):  # convert to K if in Celsius
sbiri's avatar
sbiri committed
890 891
        Ta = Ta+273.16
    g = gc(lat, None)
sbiri's avatar
sbiri committed
892
    Bf = (-g/Ta)*usr*tsrv
sbiri's avatar
sbiri committed
893 894 895 896 897 898
    ug = np.ones(np.shape(Ta))*0.2
    ug = np.where(Bf > 0, beta*np.power(Bf*zi, 1/3), 0.2)
    return ug
# ---------------------------------------------------------------------


899 900
def get_L(L, lat, usr, tsr, qsr, hin, Ta, sst, qair, qsea, wind, monob, psim,
          meth):
sbiri's avatar
sbiri committed
901 902 903 904 905 906 907
    """
    calculates Monin-Obukhov length and virtual star temperature

    Parameters
    ----------
    L : int
        Monin-Obukhov length definition options
sbiri's avatar
sbiri committed
908 909
        "S80"  : default for S80, S88, LP82, YT96 and LY04
        "ecmwf" : following ecmwf (IFS Documentation cy46r1), default for ecmwf
sbiri's avatar
sbiri committed
910 911 912 913 914 915 916 917 918 919
    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
920
    hin : float
sbiri's avatar
sbiri committed
921 922 923 924 925
        sensor heights (m)
    Ta : float
        air temperature (K)
    sst : float
        sea surface temperature (K)
sbiri's avatar
sbiri committed
926 927 928
    qair : float
        air specific humidity (g/kg)
    qsea : float
sbiri's avatar
sbiri committed
929
        specific humidity at sea surface (g/kg)
sbiri's avatar
sbiri committed
930 931
    q10n : float
        neutral specific humidity at 10m (g/kg)
sbiri's avatar
sbiri committed
932 933 934 935 936
    wind : float
        wind speed (m/s)
    monob : float
        Monin-Obukhov length from previous iteration step (m)
    meth : str
937
        bulk parameterisation method option: "S80", "S88", "LP82", "YT96",
sbiri's avatar
sbiri committed
938
        "UA", "LY04", "C30", "C35", "ecmwf", "Beljaars"
sbiri's avatar
sbiri committed
939 940 941

    Returns
    -------
942 943 944 945
    tsrv : float
        virtual star temperature (K)
    monob : float
        M-O length (m)
sbiri's avatar
sbiri committed
946 947

    """
sbiri's avatar
sbiri committed
948
    g = gc(lat)
949
    Rb = np.empty(sst.shape)
950
    if (L == "S80"):
sbiri's avatar
sbiri committed
951 952 953 954 955
        # 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
956
        temp = np.minimum(np.abs(temp), 10/hin[0])*np.sign(temp)
sbiri's avatar
sbiri committed
957 958
        monob = 1/np.copy(temp)
    elif (L == "ecmwf"):
959
        Rb = np.empty(sst.shape)
sbiri's avatar
sbiri committed
960
        tsrv = tsr*(1+0.6077*qair)+0.6077*Ta*qsr
961 962 963 964 965 966 967 968
        # 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
969 970
        zo = (0.11*visc_air(Ta)/usr+0.018*np.power(usr, 2)/g)
        zot = 0.40*visc_air(Ta)/usr
971
        zol = (Rb*(np.power(np.log((hin[1]+zo)/zo)-psim_calc((hin[1]+zo) /
sbiri's avatar
sbiri committed
972 973
                                                              monob, meth) +
                            psim_calc(zo/monob, meth), 2) /
974 975
                   (np.log((hin[1]+zo)/zot) -
                    psit_calc((hin[1]+zo)/monob, meth) +
sbiri's avatar
sbiri committed
976
                    psit_calc(zot/monob, meth))))
sbiri's avatar
sbiri committed
977
        monob = hin[1]/zol
978
    return tsrv, monob, Rb
sbiri's avatar
sbiri committed
979 980 981
#------------------------------------------------------------------------------


sbiri's avatar
sbiri committed
982
def get_strs(hin, monob, wind, zo, zot, zoq, dt, dq, dter, dqer, dtwl, ct, cq,
sbiri's avatar
sbiri committed
983
             cskin, wl, meth):
984 985 986 987 988
    """
    calculates star wind speed, temperature and specific humidity

    Parameters
    ----------
sbiri's avatar
sbiri committed
989
    hin : float
sbiri's avatar
sbiri committed
990
        sensor heights [m]
991
    monob : float
sbiri's avatar
sbiri committed
992
        M-O length     [m]
993
    wind : float
sbiri's avatar
sbiri committed
994
        wind speed     [m/s]
995
    zo : float
sbiri's avatar
sbiri committed
996
        momentum roughness length    [m]
997
    zot : float
sbiri's avatar
sbiri committed
998
        temperature roughness length [m]
999
    zoq : float
sbiri's avatar
sbiri committed
1000
        moisture roughness length    [m]
1001
    dt : float
sbiri's avatar
sbiri committed
1002
        temperature difference       [K]
1003
    dq : float
sbiri's avatar
sbiri committed
1004
        specific humidity difference [g/kg]
1005
    dter : float
sbiri's avatar
sbiri committed
1006
        cskin temperature adjustment [K]
1007
    dqer : float
sbiri's avatar
sbiri committed
1008 1009 1010
        cskin q adjustment           [g/kg]
    dtwl : float
        warm layer temperature adjustment [K]
1011 1012 1013 1014 1015 1016
    ct : float
        temperature exchange coefficient
    cq : float
        moisture exchange coefficient
    cskin : int
        cool skin adjustment switch
sbiri's avatar
sbiri committed
1017 1018
    wl : int
        warm layer correction switch
1019 1020
    meth : str
        bulk parameterisation method option: "S80", "S88", "LP82", "YT96", "UA",
sbiri's avatar
sbiri committed
1021
        "LY04", "C30", "C35", "ecmwf", "Beljaars"
1022 1023 1024 1025

    Returns
    -------
    usr : float
sbiri's avatar
sbiri committed
1026
        friction wind speed [m/s]
1027
    tsr : float
sbiri's avatar
sbiri committed
1028
        star temperature    [K]
1029
    qsr : float
sbiri's avatar
sbiri committed
1030
        star specific humidity [g/kg]
1031 1032 1033

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