flux_subs.py 23.7 KB
Newer Older
sbiri's avatar
sbiri committed
1
import numpy as np
sbiri's avatar
sbiri committed
2
from util_subs import (kappa, 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, usr, Ta, grav, meth):
sbiri's avatar
sbiri committed
8
    """
sbiri's avatar
sbiri committed
9
    Calculate 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]
15 16
    usr : float
        friction velocity      [m/s]
sbiri's avatar
sbiri committed
17
    Ta   : float
sbiri's avatar
sbiri committed
18
        air temperature        [K]
sbiri's avatar
sbiri committed
19 20
    grav : float
        gravity               [m/s^2]
sbiri's avatar
sbiri committed
21 22
    meth : str

sbiri's avatar
sbiri committed
23 24 25
    Returns
    -------
    cdn : float
sbiri's avatar
sbiri committed
26
    zo  : float
sbiri's avatar
sbiri committed
27
    """
28
    cdn = np.zeros(Ta.shape)*np.nan
sbiri's avatar
sbiri committed
29 30 31
    if meth == "S80":  # eq. 14 Smith 1980
        cdn = np.maximum((0.61+0.063*u10n)*0.001, (0.61+0.063*6)*0.001)
    elif meth == "LP82":
sbiri's avatar
sbiri committed
32 33
        #  Large & Pond 1981 u10n <11m/s & eq. 21 Large & Pond 1982
        cdn = np.where(u10n < 11, 1.2*0.001, (0.49+0.065*u10n)*0.001)
sbiri's avatar
sbiri committed
34
    elif meth in ["S88", "UA", "ecmwf", "C30", "C35", "Beljaars"]:
sbiri's avatar
sbiri committed
35
        cdn = cdn_from_roughness(u10n, usr, Ta, grav, meth)
sbiri's avatar
sbiri committed
36
    elif meth == "YT96":
37 38 39
        # 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
40
    elif meth == "NCAR":  # eq. 11 Large and Yeager 2009
sbiri's avatar
sbiri committed
41
        cdn = np.where(u10n > 0.5, (0.142+2.7/u10n+u10n/13.09 -
sbiri's avatar
sbiri committed
42
                                    3.14807e-10*np.power(u10n, 6))*1e-3,
sbiri's avatar
sbiri committed
43 44
                       (0.142+2.7/0.5+0.5/13.09 -
                        3.14807e-10*np.power(0.5, 6))*1e-3)
sbiri's avatar
sbiri committed
45 46
        cdn = np.where(u10n > 33, 2.34e-3, np.copy(cdn))
        cdn = np.maximum(np.copy(cdn), 0.1e-3)
sbiri's avatar
sbiri committed
47
    else:
sbiri's avatar
sbiri committed
48
        raise ValueError("Unknown method cdn: "+meth)
sbiri's avatar
sbiri committed
49 50 51

    zo = 10/np.exp(kappa/np.sqrt(cdn))
    return cdn, zo
sbiri's avatar
sbiri committed
52 53 54
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
55
def cdn_from_roughness(u10n, usr, Ta, grav, meth):
sbiri's avatar
sbiri committed
56
    """
sbiri's avatar
sbiri committed
57
    Calculate neutral drag coefficient from roughness length.
sbiri's avatar
sbiri committed
58

sbiri's avatar
sbiri committed
59 60 61
    Parameters
    ----------
    u10n : float
sbiri's avatar
sbiri committed
62
        neutral 10m wind speed [m/s]
63 64
    usr : float
        friction velocity      [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
    grav : float                [m/s]
        gravity
sbiri's avatar
sbiri committed
69 70
    meth : str

sbiri's avatar
sbiri committed
71 72 73 74
    Returns
    -------
    cdn : float
    """
sbiri's avatar
sbiri committed
75
    #  cdn = (0.61+0.063*u10n)*0.001
sbiri's avatar
sbiri committed
76
    zo, zc, zs = np.zeros(Ta.shape), np.zeros(Ta.shape), np.zeros(Ta.shape)
sbiri's avatar
sbiri committed
77
    for it in range(5):
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)/grav
81
            #  smooth surface roughness length (eq. 6 in Smith 88)
sbiri's avatar
sbiri committed
82
            zs = 0.11*visc_air(Ta)/usr
sbiri's avatar
sbiri committed
83 84
            zo = zc + zs  # eq. 7 & 8 in Smith 88
        elif meth == "UA":
sbiri's avatar
sbiri committed
85
            # valid for 0<u<18m/s # Zeng et al. 1998 (24)
sbiri's avatar
sbiri committed
86
            zo = 0.013*np.power(usr, 2)/grav+0.11*visc_air(Ta)/usr
