Beispiel #1
0
def test_comp2vec_single():
    input_u = 5
    input_v = 5
    correct_wdir = 225
    correct_wspd = 7.0710678118654755
    returned_wdir, returned_wspd = utils.comp2vec(input_u, input_v)
    npt.assert_almost_equal(returned_wdir, correct_wdir)
    npt.assert_almost_equal(returned_wspd, correct_wspd)
Beispiel #2
0
def test_comp2vec_single():
    input_u = 5
    input_v = 5
    correct_wdir = 225
    correct_wspd = 7.0710678118654755
    returned_wdir, returned_wspd = utils.comp2vec(input_u, input_v)
    npt.assert_almost_equal(returned_wdir, correct_wdir)
    npt.assert_almost_equal(returned_wspd, correct_wspd)
Beispiel #3
0
def test_comp2vec_zeros():
    input_u = [0, -20, 0, 40, 0]
    input_v = [-10, 0, 30, 0, -50]
    correct_wdir = [0, 90, 180, 270, 0]
    correct_wspd = [10, 20, 30, 40, 50]
    correct_wdir = np.asanyarray(correct_wdir).astype(np.float64)
    correct_wspd = np.asanyarray(correct_wspd).astype(np.float64)
    returned_wdir, returned_wspd = utils.comp2vec(input_u, input_v)
    npt.assert_equal(returned_wdir, correct_wdir)
    npt.assert_equal(returned_wspd, correct_wspd)
Beispiel #4
0
def test_comp2vec_zeros():
    input_u = [0, -20, 0, 40, 0]
    input_v = [-10, 0, 30, 0, -50]
    correct_wdir = [0, 90, 180, 270, 0]
    correct_wspd = [10, 20, 30, 40, 50]
    correct_wdir = np.asanyarray(correct_wdir).astype(np.float64)
    correct_wspd = np.asanyarray(correct_wspd).astype(np.float64)
    returned_wdir, returned_wspd = utils.comp2vec(input_u, input_v)
    npt.assert_equal(returned_wdir, correct_wdir)
    npt.assert_equal(returned_wspd, correct_wspd)
def test_comp2vec_array():
    input_u = [0, -7.0710678118654746, -15, -14.142135623730951, 0,
        21.213203435596423, 35, 28.284271247461909, 0]
    input_v = [-5, -7.0710678118654746, 0, 14.142135623730951, 25,
        21.213203435596423, 0, -28.284271247461909, -45]
    correct_wdir = [0, 45, 90, 135, 180, 225, 270, 315, 0]
    correct_wspd = [5, 10, 15, 20, 25, 30, 35, 40, 45]
    correct_wdir = np.asanyarray(correct_wdir).astype(np.float64)
    correct_wspd = np.asanyarray(correct_wspd).astype(np.float64)
    returned_wdir, returned_wspd = utils.comp2vec(input_u, input_v)
    npt.assert_almost_equal(correct_wdir, returned_wdir)
    npt.assert_almost_equal(correct_wspd, returned_wspd)
Beispiel #6
0
    def setDeviant(self, deviant):
        self.use_left = deviant == 'left'

        if self.use_left:
            self.srw_0_2km = utils.comp2vec(self.prof.left_srw_0_2km[0],
                                            self.prof.left_srw_0_2km[1])[1]
            self.srw_4_6km = utils.comp2vec(self.prof.left_srw_4_6km[0],
                                            self.prof.left_srw_4_6km[1])[1]
            self.srw_9_11km = utils.comp2vec(self.prof.left_srw_9_11km[0],
                                             self.prof.left_srw_9_11km[1])[1]
            ## get only the left mover u and v components
            self.sru = self.u - self.srwind[2]
            self.srv = self.v - self.srwind[3]
        else:
            self.srw_0_2km = utils.comp2vec(self.prof.right_srw_0_2km[0],
                                            self.prof.right_srw_0_2km[1])[1]
            self.srw_4_6km = utils.comp2vec(self.prof.right_srw_4_6km[0],
                                            self.prof.right_srw_4_6km[1])[1]
            self.srw_9_11km = utils.comp2vec(self.prof.right_srw_9_11km[0],
                                             self.prof.right_srw_9_11km[1])[1]
            ## get only the right mover u and v components
            self.sru = self.u - self.srwind[0]
            self.srv = self.v - self.srwind[1]

        self.clearData()
        self.plotBackground()
        self.plotData()
        self.update()
Beispiel #7
0
    def setProf(self, prof):
        self.prof = prof

        self.u = prof.u
        self.v = prof.v
        ## calculate the storm relative wind from the bunkers motion function
        self.srwind = prof.srwind

        if self.use_left:
            self.srw_0_2km = utils.comp2vec(self.prof.left_srw_0_2km[0],
                                            self.prof.left_srw_0_2km[1])[1]
            self.srw_4_6km = utils.comp2vec(self.prof.left_srw_4_6km[0],
                                            self.prof.left_srw_4_6km[1])[1]
            self.srw_9_11km = utils.comp2vec(self.prof.left_srw_9_11km[0],
                                             self.prof.left_srw_9_11km[1])[1]
            ## get only the left mover u and v components
            self.sru = self.u - self.srwind[2]
            self.srv = self.v - self.srwind[3]
        else:
            self.srw_0_2km = utils.comp2vec(self.prof.right_srw_0_2km[0],
                                            self.prof.right_srw_0_2km[1])[1]
            self.srw_4_6km = utils.comp2vec(self.prof.right_srw_4_6km[0],
                                            self.prof.right_srw_4_6km[1])[1]
            self.srw_9_11km = utils.comp2vec(self.prof.right_srw_9_11km[0],
                                             self.prof.right_srw_9_11km[1])[1]
            ## get only the right mover u and v components
            self.sru = self.u - self.srwind[0]
            self.srv = self.v - self.srwind[1]

        self.clearData()
        self.plotBackground()
        self.plotData()
        self.update()
Beispiel #8
0
    def setPreferences(self, update_gui=True, **prefs):
        self.bg_color = QtGui.QColor(prefs['bg_color'])
        self.fg_color = QtGui.QColor(prefs['fg_color'])
        self.clsc_color = QtGui.QColor(prefs['srw_clsc_color'])
        self.trace_color = QtGui.QColor(prefs['srw_trace_color'])
        self.m0_2_color = QtGui.QColor(prefs['srw_0_2_color'])
        self.m4_6_color = QtGui.QColor(prefs['srw_4_6_color'])
        self.m9_11_color = QtGui.QColor(prefs['srw_9_11_color'])

        if update_gui:
            if self.use_left:
                self.srw_0_2km = utils.comp2vec(self.prof.left_srw_0_2km[0], self.prof.left_srw_0_2km[1])[1]
                self.srw_4_6km = utils.comp2vec(self.prof.left_srw_4_6km[0], self.prof.left_srw_4_6km[1])[1]
                self.srw_9_11km = utils.comp2vec(self.prof.left_srw_9_11km[0], self.prof.left_srw_9_11km[1])[1]
                ## get only the left mover u and v components
                self.sru = self.u - self.srwind[2]
                self.srv = self.v - self.srwind[3]
            else:
                self.srw_0_2km = utils.comp2vec(self.prof.right_srw_0_2km[0], self.prof.right_srw_0_2km[1])[1]
                self.srw_4_6km = utils.comp2vec(self.prof.right_srw_4_6km[0], self.prof.right_srw_4_6km[1])[1]
                self.srw_9_11km = utils.comp2vec(self.prof.right_srw_9_11km[0], self.prof.right_srw_9_11km[1])[1]
                ## get only the right mover u and v components
                self.sru = self.u - self.srwind[0]
                self.srv = self.v - self.srwind[1]

            self.clearData()
            self.plotBackground()
            self.plotData()
            self.update()
Beispiel #9
0
def test_comp2vec_default_missing_val_array():
    input_u = [MISSING, -10, 0, MISSING]
    input_v = [MISSING, 0, 20, MISSING]
    correct_wdir = [0, 90, 180, MISSING]
    correct_wspd = [MISSING, 10, 20, 30]
    correct_wdir = ma.asanyarray(correct_wdir).astype(np.float64)
    correct_wspd = ma.asanyarray(correct_wspd).astype(np.float64)
    correct_wdir[correct_wdir == MISSING] = ma.masked
    correct_wspd[correct_wspd == MISSING] = ma.masked
    correct_wdir[correct_wspd.mask] = ma.masked
    correct_wspd[correct_wdir.mask] = ma.masked
    correct_wdir.set_fill_value(MISSING)
    correct_wspd.set_fill_value(MISSING)
    returned_wdir, returned_wspd = utils.comp2vec(input_u, input_v)
    npt.assert_almost_equal(returned_wdir, correct_wdir)
    npt.assert_almost_equal(returned_wspd, correct_wspd)
Beispiel #10
0
def test_comp2vec_default_missing_val_array():
    input_u = [MISSING, -10, 0, MISSING]
    input_v= [MISSING, 0, 20, MISSING]
    correct_wdir = [0, 90, 180, MISSING]
    correct_wspd = [MISSING, 10, 20, 30]
    correct_wdir = ma.asanyarray(correct_wdir).astype(np.float64)
    correct_wspd = ma.asanyarray(correct_wspd).astype(np.float64)
    correct_wdir[correct_wdir == MISSING] = ma.masked
    correct_wspd[correct_wspd == MISSING] = ma.masked
    correct_wdir[correct_wspd.mask] = ma.masked
    correct_wspd[correct_wdir.mask] = ma.masked
    correct_wdir.set_fill_value(MISSING)
    correct_wspd.set_fill_value(MISSING)
    returned_wdir, returned_wspd = utils.comp2vec(input_u, input_v)
    npt.assert_almost_equal(returned_wdir, correct_wdir)
    npt.assert_almost_equal(returned_wspd, correct_wspd)
Beispiel #11
0
    def _parse(self):

        # time = datetime.now()

        file_data = self._downloadFile()
        modInit, modName, fHour, coords = file_data.split(' ')
        # if len(modInit) == 2:
        #     import time
        #     modInit = time.strftime('%Y%m%d')+modInit

        locationStr = coords.strip('\n')

        textFile = '/home/apache/climate/data/forecast/sndgs/'+modInit+'_'+modName+'_'+fHour+'_'+coords.strip('\n')+'_raw.txt'
        #writeTimes(textFile, 'Begin')

        data_header = 'Location: '

        time = datetime.strptime(modInit, '%Y%m%d%H') + timedelta(hours=int(fHour))

        # Determine if it's a site ID:
        if ',' not in coords:
            import numpy as np
            sites, siteCoords = np.genfromtxt('/home/apache/climate/hanis/model/fsound/text/sid.txt', dtype=str, unpack=True, delimiter=' ')
            i = np.where(sites == 'KORD')
            coords = siteCoords[i[0][0]]

        variables = fsonde_decoder.decode(modInit, modName, fHour, coords)
        #writeTimes(textFile, 'After Decode')

        pres = variables['pres']
        hght = variables['hght']
        tmpc = variables['temp']
        dwpc = variables['dewp']
        u = variables['ugrd']
        v = variables['vgrd']
        omeg = variables['omeg']
        
        wdir, wspd = utils.comp2vec(u, v)
        # wspd = [s*1.94384 for s in wspd]

        # Force latitude to be 35 N. Figure out a way to fix this later.
        prof = profile.create_profile(profile='raw', pres=pres, hght=hght, tmpc=tmpc, dwpc=dwpc, wdir=wdir, wspd=wspd, omeg=omeg, location=locationStr, date=time, latitude=35.)
        prof_coll = prof_collection.ProfCollection({'':[ prof ]},[ time ],)
        prof_coll.setMeta('loc', locationStr)
        #writeTimes(textFile, 'End')
        return prof_coll
Beispiel #12
0
def test_comp2vec_user_missing_val_array():
    missing = 50
    input_u = [missing, -10, 0, missing]
    input_v = [missing, 0, 20, missing]
    correct_wdir = [0, 90, 180, missing]
    correct_wspd = [missing, 10, 20, 30]
    correct_wdir = ma.asanyarray(correct_wdir).astype(np.float64)
    correct_wspd = ma.asanyarray(correct_wspd).astype(np.float64)
    correct_wdir[correct_wdir == missing] = ma.masked
    correct_wspd[correct_wspd == missing] = ma.masked
    correct_wdir[correct_wspd.mask] = ma.masked
    correct_wspd[correct_wdir.mask] = ma.masked
    correct_wdir.set_fill_value(missing)
    correct_wspd.set_fill_value(missing)
    returned_wdir, returned_wspd = utils.comp2vec(input_u, input_v)
    npt.assert_almost_equal(returned_wdir, correct_wdir)
    npt.assert_almost_equal(returned_wspd, correct_wspd)
Beispiel #13
0
def test_comp2vec_user_missing_val_array():
    missing = 50
    input_u = [missing, -10, 0, missing]
    input_v= [missing, 0, 20, missing]
    correct_wdir = [0, 90, 180, missing]
    correct_wspd = [missing, 10, 20, 30]
    correct_wdir = ma.asanyarray(correct_wdir).astype(np.float64)
    correct_wspd = ma.asanyarray(correct_wspd).astype(np.float64)
    correct_wdir[correct_wdir == missing] = ma.masked
    correct_wspd[correct_wspd == missing] = ma.masked
    correct_wdir[correct_wspd.mask] = ma.masked
    correct_wspd[correct_wdir.mask] = ma.masked
    correct_wdir.set_fill_value(missing)
    correct_wspd.set_fill_value(missing)
    returned_wdir, returned_wspd = utils.comp2vec(input_u, input_v)
    npt.assert_almost_equal(returned_wdir, correct_wdir)
    npt.assert_almost_equal(returned_wspd, correct_wspd)
Beispiel #14
0
def vec(prof, p):
    '''
    Interpolates the given data to calculate the wind direction and speed
    at a given pressure

    Parameters
    ----------
    p : number, numpy array
        Pressure (hPa) of a level
    prof : profile object
        Profile object

    Returns
    -------
    Wind direction and magnitude at the given pressure
    '''
    U, V = components(prof, p)
    return utils.comp2vec(U, V)
Beispiel #15
0
def vec(prof, p):
    '''
    Interpolates the given data to calculate the wind direction and speed
    at a given pressure

    Parameters
    ----------
    p : number, numpy array
        Pressure (hPa) of a level
    prof : profile object
        Profile object

    Returns
    -------
    Wind direction and magnitude at the given pressure
    '''
    U, V = components(prof, p)
    return utils.comp2vec(U, V)
Beispiel #16
0
def non_parcel_bunkers_motion(prof):
    '''
    Compute the Bunkers Storm Motion for a Right Moving Supercell

    Inputs
    ------
    prof : profile object
        Profile Object

    Returns
    -------
    rstu : number
        Right Storm Motion U-component
    rstv : number
        Right Storm Motion V-component
    lstu : number
        Left Storm Motion U-component
    lstv : number
        Left Storm Motion V-component

    '''
    d = utils.MS2KTS(7.5)  # Deviation value emperically derived as 7.5 m/s
    msl6km = interp.to_msl(prof, 6000.)
    p6km = interp.pres(prof, msl6km)

    # SFC-6km Mean Wind
    mnu6, mnv6 = mean_wind_npw(prof, prof.pres[prof.sfc], p6km)

    # SFC-6km Shear Vector
    shru6, shrv6 = wind_shear(prof, prof.pres[prof.sfc], p6km)

    # Bunkers Right Motion
    tmp = d / utils.comp2vec(shru6, shrv6)[1]
    rstu = mnu6 + (tmp * shrv6)
    rstv = mnv6 - (tmp * shru6)
    lstu = mnu6 - (tmp * shrv6)
    lstv = mnv6 + (tmp * shru6)

    return rstu, rstv, lstu, lstv
Beispiel #17
0
def non_parcel_bunkers_motion(prof):
    '''
    Compute the Bunkers Storm Motion for a Right Moving Supercell

    Inputs
    ------
    prof : profile object
        Profile Object

    Returns
    -------
    rstu : number
        Right Storm Motion U-component
    rstv : number
        Right Storm Motion V-component
    lstu : number
        Left Storm Motion U-component
    lstv : number
        Left Storm Motion V-component

    '''
    d = utils.MS2KTS(7.5)     # Deviation value emperically derived as 7.5 m/s
    msl6km = interp.to_msl(prof, 6000.)
    p6km = interp.pres(prof, msl6km)

    # SFC-6km Mean Wind
    mnu6, mnv6 = mean_wind_npw(prof, prof.pres[prof.sfc], p6km)

    # SFC-6km Shear Vector
    shru6, shrv6 = wind_shear(prof, prof.pres[prof.sfc], p6km)

    # Bunkers Right Motion
    tmp = d / utils.comp2vec(shru6, shrv6)[1]
    rstu = mnu6 + (tmp * shrv6)
    rstv = mnv6 - (tmp * shru6)
    lstu = mnu6 - (tmp * shrv6)
    lstv = mnv6 + (tmp * shru6)

    return rstu, rstv, lstu, lstv
Beispiel #18
0
''' Create the Sounding (Profile) Object '''
Beispiel #19
0
def plot_sounding(file, imgName):
    try:
        prof, time, location = decode(file)
    except Exception as e:
        print(
            "\n Oops! Couldn't decode the sounding data. No plot produced!\n")
        print(e)
        # return None

    # Open up the text file with the data in columns (e.g. the sample OAX file distributed with SHARPpy)
    locInfo = location.split('_')
    title = locInfo[0] + ' ' + locInfo[1] + ' ' + locInfo[
        2] + '   ' + time.strftime('%Y%m%d/%H%M') + '   (Observed)'

    # Set up the figure in matplotlib.
    fig = plt.figure(figsize=(14, 7.25))
    gs = gridspec.GridSpec(4, 6, width_ratios=[1, 5, 1, 0.5, 3, 3])
    ax = plt.subplot(gs[0:3, 0:2], projection='skewx')
    plt.title(title, fontsize=14, loc='left', color='w')
    ax.set_facecolor('k')
    ax.spines['left'].set_color('w')
    ax.spines['right'].set_color('w')
    ax.spines['bottom'].set_color('w')
    ax.spines['top'].set_color('w')

    #     xticks = ax.xaxis.get_major_ticks() #mute a tick label outside plot
    #     xticks[-4].label1.set_visible(False)

    ax.tick_params(axis='both', colors='w', grid_color='silver')
    ax.ticklabel_format(style='plain')

    # ax.xaxis.label.set_color('w')
    # ax.yaxis.label.set_color('w')
    ax.grid(True)
    plt.grid(True)

    # Ask user for default limits or custom limits
    pt_plot, t_lower, t_upper = ask_limits(prof.pres[~prof.dwpc.mask],
                                           prof.dwpc[~prof.dwpc.mask])

    # Bounds of the pressure axis
    pb_plot = 1050
    dp_plot = 10
    plevs_plot = np.arange(pb_plot, pt_plot - 1, -dp_plot)

    # Plot the background variables
    # presvals = np.arange(1000, 0, -10)

    #draw mixing ratio lines
    draw_mixing_ratio_lines(ax)

    ax.semilogy(prof.tmpc[~prof.tmpc.mask],
                prof.pres[~prof.tmpc.mask],
                'r',
                lw=2)
    ax.semilogy(prof.dwpc[~prof.dwpc.mask],
                prof.pres[~prof.dwpc.mask],
                'lime',
                lw=2)
    ax.semilogy(prof.vtmp[~prof.dwpc.mask],
                prof.pres[~prof.dwpc.mask],
                'r--',
                lw=1)
    ax.semilogy(prof.wetbulb[~prof.dwpc.mask],
                prof.pres[~prof.dwpc.mask],
                'cyan',
                '-',
                lw=1)

    #write sfc temp and dewpoint in F
    sfcT = prof.tmpc[~prof.tmpc.mask][0]
    sfcTd = prof.dwpc[~prof.dwpc.mask][0]
    sfcW = prof.wetbulb[~prof.dwpc.mask][0]
    sfcP = prof.pres[~prof.tmpc.mask][0]
    ax.annotate(str(int(sfcW * (9 / 5) + 32)), (sfcW, sfcP),
                xytext=(-6, -9),
                textcoords='offset points',
                color='cyan',
                weight='black',
                size=8,
                path_effects=[pe.withStroke(linewidth=2, foreground="black")])
    ax.annotate(str(int(sfcT * (9 / 5) + 32)), (sfcT, sfcP),
                xytext=(-2, -9),
                textcoords='offset points',
                color='r',
                weight='black',
                size=8,
                path_effects=[pe.withStroke(linewidth=2, foreground="black")])
    ax.annotate(str(int(sfcTd * (9 / 5) + 32)), (sfcTd, sfcP),
                xytext=(-12, -9),
                textcoords='offset points',
                color='lime',
                weight='black',
                size=8,
                path_effects=[pe.withStroke(linewidth=2, foreground="black")])

    #plot significant levels
    plot_sig_levels(ax, prof)

    # Plot the parcel trace, but this may fail.  If it does so, inform the user.
    try:
        ax.semilogy(prof.mupcl.ttrace, prof.mupcl.ptrace, 'w--')
    except:
        print("Couldn't plot parcel traces...")

    # Highlight the 0 C and -20 C isotherms.
    l = ax.axvline(0, color='b', ls='--')
    l = ax.axvline(-20, color='b', ls='--')

    #plot dry adiabats
    skew.draw_dry_adiabats(ax, color='silver')

    #draw heights
    skew.draw_heights(ax, prof)

    # Disables the log-formatting that comes with semilogy
    ax.yaxis.set_major_formatter(ScalarFormatter())
    pmin = prof.pres[~prof.dwpc.mask][-1]
    if pmin > 700.:
        ax.set_yticks(np.arange(100, 1000, 50))
    else:
        ax.set_yticks(np.linspace(100, 1000, 10))
    ax.set_ylim(pb_plot, pt_plot)

    # Plot the hodograph data.
    # inset_axes = draw_hodo_inset(ax, prof)
    hodoAx = plt.subplot(gs[0:3, 3:])
    hodoAx.set_facecolor('k')
    hodoAx.axis('off')
    hodoAx = draw_hodo_inset(hodoAx, prof)

    # plotHodo(inset_axes, prof.hght, prof.u, prof.v, color='r')
    plotHodo(hodoAx, prof.hght, prof.u, prof.v, color='r')

    #plot bunkers motion unless the most unstable EL does not exist
    srwind = params.bunkers_storm_motion(prof)
    if isinstance(prof.mupcl.elpres, np.float64):
        hodoAx.text(srwind[0], srwind[1], 'RM', color='w', fontsize=8)
        hodoAx.text(srwind[2], srwind[3], 'LM', color='w', fontsize=8)
    else:
        print("couldn't plot Bunkers vectors")

    # inset_axes.text(srwind[0], srwind[1], 'RM', color='r', fontsize=8)
    # inset_axes.text(srwind[2], srwind[3], 'LM', color='b', fontsize=8)

    #mask out barbs above the top of the plot
    below_pmin = np.where(prof.pres >= pt_plot)[0]

    # Draw the wind barbs axis and everything that comes with it.
    if pmin > 700.:
        ax.xaxis.set_major_locator(MultipleLocator(5))
    else:
        ax.xaxis.set_major_locator(MultipleLocator(10))
    ax.set_xlim(t_lower, t_upper)

    ax2 = plt.subplot(gs[0:3, 2])
    ax3 = plt.subplot(gs[3, 0:3])
    plot_wind_axes(ax2, pb_plot, pt_plot, plevs_plot)

    #setting the stride for how many wind barbs plot
    # st = 15

    # plot_wind_barbs(ax2, prof.pres[below_pmin][~prof.pres.mask[below_pmin]][::st],
    #                 prof.u[below_pmin][~prof.u.mask[below_pmin]][::st],
    #                 prof.v[below_pmin][~prof.v.mask[below_pmin]][::st],
    #                 pt_plot)
    plot_wind_barbs(ax2, prof.pres[below_pmin][~prof.pres.mask[below_pmin]],
                    prof.u[below_pmin][~prof.u.mask[below_pmin]],
                    prof.v[below_pmin][~prof.v.mask[below_pmin]], pt_plot)

    gs.update(left=0.05, bottom=0.05, top=0.95, right=1, wspace=0.025)

    # Calculate indices to be shown.  More indices can be calculated here using the tutorial and reading the params module.
    p1km = interp.pres(prof, interp.to_msl(prof, 1000.))
    p6km = interp.pres(prof, interp.to_msl(prof, 6000.))
    sfc = prof.pres[prof.sfc]
    sfc_1km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p1km)
    sfc_6km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p6km)
    srh3km = winds.helicity(prof, 0, 3000., stu=srwind[0], stv=srwind[1])
    srh1km = winds.helicity(prof, 0, 1000., stu=srwind[0], stv=srwind[1])
    scp = params.scp(prof.mupcl.bplus, prof.right_esrh[0], prof.ebwspd)
    stp_cin = params.stp_cin(prof.mlpcl.bplus, prof.right_esrh[0], prof.ebwspd,
                             prof.mlpcl.lclhght, prof.mlpcl.bminus)
    stp_fixed = params.stp_fixed(
        prof.sfcpcl.bplus, prof.sfcpcl.lclhght, srh1km[0],
        utils.comp2vec(prof.sfc_6km_shear[0], prof.sfc_6km_shear[1])[1])
    ship = params.ship(prof)

    # A routine to perform the correct formatting when writing the indices out to the figure.
    def fmt(value, fmt='int'):
        if fmt == 'int':
            try:
                val = int(value)
            except:
                val = str("M")
        else:
            try:
                val = round(value, 1)
            except:
                val = "M"
        return val

    # Setting a dictionary that is a collection of all of the indices we'll be showing on the figure.
    # the dictionary includes the index name, the actual value, and the units.
    indices = {'SBCAPE': [fmt(prof.sfcpcl.bplus), 'J/kg'],\
               'SBCIN': [fmt(prof.sfcpcl.bminus), 'J/kg'],\
               'SBLCL': [fmt(prof.sfcpcl.lclhght), 'm AGL'],\
               'SBLFC': [fmt(prof.sfcpcl.lfchght), 'm AGL'],\
               'SBEL': [fmt(prof.sfcpcl.elhght), 'm AGL'],\
               'SBLI': [fmt(prof.sfcpcl.li5), 'C'],\
               'MLCAPE': [fmt(prof.mlpcl.bplus), 'J/kg'],\
               'MLCIN': [fmt(prof.mlpcl.bminus), 'J/kg'],\
               'MLLCL': [fmt(prof.mlpcl.lclhght), 'm AGL'],\
               'MLLFC': [fmt(prof.mlpcl.lfchght), 'm AGL'],\
               'MLEL': [fmt(prof.mlpcl.elhght), 'm AGL'],\
               'MLLI': [fmt(prof.mlpcl.li5), 'C'],\
               'MUCAPE': [fmt(prof.mupcl.bplus), 'J/kg'],\
               'MUCIN': [fmt(prof.mupcl.bminus), 'J/kg'],\
               'MULCL': [fmt(prof.mupcl.lclhght), 'm AGL'],\
               'MULFC': [fmt(prof.mupcl.lfchght), 'm AGL'],\
               'MUEL': [fmt(prof.mupcl.elhght), 'm AGL'],\
               'MULI': [fmt(prof.mupcl.li5), 'C'],\
               '0-1 km SRH': [fmt(srh1km[0]), 'm2/s2'],\
               '0-1 km Shear': [fmt(utils.comp2vec(sfc_1km_shear[0], sfc_1km_shear[1])[1]), 'kts'],\
               '0-3 km SRH': [fmt(srh3km[0]), 'm2/s2'],\
               '0-6 km Shear': [fmt(utils.comp2vec(sfc_6km_shear[0], sfc_6km_shear[1])[1]), 'kts'],\
               'Eff. SRH': [fmt(prof.right_esrh[0]), 'm2/s2'],\
               'EBWD': [fmt(prof.ebwspd), 'kts'],\
               'PWV': [round(prof.pwat, 2), 'inch'],\
               'K-index': [fmt(params.k_index(prof)), ''],\
               'STP(fix)': [fmt(stp_fixed, 'flt'), ''],\
               'SHIP': [fmt(ship, 'flt'), ''],\
               'SCP': [fmt(scp, 'flt'), ''],\
               'STP(cin)': [fmt(stp_cin, 'flt'), '']}

    # List the indices within the indices dictionary on the side of the plot.
    trans = transforms.blended_transform_factory(ax.transAxes, ax.transData)

    # Write out all of the indices to the figure.
    #print("##############")
    #print("   INDICES    ")
    #print("##############")
    string = ''
    keys = np.sort(list(indices.keys()))
    x = 0
    counter = 0
    for key in keys:
        string = string + key + ': ' + str(
            indices[key][0]) + ' ' + indices[key][1] + '\n'
        #    print((key + ": " + str(indices[key][0]) + ' ' + indices[key][1]))
        if counter < 7:
            counter += 1
            continue
        else:
            counter = 0
            ax3.text(x,
                     1,
                     string,
                     verticalalignment='top',
                     transform=ax3.transAxes,
                     fontsize=11,
                     color='w')
            string = ''
            x += 0.3
    ax3.text(x,
             1,
             string,
             verticalalignment='top',
             transform=ax3.transAxes,
             fontsize=11,
             color='w')
    ax3.set_axis_off()

    # Show SARS matches (edited for Keith Sherburn)
    #try:
    #    supercell_matches = prof.supercell_matches
    #    hail_matches = prof.matches
    #except:
    #    supercell_matches = prof.right_supercell_matches
    #    hail_matches = prof.right_matches

    #print()
    #print("#############")
    #print(" SARS OUTPUT ")
    #print("#############")
    #for mtype, matches in zip(['Supercell', 'Hail'], [supercell_matches, hail_matches]):
    #    print(mtype)
    #    print('-----------')
    #    if len(matches[0]) == 0:
    #        print("NO QUALITY MATCHES")
    #    for i in range(len(matches[0])):
    #        print(matches[0][i] + ' ' + matches[1][i])
    #    print("Total Loose Matches:", matches[2])
    #    print("# of Loose Matches that met Criteria:", matches[3])
    #    print("SVR Probability:", matches[4])
    #    print()

    #plot logos
    im = plt.imread('logo.png')
    #left, bottom, width, height = [0.25, 0.6, 0.2, 0.2]
    #left, bottom, width, height = [0.1, 0.175, 0.4, 0.4] #bottom left
    left, bottom, width, height = [0.035, 0.65, 0.4, 0.4]
    # ax4 = fig.add_axes([left, bottom, width, height])
    ax4 = plt.subplot(gs[3, 4])
    implot = ax4.imshow(im, alpha=0.99)
    ax4.axis('off')
    ax4.set_facecolor('k')

    im2 = plt.imread('essc_logo.png')
    ax5 = plt.subplot(gs[3, 5])
    implot = ax5.imshow(im2, alpha=0.99)
    ax5.axis('off')
    ax5.set_facecolor('k')

    #plot SHARPpy acknowledgement
    # plt.text(1, 1, 'Plotted with SHARPpy', horizontalalignment='right',
    #             verticalalignment='top', transform=ax.transAxes, color='w')
    hodoAx.annotate(
        'Plotted with SHARPpy - https://sharppy.github.io/SHARPpy/',
        (0.7, 0.96),
        xycoords='figure fraction',
        va='center',
        color='w')

    #filename for the plot
    # plotName = os.path.splitext(file)[0] + '.png'

    # Finalize the image formatting and alignments, and save the image to the file.
    #gs.tight_layout(fig)
    plt.style.use('dark_background')
    fn = time.strftime(
        '%Y%m%d.%H%M') + '_' + locInfo[0] + '_' + locInfo[1] + '.png'
    fn = fn.replace('/', '')
    print('SHARPpy quick-look image output at: ' + imgName)
    #plt.savefig(fn, bbox_inches='tight', dpi=180)
    plt.savefig(imgName, dpi=180)
Beispiel #20
0
sfc = prof.pres[prof.sfc]
p3km = interp.pres(prof, interp.to_msl(prof, 3000.))
p6km = interp.pres(prof, interp.to_msl(prof, 6000.))
p1km = interp.pres(prof, interp.to_msl(prof, 1000.))
mean_3km = winds.mean_wind(prof, pbot=sfc, ptop=p3km)
sfc_6km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p6km)
sfc_3km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p3km)
sfc_1km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p1km)
srwind = params.bunkers_storm_motion(prof)
srh3km = winds.helicity(prof, 0, 3000., stu=srwind[0], stv=srwind[1])
srh1km = winds.helicity(prof, 0, 1000., stu=srwind[0], stv=srwind[1])

stp_fixed = params.stp_fixed(
    sfcpcl.bplus, sfcpcl.lclhght, srh1km[0],
    utils.comp2vec(sfc_6km_shear[0], sfc_6km_shear[1])[1])
ship = params.ship(prof)
eff_inflow = params.effective_inflow_layer(prof)
ebot_hght = interp.to_agl(prof, interp.hght(prof, eff_inflow[0]))
etop_hght = interp.to_agl(prof, interp.hght(prof, eff_inflow[1]))
effective_srh = winds.helicity(prof,
                               ebot_hght,
                               etop_hght,
                               stu=srwind[0],
                               stv=srwind[1])
ebwd = winds.wind_shear(prof, pbot=eff_inflow[0], ptop=eff_inflow[1])
ebwspd = utils.mag(ebwd[0], ebwd[1])
scp = params.scp(mupcl.bplus, effective_srh[0], ebwspd)
stp_cin = params.stp_cin(mlpcl.bplus, effective_srh[0], ebwspd, mlpcl.lclhght,
                         mlpcl.bminus)
Beispiel #21
0
 def get_kinematics(self):
     '''
     Function to generate the numerous kinematic quantities
     used for display and calculations. It requires that the
     parcel calculations have already been called for the lcl
     to el shear and mean wind vectors, as well as indices
     that require an effective inflow layer.
     Parameters
     ----------
     None
     Returns
     -------
     None
     '''
     sfc = self.pres[self.sfc]
     heights = np.array([1000., 3000., 4000., 5000., 6000., 8000., 9000.])
     p1km, p3km, p4km, p5km, p6km, p8km, p9km = interp.pres(self, interp.to_msl(self, heights))
     ## 1km and 6km winds
     self.wind1km = interp.vec(self, p1km)
     self.wind6km = interp.vec(self, p6km)
     ## calcluate wind shear
     self.sfc_1km_shear = winds.wind_shear(self, pbot=sfc, ptop=p1km)
     self.sfc_3km_shear = winds.wind_shear(self, pbot=sfc, ptop=p3km)
     self.sfc_6km_shear = winds.wind_shear(self, pbot=sfc, ptop=p6km)
     self.sfc_8km_shear = winds.wind_shear(self, pbot=sfc, ptop=p8km)
     self.sfc_9km_shear = winds.wind_shear(self, pbot=sfc, ptop=p9km)
     self.lcl_el_shear = winds.wind_shear(self, pbot=self.mupcl.lclpres, ptop=self.mupcl.elpres)
     ## calculate mean wind
     self.mean_1km = utils.comp2vec(*winds.mean_wind(self, pbot=sfc, ptop=p1km))
     self.mean_3km = utils.comp2vec(*winds.mean_wind(self, pbot=sfc, ptop=p3km))
     self.mean_6km = utils.comp2vec(*winds.mean_wind(self, pbot=sfc, ptop=p6km))
     self.mean_8km = utils.comp2vec(*winds.mean_wind(self, pbot=sfc, ptop=p8km))
     self.mean_lcl_el = utils.comp2vec(*winds.mean_wind(self, pbot=self.mupcl.lclpres, ptop=self.mupcl.elpres))
     ## parameters that depend on the presence of an effective inflow layer
     if self.etop is ma.masked or self.ebottom is ma.masked:
         self.etopm = ma.masked; self.ebotm = ma.masked
         self.srwind = winds.non_parcel_bunkers_motion( self )
         self.eff_shear = [MISSING, MISSING]
         self.ebwd = [MISSING, MISSING, MISSING]
         self.ebwspd = MISSING
         self.mean_eff = [MISSING, MISSING, MISSING]
         self.mean_ebw = [MISSING, MISSING, MISSING]
         self.srw_eff = [MISSING, MISSING, MISSING]
         self.srw_ebw = [MISSING, MISSING, MISSING]
         self.right_esrh = [ma.masked, ma.masked, ma.masked]
         self.left_esrh = [ma.masked, ma.masked, ma.masked]
         self.critical_angle = ma.masked
     else:
         self.srwind = params.bunkers_storm_motion(self, mupcl=self.mupcl, pbot=self.ebottom)
         depth = ( self.mupcl.elhght - self.ebotm ) / 2
         elh = interp.pres(self, interp.to_msl(self, self.ebotm + depth))
         ## calculate mean wind
         self.mean_eff = winds.mean_wind(self, self.ebottom, self.etop )
         self.mean_ebw = winds.mean_wind(self, pbot=self.ebottom, ptop=elh )
         ## calculate wind shear of the effective layer
         self.eff_shear = winds.wind_shear(self, pbot=self.ebottom, ptop=self.etop)
         self.ebwd = winds.wind_shear(self, pbot=self.ebottom, ptop=elh)
         self.ebwspd = utils.mag( self.ebwd[0], self.ebwd[1] )
         ## calculate the mean sr wind
         self.srw_eff = winds.sr_wind(self, pbot=self.ebottom, ptop=self.etop, stu=self.srwind[0], stv=self.srwind[1] )
         self.srw_ebw = winds.sr_wind(self, pbot=self.ebottom, ptop=elh, stu=self.srwind[0], stv=self.srwind[1] )
         self.right_esrh = winds.helicity(self, self.ebotm, self.etopm, stu=self.srwind[0], stv=self.srwind[1])
         self.left_esrh = winds.helicity(self, self.ebotm, self.etopm, stu=self.srwind[2], stv=self.srwind[3])
         self.critical_angle = winds.critical_angle(self, stu=self.srwind[0], stv=self.srwind[1])
     ## calculate mean srw
     self.srw_1km = utils.comp2vec(*winds.sr_wind(self, pbot=sfc, ptop=p1km, stu=self.srwind[0], stv=self.srwind[1] ))
     self.srw_3km = utils.comp2vec(*winds.sr_wind(self, pbot=sfc, ptop=p3km, stu=self.srwind[0], stv=self.srwind[1] ))
     self.srw_6km = utils.comp2vec(*winds.sr_wind(self, pbot=sfc, ptop=p6km, stu=self.srwind[0], stv=self.srwind[1] ))
     self.srw_8km = utils.comp2vec(*winds.sr_wind(self, pbot=sfc, ptop=p8km, stu=self.srwind[0], stv=self.srwind[1] ))
     self.srw_4_5km = utils.comp2vec(*winds.sr_wind(self, pbot=p4km, ptop=p5km, stu=self.srwind[0], stv=self.srwind[1] ))
     self.srw_lcl_el = utils.comp2vec(*winds.sr_wind(self, pbot=self.mupcl.lclpres, ptop=self.mupcl.elpres, stu=self.srwind[0], stv=self.srwind[1] ))
     # This is for the red, blue, and purple bars that appear on the SR Winds vs. Height plot
     self.srw_0_2km = winds.sr_wind(self, pbot=sfc, ptop=interp.pres(self, interp.to_msl(self, 2000.)), stu=self.srwind[0], stv=self.srwind[1])
     self.srw_4_6km = winds.sr_wind(self, pbot=interp.pres(self, interp.to_msl(self, 4000.)), ptop=p6km, stu=self.srwind[0], stv=self.srwind[1])
     self.srw_9_11km = winds.sr_wind(self, pbot=interp.pres(self, interp.to_msl(self, 9000.)), ptop=interp.pres(self, interp.to_msl(self, 11000.)), stu=self.srwind[0], stv=self.srwind[1])
     
     ## calculate upshear and downshear
     self.upshear_downshear = winds.mbe_vectors(self)
     self.srh1km = winds.helicity(self, 0, 1000., stu=self.srwind[0], stv=self.srwind[1])
     self.srh3km = winds.helicity(self, 0, 3000., stu=self.srwind[0], stv=self.srwind[1])
Beispiel #22
0
    def __init__(self, **kwargs):
        '''
        Create the sounding data object
        
        Parameters
        ----------
        Mandatory Keywords
        pres : array_like
        The pressure values (Hectopaschals)
        hght : array_like
        The corresponding height values (Meters)
        tmpc : array_like
        The corresponding temperature values (Celsius)
        dwpc : array_like
        The corresponding dewpoint temperature values (Celsius)
            
        Optional Keyword Pairs (must use one or the other)
        wdir : array_like
        The direction from which the wind is blowing in
        meteorological degrees
        wspd : array_like
        The speed of the wind
            
        OR
            
        u : array_like
        The U-component of the direction from which the wind
        is blowing
            
        v : array_like
        The V-component of the direction from which the wind
        is blowing.
            
        Optional Keywords
        missing : number (default: sharppy.sharptab.constants.MISSING)
        The value of the missing flag
        location : string (default: None)
        The 3 character station identifier or 4 character
        WMO station ID for radiosonde locations. Used for
        the PWV database.
        
        strictQC : boolean
        A flag that indicates whether or not the strict quality control
        routines should be run on the profile upon construction.
        Returns
        -------
        prof: Profile object
            
        '''
        super(BasicProfile, self).__init__(**kwargs)

        strictQC = kwargs.get('strictQC', True)

        assert len(self.pres) == len(self.hght) == len(self.tmpc) == len(self.dwpc),\
                "Length of pres, hght, tmpc, or dwpc arrays passed to constructor are not the same."

        ## did the user provide the wind in vector form?
        if self.wdir is not None:
            assert len(self.wdir) == len(self.wspd) == len(self.pres), "Length of wdir and wspd arrays passed to constructor are not the same length as the pres array."
            self.wdir[self.wdir == self.missing] = ma.masked
            self.wspd[self.wspd == self.missing] = ma.masked
            self.wdir[self.wspd.mask] = ma.masked
            self.wspd[self.wdir.mask] = ma.masked
            self.u, self.v = utils.vec2comp(self.wdir, self.wspd)

        ## did the user provide the wind in u,v form?
        elif self.u is not None:
            assert len(self.u) == len(self.v) == len(self.pres), "Length of u and v arrays passed to constructor are not the same length as the pres array."
            self.u[self.u == self.missing] = ma.masked
            self.v[self.v == self.missing] = ma.masked
            self.u[self.v.mask] = ma.masked
            self.v[self.u.mask] = ma.masked
            self.wdir, self.wspd = utils.comp2vec(self.u, self.v)

        ## check if any standard deviation data was supplied
        if self.tmp_stdev is not None:
            self.dew_stdev[self.dew_stdev == self.missing] = ma.masked
            self.tmp_stdev[self.tmp_stdev == self.missing] = ma.masked
            self.dew_stdev.set_fill_value(self.missing)
            self.tmp_stdev.set_fill_value(self.missing)

        if self.omeg is not None:
            ## get the omega data and turn into arrays
            assert len(self.omeg) == len(self.pres), "Length of omeg array passed to constructor is not the same length as the pres array."
            self.omeg[self.omeg == self.missing] = ma.masked
        else:
            self.omeg = ma.masked_all(len(self.hght))

        # QC Checks on the arrays passed to the constructor.
        qc_tools.areProfileArrayLengthEqual(self)
       
        ## mask the missing values
        self.pres[self.pres == self.missing] = ma.masked
        self.hght[self.hght == self.missing] = ma.masked
        self.tmpc[self.tmpc == self.missing] = ma.masked
        self.dwpc[self.dwpc == self.missing] = ma.masked

        #if not qc_tools.isPRESValid(self.pres):
        ##    qc_tools.raiseError("Incorrect order of pressure array (or repeat values) or pressure array is of length <= 1.", ValueError)
        if not qc_tools.isHGHTValid(self.hght) and strictQC:
            qc_tools.raiseError("Incorrect order of height (or repeat values) array or height array is of length <= 1.", ValueError)
        if not qc_tools.isTMPCValid(self.tmpc):
            qc_tools.raiseError("Invalid temperature array. Array contains a value < 273.15 Celsius.", ValueError)
        if not qc_tools.isDWPCValid(self.dwpc):
            qc_tools.raiseError("Invalid dewpoint array. Array contains a value < 273.15 Celsius.", ValueError)
        if not qc_tools.isWSPDValid(self.wspd) and strictQC:
            qc_tools.raiseError("Invalid wind speed array. Array contains a value < 0 knots.", ValueError)
        if not qc_tools.isWDIRValid(self.wdir) and strictQC:
            qc_tools.raiseError("Invalid wind direction array. Array contains a value < 0 degrees or value >= 360 degrees.", ValueError)     


        self.logp = np.log10(self.pres.copy())
        self.vtmp = thermo.virtemp( self.pres, self.tmpc, self.dwpc )
        idx = np.ma.where(self.pres > 0)[0]
        self.vtmp[self.dwpc.mask[idx]] = self.tmpc[self.dwpc.mask[idx]] # Masking any virtual temperature 

        ## get the index of the top and bottom of the profile
        self.sfc = self.get_sfc()
        self.top = self.get_top()
        ## generate the wetbulb profile
        self.wetbulb = self.get_wetbulb_profile()
        ## generate theta-e profile
        self.thetae = self.get_thetae_profile()
Beispiel #23
0
                            alpha=0.7,
                            linestyle="None",
                            marker="o",
                            markersize=5,
                            mew=0,
                            label="right mover")
    # Explanation for red and blue dots. Put only 1 point in the legend entry.
    bunkerleg = hodo_ax.legend(handles=[bunkerL, bunkerR],
                               fontsize=5,
                               frameon=False,
                               numpoints=1)

    # show Bunker left/right movers if 0-6km shear magnitude >= 20kts
    p6km = interp.pres(prof, interp.to_msl(prof, 6000.))
    sfc_6km_shear = winds.wind_shear(prof, pbot=prof.pres[prof.sfc], ptop=p6km)
    if utils.comp2vec(sfc_6km_shear[0], sfc_6km_shear[1])[1] >= 20.:
        bunkerR.set_visible(True)
        bunkerL.set_visible(True)
        bunkerleg.set_visible(True)
        bunkerR.set_data(srwind[0],
                         srwind[1])  # Update Bunker's Storm motion right mover
        bunkerL.set_data(srwind[2],
                         srwind[3])  # Update Bunker's Storm motion left mover
    else:
        bunkerR.set_visible(False)
        bunkerL.set_visible(False)
        bunkerleg.set_visible(False)

    if debug:
        print("about to plot wind barbs")
    # Recreate stack of wind barbs
Beispiel #24
0
def test_comp2vec_default_missing_val_single():
    input_u = MISSING
    input_v = 30
    returned_wdir, returned_wspd = utils.comp2vec(input_u, input_v)
    npt.assert_(type(returned_wdir), type(ma.masked))
    npt.assert_(type(returned_wspd), type(ma.masked))
Beispiel #25
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)
Beispiel #26
0
    # Draw the hodograph on the Skew-T.
    hodo_ax = myskewt.draw_hodo()
    hodo, AGL = myskewt.add_hodo(hodo_ax, prof)

    # Plot Bunker's Storm motion left mover as a blue dot
    bunkerL, = hodo_ax.plot([], [], color='b', alpha=0.7, linestyle="None", marker="o", markersize=5, mew=0, label="left mover")
    # Plot Bunker's Storm motion right mover as a red dot
    # The comma after bunkerR de-lists it.
    bunkerR, = hodo_ax.plot([], [], color='r', alpha=0.7, linestyle="None", marker="o", markersize=5, mew=0, label="right mover")
    # Explanation for red and blue dots. Put only 1 point in the legend entry.
    bunkerleg = hodo_ax.legend(handles=[bunkerL,bunkerR], fontsize=5, frameon=False, numpoints=1)

    # show Bunker left/right movers if 0-6km shear magnitude >= 20kts
    p6km = interp.pres(prof, interp.to_msl(prof, 6000.))
    sfc_6km_shear = winds.wind_shear(prof, pbot=prof.pres[prof.sfc], ptop=p6km)
    if utils.comp2vec(sfc_6km_shear[0], sfc_6km_shear[1])[1] >= 20.:
        bunkerR.set_visible(True)
        bunkerL.set_visible(True)
        bunkerleg.set_visible(True)
        bunkerR.set_data(srwind[0], srwind[1]) # Update Bunker's Storm motion right mover
        bunkerL.set_data(srwind[2], srwind[3]) # Update Bunker's Storm motion left mover
    else:
        bunkerR.set_visible(False)
        bunkerL.set_visible(False)
        bunkerleg.set_visible(False)

    # Recreate stack of wind barbs
    s = []
    bot=2000.
    # Space out wind barbs evenly on log axis.
    for ind, i in enumerate(prof.pres):
    p8km = interp.pres(prof, interp.to_msl(prof, 8000.))
    p9km = interp.pres(prof, interp.to_msl(prof, 9000.))
    sfc = prof.pres[prof.sfc]
    sfc_1km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p1km)
    sfc_3km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p3km)
    sfc_6km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p6km)
    sfc_8km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p8km)
    sfc_9km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p9km)
    srh3km = winds.helicity(prof, 0, 3000., stu=srwind[0], stv=srwind[1])
    srh1km = winds.helicity(prof, 0, 1000., stu=srwind[0], stv=srwind[1])
    scp = params.scp(prof.mupcl.bplus, prof.right_esrh[0], prof.ebwspd)
    stp_cin = params.stp_cin(prof.mlpcl.bplus, prof.right_esrh[0], prof.ebwspd,
                             prof.mlpcl.lclhght, prof.mlpcl.bminus)
    stp_fixed = params.stp_fixed(
        prof.sfcpcl.bplus, prof.sfcpcl.lclhght, srh1km[0],
        utils.comp2vec(prof.sfc_6km_shear[0], prof.sfc_6km_shear[1])[1])
    ship = params.ship(prof)

    #########################

    # A routine to perform the correct formatting when writing the indices out to the figure.
    def fmt(value, fmt='int'):
        if fmt == 'int':
            try:
                val = int(value)
            except:
                val = str("M")
        else:
            try:
                val = round(value, 1)
            except:
