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

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

sbiri's avatar
sbiri committed
6

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


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

    Parameters
    ----------
    cdn : float
        neutral drag coefficient
    height : float
sbiri's avatar
sbiri committed
123
        original sensor height  [m]
124
    ref_ht : float
sbiri's avatar
sbiri committed
125
        reference height        [m]
126 127 128 129 130 131 132 133 134
    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
135
# ---------------------------------------------------------------------
sbiri's avatar
sbiri committed
136

sbiri's avatar
sbiri committed
137

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

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

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


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

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

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


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

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

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

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


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

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

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


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

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

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

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

sbiri's avatar
sbiri committed
390 391

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

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

    Returns
    -------
    psi : float
    """
    b, d = 2/3, 0.35
405 406 407 408 409 410 411 412
    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)
413 414
    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
415 416 417 418
    return psi
# ---------------------------------------------------------------------


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

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

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


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

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

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


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

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

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


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

    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
530 531
#------------------------------------------------------------------------------

sbiri's avatar
sbiri committed
532 533


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

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

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


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

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

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


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

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

sbiri's avatar
sbiri committed
612 613 614
    Returns
    -------
    dter : float
sbiri's avatar
sbiri committed
615
        cool skin correction         [K]
sbiri's avatar
sbiri committed
616
    dqer : float
sbiri's avatar
sbiri committed
617 618 619
       humidity corrction            [g/kg]
    delta : float
       cool skin thickness           [m]
sbiri's avatar
sbiri committed
620
    """
sbiri's avatar
sbiri committed
621 622
    # coded following Saunders (1967) with lambda = 6
    g = gc(lat, None)
sbiri's avatar
sbiri committed
623 624
    if (np.nanmin(sst) > 200):  # if sst in Kelvin convert to Celsius
        sst = sst-CtoK
sbiri's avatar
sbiri committed
625 626 627
    # ************  cool skin constants  *******
    # density of water, specific heat capacity of water, water viscosity,
    # thermal conductivity of water
sbiri's avatar
sbiri committed
628
    # rhow, cpw, visw, tcw = 1025, 4190, 1e-6, 0.6
sbiri's avatar
sbiri committed
629
    rhow, cpw, visw, tcw = 1022, 4000, 1e-6, 0.6
sbiri's avatar
sbiri committed
630
    aw = 2.1e-5*np.power(np.maximum(sst+3.2, 0), 0.79)
sbiri's avatar
sbiri committed
631 632
    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
633
    Rns = 0.945*Rs  # albedo correction
sbiri's avatar
sbiri committed
634 635 636 637 638 639
    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
640
    xlamx = 6*np.ones(sst.shape)
sbiri's avatar
sbiri committed
641
    xlamx = np.where(alq > 0, 6/(1+(bigc*alq/usr**4)**0.75)**0.333, 6)
sbiri's avatar
sbiri committed
642
    delta = np.where(alq > 0, xlamx*visw/(np.sqrt(rho/rhow)*usr),
sbiri's avatar
sbiri committed
643 644
                   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
645
    dter = qcol*delta/tcw
sbiri's avatar
sbiri committed
646
    dqer = wetc*dter
sbiri's avatar
sbiri committed
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 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))
        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
736 737 738
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
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 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 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
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]
    Rnl : TYPE
        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:
    aw = 2.1e-5*np.power(np.maximum(sst-CtoK+3.2, 0), 0.79) 
    # thermal expansion coefficient of sea-water (SST accurate enough#)
    # *** 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)
    for jwl in range(10): # itteration to solve implicitely eq. for warm layer
        dsst = skt-sst+dtc
        ## Buoyancy flux and stability parameter (zdl = -z/L) in water
        ZSRD = (Qnsol-Rns*Rd)/(rhow*cpw)
        ztmp = np.maximum(dsst, 0)
        zdl = np.where(ZSRD > 0, 2*(np.power(usrw, 2) *
                                    np.sqrt(ztmp/(5*rd0*gc(lat)*aw/visw)))+ZSRD,
                       np.power(usrw, 2)*np.sqrt(ztmp/(5*rd0*gc(lat)*aw/visw)))
        usr = np.maximum(usr, 1e-4 )
        zdL = zc3*aw*zdl/np.power(usr, 3)
        ## Stability function Phi_t(-z/L) (zdL is -z/L) :
        zphi = np.where(zdL > 0, (1+(5*zdL+4*np.power(zdL, 2)) /
                                  (1+3*zdL+0.25*np.power(zdL, 2)) +
                                  2/np.sqrt(1-16*(-np.abs(zdL)))),
                        1/np.sqrt(1-16*(-np.abs(zdL))))
        zz = zc4*(usrw)/zphi
        zz = np.maximum(np.abs(zz) , 1e-4)*np.sign(zz)
        dtwl = np.maximum(0, (zc5*ZSRD)/zz)
    return dtwl
#----------------------------------------------------------------------


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

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

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

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


sbiri's avatar
sbiri committed
880
def get_gust(beta, Ta, usr, tsrv, zi, lat):
sbiri's avatar
sbiri committed
881 882
    """
    Computes gustiness
sbiri's avatar
sbiri committed
883

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

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


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

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

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

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


sbiri's avatar
sbiri committed
988 989
def get_strs(h_in, monob, wind, zo, zot, zoq, dt, dq, dter, dqer, dtwl, ct, cq,
             cskin, wl, meth):
990 991 992 993 994 995
    """
    calculates star wind speed, temperature and specific humidity

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

    Returns
    -------
    usr : float
sbiri's avatar
sbiri committed
1032
        friction wind speed [m/s]
1033
    tsr : float
sbiri's avatar
sbiri committed
1034
        star temperature    [K]
1035
    qsr : float
sbiri's avatar
sbiri committed
1036
        star specific humidity [g/kg]
1037 1038 1039

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