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

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

sbiri's avatar
sbiri committed
6

sbiri's avatar
sbiri committed
7
def cdn_calc(u10n, Ta, Tp, lat, meth="S80"):
sbiri's avatar
sbiri committed
8
    """
sbiri's avatar
sbiri committed
9
    Calculates neutral drag coefficient
sbiri's avatar
sbiri committed
10

sbiri's avatar
sbiri committed
11 12 13
    Parameters
    ----------
    u10n : float
sbiri's avatar
sbiri committed
14
        neutral 10m wind speed [m/s]
sbiri's avatar
sbiri committed
15
    Ta   : float
sbiri's avatar
sbiri committed
16
        air temperature        [K]
sbiri's avatar
sbiri committed
17 18
    Tp   : float
        wave period
19
    lat : float
sbiri's avatar
sbiri committed
20
        latitude               [degE]
sbiri's avatar
sbiri committed
21 22
    meth : str

sbiri's avatar
sbiri committed
23 24 25 26
    Returns
    -------
    cdn : float
    """
27
    cdn = np.zeros(Ta.shape)*np.nan
sbiri's avatar
sbiri committed
28
    if (meth == "S80"):
29
        cdn = (0.61+0.063*u10n)*0.001
sbiri's avatar
sbiri committed
30
    elif (meth == "LP82"):
sbiri's avatar
sbiri committed
31 32 33 34
       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
35
          meth == "C35" or meth == "Beljaars"):
sbiri's avatar
sbiri committed
36 37
        cdn = cdn_from_roughness(u10n, Ta, None, lat, meth)
    elif (meth == "YT96"):
38 39 40
        # convert usr in eq. 21 to cdn to expand for low wind speeds
        cdn = np.power((0.10038+u10n*2.17e-3+np.power(u10n, 2)*2.78e-3 -
                        np.power(u10n, 3)*4.4e-5)/u10n, 2)
sbiri's avatar
sbiri committed
41
    elif (meth == "LY04"):
