acgc.met API documentation

# acgc.met

Functions for computing boundary layer meteorological properties

View Source

      1#!/usr/bin/env python3
      2# -*- coding: utf-8 -*-
      3"""Functions for computing boundary layer meteorological properties
      4
      5"""
      6
      7import numpy as np
      8
      9def pbl_bulk_richardson( z, thetav, ws, crit=0.25):
     10    '''Compute PBL mixing height from critical value of bulk Richardson number
     11
     12    Arguments
     13    ---------
     14    z : array, float
     15        altitude, m
     16    thetav  : array, float
     17        virtual potential temperature, K
     18    ws : array, float
     19        wind speed, m/s
     20    crit : float
     21        critical value defining the top of the PBL mixing (default=0.25)
     22
     23    Returns
     24    -------
     25    zpbl : float
     26        altitude of PBL top, m
     27    L : float
     28        level number within z array corresponding to altitude zpbl
     29    '''
     30
     31    #    if (max(z) > 100):
     32    #        print('pbl_bulk_richardson: z must by in km')
     33    #        raise SystemExit()
     34
     35    g0 = 9.81
     36
     37    # Convert km -> m
     38    #zm = z * 1e3
     39    # Input is already in meters
     40    zm = z
     41
     42    # Bulk Richardson number
     43    Ri = g0 * zm * ( thetav - thetav[0] ) / thetav[0] / ws**2
     44
     45    # mixing height is the lowest level where Ri > 0.25
     46    L=0
     47    while Ri[L] < crit:
     48        L+=1
     49    zpbl1 = z[L]
     50
     51    # Use a 2nd order polynomial fit to smooth the profile and
     52    # refine the height of the critical value
     53    pf = np.polyfit( z[L-1:L+2], Ri[L-1:L+2]-crit, 2 )
     54
     55    # Root from quadratic formula (positive root because Ri is increasing in z)
     56    zpbl = (-pf[1] + np.sqrt( pf[1]**2 - 4*pf[0]*pf[2] ) ) / (2 * pf[0])
     57
     58    # Add a fraction to L indicating where in the interval the pbl height is
     59    if zpbl >= zpbl1:
     60        L = L + (zpbl-zpbl1) / ( z[L+1] - z[L] )
     61    else:
     62        L = L + (zpbl-zpbl1) / ( z[L]   - z[L-1])
     63
     64    return zpbl, L
     65
     66def pbl_parcel(z, thetav, delta=0.5 ):
     67    '''Compute PBL mixing height with parcel method
     68
     69    The mixing height is defined as the altitude where a rising parcel of air
     70    from the base of the profile becomes neutrally buoyant. More formally, where
     71    the virtual potential temperature exceeds the value at the base plus delta.
     72
     73    Arguments
     74    ---------
     75    z : array, float
     76        altitude, m
     77    thetav  : array, float
     78        virtual potential temperature, K or C
     79    delta : float
     80        incremental threshold of virtual potential temperature, K or C
     81
     82    Returns
     83    -------
     84    zpbl : float
     85        altitude of PBL top, m
     86    L : float
     87        level number within z array corresponding to altitude zpbl
     88    '''
     89
     90    # mixing height is the highest level where thetav < thetav(0) + delta
     91    L=0
     92    while thetav[L+1] < ( thetav[0] + delta ):
     93        L += 1
     94    zpbl1 = z[L]
     95
     96    if L == 0:
     97        # Linear interpolation for lowest layer
     98        zpbl = np.interp(thetav[0]+delta, thetav[0:2], z[0:2])
     99        #zpbl = ( zpbl[0] + zpbl[1] ) / 2
    100
    101    else:
    102        # Use a 2nd order polynomial fit to smooth the profile and
    103        # refine the height of the critical value
    104        pf = np.polyfit( z[L-1:L+2], thetav[L-1:L+2]-(thetav[0]+delta), 2 )
    105
    106        # Root from quadratic formula (positive root because theta_v is increasing in z)
    107        zpbl = (-pf[1] + np.sqrt( pf[1]**2 - 4*pf[0]*pf[2] ) ) / (2 * pf[0])
    108
    109        # Add a fraction to L indicating where in the interval the pbl height is
    110        if zpbl >= zpbl1:
    111            L = L + (zpbl-zpbl1) / ( z[L+1] - z[L] )
    112        else:
    113            L = L + (zpbl-zpbl1) / ( z[L]   - z[L-1])
    114
    115    return zpbl, L
    116
    117def mo_wind(surfaceP, q2m, temp2m, ustar, shf, lhf, z0, d, z):
    118    '''Predict wind speed at altitude z using Monin-Obukhov similarity theory
    119
    120    Arguments
    121    ---------
    122    surfaceP : float
    123        surface pressure, hPa
    124    q2m : float
    125        specific humidity at 2m, kg/kg
    126    temp2m : float
    127        temperature at 2m, K
    128    ustar : float
    129        friction velocity, m/s
    130    shf, lhf : float
    131        sensible and latent heat fluxes, W/m2
    132    z0 : float
    133        roughness length, m
    134    d : float
    135        displacement height, m
    136    z : float
    137        height at which wind speed will be calculated, m
    138
    139    Returns
    140    -------
    141    ws : float
    142        wind speed at altitude z, m/s
    143    '''
    144    # Bring in SHF, LHF, ustar, z0 from A1 files
    145
    146    p = surfaceP*100    # calculations require pascals
    147    mmair = .02897      # kg/mol
    148    mmwater = 0.01801   # kg/mol
    149    Rgas = 8.3145       # Ideal gas constant (J/K*mol)
    150    g = 9.81            # gravity
    151    k = 0.4             # von Karman's constant
    152    Cp = 1004           # Specific Heat capacity of air (J/kg*K)
    153    rdcp = 0.286        # Rd/Cp constant
    154
    155    Lv = 2.501e06 - 2370*(temp2m-273.15)        # units: J/kg
    156    rho = (p*mmair)/(Rgas*temp2m)               # units: kg/m^3
    157    theta = temp2m*(1000.0/(p/100.0))**rdcp     # units are Kelvin
    158
    159    # Vapor pressure, Pa
    160    e2m = q2m / (1-q2m) * (mmair/mmwater) * surfaceP
    161    #OLD FORMULA: (P/10 appears to be an error)
    162    #e2m = q2m*(mmair/mmwater)*(surfaceP/10)
    163
    164    # Virtual potential temperature, K
    165    theta_e = theta*(1.0 + e2m/p * 0.61 )
    166    #OLD METHOD: theta_e = theta/(1.0-e2m/p*(1.0-0.622))
    167
    168    # Obukhov length, m
    169    # numerator and denominator, for safe division in case den=0
    170    num = -(ustar**3 * rho * theta_e)
    171    den =  (k*g*(shf*(1+0.61*q2m)/Cp+0.61*theta*(lhf/Lv)))
    172    if np.abs(den) > 0:
    173        L = num/den
    174    else:
    175        L = np.inf
    176
    177    # Parameters within similarity functions
    178    zeta = (z - d)/L
    179    zeta0 = z0/L
    180    eta0 = (1-(15*zeta0))**(1/4)
    181    etaR = (1-(15*zeta))**(1/4)
    182
    183
    184    if L == 0 or np.absolute(L) > 10**5:
    185        # Neutral
    186        ws = (ustar/k) * np.log( (z-d) / z0 )
    187    elif L < 10**5 and L > 0:
    188        # Stable
    189        ws = (ustar/k) * np.log( (z-d) / z0 ) + 4.7 * (zeta - zeta0)
    190    elif L > -10**5 and L < 0:
    191        # Unstable
    192        ws = (ustar/k) * np.log( (z-d) / z0 ) + \
    193             np.log( ( (eta0**2+1) * (eta0+1)**2) /
    194                     ( (etaR**2+1) * (etaR+1)**2) ) + \
    195             2*(np.arctan(etaR) - np.arctan(eta0))
    196    else:
    197        ws = np.NaN
    198
    199    return ws

def pbl_bulk_richardson(z, thetav, ws, crit=0.25): View Source

    10def pbl_bulk_richardson( z, thetav, ws, crit=0.25):
    11    '''Compute PBL mixing height from critical value of bulk Richardson number
    12
    13    Arguments
    14    ---------
    15    z : array, float
    16        altitude, m
    17    thetav  : array, float
    18        virtual potential temperature, K
    19    ws : array, float
    20        wind speed, m/s
    21    crit : float
    22        critical value defining the top of the PBL mixing (default=0.25)
    23
    24    Returns
    25    -------
    26    zpbl : float
    27        altitude of PBL top, m
    28    L : float
    29        level number within z array corresponding to altitude zpbl
    30    '''
    31
    32    #    if (max(z) > 100):
    33    #        print('pbl_bulk_richardson: z must by in km')
    34    #        raise SystemExit()
    35
    36    g0 = 9.81
    37
    38    # Convert km -> m
    39    #zm = z * 1e3
    40    # Input is already in meters
    41    zm = z
    42
    43    # Bulk Richardson number
    44    Ri = g0 * zm * ( thetav - thetav[0] ) / thetav[0] / ws**2
    45
    46    # mixing height is the lowest level where Ri > 0.25
    47    L=0
    48    while Ri[L] < crit:
    49        L+=1
    50    zpbl1 = z[L]
    51
    52    # Use a 2nd order polynomial fit to smooth the profile and
    53    # refine the height of the critical value
    54    pf = np.polyfit( z[L-1:L+2], Ri[L-1:L+2]-crit, 2 )
    55
    56    # Root from quadratic formula (positive root because Ri is increasing in z)
    57    zpbl = (-pf[1] + np.sqrt( pf[1]**2 - 4*pf[0]*pf[2] ) ) / (2 * pf[0])
    58
    59    # Add a fraction to L indicating where in the interval the pbl height is
    60    if zpbl >= zpbl1:
    61        L = L + (zpbl-zpbl1) / ( z[L+1] - z[L] )
    62    else:
    63        L = L + (zpbl-zpbl1) / ( z[L]   - z[L-1])
    64
    65    return zpbl, L

