Exemplo n.º 1
0
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
Exemplo n.º 2
0
 def qc(val):
     return -9999. if not utils.QC(val) else val
Exemplo n.º 3
0
def total_shear(prof, pbot=850, ptop=250, dp=-1, exact=True):
    '''
    Calculates the total shear (also known as the hodograph length) between
    the wind at (pbot) and (ptop).

    Parameters
    ----------
    prof: profile object
        Profile object
    pbot : number (optional; default 850 hPa)
        Pressure of the bottom level (hPa)
    ptop : number (optional; default 250 hPa)
        Pressure of the top level (hPa)
    dp : negative integer (optional; default -1)
        The pressure increment for the interpolated sounding
    exact : bool (optional; default = True)
        Switch to choose between using the exact data (slower) or using
        interpolated sounding at 'dp' pressure levels (faster)

    Returns
    -------
    tot_pos_shr : number
        Total positive shear
    tot_neg_shr : number
        Total negative shear
    tot_net_shr : number
        Total net shear (subtracting negative shear from positive shear)
    tot_abs_shr : number
        Total absolute shear (adding positive and negative shear together)
    '''
    if np.isnan(pbot) or np.isnan(ptop) or \
        type(pbot) == type(ma.masked) or type(ptop) == type(ma.masked):
        print np.ma.masked, np.ma.masked, np.ma.masked, np.ma.masked
    if exact:
        ind1 = np.where(pbot > prof.pres)[0].min()
        ind2 = np.where(ptop < prof.pres)[0].max()
        u1, v1 = interp.components(prof, pbot)
        u2, v2 = interp.components(prof, ptop)
        u = np.concatenate([[u1], prof.u[ind1:ind2+1].compressed(), [u2]])
        v = np.concatenate([[v1], prof.v[ind1:ind2+1].compressed(), [v2]])
    else:
        ps = np.arange(pbot, ptop+dp, dp)
        u, v = interp.components(prof, ps)
    shu = u[1:] - u[:-1]
    shv = v[1:] - v[:-1]
    shr = ( ( np.power(shu, 2) + np.power(shv, 2) ) ** 0.5 )
    wdr, wsp = utils.comp2vec(u,v)
    t_wdr = wdr[1:]
    mod = 180 - wdr[:-1]
    t_wdr = t_wdr + mod

    idx1 = ma.where(t_wdr < 0)[0]
    idx2 = ma.where(t_wdr >= 360)[0]
    t_wdr[idx1] = t_wdr[idx1] + 360
    t_wdr[idx2] = t_wdr[idx2] - 360
    d_wdr = t_wdr - 180
    d_wsp = wsp[1:] - wsp[:-1]
    idxdp = ma.where(d_wdr > 0)[0]
    idxdn = ma.where(d_wdr < 0)[0]
    idxsp = ma.where(np.logical_and(d_wdr == 0, d_wsp > 0) == True)[0]
    idxsn = ma.where(np.logical_and(d_wdr == 0, d_wsp < 0) == True)[0]
    if not utils.QC(idxdp):
        pos_dir_shr = ma.masked
    else:
        pos_dir_shr = shr[idxdp].sum()
    if not utils.QC(idxdn):
        neg_dir_shr = ma.masked
    else:
        neg_dir_shr = shr[idxdn].sum()
    if not utils.QC(idxsp):
        pos_spd_shr = ma.masked
    else:
        pos_spd_shr = shr[idxsp].sum()
    if not utils.QC(idxsn):
        neg_spd_shr = ma.masked
    else:
        neg_spd_shr = shr[idxsn].sum()
    tot_pos_shr = pos_dir_shr + pos_spd_shr
    tot_neg_shr = neg_dir_shr + neg_spd_shr
    tot_net_shr = tot_pos_shr - tot_neg_shr
    tot_abs_shr = tot_pos_shr + tot_neg_shr

    return tot_pos_shr, tot_neg_shr, tot_net_shr, tot_abs_shr
