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 norm_wind_shear(prof, pbot=850, ptop=250): ''' Calculates the bulk shear between the wind at (pbot) and (ptop), normalized by dividing by the thickness of the layer. Parameters ---------- prof: profile object Profile object pbot : number (optional; default 850 hPa) Pressure of the bottom level (hPa) ptop : number (optional; default 250 hPa) Pressure of the top level (hPa) Returns ------- nws : number Normalized wind shear (Thousandths of units per second) ''' shr = utils.KTS2MS(utils.mag(*wind_shear(prof, pbot=pbot, ptop=ptop))) hbot = interp.hght(prof, pbot) htop = interp.hght(prof, ptop) thickness = htop - hbot nws = shr / thickness return nws
def get_parcels(self): ''' Function to generate various parcels and parcel traces. Returns nothing, but sets the following variables: self.mupcl : Most Unstable Parcel self.sfcpcl : Surface Based Parcel self.mlpcl : Mixed Layer Parcel self.fcstpcl : Forecast Surface Parcel self.ebottom : The bottom pressure level of the effective inflow layer self.etop : the top pressure level of the effective inflow layer self.ebotm : The bottom, meters (agl), of the effective inflow layer self.etopm : The top, meters (agl), of the effective inflow layer Parameters ---------- None Returns ------- None ''' self.mupcl = params.parcelx( self, flag=3 ) if self.mupcl.lplvals.pres == self.pres[self.sfc]: self.sfcpcl = self.mupcl else: self.sfcpcl = params.parcelx( self, flag=1 ) self.fcstpcl = params.parcelx( self, flag=2 ) self.mlpcl = params.parcelx( self, flag=4 ) self.usrpcl = params.Parcel() ## get the effective inflow layer data self.ebottom, self.etop = params.effective_inflow_layer( self, mupcl=self.mupcl ) ## if there was no effective inflow layer, set the values to masked if self.etop is ma.masked or self.ebottom is ma.masked: self.ebotm = ma.masked; self.etopm = ma.masked self.effpcl = self.sfcpcl # Default to surface parcel, as in params.DefineProfile(). ## otherwise, interpolate the heights given to above ground level else: self.ebotm = interp.to_agl(self, interp.hght(self, self.ebottom)) self.etopm = interp.to_agl(self, interp.hght(self, self.etop)) # The below code was adapted from params.DefineProfile() # Lifting one additional parcel probably won't slow the program too much. # It's just one more lift compared to all the lifts in the params.effective_inflow_layer() call. mtha = params.mean_theta(self, self.ebottom, self.etop) mmr = params.mean_mixratio(self, self.ebottom, self.etop) effpres = (self.ebottom+self.etop)/2. efftmpc = thermo.theta(1000., mtha, effpres) effdwpc = thermo.temp_at_mixrat(mmr, effpres) self.effpcl = params.parcelx(self, flag=5, pres=effpres, tmpc=efftmpc, dwpc=effdwpc) #This is the effective parcel.
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 test_hght(): input_p = 900 correct_z = 1036.0022240687013 returned_z = interp.hght(prof, input_p) npt.assert_almost_equal(returned_z, correct_z) input_p = [900, 800, 600, 400] correct_z = np.asarray([1036.0022240687013, 2020.3374024, 4330.6739399, 7360.]) returned_z = interp.hght(prof, input_p) npt.assert_almost_equal(returned_z, correct_z)
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 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 norm_total_shear(prof, pbot=850, ptop=250, dp=-1, exact=True): ''' Calculates the total shear (also known as the hodograph length) between the wind at (pbot) and (ptop), normalized by dividing by the thickness of the layer. Parameters ---------- prof: profile object Profile object pbot : number (optional; default 850 hPa) Pressure of the bottom level (hPa) ptop : number (optional; default 250 hPa) Pressure of the top level (hPa) 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 ------- norm_tot_pos_shr : number Normalized total positive shear norm_tot_neg_shr : number Normalized total negative shear norm_tot_net_shr : number Normalized total net shear (subtracting negative shear from positive shear) norm_tot_abs_shr : number Normalized total absolute shear (adding positive and negative shear together) ''' shr = utils.KTS2MS(np.asarray(total_shear(prof, pbot=pbot, ptop=ptop, dp=dp, exact=exact))) hbot = interp.hght(prof, pbot) htop = interp.hght(prof, ptop) thickness = htop - hbot norm_tot_pos_shr, norm_tot_neg_shr, norm_tot_net_shr, norm_tot_abs_shr = shr / thickness return norm_tot_pos_shr, norm_tot_neg_shr, norm_tot_net_shr, norm_tot_abs_shr
def interp(self, dp=-25): """ Interpolate the profile object to a specific pressure level spacing. """ if self.isEnsemble(): raise ValueError("Cannot interpolate the ensemble profiles.") prof = self._profs[self._highlight][self._prof_idx] # Save original, if one hasn't already been saved if self._prof_idx not in self._orig_profs: self._orig_profs[self._prof_idx] = prof cls = type(prof) # Copy the tmpc, dwpc, etc. profiles to be inteprolated keys = ['tmpc', 'dwpc', 'hght', 'wspd', 'wdir', 'omeg'] prof_vars = { 'pres': np.arange(prof.pres[prof.sfc], prof.pres[prof.top], dp) } prof_vars['tmpc'] = interp.temp(prof, prof_vars['pres']) prof_vars['dwpc'] = interp.dwpt(prof, prof_vars['pres']) prof_vars['hght'] = interp.hght(prof, prof_vars['pres']) if prof.omeg.all() is not np.ma.masked: prof_vars['omeg'] = interp.omeg(prof, prof_vars['pres']) else: prof_vars['omeg'] = np.ma.masked_array(prof_vars['pres'], mask=np.ones(len( prof_vars['pres']), dtype=int)) u, v = interp.components(prof, prof_vars['pres']) prof_vars['u'] = u prof_vars['v'] = v interp_prof = cls.copy(prof, **prof_vars) self._profs[self._highlight][self._prof_idx] = interp_prof # Save the original like in modify() if self._prof_idx not in self._interp_profs: self._interp_profs[self._prof_idx] = interp_prof # Update bookkeeping self._interp[self._prof_idx] = True
def interp(self, dp=-25): """ Interpolate the profile object to a specific pressure level spacing. """ if self.isEnsemble(): raise ValueError("Cannot interpolate the ensemble profiles.") prof = self._profs[self._highlight][self._prof_idx] # Save original, if one hasn't already been saved if self._prof_idx not in self._orig_profs: self._orig_profs[self._prof_idx] = prof cls = type(prof) # Copy the tmpc, dwpc, etc. profiles to be inteprolated keys = ['tmpc', 'dwpc', 'hght', 'wspd', 'wdir', 'omeg'] prof_vars = {'pres': np.arange(prof.pres[prof.sfc], prof.pres[prof.top], dp)} prof_vars['tmpc'] = interp.temp(prof, prof_vars['pres']) prof_vars['dwpc'] = interp.dwpt(prof, prof_vars['pres']) prof_vars['hght'] = interp.hght(prof, prof_vars['pres']) if prof.omeg.all() is not np.ma.masked: prof_vars['omeg'] = interp.omeg(prof, prof_vars['pres']) else: prof_vars['omeg'] = np.ma.masked_array(prof_vars['pres'], mask=np.ones(len(prof_vars['pres']), dtype=int)) u, v = interp.components(prof, prof_vars['pres']) prof_vars['u'] = u prof_vars['v'] = v interp_prof = cls.copy(prof, **prof_vars) self._profs[self._highlight][self._prof_idx] = interp_prof # Save the original like in modify() if self._prof_idx not in self._interp_profs: self._interp_profs[self._prof_idx] = interp_prof # Update bookkeeping self._interp[self._prof_idx] = True
def draw_ensemble_point(self, qp, prof): # Plot the profile index on the scatter plot if 'pbl_h' not in dir( prof ): # Make sure a PBL top has been found in the profile object ppbl_top = params.pbl_top(prof) setattr(prof, 'pbl_h', interp.to_agl(prof, interp.hght(prof, ppbl_top))) if 'sfcpcl' not in dir( prof ): # Make sure a surface parcel has been lifted in the profile object setattr(prof, 'sfcpcl', params.parcelx(prof, flag=1)) #x = self.x_to_xpix() #y = self.y_to_ypix() color = QtCore.Qt.red qp.setPen(QtGui.QPen(color)) qp.setBrush(QtGui.QBrush(color)) x = self.x_to_xpix(prof.pbl_h) - 50 / 2. y = self.y_to_ypix(prof.sfcpcl.bplus) - (self.fsize - 1) / 2 qp.drawEllipse(x, y, 3, 3) return
''' Create the Sounding (Profile) Object '''
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) ebot_hght = interp.to_agl(prof, interp.hght(prof, eff_inflow[0])) etop_hght = interp.to_agl(prof, interp.hght(prof, eff_inflow[1])) effective_srh = winds.helicity(prof, ebot_hght, etop_hght, stu=srwind[0], stv=srwind[1]) ebwd = winds.wind_shear(prof, pbot=eff_inflow[0], ptop=eff_inflow[1]) ebwspd = utils.mag(ebwd[0], ebwd[1]) scp = params.scp(mupcl.bplus, effective_srh[0], ebwspd) stp_cin = params.stp_cin(mlpcl.bplus, effective_srh[0], ebwspd, mlpcl.lclhght, mlpcl.bminus) indices = {'SBCAPE': [int(sfcpcl.bplus), 'J/kg'],\ 'SBCIN': [int(sfcpcl.bminus), 'J/kg'],\ 'SBLCL': [int(sfcpcl.lclhght), 'm AGL'],\
# annotate dewpoint in F at bottom of dewpoint profile dewpointF = skew.ax.text(prof.dwpc[0], prof.pres[0]+10, utils.INT2STR(thermo.ctof(prof.dwpc[0])), verticalalignment='top', horizontalalignment='center', size=7, color=dwpt_trace.get_color()) skew.plot(pcl.ptrace, pcl.ttrace, 'brown', linestyle="dashed" ) # parcel temperature trace skew.ax.set_ylim(1050,100) skew.ax.set_xlim(-50,45) # Plot the effective inflow layer using purple horizontal lines eff_inflow = params.effective_inflow_layer(prof) inflow_bot = skew.ax.axhline(eff_inflow[0], color='purple',xmin=0.38, xmax=0.45) inflow_top = skew.ax.axhline(eff_inflow[1], color='purple',xmin=0.38, xmax=0.45) srwind = params.bunkers_storm_motion(prof) # annotate effective inflow layer SRH if eff_inflow[0]: ebot_hght = interp.to_agl(prof, interp.hght(prof, eff_inflow[0])) etop_hght = interp.to_agl(prof, interp.hght(prof, eff_inflow[1])) effective_srh = winds.helicity(prof, ebot_hght, etop_hght, stu = srwind[0], stv = srwind[1]) # Set position of label # x position is mean of horizontal line bounds # For some reason this makes a big white space on the left side and for all subsequent plots. inflow_SRH = skew.ax.text( np.mean(inflow_top.get_xdata()), eff_inflow[1], '%.0f' % effective_srh[0] + ' ' + '$\mathregular{m^{2}s^{-2}}$', verticalalignment='bottom', horizontalalignment='center', size=6, transform=inflow_bot.get_transform(), color=inflow_top.get_color() ) # draw indices text string to the right of plot. indices_text = plt.text(1.08, 1.0, myskewt.indices(prof), verticalalignment='top', size=5.6, transform=plt.gca().transAxes) # globe with dot
def best_guess_precip(prof, init_phase, init_lvl, init_temp, tpos, tneg): ''' Best Guess Precipitation type Adapted from SHARP code donated by Rich Thompson (SPC) Description: This algorithm utilizes the output from the init_phase() and posneg_temperature() functions to make a best guess at the preciptation type one would observe at the surface given a thermodynamic profile. Precipitation Types Supported: - None - Rain - Snow - Sleet and Snow - Sleet - Freezing Rain/Drizzle - Unknown Parameters ---------- prof : Profile object init_phase : the initial phase of the precipitation (int) (see 2nd value returned from init_phase()) init_lvl : the inital level of the precipitation source (mb) (see 1st value returned from init_phase()) init_temp : the inital level of the precipitation source (C) (see 3rd value returned from init_phase()) tpos : the positive area (> 0 C) in the temperature profile (J/kg) Returns ------- precip_type : a string containing the best guess precipitation type ''' # Needs to be tested precip_type = None # Case: No precip if init_phase < 0: precip_type = "None." # Case: Always too warm - Rain elif init_phase == 0 and tneg >= 0 and prof.tmpc[prof.get_sfc()] > 0: precip_type = "Rain." # Case: always too cold elif init_phase == 3 and tpos <= 0 and prof.tmpc[prof.get_sfc()] <= 0: precip_type = "Snow." # Case: ZR too warm at sfc - Rain elif init_phase == 1 and tpos <= 0 and prof.tmpc[prof.get_sfc()] > 0: precip_type = "Rain." # Case: non-snow init...always too cold - Initphase & sleet elif init_phase == 1 and tpos <= 0 and prof.tmpc[prof.get_sfc()] <= 0: #print interp.to_agl(prof, interp.hght(prof, init_lvl)) if interp.to_agl(prof, interp.hght(prof, init_lvl)) >= 3000: if init_temp <= -4: precip_type = "Sleet and Snow." else: precip_type = "Sleet." else: precip_type = "Freezing Rain/Drizzle." # Case: Snow...but warm at sfc elif init_phase == 3 and tpos <= 0 and prof.tmpc[prof.get_sfc()] > 0: if prof.tmpc[prof.get_sfc()] > 4: precip_type = "Rain." else: precip_type = "Snow." # Case: Warm layer. elif tpos > 0: x1 = tpos y1 = -tneg y2 = (0.62 * x1) + 60.0 if y1 > y2: precip_type = "Sleet." else: if prof.tmpc[prof.get_sfc()] <= 0: precip_type = "Freezing Rain." else: precip_type = "Rain." else: precip_type = "Unknown." return precip_type
def best_guess_precip(prof, init_phase, init_lvl, init_temp, tpos, tneg): ''' Best Guess Precipitation type Adapted from SHARP code donated by Rich Thompson (SPC) Description: This algorithm utilizes the output from the init_phase() and posneg_temperature() functions to make a best guess at the preciptation type one would observe at the surface given a thermodynamic profile. Precipitation Types Supported: - None - Rain - Snow - Sleet and Snow - Sleet - Freezing Rain/Drizzle - Unknown Parameters ---------- prof : Profile object init_phase : the initial phase of the precipitation (int) (see 2nd value returned from init_phase()) init_lvl : the inital level of the precipitation source (mb) (see 1st value returned from init_phase()) init_temp : the inital level of the precipitation source (C) (see 3rd value returned from init_phase()) tpos : the positive area (> 0 C) in the temperature profile (J/kg) Returns ------- precip_type : a string containing the best guess precipitation type ''' # Needs to be tested precip_type = None # Case: No precip if init_phase < 0: precip_type = "None." # Case: Always too warm - Rain elif init_phase == 0 and tneg >=0 and prof.tmpc[prof.get_sfc()] > 0: precip_type = "Rain." # Case: always too cold elif init_phase == 3 and tpos <= 0 and prof.tmpc[prof.get_sfc()] <= 0: precip_type = "Snow." # Case: ZR too warm at sfc - Rain elif init_phase == 1 and tpos <= 0 and prof.tmpc[prof.get_sfc()] > 0: precip_type = "Rain." # Case: non-snow init...always too cold - Initphase & sleet elif init_phase == 1 and tpos <= 0 and prof.tmpc[prof.get_sfc()] <= 0: #print interp.to_agl(prof, interp.hght(prof, init_lvl)) if interp.to_agl(prof, interp.hght(prof, init_lvl)) >= 3000: if init_temp <= -4: precip_type = "Sleet and Snow." else: precip_type = "Sleet." else: precip_type = "Freezing Rain/Drizzle." # Case: Snow...but warm at sfc elif init_phase == 3 and tpos <= 0 and prof.tmpc[prof.get_sfc()] > 0: if prof.tmpc[prof.get_sfc()] > 4: precip_type = "Rain." else: precip_type = "Snow." # Case: Warm layer. elif tpos > 0: x1 = tpos y1 = -tneg y2 = (0.62 * x1) + 60.0 if y1 > y2: precip_type = "Sleet." else: if prof.tmpc[prof.get_sfc()] <= 0: precip_type = "Freezing Rain." else: precip_type = "Rain." else: precip_type = "Unknown." return precip_type
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 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
def posneg_wetbulb(prof, start=-1): ''' Positive/Negative Wetbulb profile Adapted from SHARP code donated by Rich Thompson (SPC) Description: This routine calculates the positive (above 0 C) and negative (below 0 C) areas of the wet bulb profile starting from a specified pressure (start). If the specified pressure is not given, this routine calls init_phase() to obtain the pressure level the precipitation expected to fall begins at. This is an routine considers the wet-bulb profile instead of the temperature profile in case the profile beneath the profile beneath the falling precipitation becomes saturated. Parameters ---------- prof : Profile object start : the pressure level the precpitation originates from (found by calling init_phase()) Returns ------- pos : the positive area (> 0 C) of the wet-bulb profile in J/kg neg : the negative area (< 0 C) of the wet-bulb profile in J/kg top : the top of the precipitation layer pressure in mb bot : the bottom of the precipitation layer pressure in mb ''' # Needs to be tested # If there is no sounding, don't compute anything if utils.QC(interp.temp(prof, 500)) == False and utils.QC( interp.temp(prof, 850)) == False: return np.masked, np.masked, np.masked, np.masked # Find lowest obs in layer lower = prof.pres[prof.get_sfc()] lptr = prof.get_sfc() # Find the highest obs in the layer if start == -1: lvl, phase, st = init_phase(prof) if lvl > 0: upper = lvl else: upper = 500. else: upper = start # Find the level where the pressure is just greater than the upper pressure idxs = np.where(prof.pres > upper)[0] if len(idxs) == 0: uptr = 0 else: uptr = idxs[-1] # Start with the upper layer pe1 = upper h1 = interp.hght(prof, pe1) te1 = thermo.wetbulb(pe1, interp.temp(prof, pe1), interp.dwpt(prof, pe1)) tp1 = 0 warmlayer = coldlayer = lyre = totp = totn = tote = ptop = pbot = lyrlast = 0 for i in np.arange(uptr, lptr - 1, -1): pe2 = prof.pres[i] h2 = prof.hght[i] te2 = thermo.wetbulb(pe2, interp.temp(prof, pe2), interp.dwpt(prof, pe2)) tp2 = 0 tdef1 = (0 - te1) / thermo.ctok(te1) tdef2 = (0 - te2) / thermo.ctok(te2) lyrlast = lyre lyre = 9.8 * (tdef1 + tdef2) / 2.0 * (h2 - h1) # Has a warm layer been found yet? if te2 > 0: if warmlayer == 0: warmlayer = 1 ptop = pe2 # Has a cold layer been found yet? if te2 < 0: if warmlayer == 1 and coldlayer == 0: coldlayer = 1 pbot = pe2 if warmlayer > 0: if lyre > 0: totp += lyre else: totn += lyre tote += lyre pelast = pe1 pe1 = pe2 h1 = h2 te1 = te2 tp1 = tp2 if warmlayer == 1 and coldlayer == 1: pos = totp neg = totn top = ptop bot = pbot else: neg = 0 pos = 0 bot = 0 top = 0 return pos, neg, top, bot
def append_wbz(): #Load each ERA-Interim netcdf file, and append wbz start_lat = -44.525 end_lat = -9.975 start_lon = 111.975 end_lon = 156.275 domain = [start_lat, end_lat, start_lon, end_lon] model = "erai" region = "aus" dates = [] for y in np.arange(1979, 2019): for m in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]: if (m != 12): dates.append([dt.datetime(y,m,1,0,0,0),\ dt.datetime(y,m+1,1,0,0,0)-dt.timedelta(hours = 6)]) else: dates.append([dt.datetime(y,m,1,0,0,0),\ dt.datetime(y+1,1,1,0,0,0)-dt.timedelta(hours = 6)]) for t in np.arange(0, len(dates)): print(str(dates[t][0]) + " - " + str(dates[t][1])) fname = "/g/data/eg3/ab4502/ExtremeWind/"+region+"/"+model+"/"+model+"_"+\ dt.datetime.strftime(dates[t][0],"%Y%m%d")+"_"+\ dt.datetime.strftime(dates[t][-1],"%Y%m%d")+".nc" ta,dp,hur,hgt,terrain,p,ps,wap,ua,va,uas,vas,tas,ta2d,cp,wg10,cape,lon,lat,date_list = \ read_erai(domain,dates[t]) dp = get_dp(ta, hur, dp_mask=False) agl_idx = (p <= ps) #Replace masked dp values dp = replace_dp(dp) try: prof = profile.create_profile(pres = np.insert(p[agl_idx],0,ps), \ hght = np.insert(hgt[agl_idx],0,terrain), \ tmpc = np.insert(ta[agl_idx],0,tas), \ dwpc = np.insert(dp[agl_idx],0,ta2d), \ u = np.insert(ua[agl_idx],0,uas), \ v = np.insert(va[agl_idx],0,vas), \ strictqc=False, omeg=np.insert(wap[agl_idx],0,wap[agl_idx][0]) ) except: p = p[agl_idx] ua = ua[agl_idx] va = va[agl_idx] hgt = hgt[agl_idx] ta = ta[agl_idx] \ dp = dp[agl_idx] p[0] = ps ua[0] = uas va[0] = vas hgt[0] = terrain ta[0] = tas dp[0] = ta2d prof = profile.create_profile(pres = p, \ hght = hgt, \ tmpc = ta, \ dwpc = dp, \ u = ua, \ v = va, \ strictqc=False, omeg=wap[agl_idx]) pwb0 = params.temp_lvl(prof, 0, wetbulb=True) hwb0 = interp.to_agl(prof, interp.hght(prof, pwb0)) param_file = nc.Dataset(fname, "a") wbz_var = param_file.createVariable("wbz",float,\ ("time","lat","lon")) wbz_var.units = "m" wbz_var.long_name = "wet_bulb_zero_height" wbz_var[:] = hwb0 T1 = abs( thermo.wetlift(prof.pres[0], prof.tmpc[0], 600) - interp.temp(prof, 600)) T2 = abs( thermo.wetlift(pwb0, interp.temp(prof, pwb0), sfc) - prof.tmpc[0]) Vprime = utils.KTS2MS(13 * np.sqrt((T1 + T2) / 2) + (1 / 3 * (Umean01))) Vprime_var = param_file.createVariable("Vprime",float,\ ("time","lat","lon")) Vprime_var.units = "m/s" Vprime_var.long_name = "miller_1972_wind_speed" Vprime_var[:] = Vprime param_file.close()
def posneg_wetbulb(prof, start=-1): ''' Positive/Negative Wetbulb profile Adapted from SHARP code donated by Rich Thompson (SPC) Description: This routine calculates the positive (above 0 C) and negative (below 0 C) areas of the wet bulb profile starting from a specified pressure (start). If the specified pressure is not given, this routine calls init_phase() to obtain the pressure level the precipitation expected to fall begins at. This is an routine considers the wet-bulb profile instead of the temperature profile in case the profile beneath the profile beneath the falling precipitation becomes saturated. Parameters ---------- prof : Profile object start : the pressure level the precpitation originates from (found by calling init_phase()) Returns ------- pos : the positive area (> 0 C) of the wet-bulb profile in J/kg neg : the negative area (< 0 C) of the wet-bulb profile in J/kg top : the top of the precipitation layer pressure in mb bot : the bottom of the precipitation layer pressure in mb ''' # Needs to be tested # If there is no sounding, don't compute anything if utils.QC(interp.temp(prof, 500)) == False and utils.QC(interp.temp(prof, 850)) == False: return np.ma.masked, np.ma.masked, np.ma.masked, np.ma.masked # Find lowest obs in layer lower = prof.pres[prof.get_sfc()] lptr = prof.get_sfc() # Find the highest obs in the layer if start == -1: lvl, phase, st = init_phase(prof) if lvl > 0: upper = lvl else: upper = 500. else: upper = start # Find the level where the pressure is just greater than the upper pressure idxs = np.where(prof.pres > upper)[0] if len(idxs) == 0: uptr = 0 else: uptr = idxs[-1] # Start with the upper layer pe1 = upper; h1 = interp.hght(prof, pe1); te1 = thermo.wetbulb(pe1, interp.temp(prof, pe1), interp.dwpt(prof, pe1)) tp1 = 0 warmlayer = coldlayer = lyre = totp = totn = tote = ptop = pbot = lyrlast = 0 for i in np.arange(uptr, lptr-1, -1): pe2 = prof.pres[i] h2 = prof.hght[i] te2 = thermo.wetbulb(pe2, interp.temp(prof, pe2), interp.dwpt(prof, pe2)) tp2 = 0 tdef1 = (0 - te1) / thermo.ctok(te1); tdef2 = (0 - te2) / thermo.ctok(te2); lyrlast = lyre; lyre = 9.8 * (tdef1 + tdef2) / 2.0 * (h2 - h1); # Has a warm layer been found yet? if te2 > 0: if warmlayer == 0: warmlayer = 1 ptop = pe2 # Has a cold layer been found yet? if te2 < 0: if warmlayer == 1 and coldlayer == 0: coldlayer = 1 pbot = pe2 if warmlayer > 0: if lyre > 0: totp += lyre else: totn += lyre tote += lyre pelast = pe1 pe1 = pe2 h1 = h2 te1 = te2 tp1 = tp2 if warmlayer == 1 and coldlayer == 1: pos = totp neg = totn top = ptop bot = pbot else: neg = 0 pos = 0 bot = 0 top = 0 return pos, neg, top, bot
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