acgc.met

Functions for computing boundary layer meteorological properties

  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):
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):
 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):
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