Beispiel #28
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
Beispiel #29
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)
Beispiel #30
0
    def __init__(self, **kwargs):
        '''
        Create the sounding data object
        
        Parameters
        ----------
        Mandatory Keywords
        pres : array_like
        The pressure values (Hectopaschals)
        hght : array_like
        The corresponding height values (Meters)
        tmpc : array_like
        The corresponding temperature values (Celsius)
        dwpc : array_like
        The corresponding dewpoint temperature values (Celsius)
            
        Optional Keyword Pairs (must use one or the other)
        wdir : array_like
        The direction from which the wind is blowing in
        meteorological degrees
        wspd : array_like
        The speed of the wind
            
        OR
            
        u : array_like
        The U-component of the direction from which the wind
        is blowing
            
        v : array_like
        The V-component of the direction from which the wind
        is blowing.
            
        Optional Keywords
        missing : number (default: sharppy.sharptab.constants.MISSING)
        The value of the missing flag

        location : string (default: None)
        The 3 character station identifier or 4 character
        WMO station ID for radiosonde locations. Used for
        the PWV database.
        
        strictQC : boolean
        A flag that indicates whether or not the strict quality control
        routines should be run on the profile upon construction.

        Returns
        -------
        prof: Profile object
            
        '''
        super(BasicProfile, self).__init__(**kwargs)

        strictQC = kwargs.get('strictQC', True)

        assert len(self.pres) == len(self.hght) == len(self.tmpc) == len(self.dwpc),\
                "Length of pres, hght, tmpc, or dwpc arrays passed to constructor are not the same."

        ## did the user provide the wind in vector form?
        if self.wdir is not None:
            assert len(self.wdir) == len(self.wspd) == len(
                self.pres
            ), "Length of wdir and wspd arrays passed to constructor are not the same length as the pres array."
            self.wdir[self.wdir == self.missing] = ma.masked
            self.wspd[self.wspd == self.missing] = ma.masked
            self.wdir[self.wspd.mask] = ma.masked
            self.wspd[self.wdir.mask] = ma.masked
            self.u, self.v = utils.vec2comp(self.wdir, self.wspd)

        ## did the user provide the wind in u,v form?
        elif self.u is not None:
            assert len(self.u) == len(self.v) == len(
                self.pres
            ), "Length of u and v arrays passed to constructor are not the same length as the pres array."
            self.u[self.u == self.missing] = ma.masked
            self.v[self.v == self.missing] = ma.masked
            self.u[self.v.mask] = ma.masked
            self.v[self.u.mask] = ma.masked
            self.wdir, self.wspd = utils.comp2vec(self.u, self.v)

        ## check if any standard deviation data was supplied
        if self.tmp_stdev is not None:
            self.dew_stdev[self.dew_stdev == self.missing] = ma.masked
            self.tmp_stdev[self.tmp_stdev == self.missing] = ma.masked
            self.dew_stdev.set_fill_value(self.missing)
            self.tmp_stdev.set_fill_value(self.missing)

        if self.omeg is not None:
            ## get the omega data and turn into arrays
            assert len(self.omeg) == len(
                self.pres
            ), "Length of omeg array passed to constructor is not the same length as the pres array."
            self.omeg[self.omeg == self.missing] = ma.masked
        else:
            self.omeg = ma.masked_all(len(self.hght))

        # QC Checks on the arrays passed to the constructor.
        qc_tools.areProfileArrayLengthEqual(self)

        ## mask the missing values
        self.pres[self.pres == self.missing] = ma.masked
        self.hght[self.hght == self.missing] = ma.masked
        self.tmpc[self.tmpc == self.missing] = ma.masked
        self.dwpc[self.dwpc == self.missing] = ma.masked

        #if not qc_tools.isPRESValid(self.pres):
        ##    qc_tools.raiseError("Incorrect order of pressure array (or repeat values) or pressure array is of length <= 1.", ValueError)
        if not qc_tools.isHGHTValid(self.hght) and strictQC:
            qc_tools.raiseError(
                "Incorrect order of height (or repeat values) array or height array is of length <= 1.",
                ValueError)
        if not qc_tools.isTMPCValid(self.tmpc):
            qc_tools.raiseError(
                "Invalid temperature array. Array contains a value < -273.15 Celsius.",
                ValueError)
        if not qc_tools.isDWPCValid(self.dwpc):
            qc_tools.raiseError(
                "Invalid dewpoint array. Array contains a value < -273.15 Celsius.",
                ValueError)
        if not qc_tools.isWSPDValid(self.wspd) and strictQC:
            qc_tools.raiseError(
                "Invalid wind speed array. Array contains a value < 0 knots.",
                ValueError)
        if not qc_tools.isWDIRValid(self.wdir) and strictQC:
            qc_tools.raiseError(
                "Invalid wind direction array. Array contains a value < 0 degrees or value > 360 degrees.",
                ValueError)

        self.logp = np.log10(self.pres.copy())
        self.vtmp = thermo.virtemp(self.pres, self.tmpc, self.dwpc)
        idx = np.ma.where(self.pres > 0)[0]
        self.vtmp[self.dwpc.mask[idx]] = self.tmpc[
            self.dwpc.mask[idx]]  # Masking any virtual temperature

        ## get the index of the top and bottom of the profile
        self.sfc = self.get_sfc()
        self.top = self.get_top()
        ## generate the wetbulb profile
        self.wetbulb = self.get_wetbulb_profile()
        ## generate theta-e profile
        self.thetae = self.get_thetae_profile()
Beispiel #31
0
    def __init__(self, **kwargs):
        '''
        Create the sounding data object
        
        Parameters
        ----------
        Mandatory Keywords
        pres : array_like
            The pressure values (Hectopaschals)
        hght : array_like
            The corresponding height values (Meters)
        tmpc : array_like
            The corresponding temperature values (Celsius)
        dwpc : array_like
        The corresponding dewpoint temperature values (Celsius)
            
        Optional Keyword Pairs (must use one or the other)
        wdir : array_like
            The direction from which the wind is blowing in
            meteorological degrees
        wspd : array_like
            The speed of the wind (kts)
            
        OR
            
        u : array_like
            The U-component of the direction from which the wind
            is blowing (kts)
            
        v : array_like
            The V-component of the direction from which the wind
            is blowing. (kts)
            
        Optional Keywords
        missing : number (default: sharppy.sharptab.constants.MISSING)
            The value of the missing flag

        location : string (default: None)
            The 3 character station identifier or 4 character
            WMO station ID for radiosonde locations. Used for
            the PWV database.
        
        strictQC : boolean
            A flag that indicates whether or not the strict quality control
            routines should be run on the profile upon construction.

        Returns
        -------
        prof: Profile object
            
        '''
        super(BasicProfile, self).__init__(**kwargs)

        self.strictQC = kwargs.get('strictQC', True)

        ## did the user provide the wind in vector form?
        if self.wdir is not None:
            self.wdir[self.wdir == self.missing] = ma.masked
            self.wspd[self.wspd == self.missing] = ma.masked
            self.wdir[self.wspd.mask] = ma.masked
            self.wspd[self.wdir.mask] = ma.masked
            self.u, self.v = utils.vec2comp(self.wdir, self.wspd)

        ## did the user provide the wind in u,v form?
        elif self.u is not None:
            self.u[self.u == self.missing] = ma.masked
            self.v[self.v == self.missing] = ma.masked
            self.u[self.v.mask] = ma.masked
            self.v[self.u.mask] = ma.masked
            self.wdir, self.wspd = utils.comp2vec(self.u, self.v)

        ## check if any standard deviation data was supplied
        if self.tmp_stdev is not None:
            self.dew_stdev[self.dew_stdev == self.missing] = ma.masked
            self.tmp_stdev[self.tmp_stdev == self.missing] = ma.masked
            self.dew_stdev.set_fill_value(self.missing)
            self.tmp_stdev.set_fill_value(self.missing)

        if self.omeg is not None:
            ## get the omega data and turn into arrays
            self.omeg[self.omeg == self.missing] = ma.masked
        else:
            self.omeg = ma.masked_all(len(self.hght))

        # QC Checks on the arrays passed to the constructor.
        qc_tools.areProfileArrayLengthEqual(self)
       
        ## mask the missing values
        self.pres[self.pres == self.missing] = ma.masked
        self.hght[self.hght == self.missing] = ma.masked
        self.tmpc[self.tmpc == self.missing] = ma.masked
        self.dwpc[self.dwpc == self.missing] = ma.masked

        self.logp = np.log10(self.pres.copy())
        self.vtmp = thermo.virtemp( self.pres, self.tmpc, self.dwpc )
        idx = np.ma.where(self.pres > 0)[0]
        self.vtmp[self.dwpc.mask[idx]] = self.tmpc[self.dwpc.mask[idx]] # Masking any virtual temperature 

        ## get the index of the top and bottom of the profile
        self.sfc = self.get_sfc()
        self.top = self.get_top()

        if self.strictQC is True:
            self.checkDataIntegrity()

        ## generate the wetbulb profile
        self.wetbulb = self.get_wetbulb_profile()
        ## generate theta-e profile
        self.thetae = self.get_thetae_profile()
        ## generate theta profile
        self.theta = self.get_theta_profile()
        ## generate water vapor mixing ratio profile
        self.wvmr = self.get_wvmr_profile()
        ## generate rh profile
        self.relh = self.get_rh_profile()
def plot_wof(prof, members, figname, xlat, xlon, idateobj, vdateobj, **kwargs):
    #    '''
    #    Plots SHARPpy SPC window as .png
    #
    #    Parameters
    #    ----------
    #    prof : a Profile Object from sharppy.sharptab.profile
    #
    #    kwargs
    #    ------
    #    parcel_type: Parcel choice for plotting. 'sfc','ml','mu','fcst' Default is 'ml'
    #    filename: Filename as a string. Default is 'sounding.png'
    #    logo: Logo for upper-left portion of the skew-t. Default is 'None' and does not plot a logo.
    #    logo_dxdy: Size of logo. Actual dimensions are dT and dp as it is plotted on the skewT. Default is (20,13) This worked for a 489x132 pixel image.
    #    '''
    #kwargs
    parcel_type = kwargs.get('parcel_type', 'ml')
    xpts = kwargs.get('x_pts')
    ypts = kwargs.get('y_pts')

    #Figure User Input

    p_grid_labels = [
        '1000', '', '', '850', '', '', '700', '', '', '', '500', '', '', '',
        '300', '', '200', '', '100'
    ]  #labels for the pressure ticks. Standard.
    p_grid = [1000, 850, 700, 500, 300, 200,
              100]  #where horizontal lines go across the skew-T

    my_dpi = 55  #dots per inch for the plot. This is a pretty hi-res image.

    pmax = 1050  #lowest pressure on the skew-T
    pmin = 100  #highest pressure on the skew-T
    dp = -10  #pressure spacing for creating skew-T background lines

    presvals = np.arange(
        int(pmax),
        int(pmin) + dp,
        dp)  #pressure values used for creating skew-T background lines

    # Colors for wind speed bars and hodograph
    hgt_list_bar = [0, 1000, 3000, 6000, 9000, 20000]
    hgt_list_hodo = [0, 1000, 3000, 6000, 9000, 10000]

    hodo_color = [
        cb_colors.orange6, cb_colors.green6, cb_colors.blue6,
        cb_colors.purple6, cb_colors.red6
    ]
    hodo_label = ['0-1km', '1-3km', '3-6km', '6-9km', '9-10km']

    #convoluted mess to get the title to be aligned how I wanted. This should be changed for others...
    spaces = 10  #22
    #   title_text_1 = '' #site + ' ' + dt.strftime('%Y/%m/%d %H:%M UTC ' + data_type)
    #   title_text_3 = 'Sounding Powered by SHARPpy'
    sharptext = 'Sounding Powered by SHARPpy'
    #   title_text_2 = title_text_3 = ''
    title_text_3 = 'WoFS Sounding {}N, {}W'.format(
        xlat, xlon) + (' ' * spaces) + 'Init: {}     Valid: {}'.format(
            idateobj.strftime('%Y-%m-%d %H%M UTC'),
            vdateobj.strftime('%Y-%m-%d %H%M UTC'))
    #xlat, xlon, initdate, validdate
    title_text = title_text_3  #title_text_1 + (' '*spaces) +title_text_2 + (' '*spaces) + title_text_3

    #Figures out where at which height the sounding reached in the list of h_new
    #Then interpolates pressure to height levels
    h_new = [0, 1000, 3000, 6000, 9000, 12000, 15000]

    for i in range(len(h_new)):
        if np.max(prof.hght) > h_new[i]:
            index = i
    h_new_labels = ['0 km', '1 km', '3 km', '6 km', '9 km', '12 km', '15 km']
    h_new_labels = h_new_labels[0:index + 1]
    #p_interped_func = interpolate.interp1d(prof.hght, prof.pres)

    p_interped = sharptab.interp.pres(
        prof, sharptab.interp.to_msl(prof, h_new[0:index + 1]))

    #Thin out the winds for better plotting (significant level data points seem to bunch together to closely
    minimum_separation = 250.  #minimum spacing between wind barbs (meters)
    h_barb = np.array(prof.hght).tolist()
    p_barb = np.array(prof.pres).tolist()
    spd_barb = np.array(prof.wspd).tolist()
    direc_barb = np.array(prof.wdir).tolist()

    #adds units to our newly created pressure, speed, and direction arrays for wind barb plotting
    #p_barb = p_barb * units.mbar
    #spd_barb = spd_barb * units.knot
    #direc_barb = direc_barb * units.deg

    # Convert wind speed and direction to components
    #u, v = get_wind_components(prof.wspd * units.knot, prof.wdir * units.deg)
    u, v = utils.vec2comp(prof.wdir, prof.wspd)
    u_barb, v_barb = utils.vec2comp(prof.wdir, prof.wspd)

    #SELECT PARCEL AND GET PARCEL DATA FROM SPC_UTILS
    sfcpcl = prof.sfcpcl  #params.parcelx( prof, flag=1 )
    fcstpcl = prof.fcstpcl  #params.parcelx( prof, flag=2)
    mupcl = prof.mupcl  #params.parcelx( prof, flag=3 )
    mlpcl = prof.mlpcl  #params.parcelx( prof, flag=4 )
    if parcel_type == 'sfc':
        pcl = sfcpcl
        pcl_box_level = -0.065
        pcl_type = 1
    elif parcel_type == 'fcst':
        pcl = fcstpcl
        pcl_box_level = -0.0875
        pcl_type = 2
    elif parcel_type == 'mu':
        pcl = mupcl
        pcl_box_level = -0.1325
        pcl_type = 4
    elif parcel_type == 'ml':
        pcl = mlpcl
        pcl_box_level = -0.11
        pcl_type = 3
    else:
        print(
            "ERROR! Select 'sfc', 'fcst', 'mu', or 'ml' for parcel type. (plot_spc(prof,parcel_type)"
        )
        print("Defaulting to surface parcel...")
        pcl = sfcpcl
        pcl_box_level = -0.065

#PLOTTING *************************************************************************************************************

#Create full figure
    fig = plt.figure(figsize=(1180 / my_dpi, 800 / my_dpi),
                     dpi=my_dpi,
                     frameon=False)

    #SKEW T ***************************************************
    ax = fig.add_subplot(111, projection='skewx',
                         facecolor="w")  #skewed x-axis

    # plot dashed temperature lines
    ax.xaxis.grid(color='k',
                  linestyle='--',
                  dashes=(3, 3),
                  alpha=0.5,
                  zorder=0)

    # plot the moist-adiabats
    for temp in np.arange(-10, 45, 5):
        tw = []
        for pres in presvals:
            tw.append(thermo.wetlift(1050., temp, pres))
        ax.semilogy(tw,
                    presvals,
                    color=cb_colors.purple6,
                    linestyle='--',
                    dashes=(5, 2),
                    alpha=.3)  #cb_colors.purple6

# plot the dry adiabats
    for t in np.arange(-50, 80, 20):
        thetas = ((t + thermo.ZEROCNK) / (np.power(
            (1000. / presvals), thermo.ROCP))) - thermo.ZEROCNK
        ax.semilogy(thetas, presvals, 'k', alpha=.3)

#plot mixing ratio lines
    mixing_ratio_list = range(6, 36, 4)
    for mr in mixing_ratio_list:
        plt.plot((thermo.temp_at_mixrat(mr, 1050) - 273,
                  thermo.temp_at_mixrat(mr, 600) - 273), (1050, 600),
                 'g-',
                 lw=1.0,
                 zorder=3,
                 alpha=0.6)
        ax.annotate(str(mr),
                    xy=((thermo.temp_at_mixrat(mr, 600) - 273), (600 - 3)),
                    xytext=((thermo.temp_at_mixrat(mr, 600) - 273), (600 - 3)),
                    ha='center',
                    color='g',
                    family='sans-serif',
                    weight='bold',
                    zorder=3,
                    fontsize=10,
                    alpha=0.6)