Exemplo n.º 4
0
    def draw_profile(self, qp):
        '''
        Draws the storm relative wind profile.
        
        Parameters
        ----------
        qp: QtGui.QPainter object
        
        '''
        ## initialize a pen with a red color, thickness of 1, solid line
        if self.prof.wdir.count() == 0:
            return
        pen = QtGui.QPen(self.trace_color, 1)
        pen.setStyle(QtCore.Qt.SolidLine)

        ## if there are missing values, get the mask
        try:
            mask = np.maximum(self.sru.mask, self.srv.mask)
            sru = self.sru[~mask]
            srv = self.srv[~mask]
            hgt = self.prof.hght[~mask]
        ## otherwise the data is fine
        except:
            sru = self.sru
            srv = self.srv
            hgt = self.prof.hght

        if len(sru) == 0 or len(srv) == 0 or len(hgt) == 0:
            return

        max_hght_idx = len(hgt) - 1
        while type(hgt[max_hght_idx]) == type(np.ma.masked):
            max_hght_idx -= 1

        interp_hght = np.arange(self.prof.hght[self.prof.sfc],
                                min(hgt[max_hght_idx], self.hmax * 1000.), 10)
        interp_sru = np.interp(interp_hght, hgt[:(max_hght_idx + 1)],
                               sru[:(max_hght_idx + 1)])
        interp_srv = np.interp(interp_hght, hgt[:(max_hght_idx + 1)],
                               srv[:(max_hght_idx + 1)])
        sr_spd = np.hypot(interp_sru, interp_srv)

        qp.setPen(pen)
        for i in range(interp_hght.shape[0] - 1):
            spd1 = sr_spd[i]
            spd2 = sr_spd[i + 1]
            if utils.QC(spd1) and utils.QC(spd2):
                hgt1 = (interp_hght[i] - interp_hght[0]) / 1000
                hgt2 = (interp_hght[i + 1] - interp_hght[0]) / 1000
                ## convert the wind speeds to x pixels
                x1 = self.speed_to_pix(spd1)
                x2 = self.speed_to_pix(spd2)
                ## convert the height values to y pixels
                y1 = self.hgt_to_pix(hgt1)
                y2 = self.hgt_to_pix(hgt2)
                ## draw a line between the two points
                qp.drawLine(x1, y1, x2, y2)

        if utils.QC(self.srw_0_2km):
            # Plot the 0-2 km mean SRW
            pen = QtGui.QPen(self.m0_2_color, 2)
            pen.setStyle(QtCore.Qt.SolidLine)
            qp.setPen(pen)
            x1 = self.speed_to_pix(self.srw_0_2km)
            x2 = self.speed_to_pix(self.srw_0_2km)
            y1 = self.hgt_to_pix(0.0)
            y2 = self.hgt_to_pix(2.0)
            qp.drawLine(x1, y1, x2, y2)

        if utils.QC(self.srw_4_6km):
            # Plot the 4-6 km mean SRW
            pen = QtGui.QPen(self.m4_6_color, 2)
            pen.setStyle(QtCore.Qt.SolidLine)
            qp.setPen(pen)
            x1 = self.speed_to_pix(self.srw_4_6km)
            x2 = self.speed_to_pix(self.srw_4_6km)
            y1 = self.hgt_to_pix(4.0)
            y2 = self.hgt_to_pix(6.0)
            qp.drawLine(x1, y1, x2, y2)

        if utils.QC(self.srw_9_11km):
            # Plot the 9-11 km mean SRW
            pen = QtGui.QPen(self.m9_11_color, 2)
            pen.setStyle(QtCore.Qt.SolidLine)
            qp.setPen(pen)
            x1 = self.speed_to_pix(self.srw_9_11km)
            x2 = self.speed_to_pix(self.srw_9_11km)
            y1 = self.hgt_to_pix(9.0)
            y2 = self.hgt_to_pix(11.0)
            qp.drawLine(x1, y1, x2, y2)
Exemplo n.º 5
0
def helicity(prof, lower, upper, stu=0, stv=0, dp=-1, exact=True):
    '''
    Calculates the relative helicity (m2/s2) of a layer from lower to upper.
    If storm-motion vector is supplied, storm-relative helicity, both
    positve and negative, is returned.

    Parameters
    ----------
    prof : profile object
        Profile Object
    lower : number
        Bottom level of layer (m, AGL)
    upper : number
        Top level of layer (m, AGL)
    stu : number (optional; default = 0)
        U-component of storm-motion (kts)
    stv : number (optional; default = 0)
        V-component of storm-motion (kts)
    dp : negative integer (optional; default -1)
        The pressure increment for the interpolated sounding (mb)
    exact : bool (optional; default = True)
        Switch to choose between using the exact data (slower) or using
        interpolated sounding at 'dp' pressure levels (faster)

    Returns
    -------
    phel+nhel : number
        Combined Helicity (m2/s2)
    phel : number
        Positive Helicity (m2/s2)
    nhel : number
        Negative Helicity (m2/s2)

    '''
    if prof.wdir.count() == 0 or not utils.QC(lower) or not utils.QC(
            upper) or not utils.QC(stu) or not utils.QC(stv):
        return ma.masked, ma.masked, ma.masked

    if lower != upper:
        lower = interp.to_msl(prof, lower)
        upper = interp.to_msl(prof, upper)
        plower = interp.pres(prof, lower)
        pupper = interp.pres(prof, upper)
        if np.isnan(plower) or np.isnan(pupper) or \
            type(plower) == type(ma.masked) or type(pupper) == type(ma.masked):
            return np.ma.masked, np.ma.masked, np.ma.masked
        if exact:
            ind1 = np.where(plower >= prof.pres)[0].min()
            ind2 = np.where(pupper <= prof.pres)[0].max()
            u1, v1 = interp.components(prof, plower)
            u2, v2 = interp.components(prof, pupper)
            u = np.concatenate([[u1], prof.u[ind1:ind2 + 1].compressed(),
                                [u2]])
            v = np.concatenate([[v1], prof.v[ind1:ind2 + 1].compressed(),
                                [v2]])
        else:
            ps = np.arange(plower, pupper + dp, dp)
            u, v = interp.components(prof, ps)
        sru = utils.KTS2MS(u - stu)
        srv = utils.KTS2MS(v - stv)
        layers = (sru[1:] * srv[:-1]) - (sru[:-1] * srv[1:])
        phel = layers[layers > 0].sum()
        nhel = layers[layers < 0].sum()
    else:
        phel = nhel = 0

    return phel + nhel, phel, nhel
Exemplo n.º 6
0
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
Exemplo n.º 7
0
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