Esempio n. 1
0
def test_relh():
    input_p = 925
    input_t = 7
    input_td = 3
    correct_t = 75.45115959367405
    returned_t = thermo.relh(input_p, input_t, input_td)
    npt.assert_almost_equal(returned_t, correct_t)

    input_p = 950
    input_t = 20
    input_td = 14
    correct_t = 67.8069681577635
    returned_t = thermo.relh(input_p, input_t, input_td)
    npt.assert_almost_equal(returned_t, correct_t)
Esempio n. 2
0
def test_relh():
    input_p = 925
    input_t = 7
    input_td = 3
    correct_t = 75.6532482737457
    returned_t = thermo.relh(input_p, input_t, input_td)
    npt.assert_almost_equal(returned_t, correct_t)

    input_p = 950
    input_t = 20
    input_td = 14
    correct_t = 68.35638039676762
    returned_t = thermo.relh(input_p, input_t, input_td)
    npt.assert_almost_equal(returned_t, correct_t)
Esempio n. 3
0
    def get_fire(self):
        '''
        Function to generate different indices and information
        regarding any fire weather in the sounding.  This helps fill
        the data shown in the FIRE inset.
    
        Parameters
        ----------
        None

        Returns
        -------
        None
        '''
        self.fosberg = fire.fosberg(self)
        self.ppbl_top = params.pbl_top(self)
        self.sfc_rh = thermo.relh(self.pres[self.sfc], self.tmpc[self.sfc],
                                  self.dwpc[self.sfc])
        pres_sfc = self.pres[self.sfc]
        pres_1km = interp.pres(self, interp.to_msl(self, 1000.))
        pbl_h = interp.to_agl(self, interp.hght(self, self.ppbl_top))
        self.rh01km = params.mean_relh(self, pbot=pres_sfc, ptop=pres_1km)
        self.pblrh = params.mean_relh(self, pbot=pres_sfc, ptop=self.ppbl_top)
        self.meanwind01km = winds.mean_wind(self, pbot=pres_sfc, ptop=pres_1km)
        self.meanwindpbl = winds.mean_wind(self,
                                           pbot=pres_sfc,
                                           ptop=self.ppbl_top)
        self.pblmaxwind = winds.max_wind(self, lower=0, upper=pbl_h)
        #self.pblmaxwind = [np.ma.masked, np.ma.masked]
        mulplvals = params.DefineParcel(self, flag=3, pres=500)
        mupcl = params.cape(self, lplvals=mulplvals)
        self.bplus_fire = mupcl.bplus
Esempio n. 4
0
 def get_fire(self):
     '''
     Function to generate different indices and information
     regarding any fire weather in the sounding.  This helps fill
     the data shown in the FIRE inset.
 
     Parameters
     ----------
     None
     Returns
     -------
     None
     '''
     self.fosberg = fire.fosberg(self)
     self.ppbl_top = params.pbl_top(self)
     self.sfc_rh = thermo.relh(self.pres[self.sfc], self.tmpc[self.sfc], self.dwpc[self.sfc])
     pres_sfc = self.pres[self.sfc]
     pres_1km = interp.pres(self, interp.to_msl(self, 1000.))
     pbl_h = interp.to_agl(self, interp.hght(self, self.ppbl_top))
     self.rh01km = params.mean_relh(self, pbot=pres_sfc, ptop=pres_1km)
     self.pblrh = params.mean_relh(self, pbot=pres_sfc, ptop=self.ppbl_top)
     self.meanwind01km = winds.mean_wind(self, pbot=pres_sfc, ptop=pres_1km)
     self.meanwindpbl = winds.mean_wind(self, pbot=pres_sfc, ptop=self.ppbl_top)
     self.pblmaxwind = winds.max_wind(self, lower=0, upper=pbl_h)
     #self.pblmaxwind = [np.ma.masked, np.ma.masked]
     mulplvals = params.DefineParcel(self, flag=3, pres=500)
     mupcl = params.cape(self, lplvals=mulplvals)
     self.bplus_fire = mupcl.bplus