#plot horizontal lines at standard pressure levels
    for p_loc in p_grid:
        ax.axhline(y=p_loc, ls='-', c='k', alpha=0.5, linewidth=1.5, zorder=3)

# PLOT THE DATA ON THE SKEW-T

# Plot the data using normal plotting functions, in this case using log scaling in Y, as dicatated by the typical meteorological plot

    ax.semilogy(prof.wetbulb,
                prof.pres,
                c="c",
                linestyle='-',
                lw=1,
                alpha=1.0,
                zorder=3)  # Plot the wetbulb profile
    ax.annotate(str(int(round(thermo.ctof(prof.wetbulb[prof.sfc])))),
                xy=(prof.wetbulb[prof.sfc], prof.pres[prof.sfc] + 30),
                xytext=(prof.wetbulb[prof.sfc], prof.pres[prof.sfc] + 30),
                ha='left',
                color="c",
                family='sans-serif',
                weight='normal',
                zorder=7,
                fontsize=12,
                alpha=1.0)  # annotate the sfc wetbulb in F
    ax.semilogy(prof.dwpc,
                prof.pres,
                c=cb_colors.blue6,
                linestyle='-',
                lw=3,
                zorder=3)  # plot the dewpoint profile
    ax.annotate(str(int(round(thermo.ctof(prof.dwpc[prof.sfc])))),
                xy=(prof.dwpc[prof.sfc], prof.pres[prof.sfc] + 30),
                xytext=(prof.dwpc[prof.sfc], prof.pres[prof.sfc] + 30),
                ha='left',
                color=cb_colors.blue6,
                family='sans-serif',
                weight='bold',
                zorder=7,
                fontsize=12,
                alpha=1.0)  # annotate the sfc dewpoint in F
    ax.semilogy(prof.tmpc,
                prof.pres,
                c=cb_colors.orange6,
                linestyle='-',
                lw=3,
                zorder=3)  # Plot the temperature profile
    ax.semilogy(prof.vtmp,
                prof.pres,
                c=cb_colors.orange6,
                linestyle='--',
                lw=3,
                zorder=3)  # Plot the temperature profile
    ax.annotate(str(int(round(thermo.ctof(prof.tmpc[prof.sfc])))),
                xy=(prof.tmpc[prof.sfc], prof.pres[prof.sfc] + 30),
                xytext=(prof.tmpc[prof.sfc], prof.pres[prof.sfc] + 30),
                ha='left',
                color=cb_colors.orange6,
                family='sans-serif',
                weight='bold',
                zorder=7,
                fontsize=12,
                alpha=1.0)  # annotate the sfc temp in F
    ax.semilogy(pcl.ttrace,
                pcl.ptrace,
                c=cb_colors.gray6,
                linestyle='--',
                dashes=(3, 3),
                lw=1.5,
                alpha=1.0,
                zorder=3)  # plot the parcel trace

    #member_cape = []
    if members is not None:
        #   print( "Plotting members...")
        for m_idx in range(len(members['tmpc'])):
            ax.semilogy(members['dwpc'][m_idx],
                        members['pres'][m_idx],
                        c=cb_colors.blue6,
                        linestyle='-',
                        lw=1.,
                        zorder=1,
                        alpha=.6)  # plot the dewpoint profile
            ax.semilogy(members['tmpc'][m_idx],
                        members['pres'][m_idx],
                        c=cb_colors.orange6,
                        linestyle='-',
                        lw=1.,
                        zorder=1,
                        alpha=.6)  # Plot the temperature profile
# set label and tick marks for pressure and temperature
    ax.xaxis.set_major_locator(plt.MultipleLocator(10))
    ax.set_xticks(np.arange(-100, 60, 10))
    ax.set_xticklabels([str(i) for i in np.arange(-100, 60, 10)],
                       color=cb_colors.gray7,
                       fontsize=12)
    ax.set_xlim(-50, 50)
    ax.yaxis.set_major_formatter(plt.ScalarFormatter())
    ax.set_yticks(np.linspace(1000, 100, 19))
    ax.set_yticklabels(p_grid_labels, color=cb_colors.gray7, fontsize=12)
    ax.set_ylim(1050, 100)

    #plot the title text
    plt.text(0.05,
             0.97,
             title_text,
             fontsize=15,
             color=cb_colors.gray8,
             weight='bold',
             ha='left',
             transform=fig.transFigure)
    #x_hodo.annotate(sharptext, xy=(0.95, 0.95), xytext=(0.95, 0.95),xycoords='axes fraction',textcoords='axes fraction',ha='center', va='bottom', color=cb_colors.gray7, family='sans-serif', weight='bold', zorder=3,fontsize=14)
    plt.text(0.8,
             0.97,
             sharptext,
             fontsize=15,
             color=cb_colors.gray8,
             weight='bold',
             ha='left',
             transform=fig.transFigure)
    #adjust the skew-T plot to make room for the rest of the figures. This was important to make everything line up.
    plt.subplots_adjust(left=0.05, right=0.55, top=0.96, bottom=0.15)

    #Plot the height labels on the left axis
    ax2 = ax.twinx(
    )  #makes a twin of the skew-T subplot that's not skewed at 45 degrees
    plt.yscale('log', nonposy='clip')
    plt.yticks(p_interped, h_new_labels, color=cb_colors.green4, ha='left')
    ax2.yaxis.tick_left()
    ax2.tick_params(direction='in',
                    pad=-15,
                    axis='both',
                    which='major',
                    colors=cb_colors.green4,
                    length=10,
                    width=1.5)
    ax2.set_yticklabels(h_new_labels,
                        fontsize=12,
                        weight='bold',
                        color=cb_colors.green4)

    x = np.random.uniform(0.0, 10.0, 15)
    y = np.random.uniform(0.0, 10.0, 15)

    # Plot LCL and LFC levels
    plt.plot((38, 42), (pcl.lfcpres, pcl.lfcpres),
             c="darkgreen",
             lw=2.0,
             zorder=3)
    ax2.annotate('LFC',
                 xy=(40, pcl.lfcpres),
                 xytext=(40, pcl.lfcpres),
                 ha='center',
                 va='bottom',
                 color=cb_colors.green5,
                 family='sans-serif',
                 weight='bold',
                 zorder=3,
                 fontsize=12)
    plt.plot((38, 42), (pcl.lclpres, pcl.lclpres),
             c="r",
             linestyle='-',
             lw=2.0,
             zorder=3)
    ax2.annotate('LCL',
                 xy=(40, pcl.lclpres + 5.),
                 xytext=(40, pcl.lclpres + 5.),
                 ha='center',
                 va='top',
                 color=cb_colors.red5,
                 family='sans-serif',
                 weight='bold',
                 zorder=3,
                 fontsize=12)
    plt.plot((38, 42), (pcl.elpres, pcl.elpres),
             c="m",
             linestyle='-',
             lw=2.0,
             zorder=3)
    ax2.annotate('EL',
                 xy=(40, pcl.elpres),
                 xytext=(40, pcl.elpres),
                 ha='center',
                 va='bottom',
                 color=cb_colors.purple5,
                 family='sans-serif',
                 weight='bold',
                 zorder=3,
                 fontsize=12)

    # Plot Eff Inflow Layer
    eff_inflow = (prof.ebottom, prof.etop)
    eff_inflow_top = sharptab.interp.to_agl(
        prof, sharptab.interp.hght(prof, eff_inflow[1]))
    eff_inflow_bottom = sharptab.interp.to_agl(
        prof, sharptab.interp.hght(prof, eff_inflow[0]))
    bunkers = prof.srwind
    effective_srh = prof.right_esrh
    plt.plot((-25, -20), (eff_inflow[0], eff_inflow[0]),
             c=cb_colors.red5,
             linestyle='-',
             lw=1.75,
             zorder=3)
    plt.plot((-25, -20), (eff_inflow[1], eff_inflow[1]),
             c=cb_colors.red5,
             linestyle='-',
             lw=1.75,
             zorder=3)
    plt.plot((-22.5, -22.5), (eff_inflow[0], eff_inflow[1]),
             c=cb_colors.red5,
             linestyle='-',
             lw=1.75,
             zorder=3)
    try:
        plt.annotate(str(int(eff_inflow_bottom)) + 'm  ',
                     xy=(-25, eff_inflow[0]),
                     xytext=(-25, eff_inflow[0]),
                     ha='right',
                     va='center',
                     color=cb_colors.red5,
                     zorder=3,
                     fontsize=12,
                     weight='bold')
        plt.annotate(str(int(eff_inflow_top)) + 'm  ',
                     xy=(-25, eff_inflow[1]),
                     xytext=(-25, eff_inflow[1]),
                     ha='right',
                     va='center',
                     color=cb_colors.red5,
                     zorder=3,
                     fontsize=12,
                     weight='bold')
        plt.annotate(str(int(effective_srh[0])) + ' m$^2$/s$^2$',
                     xy=(-22.5, eff_inflow[1] - 10),
                     xytext=(-22.5, eff_inflow[1] - 10),
                     ha='center',
                     va='bottom',
                     color=cb_colors.red5,
                     zorder=3,
                     fontsize=12,
                     weight='bold')
    except:
        print("NO EFF INFLOW")

# PLOT WINDBARBS
    p_barb = np.asarray(p_barb)
    pidx = idx = np.where(np.asarray(p_barb) >= 100.)[0]
    wind_barbs = ax2.barbs(55 * np.ones(len(p_barb[idx])),
                           p_barb[idx],
                           u_barb[idx],
                           v_barb[idx],
                           barbcolor=cb_colors.gray7,
                           flagcolor='None',
                           zorder=10,
                           lw=1.0,
                           length=7)
    wind_barbs.set_clip_on(False)

    ax2.invert_yaxis()
    ax2.set_xlim(-50, 50)
    ax2.set_ylim(1050, 100)

    spd_barb = np.asarray(spd_barb)

    # Create hodograph ********************************************************************************************
    ax_hod = fig.add_axes([0.60, 0.45, 0.38, 0.475],
                          frameon=False)  #, facecolor='k')

    # Set the characteristics of the tick marks
    for tick in ax_hod.xaxis.get_major_ticks():
        tick.label.set_fontsize(12)
        tick.label.set_color(cb_colors.gray7)
        tick.label.set_weight('bold')
    for tick in ax_hod.yaxis.get_major_ticks():
        tick.label.set_fontsize(12)
        tick.label.set_color(cb_colors.gray7)
        tick.label.set_weight('bold')
    for i in range(10, 90, 10):

        # Draw the range rings around the hodograph.
        circle = plt.Circle((0, 0),
                            i,
                            linestyle='--',
                            color='k',
                            alpha=.3,
                            fill=False)
        ax_hod.add_artist(circle)

# Set the tick parameters to displace the tick labels from the hodograph axes
    ax_hod.tick_params(axis='x',
                       which='major',
                       labelsize=10,
                       color=cb_colors.gray7,
                       pad=-235,
                       length=0)
    ax_hod.tick_params(axis='y',
                       which='major',
                       labelsize=10,
                       color=cb_colors.gray7,
                       pad=-315,
                       length=0)

    # Plot the hodograph axes
    ax_hod.axvline(0, color=cb_colors.gray7, linestyle='-', linewidth=2.)
    ax_hod.axhline(0, color=cb_colors.gray7, linestyle='-', linewidth=2.)
    ax_hod.set_yticks(range(-60, 65, 10))
    ax_hod.set_xticks(range(-70, 75, 10))
    hod_yticklabels = [str(abs(i)) for i in range(-60, 65, 10)]
    #hod_yticklabels[len(hod_yticklabels)/2] = ''
    hod_xticklabels = [str(abs(i)) for i in range(-70, 75, 10)]
    #hod_xticklabels[len(hod_xticklabels)/2] = ''
    ax_hod.set_yticklabels(hod_yticklabels,
                           fontsize=12,
                           weight='bold',
                           color=cb_colors.gray7)
    ax_hod.set_xticklabels(hod_xticklabels,
                           fontsize=12,
                           weight='bold',
                           color=cb_colors.gray7)

    # Plot the hodograph using the color scheme for different layers (0-3, 3-6, etc.)
    bounds = [0, 1000, 3000, 6000, 9000, 12000]
    for i in range(1, len(bounds), 1):
        subset_idxs = np.where(
            (prof.hght <= sharptab.interp.to_msl(prof, bounds[i]))
            & (prof.hght >= sharptab.interp.to_msl(prof, bounds[i - 1])))
        subset_hghts = np.ma.concatenate(
            ([sharptab.interp.to_msl(prof, bounds[i - 1])],
             prof.hght[subset_idxs], [sharptab.interp.to_msl(prof,
                                                             bounds[i])]))
        u, v = sharptab.interp.components(
            prof, sharptab.interp.pres(prof, subset_hghts))
        ax_hod.plot(u,
                    v,
                    c=hodo_color[i - 1],
                    linewidth=2.5,
                    label=hodo_label[i - 1],
                    zorder=3)

    if members is not None:
        for mprof in members['member_profs']:
            for i in range(1, len(bounds), 1):
                subset_idxs = np.where(
                    (mprof.hght <= sharptab.interp.to_msl(mprof, bounds[i]))
                    & (mprof.hght >= sharptab.interp.to_msl(
                        mprof, bounds[i - 1])))
                subset_hghts = np.ma.concatenate(
                    ([sharptab.interp.to_msl(mprof, bounds[i - 1])
                      ], mprof.hght[subset_idxs],
                     [sharptab.interp.to_msl(mprof, bounds[i])]))
                u, v = sharptab.interp.components(
                    mprof, sharptab.interp.pres(mprof, subset_hghts))
                ax_hod.plot(u,
                            v,
                            c=hodo_color[i - 1],
                            linewidth=1.25,
                            alpha=0.6,
                            label=hodo_label[i - 1],
                            zorder=1)

# Get the Bunkers storm motions and convert them to strings to plot
    bunkers = srwind = prof.srwind
    bunkers_rt = utils.comp2vec(bunkers[0], bunkers[1])
    bunkers_lf = utils.comp2vec(bunkers[2], bunkers[3])
    bunkers_rt_str = str(int(np.ma.around(bunkers_rt[0], 0))) + "/" + str(
        int(np.ma.around(bunkers_rt[1], 0)))
    bunkers_lf_str = str(int(np.ma.around(bunkers_lf[0], 0))) + "/" + str(
        int(np.ma.around(bunkers_lf[1], 0)))

    # Plot the effective inflow layer on the hodograph
    effubot, effvbot = sharptab.interp.components(prof, eff_inflow[0])
    effutop, effvtop = sharptab.interp.components(prof, eff_inflow[1])
    ax_hod.plot([effubot, srwind[0]], [effvbot, srwind[1]],
                c='c',
                linewidth=1.5)
    ax_hod.plot([effutop, srwind[0]], [effvtop, srwind[1]],
                c='c',
                linewidth=1.5)

    # Annotate where the Bunkers storm motion vectors are on the hodograph
    ax_hod.plot(srwind[0],
                srwind[1],
                marker='o',
                fillstyle='none',
                markeredgecolor=cb_colors.blue8,
                markeredgewidth=1.5,
                markersize=11)
    ax_hod.annotate(bunkers_rt_str + ' RM', (srwind[0] + 1.5, srwind[1] - 1.5),
                    fontsize=12,
                    va="top",
                    ha="left",
                    color='k',
                    weight='bold',
                    zorder=10)
    ax_hod.plot(srwind[2],
                srwind[3],
                marker='o',
                fillstyle='none',
                markeredgecolor=cb_colors.blue8,
                markeredgewidth=1.5,
                markersize=11)
    ax_hod.annotate(bunkers_lf_str + ' LM', (srwind[2] + 1.5, srwind[3] - 1.5),
                    fontsize=12,
                    va="top",
                    ha="left",
                    color='k',
                    weight='bold',
                    zorder=10)

    # Annotate where the Corfidi MBE vectors are on the hodograph
    corfidi = prof.upshear_downshear
    corfidi_up = utils.comp2vec(corfidi[0], corfidi[1])
    corfidi_dn = utils.comp2vec(corfidi[2], corfidi[3])
    c = 'k'  #'#0A74C6'
    ax_hod.plot(corfidi[0],
                corfidi[1],
                marker='o',
                fillstyle='none',
                markeredgecolor=c,
                markeredgewidth=1.5,
                markersize=9)
    ax_hod.annotate(str(int(corfidi_up[0])) + '/' + str(int(corfidi_up[1])) +
                    ' UP', (corfidi[0] + 1.5, corfidi[1] - 1.5),
                    fontsize=12,
                    va="top",
                    ha="left",
                    color=cb_colors.purple8,
                    weight='bold',
                    zorder=10)
    ax_hod.plot(corfidi[2],
                corfidi[3],
                marker='o',
                fillstyle='none',
                markeredgecolor=c,
                markeredgewidth=1.5,
                markersize=9)
    ax_hod.annotate(str(int(corfidi_dn[0])) + '/' + str(int(corfidi_dn[1])) +
                    ' DN', (corfidi[2] + 1.5, corfidi[3] - 1.5),
                    fontsize=12,
                    va="top",
                    ha="left",
                    color=cb_colors.purple8,
                    weight='bold',
                    zorder=10)

    # Get the cloud-layer mean wind
    mean_cloudlayer = winds.mean_wind(prof, pbot=pcl.lclpres, ptop=pcl.elpres)
    mean_cloudlayer_comp = utils.comp2vec(mean_cloudlayer[0],
                                          mean_cloudlayer[1])
    try:
        mean_cloudlayer_str = str(int(np.ma.around(
            mean_cloudlayer_comp[0], 0))) + "/" + str(
                int(np.ma.around(mean_cloudlayer_comp[1], 0)))
    except:
        mean_cloudlayer_str = 'M/M'

