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 lapse_rate(lower, upper, prof, pres=1): ''' Calculates the lapse rate (C/km) from a profile object Inputs ------ lower (float) Lower Bound of lapse rate upper (float) Upper Bound of lapse rate prof (profile object) Profile Object pres (int 0/1) Flag to know to convert pres to height Returns ------- lapse rate (float [C/km]) ''' if pres: p1 = lower p2 = upper z1 = interp.hght(lower, prof) z2 = interp.hght(upper, prof) else: z1 = interp.msl(lower, prof) z2 = interp.msl(upper, prof) p1 = interp.pres(z1, prof) p2 = interp.pres(z2, prof) tv1 = interp.vtmp(p1, prof) tv2 = interp.vtmp(p2, prof) if not QC(tv1) or not QC(tv2): return RMISSD return (tv2 - tv1) / (z2 - z1) * -1000.
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_pres(): input_z = 1000. correct_p = 903.8343884049208 returned_p = interp.pres(prof, input_z) npt.assert_almost_equal(returned_p, correct_p) input_z = [1000., 3000., 6000.] correct_p = np.asarray([903.834388405, 710.02200544, 482.16636819]) returned_p = interp.pres(prof, input_z) npt.assert_almost_equal(returned_p, correct_p)
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 bulk_rich(pcl, prof): ''' Calculates the Bulk Richardson Number for a given parcel. Inputs ------ pcl (parcel object) Parcel Object prof (profile object) Profile Object Returns ------- Bulk Richardson Number ''' # Make sure parcel is initialized if pcl.lplvals.flag == RMISSD: pbot = RMISSD elif pcl.lplvals.flag > 0 and pcl.lplvals.flag < 4: ptop = interp.pres(interp.msl(6000., prof), prof) pbot = prof.gSndg[prof.sfc][prof.pind] else: h0 = interp.hght(pcl.pres, prof) try: pbot = interp.pres(h0 - 500., prof) except: pbot = RMISSD if not QC(pbot): pbot = prof.gSndg[prof.sfc][prof.pind] h1 = interp.hght(pbot, prof) ptop = interp.pres(h1 + 6000., prof) if not QC(pbot) or not QC(ptop): pcl.brnshear = RMISSD pcl.brn = RMISSD return pcl # Calculate lowest 500m mean wind p = interp.pres(interp.hght(pbot, prof) + 500., prof) mnlu, mnlv = winds.mean_wind(pbot, p, prof) # Calculate the 6000m mean wind mnuu, mnuv = winds.mean_wind(pbot, ptop, prof) # Make sure CAPE and Shear are available if not QC(pcl.bplus) or not QC(mnlu) or not QC(mnuu): pcl.brnshear = RMISSD pcl.brn = RMISSD return pcl # Calculate shear between levels dx = mnuu - mnlu dy = mnuv - mnlv pcl.brnshear = KTS2MS(vector.comp2vec(dx, dy)[1]) pcl.brnshear = pcl.brnshear**2 / 2. pcl.brn = pcl.bplus / pcl.brnshear return pcl
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 corfidi_mcs_motion(prof): ''' Calculated the Meso-beta Elements (Corfidi) Vectors Inputs ------ prof (profile object) Profile Object Returns ------- upu (float) U-component of the upshear vector upv (float) V-component of the upshear vector dnu (float) U-component of the downshear vector dnv (float) V-component of the downshear vector ''' # Compute the tropospheric (850hPa-300hPa) mean wind mnu1, mnv1 = mean_wind_npw(850., 300., prof) # Compute the low-level (SFC-1500m) mean wind p_1p5km = interp.pres(interp.msl(1500., prof), prof) mnu2, mnv2 = mean_wind_npw(prof.gSndg[prof.sfc][prof.pind], p_1p5km, prof) # 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 Inputs ------ prof (profile object) Profile Object Returns ------- rstu (float) Right Storm Motion U-component rstv (float) Right Storm Motion V-component lstu (float) Left Storm Motion U-component lstv (float) Left Storm Motion V-component ''' d = MS2KTS(7.5) # Deviation value emperically derived as 7.5 m/s msl6km = interp.msl(6000., prof) p6km = interp.pres(msl6km, prof) # SFC-6km Mean Wind mnu6, mnv6 = mean_wind_npw(prof.gSndg[prof.sfc][prof.pind], p6km, prof, 20) # SFC-6km Shear Vector shru6, shrv6 = wind_shear(prof.gSndg[prof.sfc][prof.pind], p6km, prof) # Bunkers Right Motion tmp = d / vector.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 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 bunkers_storm_motion(prof, pbot=None, **kwargs): ''' Compute the Bunkers Storm Motion for a Right Moving Supercell using a parcel based approach. Inputs ------ prof (profile object) Profile Object pbot (float) Base of effective-inflow layer (hPa) Returns ------- rstu (float) Right Storm Motion U-component rstv (float) Right Storm Motion V-component lstu (float) Left Storm Motion U-component lstv (float) Left Storm Motion V-component ''' d = MS2KTS(7.5) # Deviation value emperically derived as 7.5 m/s # If MUPCL provided, use it, otherwise create MUPCL if 'mupcl' in kwargs: mupcl = kwargs.get('mupcl') else: mulplvals = params.DefineParcel(3, prof, pres=400) mupcl = params.parcelx(-1, -1, mulplvals.pres, mulplvals.temp, mulplvals.dwpt, prof, lplvals=mulplvals) mucape = mupcl.bplus mucinh = mupcl.bminus muel = mupcl.elhght if not pbot: pbot, ptop = effective_inflow_layer(100, -250, prof) base = interp.agl(interp.hght(pbot, prof), prof) if mucape > 100. and QC(muel) and base >= 750: depth = muel - base htop = base + depth / 2. ptop = interp.pres(interp.msl(base + htop, prof), prof) mnu, mnv = winds.mean_wind_npw(pbot, ptop, prof) sru, srv = winds.wind_shear(pbot, ptop, prof) srmag = vector.mag(sru, srv) uchg = d / srmag * srv vchg = d / srmag * sru rstu = mnu + uchg rstv = mnv - vchg lstu = mnu - uchg lstv = mnv + vchg else: rstu, rstv, lstu, lstv = winds.non_parcel_bunkers_motion(prof) 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) 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 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 interpZeroWetbulbs(prof): tLength = len(prof.wetbulb) level = 1 while (level < tLength): prevLevel = level - 1 nextLevel = level + 1 prevT = prof.wetbulb[prevLevel] thisT = prof.wetbulb[level] if (thisT * prevT < 0): # print "Crossed wb zero:", prevT, thisT zeroHt = -9999 if (thisT < prevT): zeroHt = np.interp(0,np.flipud(prof.wetbulb[prevLevel:nextLevel]),np.flipud(prof.hght[prevLevel:nextLevel])) else: zeroHt = np.interp(0,prof.wetbulb[prevLevel:nextLevel],prof.hght[prevLevel:nextLevel]) zeroPres = interp.pres(prof,zeroHt) prof.wetbulb = np.insert(prof.wetbulb,level,[0]) prof.tmpc = np.insert(prof.tmpc,level, [interp.temp(prof,zeroPres)]) prof = insertLevels(prof,zeroHt,zeroPres,level) tLength += 1 # Double increment j since you just added a new element level = level + 2 else: level = level + 1 return prof
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 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 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
''' Create the Sounding (Profile) Object '''
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)
def parcelx(lower, upper, pres, temp, dwpt, prof, **kwargs): ''' Lifts the specified parcel, calculated various levels and parameters from the profile object. B+/B- are calculated based on the specified layer. !! All calculations use the virtual temperature correction unless noted. !! Inputs ------ lower (float) Lower-bound lifting level (hPa) upper (float) Upper-bound lifting level pres (float) Pressure of parcel to lift (hPa) temp (float) Temperature of parcel to lift (C) dwpt (float) Dew Point of parcel to lift (C) prof (profile object) Profile Object Returns ------- pcl (parcel object) Parcel Object ''' pcl = Parcel(-1, -1, pres, temp, dwpt) if 'lplvals' in kwargs: pcl.lplvals = kwargs.get('lplvals') else: lplvals = DefineParcel(prof, 5, pres=pres, temp=temp, dwpt=dwpt) pcl.lplvals = lplvals if prof.gNumLevels < 1: return pcl lyre = -1 cap_strength = RMISSD cap_strengthpres = RMISSD li_max = RMISSD li_maxpres = RMISSD totp = 0. totn = 0. tote = 0. cinh_old = 0. # See if default layer is specified if lower == -1: lower = prof.gSndg[prof.sfc][prof.pind] pcl.blayer = lower if upper == -1: upper = prof.gSndg[prof.gNumLevels-1][prof.pind] pcl.tlayer = upper # Make sure that this is a valid layer if lower > pres: lower = pres pcl.blayer = lower if not QC(interp.vtmp(lower, prof)) or \ not QC(interp.vtmp(upper, prof)): return RMISSD # Begin with the Mixing Layer te1 = interp.vtmp(pres, prof) pe1 = lower h1 = interp.hght(pe1, prof) tp1 = thermo.virtemp(pres, temp, dwpt) # te1 = tp1 # Lift parcel and return LCL pres (hPa) and LCL temp (c) pe2, tp2 = thermo.drylift(pres, temp, dwpt) blupper = pe2 # Define top of layer as LCL pres h2 = interp.hght(pe2, prof) te2 = interp.vtmp(pe2, prof) pcl.lclpres = pe2 pcl.lclhght = interp.agl(h2, prof) # Calculate lifted parcel theta for use in iterative CINH loop below # RECALL: lifted parcel theta is CONSTANT from LPL to LCL theta_parcel = thermo.theta(pe2, tp2, 1000.) # Environmental theta and mixing ratio at LPL bltheta = thermo.theta(pres, interp.temp(pres, prof), 1000.) blmr = thermo.mixratio(pres, dwpt) # ACCUMULATED CINH IN MIXING LAYER BELOW THE LCL # This will be done in 10mb increments, and will use the virtual # temperature correction where possible pinc = -10 a = int(lower) b = int(blupper) for pp in range(a, b, int(pinc)): pp1 = pp pp2 = pp + pinc if pp2 < blupper: pp2 = blupper dz = interp.hght(pp2, prof) - interp.hght(pp1, prof) # Calculate difference between Tv_parcel and Tv_environment at top # and bottom of 10mb layers. Make use of constant lifted parcel # theta and mixing ratio from LPL to LCL tv_env_bot = thermo.virtemp(pp1, thermo.theta(pp1, interp.temp(pp1, prof), 1000.), interp.dwpt(pp1, prof)) tdef1 = (thermo.virtemp(pp1, theta_parcel, thermo.temp_at_mixrat(blmr, pp1)) - tv_env_bot) / \ (thermo.ctok(tv_env_bot)) tv_env_top = thermo.virtemp(pp2, thermo.theta(pp2, interp.temp(pp2, prof), 1000.), interp.dwpt(pp2, prof)) tdef2 = (thermo.virtemp(pp2, theta_parcel, thermo.temp_at_mixrat(blmr, pp2)) - tv_env_top) / \ (thermo.ctok(tv_env_bot)) lyre = G * (tdef1 + tdef2) / 2. * dz if lyre < 0: totn += lyre # Move the bottom layer to the top of the boundary layer if lower > pe2: lower = pe2 pcl.blayer = lower # Calculate height of various temperature levels p0c = temp_lvl(0., prof) pm10c = temp_lvl(-10., prof) pm20c = temp_lvl(-20., prof) pm30c = temp_lvl(-30., prof) hgt0c = interp.hght(p0c, prof) hgtm10c = interp.hght(pm10c, prof) hgtm20c = interp.hght(pm20c, prof) hgtm30c = interp.hght(pm30c, prof) pcl.p0c = p0c pcl.pm10c = pm10c pcl.pm20c = pm20c pcl.pm30c = pm30c pcl.hght0c = hgt0c pcl.hghtm10c = hgtm10c pcl.hghtm20c = hgtm20c pcl.hghtm30c = hgtm30c # Find lowest observation in layer i = 0 while prof.gSndg[i][prof.pind] > lower: if i == prof.gNumLevels-1: break i += 1 while not QC(prof.gSndg[i][prof.tdind]): if i == prof.gNumLevels-1: break i += 1 lptr = i if prof.gSndg[i][prof.pind] == lower: if i != prof.gNumLevels-1: lptr += 1 # Find highest observation in layer i = prof.gNumLevels-1 while prof.gSndg[i][prof.pind] < upper: if i < lptr: break i -= 1 uptr = i if prof.gSndg[i][prof.pind] == upper: if i > lptr: uptr -= 1 # START WITH INTERPOLATED BOTTOM LAYER # Begin moist ascent from lifted parcel LCL (pe2, tp2) pe1 = lower h1 = interp.hght(pe1, prof) te1 = interp.vtmp(pe1, prof) tp1 = thermo.wetlift(pe2, tp2, pe1) lyre = 0 lyrlast = 0 for i in range(lptr, prof.gNumLevels): if not QC(prof.gSndg[i][prof.tind]): continue pe2 = prof.gSndg[i][prof.pind] h2 = prof.gSndg[i][prof.zind] te2 = interp.vtmp(pe2, prof) tp2 = thermo.wetlift(pe1, tp1, pe2) tdef1 = (thermo.virtemp(pe1, tp1, tp1) - te1) / thermo.ctok(te1) tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / thermo.ctok(te2) lyrlast = lyre lyre = G * (tdef1 + tdef2) / 2. * (h2 - h1) # Add layer energy to total positive if lyre > 0 if lyre > 0: totp += lyre # Add layer energy to total negative if lyre < 0, only up to EL else: if pe2 > 500.: totn += lyre # Check for Max LI mli = thermo.virtemp(pe2, tp2, tp2) - te2 if mli > li_max: li_max = mli li_maxpres = pe2 # Check for Max Cap Strength mcap = te2 - mli if mcap > cap_strength: cap_strength = mcap cap_strengthpres = pe2 tote += lyre pelast = pe1 pe1 = pe2 h1 = h2 te1 = te2 tp1 = tp2 # Is this the top of the specified layer if i >= uptr and not QC(pcl.bplus): pe3 = pe1 h3 = h1 te3 = te1 tp3 = tp1 lyrf = lyre if lyrf > 0: pcl.bplus = totp - lyrf pcl.bminus = totn else: pcl.bplus = totp if pe2 > 500.: pcl.bminus = totn + lyrf else: pcl.bminus = totn pe2 = upper h2 = interp.hght(pe2, prof) te2 = interp.vtmp(pe2, prof) tp2 = thermo.wetlift(pe3, tp3, pe2) tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / thermo.ctok(te3) tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / thermo.ctok(te2) lyrf = G * (tdef3 + tdef2) / 2. * (h2 - h3) if lyrf > 0: pcl.bplus += lyrf else: if pe2 > 500.: pcl.bminus += lyrf if pcl.bplus == 0: pcl.bminus = 0. # Is this the freezing level if te2 < 0. and not QC(pcl.bfzl): pe3 = pelast h3 = interp.hght(pe3, prof) te3 = interp.vtmp(pe3, prof) tp3 = thermo.wetlift(pe1, tp1, pe3) lyrf = lyre if lyrf > 0.: pcl.bfzl = totp - lyrf else: pcl.bfzl = totp if not QC(p0c) or p0c > pe3: pcl.bfzl = 0 elif QC(pe2): te2 = interp.vtmp(pe2, prof) tp2 = thermo.wetlift(pe3, tp3, pe2) tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \ thermo.ctok(te3) tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \ thermo.ctok(te2) lyrf = G * (tdef3 + tdef2) / 2. * (hgt0c - h3) if lyrf > 0: pcl.bfzl += lyrf # Is this the -10C level if te2 < -10. and not QC(pcl.wm10c): pe3 = pelast h3 = interp.hght(pe3, prof) te3 = interp.vtmp(pe3, prof) tp3 = thermo.wetlift(pe1, tp1, pe3) lyrf = lyre if lyrf > 0.: pcl.wm10c = totp - lyrf else: pcl.wm10c = totp if not QC(pm10c) or pm10c > pcl.lclpres: pcl.wm10c = 0 elif QC(pe2): te2 = interp.vtmp(pe2, prof) tp2 = thermo.wetlift(pe3, tp3, pe2) tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \ thermo.ctok(te3) tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \ thermo.ctok(te2) lyrf = G * (tdef3 + tdef2) / 2. * (hgtm10c - h3) if lyrf > 0: pcl.wm10c += lyrf # Is this the -20C level if te2 < -20. and not QC(pcl.wm20c): pe3 = pelast h3 = interp.hght(pe3, prof) te3 = interp.vtmp(pe3, prof) tp3 = thermo.wetlift(pe1, tp1, pe3) lyrf = lyre if lyrf > 0.: pcl.wm20c = totp - lyrf else: pcl.wm20c = totp if not QC(pm20c) or pm20c > pcl.lclpres: pcl.wm20c = 0 elif QC(pe2): te2 = interp.vtmp(pe2, prof) tp2 = thermo.wetlift(pe3, tp3, pe2) tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \ thermo.ctok(te3) tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \ thermo.ctok(te2) lyrf = G * (tdef3 + tdef2) / 2. * (hgtm20c - h3) if lyrf > 0: pcl.wm20c += lyrf # Is this the -30C level if te2 < -30. and not QC(pcl.wm30c): pe3 = pelast h3 = interp.hght(pe3, prof) te3 = interp.vtmp(pe3, prof) tp3 = thermo.wetlift(pe1, tp1, pe3) lyrf = lyre if lyrf > 0.: pcl.wm30c = totp - lyrf else: pcl.wm30c = totp if not QC(pm30c) or pm30c > pcl.lclpres: pcl.wm30c = 0 elif QC(pe2): te2 = interp.vtmp(pe2, prof) tp2 = thermo.wetlift(pe3, tp3, pe2) tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \ thermo.ctok(te3) tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \ thermo.ctok(te2) lyrf = G * (tdef3 + tdef2) / 2. * (hgtm30c - h3) if lyrf > 0: pcl.wm30c += lyrf # Is this the 3km level if pcl.lclhght < 3000.: h = interp.agl(interp.hght(pe2, prof), prof) if h >= 3000. and not QC(pcl.b3km): pe3 = pelast h3 = interp.hght(pe3, prof) te3 = interp.vtmp(pe3, prof) tp3 = thermo.wetlift(pe1, tp1, pe3) lyrf = lyre if lyrf > 0: pcl.b3km = totp - lyrf else: pcl.b3km = totp h2 = interp.msl(3000., prof) pe2 = interp.pres(h2, prof) if QC(pe2): te2 = interp.vtmp(pe2, prof) tp2 = thermo.wetlift(pe3, tp3, pe2) tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \ thermo.ctok(te3) tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \ thermo.ctok(te2) lyrf = G * (tdef3 + tdef2) / 2. * (h2 - h3) if lyrf > 0: pcl.b3km += lyrf else: pcl.b3km = 0. # Is this the 6km level if pcl.lclhght < 6000.: h = interp.agl(interp.hght(pe2, prof), prof) if h >= 6000. and not QC(pcl.b6km): pe3 = pelast h3 = interp.hght(pe3, prof) te3 = interp.vtmp(pe3, prof) tp3 = thermo.wetlift(pe1, tp1, pe3) lyrf = lyre if lyrf > 0: pcl.b6km = totp - lyrf else: pcl.b6km = totp h2 = interp.msl(6000., prof) pe2 = interp.pres(h2, prof) if QC(pe2): te2 = interp.vtmp(pe2, prof) tp2 = thermo.wetlift(pe3, tp3, pe2) tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \ thermo.ctok(te3) tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \ thermo.ctok(te2) lyrf = G * (tdef3 + tdef2) / 2. * (h2 - h3) if lyrf > 0: pcl.b6km += lyrf else: pcl.b6km = 0. # LFC Possibility if lyre >= 0. and lyrlast <= 0.: tp3 = tp1 te3 = te1 pe2 = pe1 pe3 = pelast while interp.vtmp(pe3, prof) > thermo.virtemp(pe3, thermo.wetlift(pe2, tp3, pe3), thermo.wetlift(pe2, tp3, pe3)): pe3 -= 5 pcl.lfcpres = pe3 pcl.lfchght = interp.agl(interp.hght(pe3, prof), prof) cinh_old = totn tote = 0. pcl.elpres = RMISSD li_max = RMISSD if cap_strength < 0.: cap_strength = 0. pcl.cap = cap_strength pcl.cappres = cap_strengthpres # Hack to force LFC to be at least at the LCL if pcl.lfcpres > pcl.lclpres: pcl.lfcpres = pcl.lclpres pcl.lfchght = pcl.lclhght # EL Possibility if lyre <= 0. and lyrlast >= 0.: tp3 = tp1 te3 = te1 pe2 = pe1 pe3 = pelast while interp.vtmp(pe3, prof) < thermo.virtemp(pe3, thermo.wetlift(pe2, tp3, pe3), thermo.wetlift(pe2, tp3, pe3)): pe3 -= 5 pcl.elpres = pe3 pcl.elhght = interp.agl(interp.hght(pe3, prof), prof) pcl.mplpres = RMISSD pcl.limax = -li_max pcl.limaxpress = li_maxpres # MPL Possibility if tote < 0. and not QC(pcl.mplpres) and QC(pcl.elpres): pe3 = pelast h3 = interp.hght(pe3, prof) te3 = interp.vtmp(pe3, prof) tp3 = thermo.wetlift(pe1, tp1, pe3) totx = tote - lyre pe2 = pelast while totx > 0: pe2 -= 1 te2 = interp.vtmp(pe2, prof) tp2 = thermo.wetlift(pe3, tp3, pe2) h2 = interp.hght(pe2, prof) tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \ thermo.ctok(te3) tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \ thermo.ctok(te2) lyrf = G * (tdef3 + tdef2) / 2. * (h2 - h3) totx += lyrf tp3 = tp2 te3 = te2 pe3 = pe2 pcl.mplpres = pe2 pcl.mplhght = interp.agl(interp.hght(pe2, prof), prof) # 500 hPa Lifted Index if prof.gSndg[i][prof.pind] <= 500. and pcl.li5 == RMISSD: a = interp.vtmp(500., prof) b = thermo.wetlift(pe1, tp1, 500.) pcl.li5 = a - thermo.virtemp(500, b, b) # 300 hPa Lifted Index if prof.gSndg[i][prof.pind] <= 300. and pcl.li3 == RMISSD: a = interp.vtmp(300., prof) b = thermo.wetlift(pe1, tp1, 300.) pcl.li3 = a - thermo.virtemp(300, b, b) # Calculate BRN if available pcl = bulk_rich(pcl, prof) pcl.bminus = cinh_old if pcl.bplus == 0: pcl.bminus = 0. return pcl
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)
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 helicity(lower, upper, prof, stu=0, stv=0): ''' 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. Inputs ------ lower (float) Bottom level of layer (m, AGL) upper (float) Top level of layer (m, AGL) prof (profile object) Profile Object stu (float; optional) U-component of storm-motion stv (float; optional) V-component of storm-motion Returns ------- phel+nhel (float) Combined Helicity (m2/s2) phel (float) Positive Helicity (m2/s2) nhel (float) Negative Helicity (m2/s2) ''' lower = interp.msl(lower, prof) upper = interp.msl(upper, prof) plower = interp.pres(lower, prof) pupper = interp.pres(upper, prof) phel = 0 nhel = 0 # Find lower and upper ind bounds for looping i = 0 while interp.msl(prof.gSndg[i][prof.zind], prof) < lower: i += 1 lptr = i if interp.msl(prof.gSndg[i][prof.zind], prof) == lower: lptr += 1 while interp.msl(prof.gSndg[i][prof.zind], prof) <= upper: i += 1 uptr = i if interp.msl(prof.gSndg[i][prof.zind], prof) == upper: uptr -= 1 # Integrate from interpolated bottom level to iptr level sru1, srv1 = interp.components(plower, prof) sru1 = KTS2MS(sru1 - stu) srv1 = KTS2MS(srv1 - stv) # Loop through levels for i in range(lptr, uptr + 1): if QC(prof.gSndg[i][prof.uind]) and QC(prof.gSndg[i][prof.vind]): sru2, srv2 = interp.components(prof.gSndg[i][prof.pind], prof) sru2 = KTS2MS(sru2 - stu) srv2 = KTS2MS(srv2 - stv) lyrh = (sru2 * srv1) - (sru1 * srv2) if lyrh > 0: phel += lyrh else: nhel += lyrh sru1 = sru2 srv1 = srv2 # Integrate from tptr level to interpolated top level sru2, srv2 = interp.components(pupper, prof) sru2 = KTS2MS(sru2 - stu) srv2 = KTS2MS(srv2 - stv) lyrh = (sru2 * srv1) - (sru1 * srv2) if lyrh > 0: phel += lyrh else: nhel += lyrh return phel + nhel, phel, nhel
def helicity(lower, upper, prof, stu=0, stv=0): ''' 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. Inputs ------ lower (float) Bottom level of layer (m, AGL) upper (float) Top level of layer (m, AGL) prof (profile object) Profile Object stu (float; optional) U-component of storm-motion stv (float; optional) V-component of storm-motion Returns ------- phel+nhel (float) Combined Helicity (m2/s2) phel (float) Positive Helicity (m2/s2) nhel (float) Negative Helicity (m2/s2) ''' lower = interp.msl(lower, prof) upper = interp.msl(upper, prof) plower = interp.pres(lower, prof) pupper = interp.pres(upper, prof) phel = 0 nhel = 0 # Find lower and upper ind bounds for looping i = 0 while interp.msl(prof.gSndg[i][prof.zind], prof) < lower: i+=1 lptr = i if interp.msl(prof.gSndg[i][prof.zind], prof) == lower: lptr+=1 while interp.msl(prof.gSndg[i][prof.zind], prof) <= upper: i+=1 uptr = i if interp.msl(prof.gSndg[i][prof.zind], prof) == upper: uptr-=1 # Integrate from interpolated bottom level to iptr level sru1, srv1 = interp.components(plower, prof) sru1 = KTS2MS(sru1 - stu) srv1 = KTS2MS(srv1 - stv) # Loop through levels for i in range(lptr, uptr+1): if QC(prof.gSndg[i][prof.uind]) and QC(prof.gSndg[i][prof.vind]): sru2, srv2 = interp.components(prof.gSndg[i][prof.pind], prof) sru2 = KTS2MS(sru2 - stu) srv2 = KTS2MS(srv2 - stv) lyrh = (sru2 * srv1) - (sru1 * srv2) if lyrh > 0: phel += lyrh else: nhel += lyrh sru1 = sru2 srv1 = srv2 # Integrate from tptr level to interpolated top level sru2, srv2 = interp.components(pupper, prof) sru2 = KTS2MS(sru2 - stu) srv2 = KTS2MS(srv2 - stv) lyrh = (sru2 * srv1) - (sru1 * srv2) if lyrh > 0: phel += lyrh else: nhel += lyrh return phel+nhel, phel, nhel
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 parcelx(lower, upper, pres, temp, dwpt, prof, **kwargs): ''' Lifts the specified parcel, calculated various levels and parameters from the profile object. B+/B- are calculated based on the specified layer. !! All calculations use the virtual temperature correction unless noted. !! Inputs ------ lower (float) Lower-bound lifting level (hPa) upper (float) Upper-bound lifting level pres (float) Pressure of parcel to lift (hPa) temp (float) Temperature of parcel to lift (C) dwpt (float) Dew Point of parcel to lift (C) prof (profile object) Profile Object Returns ------- pcl (parcel object) Parcel Object ''' pcl = Parcel(-1, -1, pres, temp, dwpt) if 'lplvals' in kwargs: pcl.lplvals = kwargs.get('lplvals') else: lplvals = DefineParcel(prof, 5, pres=pres, temp=temp, dwpt=dwpt) pcl.lplvals = lplvals if prof.gNumLevels < 1: return pcl lyre = -1 cap_strength = RMISSD cap_strengthpres = RMISSD li_max = RMISSD li_maxpres = RMISSD totp = 0. totn = 0. tote = 0. cinh_old = 0. # See if default layer is specified if lower == -1: lower = prof.gSndg[prof.sfc][prof.pind] pcl.blayer = lower if upper == -1: upper = prof.gSndg[prof.gNumLevels - 1][prof.pind] pcl.tlayer = upper # Make sure that this is a valid layer if lower > pres: lower = pres pcl.blayer = lower if not QC(interp.vtmp(lower, prof)) or \ not QC(interp.vtmp(upper, prof)): return RMISSD # Begin with the Mixing Layer te1 = interp.vtmp(pres, prof) pe1 = lower h1 = interp.hght(pe1, prof) tp1 = thermo.virtemp(pres, temp, dwpt) # te1 = tp1 # Lift parcel and return LCL pres (hPa) and LCL temp (c) pe2, tp2 = thermo.drylift(pres, temp, dwpt) blupper = pe2 # Define top of layer as LCL pres h2 = interp.hght(pe2, prof) te2 = interp.vtmp(pe2, prof) pcl.lclpres = pe2 pcl.lclhght = interp.agl(h2, prof) # Calculate lifted parcel theta for use in iterative CINH loop below # RECALL: lifted parcel theta is CONSTANT from LPL to LCL theta_parcel = thermo.theta(pe2, tp2, 1000.) # Environmental theta and mixing ratio at LPL bltheta = thermo.theta(pres, interp.temp(pres, prof), 1000.) blmr = thermo.mixratio(pres, dwpt) # ACCUMULATED CINH IN MIXING LAYER BELOW THE LCL # This will be done in 10mb increments, and will use the virtual # temperature correction where possible pinc = -10 a = int(lower) b = int(blupper) for pp in range(a, b, int(pinc)): pp1 = pp pp2 = pp + pinc if pp2 < blupper: pp2 = blupper dz = interp.hght(pp2, prof) - interp.hght(pp1, prof) # Calculate difference between Tv_parcel and Tv_environment at top # and bottom of 10mb layers. Make use of constant lifted parcel # theta and mixing ratio from LPL to LCL tv_env_bot = thermo.virtemp( pp1, thermo.theta(pp1, interp.temp(pp1, prof), 1000.), interp.dwpt(pp1, prof)) tdef1 = (thermo.virtemp(pp1, theta_parcel, thermo.temp_at_mixrat(blmr, pp1)) - tv_env_bot) / \ (thermo.ctok(tv_env_bot)) tv_env_top = thermo.virtemp( pp2, thermo.theta(pp2, interp.temp(pp2, prof), 1000.), interp.dwpt(pp2, prof)) tdef2 = (thermo.virtemp(pp2, theta_parcel, thermo.temp_at_mixrat(blmr, pp2)) - tv_env_top) / \ (thermo.ctok(tv_env_bot)) lyre = G * (tdef1 + tdef2) / 2. * dz if lyre < 0: totn += lyre # Move the bottom layer to the top of the boundary layer if lower > pe2: lower = pe2 pcl.blayer = lower # Calculate height of various temperature levels p0c = temp_lvl(0., prof) pm10c = temp_lvl(-10., prof) pm20c = temp_lvl(-20., prof) pm30c = temp_lvl(-30., prof) hgt0c = interp.hght(p0c, prof) hgtm10c = interp.hght(pm10c, prof) hgtm20c = interp.hght(pm20c, prof) hgtm30c = interp.hght(pm30c, prof) pcl.p0c = p0c pcl.pm10c = pm10c pcl.pm20c = pm20c pcl.pm30c = pm30c pcl.hght0c = hgt0c pcl.hghtm10c = hgtm10c pcl.hghtm20c = hgtm20c pcl.hghtm30c = hgtm30c # Find lowest observation in layer i = 0 while prof.gSndg[i][prof.pind] > lower: if i == prof.gNumLevels - 1: break i += 1 while not QC(prof.gSndg[i][prof.tdind]): if i == prof.gNumLevels - 1: break i += 1 lptr = i if prof.gSndg[i][prof.pind] == lower: if i != prof.gNumLevels - 1: lptr += 1 # Find highest observation in layer i = prof.gNumLevels - 1 while prof.gSndg[i][prof.pind] < upper: if i < lptr: break i -= 1 uptr = i if prof.gSndg[i][prof.pind] == upper: if i > lptr: uptr -= 1 # START WITH INTERPOLATED BOTTOM LAYER # Begin moist ascent from lifted parcel LCL (pe2, tp2) pe1 = lower h1 = interp.hght(pe1, prof) te1 = interp.vtmp(pe1, prof) tp1 = thermo.wetlift(pe2, tp2, pe1) lyre = 0 lyrlast = 0 for i in range(lptr, prof.gNumLevels): if not QC(prof.gSndg[i][prof.tind]): continue pe2 = prof.gSndg[i][prof.pind] h2 = prof.gSndg[i][prof.zind] te2 = interp.vtmp(pe2, prof) tp2 = thermo.wetlift(pe1, tp1, pe2) tdef1 = (thermo.virtemp(pe1, tp1, tp1) - te1) / thermo.ctok(te1) tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / thermo.ctok(te2) lyrlast = lyre lyre = G * (tdef1 + tdef2) / 2. * (h2 - h1) # Add layer energy to total positive if lyre > 0 if lyre > 0: totp += lyre # Add layer energy to total negative if lyre < 0, only up to EL else: if pe2 > 500.: totn += lyre # Check for Max LI mli = thermo.virtemp(pe2, tp2, tp2) - te2 if mli > li_max: li_max = mli li_maxpres = pe2 # Check for Max Cap Strength mcap = te2 - mli if mcap > cap_strength: cap_strength = mcap cap_strengthpres = pe2 tote += lyre pelast = pe1 pe1 = pe2 h1 = h2 te1 = te2 tp1 = tp2 # Is this the top of the specified layer if i >= uptr and not QC(pcl.bplus): pe3 = pe1 h3 = h1 te3 = te1 tp3 = tp1 lyrf = lyre if lyrf > 0: pcl.bplus = totp - lyrf pcl.bminus = totn else: pcl.bplus = totp if pe2 > 500.: pcl.bminus = totn + lyrf else: pcl.bminus = totn pe2 = upper h2 = interp.hght(pe2, prof) te2 = interp.vtmp(pe2, prof) tp2 = thermo.wetlift(pe3, tp3, pe2) tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / thermo.ctok(te3) tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / thermo.ctok(te2) lyrf = G * (tdef3 + tdef2) / 2. * (h2 - h3) if lyrf > 0: pcl.bplus += lyrf else: if pe2 > 500.: pcl.bminus += lyrf if pcl.bplus == 0: pcl.bminus = 0. # Is this the freezing level if te2 < 0. and not QC(pcl.bfzl): pe3 = pelast h3 = interp.hght(pe3, prof) te3 = interp.vtmp(pe3, prof) tp3 = thermo.wetlift(pe1, tp1, pe3) lyrf = lyre if lyrf > 0.: pcl.bfzl = totp - lyrf else: pcl.bfzl = totp if not QC(p0c) or p0c > pe3: pcl.bfzl = 0 elif QC(pe2): te2 = interp.vtmp(pe2, prof) tp2 = thermo.wetlift(pe3, tp3, pe2) tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \ thermo.ctok(te3) tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \ thermo.ctok(te2) lyrf = G * (tdef3 + tdef2) / 2. * (hgt0c - h3) if lyrf > 0: pcl.bfzl += lyrf # Is this the -10C level if te2 < -10. and not QC(pcl.wm10c): pe3 = pelast h3 = interp.hght(pe3, prof) te3 = interp.vtmp(pe3, prof) tp3 = thermo.wetlift(pe1, tp1, pe3) lyrf = lyre if lyrf > 0.: pcl.wm10c = totp - lyrf else: pcl.wm10c = totp if not QC(pm10c) or pm10c > pcl.lclpres: pcl.wm10c = 0 elif QC(pe2): te2 = interp.vtmp(pe2, prof) tp2 = thermo.wetlift(pe3, tp3, pe2) tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \ thermo.ctok(te3) tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \ thermo.ctok(te2) lyrf = G * (tdef3 + tdef2) / 2. * (hgtm10c - h3) if lyrf > 0: pcl.wm10c += lyrf # Is this the -20C level if te2 < -20. and not QC(pcl.wm20c): pe3 = pelast h3 = interp.hght(pe3, prof) te3 = interp.vtmp(pe3, prof) tp3 = thermo.wetlift(pe1, tp1, pe3) lyrf = lyre if lyrf > 0.: pcl.wm20c = totp - lyrf else: pcl.wm20c = totp if not QC(pm20c) or pm20c > pcl.lclpres: pcl.wm20c = 0 elif QC(pe2): te2 = interp.vtmp(pe2, prof) tp2 = thermo.wetlift(pe3, tp3, pe2) tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \ thermo.ctok(te3) tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \ thermo.ctok(te2) lyrf = G * (tdef3 + tdef2) / 2. * (hgtm20c - h3) if lyrf > 0: pcl.wm20c += lyrf # Is this the -30C level if te2 < -30. and not QC(pcl.wm30c): pe3 = pelast h3 = interp.hght(pe3, prof) te3 = interp.vtmp(pe3, prof) tp3 = thermo.wetlift(pe1, tp1, pe3) lyrf = lyre if lyrf > 0.: pcl.wm30c = totp - lyrf else: pcl.wm30c = totp if not QC(pm30c) or pm30c > pcl.lclpres: pcl.wm30c = 0 elif QC(pe2): te2 = interp.vtmp(pe2, prof) tp2 = thermo.wetlift(pe3, tp3, pe2) tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \ thermo.ctok(te3) tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \ thermo.ctok(te2) lyrf = G * (tdef3 + tdef2) / 2. * (hgtm30c - h3) if lyrf > 0: pcl.wm30c += lyrf # Is this the 3km level if pcl.lclhght < 3000.: h = interp.agl(interp.hght(pe2, prof), prof) if h >= 3000. and not QC(pcl.b3km): pe3 = pelast h3 = interp.hght(pe3, prof) te3 = interp.vtmp(pe3, prof) tp3 = thermo.wetlift(pe1, tp1, pe3) lyrf = lyre if lyrf > 0: pcl.b3km = totp - lyrf else: pcl.b3km = totp h2 = interp.msl(3000., prof) pe2 = interp.pres(h2, prof) if QC(pe2): te2 = interp.vtmp(pe2, prof) tp2 = thermo.wetlift(pe3, tp3, pe2) tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \ thermo.ctok(te3) tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \ thermo.ctok(te2) lyrf = G * (tdef3 + tdef2) / 2. * (h2 - h3) if lyrf > 0: pcl.b3km += lyrf else: pcl.b3km = 0. # Is this the 6km level if pcl.lclhght < 6000.: h = interp.agl(interp.hght(pe2, prof), prof) if h >= 6000. and not QC(pcl.b6km): pe3 = pelast h3 = interp.hght(pe3, prof) te3 = interp.vtmp(pe3, prof) tp3 = thermo.wetlift(pe1, tp1, pe3) lyrf = lyre if lyrf > 0: pcl.b6km = totp - lyrf else: pcl.b6km = totp h2 = interp.msl(6000., prof) pe2 = interp.pres(h2, prof) if QC(pe2): te2 = interp.vtmp(pe2, prof) tp2 = thermo.wetlift(pe3, tp3, pe2) tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \ thermo.ctok(te3) tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \ thermo.ctok(te2) lyrf = G * (tdef3 + tdef2) / 2. * (h2 - h3) if lyrf > 0: pcl.b6km += lyrf else: pcl.b6km = 0. # LFC Possibility if lyre >= 0. and lyrlast <= 0.: tp3 = tp1 te3 = te1 pe2 = pe1 pe3 = pelast while interp.vtmp(pe3, prof) > thermo.virtemp( pe3, thermo.wetlift(pe2, tp3, pe3), thermo.wetlift(pe2, tp3, pe3)): pe3 -= 5 pcl.lfcpres = pe3 pcl.lfchght = interp.agl(interp.hght(pe3, prof), prof) cinh_old = totn tote = 0. pcl.elpres = RMISSD li_max = RMISSD if cap_strength < 0.: cap_strength = 0. pcl.cap = cap_strength pcl.cappres = cap_strengthpres # Hack to force LFC to be at least at the LCL if pcl.lfcpres > pcl.lclpres: pcl.lfcpres = pcl.lclpres pcl.lfchght = pcl.lclhght # EL Possibility if lyre <= 0. and lyrlast >= 0.: tp3 = tp1 te3 = te1 pe2 = pe1 pe3 = pelast while interp.vtmp(pe3, prof) < thermo.virtemp( pe3, thermo.wetlift(pe2, tp3, pe3), thermo.wetlift(pe2, tp3, pe3)): pe3 -= 5 pcl.elpres = pe3 pcl.elhght = interp.agl(interp.hght(pe3, prof), prof) pcl.mplpres = RMISSD pcl.limax = -li_max pcl.limaxpress = li_maxpres # MPL Possibility if tote < 0. and not QC(pcl.mplpres) and QC(pcl.elpres): pe3 = pelast h3 = interp.hght(pe3, prof) te3 = interp.vtmp(pe3, prof) tp3 = thermo.wetlift(pe1, tp1, pe3) totx = tote - lyre pe2 = pelast while totx > 0: pe2 -= 1 te2 = interp.vtmp(pe2, prof) tp2 = thermo.wetlift(pe3, tp3, pe2) h2 = interp.hght(pe2, prof) tdef3 = (thermo.virtemp(pe3, tp3, tp3) - te3) / \ thermo.ctok(te3) tdef2 = (thermo.virtemp(pe2, tp2, tp2) - te2) / \ thermo.ctok(te2) lyrf = G * (tdef3 + tdef2) / 2. * (h2 - h3) totx += lyrf tp3 = tp2 te3 = te2 pe3 = pe2 pcl.mplpres = pe2 pcl.mplhght = interp.agl(interp.hght(pe2, prof), prof) # 500 hPa Lifted Index if prof.gSndg[i][prof.pind] <= 500. and pcl.li5 == RMISSD: a = interp.vtmp(500., prof) b = thermo.wetlift(pe1, tp1, 500.) pcl.li5 = a - thermo.virtemp(500, b, b) # 300 hPa Lifted Index if prof.gSndg[i][prof.pind] <= 300. and pcl.li3 == RMISSD: a = interp.vtmp(300., prof) b = thermo.wetlift(pe1, tp1, 300.) pcl.li3 = a - thermo.virtemp(300, b, b) # Calculate BRN if available pcl = bulk_rich(pcl, prof) pcl.bminus = cinh_old if pcl.bplus == 0: pcl.bminus = 0. return pcl
# 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.
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)
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])
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: