flux_subs.py 36.2 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 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"):
sbiri's avatar
sbiri committed
39
        # for u<3 same as S80
sbiri's avatar
sbiri committed
40 41
        cdn = np.where((u10n < 6) & (u10n >= 3),
                       (0.29+3.1/u10n+7.7/u10n**2)*0.001,
sbiri's avatar
sbiri committed
42
                       np.where((u10n >= 6),
sbiri's avatar
sbiri committed
43
                       (0.60 + 0.070*u10n)*0.001, (0.61+0.567/u10n)*0.001))
sbiri's avatar
sbiri committed
44
    elif (meth == "LY04"):
sbiri's avatar
sbiri committed
45
        cdn = np.where(u10n >= 0.5,
46 47
                       (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
48
    else:
sbiri's avatar
sbiri committed
49
        print("unknown method cdn: "+meth)
sbiri's avatar
sbiri committed
50
    return cdn
sbiri's avatar
sbiri committed
51 52 53
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
54
def cdn_from_roughness(u10n, Ta, Tp, lat, meth="S88"):
sbiri's avatar
sbiri committed
55 56
    """
    Calculates neutral drag coefficient from roughness length
sbiri's avatar
sbiri committed
57

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

sbiri's avatar
sbiri committed
70 71 72 73
    Returns
    -------
    cdn : float
    """
sbiri's avatar
sbiri committed
74
    g, tol = gc(lat, None), 0.000001
sbiri's avatar
sbiri committed
75 76 77
    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
78 79
    for it in range(5):
        cdn = np.copy(cdnn)
sbiri's avatar
sbiri committed
80
        usr = np.sqrt(cdn*u10n**2)
sbiri's avatar
sbiri committed
81
        if (meth == "S88"):
82
            # Charnock roughness length (eq. 4 in Smith 88)
sbiri's avatar
sbiri committed
83
            zc = 0.011*np.power(usr, 2)/g
84
            #  smooth surface roughness length (eq. 6 in Smith 88)
sbiri's avatar
sbiri committed
85
            zs = 0.11*visc_air(Ta)/usr
86
            zo = zc + zs  #  eq. 7 & 8 in Smith 88
sbiri's avatar
sbiri committed
87
        elif (meth == "UA"):
sbiri's avatar
sbiri committed
88 89
            # 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
90 91
        elif (meth == "C30"):
            a = 0.011*np.ones(Ta.shape)
92
            a = np.where(u10n > 10, 0.011+(u10n-10)*(0.018-0.011)/(18-10),
sbiri's avatar
sbiri committed
93 94 95 96
                         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)
sbiri's avatar
sbiri committed
97 98 99
            # a = np.where(u10n > 19, 0.0017*19-0.0050,
            #             np.where((u10n > 7) & (u10n <= 18),
            #                       0.0017*u10n-0.0050, a))
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 117
    return cdn
# ---------------------------------------------------------------------


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

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

    Returns
    -------
    cd : float
    """
    cd = (cdn/np.power(1+(np.sqrt(cdn)*(np.log(height/ref_ht)-psim))/kappa, 2))
    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 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
    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))
sbiri's avatar
sbiri committed
209 210 211
        # moisture roughness determined by the CLIMODE, GASEX and CBLAST data
#        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 as in C30
sbiri's avatar
sbiri committed
212 213
        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
214
    elif (meth == "ecmwf" or meth == "Beljaars"):
215
        # eq. (3.26) p.38 over sea IFS Documentation cy46r1
sbiri's avatar
sbiri committed
216 217 218 219 220
        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
221
    else:
sbiri's avatar
sbiri committed
222
        print("unknown method ctcqn: "+meth)
sbiri's avatar
sbiri committed
223
    return ctn, cqn
sbiri's avatar
sbiri committed
224 225 226
# ---------------------------------------------------------------------


227
def ctcq_calc(cdn, cd, ctn, cqn, ht, hq, ref_ht, psit, psiq):
sbiri's avatar
sbiri committed
228 229
    """
    Calculates heat and moisture exchange coefficients at reference height
sbiri's avatar
sbiri committed
230

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

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


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

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

sbiri's avatar
sbiri committed
302 303 304 305
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
306 307
    meth : str

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


sbiri's avatar
sbiri committed
325
def psit_calc(zol, meth="S80"):
sbiri's avatar
sbiri committed
326 327
    """
    Calculates heat stability function
sbiri's avatar
sbiri committed
328

sbiri's avatar
sbiri committed
329 330 331 332
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
333
    meth : str
334
        parameterisation method
sbiri's avatar
sbiri committed
335

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


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

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

sbiri's avatar
sbiri committed
380 381 382 383
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
384

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

sbiri's avatar
sbiri committed
395 396

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

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

    Returns
    -------
    psi : float
    """
    b, d = 2/3, 0.35
410 411 412 413 414 415 416 417
    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)
418 419
    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
420 421 422 423
    return psi
# ---------------------------------------------------------------------


424
def psi_conv(zol, meth):
sbiri's avatar
sbiri committed
425 426
    """
    Calculates heat stability function for unstable conditions
sbiri's avatar
sbiri committed
427

sbiri's avatar
sbiri committed
428 429 430 431
    Parameters
    ----------
    zol : float
        height over MO length
432 433
    meth : str
        parameterisation method
sbiri's avatar
sbiri committed
434

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


447
def psi_stab(zol, meth):
sbiri's avatar
sbiri committed
448 449
    """
    Calculates heat stability function for stable conditions
sbiri's avatar
sbiri committed
450

sbiri's avatar
sbiri committed
451 452 453 454
    Parameters
    ----------
    zol : float
        height over MO length
455 456
    meth : str
        parameterisation method
sbiri's avatar
sbiri committed
457

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


sbiri's avatar
sbiri committed
469 470 471
def psim_ecmwf(zol):
    """
    Calculates momentum stability function for method ecmwf
sbiri's avatar
sbiri committed
472

sbiri's avatar
sbiri committed
473 474 475 476
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
477

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


sbiri's avatar
sbiri committed
494
def psiu_26(zol, meth):
sbiri's avatar
sbiri committed
495 496
    """
    Computes velocity structure function C35
sbiri's avatar
sbiri committed
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 532 533 534

    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
535 536
#------------------------------------------------------------------------------

sbiri's avatar
sbiri committed
537 538


539
def psim_conv(zol, meth):
sbiri's avatar
sbiri committed
540 541
    """
    Calculates momentum stability function for unstable conditions
sbiri's avatar
sbiri committed
542

sbiri's avatar
sbiri committed
543 544 545 546
    Parameters
    ----------
    zol : float
        height over MO length
547 548
    meth : str
        parameterisation method
sbiri's avatar
sbiri committed
549

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


563
def psim_stab(zol, meth):
sbiri's avatar
sbiri committed
564 565
    """
    Calculates momentum stability function for stable conditions
sbiri's avatar
sbiri committed
566

sbiri's avatar
sbiri committed
567 568 569 570
    Parameters
    ----------
    zol : float
        height over MO length
571 572
    meth : str
        parameterisation method
sbiri's avatar
sbiri committed
573

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


sbiri's avatar
sbiri committed
585 586 587 588
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
589

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

sbiri's avatar
sbiri committed
617 618 619
    Returns
    -------
    dter : float
sbiri's avatar
sbiri committed
620
        cool skin correction         [K]
sbiri's avatar
sbiri committed
621
    dqer : float
sbiri's avatar
sbiri committed
622 623 624
       humidity corrction            [g/kg]
    delta : float
       cool skin thickness           [m]
sbiri's avatar
sbiri committed
625
    """
sbiri's avatar
sbiri committed
626 627
    # coded following Saunders (1967) with lambda = 6
    g = gc(lat, None)
sbiri's avatar
sbiri committed
628 629
    if (np.nanmin(sst) > 200):  # if sst in Kelvin convert to Celsius
        sst = sst-CtoK
sbiri's avatar
sbiri committed
630 631 632 633
    # ************  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
634
    aw = 2.1e-5*np.power(np.maximum(sst+3.2, 0), 0.79)
sbiri's avatar
sbiri committed
635 636
    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
637
    Rns = 0.945*Rs  # albedo correction
sbiri's avatar
sbiri committed
638 639 640 641 642 643
    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
644
    xlamx = 6*np.ones(sst.shape)
sbiri's avatar
sbiri committed
645
    xlamx = np.where(alq > 0, 6/(1+(bigc*alq/usr**4)**0.75)**0.333, 6)
sbiri's avatar
sbiri committed
646
    delta = np.where(alq > 0, xlamx*visw/(np.sqrt(rho/rhow)*usr),
sbiri's avatar
sbiri committed
647 648
                   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
649
    dter = qcol*delta/tcw
sbiri's avatar
sbiri committed
650
    dqer = wetc*dter
sbiri's avatar
sbiri committed
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
    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))
sbiri's avatar
sbiri committed
736 737
        # fs = np.maximum(0.065+11*delta-(6.6e-5/delta)*(1-np.exp(-delta/8e-4)),
        #                 0.01)
sbiri's avatar
sbiri committed
738 739 740 741
        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
742 743 744
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
745 746 747 748 749 750 751 752 753 754 755
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
756
    Rnl : float
sbiri's avatar
sbiri committed
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 783 784 785 786 787
        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
788
    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
789 790 791 792 793 794 795 796 797 798 799
    # *** 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
800
    for jwl in range(10): # itteration to solve implicitely equation for warm layer
sbiri's avatar
sbiri committed
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 880 881 882 883 884
        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
885
def get_gust(beta, Ta, usr, tsrv, zi, lat):
sbiri's avatar
sbiri committed
886 887
    """
    Computes gustiness
sbiri's avatar
sbiri committed
888

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

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


sbiri's avatar
sbiri committed
918
def get_L(L, lat, usr, tsr, qsr, t10n, hin, Ta, sst, qair, qsea, q10n,
sbiri's avatar
sbiri committed
919
          wind, monob, meth):
sbiri's avatar
sbiri committed
920 921 922 923 924 925 926
    """
    calculates Monin-Obukhov length and virtual star temperature

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

    Returns
    -------
961 962 963 964
    tsrv : float
        virtual star temperature (K)
    monob : float
        M-O length (m)
sbiri's avatar
sbiri committed
965 966

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


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

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

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

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