# Write the critical angle to the hodograph.
    if eff_inflow[0] == prof.pres[prof.sfc]:
        ax_hod.annotate('Critical Angle = ' + str(int(prof.critical_angle)),
                        (-65, -50),
                        fontsize=12,
                        va="bottom",
                        ha="left",
                        color=cb_colors.green6,
                        weight='bold',
                        zorder=10)

    ax_hod.set_xlim(-80, 80)
    ax_hod.set_ylim(-70, 70)

    #BELOW IS STUFF FOR BOXES/BORDERS ******************************************************************************

    ax3 = ax2.twinx()
    ax3.axes.get_xaxis().set_visible(False)
    ax3.axes.get_yaxis().set_visible(False)
    ax3.set_yticks([])
    ax3.set_yticklabels([])

    #Big Thick Box around Skew-T
    #box = ax3.add_patch(patches.Rectangle((-50, 0), 110.0, 1.0,fill=False,linewidth=2,edgecolor="w",zorder=3))
    #box.set_clip_on(False)

    #box around hodograph
    #box = ax_hod.add_patch(patches.Rectangle((-80., -70.), 160., 140.,fill=False,linewidth=2,edgecolor="w",zorder=4))
    #box.set_clip_on(False)

    inset_color = cb_colors.gray7

    #THICK TEXT BOX around Thermodynamics Text
    box = ax3.add_patch(
        patches.Rectangle((-58.0, -0.035),
                          54,
                          -0.12,
                          fill=False,
                          linewidth=2,
                          edgecolor=inset_color,
                          zorder=4))
    box.set_clip_on(False)

    #THICK TEXT BOX around Kinematics Text
    box = ax3.add_patch(
        patches.Rectangle((-3.0, -0.035),
                          60,
                          -0.12,
                          fill=False,
                          linewidth=2,
                          edgecolor=inset_color,
                          zorder=4))
    box.set_clip_on(False)

    #THICK TEXT BOX Around Dynamics Text
    #   box = ax3.add_patch(patches.Rectangle((9.0, -0.04), 60, -0.38,fill=False,linewidth=2,edgecolor=inset_color,zorder=4))
    #   box.set_clip_on(False)

    #THICK TEXT BOX Around SARS Text
    #   box = ax3.add_patch(patches.Rectangle((69.0, -0.04), 55, -0.38,fill=False,linewidth=2,edgecolor=inset_color,zorder=4))
    #   box.set_clip_on(False)

    #Thermodynamics
    #box = ax3.add_patch(patches.Rectangle((-55.0, -0.04), 64.0, -0.12,fill=False,linewidth=1,edgecolor=inset_color,zorder=4))
    #box.set_clip_on(False)
    #box = ax3.add_patch(patches.Rectangle((-55.0, -0.04), 64.0, -0.025,fill=False,linewidth=1,edgecolor=inset_color,zorder=4))
    #box.set_clip_on(False)

    # Write the parcel properties to the inset.
    #x_list = [0, 0.08, 0.17, 0.25, 0.33, 0.39, 0.47]
    x_list = np.array([0, 0.08, 0.17, 0.25, 0.33, 0.39, 0.47]) - 0.075
    y_list = [-0.045, -0.07, -0.0925, -0.115, -0.1375]
    A = [
        "SFC", prof.sfcpcl.bplus,
        int(prof.sfcpcl.bminus), prof.sfcpcl.lclhght, prof.sfcpcl.li5,
        prof.sfcpcl.lfchght, prof.sfcpcl.elhght
    ]
    #   B = ["FCST", prof.fcstpcl.bplus, int(prof.fcstpcl.bminus), prof.fcstpcl.lclhght, prof.fcstpcl.li5, prof.fcstpcl.lfchght, prof.fcstpcl.elhght]
    C = [
        "ML", prof.mlpcl.bplus,
        int(prof.mlpcl.bminus), prof.mlpcl.lclhght, prof.mlpcl.li5,
        prof.mlpcl.lfchght, prof.mlpcl.elhght
    ]
    D = [
        "MU", prof.mupcl.bplus,
        int(prof.mupcl.bminus), prof.mupcl.lclhght, prof.mupcl.li5,
        prof.mupcl.lfchght, prof.mupcl.elhght
    ]
    #mlcape = C[1]
    #print('mlcape',mlcape)
    data = np.array([["PCL", "CAPE", "CINH", "LCL", "LI", "LFC", "EL"],
                     [
                         str(int(np.ma.around(A[i], 0))) if
                         (type(A[i]) == np.float64) else str(A[i])
                         for i in range(len(A))
                     ],
                     [
                         str(int(np.ma.around(C[i], 0))) if
                         (type(C[i]) == np.float64) else str(C[i])
                         for i in range(len(C))
                     ],
                     [
                         str(int(np.ma.around(D[i], 0))) if
                         (type(D[i]) == np.float64) else str(D[i])
                         for i in range(len(D))
                     ]])

    for i in range(data.shape[0]):
        for j in range(data.shape[1]):
            ax2.annotate(data[i, j], (x_list[j], y_list[i]),
                         xycoords="axes fraction",
                         fontsize=12,
                         va="top",
                         ha="left",
                         color=cb_colors.gray7,
                         weight='bold')

# Draw a box around the selected parcel being shown in the Skew-T
    box = ax3.add_patch(
        patches.Rectangle((-57.7, pcl_box_level),
                          53.,
                          0.0225,
                          fill=False,
                          linewidth=1,
                          edgecolor=cb_colors.purple4,
                          zorder=4))
    box.set_clip_on(False)

    # Write the lapse rates to the inset.
    #box = ax3.add_patch(patches.Rectangle((-55.0, -0.305), 43.0, -0.115,fill=False,linewidth=1,edgecolor="w",zorder=4))
    #box.set_clip_on(False)
    x_list = [0.15, 0.16]
    y_list = np.arange(-0.315, -.40, -0.0225)
    data = np.array([[
        "0-3km AGL LR =",
        str(np.ma.around(prof.lapserate_3km, 1)) + " C/km"
    ], [
        "3-6km AGL LR =",
        str(np.ma.around(prof.lapserate_3_6km, 1)) + " C/km"
    ],
                     [
                         "850-500mb LR =",
                         str(np.ma.around(prof.lapserate_850_500, 1)) + " C/km"
                     ],
                     [
                         "700-500mb LR =",
                         str(np.ma.around(prof.lapserate_700_500, 1)) + " C/km"
                     ]])
    for i in range(data.shape[0]):
        for j in range(data.shape[1]):
            if (j % 2 == 0):
                ax2.annotate(data[i, j], (x_list[j], y_list[i]),
                             xycoords="axes fraction",
                             fontsize=12,
                             va="top",
                             ha="right",
                             color='k',
                             weight='bold')
            else:
                ax2.annotate(data[i, j], (x_list[j], y_list[i]),
                             xycoords="axes fraction",
                             fontsize=12,
                             va="top",
                             ha="left",
                             color='k',
                             weight='bold')

#Severe Indices
#box = ax3.add_patch(patches.Rectangle((-12.0, -0.305), 21.0, -0.115,fill=False,linewidth=1,edgecolor="w",zorder=4))
#box.set_clip_on(False)
    x_list = [0.52, 0.53]
    y_list = np.arange(-0.315, -.40, -0.0225)

    # This looks lifted from the Profile class.  Don't need this.
    sfc = prof.pres[prof.sfc]
    p6km = sharptab.interp.pres(prof, sharptab.interp.to_msl(prof, 6000.))
    p8km = sharptab.interp.pres(prof, sharptab.interp.to_msl(prof, 8000.))
    #   ebot_hght = interp.to_agl(prof, interp.hght(prof, eff_inflow[0]))
    #   etop_hght = interp.to_agl(prof, interp.hght(prof, eff_inflow[1]))

    # Mean winds
    #mean_1km = winds.mean_wind(prof, pbot=sfc, ptop=p1km)
    mean_1km_comp = prof.mean_1km  #utils.comp2vec(mean_1km[0],mean_1km[1])
    #mean_3km = winds.mean_wind(prof, pbot=sfc, ptop=p3km)
    mean_3km_comp = prof.mean_3km  #utils.comp2vec(mean_3km[0],mean_3km[1])
    #mean_eff = winds.mean_wind(prof, pbot=eff_inflow[0], ptop=eff_inflow[1])
    mean_eff_comp = utils.comp2vec(
        *prof.mean_eff)  #utils.comp2vec(mean_eff[0],mean_eff[1])

    if type(eff_inflow[0]) != np.float64:
        mean_eff_comp = ['---', '--']
    mean_6km = winds.mean_wind(prof, pbot=sfc, ptop=p6km)
    mean_6km_comp = prof.mean_6km  #utils.comp2vec(mean_6km[0],mean_6km[1])
    mean_8km = winds.mean_wind(prof, pbot=sfc, ptop=p8km)
    mean_8km_comp = prof.mean_8km  #utils.comp2vec(mean_8km[0],mean_8km[1])
    mean_cloudlayer = winds.mean_wind(prof, pbot=pcl.lclpres, ptop=pcl.elpres)
    mean_cloudlayer_comp = prof.mean_lcl_el  #utils.comp2vec(mean_cloudlayer[0],mean_cloudlayer[1])
    mean_ebwd = winds.mean_wind(prof, pbot=eff_inflow[0], ptop=eff_inflow[1])
    mean_ebwd_comp = utils.comp2vec(
        *prof.mean_ebw)  #utils.comp2vec(mean_ebwd[0],mean_ebwd[1])

    if type(eff_inflow[0]) != np.float64:
        mean_ebwd_comp = ['---', '--']

    bunkers_rt = utils.comp2vec(bunkers[0], bunkers[1])
    bunkers_lf = utils.comp2vec(bunkers[2], bunkers[3])
    corfidi_up = utils.comp2vec(corfidi[0], corfidi[1])
    corfidi_dn = utils.comp2vec(corfidi[2], corfidi[3])
    srw_1km = prof.srw_1km  #utils.comp2vec(*winds.sr_wind(prof, pbot=sfc, ptop=p1km, stu=prof.srwind[0], stv=prof.srwind[1]))
    srw_3km = prof.srw_3km  #utils.comp2vec(*winds.sr_wind(prof, pbot=sfc, ptop=p3km, stu=prof.srwind[0], stv=prof.srwind[1]))
    srw_eff = utils.comp2vec(
        *prof.srw_eff
    )  #utils.comp2vec(*winds.sr_wind(prof, pbot=eff_inflow[0], ptop=eff_inflow[1], stu=prof.srwind[0], stv=prof.srwind[1]))
    if type(eff_inflow[0]) != np.float64:
        srw_eff = ['---', '--']
    srw_6km = prof.srw_6km  #utils.comp2vec(*winds.sr_wind(prof, pbot=sfc, ptop=p6km, stu=prof.srwind[0], stv=prof.srwind[1]))
    srw_8km = prof.srw_8km  #utils.comp2vec(*winds.sr_wind(prof, pbot=sfc, ptop=p8km, stu=prof.srwind[0], stv=prof.srwind[1]))
    srw_cloudlayer = prof.srw_lcl_el  #utils.comp2vec(*winds.sr_wind(prof, pbot=pcl.lclpres, ptop=pcl.elpres, stu=prof.srwind[0], stv=prof.srwind[1]))
    srw_ebwd = utils.comp2vec(
        *prof.srw_ebw
    )  #utils.comp2vec(*winds.sr_wind(prof, pbot=eff_inflow[0], ptop=eff_inflow[1], stu=prof.srwind[0], stv=prof.srwind[1]))
    if type(eff_inflow[0]) != np.float64:
        srw_ebwd = ['---', '--']
    srw_46km = utils.comp2vec(
        *prof.srw_4_6km
    )  #utils.comp2vec(*winds.sr_wind(prof, pbot=p4km, ptop=p6km, stu=prof.srwind[0], stv=prof.srwind[1]))
    sfc_8km_shear = prof.sfc_8km_shear  #winds.wind_shear(prof, pbot=sfc, ptop=p8km)
    sfc_6km_shear = prof.sfc_6km_shear  #winds.wind_shear(prof, pbot=sfc, ptop=p6km)
    sfc_3km_shear = prof.sfc_3km_shear  #winds.wind_shear(prof, pbot=sfc, ptop=p3km)
    sfc_1km_shear = prof.sfc_1km_shear  #winds.wind_shear(prof, pbot=sfc, ptop=p1km)
    effective_shear = prof.eff_shear  #winds.wind_shear(prof, pbot=eff_inflow[0], ptop=etop_hght)
    cloudlayer_shear = prof.lcl_el_shear  #winds.wind_shear(prof,pbot= pcl.lclpres, ptop=pcl.elpres)
    srh3km = prof.srh3km  #winds.helicity(prof, 0, 3000., stu = bunkers[0], stv = bunkers[1])
    srh1km = prof.srh1km  #winds.helicity(prof, 0, 1000., stu = bunkers[0], stv = bunkers[1])
    stp_fixed = prof.stp_fixed  #params.stp_fixed(pcl.bplus, pcl.lclhght, srh1km[0], utils.comp2vec(sfc_6km_shear[0], sfc_6km_shear[1])[1])
    ship = prof.ship
    effective_srh = prof.right_esrh  #winds.helicity(prof, ebot_hght, etop_hght, stu = bunkers[0], stv = bunkers[1])
    ebwd = prof.ebwd  #winds.wind_shear(prof, pbot=eff_inflow[0], ptop=eff_inflow[1])
    ebwspd = prof.ebwspd
    scp = prof.right_scp
    stp_cin = prof.stp_cin  #params.stp_cin(pcl.bplus, effective_srh[0], ebwspd, pcl.lclhght, pcl.bminus)
    brn_shear = pcl.brnshear

    # Draw the SCP, STP, SHIP indices to the plot
    # TODO: Include the color variations on this to denote intensity of the index.

    #   if prof.stp_fixed is ma.masked:
    #      temp_stp_fixed = '0.0'
    #   else:
    #      temp_stp_fixed = str(round(prof.stp_fixed,1))

    #   if prof.stp_cin is ma.masked:
    #      temp_stp_cin = '0.0'
    #   else:
    #      temp_stp_cin = str(round(prof.stp_cin,1))

    #   if prof.right_scp is ma.masked:
    #      temp_right_scp = '0.0'
    #   else:
    #      temp_right_scp = str(round(prof.right_scp,1))

    #   data = np.array([["Supercell =",temp_right_scp],
    #                       ["STP (cin) =",temp_stp_cin],
    #                       ["STP (fix) =",temp_stp_fixed]])

    data = np.array([["Supercell =",
                      str(np.ma.around(prof.right_scp, 1))],
                     ["STP (cin) =",
                      str(np.ma.around(prof.stp_cin, 1))],
                     ["STP (fix) =",
                      str(np.ma.around(prof.stp_fixed, 1))]])

    data = np.array([["Supercell =",
                      str(np.ma.around(prof.right_scp, 1))],
                     ["STP (cin) =",
                      str(np.ma.around(prof.stp_cin, 1))],
                     ["STP (fix) =",
                      str(np.ma.around(prof.stp_fixed, 1))],
                     ["SHIP =", str(np.ma.around(prof.ship, 1))]])
    '''
   for i in range(data.shape[0]):
      for j in range(data.shape[1]):
         d = float(data[i,1])
         if i == 0:
            if d >= 19.95:
               c = MAGENTA
            elif d >= 11.95:
               c = RED
            elif d >= 1.95:
               c = YELLOW
            elif d >= .45:
               c = WHITE
            elif d >= -.45:
               c = LBROWN
            elif d < -0.45:
               c = CYAN
         elif i == 1:
            if d >= 8:
               c = MAGENTA
            elif d >= 4:
               c = RED
            elif d >= 2:
               c = YELLOW
            elif d >= 1:
               c = WHITE
            elif d >= .5:
               c = LBROWN
            elif d < .5:
               c = DBROWN
               stpCinColor = c
         elif i == 2:
            if d >= 7:
               c = MAGENTA
            elif d >= 5:
               c = RED
            elif d >= 2:
               c = YELLOW
            elif d >= 1:
               c = WHITE
            elif d >= 0.5:
               c = LBROWN
            else:
               c = DBROWN
         elif i == 3:
            if d >= 5:
               c = MAGENTA
            elif d >= 2:
               c = RED
            elif d >= 1:
               c = YELLOW
            elif d >= .5:
               c = WHITE
            else:
               c = DBROWN
         if (j % 2 == 0):
            ax2.annotate(data[i,j], (x_list[j], y_list[i]), xycoords="axes fraction",
                         fontsize=12, va="top", ha="right", color=c, weight='bold')
         else:
            ax2.annotate(data[i,j], (x_list[j], y_list[i]), xycoords="axes fraction",
                       fontsize=12, va="top", ha="left", color=cb_colors.gray7, weight='bold')
   '''
    # Draw the kinematic inset on the plot
    #   box = ax3.add_patch(patches.Rectangle((9.0, -0.04), 60.0, -0.025,fill=False,linewidth=1,edgecolor="w",zorder=4))
    #   box.set_clip_on(False)
    x_list = np.array([0.60, 0.84, 0.97, 1.08, 1.18]) - 0.12
    y_list = [-0.045]
    y_list.extend(np.arange(-.07, -0.12, -.0225).tolist())
    y_list.extend(np.arange(-.145, -0.22, -.0225).tolist())
    y_list.extend(np.arange(-.2425, -0.27, -.0225).tolist())
    y_list.extend(np.arange(-0.295, -.4, -0.0225).tolist())

    A = [
        "SFC-1km", srh1km[0],
        utils.comp2vec(sfc_1km_shear[0], sfc_1km_shear[1])[1]
    ]
    A2 = [mean_1km_comp, srw_1km]
    B = [
        "SFC-3km", srh3km[0],
        utils.comp2vec(sfc_3km_shear[0], sfc_3km_shear[1])[1]
    ]
    B2 = [mean_3km_comp, srw_3km]
    C = [
        "Eff Inflow Layer", effective_srh[0],
        utils.comp2vec(effective_shear[0], effective_shear[1])[1]
    ]
    C2 = [mean_eff_comp, srw_eff]
    #   D = ["SFC-6km", "", utils.comp2vec(sfc_6km_shear[0],sfc_6km_shear[1])[1]]
    #   D2 = [mean_6km_comp, srw_6km]
    #   E = ["SFC-8km", "", utils.comp2vec(sfc_8km_shear[0],sfc_8km_shear[1])[1]]
    #   E2 = [mean_8km_comp, srw_8km]
    #   F = ["LCL-EL (CLoud Layer)", "", utils.comp2vec(cloudlayer_shear[0],cloudlayer_shear[1])[1]]
    #   F2 = [mean_cloudlayer_comp, srw_cloudlayer]
    #   G = ["Eff Shear (EBWD)", "", utils.comp2vec(ebwd[0],ebwd[1])[1]]
    #   G2 = [mean_ebwd_comp, srw_ebwd]
    #   H = ["BRN Shear (m2/s2)", "", brn_shear, "", ""]
    #   I = ["4-6km SR Wind", ""]
    #   I2 = [str(int(round(srw_46km[0],0)))+"/"+str(int(round(srw_46km[1],0)))]
    #   I3 = ["", ""]
    #   J = ["...Storm Motion Vectors...", "", "", "", ""]
    #   K = ["Bunkers Right", ""]
    #   K2 = [str(int(round(bunkers_rt[0],0)))+"/"+str(int(round(bunkers_rt[1],0)))]
    #   K3 = ["", ""]
    #   L = ["Bunkers Left", ""]
    #   L2 = [str(int(round(bunkers_lf[0],0)))+"/"+str(int(round(bunkers_lf[1],0)))]
    #   L3 = ["", ""]
    #   M = ["Corfidi Downshear", ""]
    #   M2 = [str(int(round(corfidi_dn[0],0)))+"/"+str(int(round(corfidi_dn[1],0)))]
    #   M3 = ["", ""]
    #   N = ["Corfidi Upshear", ""]
    #   N2 = [str(int(round(corfidi_up[0],0)))+"/"+str(int(round(corfidi_up[1],0)))]
    #   N3 = ["", ""]

    data = np.array([np.array(["", "SRH (m2/s2)", "Shear (kt)", "MnWind", "SRW"]),
                     np.array([ str(int(round(A[i],0))) if (type(A[i])== np.float64) else str(A[i]) for i in range(len(A)) ]+\
                        [ str(int(np.ma.around(A2[i][0],0)))+"/"+str(int(np.ma.around(A2[i][1],0))) if (type(A2[i][0])== np.ma.core.MaskedArray) else str(A2[i][0])+"/"+str(A2[i][1]) for i in range(len(A2)) ]),

                     np.array([ str(int(round(B[i],0))) if (type(B[i])== np.float64) else str(B[i]) for i in range(len(B)) ]+\
                         [ str(int(np.ma.around(B2[i][0],0)))+"/"+str(int(np.ma.around(B2[i][1],0))) if (type(B2[i][0])== np.ma.core.MaskedArray) else str(B2[i][0])+"/"+str(B2[i][1]) for i in range(len(B2)) ]),

                     np.array([ str(int(np.ma.around(C[i],0))) if (type(C[i])== np.float64) else str(C[i]) for i in range(len(C)) ]+\
                         [ str(int(np.ma.around(C2[i][0],0)))+"/"+str(int(np.ma.around(C2[i][1],0))) if (type(C2[i][0])== np.ma.core.MaskedArray) else str(C2[i][0])+"/"+str(C2[i][1]) for i in range(len(C2)) ])]) #,

    #                    np.array([ str(int(round(D[i],0))) if (type(D[i])== np.float64) else str(D[i]) for i in range(len(D)) ]+\
    #                        [ str(int(round(D2[i][0],0)))+"/"+str(int(round(D2[i][1],0))) if (type(D2[i][0])== np.ma.core.MaskedArray) else str(D2[i][0])+"/"+str(D2[i][1]) for i in range(len(D2)) ]),

    #                    np.array([ str(int(round(E[i],0))) if (type(E[i])== np.float64) else str(E[i]) for i in range(len(E)) ]+\
    #                        [ str(int(round(E2[i][0],0)))+"/"+str(int(round(E2[i][1],0))) if (type(E2[i][0])== np.ma.core.MaskedArray) else str(E2[i][0])+"/"+str(E2[i][1]) for i in range(len(E2)) ]),

    #                    np.array([ str(int(round(F[i],0))) if (type(F[i])== np.float64) else str(F[i]) for i in range(len(F)) ]+\
    #                        [ str(int(round(F2[i][0],0)))+"/"+str(int(round(F2[i][1],0))) if (type(F2[i][0])== np.ma.core.MaskedArray) else str(F2[i][0])+"/"+str(F2[i][1]) for i in range(len(F2)) ]),

    #                    np.array([ str(int(round(G[i],0))) if (type(G[i])== np.float64) else str(G[i]) for i in range(len(G)) ]+\
    #                        [ str(int(round(G2[i][0],0)))+"/"+str(int(round(G2[i][1],0))) if (type(G2[i][0])== np.ma.core.MaskedArray) else str(G2[i][0])+"/"+str(G2[i][1]) for i in range(len(G2)) ]),

    #                    np.array([ str(int(round(H[i],0))) if (type(H[i])== np.float64) else str(H[i]) for i in range(len(H)) ]),
    #                    np.array(I+I2+I3),
    #                    np.array(J),
    #                    np.array(K+K2+K3),
    #                    np.array(L+L2+L3),
    #                    np.array(M+M2+M3),
    #                    np.array(N+N2+N3)])
    #x_list = np.array([0, 0.08, 0.17, 0.25, 0.33, 0.39, 0.47])-0.05
    y_list = [-0.045, -0.07, -0.0925, -0.115, -0.1375]
    for i in range(data.shape[0]):
        for j in range(data[0].shape[0]):
            if j > 0:
                ax2.annotate(data[i][j], (x_list[j], y_list[i]),
                             xycoords="axes fraction",
                             fontsize=12,
                             va="top",
                             ha="right",
                             color=cb_colors.gray7,
                             weight='bold')
            else:
                ax2.annotate(data[i][j], (x_list[j], y_list[i]),
                             xycoords="axes fraction",
                             fontsize=12,
                             va="top",
                             ha="left",
                             color=cb_colors.gray7,
                             weight='bold')