Compute PBL mixing height from critical value of bulk Richardson number

###### Arguments

z : array, float altitude, m thetav : array, float virtual potential temperature, K ws : array, float wind speed, m/s crit : float critical value defining the top of the PBL mixing (default=0.25)

###### Returns

- zpbl (float): altitude of PBL top, m
- L (float): level number within z array corresponding to altitude zpbl

def pbl_parcel(z, thetav, delta=0.5): View Source

     67def pbl_parcel(z, thetav, delta=0.5 ):
     68    '''Compute PBL mixing height with parcel method
     69
     70    The mixing height is defined as the altitude where a rising parcel of air
     71    from the base of the profile becomes neutrally buoyant. More formally, where
     72    the virtual potential temperature exceeds the value at the base plus delta.
     73
     74    Arguments
     75    ---------
     76    z : array, float
     77        altitude, m
     78    thetav  : array, float
     79        virtual potential temperature, K or C
     80    delta : float
     81        incremental threshold of virtual potential temperature, K or C
     82
     83    Returns
     84    -------
     85    zpbl : float
     86        altitude of PBL top, m
     87    L : float
     88        level number within z array corresponding to altitude zpbl
     89    '''
     90
     91    # mixing height is the highest level where thetav < thetav(0) + delta
     92    L=0
     93    while thetav[L+1] < ( thetav[0] + delta ):
     94        L += 1
     95    zpbl1 = z[L]
     96
     97    if L == 0:
     98        # Linear interpolation for lowest layer
     99        zpbl = np.interp(thetav[0]+delta, thetav[0:2], z[0:2])
    100        #zpbl = ( zpbl[0] + zpbl[1] ) / 2
    101
    102    else:
    103        # Use a 2nd order polynomial fit to smooth the profile and
    104        # refine the height of the critical value
    105        pf = np.polyfit( z[L-1:L+2], thetav[L-1:L+2]-(thetav[0]+delta), 2 )
    106
    107        # Root from quadratic formula (positive root because theta_v is increasing in z)
    108        zpbl = (-pf[1] + np.sqrt( pf[1]**2 - 4*pf[0]*pf[2] ) ) / (2 * pf[0])
    109
    110        # Add a fraction to L indicating where in the interval the pbl height is
    111        if zpbl >= zpbl1:
    112            L = L + (zpbl-zpbl1) / ( z[L+1] - z[L] )
    113        else:
    114            L = L + (zpbl-zpbl1) / ( z[L]   - z[L-1])
    115
    116    return zpbl, L

Compute PBL mixing height with parcel method

The mixing height is defined as the altitude where a rising parcel of air from the base of the profile becomes neutrally buoyant. More formally, where the virtual potential temperature exceeds the value at the base plus delta.

###### Arguments

z : array, float altitude, m thetav : array, float virtual potential temperature, K or C delta : float incremental threshold of virtual potential temperature, K or C