sbiri's avatar
sbiri committed
87
        elif meth == "C30":  # eq. 25 Fairall et al. 1996a
sbiri's avatar
sbiri committed
88
            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
                         np.where(u10n > 18, 0.018, a))
sbiri's avatar
sbiri committed
91
            zo = a*np.power(usr, 2)/grav+0.11*visc_air(Ta)/usr
sbiri's avatar
sbiri committed
92
        elif meth == "C35":  # eq.6-11 Edson et al. (2013)
93 94
            zo = (0.11*visc_air(Ta)/usr +
                  np.minimum(0.0017*19-0.0050, 0.0017*u10n-0.0050) *
sbiri's avatar
sbiri committed
95
                  np.power(usr, 2)/grav)
sbiri's avatar
sbiri committed
96
        elif meth in ["ecmwf", "Beljaars"]:
97
            # eq. (3.26) p.38 over sea IFS Documentation cy46r1
sbiri's avatar
sbiri committed
98
            zo = 0.018*np.power(usr, 2)/grav+0.11*visc_air(Ta)/usr
sbiri's avatar
sbiri committed
99
        else:
sbiri's avatar
sbiri committed
100 101
            raise ValueError("Unknown method for cdn_from_roughness "+meth)

102
        cdn = np.power(kappa/np.log(10/zo), 2)
103 104 105 106
    return cdn
# ---------------------------------------------------------------------


107
def cd_calc(cdn, hin, hout, psim):
sbiri's avatar
sbiri committed
108
    """
sbiri's avatar
sbiri committed
109
    Calculate 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 ctqn_calc(corq, zol, cdn, usr, zo, Ta, meth):
sbiri's avatar
sbiri committed
132
    """
sbiri's avatar
sbiri committed
133
    Calculate neutral heat and moisture exchange coefficients.
sbiri's avatar
sbiri committed
134

sbiri's avatar
sbiri committed
135 136
    Parameters
    ----------
sbiri's avatar
sbiri committed
137 138
    corq : flag to select
           "ct" or "cq"
sbiri's avatar
sbiri committed
139 140 141
    zol  : float
        height over MO length
    cdn  : float
142
        neutral drag coefficient
143 144
    usr : float
        friction velocity      [m/s]
sbiri's avatar
sbiri committed
145
    zo   : float
sbiri's avatar
sbiri committed
146
        surface roughness       [m]
sbiri's avatar
sbiri committed
147
    Ta   : float
sbiri's avatar
sbiri committed
148
        air temperature         [K]
sbiri's avatar
sbiri committed
149 150
    meth : str

sbiri's avatar
sbiri committed
151 152
    Returns
    -------
sbiri's avatar
sbiri committed
153
    ctqn : float
sbiri's avatar
sbiri committed
154
        neutral heat exchange coefficient
sbiri's avatar
sbiri committed
155 156
    zotq : float
        roughness length for t or q
