def helicity(prof, lower, upper, stu=0, stv=0, dp=-1, exact=True): ''' Calculates the relative helicity (m2/s2) of a layer from lower to upper. If storm-motion vector is supplied, storm-relative helicity, both positve and negative, is returned. Parameters ---------- prof : profile object Profile Object lower : number Bottom level of layer (m, AGL) upper : number Top level of layer (m, AGL) stu : number (optional; default = 0) U-component of storm-motion stv : number (optional; default = 0) V-component of storm-motion dp : negative integer (optional; default -1) The pressure increment for the interpolated sounding exact : bool (optional; default = True) Switch to choose between using the exact data (slower) or using interpolated sounding at 'dp' pressure levels (faster) Returns ------- phel+nhel : number Combined Helicity (m2/s2) phel : number Positive Helicity (m2/s2) nhel : number Negative Helicity (m2/s2) ''' if lower != upper: lower = interp.to_msl(prof, lower) upper = interp.to_msl(prof, upper) plower = interp.pres(prof, lower) pupper = interp.pres(prof, upper) if exact: ind1 = np.where(plower >= prof.pres)[0].min() ind2 = np.where(pupper <= prof.pres)[0].max() u1, v1 = interp.components(prof, plower) u2, v2 = interp.components(prof, pupper) u = np.concatenate([[u1], prof.u[ind1:ind2 + 1].compressed(), [u2]]) v = np.concatenate([[v1], prof.v[ind1:ind2 + 1].compressed(), [v2]]) else: ps = np.arange(plower, pupper + dp, dp) u, v = interp.components(prof, ps) sru = utils.KTS2MS(u - stu) srv = utils.KTS2MS(v - stv) layers = (sru[1:] * srv[:-1]) - (sru[:-1] * srv[1:]) phel = layers[layers > 0].sum() nhel = layers[layers < 0].sum() else: phel = nhel = 0 return phel + nhel, phel, nhel
def helicity(prof, lower, upper, stu=0, stv=0, dp=-1, exact=True): ''' Calculates the relative helicity (m2/s2) of a layer from lower to upper. If storm-motion vector is supplied, storm-relative helicity, both positve and negative, is returned. Parameters ---------- prof : profile object Profile Object lower : number Bottom level of layer (m, AGL) upper : number Top level of layer (m, AGL) stu : number (optional; default = 0) U-component of storm-motion stv : number (optional; default = 0) V-component of storm-motion dp : negative integer (optional; default -1) The pressure increment for the interpolated sounding exact : bool (optional; default = True) Switch to choose between using the exact data (slower) or using interpolated sounding at 'dp' pressure levels (faster) Returns ------- phel+nhel : number Combined Helicity (m2/s2) phel : number Positive Helicity (m2/s2) nhel : number Negative Helicity (m2/s2) ''' if lower != upper: lower = interp.to_msl(prof, lower) upper = interp.to_msl(prof, upper) plower = interp.pres(prof, lower) pupper = interp.pres(prof, upper) if np.isnan(plower) or np.isnan(pupper): return np.ma.masked, np.ma.masked, np.ma.masked if exact: ind1 = np.where(plower >= prof.pres)[0].min() ind2 = np.where(pupper <= prof.pres)[0].max() u1, v1 = interp.components(prof, plower) u2, v2 = interp.components(prof, pupper) u = np.concatenate([[u1], prof.u[ind1:ind2+1].compressed(), [u2]]) v = np.concatenate([[v1], prof.v[ind1:ind2+1].compressed(), [v2]]) else: ps = np.arange(plower, pupper+dp, dp) u, v = interp.components(prof, ps) sru = utils.KTS2MS(u - stu) srv = utils.KTS2MS(v - stv) layers = (sru[1:] * srv[:-1]) - (sru[:-1] * srv[1:]) phel = layers[layers > 0].sum() nhel = layers[layers < 0].sum() else: phel = nhel = 0 return phel+nhel, phel, nhel
def max_wind(prof, lower, upper, all=False): ''' Finds the maximum wind speed of the layer given by lower and upper levels. In the event of the maximum wind speed occurring at multiple levels, the lowest level it occurs is returned by default. Parameters ---------- prof : profile object Profile Object lower : number Bottom level of layer (m, AGL) upper : number Top level of layer (m, AGL) all : Boolean Switch to change the output to sorted wind levels or maximum level. Returns ------- p : number, numpy array Pressure level (hPa) of max wind speed maxu : number, numpy array Maximum Wind Speed U-component (kts) maxv : number, numpy array Maximum Wind Speed V-component (kts) ''' if prof.wdir.count() == 0 or not utils.QC(lower) or not utils.QC(upper): return ma.masked, ma.masked, ma.masked lower = interp.to_msl(prof, lower) upper = interp.to_msl(prof, upper) plower = interp.pres(prof, lower) pupper = interp.pres(prof, upper) if np.ma.is_masked(plower) or np.ma.is_masked(pupper): warnings.warn( "winds.max_wind() was unable to interpolate between height and pressure correctly. This may be due to a data integrity issue." ) return ma.masked, ma.masked, ma.masked #print(lower, upper, plower, pupper, prof.pres) ind1 = np.where((plower > prof.pres) | (np.isclose(plower, prof.pres)))[0][0] ind2 = np.where((pupper < prof.pres) | (np.isclose(pupper, prof.pres)))[0][-1] if len(prof.wspd[ind1:ind2 + 1]) == 0 or ind1 == ind2: maxu, maxv = utils.vec2comp([prof.wdir[ind1]], [prof.wspd[ind1]]) return maxu, maxv, prof.pres[ind1] arr = prof.wspd[ind1:ind2 + 1] inds = np.ma.argsort(arr) inds = inds[~arr[inds].mask][::-1] maxu, maxv = utils.vec2comp(prof.wdir[ind1:ind2 + 1][inds], prof.wspd[ind1:ind2 + 1][inds]) if all: return maxu, maxv, prof.pres[inds] else: return maxu[0], maxv[0], prof.pres[inds[0]]
def non_parcel_bunkers_motion_experimental(prof): ''' Compute the Bunkers Storm Motion for a Right Moving Supercell Parameters ---------- prof : profile object Profile Object Returns ------- rstu : number Right Storm Motion U-component (kts) rstv : number Right Storm Motion V-component (kts) lstu : number Left Storm Motion U-component (kts) lstv : number Left Storm Motion V-component (kts) ''' if prof.wdir.count() == 0: return ma.masked, ma.masked, ma.masked, ma.masked d = utils.MS2KTS(7.5) # Deviation value emperically derived as 7.5 m/s ## get the msl height of 500m, 5.5km, and 6.0km above the surface msl500m = interp.to_msl(prof, 500.) msl5500m = interp.to_msl(prof, 5500.) msl6000m = interp.to_msl(prof, 6000.) ## get the pressure of the surface, 500m, 5.5km, and 6.0km levels psfc = prof.pres[prof.sfc] p500m = interp.pres(prof, msl500m) p5500m = interp.pres(prof, msl5500m) p6000m = interp.pres(prof, msl6000m) ## sfc-500m Mean Wind mnu500m, mnv500m = mean_wind(prof, psfc, p500m) ## 5.5km-6.0km Mean Wind mnu5500m_6000m, mnv5500m_6000m = mean_wind(prof, p5500m, p6000m) # shear vector of the two mean winds shru = mnu5500m_6000m - mnu500m shrv = mnv5500m_6000m - mnv500m # SFC-6km Mean Wind mnu6, mnv6 = mean_wind(prof, psfc, p6000m) # Bunkers Right Motion tmp = d / utils.mag(shru, shrv) rstu = mnu6 + (tmp * shrv) rstv = mnv6 - (tmp * shru) lstu = mnu6 - (tmp * shrv) lstv = mnv6 + (tmp * shru) return rstu, rstv, lstu, lstv
def test_to_msl(): input_z = 1000. correct_agl = 1357. returned_agl = interp.to_msl(prof, input_z) npt.assert_almost_equal(returned_agl, correct_agl) input_z = [1000., 3000., 6000.] correct_agl = [1357., 3357., 6357] returned_agl = interp.to_msl(prof, input_z) npt.assert_almost_equal(returned_agl, correct_agl)
def test_wind_shear(): agl1 = 0 agl2 = 1000 msl1 = interp.to_msl(prof, agl1) msl2 = interp.to_msl(prof, agl2) pbot = interp.pres(prof, msl1) ptop = interp.pres(prof, msl2) correct_u, correct_v = -2.625075135691132, 10.226725739920353 returned = winds.wind_shear(prof, pbot, ptop) npt.assert_almost_equal(returned, [correct_u, correct_v])
def test_to_msl(): input_z = 1000. correct_agl = 1357. returned_agl = interp.to_msl(prof, input_z) npt.assert_almost_equal(returned_agl, correct_agl) input_z = [1000., 3000., 6000.] correct_agl = [1357., 3357., 6357] returned_agl = interp.to_msl(prof, input_z) npt.assert_almost_equal(returned_agl, correct_agl)
def test_wind_shear(): agl1 = 0 agl2 = 1000 msl1 = interp.to_msl(prof, agl1) msl2 = interp.to_msl(prof, agl2) pbot = interp.pres(prof, msl1) ptop = interp.pres(prof, msl2) correct_u, correct_v = -2.625075135691132, 10.226725739920353 returned = winds.wind_shear(prof, pbot, ptop) npt.assert_almost_equal(returned, [correct_u, correct_v])
def non_parcel_bunkers_motion_experimental(prof): ''' Compute the Bunkers Storm Motion for a Right Moving Supercell Inputs ------ prof : profile object Profile Object Returns ------- rstu : number Right Storm Motion U-component rstv : number Right Storm Motion V-component lstu : number Left Storm Motion U-component lstv : number Left Storm Motion V-component ''' d = utils.MS2KTS(7.5) # Deviation value emperically derived as 7.5 m/s ## get the msl height of 500m, 5.5km, and 6.0km above the surface msl500m = interp.to_msl(prof, 500.) msl5500m = interp.to_msl(prof, 5500.) msl6000m = interp.to_msl(prof, 6000.) ## get the pressure of the surface, 500m, 5.5km, and 6.0km levels psfc = prof.pres[prof.sfc] p500m = interp.pres(prof, msl500m) p5500m = interp.pres(prof, msl5500m) p6000m = interp.pres(prof, msl6000m) ## sfc-500m Mean Wind mnu500m, mnv500m = mean_wind(prof, psfc, p500m) ## 5.5km-6.0km Mean Wind mnu5500m_6000m, mnv5500m_6000m = mean_wind(prof, p5500m, p6000m) # shear vector of the two mean winds shru = mnu5500m_6000m - mnu500m shrv = mnv5500m_6000m - mnv500m # SFC-6km Mean Wind mnu6, mnv6 = mean_wind(prof, psfc, p6000m) # Bunkers Right Motion tmp = d / utils.mag(shru, shrv) rstu = mnu6 + (tmp * shrv) rstv = mnv6 - (tmp * shru) lstu = mnu6 - (tmp * shrv) lstv = mnv6 + (tmp * shru) return rstu, rstv, lstu, lstv
def max_wind(prof, lower, upper, all=False): ''' Finds the maximum wind speed of the layer given by lower and upper levels. In the event of the maximum wind speed occurring at multiple levels, the lowest level it occurs is returned by default. Parameters ---------- prof : profile object Profile Object lower : number Bottom level of layer (m, AGL) upper : number Top level of layer (m, AGL) all : Boolean Switch to change the output to sorted wind levels or maximum level. Returns ------- p : number, numpy array Pressure level (hPa) of max wind speed maxu : number, numpy array Maximum Wind Speed U-component maxv : number, numpy array Maximum Wind Speed V-component ''' lower = interp.to_msl(prof, lower) upper = interp.to_msl(prof, upper) plower = interp.pres(prof, lower) pupper = interp.pres(prof, upper) ind1 = np.where((plower > prof.pres) | (np.isclose(plower, prof.pres)))[0][0] ind2 = np.where((pupper < prof.pres) | (np.isclose(pupper, prof.pres)))[0][-1] if len(prof.wspd[ind1:ind2 + 1]) == 0 or ind1 == ind2: maxu, maxv = utils.vec2comp([prof.wdir[ind1]], [prof.wspd[ind1]]) return maxu, maxv, prof.pres[ind1] arr = prof.wspd[ind1:ind2 + 1] inds = np.ma.argsort(arr) inds = inds[~arr[inds].mask][::-1] maxu, maxv = utils.vec2comp(prof.wdir[ind1:ind2 + 1][inds], prof.wspd[ind1:ind2 + 1][inds]) if all: return maxu, maxv, prof.pres[inds] else: return maxu[0], maxv[0], prof.pres[inds[0]]
def get_fire(self): ''' Function to generate different indices and information regarding any fire weather in the sounding. This helps fill the data shown in the FIRE inset. Parameters ---------- None Returns ------- None ''' self.fosberg = fire.fosberg(self) self.ppbl_top = params.pbl_top(self) self.sfc_rh = thermo.relh(self.pres[self.sfc], self.tmpc[self.sfc], self.dwpc[self.sfc]) pres_sfc = self.pres[self.sfc] pres_1km = interp.pres(self, interp.to_msl(self, 1000.)) pbl_h = interp.to_agl(self, interp.hght(self, self.ppbl_top)) self.rh01km = params.mean_relh(self, pbot=pres_sfc, ptop=pres_1km) self.pblrh = params.mean_relh(self, pbot=pres_sfc, ptop=self.ppbl_top) self.meanwind01km = winds.mean_wind(self, pbot=pres_sfc, ptop=pres_1km) self.meanwindpbl = winds.mean_wind(self, pbot=pres_sfc, ptop=self.ppbl_top) self.pblmaxwind = winds.max_wind(self, lower=0, upper=pbl_h) #self.pblmaxwind = [np.ma.masked, np.ma.masked] mulplvals = params.DefineParcel(self, flag=3, pres=500) mupcl = params.cape(self, lplvals=mulplvals) self.bplus_fire = mupcl.bplus
def get_fire(self): ''' Function to generate different indices and information regarding any fire weather in the sounding. This helps fill the data shown in the FIRE inset. Parameters ---------- None Returns ------- None ''' self.fosberg = fire.fosberg(self) self.ppbl_top = params.pbl_top(self) self.sfc_rh = thermo.relh(self.pres[self.sfc], self.tmpc[self.sfc], self.dwpc[self.sfc]) pres_sfc = self.pres[self.sfc] pres_1km = interp.pres(self, interp.to_msl(self, 1000.)) pbl_h = interp.to_agl(self, interp.hght(self, self.ppbl_top)) self.rh01km = params.mean_relh(self, pbot=pres_sfc, ptop=pres_1km) self.pblrh = params.mean_relh(self, pbot=pres_sfc, ptop=self.ppbl_top) self.meanwind01km = winds.mean_wind(self, pbot=pres_sfc, ptop=pres_1km) self.meanwindpbl = winds.mean_wind(self, pbot=pres_sfc, ptop=self.ppbl_top) self.pblmaxwind = winds.max_wind(self, lower=0, upper=pbl_h) #self.pblmaxwind = [np.ma.masked, np.ma.masked] mulplvals = params.DefineParcel(self, flag=3, pres=500) mupcl = params.cape(self, lplvals=mulplvals) self.bplus_fire = mupcl.bplus
def lift_parcels(prof): """Lift all the parcels within a given height interval and return the CAPEs, CINHs, and LFCs""" ## the height bottom, top, and interval zvals = np.arange(0, 5000, 100) pvals = interp.pres(prof, interp.to_msl(prof, zvals)) tvals = interp.temp(prof, pvals) dvals = interp.dwpt(prof, pvals) hvals = interp.hght(prof, pvals) hvals = interp.to_agl(prof, hvals) ## empty lists for storing the result cape_arr = [] cinh_arr = [] lfc_arr = [] ## lift each parcel in the vertical profile for p, t, td, h in zip(pvals, tvals, dvals, hvals): ## use SHARPpy to compute the parcel indices pcl = params.parcelx(prof, pres=p, tmpc=t, dwpc=td) ## store the parcel indices cape_arr.append(pcl.bplus) cinh_arr.append(pcl.bminus) lfc_arr.append(pcl.lfchght - h) ## return the data return np.ma.masked_invalid(cape_arr), np.ma.masked_invalid( cinh_arr), np.ma.masked_invalid(lfc_arr)
def max_wind(prof, lower, upper, all=False): ''' Finds the maximum wind speed of the layer given by lower and upper levels. In the event of the maximum wind speed occurring at multiple levels, the lowest level it occurs is returned by default. Parameters ---------- prof : profile object Profile Object lower : number Bottom level of layer (m, AGL) upper : number Top level of layer (m, AGL) all : Boolean Switch to change the output to sorted wind levels or maximum level. Returns ------- p : number, numpy array Pressure level (hPa) of max wind speed maxu : number, numpy array Maximum Wind Speed U-component maxv : number, numpy array Maximum Wind Speed V-component ''' lower = interp.to_msl(prof, lower) upper = interp.to_msl(prof, upper) plower = interp.pres(prof, lower) pupper = interp.pres(prof, upper) ind1 = np.where((plower > prof.pres) | (np.isclose(plower, prof.pres)))[0][0] ind2 = np.where((pupper < prof.pres) | (np.isclose(pupper, prof.pres)))[0][-1] if len(prof.wspd[ind1:ind2+1]) == 0 or ind1 == ind2: maxu, maxv = utils.vec2comp([prof.wdir[ind1]], [prof.wspd[ind1]]) return maxu, maxv, prof.pres[ind1] arr = prof.wspd[ind1:ind2+1] inds = np.ma.argsort(arr) inds = inds[~arr[inds].mask][::-1] maxu, maxv = utils.vec2comp(prof.wdir[ind1:ind2+1][inds], prof.wspd[ind1:ind2+1][inds]) if all: return maxu, maxv, prof.pres[inds] else: return maxu[0], maxv[0], prof.pres[inds[0]]
def max_wind(prof, lower, upper, all=False): ''' Finds the maximum wind speed of the layer given by lower and upper levels. In the event of the maximum wind speed occurring at multiple levels, the lowest level it occurs is returned by default. Parameters ---------- prof : profile object Profile Object lower : number Bottom level of layer (m, AGL) upper : number Top level of layer (m, AGL) Returns ------- p : number, numpy array Pressure level (hPa) of max wind speed maxu : number, numpy array Maximum Wind Speed U-component maxv : number, numpy array Maximum Wind Speed V-component ''' lower = interp.to_msl(prof, lower) upper = interp.to_msl(prof, upper) plower = interp.pres(prof, lower) pupper = interp.pres(prof, upper) ind1 = np.where(plower > prof.pres)[0].min() ind2 = np.where(pupper < prof.pres)[0].max() inds = np.where( np.fabs(prof.wspd[ind1:ind2 + 1] - prof.wspd[ind1:ind2 + 1].max()) < TOL)[0] inds += ind1 inds.sort() maxu, maxv = utils.vec2comp(prof.wdir[inds], prof.wspd[inds]) if all: return maxu, maxv, prof.pres[inds] else: return maxu[0], maxv[0], prof.pres[inds[0]]
def max_wind(prof, lower, upper, all=False): ''' Finds the maximum wind speed of the layer given by lower and upper levels. In the event of the maximum wind speed occurring at multiple levels, the lowest level it occurs is returned by default. Parameters ---------- prof : profile object Profile Object lower : number Bottom level of layer (m, AGL) upper : number Top level of layer (m, AGL) Returns ------- p : number, numpy array Pressure level (hPa) of max wind speed maxu : number, numpy array Maximum Wind Speed U-component maxv : number, numpy array Maximum Wind Speed V-component ''' lower = interp.to_msl(prof, lower) upper = interp.to_msl(prof, upper) plower = interp.pres(prof, lower) pupper = interp.pres(prof, upper) ind1 = np.where(plower > prof.pres)[0].min() ind2 = np.where(pupper < prof.pres)[0].max() inds = np.where(np.fabs(prof.wspd[ind1:ind2+1] - prof.wspd[ind1:ind2+1].max()) < TOL)[0] inds += ind1 inds.sort() maxu, maxv = utils.vec2comp(prof.wdir[inds], prof.wspd[inds]) if all: return maxu, maxv, prof.pres[inds] else: return maxu[0], maxv[0], prof.pres[inds[0]]
def non_parcel_bunkers_motion(prof): ''' Compute the Bunkers Storm Motion for a Right Moving Supercell Inputs ------ prof : profile object Profile Object Returns ------- rstu : number Right Storm Motion U-component rstv : number Right Storm Motion V-component lstu : number Left Storm Motion U-component lstv : number Left Storm Motion V-component ''' d = utils.MS2KTS(7.5) # Deviation value emperically derived as 7.5 m/s msl6km = interp.to_msl(prof, 6000.) p6km = interp.pres(prof, msl6km) # SFC-6km Mean Wind mnu6, mnv6 = mean_wind_npw(prof, prof.pres[prof.sfc], p6km) # SFC-6km Shear Vector shru, shrv = wind_shear(prof, prof.pres[prof.sfc], p6km) # Bunkers Right Motion tmp = d / utils.mag(shru, shrv) # Cambios para el hemisferio sur JP JP if prof.latitude < 0: lstu = mnu6 + (tmp * shrv) lstv = mnv6 - (tmp * shru) rstu = mnu6 - (tmp * shrv) rstv = mnv6 + (tmp * shru) else: rstu = mnu6 + (tmp * shrv) rstv = mnv6 - (tmp * shru) lstu = mnu6 - (tmp * shrv) lstv = mnv6 + (tmp * shru) return rstu, rstv, lstu, lstv
def corfidi_mcs_motion(prof): ''' Calculated the Meso-beta Elements (Corfidi) Vectors Parameters ---------- prof : profile object Profile Object Returns ------- upu : number U-component of the upshear vector (kts) upv : number V-component of the upshear vector (kts) dnu : number U-component of the downshear vector (kts) dnv : number V-component of the downshear vector (kts) ''' if prof.wdir.count() == 0: return ma.masked, ma.masked, ma.masked, ma.masked # Compute the tropospheric (850hPa-300hPa) mean wind if prof.pres[prof.sfc] < 850: mnu1, mnv1 = mean_wind_npw(prof, pbot=prof.pres[prof.sfc], ptop=300.) else: mnu1, mnv1 = mean_wind_npw(prof, pbot=850., ptop=300.) # Compute the low-level (SFC-1500m) mean wind p_1p5km = interp.pres(prof, interp.to_msl(prof, 1500.)) mnu2, mnv2 = mean_wind_npw(prof, prof.pres[prof.sfc], p_1p5km) # Compute the upshear vector upu = mnu1 - mnu2 upv = mnv1 - mnv2 # Compute the downshear vector dnu = mnu1 + upu dnv = mnv1 + upv return upu, upv, dnu, dnv
def corfidi_mcs_motion(prof): ''' Calculated the Meso-beta Elements (Corfidi) Vectors Parameters ---------- prof : profile object Profile Object Returns ------- upu : number U-component of the upshear vector upv : number V-component of the upshear vector dnu : number U-component of the downshear vector dnv : number V-component of the downshear vector ''' # Compute the tropospheric (850hPa-300hPa) mean wind if prof.pres[ prof.sfc ] < 850: mnu1, mnv1 = mean_wind_npw(prof, pbot=prof.pres[prof.sfc], ptop=300.) else: mnu1, mnv1 = mean_wind_npw(prof, pbot=850., ptop=300.) # Compute the low-level (SFC-1500m) mean wind p_1p5km = interp.pres(prof, interp.to_msl(prof, 1500.)) mnu2, mnv2 = mean_wind_npw(prof, prof.pres[prof.sfc], p_1p5km) # Compute the upshear vector upu = mnu1 - mnu2 upv = mnv1 - mnv2 # Compute the downshear vector dnu = mnu1 + upu dnv = mnv1 + upv return upu, upv, dnu, dnv
def non_parcel_bunkers_motion(prof): ''' Compute the Bunkers Storm Motion for a Right Moving Supercell Parameters ---------- prof : profile object Profile Object Returns ------- rstu : number Right Storm Motion U-component (kts) rstv : number Right Storm Motion V-component (kts) lstu : number Left Storm Motion U-component (kts) lstv : number Left Storm Motion V-component (kts) ''' if prof.wdir.count() == 0: return ma.masked, ma.masked, ma.masked, ma.masked d = utils.MS2KTS(7.5) # Deviation value emperically derived as 7.5 m/s msl6km = interp.to_msl(prof, 6000.) p6km = interp.pres(prof, msl6km) # SFC-6km Mean Wind mnu6, mnv6 = mean_wind_npw(prof, prof.pres[prof.sfc], p6km) # SFC-6km Shear Vector shru, shrv = wind_shear(prof, prof.pres[prof.sfc], p6km) # Bunkers Right Motion tmp = d / utils.mag(shru, shrv) rstu = mnu6 + (tmp * shrv) rstv = mnv6 - (tmp * shru) lstu = mnu6 - (tmp * shrv) lstv = mnv6 + (tmp * shru) return rstu, rstv, lstu, lstv
def critical_angle(prof, stu=0, stv=0): ''' Calculates the critical angle (degrees) as specified by Esterheld and Giuliano (2008). If the critical angle is 90 degrees, this indicates that the lowest 500 meters of the storm is experiencing pure streamwise vorticity. Parameters ---------- prof : profile object Profile Object stu : number (optional; default = 0) U-component of storm-motion (kts) stv : number (optional; default = 0) V-component of storm-motion (kts) Returns ------- angle : number Critical Angle (degrees) ''' if prof.wdir.count() == 0: return ma.masked if not utils.QC(stu) or not utils.QC(stv): return ma.masked pres_500m = interp.pres(prof, interp.to_msl(prof, 500)) u500, v500 = interp.components(prof, pres_500m) sfc_u, sfc_v = interp.components(prof, prof.pres[prof.sfc]) vec1_u, vec1_v = u500 - sfc_u, v500 - sfc_v vec2_u, vec2_v = stu - sfc_u, stv - sfc_v vec_1_mag = np.sqrt(np.power(vec1_u, 2) + np.power(vec1_v, 2)) vec_2_mag = np.sqrt(np.power(vec2_u, 2) + np.power(vec2_v, 2)) dot = vec1_u * vec2_u + vec1_v * vec2_v angle = np.degrees(np.arccos(dot / (vec_1_mag * vec_2_mag))) return angle
def non_parcel_bunkers_motion(prof): ''' Compute the Bunkers Storm Motion for a Right Moving Supercell Inputs ------ prof : profile object Profile Object Returns ------- rstu : number Right Storm Motion U-component rstv : number Right Storm Motion V-component lstu : number Left Storm Motion U-component lstv : number Left Storm Motion V-component ''' d = utils.MS2KTS(7.5) # Deviation value emperically derived as 7.5 m/s msl6km = interp.to_msl(prof, 6000.) p6km = interp.pres(prof, msl6km) # SFC-6km Mean Wind mnu6, mnv6 = mean_wind_npw(prof, prof.pres[prof.sfc], p6km) # SFC-6km Shear Vector shru6, shrv6 = wind_shear(prof, prof.pres[prof.sfc], p6km) # Bunkers Right Motion tmp = d / utils.comp2vec(shru6, shrv6)[1] rstu = mnu6 + (tmp * shrv6) rstv = mnv6 - (tmp * shru6) lstu = mnu6 - (tmp * shrv6) lstv = mnv6 + (tmp * shru6) return rstu, rstv, lstu, lstv
def critical_angle(prof, stu=0, stv=0): ''' Calculates the critical angle (degrees) as specified by Esterheld and Giuliano (2008). If the critical angle is 90 degrees, this indicates that the lowest 500 meters of the storm is experiencing pure streamwise vorticity. Parameters ---------- prof : profile object Profile Object stu : number (optional; default = 0) U-component of storm-motion stv : number (optional; default = 0) V-component of storm-motion Returns ------- angle : number Critical Angle (degrees) ''' if not utils.QC(stu) or not utils.QC(stv): return ma.masked pres_500m = interp.pres(prof, interp.to_msl(prof, 500)) u500, v500 = interp.components(prof, pres_500m) sfc_u, sfc_v = interp.components(prof, prof.pres[prof.sfc]) vec1_u, vec1_v = u500 - sfc_u, v500 - sfc_v vec2_u, vec2_v = stu - sfc_u, stv - sfc_v vec_1_mag = np.sqrt(np.power(vec1_u, 2) + np.power(vec1_v, 2)) vec_2_mag = np.sqrt(np.power(vec2_u, 2) + np.power(vec2_v, 2)) dot = vec1_u * vec2_u + vec1_v * vec2_v angle = np.degrees(np.arccos(dot / (vec_1_mag * vec_2_mag))) return angle
def plot_sounding(file, imgName): try: prof, time, location = decode(file) except Exception as e: print( "\n Oops! Couldn't decode the sounding data. No plot produced!\n") print(e) # return None # Open up the text file with the data in columns (e.g. the sample OAX file distributed with SHARPpy) locInfo = location.split('_') title = locInfo[0] + ' ' + locInfo[1] + ' ' + locInfo[ 2] + ' ' + time.strftime('%Y%m%d/%H%M') + ' (Observed)' # Set up the figure in matplotlib. fig = plt.figure(figsize=(14, 7.25)) gs = gridspec.GridSpec(4, 6, width_ratios=[1, 5, 1, 0.5, 3, 3]) ax = plt.subplot(gs[0:3, 0:2], projection='skewx') plt.title(title, fontsize=14, loc='left', color='w') ax.set_facecolor('k') ax.spines['left'].set_color('w') ax.spines['right'].set_color('w') ax.spines['bottom'].set_color('w') ax.spines['top'].set_color('w') # xticks = ax.xaxis.get_major_ticks() #mute a tick label outside plot # xticks[-4].label1.set_visible(False) ax.tick_params(axis='both', colors='w', grid_color='silver') ax.ticklabel_format(style='plain') # ax.xaxis.label.set_color('w') # ax.yaxis.label.set_color('w') ax.grid(True) plt.grid(True) # Ask user for default limits or custom limits pt_plot, t_lower, t_upper = ask_limits(prof.pres[~prof.dwpc.mask], prof.dwpc[~prof.dwpc.mask]) # Bounds of the pressure axis pb_plot = 1050 dp_plot = 10 plevs_plot = np.arange(pb_plot, pt_plot - 1, -dp_plot) # Plot the background variables # presvals = np.arange(1000, 0, -10) #draw mixing ratio lines draw_mixing_ratio_lines(ax) ax.semilogy(prof.tmpc[~prof.tmpc.mask], prof.pres[~prof.tmpc.mask], 'r', lw=2) ax.semilogy(prof.dwpc[~prof.dwpc.mask], prof.pres[~prof.dwpc.mask], 'lime', lw=2) ax.semilogy(prof.vtmp[~prof.dwpc.mask], prof.pres[~prof.dwpc.mask], 'r--', lw=1) ax.semilogy(prof.wetbulb[~prof.dwpc.mask], prof.pres[~prof.dwpc.mask], 'cyan', '-', lw=1) #write sfc temp and dewpoint in F sfcT = prof.tmpc[~prof.tmpc.mask][0] sfcTd = prof.dwpc[~prof.dwpc.mask][0] sfcW = prof.wetbulb[~prof.dwpc.mask][0] sfcP = prof.pres[~prof.tmpc.mask][0] ax.annotate(str(int(sfcW * (9 / 5) + 32)), (sfcW, sfcP), xytext=(-6, -9), textcoords='offset points', color='cyan', weight='black', size=8, path_effects=[pe.withStroke(linewidth=2, foreground="black")]) ax.annotate(str(int(sfcT * (9 / 5) + 32)), (sfcT, sfcP), xytext=(-2, -9), textcoords='offset points', color='r', weight='black', size=8, path_effects=[pe.withStroke(linewidth=2, foreground="black")]) ax.annotate(str(int(sfcTd * (9 / 5) + 32)), (sfcTd, sfcP), xytext=(-12, -9), textcoords='offset points', color='lime', weight='black', size=8, path_effects=[pe.withStroke(linewidth=2, foreground="black")]) #plot significant levels plot_sig_levels(ax, prof) # Plot the parcel trace, but this may fail. If it does so, inform the user. try: ax.semilogy(prof.mupcl.ttrace, prof.mupcl.ptrace, 'w--') except: print("Couldn't plot parcel traces...") # Highlight the 0 C and -20 C isotherms. l = ax.axvline(0, color='b', ls='--') l = ax.axvline(-20, color='b', ls='--') #plot dry adiabats skew.draw_dry_adiabats(ax, color='silver') #draw heights skew.draw_heights(ax, prof) # Disables the log-formatting that comes with semilogy ax.yaxis.set_major_formatter(ScalarFormatter()) pmin = prof.pres[~prof.dwpc.mask][-1] if pmin > 700.: ax.set_yticks(np.arange(100, 1000, 50)) else: ax.set_yticks(np.linspace(100, 1000, 10)) ax.set_ylim(pb_plot, pt_plot) # Plot the hodograph data. # inset_axes = draw_hodo_inset(ax, prof) hodoAx = plt.subplot(gs[0:3, 3:]) hodoAx.set_facecolor('k') hodoAx.axis('off') hodoAx = draw_hodo_inset(hodoAx, prof) # plotHodo(inset_axes, prof.hght, prof.u, prof.v, color='r') plotHodo(hodoAx, prof.hght, prof.u, prof.v, color='r') #plot bunkers motion unless the most unstable EL does not exist srwind = params.bunkers_storm_motion(prof) if isinstance(prof.mupcl.elpres, np.float64): hodoAx.text(srwind[0], srwind[1], 'RM', color='w', fontsize=8) hodoAx.text(srwind[2], srwind[3], 'LM', color='w', fontsize=8) else: print("couldn't plot Bunkers vectors") # inset_axes.text(srwind[0], srwind[1], 'RM', color='r', fontsize=8) # inset_axes.text(srwind[2], srwind[3], 'LM', color='b', fontsize=8) #mask out barbs above the top of the plot below_pmin = np.where(prof.pres >= pt_plot)[0] # Draw the wind barbs axis and everything that comes with it. if pmin > 700.: ax.xaxis.set_major_locator(MultipleLocator(5)) else: ax.xaxis.set_major_locator(MultipleLocator(10)) ax.set_xlim(t_lower, t_upper) ax2 = plt.subplot(gs[0:3, 2]) ax3 = plt.subplot(gs[3, 0:3]) plot_wind_axes(ax2, pb_plot, pt_plot, plevs_plot) #setting the stride for how many wind barbs plot # st = 15 # plot_wind_barbs(ax2, prof.pres[below_pmin][~prof.pres.mask[below_pmin]][::st], # prof.u[below_pmin][~prof.u.mask[below_pmin]][::st], # prof.v[below_pmin][~prof.v.mask[below_pmin]][::st], # pt_plot) plot_wind_barbs(ax2, prof.pres[below_pmin][~prof.pres.mask[below_pmin]], prof.u[below_pmin][~prof.u.mask[below_pmin]], prof.v[below_pmin][~prof.v.mask[below_pmin]], pt_plot) gs.update(left=0.05, bottom=0.05, top=0.95, right=1, wspace=0.025) # Calculate indices to be shown. More indices can be calculated here using the tutorial and reading the params module. p1km = interp.pres(prof, interp.to_msl(prof, 1000.)) p6km = interp.pres(prof, interp.to_msl(prof, 6000.)) sfc = prof.pres[prof.sfc] sfc_1km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p1km) sfc_6km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p6km) srh3km = winds.helicity(prof, 0, 3000., stu=srwind[0], stv=srwind[1]) srh1km = winds.helicity(prof, 0, 1000., stu=srwind[0], stv=srwind[1]) scp = params.scp(prof.mupcl.bplus, prof.right_esrh[0], prof.ebwspd) stp_cin = params.stp_cin(prof.mlpcl.bplus, prof.right_esrh[0], prof.ebwspd, prof.mlpcl.lclhght, prof.mlpcl.bminus) stp_fixed = params.stp_fixed( prof.sfcpcl.bplus, prof.sfcpcl.lclhght, srh1km[0], utils.comp2vec(prof.sfc_6km_shear[0], prof.sfc_6km_shear[1])[1]) ship = params.ship(prof) # A routine to perform the correct formatting when writing the indices out to the figure. def fmt(value, fmt='int'): if fmt == 'int': try: val = int(value) except: val = str("M") else: try: val = round(value, 1) except: val = "M" return val # Setting a dictionary that is a collection of all of the indices we'll be showing on the figure. # the dictionary includes the index name, the actual value, and the units. indices = {'SBCAPE': [fmt(prof.sfcpcl.bplus), 'J/kg'],\ 'SBCIN': [fmt(prof.sfcpcl.bminus), 'J/kg'],\ 'SBLCL': [fmt(prof.sfcpcl.lclhght), 'm AGL'],\ 'SBLFC': [fmt(prof.sfcpcl.lfchght), 'm AGL'],\ 'SBEL': [fmt(prof.sfcpcl.elhght), 'm AGL'],\ 'SBLI': [fmt(prof.sfcpcl.li5), 'C'],\ 'MLCAPE': [fmt(prof.mlpcl.bplus), 'J/kg'],\ 'MLCIN': [fmt(prof.mlpcl.bminus), 'J/kg'],\ 'MLLCL': [fmt(prof.mlpcl.lclhght), 'm AGL'],\ 'MLLFC': [fmt(prof.mlpcl.lfchght), 'm AGL'],\ 'MLEL': [fmt(prof.mlpcl.elhght), 'm AGL'],\ 'MLLI': [fmt(prof.mlpcl.li5), 'C'],\ 'MUCAPE': [fmt(prof.mupcl.bplus), 'J/kg'],\ 'MUCIN': [fmt(prof.mupcl.bminus), 'J/kg'],\ 'MULCL': [fmt(prof.mupcl.lclhght), 'm AGL'],\ 'MULFC': [fmt(prof.mupcl.lfchght), 'm AGL'],\ 'MUEL': [fmt(prof.mupcl.elhght), 'm AGL'],\ 'MULI': [fmt(prof.mupcl.li5), 'C'],\ '0-1 km SRH': [fmt(srh1km[0]), 'm2/s2'],\ '0-1 km Shear': [fmt(utils.comp2vec(sfc_1km_shear[0], sfc_1km_shear[1])[1]), 'kts'],\ '0-3 km SRH': [fmt(srh3km[0]), 'm2/s2'],\ '0-6 km Shear': [fmt(utils.comp2vec(sfc_6km_shear[0], sfc_6km_shear[1])[1]), 'kts'],\ 'Eff. SRH': [fmt(prof.right_esrh[0]), 'm2/s2'],\ 'EBWD': [fmt(prof.ebwspd), 'kts'],\ 'PWV': [round(prof.pwat, 2), 'inch'],\ 'K-index': [fmt(params.k_index(prof)), ''],\ 'STP(fix)': [fmt(stp_fixed, 'flt'), ''],\ 'SHIP': [fmt(ship, 'flt'), ''],\ 'SCP': [fmt(scp, 'flt'), ''],\ 'STP(cin)': [fmt(stp_cin, 'flt'), '']} # List the indices within the indices dictionary on the side of the plot. trans = transforms.blended_transform_factory(ax.transAxes, ax.transData) # Write out all of the indices to the figure. #print("##############") #print(" INDICES ") #print("##############") string = '' keys = np.sort(list(indices.keys())) x = 0 counter = 0 for key in keys: string = string + key + ': ' + str( indices[key][0]) + ' ' + indices[key][1] + '\n' # print((key + ": " + str(indices[key][0]) + ' ' + indices[key][1])) if counter < 7: counter += 1 continue else: counter = 0 ax3.text(x, 1, string, verticalalignment='top', transform=ax3.transAxes, fontsize=11, color='w') string = '' x += 0.3 ax3.text(x, 1, string, verticalalignment='top', transform=ax3.transAxes, fontsize=11, color='w') ax3.set_axis_off() # Show SARS matches (edited for Keith Sherburn) #try: # supercell_matches = prof.supercell_matches # hail_matches = prof.matches #except: # supercell_matches = prof.right_supercell_matches # hail_matches = prof.right_matches #print() #print("#############") #print(" SARS OUTPUT ") #print("#############") #for mtype, matches in zip(['Supercell', 'Hail'], [supercell_matches, hail_matches]): # print(mtype) # print('-----------') # if len(matches[0]) == 0: # print("NO QUALITY MATCHES") # for i in range(len(matches[0])): # print(matches[0][i] + ' ' + matches[1][i]) # print("Total Loose Matches:", matches[2]) # print("# of Loose Matches that met Criteria:", matches[3]) # print("SVR Probability:", matches[4]) # print() #plot logos im = plt.imread('logo.png') #left, bottom, width, height = [0.25, 0.6, 0.2, 0.2] #left, bottom, width, height = [0.1, 0.175, 0.4, 0.4] #bottom left left, bottom, width, height = [0.035, 0.65, 0.4, 0.4] # ax4 = fig.add_axes([left, bottom, width, height]) ax4 = plt.subplot(gs[3, 4]) implot = ax4.imshow(im, alpha=0.99) ax4.axis('off') ax4.set_facecolor('k') im2 = plt.imread('essc_logo.png') ax5 = plt.subplot(gs[3, 5]) implot = ax5.imshow(im2, alpha=0.99) ax5.axis('off') ax5.set_facecolor('k') #plot SHARPpy acknowledgement # plt.text(1, 1, 'Plotted with SHARPpy', horizontalalignment='right', # verticalalignment='top', transform=ax.transAxes, color='w') hodoAx.annotate( 'Plotted with SHARPpy - https://sharppy.github.io/SHARPpy/', (0.7, 0.96), xycoords='figure fraction', va='center', color='w') #filename for the plot # plotName = os.path.splitext(file)[0] + '.png' # Finalize the image formatting and alignments, and save the image to the file. #gs.tight_layout(fig) plt.style.use('dark_background') fn = time.strftime( '%Y%m%d.%H%M') + '_' + locInfo[0] + '_' + locInfo[1] + '.png' fn = fn.replace('/', '') print('SHARPpy quick-look image output at: ' + imgName) #plt.savefig(fn, bbox_inches='tight', dpi=180) plt.savefig(imgName, dpi=180)
spd_std = spd_std * units.knot direc = direc * units.deg direc_std = direc_std * units.deg # Convert wind speed and direction to components u, v = get_wind_components(spd, direc) u_std, v_std = get_wind_components(spd_std, direc_std) #PARCEL CALCULATIONS with sharppy sfcpcl = params.parcelx(prof, flag=1) # Surface Parcel fcstpcl = params.parcelx(prof, flag=2) # Forecast Parcel mupcl = params.parcelx(prof, flag=3) # Most-Unstable Parcel mlpcl = params.parcelx(prof, flag=4) # 100 mb Mean Layer Parcel sfc = prof.pres[prof.sfc] p3km = interp.pres(prof, interp.to_msl(prof, 3000.)) p6km = interp.pres(prof, interp.to_msl(prof, 6000.)) p1km = interp.pres(prof, interp.to_msl(prof, 1000.)) mean_3km = winds.mean_wind(prof, pbot=sfc, ptop=p3km) sfc_6km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p6km) sfc_3km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p3km) sfc_1km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p1km) srwind = params.bunkers_storm_motion(prof) srh3km = winds.helicity(prof, 0, 3000., stu=srwind[0], stv=srwind[1]) srh1km = winds.helicity(prof, 0, 1000., stu=srwind[0], stv=srwind[1]) stp_fixed = params.stp_fixed( sfcpcl.bplus, sfcpcl.lclhght, srh1km[0], utils.comp2vec(sfc_6km_shear[0], sfc_6km_shear[1])[1]) ship = params.ship(prof) eff_inflow = params.effective_inflow_layer(prof)
bunkerR, = hodo_ax.plot([], [], color='r', alpha=0.7, linestyle="None", marker="o", markersize=5, mew=0, label="right mover") # Explanation for red and blue dots. Put only 1 point in the legend entry. bunkerleg = hodo_ax.legend(handles=[bunkerL, bunkerR], fontsize=5, frameon=False, numpoints=1) # show Bunker left/right movers if 0-6km shear magnitude >= 20kts p6km = interp.pres(prof, interp.to_msl(prof, 6000.)) sfc_6km_shear = winds.wind_shear(prof, pbot=prof.pres[prof.sfc], ptop=p6km) if utils.comp2vec(sfc_6km_shear[0], sfc_6km_shear[1])[1] >= 20.: bunkerR.set_visible(True) bunkerL.set_visible(True) bunkerleg.set_visible(True) bunkerR.set_data(srwind[0], srwind[1]) # Update Bunker's Storm motion right mover bunkerL.set_data(srwind[2], srwind[3]) # Update Bunker's Storm motion left mover else: bunkerR.set_visible(False) bunkerL.set_visible(False) bunkerleg.set_visible(False) if debug:
def do_sharppy(spc_file): """ Based on the tutorial which can be found here: http://nbviewer.ipython.org/github/sharppy/SHARPpy/blob/master/tutorials/SHARPpy_basics.ipynb SHARPpy can be found here: https://github.com/sharppy/SHARPpy Credit goes to: Patrick Marsh (SPC) Kelton Halbert (OU School of Meteorology) Greg Blumberg (OU/CIMMS) Tim Supinie (OU School of Meteorology) """ import sharppy import sharppy.sharptab.profile as profile import sharppy.sharptab.interp as interp import sharppy.sharptab.winds as winds import sharppy.sharptab.utils as utils import sharppy.sharptab.params as params import sharppy.sharptab.thermo as thermo import matplotlib.pyplot as plt from StringIO import StringIO from matplotlib.axes import Axes import matplotlib.transforms as transforms import matplotlib.axis as maxis import matplotlib.spines as mspines import matplotlib.path as mpath from matplotlib.projections import register_projection spc_file = open('skewt_data', 'r').read() def parseSPC(spc_file): ## read in the file data = np.array([l.strip() for l in spc_file.split('\n')]) ## necessary index points title_idx = np.where( data == '%TITLE%')[0][0] start_idx = np.where( data == '%RAW%' )[0] + 1 finish_idx = np.where( data == '%END%')[0] ## create the plot title data_header = data[title_idx + 1].split() location = data_header[0]+' '+data_header[1] time = data_header[2] title = location+' '+time ## put it all together for StringIO full_data = '\n'.join(data[start_idx : finish_idx][:]) sound_data = StringIO( full_data ) ## read the data into arrays p, h, T, Td, wdir, wspd = np.genfromtxt( sound_data, delimiter=',', comments="%", unpack=True ) return p, h, T, Td, wdir, wspd, title pres, hght, tmpc, dwpc, wdir, wspd, title = parseSPC(spc_file) prof = profile.create_profile(profile='default', pres=pres, hght=hght, tmpc=tmpc, \ dwpc=dwpc, wspd=wspd, wdir=wdir, missing=-9999, strictQC=True) sfcpcl = params.parcelx( prof, flag=1 ) # Surface Parcel fcstpcl = params.parcelx( prof, flag=2 ) # Forecast Parcel mupcl = params.parcelx( prof, flag=3 ) # Most-Unstable Parcel mlpcl = params.parcelx( prof, flag=4 ) # 100 mb Mean Layer Parcel msl_hght = prof.hght[prof.sfc] # Grab the surface height value print "SURFACE HEIGHT (m MSL):",msl_hght agl_hght = interp.to_agl(prof, msl_hght) # Converts to AGL print "SURFACE HEIGHT (m AGL):", agl_hght msl_hght = interp.to_msl(prof, agl_hght) # Converts to MSL print "SURFACE HEIGHT (m MSL):",msl_hght print "Most-Unstable CAPE:", mupcl.bplus # J/kg print "Most-Unstable CIN:", mupcl.bminus # J/kg print "Most-Unstable LCL:", mupcl.lclhght # meters AGL print "Most-Unstable LFC:", mupcl.lfchght # meters AGL print "Most-Unstable EL:", mupcl.elhght # meters AGL print "Most-Unstable LI:", mupcl.li5 # C class SkewXTick(maxis.XTick): def draw(self, renderer): if not self.get_visible(): return renderer.open_group(self.__name__) lower_interval = self.axes.xaxis.lower_interval upper_interval = self.axes.xaxis.upper_interval if self.gridOn and transforms.interval_contains( self.axes.xaxis.get_view_interval(), self.get_loc()): self.gridline.draw(renderer) if transforms.interval_contains(lower_interval, self.get_loc()): if self.tick1On: self.tick1line.draw(renderer) if self.label1On: self.label1.draw(renderer) if transforms.interval_contains(upper_interval, self.get_loc()): if self.tick2On: self.tick2line.draw(renderer) if self.label2On: self.label2.draw(renderer) renderer.close_group(self.__name__) # This class exists to provide two separate sets of intervals to the tick, # as well as create instances of the custom tick class SkewXAxis(maxis.XAxis): def __init__(self, *args, **kwargs): maxis.XAxis.__init__(self, *args, **kwargs) self.upper_interval = 0.0, 1.0 def _get_tick(self, major): return SkewXTick(self.axes, 0, '', major=major) @property def lower_interval(self): return self.axes.viewLim.intervalx def get_view_interval(self): return self.upper_interval[0], self.axes.viewLim.intervalx[1] # This class exists to calculate the separate data range of the # upper X-axis and draw the spine there. It also provides this range # to the X-axis artist for ticking and gridlines class SkewSpine(mspines.Spine): def _adjust_location(self): trans = self.axes.transDataToAxes.inverted() if self.spine_type == 'top': yloc = 1.0 else: yloc = 0.0 left = trans.transform_point((0.0, yloc))[0] right = trans.transform_point((1.0, yloc))[0] pts = self._path.vertices pts[0, 0] = left pts[1, 0] = right self.axis.upper_interval = (left, right) # This class handles registration of the skew-xaxes as a projection as well # as setting up the appropriate transformations. It also overrides standard # spines and axes instances as appropriate. class SkewXAxes(Axes): # The projection must specify a name. This will be used be the # user to select the projection, i.e. ``subplot(111, # projection='skewx')``. name = 'skewx' def _init_axis(self): #Taken from Axes and modified to use our modified X-axis self.xaxis = SkewXAxis(self) self.spines['top'].register_axis(self.xaxis) self.spines['bottom'].register_axis(self.xaxis) self.yaxis = maxis.YAxis(self) self.spines['left'].register_axis(self.yaxis) self.spines['right'].register_axis(self.yaxis) def _gen_axes_spines(self): spines = {'top':SkewSpine.linear_spine(self, 'top'), 'bottom':mspines.Spine.linear_spine(self, 'bottom'), 'left':mspines.Spine.linear_spine(self, 'left'), 'right':mspines.Spine.linear_spine(self, 'right')} return spines def _set_lim_and_transforms(self): """ This is called once when the plot is created to set up all the transforms for the data, text and grids. """ rot = 30 #Get the standard transform setup from the Axes base class Axes._set_lim_and_transforms(self) # Need to put the skew in the middle, after the scale and limits, # but before the transAxes. This way, the skew is done in Axes # coordinates thus performing the transform around the proper origin # We keep the pre-transAxes transform around for other users, like the # spines for finding bounds self.transDataToAxes = self.transScale + (self.transLimits + transforms.Affine2D().skew_deg(rot, 0)) # Create the full transform from Data to Pixels self.transData = self.transDataToAxes + self.transAxes # Blended transforms like this need to have the skewing applied using # both axes, in axes coords like before. self._xaxis_transform = (transforms.blended_transform_factory( self.transScale + self.transLimits, transforms.IdentityTransform()) + transforms.Affine2D().skew_deg(rot, 0)) + self.transAxes # Now register the projection with matplotlib so the user can select # it. register_projection(SkewXAxes) pcl = mupcl # Create a new figure. The dimensions here give a good aspect ratio fig = plt.figure(figsize=(6.5875, 6.2125)) ax = fig.add_subplot(111, projection='skewx') ax.grid(True) pmax = 1000 pmin = 10 dp = -10 presvals = np.arange(int(pmax), int(pmin)+dp, dp) # plot the moist-adiabats for t in np.arange(-10,45,5): tw = [] for p in presvals: tw.append(thermo.wetlift(1000., t, p)) ax.semilogy(tw, presvals, 'k-', alpha=.2) def thetas(theta, presvals): return ((theta + thermo.ZEROCNK) / (np.power((1000. / presvals),thermo.ROCP))) - thermo.ZEROCNK # plot the dry adiabats for t in np.arange(-50,110,10): ax.semilogy(thetas(t, presvals), presvals, 'r-', alpha=.2) plt.title(title, fontsize=14, loc='left') # Plot the data using normal plotting functions, in this case using # log scaling in Y, as dicatated by the typical meteorological plot ax.semilogy(prof.tmpc, prof.pres, 'r', lw=2) ax.semilogy(prof.dwpc, prof.pres, 'g', lw=2) ax.semilogy(pcl.ttrace, pcl.ptrace, 'k-.', lw=2) # An example of a slanted line at constant X l = ax.axvline(0, color='b', linestyle='--') l = ax.axvline(-20, color='b', linestyle='--') # Disables the log-formatting that comes with semilogy ax.yaxis.set_major_formatter(plt.ScalarFormatter()) ax.set_yticks(np.linspace(100,1000,10)) ax.set_ylim(1050,100) ax.xaxis.set_major_locator(plt.MultipleLocator(10)) ax.set_xlim(-50,50) plt.show() ##PLOTS SKEWT OK ABOVE HERE ## """
def do_sharppy(spc_file): """ Based on the tutorial which can be found here: http://nbviewer.ipython.org/github/sharppy/SHARPpy/blob/master/tutorials/SHARPpy_basics.ipynb SHARPpy can be found here: https://github.com/sharppy/SHARPpy Credit goes to: Patrick Marsh (SPC) Kelton Halbert (OU School of Meteorology) Greg Blumberg (OU/CIMMS) Tim Supinie (OU School of Meteorology) """ import sharppy import sharppy.sharptab.profile as profile import sharppy.sharptab.interp as interp import sharppy.sharptab.winds as winds import sharppy.sharptab.utils as utils import sharppy.sharptab.params as params import sharppy.sharptab.thermo as thermo import matplotlib.pyplot as plt from StringIO import StringIO from matplotlib.axes import Axes import matplotlib.transforms as transforms import matplotlib.axis as maxis import matplotlib.spines as mspines import matplotlib.path as mpath from matplotlib.projections import register_projection spc_file = open('skewt_data', 'r').read() def parseSPC(spc_file): ## read in the file data = np.array([l.strip() for l in spc_file.split('\n')]) ## necessary index points title_idx = np.where(data == '%TITLE%')[0][0] start_idx = np.where(data == '%RAW%')[0] + 1 finish_idx = np.where(data == '%END%')[0] ## create the plot title data_header = data[title_idx + 1].split() location = data_header[0] + ' ' + data_header[1] time = data_header[2] title = location + ' ' + time ## put it all together for StringIO full_data = '\n'.join(data[start_idx:finish_idx][:]) sound_data = StringIO(full_data) ## read the data into arrays p, h, T, Td, wdir, wspd = np.genfromtxt(sound_data, delimiter=',', comments="%", unpack=True) return p, h, T, Td, wdir, wspd, title pres, hght, tmpc, dwpc, wdir, wspd, title = parseSPC(spc_file) prof = profile.create_profile(profile='default', pres=pres, hght=hght, tmpc=tmpc, \ dwpc=dwpc, wspd=wspd, wdir=wdir, missing=-9999, strictQC=True) sfcpcl = params.parcelx(prof, flag=1) # Surface Parcel fcstpcl = params.parcelx(prof, flag=2) # Forecast Parcel mupcl = params.parcelx(prof, flag=3) # Most-Unstable Parcel mlpcl = params.parcelx(prof, flag=4) # 100 mb Mean Layer Parcel msl_hght = prof.hght[prof.sfc] # Grab the surface height value print "SURFACE HEIGHT (m MSL):", msl_hght agl_hght = interp.to_agl(prof, msl_hght) # Converts to AGL print "SURFACE HEIGHT (m AGL):", agl_hght msl_hght = interp.to_msl(prof, agl_hght) # Converts to MSL print "SURFACE HEIGHT (m MSL):", msl_hght print "Most-Unstable CAPE:", mupcl.bplus # J/kg print "Most-Unstable CIN:", mupcl.bminus # J/kg print "Most-Unstable LCL:", mupcl.lclhght # meters AGL print "Most-Unstable LFC:", mupcl.lfchght # meters AGL print "Most-Unstable EL:", mupcl.elhght # meters AGL print "Most-Unstable LI:", mupcl.li5 # C class SkewXTick(maxis.XTick): def draw(self, renderer): if not self.get_visible(): return renderer.open_group(self.__name__) lower_interval = self.axes.xaxis.lower_interval upper_interval = self.axes.xaxis.upper_interval if self.gridOn and transforms.interval_contains( self.axes.xaxis.get_view_interval(), self.get_loc()): self.gridline.draw(renderer) if transforms.interval_contains(lower_interval, self.get_loc()): if self.tick1On: self.tick1line.draw(renderer) if self.label1On: self.label1.draw(renderer) if transforms.interval_contains(upper_interval, self.get_loc()): if self.tick2On: self.tick2line.draw(renderer) if self.label2On: self.label2.draw(renderer) renderer.close_group(self.__name__) # This class exists to provide two separate sets of intervals to the tick, # as well as create instances of the custom tick class SkewXAxis(maxis.XAxis): def __init__(self, *args, **kwargs): maxis.XAxis.__init__(self, *args, **kwargs) self.upper_interval = 0.0, 1.0 def _get_tick(self, major): return SkewXTick(self.axes, 0, '', major=major) @property def lower_interval(self): return self.axes.viewLim.intervalx def get_view_interval(self): return self.upper_interval[0], self.axes.viewLim.intervalx[1] # This class exists to calculate the separate data range of the # upper X-axis and draw the spine there. It also provides this range # to the X-axis artist for ticking and gridlines class SkewSpine(mspines.Spine): def _adjust_location(self): trans = self.axes.transDataToAxes.inverted() if self.spine_type == 'top': yloc = 1.0 else: yloc = 0.0 left = trans.transform_point((0.0, yloc))[0] right = trans.transform_point((1.0, yloc))[0] pts = self._path.vertices pts[0, 0] = left pts[1, 0] = right self.axis.upper_interval = (left, right) # This class handles registration of the skew-xaxes as a projection as well # as setting up the appropriate transformations. It also overrides standard # spines and axes instances as appropriate. class SkewXAxes(Axes): # The projection must specify a name. This will be used be the # user to select the projection, i.e. ``subplot(111, # projection='skewx')``. name = 'skewx' def _init_axis(self): #Taken from Axes and modified to use our modified X-axis self.xaxis = SkewXAxis(self) self.spines['top'].register_axis(self.xaxis) self.spines['bottom'].register_axis(self.xaxis) self.yaxis = maxis.YAxis(self) self.spines['left'].register_axis(self.yaxis) self.spines['right'].register_axis(self.yaxis) def _gen_axes_spines(self): spines = { 'top': SkewSpine.linear_spine(self, 'top'), 'bottom': mspines.Spine.linear_spine(self, 'bottom'), 'left': mspines.Spine.linear_spine(self, 'left'), 'right': mspines.Spine.linear_spine(self, 'right') } return spines def _set_lim_and_transforms(self): """ This is called once when the plot is created to set up all the transforms for the data, text and grids. """ rot = 30 #Get the standard transform setup from the Axes base class Axes._set_lim_and_transforms(self) # Need to put the skew in the middle, after the scale and limits, # but before the transAxes. This way, the skew is done in Axes # coordinates thus performing the transform around the proper origin # We keep the pre-transAxes transform around for other users, like the # spines for finding bounds self.transDataToAxes = self.transScale + ( self.transLimits + transforms.Affine2D().skew_deg(rot, 0)) # Create the full transform from Data to Pixels self.transData = self.transDataToAxes + self.transAxes # Blended transforms like this need to have the skewing applied using # both axes, in axes coords like before. self._xaxis_transform = ( transforms.blended_transform_factory( self.transScale + self.transLimits, transforms.IdentityTransform()) + transforms.Affine2D().skew_deg(rot, 0)) + self.transAxes # Now register the projection with matplotlib so the user can select # it. register_projection(SkewXAxes) pcl = mupcl # Create a new figure. The dimensions here give a good aspect ratio fig = plt.figure(figsize=(6.5875, 6.2125)) ax = fig.add_subplot(111, projection='skewx') ax.grid(True) pmax = 1000 pmin = 10 dp = -10 presvals = np.arange(int(pmax), int(pmin) + dp, dp) # plot the moist-adiabats for t in np.arange(-10, 45, 5): tw = [] for p in presvals: tw.append(thermo.wetlift(1000., t, p)) ax.semilogy(tw, presvals, 'k-', alpha=.2) def thetas(theta, presvals): return ((theta + thermo.ZEROCNK) / (np.power( (1000. / presvals), thermo.ROCP))) - thermo.ZEROCNK # plot the dry adiabats for t in np.arange(-50, 110, 10): ax.semilogy(thetas(t, presvals), presvals, 'r-', alpha=.2) plt.title(title, fontsize=14, loc='left') # Plot the data using normal plotting functions, in this case using # log scaling in Y, as dicatated by the typical meteorological plot ax.semilogy(prof.tmpc, prof.pres, 'r', lw=2) ax.semilogy(prof.dwpc, prof.pres, 'g', lw=2) ax.semilogy(pcl.ttrace, pcl.ptrace, 'k-.', lw=2) # An example of a slanted line at constant X l = ax.axvline(0, color='b', linestyle='--') l = ax.axvline(-20, color='b', linestyle='--') # Disables the log-formatting that comes with semilogy ax.yaxis.set_major_formatter(plt.ScalarFormatter()) ax.set_yticks(np.linspace(100, 1000, 10)) ax.set_ylim(1050, 100) ax.xaxis.set_major_locator(plt.MultipleLocator(10)) ax.set_xlim(-50, 50) plt.show() ##PLOTS SKEWT OK ABOVE HERE ## """
# Update the hodograph on the Skew-T. # Draw the hodograph on the Skew-T. hodo_ax = myskewt.draw_hodo() hodo, AGL = myskewt.add_hodo(hodo_ax, prof) # Plot Bunker's Storm motion left mover as a blue dot bunkerL, = hodo_ax.plot([], [], color='b', alpha=0.7, linestyle="None", marker="o", markersize=5, mew=0, label="left mover") # Plot Bunker's Storm motion right mover as a red dot # The comma after bunkerR de-lists it. bunkerR, = hodo_ax.plot([], [], color='r', alpha=0.7, linestyle="None", marker="o", markersize=5, mew=0, label="right mover") # Explanation for red and blue dots. Put only 1 point in the legend entry. bunkerleg = hodo_ax.legend(handles=[bunkerL,bunkerR], fontsize=5, frameon=False, numpoints=1) # show Bunker left/right movers if 0-6km shear magnitude >= 20kts p6km = interp.pres(prof, interp.to_msl(prof, 6000.)) sfc_6km_shear = winds.wind_shear(prof, pbot=prof.pres[prof.sfc], ptop=p6km) if utils.comp2vec(sfc_6km_shear[0], sfc_6km_shear[1])[1] >= 20.: bunkerR.set_visible(True) bunkerL.set_visible(True) bunkerleg.set_visible(True) bunkerR.set_data(srwind[0], srwind[1]) # Update Bunker's Storm motion right mover bunkerL.set_data(srwind[2], srwind[3]) # Update Bunker's Storm motion left mover else: bunkerR.set_visible(False) bunkerL.set_visible(False) bunkerleg.set_visible(False) # Recreate stack of wind barbs s = [] bot=2000.
''' Create the Sounding (Profile) Object '''
def get_kinematics(self): ''' Function to generate the numerous kinematic quantities used for display and calculations. It requires that the parcel calculations have already been called for the lcl to el shear and mean wind vectors, as well as indices that require an effective inflow layer. Parameters ---------- None Returns ------- None ''' sfc = self.pres[self.sfc] heights = np.array([1000., 3000., 4000., 5000., 6000., 8000., 9000.]) p1km, p3km, p4km, p5km, p6km, p8km, p9km = interp.pres(self, interp.to_msl(self, heights)) ## 1km and 6km winds self.wind1km = interp.vec(self, p1km) self.wind6km = interp.vec(self, p6km) ## calcluate wind shear self.sfc_1km_shear = winds.wind_shear(self, pbot=sfc, ptop=p1km) self.sfc_3km_shear = winds.wind_shear(self, pbot=sfc, ptop=p3km) self.sfc_6km_shear = winds.wind_shear(self, pbot=sfc, ptop=p6km) self.sfc_8km_shear = winds.wind_shear(self, pbot=sfc, ptop=p8km) self.sfc_9km_shear = winds.wind_shear(self, pbot=sfc, ptop=p9km) self.lcl_el_shear = winds.wind_shear(self, pbot=self.mupcl.lclpres, ptop=self.mupcl.elpres) ## calculate mean wind self.mean_1km = utils.comp2vec(*winds.mean_wind(self, pbot=sfc, ptop=p1km)) self.mean_3km = utils.comp2vec(*winds.mean_wind(self, pbot=sfc, ptop=p3km)) self.mean_6km = utils.comp2vec(*winds.mean_wind(self, pbot=sfc, ptop=p6km)) self.mean_8km = utils.comp2vec(*winds.mean_wind(self, pbot=sfc, ptop=p8km)) self.mean_lcl_el = utils.comp2vec(*winds.mean_wind(self, pbot=self.mupcl.lclpres, ptop=self.mupcl.elpres)) ## parameters that depend on the presence of an effective inflow layer if self.etop is ma.masked or self.ebottom is ma.masked: self.etopm = ma.masked; self.ebotm = ma.masked self.srwind = winds.non_parcel_bunkers_motion( self ) self.eff_shear = [MISSING, MISSING] self.ebwd = [MISSING, MISSING, MISSING] self.ebwspd = MISSING self.mean_eff = [MISSING, MISSING, MISSING] self.mean_ebw = [MISSING, MISSING, MISSING] self.srw_eff = [MISSING, MISSING, MISSING] self.srw_ebw = [MISSING, MISSING, MISSING] self.right_esrh = [ma.masked, ma.masked, ma.masked] self.left_esrh = [ma.masked, ma.masked, ma.masked] self.critical_angle = ma.masked else: self.srwind = params.bunkers_storm_motion(self, mupcl=self.mupcl, pbot=self.ebottom) depth = ( self.mupcl.elhght - self.ebotm ) / 2 elh = interp.pres(self, interp.to_msl(self, self.ebotm + depth)) ## calculate mean wind self.mean_eff = winds.mean_wind(self, self.ebottom, self.etop ) self.mean_ebw = winds.mean_wind(self, pbot=self.ebottom, ptop=elh ) ## calculate wind shear of the effective layer self.eff_shear = winds.wind_shear(self, pbot=self.ebottom, ptop=self.etop) self.ebwd = winds.wind_shear(self, pbot=self.ebottom, ptop=elh) self.ebwspd = utils.mag( self.ebwd[0], self.ebwd[1] ) ## calculate the mean sr wind self.srw_eff = winds.sr_wind(self, pbot=self.ebottom, ptop=self.etop, stu=self.srwind[0], stv=self.srwind[1] ) self.srw_ebw = winds.sr_wind(self, pbot=self.ebottom, ptop=elh, stu=self.srwind[0], stv=self.srwind[1] ) self.right_esrh = winds.helicity(self, self.ebotm, self.etopm, stu=self.srwind[0], stv=self.srwind[1]) self.left_esrh = winds.helicity(self, self.ebotm, self.etopm, stu=self.srwind[2], stv=self.srwind[3]) self.critical_angle = winds.critical_angle(self, stu=self.srwind[0], stv=self.srwind[1]) ## calculate mean srw self.srw_1km = utils.comp2vec(*winds.sr_wind(self, pbot=sfc, ptop=p1km, stu=self.srwind[0], stv=self.srwind[1] )) self.srw_3km = utils.comp2vec(*winds.sr_wind(self, pbot=sfc, ptop=p3km, stu=self.srwind[0], stv=self.srwind[1] )) self.srw_6km = utils.comp2vec(*winds.sr_wind(self, pbot=sfc, ptop=p6km, stu=self.srwind[0], stv=self.srwind[1] )) self.srw_8km = utils.comp2vec(*winds.sr_wind(self, pbot=sfc, ptop=p8km, stu=self.srwind[0], stv=self.srwind[1] )) self.srw_4_5km = utils.comp2vec(*winds.sr_wind(self, pbot=p4km, ptop=p5km, stu=self.srwind[0], stv=self.srwind[1] )) self.srw_lcl_el = utils.comp2vec(*winds.sr_wind(self, pbot=self.mupcl.lclpres, ptop=self.mupcl.elpres, stu=self.srwind[0], stv=self.srwind[1] )) # This is for the red, blue, and purple bars that appear on the SR Winds vs. Height plot self.srw_0_2km = winds.sr_wind(self, pbot=sfc, ptop=interp.pres(self, interp.to_msl(self, 2000.)), stu=self.srwind[0], stv=self.srwind[1]) self.srw_4_6km = winds.sr_wind(self, pbot=interp.pres(self, interp.to_msl(self, 4000.)), ptop=p6km, stu=self.srwind[0], stv=self.srwind[1]) self.srw_9_11km = winds.sr_wind(self, pbot=interp.pres(self, interp.to_msl(self, 9000.)), ptop=interp.pres(self, interp.to_msl(self, 11000.)), stu=self.srwind[0], stv=self.srwind[1]) ## calculate upshear and downshear self.upshear_downshear = winds.mbe_vectors(self) self.srh1km = winds.helicity(self, 0, 1000., stu=self.srwind[0], stv=self.srwind[1]) self.srh3km = winds.helicity(self, 0, 3000., stu=self.srwind[0], stv=self.srwind[1])
def indices(prof, debug=False): # return a formatted-string list of stability and kinematic indices sfcpcl = params.parcelx(prof, flag=1) mupcl = params.parcelx(prof, flag=3) # most unstable mlpcl = params.parcelx(prof, flag=4) # 100 mb mean layer parcel pcl = mupcl sfc = prof.pres[prof.sfc] p3km = interp.pres(prof, interp.to_msl(prof, 3000.)) p6km = interp.pres(prof, interp.to_msl(prof, 6000.)) p1km = interp.pres(prof, interp.to_msl(prof, 1000.)) mean_3km = winds.mean_wind(prof, pbot=sfc, ptop=p3km) sfc_6km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p6km) sfc_3km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p3km) sfc_1km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p1km) #print "0-3 km Pressure-Weighted Mean Wind (kt):", utils.comp2vec(mean_3km[0], mean_3km[1])[1] #print "0-6 km Shear (kt):", utils.comp2vec(sfc_6km_shear[0], sfc_6km_shear[1])[1] srwind = params.bunkers_storm_motion(prof) srh3km = winds.helicity(prof, 0, 3000., stu=srwind[0], stv=srwind[1]) srh1km = winds.helicity(prof, 0, 1000., stu=srwind[0], stv=srwind[1]) #print "0-3 km Storm Relative Helicity [m2/s2]:",srh3km[0] #### Calculating variables based off of the effective inflow layer: # The effective inflow layer concept is used to obtain the layer of buoyant parcels that feed a storm's inflow. # Here are a few examples of how to compute variables that require the effective inflow layer in order to calculate them: stp_fixed = params.stp_fixed( sfcpcl.bplus, sfcpcl.lclhght, srh1km[0], utils.comp2vec(sfc_6km_shear[0], sfc_6km_shear[1])[1]) ship = params.ship(prof) # If you get an error about not converting masked constant to python int # use the round() function instead of int() - Ahijevych May 11 2016 # 2nd element of list is the # of decimal places indices = { 'SBCAPE': [sfcpcl.bplus, 0, 'J $\mathregular{kg^{-1}}$'], 'SBCIN': [sfcpcl.bminus, 0, 'J $\mathregular{kg^{-1}}$'], 'SBLCL': [sfcpcl.lclhght, 0, 'm AGL'], 'SBLFC': [sfcpcl.lfchght, 0, 'm AGL'], 'SBEL': [sfcpcl.elhght, 0, 'm AGL'], 'SBLI': [sfcpcl.li5, 0, 'C'], 'MLCAPE': [mlpcl.bplus, 0, 'J $\mathregular{kg^{-1}}$'], 'MLCIN': [mlpcl.bminus, 0, 'J $\mathregular{kg^{-1}}$'], 'MLLCL': [mlpcl.lclhght, 0, 'm AGL'], 'MLLFC': [mlpcl.lfchght, 0, 'm AGL'], 'MLEL': [mlpcl.elhght, 0, 'm AGL'], 'MLLI': [mlpcl.li5, 0, 'C'], 'MUCAPE': [mupcl.bplus, 0, 'J $\mathregular{kg^{-1}}$'], 'MUCIN': [mupcl.bminus, 0, 'J $\mathregular{kg^{-1}}$'], 'MULCL': [mupcl.lclhght, 0, 'm AGL'], 'MULFC': [mupcl.lfchght, 0, 'm AGL'], 'MUEL': [mupcl.elhght, 0, 'm AGL'], 'MULI': [mupcl.li5, 0, 'C'], '0-1 km SRH': [srh1km[0], 0, '$\mathregular{m^{2}s^{-2}}$'], '0-1 km Shear': [utils.comp2vec(sfc_1km_shear[0], sfc_1km_shear[1])[1], 0, 'kt'], '0-3 km SRH': [srh3km[0], 0, '$\mathregular{m^{2}s^{-2}}$'], '0-6 km Shear': [utils.comp2vec(sfc_6km_shear[0], sfc_6km_shear[1])[1], 0, 'kt'], 'PWV': [params.precip_water(prof), 2, 'inch'], 'K-index': [params.k_index(prof), 0, ''], 'STP(fix)': [stp_fixed, 1, ''], 'SHIP': [ship, 1, ''] } eff_inflow = params.effective_inflow_layer(prof) if any(eff_inflow): ebot_hght = interp.to_agl(prof, interp.hght(prof, eff_inflow[0])) etop_hght = interp.to_agl(prof, interp.hght(prof, eff_inflow[1])) #print "Effective Inflow Layer Bottom Height (m AGL):", ebot_hght #print "Effective Inflow Layer Top Height (m AGL):", etop_hght effective_srh = winds.helicity(prof, ebot_hght, etop_hght, stu=srwind[0], stv=srwind[1]) indices['Eff. SRH'] = [ effective_srh[0], 0, '$\mathregular{m^{2}s^{-2}}$' ] #print "Effective Inflow Layer SRH (m2/s2):", effective_srh[0] ebwd = winds.wind_shear(prof, pbot=eff_inflow[0], ptop=eff_inflow[1]) ebwspd = utils.mag(*ebwd) indices['EBWD'] = [ebwspd, 0, 'kt'] #print "Effective Bulk Wind Difference:", ebwspd scp = params.scp(mupcl.bplus, effective_srh[0], ebwspd) indices['SCP'] = [scp, 1, ''] stp_cin = params.stp_cin(mlpcl.bplus, effective_srh[0], ebwspd, mlpcl.lclhght, mlpcl.bminus) indices['STP(cin)'] = [stp_cin, 1, ''] #print "Supercell Composite Parameter:", scp #print "Significant Tornado Parameter (w/CIN):", stp_cin #print "Significant Tornado Parameter (fixed):", stp_fixed # Update the indices within the indices dictionary on the side of the plot. string = '' for index, value in sorted(indices.items()): if np.ma.is_masked(value[0]): if debug: print("skipping masked value for index=", index) continue if debug: print("index=", index) print("value=", value) format = '%.' + str(value[1]) + 'f' string += index + ": " + format % value[0] + " " + value[2] + '\n' return string
def process_site(file): try: ## switch out the reading function for the anticipated data type data = read_wyoming_file(file) #data = read_sounding_file(file) ## depending on the data reader used, different block of code is requires #""" p = data[:, 0] Z = data[:, 1] T = data[:, 2] Td = data[:, 3] U = np.zeros(T.shape) V = np.zeros(T.shape) """ p = data[:, 0] T = data[:, 1] Td = data[:, 2] Z = data[:, 5] U = np.zeros(T.shape) V = np.zeros(T.shape) """ ## comment out this triple quote if using the above block dz = None p_og = None z_og = None p[p == 9999.0] = np.nan T[T == 999.0] = np.nan Td[Td == 999.0] = np.nan Z[Z == 9999.0] = np.nan p = np.ma.masked_invalid(p) T = np.ma.masked_invalid(T) Td = np.ma.masked_invalid(Td) Z = np.ma.masked_invalid(Z) except: ## read in Kevins beautiful data data = read_kevins_file(file) Z = data[:, 0] p = data[:, 1] T = data[:, 2] U = np.zeros(T.shape) V = np.zeros(T.shape) Td = data[:, 3] dz = data[:, 4][::10] p_og = data[:, 5][::10] z_og = data[:, 6][::10] p = np.ma.masked_invalid(p) T = np.ma.masked_invalid(T) Td = np.ma.masked_invalid(Td) Z = np.ma.masked_invalid(Z) print file ## create profile object for processing prof = profile.create_profile(profile='default', pres=p, hght=Z, tmpc=T, dwpc=Td, u=U, v=V, strictQC=False) if dz is not None: pvals = interp.pres(prof, interp.to_msl(prof, np.arange(0, 5000, 100))) dz = interp.generic_interp_pres(np.log10(pvals), np.log10(p_og)[::-1], dz[::-1]) print dz ## return the lifted parcel indices and the bore dz return lift_parcels(prof), dz
def get_kinematics(self): ''' Function to generate the numerous kinematic quantities used for display and calculations. It requires that the parcel calculations have already been called for the lcl to el shear and mean wind vectors, as well as indices that require an effective inflow layer. Parameters ---------- None Returns ------- None ''' sfc = self.pres[self.sfc] heights = np.array([1000., 3000., 4000., 5000., 6000., 8000., 9000.]) p1km, p3km, p4km, p5km, p6km, p8km, p9km = interp.pres( self, interp.to_msl(self, heights)) ## 1km and 6km winds self.wind1km = interp.vec(self, p1km) self.wind6km = interp.vec(self, p6km) ## calcluate wind shear self.sfc_1km_shear = winds.wind_shear(self, pbot=sfc, ptop=p1km) self.sfc_3km_shear = winds.wind_shear(self, pbot=sfc, ptop=p3km) self.sfc_6km_shear = winds.wind_shear(self, pbot=sfc, ptop=p6km) self.sfc_8km_shear = winds.wind_shear(self, pbot=sfc, ptop=p8km) self.sfc_9km_shear = winds.wind_shear(self, pbot=sfc, ptop=p9km) self.lcl_el_shear = winds.wind_shear(self, pbot=self.mupcl.lclpres, ptop=self.mupcl.elpres) ## calculate mean wind self.mean_1km = utils.comp2vec( *winds.mean_wind(self, pbot=sfc, ptop=p1km)) self.mean_3km = utils.comp2vec( *winds.mean_wind(self, pbot=sfc, ptop=p3km)) self.mean_6km = utils.comp2vec( *winds.mean_wind(self, pbot=sfc, ptop=p6km)) self.mean_8km = utils.comp2vec( *winds.mean_wind(self, pbot=sfc, ptop=p8km)) self.mean_lcl_el = utils.comp2vec(*winds.mean_wind( self, pbot=self.mupcl.lclpres, ptop=self.mupcl.elpres)) ## parameters that depend on the presence of an effective inflow layer if self.etop is ma.masked or self.ebottom is ma.masked: self.etopm = ma.masked self.ebotm = ma.masked self.srwind = winds.non_parcel_bunkers_motion(self) self.eff_shear = [MISSING, MISSING] self.ebwd = [MISSING, MISSING, MISSING] self.ebwspd = MISSING self.mean_eff = [MISSING, MISSING, MISSING] self.mean_ebw = [MISSING, MISSING, MISSING] self.srw_eff = [MISSING, MISSING, MISSING] self.srw_ebw = [MISSING, MISSING, MISSING] self.right_esrh = [ma.masked, ma.masked, ma.masked] self.left_esrh = [ma.masked, ma.masked, ma.masked] self.critical_angle = ma.masked else: self.srwind = params.bunkers_storm_motion(self, mupcl=self.mupcl, pbot=self.ebottom) depth = (self.mupcl.elhght - self.ebotm) / 2 elh = interp.pres(self, interp.to_msl(self, self.ebotm + depth)) ## calculate mean wind self.mean_eff = winds.mean_wind(self, self.ebottom, self.etop) self.mean_ebw = winds.mean_wind(self, pbot=self.ebottom, ptop=elh) ## calculate wind shear of the effective layer self.eff_shear = winds.wind_shear(self, pbot=self.ebottom, ptop=self.etop) self.ebwd = winds.wind_shear(self, pbot=self.ebottom, ptop=elh) self.ebwspd = utils.mag(self.ebwd[0], self.ebwd[1]) ## calculate the mean sr wind self.srw_eff = winds.sr_wind(self, pbot=self.ebottom, ptop=self.etop, stu=self.srwind[0], stv=self.srwind[1]) self.srw_ebw = winds.sr_wind(self, pbot=self.ebottom, ptop=elh, stu=self.srwind[0], stv=self.srwind[1]) self.right_esrh = winds.helicity(self, self.ebotm, self.etopm, stu=self.srwind[0], stv=self.srwind[1]) self.left_esrh = winds.helicity(self, self.ebotm, self.etopm, stu=self.srwind[2], stv=self.srwind[3]) self.critical_angle = winds.critical_angle(self, stu=self.srwind[0], stv=self.srwind[1]) ## calculate mean srw self.srw_1km = utils.comp2vec(*winds.sr_wind( self, pbot=sfc, ptop=p1km, stu=self.srwind[0], stv=self.srwind[1])) self.srw_3km = utils.comp2vec(*winds.sr_wind( self, pbot=sfc, ptop=p3km, stu=self.srwind[0], stv=self.srwind[1])) self.srw_6km = utils.comp2vec(*winds.sr_wind( self, pbot=sfc, ptop=p6km, stu=self.srwind[0], stv=self.srwind[1])) self.srw_8km = utils.comp2vec(*winds.sr_wind( self, pbot=sfc, ptop=p8km, stu=self.srwind[0], stv=self.srwind[1])) self.srw_4_5km = utils.comp2vec(*winds.sr_wind( self, pbot=p4km, ptop=p5km, stu=self.srwind[0], stv=self.srwind[1])) self.srw_lcl_el = utils.comp2vec( *winds.sr_wind(self, pbot=self.mupcl.lclpres, ptop=self.mupcl.elpres, stu=self.srwind[0], stv=self.srwind[1])) # This is for the red, blue, and purple bars that appear on the SR Winds vs. Height plot self.srw_0_2km = winds.sr_wind(self, pbot=sfc, ptop=interp.pres( self, interp.to_msl(self, 2000.)), stu=self.srwind[0], stv=self.srwind[1]) self.srw_4_6km = winds.sr_wind(self, pbot=interp.pres( self, interp.to_msl(self, 4000.)), ptop=p6km, stu=self.srwind[0], stv=self.srwind[1]) self.srw_9_11km = winds.sr_wind( self, pbot=interp.pres(self, interp.to_msl(self, 9000.)), ptop=interp.pres(self, interp.to_msl(self, 11000.)), stu=self.srwind[0], stv=self.srwind[1]) ## calculate upshear and downshear self.upshear_downshear = winds.mbe_vectors(self) self.srh1km = winds.helicity(self, 0, 1000., stu=self.srwind[0], stv=self.srwind[1]) self.srh3km = winds.helicity(self, 0, 3000., stu=self.srwind[0], stv=self.srwind[1])
ax3 = plt.subplot(gs[3, 0:3]) skew.plot_wind_axes(ax2) #skew.plot_wind_barbs(ax2, prof.pres, prof.u, prof.v) # all the wind barbs (usually too many to see pattern) # Reduce the number of data points to use for the wind barbs p_less, u_less, v_less = pressure_interval(prof.pres, prof.u, prof.v, 0, 1050, 25) skew.plot_wind_barbs(ax2, p_less, u_less, v_less) srwind = params.bunkers_storm_motion(prof) gs.update(left=0.05, bottom=0.05, top=0.95, right=1, wspace=0.025) ######################### # Calculate indices to be shown. p1km = interp.pres(prof, interp.to_msl(prof, 1000.)) p3km = interp.pres(prof, interp.to_msl(prof, 3000.)) p6km = interp.pres(prof, interp.to_msl(prof, 6000.)) p8km = interp.pres(prof, interp.to_msl(prof, 8000.)) p9km = interp.pres(prof, interp.to_msl(prof, 9000.)) sfc = prof.pres[prof.sfc] sfc_1km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p1km) sfc_3km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p3km) sfc_6km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p6km) sfc_8km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p8km) sfc_9km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p9km) srh3km = winds.helicity(prof, 0, 3000., stu=srwind[0], stv=srwind[1]) srh1km = winds.helicity(prof, 0, 1000., stu=srwind[0], stv=srwind[1]) scp = params.scp(prof.mupcl.bplus, prof.right_esrh[0], prof.ebwspd) stp_cin = params.stp_cin(prof.mlpcl.bplus, prof.right_esrh[0], prof.ebwspd, prof.mlpcl.lclhght, prof.mlpcl.bminus)