#    wind_1km = utils.vec2comp(prof.wind1km[0], prof.wind1km[1])
#    wind_6km = utils.vec2comp(prof.wind6km[0], prof.wind6km[1])
#    wind_barbs = ax2.barbs(58, 2200, wind_1km[0], wind_1km[1], color='#AA0000', zorder=3, lw=1.25,length=9)
#    wind_barbs.set_clip_on(False)
#    wind_barbs = ax2.barbs(58, 2200, wind_6km[0], wind_6km[1], color='#0A74C6', zorder=3, lw=1.25,length=9)
#    wind_barbs.set_clip_on(False)
#    ax2.annotate("1km & 6km AGL\nWind Barbs", (1.08,-0.37), xycoords="axes fraction", fontsize=12, va="top", ha="center", color='#0A74C6', weight='bold')

# Draw the CAPE vs. SRH Scatter
#ax_EFSTP = fig.add_axes([0.74625, 0.0229, 0.20375, 0.2507], frameon=False)
#x_EFSTP = fig.add_axes([0.72625, 0.0229, 0.20375, 0.2507], frameon=False)
    ax_EFSTP = fig.add_axes([0.625, 0.05, 0.35, 0.3], frameon=False)

    for member_no, c in zip(
            np.arange(1, 19, 1),
            np.tile([
                cb_colors.orange6, cb_colors.orange6, cb_colors.green6,
                cb_colors.green6, cb_colors.purple6, cb_colors.purple6
            ], (3, 1))):
        memidx = [0, 10, 11, 12, 13, 14, 15, 16, 17, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        ax_EFSTP.scatter(xpts[memidx[member_no - 1], :, :].ravel(),
                         ypts[memidx[member_no - 1], :, :].ravel(),
                         color=c,
                         marker='o',
                         s=5,
                         alpha=0.7)
    ax_EFSTP.set_xticks(np.arange(0, 5001, 1000))
    ax_EFSTP.set_yticks(np.arange(0, 601, 100))
    #ax_EFSTP.set_xticklabels(np.arange(0,5001,1000),color='k',fontsize=12)
    #ax_EFSTP.set_yticklabels(np.arange(0,601,100),color='k',fontsize=12)
    ax_EFSTP.set_xlabel('MLCAPE', weight='bold', fontsize=14)
    ax_EFSTP.set_ylabel('0-1km SRH', weight='bold', fontsize=14)
    ax_EFSTP.tick_params(axis='both', labelsize=12, labelcolor='k')
    ax_EFSTP.set_xlim(-200, 5000)
    ax_EFSTP.set_ylim(-100, 600)
    #ax_EFSTP.tick_params(direction='in', axis='x', which='major', colors=cb_colors.gray4,length=0,width=1.5,size=12)#pad=-10
    #ax_EFSTP.tick_params(direction='in', axis='y', which='major', colors=cb_colors.gray4,length=0,width=1.5,size=12)#pad=-23
    ax_EFSTP.grid(color=cb_colors.gray4,
                  linestyle='--',
                  dashes=(3, 3),
                  alpha=0.75,
                  zorder=0,
                  linewidth=1.25)
    ax_EFSTP.text(0.8,
                  0.95,
                  'YSU',
                  color=cb_colors.orange6,
                  transform=ax_EFSTP.transAxes,
                  fontsize=16,
                  weight='bold')
    ax_EFSTP.text(0.8,
                  0.89,
                  'MYJ',
                  color=cb_colors.green6,
                  transform=ax_EFSTP.transAxes,
                  fontsize=16,
                  weight='bold')
    ax_EFSTP.text(0.8,
                  0.83,
                  'MYNN',
                  color=cb_colors.purple6,
                  transform=ax_EFSTP.transAxes,
                  fontsize=16,
                  weight='bold')
    box = ax_EFSTP.add_patch(
        patches.Rectangle((0, -1),
                          13,
                          13,
                          fill=False,
                          linewidth=2,
                          edgecolor=cb_colors.gray4,
                          zorder=10))
    box.set_clip_on(False)

    ax_EFSTP.annotate("0-1 km SRH vs. 100-mb MLCAPE", (0.5, 1.075),
                      xycoords="axes fraction",
                      fontsize=14,
                      va="center",
                      ha="center",
                      color=cb_colors.gray7,
                      weight='bold')
    ax_EFSTP.annotate("(9 km neighborhood)", (0.5, 1.025),
                      xycoords="axes fraction",
                      fontsize=12,
                      va="center",
                      ha="center",
                      color=cb_colors.gray7,
                      weight='bold')

    plt.savefig(figname, facecolor=fig.get_facecolor())  #, edgecolor=None)
Beispiel #33
0
def indices(prof, debug=False):

    # return a formatted-string list of stability and kinematic indices

    sfcpcl = params.parcelx(prof, flag=1)
    mupcl = params.parcelx(prof, flag=3)  # most unstable
    mlpcl = params.parcelx(prof, flag=4)  # 100 mb mean layer parcel

    pcl = mupcl
    sfc = prof.pres[prof.sfc]
    p3km = interp.pres(prof, interp.to_msl(prof, 3000.))
    p6km = interp.pres(prof, interp.to_msl(prof, 6000.))
    p1km = interp.pres(prof, interp.to_msl(prof, 1000.))
    mean_3km = winds.mean_wind(prof, pbot=sfc, ptop=p3km)
    sfc_6km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p6km)
    sfc_3km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p3km)
    sfc_1km_shear = winds.wind_shear(prof, pbot=sfc, ptop=p1km)
    #print "0-3 km Pressure-Weighted Mean Wind (kt):", utils.comp2vec(mean_3km[0], mean_3km[1])[1]
    #print "0-6 km Shear (kt):", utils.comp2vec(sfc_6km_shear[0], sfc_6km_shear[1])[1]
    srwind = params.bunkers_storm_motion(prof)
    srh3km = winds.helicity(prof, 0, 3000., stu=srwind[0], stv=srwind[1])
    srh1km = winds.helicity(prof, 0, 1000., stu=srwind[0], stv=srwind[1])
    #print "0-3 km Storm Relative Helicity [m2/s2]:",srh3km[0]

    #### Calculating variables based off of the effective inflow layer:

    # The effective inflow layer concept is used to obtain the layer of buoyant parcels that feed a storm's inflow.
    # Here are a few examples of how to compute variables that require the effective inflow layer in order to calculate them:

    stp_fixed = params.stp_fixed(
        sfcpcl.bplus, sfcpcl.lclhght, srh1km[0],
        utils.comp2vec(sfc_6km_shear[0], sfc_6km_shear[1])[1])
    ship = params.ship(prof)

    # If you get an error about not converting masked constant to python int
    # use the round() function instead of int() - Ahijevych May 11 2016
    # 2nd element of list is the # of decimal places
    indices = {
        'SBCAPE': [sfcpcl.bplus, 0, 'J $\mathregular{kg^{-1}}$'],
        'SBCIN': [sfcpcl.bminus, 0, 'J $\mathregular{kg^{-1}}$'],
        'SBLCL': [sfcpcl.lclhght, 0, 'm AGL'],
        'SBLFC': [sfcpcl.lfchght, 0, 'm AGL'],
        'SBEL': [sfcpcl.elhght, 0, 'm AGL'],
        'SBLI': [sfcpcl.li5, 0, 'C'],
        'MLCAPE': [mlpcl.bplus, 0, 'J $\mathregular{kg^{-1}}$'],
        'MLCIN': [mlpcl.bminus, 0, 'J $\mathregular{kg^{-1}}$'],
        'MLLCL': [mlpcl.lclhght, 0, 'm AGL'],
        'MLLFC': [mlpcl.lfchght, 0, 'm AGL'],
        'MLEL': [mlpcl.elhght, 0, 'm AGL'],
        'MLLI': [mlpcl.li5, 0, 'C'],
        'MUCAPE': [mupcl.bplus, 0, 'J $\mathregular{kg^{-1}}$'],
        'MUCIN': [mupcl.bminus, 0, 'J $\mathregular{kg^{-1}}$'],
        'MULCL': [mupcl.lclhght, 0, 'm AGL'],
        'MULFC': [mupcl.lfchght, 0, 'm AGL'],
        'MUEL': [mupcl.elhght, 0, 'm AGL'],
        'MULI': [mupcl.li5, 0, 'C'],
        '0-1 km SRH': [srh1km[0], 0, '$\mathregular{m^{2}s^{-2}}$'],
        '0-1 km Shear':
        [utils.comp2vec(sfc_1km_shear[0], sfc_1km_shear[1])[1], 0, 'kt'],
        '0-3 km SRH': [srh3km[0], 0, '$\mathregular{m^{2}s^{-2}}$'],
        '0-6 km Shear':
        [utils.comp2vec(sfc_6km_shear[0], sfc_6km_shear[1])[1], 0, 'kt'],
        'PWV': [params.precip_water(prof), 2, 'inch'],
        'K-index': [params.k_index(prof), 0, ''],
        'STP(fix)': [stp_fixed, 1, ''],
        'SHIP': [ship, 1, '']
    }

    eff_inflow = params.effective_inflow_layer(prof)
    if any(eff_inflow):
        ebot_hght = interp.to_agl(prof, interp.hght(prof, eff_inflow[0]))
        etop_hght = interp.to_agl(prof, interp.hght(prof, eff_inflow[1]))
        #print "Effective Inflow Layer Bottom Height (m AGL):", ebot_hght
        #print "Effective Inflow Layer Top Height (m AGL):", etop_hght
        effective_srh = winds.helicity(prof,
                                       ebot_hght,
                                       etop_hght,
                                       stu=srwind[0],
                                       stv=srwind[1])
        indices['Eff. SRH'] = [
            effective_srh[0], 0, '$\mathregular{m^{2}s^{-2}}$'
        ]
        #print "Effective Inflow Layer SRH (m2/s2):", effective_srh[0]
        ebwd = winds.wind_shear(prof, pbot=eff_inflow[0], ptop=eff_inflow[1])
        ebwspd = utils.mag(*ebwd)
        indices['EBWD'] = [ebwspd, 0, 'kt']
        #print "Effective Bulk Wind Difference:", ebwspd
        scp = params.scp(mupcl.bplus, effective_srh[0], ebwspd)
        indices['SCP'] = [scp, 1, '']
        stp_cin = params.stp_cin(mlpcl.bplus, effective_srh[0], ebwspd,
                                 mlpcl.lclhght, mlpcl.bminus)
        indices['STP(cin)'] = [stp_cin, 1, '']
        #print "Supercell Composite Parameter:", scp
        #print "Significant Tornado Parameter (w/CIN):", stp_cin
        #print "Significant Tornado Parameter (fixed):", stp_fixed

    # Update the indices within the indices dictionary on the side of the plot.
    string = ''
    for index, value in sorted(indices.items()):
        if np.ma.is_masked(value[0]):
            if debug:
                print("skipping masked value for index=", index)
            continue
        if debug:
            print("index=", index)
            print("value=", value)
        format = '%.' + str(value[1]) + 'f'
        string += index + ": " + format % value[0] + " " + value[2] + '\n'

    return string