sbiri's avatar
sbiri committed
157
    """
sbiri's avatar
sbiri committed
158
    if meth in ["S80", "S88", "YT96"]:
sbiri's avatar
sbiri committed
159
        cqn = np.ones(Ta.shape)*1.20*0.001  # from S88
sbiri's avatar
sbiri committed
160
        ctn = np.ones(Ta.shape)*1.00*0.001
sbiri's avatar
sbiri committed
161 162
        zot = 10/(np.exp(np.power(kappa, 2) / (ctn*np.log(10/zo))))
        zoq = 10/(np.exp(np.power(kappa, 2) / (cqn*np.log(10/zo))))
sbiri's avatar
sbiri committed
163
    elif meth == "LP82":
sbiri's avatar
sbiri committed
164 165
        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)
166 167
        zot = 10/(np.exp(np.power(kappa, 2)/(ctn*np.log(10/zo))))
        zoq = 10/(np.exp(np.power(kappa, 2)/(cqn*np.log(10/zo))))
sbiri's avatar
sbiri committed
168
    elif meth == "NCAR":
169
        # Eq. (9),(12), (13) Large & Yeager, 2009
sbiri's avatar
sbiri committed
170
        cqn = np.maximum(34.6*0.001*np.sqrt(cdn), 0.1e-3)
171 172 173 174
        ctn = np.maximum(np.where(zol < 0, 32.7*1e-3*np.sqrt(cdn),
                                  18*1e-3*np.sqrt(cdn)), 0.1e-3)
        zot = 10/(np.exp(np.power(kappa, 2)/(ctn*np.log(10/zo))))
        zoq = 10/(np.exp(np.power(kappa, 2)/(cqn*np.log(10/zo))))
sbiri's avatar
sbiri committed
175
    elif meth == "UA":
sbiri's avatar
sbiri committed
176
        # Zeng et al. 1998 (25)
177 178
        rr = usr*zo/visc_air(Ta)
        zoq = zo/np.exp(2.67*np.power(rr, 1/4)-2.57)
179 180 181
        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
182
    elif meth == "C30":
sbiri's avatar
sbiri committed
183
        rr = zo*usr/visc_air(Ta)
184 185
        zoq = np.minimum(5e-5/np.power(rr, 0.6), 1.15e-4)  # moisture roughness
        zot = np.copy(zoq)  # temperature roughness
186 187
        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/zot)
sbiri's avatar
sbiri committed
188
    elif meth == "C35":
sbiri's avatar
sbiri committed
189
        rr = zo*usr/visc_air(Ta)
sbiri's avatar
sbiri committed
190
        zoq = np.minimum(5.8e-5/np.power(rr, 0.72), 1.6e-4)  # moisture rough.
191
        zot = np.copy(zoq)  # temperature roughness
192 193
        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/zot)
sbiri's avatar
sbiri committed
194
    elif meth in ["ecmwf", "Beljaars"]:
195
        # eq. (3.26) p.38 over sea IFS Documentation cy46r1
sbiri's avatar
sbiri committed
196 197
        zot = 0.40*visc_air(Ta)/usr
        zoq = 0.62*visc_air(Ta)/usr
198 199
        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/zot)
sbiri's avatar
sbiri committed
200
    else:
sbiri's avatar
sbiri committed
201
        raise ValueError("Unknown method ctqn: "+meth)
sbiri's avatar
sbiri committed
202 203 204 205 206 207 208 209

    if corq == "ct":
        ctqn = ctn
        zotq = zot
    elif corq == "cq":
        ctqn = cqn
        zotq = zoq
    else:
sbiri's avatar
sbiri committed
210 211
        raise ValueError("Unknown flag - should be ct or cq: "+corq)

sbiri's avatar
sbiri committed
212 213
    return ctqn, zotq
# ---------------------------------------------------------------------
sbiri's avatar
sbiri committed
214

sbiri's avatar
sbiri committed
215

sbiri's avatar
sbiri committed
216
def ctq_calc(cdn, cd, ctqn, hin, hout, psitq):
sbiri's avatar
sbiri committed
217
    """
sbiri's avatar
sbiri committed
218
    Calculate heat and moisture exchange coefficients at reference height.
sbiri's avatar
sbiri committed
219

sbiri's avatar
sbiri committed
220 221 222 223 224 225
    Parameters
    ----------
    cdn : float
        neutral drag coefficient
    cd  : float
        drag coefficient at reference height
sbiri's avatar
sbiri committed
226 227
    ctqn : float
        neutral heat or moisture exchange coefficient
228
    hin : float
sbiri's avatar
sbiri committed
229
        original temperature or humidity sensor height [m]
230
    hout : float
sbiri's avatar
sbiri committed
231
        reference height                   [m]
sbiri's avatar
sbiri committed
232 233
    psitq : float
        heat or moisture stability function
sbiri's avatar
sbiri committed
234

sbiri's avatar
sbiri committed
235 236
    Returns
    -------
sbiri's avatar
sbiri committed
237 238
    ctq : float
       heat or moisture exchange coefficient
sbiri's avatar
sbiri committed
239
    """
sbiri's avatar
sbiri committed
240
    ctq = (ctqn*np.sqrt(cd/cdn) /
sbiri's avatar
sbiri committed
241
           (1+ctqn*((np.log(hin/hout)-psitq)/(kappa*np.sqrt(cdn)))))
sbiri's avatar
sbiri committed
242

sbiri's avatar
sbiri committed
243 244
    return ctq
# ---------------------------------------------------------------------
sbiri's avatar
sbiri committed
245

sbiri's avatar
sbiri committed
246

sbiri's avatar
sbiri committed
247
def get_stabco(meth):
sbiri's avatar
sbiri committed
248 249
    r"""
    Give the coefficients $\alpha$, $\beta$, $\gamma$ for stability functions.
