def wind_chill(prof): ''' Surface Wind Chill Equation Computes wind chill at the surface data point in the profile object using the equation found at: www.nws.noaa.gov/os/windchill/index.shtml Parameters ---------- prof : Profile object Returns ------- wind_chill : wind chill value in (F) ''' # Needs to be tested sfc_temp = thermo.ctof(prof.tmpc[prof.get_sfc()]) sfc_wspd = utils.KTS2MPH(prof.wspd[prof.get_sfc()]) wind_chill = 35.74 + (0.6215*sfc_temp) - (35.75*(sfc_wspd**0.16)) + \ 0.4275 * (sfc_temp) * (sfc_wspd**0.16) return wind_chill
def fosberg(prof): ''' The Fosberg Fire Weather Index Adapted from code donated by Rich Thompson - NOAA Storm Prediction Center Description: The FWI (Fire Weather Index) is defined by a quantitative model that provides a nonlinear filter of meteorological data which results in a linear relationship between the combined meteorological variables of relative humidity and wind speed, and the behavior of wildfires. Thus the index deals with only the weather conditions, not the fuels. Several sets of conditions have been defined by Fosberg (Fosberg, 1978) to apply this to fire weather management. The upper limits have been set to give an index value of 100 if the moisture content is zero and the wind is 30 mph. Thus, the numbers range from 0 to 100 and if any number is larger than 100, it is set back to 100. The index can be used to measure changes in fire weather conditions. Over several years of use, Fosberg index values of 50 or greater generally appear significant on a national scale. The SPC fire weather verification scheme uses the Fosberg Index, but with a check for both temperature (60F) and adjective fire danger rating (3-High, 4-Very High, 5-Extreme). Description Source - http://www.spc.noaa.gov/exper/firecomp/INFO/fosbinfo.html WARNING: This function has not been fully tested. Parameters ---------- prof : profile object Profile object Returns ------- param : number Fosberg Fire Weather Index ''' tmpf = thermo.ctof(prof.tmpc[prof.get_sfc()]) fmph = utils.KTS2MPH(prof.wspd[prof.get_sfc()]) rh = thermo.relh(prof.pres[prof.sfc], prof.tmpc[prof.sfc], prof.dwpc[prof.sfc]) if (rh <= 10): em = 0.03229 + 0.281073*rh - 0.000578*rh*tmpf elif (10 < rh <= 50): em = 2.22749 + 0.160107*rh - 0.014784*tmpf else: em = 21.0606 + 0.005565*rh*rh - 0.00035*rh*tmpf - 0.483199*rh em30 = em/30 u_sq = fmph*fmph fmdc = 1 - 2*em30 + 1.5*em30*em30 - 0.5*em30*em30*em30 param = (fmdc*np.sqrt(1+u_sq))/0.3002 return param
def possible_watch(prof): ''' Possible Weather/Hazard/Watch Type This function generates a list of possible significant weather types one can expect given a Profile object. (Currently works only for ConvectiveProfile.) These possible weather types are computed via fuzzy logic through set thresholds that have been found through a.) analyzing ingredients within the profile and b.) combining those ingredients with forecasting experience to produce a suggestion of what hazards may exist. Some of the logic is based on experience, some of it is based on actual National Weather Service criteria. This function has not been formally verified and is not meant to be comprehensive nor a source of strict guidance for weather forecasters. As always, the raw data is to be consulted. This code base is currently under development. Wx Categories (ranked in terms of severity): - PDS TOR - TOR - MRGL TOR - SVR - MRGL SVR - FLASH FLOOD - BLIZZARD - WINTER STORM - WIND CHILL - FIRE WEATHER - EXCESSIVE HEAT - FREEZE Suggestions for severe/tornado thresholds were contributed by Rich Thompson - NOAA Storm Prediction Center Parameters ---------- prof : ConvectiveProfile object Returns ------- watch_types : a list of strings containing the weather types in code colors : a list of the HEX colors corresponding to each weather type ''' watch_types = [] colors = [] lr1 = params.lapse_rate(prof, 0, 1000, pres=False) stp_eff = prof.stp_cin stp_fixed = prof.stp_fixed srw_4_6km = utils.mag(prof.srw_4_6km[0], prof.srw_4_6km[1]) sfc_8km_shear = utils.mag(prof.sfc_8km_shear[0], prof.sfc_8km_shear[1]) right_esrh = prof.right_esrh[0] srh1km = prof.srh1km[0] right_scp = prof.right_scp ## Cambios para el hemisferio sur JP JP if prof.latitude < 0: srh1km = -srh1km stp_eff = -stp_eff stp_fixed = -stp_fixed right_scp = -prof.left_scp right_esrh = -prof.left_esrh[0] if stp_eff >= 3 and stp_fixed >= 3 and srh1km >= 200 and right_esrh >= 200 and srw_4_6km >= 15.0 and \ sfc_8km_shear > 45.0 and prof.sfcpcl.lclhght < 1000. and prof.mlpcl.lclhght < 1200 and lr1 >= 5.0 and \ prof.mlpcl.bminus >= -50 and prof.ebotm == 0: watch_types.append("SPP TOR") colors.append(constants.MAGENTA) elif (stp_eff >= 3 or stp_fixed >= 4) and prof.mlpcl.bminus >= -125. and prof.ebotm == 0: watch_types.append("TOR") colors.append("#FF0000") elif (stp_eff >= 1 or stp_fixed >= 1) and (srw_4_6km >= 15.0 or sfc_8km_shear >= 40) and \ prof.mlpcl.bminus >= -50 and prof.ebotm == 0: watch_types.append("TOR") colors.append("#FF0000") elif (stp_eff >= 1 or stp_fixed >= 1) and ((prof.low_rh + prof.mid_rh)/2. >= 60) and lr1 >= 5.0 and \ prof.mlpcl.bminus >= -50 and prof.ebotm == 0: watch_types.append("TOR") colors.append("#FF0000") elif (stp_eff >= 1 or stp_fixed >= 1) and prof.mlpcl.bminus >= -150 and prof.ebotm == 0.: watch_types.append("MRGL TOR") colors.append("#FF0000") elif (stp_eff >= 0.5 and prof.right_esrh >= 150) or (stp_fixed >= 0.5 and srh1km >= 150) and \ prof.mlpcl.bminus >= -50 and prof.ebotm == 0.: watch_types.append("MRGL TOR") colors.append("#FF0000") #t1 = tab.utils.FLOAT2STR(stp_eff, 1) #t2 = tab.utils.FLOAT2STR(stp_fixed, 1) #t3 = tab.utils.FLOAT2STR(srw_4_6km, 1) #t4 = tab.utils.INT2STR(sfc_8km_shear) #t5 = tab.utils.INT2STR(prof.mlpcl.bminus) #t6 = tab.utils.INT2STR(prof.ebotm) #with open('C:\\temp.txt', 'a') as f: # f.write(t1 + ',' + t2 + ',' + t3 + ',' + t4 + ',' + t5 + ',' + t6 + '\n') #SVR LOGIC if (stp_fixed >= 1.0 or right_scp >= 4.0 or stp_eff >= 1.0) and prof.mupcl.bminus >= -50: colors.append("#FFFF00") watch_types.append("SVR") elif right_scp >= 2.0 and (prof.ship >= 1.0 or prof.dcape >= 750) and prof.mupcl.bminus >= -50: colors.append("#FFFF00") watch_types.append("SVR") elif prof.sig_severe >= 30000 and prof.mmp >= 0.6 and prof.mupcl.bminus >= -50: colors.append("#FFFF00") watch_types.append("SVR") elif prof.mupcl.bminus >= -75.0 and (prof.wndg >= 0.5 or prof.ship >= 0.5 or right_scp >= 0.5): colors.append("#0099CC") watch_types.append("MRGL SVR") # Flash Flood Watch PWV is larger than normal and cloud layer mean wind speeds are slow # This is trying to capture the ingredients of moisture and advection speed, but cannot # handle precipitation efficiency or vertical motion pw_climo_flag = prof.pwv_flag pwat = prof.pwat upshear = utils.comp2vec(prof.upshear_downshear[0], prof.upshear_downshear[1]) if pw_climo_flag >= 2 and upshear[1] < 25: watch_types.append("INUND REPENT") colors.append("#5FFB17") #elif pwat > 1.3 and upshear[1] < 25: # watch_types.append("FLASH FLOOD") # colors.append("#5FFB17") # Blizzard if sfc winds > 35 mph and precip type detects snow # Still needs to be tied into the sfc_wspd = utils.KTS2MPH(prof.wspd[prof.get_sfc()]) if sfc_wspd > 35. and prof.tmpc[ prof.get_sfc()] <= 0 and "Snow" in prof.precip_type: watch_types.append("TORM NIEVE") colors.append("#3366FF") # Wind Chill (if wind chill gets below -20 F) if wind_chill(prof) < -20.: watch_types.append("ST VIENTO") colors.append("#3366FF") # Fire WX (sfc RH < 30% and sfc_wind speed > 15 mph) (needs to be updated to include SPC Fire Wx Indices) if sfc_wspd > 15. and thermo.relh(prof.pres[prof.get_sfc()], prof.tmpc[prof.get_sfc()], prof.tmpc[prof.get_sfc()]) < 30.: watch_types.append("INCENDIOS") colors.append("#FF9900") # Excessive Heat (if Max_temp > 105 F and sfc dewpoint > 75 F) if thermo.ctof(prof.dwpc[prof.get_sfc()]) > 75. and thermo.ctof( params.max_temp(prof)) >= 105.: watch_types.append("CALOR INTENSO") colors.append("#CC33CC") # Freeze (checks to see if wetbulb is below freezing and temperature isn't and wind speeds are low) # Still in testing. if thermo.ctof(prof.dwpc[prof.get_sfc()]) <= 32. and thermo.ctof( prof.wetbulb[prof.get_sfc()]) <= 32 and prof.wspd[ prof.get_sfc()] < 5.: watch_types.append("HELADAS") colors.append("#3366FF") watch_types.append("NINGUNA") colors.append("#FFCC33") return np.asarray(watch_types), np.asarray(colors)
def possible_watch(prof, use_left=False): ''' Possible Weather/Hazard/Watch Type This function generates a list of possible significant weather types one can expect given a Profile object. (Currently works only for ConvectiveProfile.) These possible weather types are computed via fuzzy logic through set thresholds that have been found through a.) analyzing ingredients within the profile and b.) combining those ingredients with forecasting experience to produce a suggestion of what hazards may exist. Some of the logic is based on experience, some of it is based on actual National Weather Service criteria. This function has not been formally verified and is not meant to be comprehensive nor a source of strict guidance for weather forecasters. As always, the raw data is to be consulted. Wx Categories (ranked in terms of severity): * PDS TOR * TOR * MRGL TOR * SVR * MRGL SVR * FLASH FLOOD * BLIZZARD * EXCESSIVE HEAT Suggestions for severe/tornado thresholds were contributed by Rich Thompson - NOAA Storm Prediction Center Parameters ---------- prof : profile object ConvectiveProfile object use_left : bool If True, uses the parameters computed from the left-mover bunkers vector to decide the watch type. If False, uses parameters from the right-mover vector. The default is False. Returns ------- watch_types : numpy array strings containing the weather types in code ''' watch_types = [] lr1 = params.lapse_rate(prof, 0, 1000, pres=False) if use_left: stp_eff = prof.left_stp_cin stp_fixed = prof.left_stp_fixed srw_4_6km = utils.mag(prof.left_srw_4_6km[0], prof.left_srw_4_6km[1]) esrh = prof.left_esrh[0] srh1km = prof.left_srh1km[0] else: stp_eff = prof.right_stp_cin stp_fixed = prof.right_stp_fixed srw_4_6km = utils.mag(prof.right_srw_4_6km[0], prof.right_srw_4_6km[1]) esrh = prof.right_esrh[0] srh1km = prof.right_srh1km[0] if prof.latitude < 0: stp_eff = -stp_eff stp_fixed = -stp_fixed esrh = -esrh srh1km = -srh1km sfc_8km_shear = utils.mag(prof.sfc_8km_shear[0], prof.sfc_8km_shear[1]) if stp_eff >= 3 and stp_fixed >= 3 and srh1km >= 200 and esrh >= 200 and srw_4_6km >= 15.0 and \ sfc_8km_shear > 45.0 and prof.sfcpcl.lclhght < 1000. and prof.mlpcl.lclhght < 1200 and lr1 >= 5.0 and \ prof.mlpcl.bminus > -50 and prof.ebotm == 0: watch_types.append("PDS TOR") elif (stp_eff >= 3 or stp_fixed >= 4) and prof.mlpcl.bminus > -125. and prof.ebotm == 0: watch_types.append("TOR") elif (stp_eff >= 1 or stp_fixed >= 1) and (srw_4_6km >= 15.0 or sfc_8km_shear >= 40) and \ prof.mlpcl.bminus > -50 and prof.ebotm == 0: watch_types.append("TOR") elif (stp_eff >= 1 or stp_fixed >= 1) and ((prof.low_rh + prof.mid_rh)/2. >= 60) and lr1 >= 5.0 and \ prof.mlpcl.bminus > -50 and prof.ebotm == 0: watch_types.append("TOR") elif (stp_eff >= 1 or stp_fixed >= 1) and prof.mlpcl.bminus > -150 and prof.ebotm == 0.: watch_types.append("MRGL TOR") elif (stp_eff >= 0.5 and esrh >= 150) or (stp_fixed >= 0.5 and srh1km >= 150) and \ prof.mlpcl.bminus > -50 and prof.ebotm == 0.: watch_types.append("MRGL TOR") #SVR LOGIC if use_left: scp = prof.left_scp else: scp = prof.right_scp if (stp_fixed >= 1.0 or scp >= 4.0 or stp_eff >= 1.0) and prof.mupcl.bminus >= -50: watch_types.append("SVR") elif scp >= 2.0 and (prof.ship >= 1.0 or prof.dcape >= 750) and prof.mupcl.bminus >= -50: watch_types.append("SVR") elif prof.sig_severe >= 30000 and prof.mmp >= 0.6 and prof.mupcl.bminus >= -50: watch_types.append("SVR") elif prof.mupcl.bminus >= -75.0 and (prof.wndg >= 0.5 or prof.ship >= 0.5 or scp >= 0.5): watch_types.append("MRGL SVR") # Flash Flood Watch PWV is larger than normal and cloud layer mean wind speeds are slow # This is trying to capture the ingredients of moisture and advection speed, but cannot # handle precipitation efficiency or vertical motion # Likely is good for handling slow moving MCSes. pw_climo_flag = prof.pwv_flag pwat = prof.pwat upshear = utils.comp2vec(prof.upshear_downshear[0], prof.upshear_downshear[1]) if pw_climo_flag >= 2 and upshear[1] < 25: watch_types.append("FLASH FLOOD") #elif pwat > 1.3 and upshear[1] < 25: # watch_types.append("FLASH FLOOD") # Blizzard if sfc winds > 35 mph and precip type detects snow # Still needs to be tied into the sfc_wspd = utils.KTS2MPH(prof.wspd[prof.get_sfc()]) if sfc_wspd > 35. and prof.tmpc[ prof.get_sfc()] <= 0 and "Snow" in prof.precip_type: watch_types.append("BLIZZARD") # Wind Chill (if wind chill gets below -20 F) # TODO: Be reinstated in future releases if the logic becomes a little more solid. #if wind_chill(prof) < -20.: # watch_types.append("WIND CHILL") # Fire WX (sfc RH < 30% and sfc_wind speed > 15 mph) (needs to be updated to include SPC Fire Wx Indices) # TODO: Be reinstated in future releases once the logic becomes a little more solid #if sfc_wspd > 15. and thermo.relh(prof.pres[prof.get_sfc()], prof.tmpc[prof.get_sfc()], prof.dwpc[prof.get_sfc()]) < 30. : #watch_types.append("FIRE WEATHER") # Excessive Heat (use the heat index calculation (and the max temperature algorithm)) temp = thermo.ctof(prof.tmpc[prof.get_sfc()]) rh = thermo.relh(prof.pres[prof.get_sfc()], temp, prof.dwpc[prof.get_sfc()]) hi = heat_index(temp, rh) if hi > 105.: watch_types.append("EXCESSIVE HEAT") # Freeze (checks to see if wetbulb is below freezing and temperature isn't and wind speeds are low) # Still in testing. To be reinstated in future releases. #if thermo.ctof(prof.dwpc[prof.get_sfc()]) <= 32. and thermo.ctof(prof.wetbulb[prof.get_sfc()]) <= 32 and prof.wspd[prof.get_sfc()] < 5.: # watch_types.append("FREEZE") watch_types.append("NONE") return np.asarray(watch_types)