def haines_high(prof): ''' Haines Index High Elevation calculation Calculates the Haines Index(Lower Atmosphere Severity Index) using the higher elevation parmeters, used above 3000ft or 914 m. Pressure levels 700 mb and 500 mb Dewpoint depression at 700 mb Lapse Rate Term --------------- 1 : < 18C 2 : 18C to 21C 3 : > 21C Dewpoint Depression Term ------------------------ 1 : < 15C 2 : 15C to 20C 3 : > 20C Adapted from S-591 course Added by Nickolai Reimer (NWS Billings, MT) Parameters ---------- prof : profile object Profile object Returns ------- param : number the Haines Index high ''' tp1 = interp.temp(prof, 700) tp2 = interp.temp(prof, 500) tdp1 = interp.dwpt(prof, 700) if utils.QC(tp1) and utils.QC(tp2) and utils.QC(tdp1): lapse_rate = tp1 - tp2 dewpoint_depression = tp1 - tdp1 if lapse_rate < 18: a = 1 elif 18 <= lapse_rate and lapse_rate <= 21: a = 2 else: a = 3 if dewpoint_depression < 15: b = 1 elif 15 <= dewpoint_depression and dewpoint_depression <= 20: b = 2 else: b = 3 return a + b else: return constants.MISSING
def mean_theta(prof, lower=-1, upper=-1): ''' Calculates the mean theta from a profile object within the specified layer. Inputs ------ prof (profile object) Profile Object lower (float) Bottom level (hPa) [-1=SFC] upper (float) Top level (hPa) [-1=SFC-100hPa] Returns ------- Mean Theta (float) ''' if lower == -1: lower = prof.gSndg[prof.sfc][prof.pind] if upper == -1: upper = prof.gSndg[prof.sfc][prof.pind] - 100. if not QC(interp.temp(upper, prof)): mmw = RMISSD if not QC(interp.temp(lower, prof)): prof.gSndg[prof.sfc][prof.pind] # Find lowest observations in the layer i = 0 while prof.gSndg[i][prof.pind] > lower: i += 1 while not QC(prof.gSndg[i][prof.tind]): i += 1 lptr = i if prof.gSndg[i][prof.pind] == lower: lptr += 1 # Find highest observations in the layer i = prof.gNumLevels - 1 while prof.gSndg[i][prof.pind] < upper: i -= 1 uptr = i if prof.gSndg[i][prof.pind] == upper: uptr -= 1 tott = 0 # Start with interpolated bottom layer t1 = thermo.theta(lower, interp.temp(lower, prof), 1000.) num = 1 # Calculate every level that reports a dew point for i in range(lptr, uptr + 1): if QC(prof.gSndg[i][prof.tind]): t2 = thermo.theta(prof.gSndg[i][prof.pind], prof.gSndg[i][prof.tind], 1000.) tbar = (t1 + t2) / 2. tott += tbar t1 = t2 num += 1 # Finish with top layer t2 = thermo.theta(upper, interp.temp(upper, prof), 1000.) tbar = (t1 + t2) / 2. tott += tbar return tott / num
def test_temp(): input_p = 900 correct_t = 14.589978853117268 returned_t = interp.temp(prof, input_p) npt.assert_almost_equal(returned_t, correct_t) input_p = [900, 800, 600, 400] correct_t = np.asarray([14.589978853117268, 8.3624187, -7.48619696, -29.7]) returned_t = interp.temp(prof, input_p) npt.assert_almost_equal(returned_t, correct_t)
def mean_theta(prof, lower=-1, upper=-1): ''' Calculates the mean theta from a profile object within the specified layer. Inputs ------ prof (profile object) Profile Object lower (float) Bottom level (hPa) [-1=SFC] upper (float) Top level (hPa) [-1=SFC-100hPa] Returns ------- Mean Theta (float) ''' if lower == -1: lower = prof.gSndg[prof.sfc][prof.pind] if upper == -1: upper = prof.gSndg[prof.sfc][prof.pind] - 100. if not QC(interp.temp(upper, prof)): mmw = RMISSD if not QC(interp.temp(lower, prof)): prof.gSndg[prof.sfc][prof.pind] # Find lowest observations in the layer i = 0 while prof.gSndg[i][prof.pind] > lower: i+=1 while not QC(prof.gSndg[i][prof.tind]): i+=1 lptr = i if prof.gSndg[i][prof.pind] == lower: lptr+=1 # Find highest observations in the layer i = prof.gNumLevels - 1 while prof.gSndg[i][prof.pind] < upper: i-=1 uptr = i if prof.gSndg[i][prof.pind] == upper: uptr-=1 tott = 0 # Start with interpolated bottom layer t1 = thermo.theta(lower, interp.temp(lower, prof), 1000.) num = 1 # Calculate every level that reports a dew point for i in range(lptr, uptr+1): if QC(prof.gSndg[i][prof.tind]): t2 = thermo.theta(prof.gSndg[i][prof.pind], prof.gSndg[i][prof.tind], 1000.) tbar = (t1 + t2) / 2. tott += tbar t1 = t2 num += 1 # Finish with top layer t2 = thermo.theta(upper, interp.temp(upper, prof), 1000.) tbar = (t1 + t2) / 2. tott += tbar return tott / num
def v_totals(prof): ''' Calculates the Vertical Totals Index from data in profile object. Inputs ------ prof (profile object) Profile Object Returns ------- v_totals (float) Vertical Totals ''' t8 = interp.temp(850., prof) t5 = interp.temp(500., prof) return t8 - t5
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 get_sars(self): ''' Function to get the SARS analogues from the hail and supercell databases. Requires calling get_kinematics() and get_parcels() first. Also calculates the significant hail parameter. Function returns nothing, but sets the following variables: self.matches - the matches from SARS HAIL self.ship - significant hail parameter self.supercell_matches - the matches from SARS SUPERCELL Parameters ---------- None Returns ------- None ''' sfc_6km_shear = utils.KTS2MS( utils.mag(self.sfc_6km_shear[0], self.sfc_6km_shear[1])) sfc_3km_shear = utils.KTS2MS( utils.mag(self.sfc_3km_shear[0], self.sfc_3km_shear[1])) sfc_9km_shear = utils.KTS2MS( utils.mag(self.sfc_9km_shear[0], self.sfc_9km_shear[1])) h500t = interp.temp(self, 500.) lapse_rate = params.lapse_rate(self, 700., 500., pres=True) srh3km = self.srh3km[0] srh1km = self.srh1km[0] mucape = self.mupcl.bplus mlcape = self.mlpcl.bplus mllcl = self.mlpcl.lclhght mumr = thermo.mixratio(self.mupcl.pres, self.mupcl.dwpc) self.ship = params.ship(self) ## Cambios para el hemisferio sur JP JP if self.latitude < 0: srh1km = -srh1km srh3km = -srh3km self.hail_database = 'sars_hail.txt' self.supercell_database = 'sars_supercell.txt' try: self.matches = hail(self.hail_database, mumr, mucape, h500t, lapse_rate, sfc_6km_shear, sfc_9km_shear, sfc_3km_shear, srh3km) except: self.matches = ([], [], 0, 0, 0) try: self.supercell_matches = supercell(self.supercell_database, mlcape, mllcl, h500t, lapse_rate, utils.MS2KTS(sfc_6km_shear), srh1km, utils.MS2KTS(sfc_3km_shear), utils.MS2KTS(sfc_9km_shear), srh3km) except Exception as e: self.supercell_matches = ([], [], 0, 0, 0)
def haines_ma(prof): ''' Haines Index, Middle Altitude The Haines Index (or Lower Atmospheric Severity Index) was developed in order to help forecast fire "blow up" potential. It is a function of lower atmospheric stability and moisture content. Three versions were developed, to be used depending on the altitude of the surface. Values of 2 or 3 indicate very low "blow up" potential; a value of 4 indicates low potential; a value of 5 indicates moderate potential; and a value of 6 indicates high potential. The middle altitude version was developed primarily for use when the surface level pressure is between 950 and 850 mb. Parameters ---------- prof - Profile object Returns ------- haines_ma : number Haines Index, Middle Altitude (number) ''' tmp850 = interp.temp(prof, 850) tmp700 = interp.temp(prof, 700) dpt850 = interp.dwpt(prof, 850) lr87 = tmp850 - tmp700 tdd850 = tmp850 - dpt850 if lr87 <= 5: stab_tm = 1 elif 5 < lr87 and lr87 < 11: stab_tm = 2 else: stab_tm = 3 if tdd850 <= 5: mois_tm = 1 elif 5 < tdd850 and tdd850 < 13: mois_tm = 2 else: mois_tm = 3 haines_ma = stab_tm + mois_tm return haines_ma
def haines_ha(prof): ''' Haines Index, High Altitude The Haines Index (or Lower Atmospheric Severity Index) was developed in order to help forecast fire "blow up" potential. It is a function of lower atmospheric stability and moisture content. Three versions were developed, to be used depending on the altitude of the surface. Values of 2 or 3 indicate very low "blow up" potential; a value of 4 indicates low potential; a value of 5 indicates moderate potential; and a value of 6 indicates high potential. The low altitude version was developed primarily for use when the surface level pressure is between 850 and 700 mb. Parameters ---------- prof - Profile object Returns ------- haines_ha : number Haines Index, High Altitude (number) ''' tmp700 = interp.temp(prof, 700) tmp500 = interp.temp(prof, 500) dpt700 = interp.dwpt(prof, 700) lr75 = tmp700 - tmp500 tdd700 = tmp700 - dpt700 if lr75 <= 17: stab_tm = 1 elif 17 < lr75 and lr75 < 22: stab_tm = 2 else: stab_tm = 3 if tdd700 <= 14: mois_tm = 1 elif 14 < tdd700 and tdd700 < 21: mois_tm = 2 else: mois_tm = 3 haines_ha = stab_tm + mois_tm return haines_ha
def __mu(self, prof, **kwargs): ''' Create the most unstable parcel within defined level ''' self.desc = 'Most Unstable Parcel in Lowest %.2f hPa' % self.presval diff = prof.gSndg[prof.sfc][prof.pind] - self.presval self.pres = unstable_level(prof, -1, diff) self.temp = interp.temp(self.pres, prof) self.dwpt = interp.dwpt(self.pres, prof) return
def __user(self, prof, **kwargs): ''' Create a user-defined parcel ''' self.desc = '%.2f hPa Parcel' % self.presval self.pres = self.presval if 'temp' in kwargs: self.temp = kwargs.get('temp') else: self.temp = interp.temp(self.pres, prof) if 'dwpt' in kwargs: self.dwpt = kwargs.get('dwpt') else: self.dwpt = interp.dwpt(self.pres, prof) return
def k_index(prof): ''' Calculates the K-Index from a profile object Inputs ------ prof (profile object) Profile Object Returns ------- kind (float) K-Index ''' t8 = interp.temp(850., prof) t7 = interp.temp(700., prof) t5 = interp.temp(500., prof) td7 = interp.dwpt(700., prof) td8 = interp.dwpt(850., prof) if not QC(t8) or not QC(t7) or not QC(t5) or not QC(td8) or not QC(td7): return RMISSD else: return t8 - t5 + td8 - (t7 - td7)
def c_totals(prof): ''' Calculates the Cross Totals Index from data in profile object. Inputs ------ prof (profile object) Profile Object Returns ------- c_totals (float) Cross Totals ''' t5 = interp.temp(500., prof) td8 = interp.dwpt(850., prof) return td8 - t5
def get_sars(self): ''' Function to get the SARS analogues from the hail and supercell databases. Requires calling get_kinematics() and get_parcels() first. Also calculates the significant hail parameter. Function returns nothing, but sets the following variables: self.matches - the matches from SARS HAIL self.ship - significant hail parameter self.supercell_matches - the matches from SARS SUPERCELL Parameters ---------- None Returns ------- None ''' sfc_6km_shear = utils.KTS2MS( utils.mag( self.sfc_6km_shear[0], self.sfc_6km_shear[1]) ) sfc_3km_shear = utils.KTS2MS( utils.mag( self.sfc_3km_shear[0], self.sfc_3km_shear[1]) ) sfc_9km_shear = utils.KTS2MS( utils.mag( self.sfc_9km_shear[0], self.sfc_9km_shear[1]) ) h500t = interp.temp(self, 500.) lapse_rate = params.lapse_rate( self, 700., 500., pres=True ) srh3km = self.srh3km[0] srh1km = self.srh1km[0] mucape = self.mupcl.bplus mlcape = self.mlpcl.bplus mllcl = self.mlpcl.lclhght mumr = thermo.mixratio(self.mupcl.pres, self.mupcl.dwpc) self.ship = params.ship(self) self.hail_database = 'sars_hail.txt' self.supercell_database = 'sars_supercell.txt' try: self.matches = hail(self.hail_database, mumr, mucape, h500t, lapse_rate, sfc_6km_shear, sfc_9km_shear, sfc_3km_shear, srh3km) except: self.matches = ([], [], 0, 0, 0) try: self.supercell_matches = supercell(self.supercell_database, mlcape, mllcl, h500t, lapse_rate, utils.MS2KTS(sfc_6km_shear), srh1km, utils.MS2KTS(sfc_3km_shear), utils.MS2KTS(sfc_9km_shear), srh3km) except Exception as e: self.supercell_matches = ([], [], 0, 0, 0)
def max_temp(prof, mixlyr=-1): ''' Calculates a maximum temperature forecast based on the depth of the mixing layer and low-level temperatures Inputs ------ prof (profile object) Profile Object mixlyr (float) Top of layer over which to "mix" (hPa) Returns ------- mtemp (float) Forecast Maximum Temperature ''' sfcpres = prof.gSndg[prof.sfc][prof.pind] if mixlyr == -1: mixlyr = sfcpres - 100. temp = thermo.ctok(interp.temp(mixlyr, prof)) + 2. return thermo.ktoc(temp * (sfcpres / mixlyr)**ROCP)
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 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 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 unstable_level(prof, lower, upper): ''' Finds the most unstable level between the lower and upper levels. Inputs ------ prof (profile object) Profile Object lower (float) Bottom level (hPa) [-1=SFC] upper (float) Top level (hPa) [-1=SFC-100hPa] Returns ------- Pressure Level of most unstable level (float [hPa]) ''' if lower == -1: lower = prof.gSndg[prof.sfc][prof.pind] if upper == -1: upper = prof.gSndg[prof.sfc][prof.pind] - 400. # Make sure this is a valid layer while not QC(interp.dwpt(upper, prof)): upper += 50. if not QC(interp.temp(lower, prof)): lower = prof.gSndg[prof.sfc][0] # Find lowest observations in the layer i = 0 while prof.gSndg[i][prof.pind] > lower: i += 1 while not QC(prof.gSndg[i][prof.tind]): i += 1 lptr = i if prof.gSndg[i][prof.pind] == lower: lptr += 1 # Find highest observations in the layer i = prof.gNumLevels - 1 while prof.gSndg[i][prof.pind] < upper: i -= 1 uptr = i if prof.gSndg[i][prof.pind] == upper: uptr -= 1 # Start with interpolated bottom layer p1 = lower t1 = interp.temp(p1, prof) td1 = interp.dwpt(p1, prof) p2, t2 = thermo.drylift(p1, t1, td1) tmax = thermo.wetlift(p2, t2, 1000.) pmax = p1 # Calculate every level that reports a dew point for i in range(lptr, uptr + 1): if QC(prof.gSndg[i][prof.tdind]): p1 = prof.gSndg[i][prof.pind] t1 = prof.gSndg[i][prof.tind] td1 = prof.gSndg[i][prof.tdind] p2, t2 = thermo.drylift(p1, t1, td1) t1 = thermo.wetlift(p2, t2, 1000.) if t1 > tmax: tmax = t1 pmax = p1 # Finish with interpolated top layer p1 = upper t1 = interp.temp(p1, prof) td1 = interp.dwpt(p1, prof) p2, t2 = thermo.drylift(p1, t1, td1) t1 = thermo.wetlift(p2, t2, 1000.) if t1 > tmax: pmax = prof.gSndg[i][prof.pind] return pmax
def mean_mixratio(prof, lower=-1, upper=-1): ''' Calculates the mean mixing ratio from a profile object within the specified layer. Inputs ------ prof (profile object) Profile Object lower (float) Bottom level (hPa) [-1=SFC] upper (float) Top level (hPa) [-1=SFC-100hPa] Returns ------- Mean Mixing Ratio (float) ''' if lower == -1: lower = prof.gSndg[prof.sfc][prof.pind] if upper == -1: upper = prof.gSndg[prof.sfc][prof.pind] - 100. if not QC(interp.temp(upper, prof)): mmw = RMISSD if not QC(interp.temp(lower, prof)): prof.gSndg[prof.sfc][prof.pind] # Find lowest observations in the layer i = 0 while prof.gSndg[i][prof.pind] > lower: i+=1 while not QC(prof.gSndg[i][prof.tdind]): i+=1 lptr = i if prof.gSndg[i][prof.pind] == lower: lptr+=1 # Find highest observations in the layer i = prof.gNumLevels - 1 while prof.gSndg[i][prof.pind] < upper: i-=1 uptr = i if prof.gSndg[i][prof.pind] == upper: uptr-=1 totd = 0 totp = 0 # Start with interpolated bottom layer p1 = lower dp1 = interp.dwpt(p1, prof) num = 1 # Calculate every level that reports a dew point for i in range(lptr, uptr+1): if QC(prof.gSndg[i][prof.tdind]): dp2 = prof.gSndg[i][prof.tdind] p2 = prof.gSndg[i][prof.pind] dpbar = (dp1 + dp2) / 2. pbar = (p1 + p2) / 2. totd += dpbar totp += pbar dp1 = dp2 p1 = p2 num += 1 # Finish with top layer dp2 = interp.dwpt(upper, prof) p2 = upper dbar = (dp1 + dp2) / 2. pbar = (p1 + p2) / 2. totd += dbar totp += pbar return thermo.mixratio(totp/num, totd/num)
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 mean_mixratio(prof, lower=-1, upper=-1): ''' Calculates the mean mixing ratio from a profile object within the specified layer. Inputs ------ prof (profile object) Profile Object lower (float) Bottom level (hPa) [-1=SFC] upper (float) Top level (hPa) [-1=SFC-100hPa] Returns ------- Mean Mixing Ratio (float) ''' if lower == -1: lower = prof.gSndg[prof.sfc][prof.pind] if upper == -1: upper = prof.gSndg[prof.sfc][prof.pind] - 100. if not QC(interp.temp(upper, prof)): mmw = RMISSD if not QC(interp.temp(lower, prof)): prof.gSndg[prof.sfc][prof.pind] # Find lowest observations in the layer i = 0 while prof.gSndg[i][prof.pind] > lower: i += 1 while not QC(prof.gSndg[i][prof.tdind]): i += 1 lptr = i if prof.gSndg[i][prof.pind] == lower: lptr += 1 # Find highest observations in the layer i = prof.gNumLevels - 1 while prof.gSndg[i][prof.pind] < upper: i -= 1 uptr = i if prof.gSndg[i][prof.pind] == upper: uptr -= 1 totd = 0 totp = 0 # Start with interpolated bottom layer p1 = lower dp1 = interp.dwpt(p1, prof) num = 1 # Calculate every level that reports a dew point for i in range(lptr, uptr + 1): if QC(prof.gSndg[i][prof.tdind]): dp2 = prof.gSndg[i][prof.tdind] p2 = prof.gSndg[i][prof.pind] dpbar = (dp1 + dp2) / 2. pbar = (p1 + p2) / 2. totd += dpbar totp += pbar dp1 = dp2 p1 = p2 num += 1 # Finish with top layer dp2 = interp.dwpt(upper, prof) p2 = upper dbar = (dp1 + dp2) / 2. pbar = (p1 + p2) / 2. totd += dbar totp += pbar return thermo.mixratio(totp / num, totd / num)
def init_phase(prof): ''' Inital Precipitation Phase Adapted from SHARP code donated by Rich Thompson (SPC) This function determines the initial phase of any precipitation source in the profile. It does this either by finding a source of precipitation by searching for the highest 50 mb layer that has a relative humidity greater than 80 percent at the top and the bottom of the layer. This layer may be found either in the lowest 5 km of the profile, and if an OMEG profile is specified in the profile object, it will search for the layers with upward motion. The precipitation type is determined by using a.) the interpolated temperature in the middle of the precipitation source layer and b.) set temperature thresholds to determine the precipitation type. The type may be "Rain", "Freezing Rain", "ZR/S Mix", or "Snow". Parameters ---------- prof : Profile object (omega profile optional) Returns ------- plevel : the pressure level of the precipitation source (mb) phase : the phase type of the precipitation (int) phase == 0 for "Rain" phase == 1 for "Freezing Rain" or "ZR/S Mix" phase == 3 for "Snow" tmp : the temperature at the level that is the precipitation source st : a string naming the precipitation type ''' # Needs to be tested plevel = 0 phase = -1 # First, determine whether Upward VVELS are available. If they are, # use them to determine level where precipitation will develop. avail = np.ma.where(prof.omeg < .1)[0] hght_agl = interp.to_agl(prof, prof.hght) if len(avail) < 5: # No VVELS...must look for saturated level # Find the highest near-saturated 50mb layer below 5km agl below_5km_idx = np.ma.where((hght_agl < 5000.) &\ (hght_agl >= 0))[0] else: # Use the VV to find the source of precip. below_5km_idx = np.ma.where((hght_agl < 5000.) &\ (hght_agl >= 0) &\ (prof.omeg <= 0))[0] # Compute the RH at the top and bottom of 50 mb layers rh = thermo.relh(prof.pres, prof.tmpc, prof.dwpc)[below_5km_idx] sats = np.ma.where(rh > 80)[0] new_pres = prof.pres[below_5km_idx][sats] + 50. new_temp = interp.temp(prof, new_pres) new_dwpt = interp.dwpt(prof, new_pres) rh_plus50 = thermo.relh(new_pres, new_temp, new_dwpt) # Find layers where the RH is >80% at the top and bottom layers_idx = np.ma.where(rh_plus50 > 80)[0] if len(layers_idx) == 0: # Found no precipitation source layers st = "N/A" return prof.missing, phase, prof.missing, st # Find the highest layer up via the largest index top_most_layer = np.ma.max(layers_idx) plevel = new_pres[top_most_layer] - 25. # Determine the initial precip type based on the temp in the layer tmp = interp.temp(prof, plevel) if tmp > 0: phase = 0 st = "Rain" elif tmp <= 0 and tmp > -5: phase = 1 st = "Freezing Rain" elif tmp <=-5 and tmp > -9: phase = 1 st = "ZR/S Mix" elif tmp <= -9: phase = 3 st = "Snow" else: st = "N/A" return plevel, phase, tmp, st
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 haines_mid(prof): ''' Haines Index Mid Elevation calculation Calculates the Haines Index(Lower Atmosphere Severity Index) using the middle elevation parmeters, used between 1000 ft or 305 m and 3000 ft or 914 m. Pressure levels 850 mb and 700 mb Dewpoint depression at 850 mb Lapse Rate Term --------------- 1 : < 6C 2 : 6C to 10C 3 : > 10C Dewpoint Depression Term ------------------------ 1 : < 6C 2 : 6C to 12C 3 : > 12C Adapted from S-591 course Added by Nickolai Reimer (NWS Billings, MT) Parameters ---------- prof : profile object Profile object Returns ------- param : number the Haines Index mid ''' tp1 = interp.temp(prof, 850) tp2 = interp.temp(prof, 700) tdp1 = interp.dwpt(prof, 850) if utils.QC(tp1) and utils.QC(tp2) and utils.QC(tdp1): lapse_rate = tp1 - tp2 dewpoint_depression = tp1 - tdp1 if lapse_rate < 6: a = 1 elif 6 <= lapse_rate and lapse_rate <= 10: a = 2 else: a = 3 if dewpoint_depression < 6: b = 1 elif 6 <= dewpoint_depression and dewpoint_depression <= 12: b = 2 else: b = 3 return a + b else: return constants.MISSING
def haines_low(prof): ''' Haines Index Low Elevation calculation Calculates the Haines Index(Lower Atmosphere Severity Index) using the lower elevation parmeters, used below 1000ft or 305 m. Pressure levels 950 mb and 850 mb Dewpoint depression at 850 mb Lapse Rate Term --------------- 1 : < 4C 2 : 4C to 7C 3 : > 7C Dewpoint Depression Term ------------------------ 1 : < 6C 2 : 6C to 9C 3 : > 9C Adapted from S-591 course Added by Nickolai Reimer (NWS Billings, MT) Parameters ---------- prof : profile object Profile object Returns ------- param : number the Haines Index low ''' tp1 = interp.temp(prof, 950) tp2 = interp.temp(prof, 850) tdp2 = interp.dwpt(prof, 850) if utils.QC(tp1) and utils.QC(tp2) and utils.QC(tdp2): lapse_rate = tp1 - tp2 dewpoint_depression = tp2 - tdp2 if lapse_rate < 4: a = 1 elif 4 <= lapse_rate and lapse_rate <= 7: a = 2 else: a = 3 if dewpoint_depression < 6: b = 1 elif 6 <= dewpoint_depression and dewpoint_depression <= 9: b = 2 else: b = 3 return a + b else: return constants.MISSING
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.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
''' Create the Sounding (Profile) Object '''
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 init_phase(prof): ''' Inital Precipitation Phase Adapted from SHARP code donated by Rich Thompson (SPC) This function determines the initial phase of any precipitation source in the profile. It does this either by finding a source of precipitation by searching for the highest 50 mb layer that has a relative humidity greater than 80 percent at the top and the bottom of the layer. This layer may be found either in the lowest 5 km of the profile, and if an OMEG profile is specified in the profile object, it will search for the layers with upward motion. The precipitation type is determined by using a.) the interpolated temperature in the middle of the precipitation source layer and b.) set temperature thresholds to determine the precipitation type. The type may be "Rain", "Freezing Rain", "ZR/S Mix", or "Snow". Parameters ---------- prof : Profile object (omega profile optional) Returns ------- plevel : the pressure level of the precipitation source (mb) phase : the phase type of the precipitation (int) phase == 0 for "Rain" phase == 1 for "Freezing Rain" or "ZR/S Mix" phase == 3 for "Snow" tmp : the temperature at the level that is the precipitation source st : a string naming the precipitation type ''' # Needs to be tested plevel = 0 phase = -1 # First, determine whether Upward VVELS are available. If they are, # use them to determine level where precipitation will develop. avail = np.ma.where(prof.omeg < .1)[0] hght_agl = interp.to_agl(prof, prof.hght) if len(avail) < 5: # No VVELS...must look for saturated level # Find the highest near-saturated 50mb layer below 5km agl below_5km_idx = np.ma.where((hght_agl < 5000.) &\ (hght_agl >= 0))[0] else: # Use the VV to find the source of precip. below_5km_idx = np.ma.where((hght_agl < 5000.) &\ (hght_agl >= 0) &\ (prof.omeg <= 0))[0] # Compute the RH at the top and bottom of 50 mb layers rh = thermo.relh(prof.pres, prof.tmpc, prof.dwpc)[below_5km_idx] sats = np.ma.where(rh > 80)[0] new_pres = prof.pres[below_5km_idx][sats] + 50. new_temp = interp.temp(prof, new_pres) new_dwpt = interp.dwpt(prof, new_pres) rh_plus50 = thermo.relh(new_pres, new_temp, new_dwpt) # Find layers where the RH is >80% at the top and bottom layers_idx = np.ma.where(rh_plus50 > 80)[0] if len(layers_idx) == 0: # Found no precipitation source layers st = "N/A" return prof.missing, phase, prof.missing, st # Find the highest layer up via the largest index top_most_layer = np.ma.max(layers_idx) plevel = new_pres[top_most_layer] - 25. # Determine the initial precip type based on the temp in the layer tmp = interp.temp(prof, plevel) if tmp > 0: phase = 0 st = "Rain" elif tmp <= 0 and tmp > -5: phase = 1 st = "Freezing Rain" elif tmp <= -5 and tmp > -9: phase = 1 st = "ZR/S Mix" elif tmp <= -9: phase = 3 st = "Snow" else: st = "N/A" return plevel, phase, tmp, st
def unstable_level(prof, lower, upper): ''' Finds the most unstable level between the lower and upper levels. Inputs ------ prof (profile object) Profile Object lower (float) Bottom level (hPa) [-1=SFC] upper (float) Top level (hPa) [-1=SFC-100hPa] Returns ------- Pressure Level of most unstable level (float [hPa]) ''' if lower == -1: lower = prof.gSndg[prof.sfc][prof.pind] if upper == -1: upper = prof.gSndg[prof.sfc][prof.pind] - 400. # Make sure this is a valid layer while not QC(interp.dwpt(upper, prof)): upper += 50. if not QC(interp.temp(lower, prof)): lower = prof.gSndg[prof.sfc][0] # Find lowest observations in the layer i = 0 while prof.gSndg[i][prof.pind] > lower: i+=1 while not QC(prof.gSndg[i][prof.tind]): i+=1 lptr = i if prof.gSndg[i][prof.pind] == lower: lptr+=1 # Find highest observations in the layer i = prof.gNumLevels - 1 while prof.gSndg[i][prof.pind] < upper: i-=1 uptr = i if prof.gSndg[i][prof.pind] == upper: uptr-=1 # Start with interpolated bottom layer p1 = lower t1 = interp.temp(p1, prof) td1 = interp.dwpt(p1, prof) p2, t2 = thermo.drylift(p1, t1, td1) tmax = thermo.wetlift(p2, t2, 1000.) pmax = p1 # Calculate every level that reports a dew point for i in range(lptr, uptr+1): if QC(prof.gSndg[i][prof.tdind]): p1 = prof.gSndg[i][prof.pind] t1 = prof.gSndg[i][prof.tind] td1 = prof.gSndg[i][prof.tdind] p2, t2 = thermo.drylift(p1, t1, td1) t1 = thermo.wetlift(p2, t2, 1000.) if t1 > tmax: tmax = t1 pmax = p1 # Finish with interpolated top layer p1 = upper t1 = interp.temp(p1, prof) td1 = interp.dwpt(p1, prof) p2, t2 = thermo.drylift(p1, t1, td1) t1 = thermo.wetlift(p2, t2, 1000.) if t1 > tmax: pmax = prof.gSndg[i][prof.pind] return pmax