sbiri's avatar
sbiri committed
250 251 252 253 254 255 256 257 258 259

    Parameters
    ----------
    meth : str

    Returns
    -------
    coeffs : float
    """
    alpha, beta, gamma = 0, 0, 0
sbiri's avatar
sbiri committed
260
    if meth in ["S80", "S88", "NCAR", "UA", "ecmwf", "C30", "C35", "Beljaars"]:
sbiri's avatar
sbiri committed
261
        alpha, beta, gamma = 16, 0.25, 5  # Smith 1980, from Dyer (1974)
sbiri's avatar
sbiri committed
262
    elif meth == "LP82":
sbiri's avatar
sbiri committed
263
        alpha, beta, gamma = 16, 0.25, 7
sbiri's avatar
sbiri committed
264
    elif meth == "YT96":
sbiri's avatar
sbiri committed
265 266
        alpha, beta, gamma = 20, 0.25, 5
    else:
sbiri's avatar
sbiri committed
267
        raise ValueError("Unknown method stabco: "+meth)
sbiri's avatar
sbiri committed
268 269 270 271 272 273 274 275
    coeffs = np.zeros(3)
    coeffs[0] = alpha
    coeffs[1] = beta
    coeffs[2] = gamma
    return coeffs
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
276
def psim_calc(zol, meth):
sbiri's avatar
sbiri committed
277
    """
sbiri's avatar
sbiri committed
278
    Calculate momentum stability function.
sbiri's avatar
sbiri committed
279

sbiri's avatar
sbiri committed
280 281 282 283
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
284 285
    meth : str

sbiri's avatar
sbiri committed
286 287 288 289
    Returns
    -------
    psim : float
    """
sbiri's avatar
sbiri committed
290
    if meth == "ecmwf":
sbiri's avatar
sbiri committed
291
        psim = psim_ecmwf(zol)
sbiri's avatar
sbiri committed
292
    elif meth in ["C30", "C35"]:
sbiri's avatar
sbiri committed
293
        psim = psiu_26(zol, meth)
sbiri's avatar
sbiri committed
294
    elif meth == "Beljaars":  # Beljaars (1997) eq. 16, 17
295
        psim = np.where(zol < 0, psim_conv(zol, meth), psi_Bel(zol))
sbiri's avatar
sbiri committed
296
    else:
297 298
        psim = np.where(zol < 0, psim_conv(zol, meth),
                        psim_stab(zol, meth))
sbiri's avatar
sbiri committed
299
    return psim
sbiri's avatar
sbiri committed
300 301 302
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
303
def psit_calc(zol, meth):
sbiri's avatar
sbiri committed
304
    """
sbiri's avatar
sbiri committed
305
    Calculate heat stability function.
sbiri's avatar
sbiri committed
306

sbiri's avatar
sbiri committed
307 308 309 310
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
311
    meth : str
312
        parameterisation method
sbiri's avatar
sbiri committed
313

sbiri's avatar
sbiri committed
314 315 316 317
    Returns
    -------
    psit : float
    """
sbiri's avatar
sbiri committed
318
    if meth == "ecmwf":
319
        psit = np.where(zol < 0, psi_conv(zol, meth),
sbiri's avatar
sbiri committed
320
                        psi_ecmwf(zol))
sbiri's avatar
sbiri committed
321
    elif meth in ["C30", "C35"]:
sbiri's avatar
sbiri committed
322
        psit = psit_26(zol)
sbiri's avatar
sbiri committed
323
    elif meth == "Beljaars":  # Beljaars (1997) eq. 16, 17
324
        psit = np.where(zol < 0, psi_conv(zol, meth), psi_Bel(zol))
sbiri's avatar
sbiri committed
325
    else:
326 327
        psit = np.where(zol < 0, psi_conv(zol, meth),
                        psi_stab(zol, meth))
sbiri's avatar
sbiri committed
328
    return psit
sbiri's avatar
sbiri committed
329 330 331
# ---------------------------------------------------------------------


332
def psi_Bel(zol):
sbiri's avatar
sbiri committed
333
    """
sbiri's avatar
sbiri committed
334
    Calculate momentum/heat stability function.
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352

    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
353 354
def psi_ecmwf(zol):
    """
sbiri's avatar
sbiri committed
355 356 357
    Calculate heat stability function for stable conditions.

    For method ecmwf
sbiri's avatar
sbiri committed
358

sbiri's avatar
sbiri committed
359 360 361 362
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
363

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

sbiri's avatar
sbiri committed
374 375

def psit_26(zol):
sbiri's avatar
sbiri committed
376
    """
sbiri's avatar
sbiri committed
377
    Compute temperature structure function as in C35.
sbiri's avatar
sbiri committed
378 379 380 381 382 383 384 385 386 387 388

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

    Returns
    -------
    psi : float
    """
    b, d = 2/3, 0.35
389 390 391 392 393 394 395 396 397 398
    dzol = np.minimum(d*zol, 50)
    psi = -1*((1+b*zol)**1.5+b*(zol-14.28)*np.exp(-dzol)+8.525)
    k = np.where(zol < 0)
    x = np.sqrt(1-15*zol[k])
    psik = 2*np.log((1+x)/2)
    x = np.power(1-34.15*zol[k], 1/3)
    psic = (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))
    f = np.power(zol[k], 2)/(1+np.power(zol[k], 2))
    psi[k] = (1-f)*psik+f*psic
sbiri's avatar
sbiri committed
399 400 401 402
    return psi
# ---------------------------------------------------------------------


403
def psi_conv(zol, meth):
sbiri's avatar
sbiri committed
404
    """
sbiri's avatar
sbiri committed
405
    Calculate heat stability function for unstable conditions.
sbiri's avatar
sbiri committed
406

sbiri's avatar
sbiri committed
407 408 409 410
    Parameters
    ----------
    zol : float
        height over MO length
411 412
    meth : str
        parameterisation method
sbiri's avatar
sbiri committed
413

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


426
def psi_stab(zol, meth):
sbiri's avatar
sbiri committed
427
    """
sbiri's avatar
sbiri committed
428
    Calculate heat stability function for stable conditions.
sbiri's avatar
sbiri committed
429

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

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


sbiri's avatar
sbiri committed
448 449
def psim_ecmwf(zol):
    """
sbiri's avatar
sbiri committed
450
    Calculate momentum stability function for method ecmwf.
sbiri's avatar
sbiri committed
451

sbiri's avatar
sbiri committed
452 453 454 455
    Parameters
    ----------
    zol : float
        height over MO length
sbiri's avatar
sbiri committed
456

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


sbiri's avatar
sbiri committed
473
def psiu_26(zol, meth):
sbiri's avatar
sbiri committed
474
    """
sbiri's avatar
sbiri committed
475
    Compute velocity structure function C35.
sbiri's avatar
sbiri committed
476 477 478 479 480 481 482 483 484 485

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

    Returns
    -------
    psi : float
    """
sbiri's avatar
sbiri committed
486 487
    if meth == "C30":
        dzol = np.minimum(0.35*zol, 50)  # stable
488
        psi = -1*((1+zol)+0.6667*(zol-14.28)*np.exp(-dzol)+8.525)
sbiri's avatar
sbiri committed
489
        k = np.where(zol < 0)  # unstable
490 491 492 493 494 495 496 497 498
        x = (1-15*zol[k])**0.25
        psik = (2*np.log((1+x)/2)+np.log((1+x*x)/2)-2*np.arctan(x) +
                2*np.arctan(1))
        x = (1-10.15*zol[k])**(1/3)
        psic = (1.5*np.log((1+x+x*x)/3) -
                np.sqrt(3)*np.arctan((1+2*x)/np.sqrt(3)) +
                4*np.arctan(1)/np.sqrt(3))
        f = zol[k]**2/(1+zol[k]**2)
        psi[k] = (1-f)*psik+f*psic
sbiri's avatar
sbiri committed
499
    elif meth == "C35":
500
        dzol = np.minimum(50, 0.35*zol)  # stable
sbiri's avatar
sbiri committed
501
        a, b, c, d = 0.7, 3/4, 5, 0.35
502 503 504
        psi = -1*(a*zol+b*(zol-c/d)*np.exp(-dzol)+b*c/d)
        k = np.where(zol < 0)  # unstable
        x = np.power(1-15*zol[k], 1/4)
sbiri's avatar
sbiri committed
505 506
        psik = 2*np.log((1+x)/2)+np.log((1+x*x)/2) - \
            2*np.arctan(x)+2*np.arctan(1)
507 508 509 510 511
        x = np.power(1-10.15*zol[k], 1/3)
        psic = (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))
        f = np.power(zol[k], 2)/(1+np.power(zol[k], 2))
        psi[k] = (1-f)*psik+f*psic
512

sbiri's avatar
sbiri committed
513 514
    return psi
# ----------------------------------------------------------------------------
sbiri's avatar
sbiri committed
515 516


517
def psim_conv(zol, meth):
sbiri's avatar
sbiri committed
518
    """
sbiri's avatar
sbiri committed
519
    Calculate momentum stability function for unstable conditions.
sbiri's avatar
sbiri committed
520

sbiri's avatar
sbiri committed
521 522 523 524
    Parameters
    ----------
    zol : float
        height over MO length
525 526
    meth : str
        parameterisation method
sbiri's avatar
sbiri committed
527

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


541
def psim_stab(zol, meth):
sbiri's avatar
sbiri committed
542
    """
sbiri's avatar
sbiri committed
543
    Calculate momentum stability function for stable conditions.
sbiri's avatar
sbiri committed
544

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

