flux_subs.py 36.5 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"):
39
        # for u<3 YT96 convert usr in eq. 21 to cdn
sbiri's avatar
sbiri committed
40
        cdn = np.where((u10n < 6) & (u10n >= 3),
41 42 43 44 45
                        (0.29+3.1/u10n+7.7/np.power(u10n, 2))*0.001,
                        np.where((u10n >= 6),
                        (0.60 + 0.070*u10n)*0.001,
                        np.power((0.10038+u10n*2.17e-3 +
                                  np.power(u10n, 2)*2.78e-3 -
sbiri's avatar
sbiri committed
46
                                  np.power(u10n, 3)*4.4e-5)/u10n, 2)))
sbiri's avatar
sbiri committed
47
    elif (meth == "LY04"):
sbiri's avatar
sbiri committed
48
        cdn = np.where(u10n >= 0.5,
49 50
                       (0.142+(2.7/u10n)+(u10n/13.09))*0.001,
                       (0.142+(2.7/0.5)+(0.5/13.09))*0.001)
sbiri's avatar
sbiri committed
51
    else:
sbiri's avatar
sbiri committed
52
        print("unknown method cdn: "+meth)
sbiri's avatar
sbiri committed
53
    return cdn
sbiri's avatar
sbiri committed
54 55 56
# ---------------------------------------------------------------------


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

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

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


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

    Parameters
    ----------
    cdn : float
        neutral drag coefficient
    height : float
sbiri's avatar
sbiri committed
129
        original sensor height  [m]
130
    ref_ht : float
sbiri's avatar
sbiri committed
131
        reference height        [m]
132 133 134 135 136 137 138 139 140
    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
141
# ---------------------------------------------------------------------
sbiri's avatar
sbiri committed
142

sbiri's avatar
sbiri committed
143

sbiri's avatar
sbiri committed
144
def ctcqn_calc(zol, cdn, u10n, zo, Ta, meth="S80"):
sbiri's avatar
sbiri committed
145 146
    """
    Calculates neutral heat and moisture exchange coefficients
sbiri's avatar
sbiri committed
147

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

sbiri's avatar
sbiri committed
162 163 164 165 166 167 168
    Returns
    -------
    ctn : float
        neutral heat exchange coefficient
    cqn : float
        neutral moisture exchange coefficient
    """
sbiri's avatar
sbiri committed
169
    if (meth == "S80" or meth == "S88" or meth == "YT96"):
sbiri's avatar
sbiri committed
170
        cqn = np.ones(Ta.shape)*1.20*0.001  # from S88
sbiri's avatar
sbiri committed
171
        ctn = np.ones(Ta.shape)*1.00*0.001
sbiri's avatar
sbiri committed
172
    elif (meth == "LP82"):
sbiri's avatar
sbiri committed
173 174
        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
175 176 177 178 179
    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
180
        # Zeng et al. 1998 (25)
sbiri's avatar
sbiri committed
181 182
        re=usr*zo/visc_air(Ta)
        zoq = zo/np.exp(2.67*np.power(re, 1/4)-2.57)
sbiri's avatar
sbiri committed
183 184 185 186 187
        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
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
    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
212 213 214
        # 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
215 216
        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
217
    elif (meth == "ecmwf" or meth == "Beljaars"):
218
        # eq. (3.26) p.38 over sea IFS Documentation cy46r1
sbiri's avatar
sbiri committed
219 220 221 222 223
        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
224
    else:
sbiri's avatar
sbiri committed
225
        print("unknown method ctcqn: "+meth)
sbiri's avatar
sbiri committed
226
    return ctn, cqn
sbiri's avatar
sbiri committed
227 228 229
# ---------------------------------------------------------------------


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

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

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


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

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

sbiri's avatar
sbiri committed
305 306 307 308
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
309 310
    meth : str

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


sbiri's avatar
sbiri committed
328
def psit_calc(zol, meth="S80"):
sbiri's avatar
sbiri committed
329 330
    """
    Calculates heat stability function
sbiri's avatar
sbiri committed
331

sbiri's avatar
sbiri committed
332 333 334 335
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
336
    meth : str
337
        parameterisation method
sbiri's avatar
sbiri committed
338

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


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

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

sbiri's avatar
sbiri committed
383 384 385 386
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
387

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

sbiri's avatar
sbiri committed
398 399

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

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

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


427
def psi_conv(zol, meth):
sbiri's avatar
sbiri committed
428 429
    """
    Calculates heat stability function for unstable conditions
sbiri's avatar
sbiri committed
430

sbiri's avatar
sbiri committed
431 432 433 434
    Parameters
    ----------
    zol : float
        height over MO length
435 436
    meth : str
        parameterisation method
sbiri's avatar
sbiri committed
437

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


450
def psi_stab(zol, meth):
sbiri's avatar
sbiri committed
451 452
    """
    Calculates heat stability function for stable conditions
sbiri's avatar
sbiri committed
453

sbiri's avatar
sbiri committed
454 455 456 457
    Parameters
    ----------
    zol : float
        height over MO length
458 459
    meth : str
        parameterisation method
sbiri's avatar
sbiri committed
460

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


sbiri's avatar
sbiri committed
472 473 474
def psim_ecmwf(zol):
    """
    Calculates momentum stability function for method ecmwf
sbiri's avatar
sbiri committed
475

sbiri's avatar
sbiri committed
476 477 478 479
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
480

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


sbiri's avatar
sbiri committed
497
def psiu_26(zol, meth):
sbiri's avatar
sbiri committed
498 499
    """
    Computes velocity structure function C35
sbiri's avatar
sbiri committed
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 535 536 537

    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
538 539
#------------------------------------------------------------------------------

sbiri's avatar
sbiri committed
540 541


542
def psim_conv(zol, meth):
sbiri's avatar
sbiri committed
543 544
    """
    Calculates momentum stability function for unstable conditions
sbiri's avatar
sbiri committed
545

sbiri's avatar
sbiri committed
546 547 548 549
    Parameters
    ----------
    zol : float
        height over MO length
550 551
    meth : str
        parameterisation method
sbiri's avatar
sbiri committed
552

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


566
def psim_stab(zol, meth):
sbiri's avatar
sbiri committed
567 568
    """
    Calculates momentum stability function for stable conditions
sbiri's avatar
sbiri committed
569

sbiri's avatar
sbiri committed
570 571 572 573
    Parameters
    ----------
    zol : float
        height over MO length
574 575
    meth : str
        parameterisation method
sbiri's avatar
sbiri committed
576

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


sbiri's avatar
sbiri committed
588 589 590 591
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
592

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

sbiri's avatar
sbiri committed
620 621 622
    Returns
    -------
    dter : float
sbiri's avatar
sbiri committed
623
        cool skin correction         [K]
sbiri's avatar
sbiri committed
624
    dqer : float
sbiri's avatar
sbiri committed
625 626 627
       humidity corrction            [g/kg]
    delta : float
       cool skin thickness           [m]
sbiri's avatar
sbiri committed
628
    """
sbiri's avatar
sbiri committed
629 630
    # coded following Saunders (1967) with lambda = 6
    g = gc(lat, None)
sbiri's avatar
sbiri committed
631 632
    if (np.nanmin(sst) > 200):  # if sst in Kelvin convert to Celsius
        sst = sst-CtoK
sbiri's avatar
sbiri committed
633 634 635 636
    # ************  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
637
    aw = 2.1e-5*np.power(np.maximum(sst+3.2, 0), 0.79)
sbiri's avatar
sbiri committed
638 639
    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
640
    Rns = 0.945*Rs  # albedo correction
sbiri's avatar
sbiri committed
641 642 643 644 645 646
    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
647
    xlamx = 6*np.ones(sst.shape)
sbiri's avatar
sbiri committed
648
    xlamx = np.where(alq > 0, 6/(1+(bigc*alq/usr**4)**0.75)**0.333, 6)
sbiri's avatar
sbiri committed
649
    delta = np.where(alq > 0, xlamx*visw/(np.sqrt(rho/rhow)*usr),
sbiri's avatar
sbiri committed
650 651
                   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
652
    dter = qcol*delta/tcw
sbiri's avatar
sbiri committed
653
    dqer = wetc*dter
sbiri's avatar
sbiri committed
654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738
    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
739 740
        # fs = np.maximum(0.065+11*delta-(6.6e-5/delta)*(1-np.exp(-delta/8e-4)),
        #                 0.01)
sbiri's avatar
sbiri committed
741 742 743 744
        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
745 746 747
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
748 749 750 751 752 753 754 755 756 757 758
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
759
    Rnl : float
sbiri's avatar
sbiri committed
760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
        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
791
    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
792 793 794 795 796 797 798 799 800 801 802
    # *** 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
803
    for jwl in range(10): # itteration to solve implicitely equation for warm layer
sbiri's avatar
sbiri committed
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 885 886 887
        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
888
def get_gust(beta, Ta, usr, tsrv, zi, lat):
sbiri's avatar
sbiri committed
889 890
    """
    Computes gustiness
sbiri's avatar
sbiri committed
891

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

sbiri's avatar
sbiri committed
907 908
    Returns
    -------
sbiri's avatar
sbiri committed
909
    ug : float        [m/s]
sbiri's avatar
sbiri committed
910
    """
911
    if (np.nanmax(Ta) < 200):  # convert to K if in Celsius
sbiri's avatar
sbiri committed
912 913
        Ta = Ta+273.16
    g = gc(lat, None)
sbiri's avatar
sbiri committed
914
    Bf = (-g/Ta)*usr*tsrv
sbiri's avatar
sbiri committed
915 916 917 918 919 920
    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
921
def get_L(L, lat, usr, tsr, qsr, t10n, hin, Ta, sst, qair, qsea, q10n,
sbiri's avatar
sbiri committed
922
          wind, monob, meth):
sbiri's avatar
sbiri committed
923 924 925 926 927 928 929
    """
    calculates Monin-Obukhov length and virtual star temperature

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

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

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


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

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

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

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