Beispiel #34
0
    def __init__(self, **kwargs):
        '''
        Create the sounding data object
        
        Parameters
        ----------
        Mandatory Keywords
        hght : array_like
        The corresponding height values (Meters)
            
        Optional Keyword Pairs (must use one or the other)
        wdir : array_like
        The direction from which the wind is blowing in
        meteorological degrees
        wspd : array_like
        The speed of the wind
        rms : array_like
        The RMS from the VAD algorithm.
        OR
            
        u : array_like
        The U-component of the direction from which the wind
        is blowing
            
        v : array_like
        The V-component of the direction from which the wind
        is blowing.
            
        Optional Keywords
        missing : number (default: sharppy.sharptab.constants.MISSING)
        The value of the missing flag
        location : string (default: None)
        The 3 character station identifier or 4 character
        WMO station ID for radiosonde locations. Used for
        the PWV database.
        Returns
        -------
        prof: Profile object
            
        '''

        #Dummy variables so Profile doesn't crash
        wspd = kwargs.get('wspd')
        kwargs['tmpc'] = np.ones(len(wspd))
        kwargs['dwpc'] = np.ones(len(wspd))
        kwargs['pres'] = np.arange(1,len(wspd)+1,1)[::-1]

        super(VADProfile, self).__init__(**kwargs)
        
        self.rms = kwargs.get('rms')
        
        assert len(self.hght) == len(self.tmpc) == len(self.dwpc),\
                "Length of pres, hght, tmpc, or dwpc arrays passed to constructor are not the same."

        ## did the user provide the wind in vector form?
        if self.wdir is not None:
            assert len(self.wdir) == len(self.wspd) == len(self.hght), "Length of wdir and wspd arrays passed to constructor are not the same length as the pres array."
            self.wdir[self.wdir == self.missing] = ma.masked
            self.wspd[self.wspd == self.missing] = ma.masked
            self.wdir[self.wspd.mask] = ma.masked
            self.wspd[self.wdir.mask] = ma.masked
            self.u, self.v = utils.vec2comp(self.wdir, self.wspd)

        ## did the user provide the wind in u,v form?
        elif self.u is not None:
            assert len(self.u) == len(self.v) == len(self.hght), "Length of u and v arrays passed to constructor are not the same length as the pres array."
            self.u[self.u == self.missing] = ma.masked
            self.v[self.v == self.missing] = ma.masked
            self.u[self.v.mask] = ma.masked
            self.v[self.u.mask] = ma.masked
            self.wdir, self.wspd = utils.comp2vec(self.u, self.v)

                ## mask the missing values
        self.hght[self.hght == self.missing] = ma.masked
        strictQC = False
        #if not qc_tools.isHGHTValid(self.hght):
        #    qc_tools.raiseError("Incorrect order of height (or repeat values) array or height array is of length <= 1.", ValueError)
        if not qc_tools.isWSPDValid(self.wspd) and strictQC:
            qc_tools.raiseError("Invalid wind speed array. Array contains a value < 0 knots.", ValueError)
        if not qc_tools.isWDIRValid(self.wdir) and strictQC:
            qc_tools.raiseError("Invalid wind direction array. Array contains a value < 0 degrees or value >= 360 degrees.", ValueError)     

        print """Using the VADDecoder...something to keep in mind is that all interp.py routines
               use the prof.logp variable to perform the pressure based interpolation.  Because VAD 
               and radar wind profiler data does not contain pressure data, this will break
               any interpolation routines and any routines that require pressure. 
               Will have to find a way around this."""
        self.logp = np.log10(self.pres.copy())
        self.vtmp = thermo.virtemp( self.pres, self.tmpc, self.dwpc )
        idx = np.ma.where(self.pres > 0)[0]
        #self.vtmp[slf.dwpc.mask[idx]] = self.tmpc[self.dwpc.mask[idx]] # Masking any virtual temperature 

        ## get the index of the top and bottom of the profile
        self.sfc = self.get_sfc()
        self.top = self.get_top()
Beispiel #35
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)
Beispiel #36
0
    def __init__(self, **kwargs):
        '''
        Create the sounding data object

        Parameters
        ----------
        Mandatory Keywords
            pres : array_like
                The pressure values (Hectopaschals)
            hght : array_like
                The corresponding height values (Meters)
            tmpc : array_like
                The corresponding temperature values (Celsius)
            dwpc : array_like
                The corresponding dewpoint temperature values (Celsius)

        Optional Keyword Pairs (must use one or the other)
            wdir : array_like
                The direction from which the wind is blowing in
                meteorological degrees
            wspd : array_like
                The speed of the wind

            OR

            u : array_like
                The U-component of the direction from which the wind
                is blowing
            v : array_like
                The V-component of the direction from which the wind
                is blowing.

        Optional Keywords
            missing : number (default: sharppy.sharptab.constants.MISSING)
                The value of the missing flag

        Returns
        -------
        A profile object

        '''
        self.missing = kwargs.get('missing', MISSING)
        self.masked = ma.masked
        self.pres = ma.asanyarray(kwargs.get('pres'))
        self.hght = ma.asanyarray(kwargs.get('hght'))
        self.tmpc = ma.asanyarray(kwargs.get('tmpc'))
        self.dwpc = ma.asanyarray(kwargs.get('dwpc'))
        self.pres[self.pres == self.missing] = ma.masked
        self.hght[self.hght == self.missing] = ma.masked
        self.tmpc[self.tmpc == self.missing] = ma.masked
        self.dwpc[self.dwpc == self.missing] = ma.masked
        self.logp = np.log10(self.pres.copy())
        if 'wdir' in kwargs:
            self.wdir = ma.asanyarray(kwargs.get('wdir'))
            self.wspd = ma.asanyarray(kwargs.get('wspd'))
            self.wdir[self.wdir == self.missing] = ma.masked
            self.wspd[self.wspd == self.missing] = ma.masked
            self.wdir[self.wspd.mask] = ma.masked
            self.wspd[self.wdir.mask] = ma.masked
            self.u, self.v = utils.vec2comp(self.wdir, self.wspd)
        elif 'u' in kwargs:
            self.u = ma.asanyarray(kwargs.get('u'))
            self.v = ma.asanyarray(kwargs.get('v'))
            self.u[self.u == self.missing] = ma.masked
            self.v[self.v == self.missing] = ma.masked
            self.u[self.v.mask] = ma.masked
            self.v[self.u.mask] = ma.masked
            self.wdir, self.wspd = utils.comp2vec(self.u, self.v)
        self.pres.set_fill_value(self.missing)
        self.hght.set_fill_value(self.missing)
        self.tmpc.set_fill_value(self.missing)
        self.dwpc.set_fill_value(self.missing)
        self.wdir.set_fill_value(self.missing)
        self.wspd.set_fill_value(self.missing)
        self.u.set_fill_value(self.missing)
        self.v.set_fill_value(self.missing)
        self.sfc = self.get_sfc()
Beispiel #37
0
    def get_kinematics(self):
        '''
        Function to generate the numerous kinematic quantities
        used for display and calculations. It requires that the
        parcel calculations have already been called for the lcl
        to el shear and mean wind vectors, as well as indices
        that require an effective inflow layer.

        Parameters
        ----------
        None

        Returns
        -------
        None
        '''
        sfc = self.pres[self.sfc]
        heights = np.array([1000., 3000., 4000., 5000., 6000., 8000., 9000.])
        p1km, p3km, p4km, p5km, p6km, p8km, p9km = interp.pres(
            self, interp.to_msl(self, heights))
        ## 1km and 6km winds
        self.wind1km = interp.vec(self, p1km)
        self.wind6km = interp.vec(self, p6km)
        ## calcluate wind shear
        self.sfc_1km_shear = winds.wind_shear(self, pbot=sfc, ptop=p1km)
        self.sfc_3km_shear = winds.wind_shear(self, pbot=sfc, ptop=p3km)
        self.sfc_6km_shear = winds.wind_shear(self, pbot=sfc, ptop=p6km)
        self.sfc_8km_shear = winds.wind_shear(self, pbot=sfc, ptop=p8km)
        self.sfc_9km_shear = winds.wind_shear(self, pbot=sfc, ptop=p9km)
        self.lcl_el_shear = winds.wind_shear(self,
                                             pbot=self.mupcl.lclpres,
                                             ptop=self.mupcl.elpres)
        ## calculate mean wind
        self.mean_1km = utils.comp2vec(
            *winds.mean_wind(self, pbot=sfc, ptop=p1km))
        self.mean_3km = utils.comp2vec(
            *winds.mean_wind(self, pbot=sfc, ptop=p3km))
        self.mean_6km = utils.comp2vec(
            *winds.mean_wind(self, pbot=sfc, ptop=p6km))
        self.mean_8km = utils.comp2vec(
            *winds.mean_wind(self, pbot=sfc, ptop=p8km))
        self.mean_lcl_el = utils.comp2vec(*winds.mean_wind(
            self, pbot=self.mupcl.lclpres, ptop=self.mupcl.elpres))
        ## parameters that depend on the presence of an effective inflow layer
        if self.etop is ma.masked or self.ebottom is ma.masked:
            self.etopm = ma.masked
            self.ebotm = ma.masked
            self.srwind = winds.non_parcel_bunkers_motion(self)
            self.eff_shear = [MISSING, MISSING]
            self.ebwd = [MISSING, MISSING, MISSING]
            self.ebwspd = MISSING
            self.mean_eff = [MISSING, MISSING, MISSING]
            self.mean_ebw = [MISSING, MISSING, MISSING]
            self.srw_eff = [MISSING, MISSING, MISSING]
            self.srw_ebw = [MISSING, MISSING, MISSING]
            self.right_esrh = [ma.masked, ma.masked, ma.masked]
            self.left_esrh = [ma.masked, ma.masked, ma.masked]
            self.critical_angle = ma.masked
        else:
            self.srwind = params.bunkers_storm_motion(self,
                                                      mupcl=self.mupcl,
                                                      pbot=self.ebottom)
            depth = (self.mupcl.elhght - self.ebotm) / 2
            elh = interp.pres(self, interp.to_msl(self, self.ebotm + depth))
            ## calculate mean wind
            self.mean_eff = winds.mean_wind(self, self.ebottom, self.etop)
            self.mean_ebw = winds.mean_wind(self, pbot=self.ebottom, ptop=elh)
            ## calculate wind shear of the effective layer
            self.eff_shear = winds.wind_shear(self,
                                              pbot=self.ebottom,
                                              ptop=self.etop)
            self.ebwd = winds.wind_shear(self, pbot=self.ebottom, ptop=elh)
            self.ebwspd = utils.mag(self.ebwd[0], self.ebwd[1])
            ## calculate the mean sr wind
            self.srw_eff = winds.sr_wind(self,
                                         pbot=self.ebottom,
                                         ptop=self.etop,
                                         stu=self.srwind[0],
                                         stv=self.srwind[1])
            self.srw_ebw = winds.sr_wind(self,
                                         pbot=self.ebottom,
                                         ptop=elh,
                                         stu=self.srwind[0],
                                         stv=self.srwind[1])
            self.right_esrh = winds.helicity(self,
                                             self.ebotm,
                                             self.etopm,
                                             stu=self.srwind[0],
                                             stv=self.srwind[1])
            self.left_esrh = winds.helicity(self,
                                            self.ebotm,
                                            self.etopm,
                                            stu=self.srwind[2],
                                            stv=self.srwind[3])
            self.critical_angle = winds.critical_angle(self,
                                                       stu=self.srwind[0],
                                                       stv=self.srwind[1])
        ## calculate mean srw
        self.srw_1km = utils.comp2vec(*winds.sr_wind(
            self, pbot=sfc, ptop=p1km, stu=self.srwind[0], stv=self.srwind[1]))
        self.srw_3km = utils.comp2vec(*winds.sr_wind(
            self, pbot=sfc, ptop=p3km, stu=self.srwind[0], stv=self.srwind[1]))
        self.srw_6km = utils.comp2vec(*winds.sr_wind(
            self, pbot=sfc, ptop=p6km, stu=self.srwind[0], stv=self.srwind[1]))
        self.srw_8km = utils.comp2vec(*winds.sr_wind(
            self, pbot=sfc, ptop=p8km, stu=self.srwind[0], stv=self.srwind[1]))
        self.srw_4_5km = utils.comp2vec(*winds.sr_wind(
            self, pbot=p4km, ptop=p5km, stu=self.srwind[0],
            stv=self.srwind[1]))
        self.srw_lcl_el = utils.comp2vec(
            *winds.sr_wind(self,
                           pbot=self.mupcl.lclpres,
                           ptop=self.mupcl.elpres,
                           stu=self.srwind[0],
                           stv=self.srwind[1]))
        # This is for the red, blue, and purple bars that appear on the SR Winds vs. Height plot
        self.srw_0_2km = winds.sr_wind(self,
                                       pbot=sfc,
                                       ptop=interp.pres(
                                           self, interp.to_msl(self, 2000.)),
                                       stu=self.srwind[0],
                                       stv=self.srwind[1])
        self.srw_4_6km = winds.sr_wind(self,
                                       pbot=interp.pres(
                                           self, interp.to_msl(self, 4000.)),
                                       ptop=p6km,
                                       stu=self.srwind[0],
                                       stv=self.srwind[1])
        self.srw_9_11km = winds.sr_wind(
            self,
            pbot=interp.pres(self, interp.to_msl(self, 9000.)),
            ptop=interp.pres(self, interp.to_msl(self, 11000.)),
            stu=self.srwind[0],
            stv=self.srwind[1])

        ## calculate upshear and downshear
        self.upshear_downshear = winds.mbe_vectors(self)
        self.srh1km = winds.helicity(self,
                                     0,
                                     1000.,
                                     stu=self.srwind[0],
                                     stv=self.srwind[1])
        self.srh3km = winds.helicity(self,
                                     0,
                                     3000.,
                                     stu=self.srwind[0],
                                     stv=self.srwind[1])
Beispiel #38
0
def test_comp2vec_default_missing_val_single():
    input_u = MISSING
    input_v = 30
    returned_wdir, returned_wspd = utils.comp2vec(input_u, input_v)
    npt.assert_equal(type(returned_wdir), type(ma.masked))
    npt.assert_equal(type(returned_wspd), type(ma.masked))