sbiri's avatar
sbiri committed
552 553 554 555
    Returns
    -------
    psim : float
    """
556 557
    coeffs = get_stabco(meth)
    gamma = coeffs[2]
sbiri's avatar
sbiri committed
558 559 560 561 562
    psim = -gamma*zol
    return psim
# ---------------------------------------------------------------------


sbiri's avatar
sbiri committed
563
def get_gust(beta, Ta, usr, tsrv, zi, grav):
sbiri's avatar
sbiri committed
564
    """
sbiri's avatar
sbiri committed
565
    Compute gustiness.
sbiri's avatar
sbiri committed
566

sbiri's avatar
sbiri committed
567 568 569 570 571
    Parameters
    ----------
    beta : float
        constant
    Ta : float
sbiri's avatar
sbiri committed
572
        air temperature   [K]
sbiri's avatar
sbiri committed
573
    usr : float
sbiri's avatar
sbiri committed
574
        friction velocity [m/s]
sbiri's avatar
sbiri committed
575
    tsrv : float
sbiri's avatar
sbiri committed
576
        star virtual temperature of air [K]
sbiri's avatar
sbiri committed
577
    zi : int
sbiri's avatar
sbiri committed
578
        scale height of the boundary layer depth [m]
sbiri's avatar
sbiri committed
579 580
    grav : float
        gravity
sbiri's avatar
sbiri committed
581

sbiri's avatar
sbiri committed
582 583
    Returns
    -------
sbiri's avatar
sbiri committed
584
    ug : float        [m/s]
sbiri's avatar
sbiri committed
585
    """
sbiri's avatar
sbiri committed
586
    if np.nanmax(Ta) < 200:  # convert to K if in Celsius
sbiri's avatar
sbiri committed
587
        Ta = Ta+273.16
sbiri's avatar
sbiri committed
588 589
    # minus sign to allow cube root
    Bf = (-grav/Ta)*usr*tsrv
sbiri's avatar
sbiri committed
590 591 592 593 594
    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
595

sbiri's avatar
sbiri committed
596
def get_strs(hin, monob, wind, zo, zot, zoq, dt, dq, cd, ct, cq, meth):
597
    """
sbiri's avatar
sbiri committed
598
    Calculate star wind speed, temperature and specific humidity.
599 600 601

    Parameters
    ----------
sbiri's avatar
sbiri committed
602
    hin : float
sbiri's avatar
sbiri committed
603
        sensor heights [m]
604
    monob : float
sbiri's avatar
sbiri committed
605
        M-O length     [m]
606
    wind : float
sbiri's avatar
sbiri committed
607
        wind speed     [m/s]
608
    zo : float
sbiri's avatar
sbiri committed
609
        momentum roughness length    [m]
610
    zot : float
sbiri's avatar
sbiri committed
611
        temperature roughness length [m]
612
    zoq : float
sbiri's avatar
sbiri committed
613
        moisture roughness length    [m]
614
    dt : float
sbiri's avatar
sbiri committed
615
        temperature difference       [K]
616
    dq : float
sbiri's avatar
sbiri committed
617
        specific humidity difference [g/kg]
618 619 620 621 622
    ct : float
        temperature exchange coefficient
    cq : float
        moisture exchange coefficient
    meth : str
sbiri's avatar
sbiri committed
623 624
        bulk parameterisation method option: "S80", "S88", "LP82", "YT96",
        "UA", "NCAR", "C30", "C35", "ecmwf", "Beljaars"
625 626 627 628

    Returns
    -------
    usr : float
sbiri's avatar
sbiri committed
629
        friction wind speed [m/s]
630
    tsr : float
sbiri's avatar
sbiri committed
631
        star temperature    [K]
632
    qsr : float
sbiri's avatar
sbiri committed
633
        star specific humidity [g/kg]