###### Returns

- zpbl (float): altitude of PBL top, m
- L (float): level number within z array corresponding to altitude zpbl

def mo_wind(surfaceP, q2m, temp2m, ustar, shf, lhf, z0, d, z): View Source

    118def mo_wind(surfaceP, q2m, temp2m, ustar, shf, lhf, z0, d, z):
    119    '''Predict wind speed at altitude z using Monin-Obukhov similarity theory
    120
    121    Arguments
    122    ---------
    123    surfaceP : float
    124        surface pressure, hPa
    125    q2m : float
    126        specific humidity at 2m, kg/kg
    127    temp2m : float
    128        temperature at 2m, K
    129    ustar : float
    130        friction velocity, m/s
    131    shf, lhf : float
    132        sensible and latent heat fluxes, W/m2
    133    z0 : float
    134        roughness length, m
    135    d : float
    136        displacement height, m
    137    z : float
    138        height at which wind speed will be calculated, m
    139
    140    Returns
    141    -------
    142    ws : float
    143        wind speed at altitude z, m/s
    144    '''
    145    # Bring in SHF, LHF, ustar, z0 from A1 files
    146
    147    p = surfaceP*100    # calculations require pascals
    148    mmair = .02897      # kg/mol
    149    mmwater = 0.01801   # kg/mol
    150    Rgas = 8.3145       # Ideal gas constant (J/K*mol)
    151    g = 9.81            # gravity
    152    k = 0.4             # von Karman's constant
    153    Cp = 1004           # Specific Heat capacity of air (J/kg*K)
    154    rdcp = 0.286        # Rd/Cp constant
    155
    156    Lv = 2.501e06 - 2370*(temp2m-273.15)        # units: J/kg
    157    rho = (p*mmair)/(Rgas*temp2m)               # units: kg/m^3
    158    theta = temp2m*(1000.0/(p/100.0))**rdcp     # units are Kelvin
    159
    160    # Vapor pressure, Pa
    161    e2m = q2m / (1-q2m) * (mmair/mmwater) * surfaceP
    162    #OLD FORMULA: (P/10 appears to be an error)
    163    #e2m = q2m*(mmair/mmwater)*(surfaceP/10)
    164
    165    # Virtual potential temperature, K
    166    theta_e = theta*(1.0 + e2m/p * 0.61 )
    167    #OLD METHOD: theta_e = theta/(1.0-e2m/p*(1.0-0.622))
    168
    169    # Obukhov length, m
    170    # numerator and denominator, for safe division in case den=0
    171    num = -(ustar**3 * rho * theta_e)
    172    den =  (k*g*(shf*(1+0.61*q2m)/Cp+0.61*theta*(lhf/Lv)))
    173    if np.abs(den) > 0:
    174        L = num/den
    175    else:
    176        L = np.inf
    177
    178    # Parameters within similarity functions
    179    zeta = (z - d)/L
    180    zeta0 = z0/L
    181    eta0 = (1-(15*zeta0))**(1/4)
    182    etaR = (1-(15*zeta))**(1/4)
    183
    184
    185    if L == 0 or np.absolute(L) > 10**5:
    186        # Neutral
    187        ws = (ustar/k) * np.log( (z-d) / z0 )
    188    elif L < 10**5 and L > 0:
    189        # Stable
    190        ws = (ustar/k) * np.log( (z-d) / z0 ) + 4.7 * (zeta - zeta0)
    191    elif L > -10**5 and L < 0:
    192        # Unstable
    193        ws = (ustar/k) * np.log( (z-d) / z0 ) + \
    194             np.log( ( (eta0**2+1) * (eta0+1)**2) /
    195                     ( (etaR**2+1) * (etaR+1)**2) ) + \
    196             2*(np.arctan(etaR) - np.arctan(eta0))
    197    else:
    198        ws = np.NaN
    199
    200    return ws

Predict wind speed at altitude z using Monin-Obukhov similarity theory

###### Arguments

surfaceP : float surface pressure, hPa q2m : float specific humidity at 2m, kg/kg temp2m : float temperature at 2m, K ustar : float friction velocity, m/s shf, lhf : float sensible and latent heat fluxes, W/m2 z0 : float roughness length, m d : float displacement height, m z : float height at which wind speed will be calculated, m

###### Returns

- ws (float): wind speed at altitude z, m/s