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