634 635

    """
sbiri's avatar
sbiri committed
636
    if meth == "UA":
sbiri's avatar
sbiri committed
637 638
        # Zeng et al. 1998
        # away from extremes UA follows e.g. S80
sbiri's avatar
sbiri committed
639
        usr = wind*np.sqrt(cd)
sbiri's avatar
sbiri committed
640 641 642 643 644 645
        tsr = ct*wind*dt/usr
        qsr = cq*wind*dq/usr

        # momentum
        hol0 = hin[0]/np.copy(monob)
        # very unstable (Zeng et al. 1998 eq 7)
sbiri's avatar
sbiri committed
646 647 648 649 650 651
        usr = np.where(
            hol0 <= -1.574, wind*kappa/(np.log(-1.574*monob/zo) -
                                        psim_calc(-1.574, meth) +
                                        psim_calc(zo/monob, meth) +
                                        1.14*(np.power(-hin[0]/monob, 1/3) -
                                              np.power(1.574, 1/3))), usr)
sbiri's avatar
sbiri committed
652
        # very stable (Zeng et al. 1998 eq 10)
sbiri's avatar
sbiri committed
653 654 655
        usr = np.where(
            hol0 > 1, wind*kappa/(np.log(monob/zo)+5-5*zo/monob +
                                  5*np.log(hin[0]/monob)+hin[0]/monob-1), usr)
sbiri's avatar
sbiri committed
656 657 658 659

        # temperature
        hol1 = hin[1]/np.copy(monob)
        # very unstable (Zeng et al. 1998 eq 11)
sbiri's avatar
sbiri committed
660 661 662 663 664
        tsr = np.where(
            hol1 < -0.465, kappa*dt/(np.log((-0.465*monob)/zot) -
                                     psit_calc(-0.465, meth) +
                                     0.8*(np.power(0.465, -1/3) -
                                          np.power(-hin[1]/monob, -1/3))), tsr)
sbiri's avatar
sbiri committed
665
        # very stable (Zeng et al. 1998 eq 14)
sbiri's avatar
sbiri committed
666 667 668
        tsr = np.where(
            hol1 > 1, kappa*(dt)/(np.log(monob/zot)+5-5*zot/monob +
                                  5*np.log(hin[1]/monob)+hin[1]/monob-1), tsr)
sbiri's avatar
sbiri committed
669 670 671 672

        # humidity
        hol2 = hin[2]/monob
        # very unstable (Zeng et al. 1998 eq 11)
sbiri's avatar
sbiri committed
673 674 675 676 677 678
        qsr = np.where(
            hol2 < -0.465, kappa*dq/(np.log((-0.465*monob)/zoq) -
                                     psit_calc(-0.465, meth) +
                                     psit_calc(zoq/monob, meth) +
                                     0.8*(np.power(0.465, -1/3) -
                                          np.power(-hin[2]/monob, -1/3))), qsr)
sbiri's avatar
sbiri committed
679
        # very stable (Zeng et al. 1998 eq 14)
sbiri's avatar
sbiri committed
680 681 682
        qsr = np.where(hol2 > 1, kappa*dq/(np.log(monob/zoq)+5-5*zoq/monob +
                                           5*np.log(hin[2]/monob) +
                                           hin[2]/monob-1), qsr)
683
    else:
sbiri's avatar
sbiri committed
684
        usr = wind*np.sqrt(cd)
sbiri's avatar
sbiri committed
685 686
        tsr = ct*wind*dt/usr
        qsr = cq*wind*dq/usr
687
    return usr, tsr, qsr
sbiri's avatar
sbiri committed
688
# ---------------------------------------------------------------------
sbiri's avatar
sbiri committed
689 690 691 692


def get_tsrv(tsr, qsr, Ta, qair):
    """
sbiri's avatar
sbiri committed
693 694
    Calculate virtual star temperature.

sbiri's avatar
sbiri committed
695 696 697 698 699 700 701 702 703 704
    Parameters
    ----------
    tsr : float
        star temperature (K)
    qsr : float
        star specific humidity (g/kg)
    Ta : float
        air temperature (K)
    qair : float
        air specific humidity (g/kg)
sbiri's avatar
sbiri committed
705

sbiri's avatar
sbiri committed
706 707 708 709
    Returns
    -------
    tsrv : float
        virtual star temperature (K)
sbiri's avatar
sbiri committed
710

sbiri's avatar
sbiri committed
711 712 713 714
    """
    # as in aerobulk One_on_L in mod_phymbl.f90
    tsrv = tsr*(1+0.6077*qair)+0.6077*Ta*qsr
    return tsrv
sbiri's avatar
sbiri committed
715

sbiri's avatar
sbiri committed
716
# ---------------------------------------------------------------------
sbiri's avatar
sbiri committed
717 718


sbiri's avatar
sbiri committed
719 720
def get_Rb(grav, usr, hin_u, hin_t, tv, dtv, wind, monob, meth):
    """
sbiri's avatar
sbiri committed
721 722
    Calculate bulk Richardson number.

sbiri's avatar
sbiri committed
723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745
    Parameters
    ----------
    grav : float
        acceleration due to gravity (m/s2)
    usr : float
        friction wind speed (m/s)
    hin_u : float
        u sensor height (m)
    hin_t : float
        t sensor height (m)
    tv : float
        virtual temperature (K)
    dtv : float
        virtual temperature difference, air and sea (K)
    wind : float
        wind speed (m/s)
    monob : float
        Monin-Obukhov length from previous iteration step (m)
    psim : float
        momentum stability function
    meth : str
        bulk parameterisation method option: "S80", "S88", "LP82", "YT96",
        "UA", "NCAR", "C30", "C35", "ecmwf", "Beljaars"