Esempio n. 5
0
def cbi(prof):
    '''
        Chandler Burning Index

        This index uses air temperature and relative humidity to create a numerical index of fire danger.
        It is based solely on weather conditions, with no adjustment for fuel moisture.  Values of less
        than 50 indicate low fire danger; Values between 50 and 75 indicate moderate fire danger; values
        between 75 and 90 indicate high fire danger; values between 90 and 97.5 indicate very high fire
        danger; and values above 97.5 indicate extreme fire danger.

        Parameters
        ----------
        prof - Profile object

        Returns
        -------
        cbi : number
            Chandler Burning Index (number)
    '''

    sfc_tmp = prof.tmpc[prof.sfc]
    sfc_rh = thermo.relh(prof.pres[prof.sfc], prof.tmpc[prof.sfc],
                         prof.dwpc[prof.sfc])

    cbi = (((110 - (1.373 * sfc_rh)) - (0.54 * (10.2 - sfc_tmp))) *
           (124 * (10**(-0.0142 * sfc_rh)))) / 60

    return cbi
Esempio n. 6
0
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
Esempio n. 7
0
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

        Returns
        -------
        param - the 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
Esempio n. 8
0
    def get_rh_profile(self):
        '''
        Function to calculate the relative humidity profile
        
        Parameters
        ----------
        None
    
        Returns
        -------
        Array of the relative humidity profile
        '''

        rh = thermo.relh(self.pres, self.tmpc, self.dwpc)
        rh[rh == self.missing] = ma.masked
        rh.set_fill_value(self.missing)
        return rh
Esempio n. 9
0
    def get_relh_profile(self):
        '''
            Function to calculate the relative humidity profile.
            
            Parameters
            ----------
            None
            
            Returns
            -------
            Array of relative humidity profile
            '''

        relh = ma.empty(self.pres.shape[0])
        for i in range(len(self.v)):
            relh[i] = thermo.relh(self.pres[i], self.tmpc[i], self.dwpc[i])
        relh[relh == self.missing] = ma.masked
        relh.set_fill_value(self.missing)
        return relh
Esempio n. 10
0
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)
Esempio n. 11
0
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
Esempio n. 12
0
''' Create the Sounding (Profile) Object '''
Esempio n. 13
0
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]
    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("PDS 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")

    #SVR LOGIC
    if (stp_fixed >= 1.0 or prof.right_scp >= 4.0 or stp_eff >= 1.0) and prof.mupcl.bminus >= -50:
        colors.append("#FFFF00")
        watch_types.append("SVR")
    elif prof.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 prof.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("FLASH FLOOD")
        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:
        watch_types.append("BLIZZARD")
        colors.append("#3366FF")
    
    # Wind Chill (if wind chill gets below -20 F)
    if wind_chill(prof) < -20.:
        watch_types.append("WIND CHILL")
        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.dwpc[prof.get_sfc()]) < 30. :
        watch_types.append("FIRE WEATHER")
        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("EXCESSIVE HEAT")
        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("FREEZE")
        colors.append("#3366FF")
    
    watch_types.append("NONE")
    colors.append("#FFCC33")
    
    return np.asarray(watch_types), np.asarray(colors)
Esempio n. 14
0
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
Esempio n. 15
0
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)
Esempio n. 16
0
            
            if m:
                if (levels > 30):
                    #print sounding_text
                    
                    pres, hght, tmpc, dwpc, wdir, wspd, loc, datestring = parseSPC(sounding_text)                                       
                    
                    # Somewhat arbitrary choice for minimum number of levels in a sounding.
                    # But also require the top-level temperature to be below -20.
                    
                    if (tmpc[-1] < -20) and (tmpc[0] > -12): #  and (m15_rh > 50):
                        
                        # Do a rough estimate of RH in the DGZ to weed out dry-ish soundings.
                        m15 = next(x[0] for x in enumerate(tmpc) if x[1] <= -15)
                        
                        m15_rh = thermo.relh(pres[m15], tmpc[m15], dwpc[m15])     

                        if m15_rh > 50:                                   
                            
                            date = datetime.datetime.strptime(datestring, "%m%d%y/%H%M")
                            
                            # this returns a SHARPPy profile, including theta-e and wetbulb                        
                            prof = profile.create_profile(profile='default', pres=pres, hght=hght, tmpc=tmpc, \
                                            dwpc=dwpc, wspd=wspd, wdir=wdir, missing=-9999, date=date, \
                                            location=loc, strictQC=True)
                        
                            prof = interpZeroTemps(prof)
                            
                            prof = interpZeroWetbulbs(prof)
                            
                            #mean_rh_tmp = params.mean_relh(prof)