sbiri's avatar
sbiri committed
42
        cdn = np.where(u10n >= 0.5,
43 44
                       (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
45
    else:
sbiri's avatar
sbiri committed
46
        print("unknown method cdn: "+meth)
sbiri's avatar
sbiri committed
47
    return cdn
sbiri's avatar
sbiri committed
48 49 50
# ---------------------------------------------------------------------


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

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

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


107
def cd_calc(cdn, hin, hout, psim):
sbiri's avatar
sbiri committed
108 109
    """
    Calculates drag coefficient at reference height
110 111 112 113 114

    Parameters
    ----------
    cdn : float
        neutral drag coefficient
115
    hin : float
116
        wind speed height       [m]
117
    hout : float
sbiri's avatar
sbiri committed
118
        reference height        [m]
119 120 121 122 123 124 125
    psim : float
        momentum stability function

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

sbiri's avatar
sbiri committed
130

sbiri's avatar
sbiri committed
131
def ctcqn_calc(zol, cdn, u10n, zo, Ta, meth="S80"):
sbiri's avatar
sbiri committed
132
    """
sbiri's avatar
sbiri committed
133
    Calculates neutral heat and moisture exchange coefficients
sbiri's avatar
sbiri committed
134

sbiri's avatar
sbiri committed
135 136 137 138 139
    Parameters
    ----------
    zol  : float
        height over MO length
    cdn  : float
140
        neutral drag coefficient
sbiri's avatar
sbiri committed
141
    u10n : float
sbiri's avatar
sbiri committed
142
        neutral 10m wind speed  [m/s]
sbiri's avatar
sbiri committed
143
    zo   : float
sbiri's avatar
sbiri committed
144
        surface roughness       [m]
sbiri's avatar
sbiri committed
145
    Ta   : float
sbiri's avatar
sbiri committed
146
        air temperature         [K]
sbiri's avatar
sbiri committed
147 148
    meth : str

sbiri's avatar
sbiri committed
149 150 151 152 153 154 155
    Returns
    -------
    ctn : float
        neutral heat exchange coefficient
    cqn : float
        neutral moisture exchange coefficient
    """
sbiri's avatar
sbiri committed
156
    if (meth == "S80" or meth == "S88" or meth == "YT96"):
sbiri's avatar
sbiri committed
157
        cqn = np.ones(Ta.shape)*1.20*0.001  # from S88
sbiri's avatar
sbiri committed
158
        ctn = np.ones(Ta.shape)*1.00*0.001
sbiri's avatar
sbiri committed
159
    elif (meth == "LP82"):
sbiri's avatar
sbiri committed
160 161
        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
162 163 164 165 166
    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
167
        # Zeng et al. 1998 (25)
sbiri's avatar
sbiri committed
168 169
        re=usr*zo/visc_air(Ta)
        zoq = zo/np.exp(2.67*np.power(re, 1/4)-2.57)
170 171 172
        zot = np.copy(zoq)
        cqn = np.power(kappa, 2)/(np.log(10/zo)*np.log(10/zoq))
        ctn = np.power(kappa, 2)/(np.log(10/zo)*np.log(10/zoq))
sbiri's avatar
sbiri committed
173 174 175
    elif (meth == "C30"):
        usr = np.sqrt(cdn*np.power(u10n, 2))
        rr = zo*usr/visc_air(Ta)
176 177
        zoq = np.minimum(5e-5/np.power(rr, 0.6), 1.15e-4)  # moisture roughness
        zot = np.copy(zoq)  # temperature roughness
sbiri's avatar
sbiri committed
178 179 180 181 182
        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)
183 184
        zoq = np.minimum(5.8e-5/np.power(rr, 0.72), 1.6e-4)  # moisture roughness
        zot = np.copy(zoq)  # temperature roughness
sbiri's avatar
sbiri committed
185 186
        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
187
    elif (meth == "ecmwf" or meth == "Beljaars"):
188
        # eq. (3.26) p.38 over sea IFS Documentation cy46r1
sbiri's avatar
sbiri committed
189 190 191 192 193
        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
194
    else:
sbiri's avatar
sbiri committed
195
        print("unknown method ctcqn: "+meth)
sbiri's avatar
sbiri committed
196
    return ctn, cqn
sbiri's avatar
sbiri committed
197 198 199
# ---------------------------------------------------------------------


200
def ctcq_calc(cdn, cd, ctn, cqn, ht, hq, hout, psit, psiq):
sbiri's avatar
sbiri committed
201 202
    """
    Calculates heat and moisture exchange coefficients at reference height
sbiri's avatar
sbiri committed
203

sbiri's avatar
sbiri committed
204 205 206 207 208 209 210 211 212 213
    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
214
    ht : float
sbiri's avatar
sbiri committed
215
        original temperature sensor height [m]
sbiri's avatar
sbiri committed
216
    hq : float
sbiri's avatar
sbiri committed
217
        original moisture sensor height    [m]
218
    hout : float
sbiri's avatar
sbiri committed
219
        reference height                   [m]
sbiri's avatar
sbiri committed
220 221 222 223
    psit : float
        heat stability function
    psiq : float
        moisture stability function
sbiri's avatar
sbiri committed
224

sbiri's avatar
sbiri committed
225 226 227
    Returns
    -------
    ct : float
228
       heat exchange coefficient
sbiri's avatar
sbiri committed
229
    cq : float
230
       moisture exchange coefficient
sbiri's avatar
sbiri committed
231
    """
232
    ct = (ctn*np.sqrt(cd/cdn) /
233
          (1+ctn*((np.log(ht/hout)-psit)/(kappa*np.sqrt(cdn)))))
234
    cq = (cqn*np.sqrt(cd/cdn) /
235
          (1+cqn*((np.log(hq/hout)-psiq)/(kappa*np.sqrt(cdn)))))
sbiri's avatar
sbiri committed
236
    return ct, cq
sbiri's avatar
sbiri committed
237 238 239
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
240
def get_stabco(meth="S80"):
sbiri's avatar
sbiri committed
241 242
    """
    Gives the coefficients \\alpha, \\beta, \\gamma for stability functions
sbiri's avatar
sbiri committed
243 244 245 246 247 248 249 250 251 252 253

    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
254
        meth == "UA" or meth == "ecmwf" or meth == "C30" or
sbiri's avatar
sbiri committed
255
        meth == "C35" or meth == "Beljaars"):
sbiri's avatar
sbiri committed
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
        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
271
def psim_calc(zol, meth="S80"):
sbiri's avatar
sbiri committed
272 273
    """
    Calculates momentum stability function
sbiri's avatar
sbiri committed
274

sbiri's avatar
sbiri committed
275 276 277 278
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
279 280
    meth : str

sbiri's avatar
sbiri committed
281 282 283 284
    Returns
    -------
    psim : float
    """
sbiri's avatar
sbiri committed
285 286
    if (meth == "ecmwf"):
        psim = psim_ecmwf(zol)
sbiri's avatar
sbiri committed
287
    elif (meth == "C30" or meth == "C35"):  # or meth == "C40"
sbiri's avatar
sbiri committed
288
        psim = psiu_26(zol, meth)
289 290
    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
291
    else:
292 293
        psim = np.where(zol < 0, psim_conv(zol, meth),
                        psim_stab(zol, meth))
sbiri's avatar
sbiri committed
294
    return psim
sbiri's avatar
sbiri committed
295 296 297
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
298
def psit_calc(zol, meth="S80"):
sbiri's avatar
sbiri committed
299 300
    """
    Calculates heat stability function
sbiri's avatar
sbiri committed
301

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

sbiri's avatar
sbiri committed
309 310 311 312
    Returns
    -------
    psit : float
    """
sbiri's avatar
sbiri committed
313
    if (meth == "ecmwf"):
314
        psit = np.where(zol < 0, psi_conv(zol, meth),
sbiri's avatar
sbiri committed
315
                        psi_ecmwf(zol))
sbiri's avatar
sbiri committed
316
    elif (meth == "C30" or meth == "C35"):
sbiri's avatar
sbiri committed
317
        psit = psit_26(zol)
318 319
    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
320
    else:
321 322
        psit = np.where(zol < 0, psi_conv(zol, meth),
                        psi_stab(zol, meth))
sbiri's avatar
sbiri committed
323
    return psit
sbiri's avatar
sbiri committed
324 325 326
# ---------------------------------------------------------------------


327
def psi_Bel(zol):
sbiri's avatar
sbiri committed
328 329
    """
    Calculates momentum/heat stability function
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347

    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
348 349 350 351
def psi_ecmwf(zol):
    """
    Calculates heat stability function for stable conditions
    for method ecmwf
sbiri's avatar
sbiri committed
352

sbiri's avatar
sbiri committed
353 354 355 356
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
357

sbiri's avatar
sbiri committed
358 359 360 361
    Returns
    -------
    psit : float
    """
362
    # eq (3.22) p. 37 IFS Documentation cy46r1
sbiri's avatar
sbiri committed
363 364
    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
365
    return psit
sbiri's avatar
sbiri committed
366 367
# ---------------------------------------------------------------------

sbiri's avatar
sbiri committed
368 369

def psit_26(zol):
sbiri's avatar
sbiri committed
370 371
    """
    Computes temperature structure function as in C35
sbiri's avatar
sbiri committed
372 373 374 375 376 377 378 379 380 381 382

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

    Returns
    -------
    psi : float
    """
    b, d = 2/3, 0.35
383 384 385 386 387 388 389 390
    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)
391 392
    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
393 394 395 396
    return psi
# ---------------------------------------------------------------------


397
def psi_conv(zol, meth):
sbiri's avatar
sbiri committed
398 399
    """
    Calculates heat stability function for unstable conditions
sbiri's avatar
sbiri committed
400

sbiri's avatar
sbiri committed
401 402 403 404
    Parameters
    ----------
    zol : float
        height over MO length
405 406
    meth : str
        parameterisation method
sbiri's avatar
sbiri committed
407

sbiri's avatar
sbiri committed
408 409 410 411
    Returns
    -------
    psit : float
    """
412 413
    coeffs = get_stabco(meth)
    alpha, beta = coeffs[0], coeffs[1]
414 415
    xtmp = np.power(1-alpha*zol, beta)
    psit = 2*np.log((1+np.power(xtmp, 2))*0.5)
sbiri's avatar
sbiri committed
416
    return psit
sbiri's avatar
sbiri committed
417 418 419
# ---------------------------------------------------------------------


420
def psi_stab(zol, meth):
sbiri's avatar
sbiri committed
421 422
    """
    Calculates heat stability function for stable conditions
sbiri's avatar
sbiri committed
423

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

sbiri's avatar
sbiri committed
431 432 433 434
    Returns
    -------
    psit : float
    """
435 436
    coeffs = get_stabco(meth)
    gamma = coeffs[2]
sbiri's avatar
sbiri committed
437
    psit = -gamma*zol
sbiri's avatar
sbiri committed
438
    return psit
sbiri's avatar
sbiri committed
439 440 441
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
442 443 444
def psim_ecmwf(zol):
    """
    Calculates momentum stability function for method ecmwf
sbiri's avatar
sbiri committed
445

sbiri's avatar
sbiri committed
446 447 448 449
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
450

sbiri's avatar
sbiri committed
451 452 453 454
    Returns
    -------
    psim : float
    """
455
    # eq (3.20, 3.22) p. 37 IFS Documentation cy46r1
sbiri's avatar
sbiri committed
456
    coeffs = get_stabco("ecmwf")
457 458
    alpha, beta = coeffs[0], coeffs[1]
    xtmp = np.power(1-alpha*zol, beta)
sbiri's avatar
sbiri committed
459
    a, b, c, d = 1, 2/3, 5, 0.35
460 461 462
    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
463 464 465 466
    return psim
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
467
def psiu_26(zol, meth):
sbiri's avatar
sbiri committed
468 469
    """
    Computes velocity structure function C35
sbiri's avatar
sbiri committed
470 471 472 473 474 475 476 477 478 479 480

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

    Returns
    -------
    psi : float
    """
    if (meth == "C30"):
481 482
        dzol = np.minimum(0.35*zol, 50) # stable
        psi = np.where(zol >= 0, -((1+zol)+0.6667*(zol-14.28)*np.exp(-dzol) +
sbiri's avatar
sbiri committed
483 484 485 486 487 488 489 490 491 492
                                  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)
493 494
    elif (meth == "C35"): #  or meth == "C40"
        dzol = np.minimum(0.35*zol, 50)  # stable
sbiri's avatar
sbiri committed
495
        a, b, c, d = 0.7, 3/4, 5, 0.35
496
        psi = np.where(zol >= 0, -(a*zol+b*(zol-c/d)*np.exp(-dzol)+b*c/d),
sbiri's avatar
sbiri committed
497 498 499 500 501 502 503 504 505 506 507
                       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
508 509
#------------------------------------------------------------------------------

sbiri's avatar
sbiri committed
510 511


512
def psim_conv(zol, meth):
sbiri's avatar
sbiri committed
513 514
    """
    Calculates momentum stability function for unstable conditions
sbiri's avatar
sbiri committed
515

sbiri's avatar
sbiri committed
516 517 518 519
    Parameters
    ----------
    zol : float
        height over MO length
520 521
    meth : str
        parameterisation method
sbiri's avatar
sbiri committed
522

sbiri's avatar
sbiri committed
523 524 525 526
    Returns
    -------
    psim : float
    """
527 528
    coeffs = get_stabco(meth)
    alpha, beta = coeffs[0], coeffs[1]
529 530
    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
531
            2*np.arctan(xtmp)+np.pi/2)
sbiri's avatar
sbiri committed
532
    return psim
sbiri's avatar
sbiri committed
533 534 535
# ---------------------------------------------------------------------


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

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

sbiri's avatar
sbiri committed
547 548 549 550
    Returns
    -------
    psim : float
    """
551 552
    coeffs = get_stabco(meth)
    gamma = coeffs[2]
sbiri's avatar
sbiri committed
553 554 555 556 557
    psim = -gamma*zol
    return psim
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
558 559 560 561
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
562

sbiri's avatar
sbiri committed
563 564 565
    Parameters
    ----------
    sst : float
sbiri's avatar
sbiri committed
566
        sea surface temperature      [K]
sbiri's avatar
sbiri committed
567
    qsea : float
sbiri's avatar
sbiri committed
568
        specific humidity over sea   [g/kg]
sbiri's avatar
sbiri committed
569
    rho : float
sbiri's avatar
sbiri committed
570
        density of air               [kg/m^3]
sbiri's avatar
sbiri committed
571
    Rs : float
sbiri's avatar
sbiri committed
572
        downward shortwave radiation [Wm-2]
sbiri's avatar
sbiri committed
573
    Rnl : float
sbiri's avatar
sbiri committed
574
        upwelling IR radiation       [Wm-2]
sbiri's avatar
sbiri committed
575
    cp : float
sbiri's avatar
sbiri committed
576
       specific heat of air at constant pressure [J/K/kg]
sbiri's avatar
sbiri committed
577
    lv : float
sbiri's avatar
sbiri committed
578 579 580
       latent heat of vaporization   [J/kg]
    delta : float
       cool skin thickness           [m]
sbiri's avatar
sbiri committed
581
    usr : float
sbiri's avatar
sbiri committed
582
       friction velocity             [m/s]
sbiri's avatar
sbiri committed
583
    tsr : float
sbiri's avatar
sbiri committed
584
       star temperature              [K]
sbiri's avatar
sbiri committed
585
    qsr : float
sbiri's avatar
sbiri committed
586
       star humidity                 [g/kg]
sbiri's avatar
sbiri committed
587
    lat : float
sbiri's avatar
sbiri committed
588
       latitude                      [degE]
sbiri's avatar
sbiri committed
589

sbiri's avatar
sbiri committed
590 591 592
    Returns
    -------
    dter : float
sbiri's avatar
sbiri committed
593
        cool skin correction         [K]
sbiri's avatar
sbiri committed
594
    dqer : float
sbiri's avatar
sbiri committed
595 596 597
       humidity corrction            [g/kg]
    delta : float
       cool skin thickness           [m]
sbiri's avatar
sbiri committed
598
    """
sbiri's avatar
sbiri committed
599 600
    # coded following Saunders (1967) with lambda = 6
    g = gc(lat, None)
sbiri's avatar
sbiri committed
601 602
    if (np.nanmin(sst) > 200):  # if sst in Kelvin convert to Celsius
        sst = sst-CtoK
sbiri's avatar
sbiri committed
603 604 605 606
    # ************  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
607
    aw = 2.1e-5*np.power(np.maximum(sst+3.2, 0), 0.79)
sbiri's avatar
sbiri committed
608 609
    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
610
    Rns = 0.945*Rs  # albedo correction
sbiri's avatar
sbiri committed
611 612 613 614 615 616
    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
617
    xlamx = 6*np.ones(sst.shape)
sbiri's avatar
sbiri committed
618
    xlamx = np.where(alq > 0, 6/(1+(bigc*alq/usr**4)**0.75)**0.333, 6)
sbiri's avatar
sbiri committed
619
    delta = np.where(alq > 0, xlamx*visw/(np.sqrt(rho/rhow)*usr),
sbiri's avatar
sbiri committed
620 621
                   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
622
    dter = qcol*delta/tcw
sbiri's avatar
sbiri committed
623
    dqer = wetc*dter
sbiri's avatar
sbiri committed
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712
    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
713 714 715
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
716 717 718 719 720 721 722 723 724 725 726
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
727
    Rnl : float
sbiri's avatar
sbiri committed
728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
        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
759
    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
760 761 762 763 764 765 766 767 768 769 770
    # *** 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
771
    for jwl in range(10): # itteration to solve implicitely equation for warm layer
sbiri's avatar
sbiri committed
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
        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
856
def get_gust(beta, Ta, usr, tsrv, zi, lat):
sbiri's avatar
sbiri committed
857 858
    """
    Computes gustiness
sbiri's avatar
sbiri committed
859

sbiri's avatar
sbiri committed
860 861 862 863 864
    Parameters
    ----------
    beta : float
        constant
    Ta : float
sbiri's avatar
sbiri committed
865
        air temperature   [K]
sbiri's avatar
sbiri committed
866
    usr : float
sbiri's avatar
sbiri committed
867
        friction velocity [m/s]
sbiri's avatar
sbiri committed
868
    tsrv : float
sbiri's avatar
sbiri committed
869
        star virtual temperature of air [K]
sbiri's avatar
sbiri committed
870
    zi : int
sbiri's avatar
sbiri committed
871
        scale height of the boundary layer depth [m]
sbiri's avatar
sbiri committed
872 873
    lat : float
        latitude
sbiri's avatar
sbiri committed
874

sbiri's avatar
sbiri committed
875 876
    Returns
    -------
sbiri's avatar
sbiri committed
877
    ug : float        [m/s]
sbiri's avatar
sbiri committed
878
    """
879
    if (np.nanmax(Ta) < 200):  # convert to K if in Celsius
sbiri's avatar
sbiri committed
880 881
        Ta = Ta+273.16
    g = gc(lat, None)
sbiri's avatar
sbiri committed
882
    Bf = (-g/Ta)*usr*tsrv
sbiri's avatar
sbiri committed
883 884 885 886 887 888
    ug = np.ones(np.shape(Ta))*0.2
    ug = np.where(Bf > 0, beta*np.power(Bf*zi, 1/3), 0.2)
    return ug
# ---------------------------------------------------------------------


889 890
def get_L(L, lat, usr, tsr, qsr, hin, Ta, sst, qair, qsea, wind, monob, psim,
          meth):
sbiri's avatar
sbiri committed
891 892 893 894 895 896 897
    """
    calculates Monin-Obukhov length and virtual star temperature

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

    Returns
    -------
932 933 934 935
    tsrv : float
        virtual star temperature (K)
    monob : float
        M-O length (m)
sbiri's avatar
sbiri committed
936 937

    """
sbiri's avatar
sbiri committed
938
    g = gc(lat)
939
    Rb = np.empty(sst.shape)
940 941 942 943 944 945 946 947 948 949
    # as in NCAR, LY04
    tsrv = tsr*(1+0.6077*qair)+0.6077*Ta*qsr
    # from eq. 3.24 ifs Cy46r1 pp. 37
    thvs = sst*(1+0.6077*qsea) # virtual SST
    dthv = (Ta-sst)*(1+0.6077*qair)+0.6077*Ta*(qair-qsea)
    tv = 0.5*(thvs+Ta*(1+0.6077*qair)) # estimate tv within surface layer
    # adjust wind to T sensor's height
    uz = (wind-usr/kappa*(np.log(hin[0]/hin[1])-psim +
                            psim_calc(hin[1]/monob, meth)))
    Rb = g*dthv*hin[1]/(tv*uz*uz)
950
    if (L == "S80"):
sbiri's avatar
sbiri committed
951 952 953
        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
954
        temp = np.minimum(np.abs(temp), 10/hin[0])*np.sign(temp)
sbiri's avatar
sbiri committed
955 956
        monob = 1/np.copy(temp)
    elif (L == "ecmwf"):
sbiri's avatar
sbiri committed
957 958
        zo = (0.11*visc_air(Ta)/usr+0.018*np.power(usr, 2)/g)
        zot = 0.40*visc_air(Ta)/usr
959
        zol = (Rb*(np.power(np.log((hin[1]+zo)/zo)-psim_calc((hin[1]+zo) /
sbiri's avatar
sbiri committed
960 961
                                                              monob, meth) +
                            psim_calc(zo/monob, meth), 2) /
962 963
                   (np.log((hin[1]+zo)/zot) -
                    psit_calc((hin[1]+zo)/monob, meth) +
sbiri's avatar
sbiri committed
964
                    psit_calc(zot/monob, meth))))
sbiri's avatar
sbiri committed
965
        monob = hin[1]/zol
966
    return tsrv, monob, Rb
sbiri's avatar
sbiri committed
967 968 969
#------------------------------------------------------------------------------


sbiri's avatar
sbiri committed
970
def get_strs(hin, monob, wind, zo, zot, zoq, dt, dq, dter, dqer, dtwl, ct, cq,
sbiri's avatar
sbiri committed
971
             cskin, wl, meth):
972 973 974 975 976
    """
    calculates star wind speed, temperature and specific humidity

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

    Returns
    -------
    usr : float
sbiri's avatar
sbiri committed
1014
        friction wind speed [m/s]
1015
    tsr : float
sbiri's avatar
sbiri committed
1016
        star temperature    [K]
1017
    qsr : float
sbiri's avatar
sbiri committed
1018
        star specific humidity [g/kg]
1019 1020 1021

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