sbiri's avatar
sbiri committed
746

sbiri's avatar
sbiri committed
747 748 749 750
    Returns
    -------
    Rb  : float
       Richardson number
sbiri's avatar
sbiri committed
751

sbiri's avatar
sbiri committed
752 753
    """
    # now input dtv
sbiri's avatar
sbiri committed
754 755
    # tvs = sst*(1+0.6077*qsea) # virtual SST
    # dtv = tv - tvs          # virtual air - sea temp. diff
sbiri's avatar
sbiri committed
756 757 758 759 760
    # adjust wind to t measurement height
    uz = (wind-usr/kappa*(np.log(hin_u/hin_t)-psim_calc(hin_u/monob, meth) +
                          psim_calc(hin_t/monob, meth)))
    Rb = grav*dtv*hin_t/(tv*uz*uz)
    return Rb
sbiri's avatar
sbiri committed
761

sbiri's avatar
sbiri committed
762
# ---------------------------------------------------------------------
sbiri's avatar
sbiri committed
763 764


sbiri's avatar
sbiri committed
765 766
def get_LRb(Rb, hin_t, monob, zo, zot, meth):
    """
sbiri's avatar
sbiri committed
767 768
    Calculate Monin-Obukhov length following ecmwf (IFS Documentation cy46r1).

sbiri's avatar
sbiri committed
769
    default for methods ecmwf and Beljaars
sbiri's avatar
sbiri committed
770

sbiri's avatar
sbiri committed
771 772 773 774 775 776 777 778 779 780 781 782 783 784 785
    Parameters
    ----------
    Rb  : float
       Richardson number
    hin_t : float
        t sensor height (m)
    monob : float
        Monin-Obukhov length from previous iteration step (m)
    zo   : float
        surface roughness       (m)
    zot   : float
        temperature roughness length       (m)
    meth : str
        bulk parameterisation method option: "S80", "S88", "LP82", "YT96",
        "UA", "NCAR", "C30", "C35", "ecmwf", "Beljaars"
sbiri's avatar
sbiri committed
786

sbiri's avatar
sbiri committed
787 788 789 790
    Returns
    -------
    monob : float
        M-O length (m)
sbiri's avatar
sbiri committed
791

sbiri's avatar
sbiri committed
792
    """
sbiri's avatar
sbiri committed
793 794 795 796 797
    zol = Rb*(np.power(
        np.log((hin_t+zo)/zo)-psim_calc((hin_t+zo)/monob, meth) +
        psim_calc(zo/monob, meth), 2)/(np.log((hin_t+zo)/zot) -
                                       psit_calc((hin_t+zo)/monob, meth) +
                                       psit_calc(zot/monob, meth)))
sbiri's avatar
sbiri committed
798 799
    monob = hin_t/zol
    return monob
sbiri's avatar
sbiri committed
800

sbiri's avatar
sbiri committed
801
# ---------------------------------------------------------------------
sbiri's avatar
sbiri committed
802 803


sbiri's avatar
sbiri committed
804 805
def get_Ltsrv(tsrv, grav, tv, usr):
    """
sbiri's avatar
sbiri committed
806 807
    Calculate Monin-Obukhov length from tsrv.

sbiri's avatar
sbiri committed
808 809 810 811 812 813 814 815 816 817
    Parameters
    ----------
    tsrv : float
        virtual star temperature (K)
    grav : float
        acceleration due to gravity (m/s2)
    tv : float
        virtual temperature (K)
    usr : float
        friction wind speed (m/s)
sbiri's avatar
sbiri committed
818

sbiri's avatar
sbiri committed
819 820 821 822
    Returns
    -------
    monob : float
        M-O length (m)
sbiri's avatar
sbiri committed
823

sbiri's avatar
sbiri committed
824
    """
sbiri's avatar
sbiri committed
825
    # tmp = (g*kappa*tsrv /
sbiri's avatar
sbiri committed
826
    #        np.maximum(np.power(usr, 2)*Ta*(1+0.6077*qair), 1e-9))
sbiri's avatar
sbiri committed
827 828
    # tmp = np.minimum(np.abs(tmp), 200)*np.sign(tmp)
    # monob = 1/np.copy(tmp)
sbiri's avatar
sbiri committed
829 830 831
    tsrv = np.maximum(np.abs(tsrv), 1e-9)*np.sign(tsrv)
    monob = (np.power(usr, 2)*tv)/(grav*kappa*tsrv)
    return monob
sbiri's avatar
sbiri committed
832

sbiri's avatar
sbiri committed
833
# ---------------------------------------------------------------------