Example #1
0
        def execute(port, data_in, req):
            """
            expects the output of the teca_tc_classify algorithm
            generates a handful of histograms, summary statistics,
            and plots. returns summary table with counts of annual
            storms and their categories.
            """
            global plt
            global plt_mp
            global plt_tick

            import matplotlib.pyplot as plt
            import matplotlib.patches as plt_mp
            import matplotlib.ticker as plt_tick

            # store matplotlib state we modify
            legend_frame_on_orig = plt.rcParams['legend.frameon']

            # tweak matplotlib slightly
            plt.rcParams['figure.max_open_warning'] = 0
            plt.rcParams['legend.frameon'] = 1

            # get the input table
            in_table = teca_py.as_teca_table(data_in[0])
            if in_table is None:
                # TODO if this is part of a parallel pipeline then
                # only rank 0 should report an error.
                sys.stderr.write('ERROR: empty input, or not a table\n')
                return teca_table.New()

            time_units = in_table.get_time_units()

            # get the columns of raw data
            year = in_table.get_column('year').as_array()
            region_id = in_table.get_column('region_id').as_array()
            region_name = in_table.get_column('region_name')
            region_long_name = in_table.get_column('region_long_name')
            start_y = in_table.get_column('start_y').as_array()

            ACE = in_table.get_column('ACE').as_array()
            PDI = in_table.get_column('PDI').as_array()

            # organize the data by year month etc...
            regional_ACE = []
            regional_PDI = []

            # get unique for use as indices etc
            uyear = sorted(set(year))
            n_year = len(uyear)
            ureg = sorted(set(zip(region_id, region_name, region_long_name)))


            teca_tc_activity.accum_by_year_and_region(uyear, \
                ureg, year, region_id, start_y, ACE, regional_ACE)

            teca_tc_activity.accum_by_year_and_region(uyear, \
                ureg, year, region_id, start_y, PDI, regional_PDI)

            # now plot the organized data in various ways
            if state.color_map is None:
                state.color_map = plt.cm.jet

            teca_tc_activity.plot_individual(state, uyear, \
                 ureg, regional_ACE,'ACE', '$10^4 kn^2$')

            teca_tc_activity.plot_individual(state, uyear, \
                ureg, regional_PDI, 'PDI', '$m^3 s^{-2}$')

            teca_tc_activity.plot_cumulative(state, uyear, \
                ureg, regional_ACE, 'ACE', '$10^4 kn^2$')

            teca_tc_activity.plot_cumulative(state, uyear, \
                ureg, regional_PDI, 'PDI', '$m^3 s^{-2}$')

            if (state.interactive):
                plt.show()

            # restore matplot lib global state
            plt.rcParams['legend.frameon'] = legend_frame_on_orig

            # send data downstream
            return in_table
Example #2
0
        def execute(port, data_in, req):
            """
            expects the output of the teca_tc_classify algorithm
            generates a handful of histograms, summary statistics,
            and plots. returns summary table with counts of annual
            storms and their categories.
            """
            #sys.stderr.write('teca_tc_trajectory_scalars::execute\n')

            import matplotlib.pyplot as plt
            import matplotlib.patches as plt_mp
            import matplotlib.image as plt_img
            import matplotlib.gridspec as plt_gridspec

            # store matplotlib state we modify
            legend_frame_on_orig = plt.rcParams['legend.frameon']

            # tweak matplotlib slightly
            plt.rcParams['figure.max_open_warning'] = 0
            plt.rcParams['legend.frameon'] = 1

            # get the input table
            in_table = teca_py.as_teca_table(data_in[0])
            if in_table is None:
                # TODO if this is part of a parallel pipeline then
                # only rank 0 should report an error.
                sys.stderr.write('ERROR: empty input, or not a table\n')
                return teca_py.teca_table.New()

            # use this color map for Saphir-Simpson scale
            red_cmap = ['#ffd2a3','#ffa749','#ff7c04', \
                '#ea4f00','#c92500','#a80300']

            km_per_deg_lat = 111

            time_units = in_table.get_time_units()

            time = in_table.get_column('time').as_array()
            step = in_table.get_column('step').as_array()
            track = in_table.get_column('track_id').as_array()

            lon = in_table.get_column('lon').as_array()
            lat = in_table.get_column('lat').as_array()

            year = in_table.get_column('year').as_array()
            month = in_table.get_column('month').as_array()
            day = in_table.get_column('day').as_array()
            hour = in_table.get_column('hour').as_array()
            minute = in_table.get_column('minute').as_array()

            wind = in_table.get_column('surface_wind').as_array()
            vort = in_table.get_column('850mb_vorticity').as_array()
            psl = in_table.get_column('sea_level_pressure').as_array()
            temp = in_table.get_column('core_temp').as_array()
            have_temp = in_table.get_column('have_core_temp').as_array()
            thick = in_table.get_column('thickness').as_array()
            have_thick = in_table.get_column('have_thickness').as_array()
            speed = in_table.get_column('storm_speed').as_array()

            wind_rad = []
            i = 0
            while i < 5:
                col_name = 'wind_radius_%d'%(i)
                if in_table.has_column(col_name):
                    wind_rad.append(in_table.get_column(col_name).as_array())
                i += 1
            peak_rad = in_table.get_column('peak_radius').as_array() \
                if in_table.has_column('peak_radius') else None

            # get the list of unique track ids, this is our loop index
            utrack = sorted(set(track))
            nutracks = len(utrack)

            # load background image
            if (self.tex is None) and self.tex_file:
                self.tex = plt_img.imread(self.tex_file)

            for i in utrack:
                #sys.stderr.write('processing track %d\n'%(i))
                sys.stderr.write('.')

                fig = plt.figure()
                fig.set_size_inches(10,9.75)

                ii = np.where(track == i)[0]

                # get the scalar values for this storm
                lon_i = lon[ii]
                lat_i = lat[ii]
                wind_i = wind[ii]
                psl_i = psl[ii]
                vort_i = vort[ii]
                thick_i = thick[ii]
                temp_i = temp[ii]
                speed_i = speed[ii]/24.0

                wind_rad_i = []
                for col in wind_rad:
                    wind_rad_i.append(col[ii])
                peak_rad_i = peak_rad[ii] if peak_rad is not None else None

                # construct the title
                q = ii[0]
                r = ii[-1]

                t0 = time[q]
                t1 = time[r]

                s0 = step[q]
                s1 = step[r]

                Y0 = year[q]
                Y1 = year[r]

                M0 = month[q]
                M1 = month[r]

                D0 = day[q]
                D1 = day[r]

                h0 = hour[q]
                h1 = hour[r]

                m0 = minute[q]
                m1 = minute[r]

                tt = time[ii] - t0

                cat = teca_py.teca_tc_saffir_simpson.classify_mps(float(np.max(wind_i)))

                plt.suptitle( \
                    'Track %d, cat %d, steps %d - %d\n%d/%d/%d %d:%d:00 - %d/%d/%d %d:%d:00'%(\
                    i, cat, s0, s1, Y0, M0, D0, h0, m0, Y1, M1, D1, h1, m1), \
                    fontweight='bold')

                # plot the scalars
                gs = plt_gridspec.GridSpec(5, 4)

                plt.subplot2grid((5,4),(0,0),colspan=2,rowspan=2)
                # prepare the texture
                if self.tex is not None:
                    ext = [np.min(lon_i), np.max(lon_i), np.min(lat_i), np.max(lat_i)]
                    if self.axes_equal:
                        w = ext[1]-ext[0]
                        h = ext[3]-ext[2]
                        if w > h:
                            c = (ext[2] + ext[3])/2.0
                            w2 = w/2.0
                            ext[2] = c - w2
                            ext[3] = c + w2
                        else:
                            c = (ext[0] + ext[1])/2.0
                            h2 = h/2.0
                            ext[0] = c - h2
                            ext[1] = c + h2
                    border_size = 0.15
                    wrimax = 0 if peak_rad_i is None else \
                        max(0 if not self.plot_peak_radius else \
                            np.max(peak_rad_i), np.max(wind_rad_i[0]))
                    dlon = max(wrimax, (ext[1] - ext[0])*border_size)
                    dlat = max(wrimax, (ext[3] - ext[2])*border_size)
                    ext[0] = max(ext[0] - dlon, 0.0)
                    ext[1] = min(ext[1] + dlon, 360.0)
                    ext[2] = max(ext[2] - dlat, -90.0)
                    ext[3] = min(ext[3] + dlat, 90.0)
                    i0 = int(self.tex.shape[1]/360.0*ext[0])
                    i1 = int(self.tex.shape[1]/360.0*ext[1])
                    j0 = int(-((ext[3] + 90.0)/180.0 - 1.0)*self.tex.shape[0])
                    j1 = int(-((ext[2] + 90.0)/180.0 - 1.0)*self.tex.shape[0])
                    plt.imshow(self.tex[j0:j1, i0:i1], extent=ext, aspect='auto')

                edge_color = '#ffff00' if self.tex is not None else 'b'

                # plot the storm size
                if peak_rad_i is None:
                    plt.plot(lon_i, lat_i, '.', linewidth=2, color=edge_color)
                else:
                    # compute track unit normals
                    npts = len(ii)
                    norm_x = np.zeros(npts)
                    norm_y = np.zeros(npts)
                    npts -= 1
                    q = 1
                    while q < npts:
                        norm_x[q] = lat_i[q+1] - lat_i[q-1]
                        norm_y[q] = -(lon_i[q+1] - lon_i[q-1])
                        nmag = np.sqrt(norm_x[q]**2 + norm_y[q]**2)
                        norm_x[q] = norm_x[q]/nmag
                        norm_y[q] = norm_y[q]/nmag
                        q += 1
                    # normal at first and last point on the track
                    norm_x[0] = lat_i[1] - lat_i[0]
                    norm_y[0] = -(lon_i[1] - lon_i[0])
                    norm_x[0] = norm_x[0]/nmag
                    norm_y[0] = norm_y[0]/nmag
                    norm_x[npts] = lat_i[npts] - lat_i[npts-1]
                    norm_y[npts] = -(lon_i[npts] - lon_i[npts-1])
                    norm_x[npts] = norm_x[npts]/nmag
                    norm_y[npts] = norm_y[npts]/nmag
                    # for each wind radius, render a polygon of width 2*wind
                    # centered on the track. have to break it into continuous
                    # segments
                    nwri = len(wind_rad_i)
                    q = nwri - 1
                    while q >= 0:
                        self.plot_wind_rad(lon_i, lat_i, norm_x, norm_y, \
                            wind_rad_i[q], '-', edge_color if q==0 else red_cmap[q], \
                            2 if q==0 else 1, red_cmap[q], 0.98, q+4)
                        q -= 1
                    # plot the peak radius
                    if (self.plot_peak_radius):
                        # peak radius is only valid if one of the other wind radii
                        # exist, zero out other values
                        kk = wind_rad_i[0] > 1.0e-6
                        q = 1
                        while q < nwri:
                            kk = np.logical_or(kk, wind_rad_i[q] > 1.0e-6)
                            q += 1
                        peak_rad_i[np.logical_not(kk)] = 0.0
                        self.plot_wind_rad(lon_i, lat_i, norm_x, norm_y, \
                            peak_rad_i, '--', (0,0,0,0.25), 1, 'none', 1.00, nwri+4)
                    # mark track
                    marks = wind_rad_i[0] <= 1.0e-6
                    q = 1
                    while q < nwri:
                        marks = np.logical_and(marks, np.logical_not(wind_rad_i[q] > 1.0e-6))
                        q += 1
                    kk = np.where(marks)[0]

                    plt.plot(lon_i[kk], lat_i[kk], '.', linewidth=2, \
                        color=edge_color,zorder=10)

                    plt.plot(lon_i[kk], lat_i[kk], '.', linewidth=1, \
                        color='k', zorder=10, markersize=1)

                    marks = wind_rad_i[0] > 1.0e-6
                    q = 1
                    while q < nwri:
                        marks = np.logical_or(marks, wind_rad_i[q] > 1.0e-6)
                        q += 1
                    kk = np.where(marks)[0]

                    plt.plot(lon_i[kk], lat_i[kk], '.', linewidth=1, \
                        color='k', zorder=10, markersize=2, alpha=0.1)

                # mark track start and end
                plt.plot(lon_i[0], lat_i[0], 'o', markersize=6, markeredgewidth=2, \
                    color=edge_color, markerfacecolor='g',zorder=10)

                plt.plot(lon_i[-1], lat_i[-1], '^', markersize=6, markeredgewidth=2, \
                    color=edge_color, markerfacecolor='r',zorder=10)

                plt.grid(True)
                plt.xlabel('deg lon')
                plt.ylabel('deg lat')
                plt.title('Track', fontweight='bold')

                plt.subplot2grid((5,4),(0,2),colspan=2)
                plt.plot(tt, psl_i, 'b-', linewidth=2)
                plt.grid(True)
                plt.xlabel('time (days)')
                plt.ylabel('millibars')
                plt.title('Sea Level Pressure', fontweight='bold')
                plt.xlim([0, tt[-1]])

                plt.subplot2grid((5,4),(1,2),colspan=2)
                plt.plot(tt, wind_i, 'b-', linewidth=2)
                plt.grid(True)
                plt.xlabel('time (days)')
                plt.ylabel('ms^-1')
                plt.title('Surface Wind', fontweight='bold')
                plt.xlim([0, tt[-1]])

                plt.subplot2grid((5,4),(2,0),colspan=2)
                plt.plot(tt, speed_i, 'b-', linewidth=2)
                plt.grid(True)
                plt.xlabel('time (days)')
                plt.ylabel('km d^-1')
                plt.title('Propagation Speed', fontweight='bold')
                plt.xlim([0, tt[-1]])

                plt.subplot2grid((5,4),(2,2),colspan=2)
                plt.plot(tt, vort_i, 'b-', linewidth=2)
                plt.grid(True)
                plt.xlabel('time (days)')
                plt.ylabel('s^-1')
                plt.title('Vorticity', fontweight='bold')
                plt.xlim([0, tt[-1]])

                plt.subplot2grid((5,4),(3,0),colspan=2)
                plt.plot(tt, thick_i, 'b-', linewidth=2)
                plt.grid(True)
                plt.xlabel('time (days)')
                plt.ylabel('meters')
                plt.title('Thickness', fontweight='bold')
                plt.xlim([0, tt[-1]])

                plt.subplot2grid((5,4),(3,2),colspan=2)
                plt.plot(tt, temp_i, 'b-', linewidth=2)
                plt.grid(True)
                plt.xlabel('time (days)')
                plt.ylabel('deg K')
                plt.title('Core Temperature', fontweight='bold')
                plt.xlim([0, tt[-1]])

                if peak_rad_i is not None:
                    plt.subplot2grid((5,4),(4,0),colspan=2)
                    q = len(wind_rad_i) - 1
                    while q >= 0:
                        wr_i_q = km_per_deg_lat*wind_rad_i[q]
                        plt.fill_between(tt, 0, wr_i_q, color=red_cmap[q], alpha=0.9, zorder=q+3)
                        plt.plot(tt, wr_i_q, '-', linewidth=2, color=red_cmap[q], zorder=q+3)
                        q -= 1
                    if (self.plot_peak_radius):
                        plt.plot(tt, km_per_deg_lat*peak_rad_i, 'k--', linewidth=1, zorder=10)
                    plt.plot(tt, np.zeros(len(tt)), 'w-', linewidth=2, zorder=10)
                    plt.grid(True)
                    plt.xlabel('time (days)')
                    plt.ylabel('radius (km)')
                    plt.title('Storm Size', fontweight='bold')
                    plt.xlim([0, tt[-1]])
                    plt.ylim(ymin=0)

                    plt.subplot2grid((5,4),(4,2))
                    red_cmap_pats = []
                    q = 0
                    while q < 6:
                        red_cmap_pats.append( \
                            plt_mp.Patch(color=red_cmap[q], label='R%d'%(q)))
                        q += 1
                    if (self.plot_peak_radius):
                        red_cmap_pats.append(plt_mp.Patch(color='k', label='RP'))
                    l = plt.legend(handles=red_cmap_pats, loc=2, \
                            bbox_to_anchor=(-0.1, 1.0), borderaxespad=0.0, \
                            frameon=True, ncol=2)
                    plt.axis('off')

                plt.subplots_adjust(left=0.065, right=0.98, \
                    bottom=0.05, top=0.9, wspace=0.6, hspace=0.7)

                plt.savefig('%s_%06d.png'%(self.basename, i), dpi=self.dpi)
                if (not self.interactive):
                    plt.close(fig)

            if (self.interactive):
                plt.show()

            out_table = teca_py.teca_table.New()
            out_table.shallow_copy(in_table)
            return out_table
Example #3
0
        def execute(port, data_in, req):
            """
            expects the output of the teca_tc_classify algorithm
            generates a handful of histograms, summary statistics,
            and plots. returns summary table with counts of annual
            storms and their categories.
            """
            import matplotlib.pyplot as plt
            import matplotlib.patches as plt_mp

            # store matplotlib state we modify
            legend_frame_on_orig = plt.rcParams['legend.frameon']

            # tweak matplotlib slightly
            plt.rcParams['figure.max_open_warning'] = 0
            plt.rcParams['legend.frameon'] = 1

            # get the input table
            in_table = teca_py.as_teca_table(data_in[0])
            if in_table is None:
                # TODO if this is part of a parallel pipeline then
                # only rank 0 should report an error.
                sys.stderr.write('ERROR: empty input, or not a table\n')
                return teca_table.New()

            time_units = in_table.get_time_units()

            # get the columns of raw data
            year = in_table.get_column('year').as_array()
            month = in_table.get_column('month').as_array()
            duration = in_table.get_column('duration').as_array()
            length = in_table.get_column('length').as_array() / 1000.0
            category = in_table.get_column('category').as_array()
            region_id = in_table.get_column('region_id').as_array()
            region_name = in_table.get_column('region_name')
            region_long_name = in_table.get_column('region_long_name')
            wind = in_table.get_column('max_surface_wind').as_array()
            press = in_table.get_column('min_sea_level_pressure').as_array()
            start_y = in_table.get_column('start_y').as_array()
            ACE = in_table.get_column('ACE').as_array()

            # organize the data by year month etc...
            annual_cat = []
            annual_count = []
            annual_wind = []
            annual_press = []
            annual_dur = []
            annual_len = []
            annual_ACE = []
            by_month = []
            by_region = []
            totals = []

            # get unique for use as indices etc
            uyear = sorted(set(year))
            n_year = len(uyear)

            ureg = sorted(set(zip(region_id, region_name, region_long_name)))
            n_reg = len(ureg) + 3  # add 2 for n & s hemi, 1 for global

            for yy in uyear:
                ids = np.where(year == yy)
                # break these down by year
                annual_count.append(len(ids[0]))
                annual_cat.append(category[ids])
                annual_wind.append(wind[ids])
                annual_press.append(press[ids])
                annual_dur.append(duration[ids])
                annual_len.append(length[ids])
                annual_ACE.append(ACE[ids])

                # global totals
                tmp = [annual_count[-1]]
                for c in np.arange(0, 6, 1):
                    cids = np.where(category[ids] == c)
                    tmp.append(len(cids[0]))
                totals.append(tmp)

                # break down by year, month, and category
                mm = month[ids]
                mnum = np.arange(1, 13, 1)
                monthly = []
                for m in mnum:
                    mids = np.where(mm == m)
                    mcats = category[ids][mids]
                    cats = []
                    for c in np.arange(0, 6, 1):
                        cids = np.where(mcats == c)
                        cats.append(len(cids[0]))
                    monthly.append(cats)
                by_month.append(monthly)
                # break down by year and region
                rr = region_id[ids]
                max_reg = np.max(rr)
                regional = []
                for r, n, l in ureg:
                    rids = np.where(rr == r)
                    rcats = category[ids][rids]
                    cats = []
                    for c in np.arange(0, 6, 1):
                        cids = np.where(rcats == c)
                        cats.append(len(cids[0]))
                    regional.append(cats)
                by_region.append(regional)
                # add north and south hemisphere regions
                hemi = []
                nhids = np.where(start_y[ids] >= 0.0)
                cats = category[ids][nhids]
                nhcats = []
                for c in np.arange(0, 6, 1):
                    cids = np.where(cats == c)
                    nhcats.append(len(cids[0]))
                by_region[-1].append(nhcats)
                shids = np.where(start_y[ids] < 0.0)
                cats = category[ids][shids]
                shcats = []
                for c in np.arange(0, 6, 1):
                    cids = np.where(cats == c)
                    shcats.append(len(cids[0]))
                by_region[-1].append(shcats)
                # global break down
                gcats = []
                cats = category[ids]
                for c in np.arange(0, 6, 1):
                    cids = np.where(cats == c)
                    gcats.append(len(cids[0]))
                by_region[-1].append(gcats)

            # dump annual totals
            summary = teca_py.teca_table.New()
            summary.declare_columns(['year', 'total', 'cat 0', \
                 'cat 1', 'cat 2', 'cat 3', 'cat 4', 'cat 5'], \
                 ['i', 'ul', 'i', 'i', 'i', 'i', 'i', 'i'])
            q = 0
            while q < n_year:
                summary << int(uyear[q]) << int(totals[q][0]) \
                    << int(totals[q][1]) << int(totals[q][2]) \
                    << int(totals[q][3]) << int(totals[q][4]) \
                    << int(totals[q][5]) << int(totals[q][6])
                q += 1
            f = open('%s_summary.csv' % (state.basename), 'wc')
            f.write(str(summary))
            f.close()

            # now plot the organized data in various ways
            n_cols = 3
            n_plots = n_year + 1
            n_left = n_plots % n_cols
            n_rows = n_plots / n_cols + (1 if n_left else 0)
            wid = 2.5 * n_cols
            ht = 2.0 * n_rows

            # use this color map for Saphir-Simpson scale
            red_cmap = ['#ffd2a3','#ffa749','#ff7c04', \
                '#ea4f00','#c92500','#a80300']

            red_cmap_pats = []
            q = 0
            while q < 6:
                red_cmap_pats.append( \
                    plt_mp.Patch(color=red_cmap[q], label='cat %d'%(q)))
                q += 1

            # plot annual saphir-simpson distribution
            page_no = 1
            cat_fig = plt.figure()
            cat_fig.set_size_inches(wid, ht)

            max_y = 0
            q = 0
            while q < n_year:
                max_y = max(max_y, len(np.where(annual_cat[q] == 0)[0]))
                q += 1

            q = 0
            for yy in uyear:
                plt.subplot(n_rows, n_cols, q + 1)
                ax = plt.gca()
                ax.grid(zorder=0)
                n,bins,pats = plt.hist(annual_cat[q], bins=np.arange(-0.5, 6.0, 1.0), \
                    facecolor='steelblue', alpha=0.95, edgecolor='black', \
                    linewidth=2, zorder=3)
                j = 0
                while j < 6:
                    pats[j].set_facecolor(red_cmap[j])
                    j += 1
                plt.xticks(np.arange(0, 6, 1))
                if state.rel_axes:
                    ax.set_ylim([0, max_y * 1.05])
                if (q % n_cols == 0):
                    plt.ylabel('Count', fontweight='normal', fontsize=10)
                if (q >= (n_year - n_cols)):
                    plt.xlabel('Category', fontweight='normal', fontsize=10)
                plt.title('%d' % (yy), fontweight='bold', fontsize=11)
                plt.grid(True)

                q += 1

            plt.subplot(n_rows, n_cols, q + 1)
            ax = plt.gca()
            ax.grid(zorder=0)
            l = plt.legend(handles=red_cmap_pats,
                           loc=2,
                           bbox_to_anchor=(0.0, 1.0))
            plt.axis('off')

            plt.suptitle('Annual Saphir-Simpson Distribution',
                         fontweight='bold')
            plt.subplots_adjust(hspace=0.4, top=0.92)

            plt.savefig('%s_annual_saphire_simpson_distribution_%d.png'%( \
                state.basename, page_no), dpi=state.dpi)

            # break annual distributions down by month
            mos_fig = plt.figure()
            mos_fig.set_size_inches(wid, ht)

            max_y = 0
            q = 0
            while q < n_year:
                p = 0
                while p < 12:
                    max_y = max(max_y, sum(by_month[q][p]))
                    p += 1
                q += 1

            q = 0
            for yy in uyear:
                plt.subplot(n_rows, n_cols, q + 1)
                ax = plt.gca()
                ax.grid(zorder=0)
                # build up a stacked bar chart, each category is a layer
                # copy that cat for all months into a temp array then add
                # it to the plot at the right hight and color.
                mcts = by_month[q]
                bot = np.zeros((12))
                c = 0
                while c < 6:
                    tmp = []
                    p = 0
                    while p < 12:
                        tmp.append(mcts[p][c])
                        p += 1
                    plt.bar(np.arange(1,13,1)-0.375, tmp, width=0.75, bottom=bot, \
                        facecolor=red_cmap[c], edgecolor='k', linewidth=1, \
                        tick_label=['J','F','M','A','M','J','J','A','S','O','N','D'], \
                        zorder=3)
                    bot += tmp
                    c += 1

                plt.xticks(np.arange(1, 13, 1))
                if state.rel_axes:
                    ax.set_ylim([0, 1.05 * max_y])
                if (q % n_cols == 0):
                    plt.ylabel('Count', fontweight='normal', fontsize=10)
                if (q >= (n_year - n_cols)):
                    plt.xlabel('Month', fontweight='normal', fontsize=10)
                plt.title('%d' % (yy), fontweight='bold', fontsize=11)
                plt.grid(True)

                q += 1

            plt.subplot(n_rows, n_cols, q + 1)
            ax = plt.gca()
            ax.grid(zorder=0)
            l = plt.legend(handles=red_cmap_pats,
                           loc=2,
                           bbox_to_anchor=(0.0, 1.0))
            plt.axis('off')

            plt.suptitle('Monthly Breakdown', fontweight='bold')
            plt.subplots_adjust(hspace=0.4, top=0.92)

            plt.savefig('%s_monthly_breakdown_%d.png'%( \
                state.basename, page_no), dpi=state.dpi)

            # plot annual counts by region
            reg_fig = plt.figure()
            reg_fig.set_size_inches(wid, ht)

            rcds = zip(*ureg)[1]
            rcds += ('NH', 'SH', 'G')

            max_y = 0
            q = 0
            while q < n_year:
                j = 0
                while j < n_reg:
                    max_y = max(max_y, sum(by_region[q][j]))
                    j += 1
                q += 1

            q = 0
            for yy in uyear:
                plt.subplot(n_rows, n_cols, q + 1)
                ax = plt.gca()
                ax.grid(zorder=0)
                # build up a stacked bar chart, each category is a layer
                # copy that cat for all months into a temp array then add
                # it to the plot at the right height and color.
                rcnts = by_region[q]
                bot = np.zeros((n_reg))
                c = 0
                while c < 6:
                    tmp = []
                    p = 0
                    while p < n_reg:
                        tmp.append(rcnts[p][c])
                        p += 1

                    plt.bar(np.arange(0,n_reg,1)-0.375, tmp, width=0.75, bottom=bot, \
                        facecolor=red_cmap[c], edgecolor='k', linewidth=1, \
                        tick_label=rcds, \
                        zorder=3)

                    bot += tmp
                    c += 1

                plt.xticks(np.arange(0, n_reg, 1), rotation='vertical')
                if state.rel_axes:
                    ax.set_ylim([0, 1.05 * max_y])
                if (q % n_cols == 0):
                    plt.ylabel('Count', fontweight='normal', fontsize=10)
                if (q >= (n_year - n_cols)):
                    plt.xlabel('Region', fontweight='normal', fontsize=10)
                plt.title('%d' % (yy), fontweight='bold', fontsize=11)
                plt.grid(True)

                q += 1

            # add the color map legend
            plt.subplot(n_rows, n_cols, q + 1)
            ax = plt.gca()
            ax.grid(zorder=0)
            l = plt.legend(handles=red_cmap_pats,
                           loc=2,
                           bbox_to_anchor=(0.0, 1.0))
            plt.axis('off')

            plt.suptitle('Regional Breakdown', fontweight='bold')
            plt.subplots_adjust(wspace=0.3, hspace=0.6, top=0.92)

            plt.savefig('%s_regional_break_down_%d.png'%( \
                state.basename, page_no), dpi=state.dpi)

            # plot annual distributions
            dist_fig = plt.figure()

            wid = n_year * 0.65
            dist_fig.set_size_inches(wid, 9.0)

            ax = plt.subplot(5, 1, 1)
            plt.boxplot(annual_wind, labels=uyear)
            plt.xlabel('Year')
            plt.ylabel('ms^-1')
            plt.title('Peak Instantaneous Wind', fontweight='bold')
            ax.get_yaxis().set_label_coords(-0.1, 0.5)

            ax = plt.subplot(5, 1, 2)
            plt.boxplot(annual_press, labels=uyear)
            plt.xlabel('Year')
            plt.ylabel('Pa')
            plt.title('Min Instantaneous Pressure', fontweight='bold')
            ax.get_yaxis().set_label_coords(-0.1, 0.5)

            ax = plt.subplot(5, 1, 3)
            plt.boxplot(annual_dur, labels=uyear)
            plt.xlabel('Year')
            plt.ylabel('%s' % (time_units.split()[0]))
            plt.title('Track Duration', fontweight='bold')
            ax.get_yaxis().set_label_coords(-0.1, 0.5)

            ax = plt.subplot(5, 1, 4)
            plt.boxplot(annual_len, labels=uyear)
            plt.xlabel('Year')
            plt.ylabel('km')
            plt.title('Track Length', fontweight='bold')
            ax.get_yaxis().set_label_coords(-0.1, 0.5)

            ax = plt.subplot(5, 1, 5)
            #plt.axhline(82,color='k',linestyle='--',alpha=0.25)
            plt.boxplot(annual_ACE, labels=uyear)
            plt.xlabel('Year')
            plt.ylabel('10^4 kn^2')
            plt.title('ACE', fontweight='bold')
            ax.get_yaxis().set_label_coords(-0.1, 0.5)

            plt.suptitle('Distributions', fontweight='bold')
            plt.subplots_adjust(hspace=0.72, top=0.93)

            plt.savefig('%s_distribution_%d.png'%( \
                state.basename, page_no), dpi=state.dpi)

            # plot region over time
            reg_t_fig = plt.figure()

            rnms = zip(*ureg)[2]
            rnms += ('Northern', 'Southern', 'Global')

            tmp = np.array(uyear)
            tmp = tmp - tmp / 100 * 100
            ynms = []
            for t in tmp:
                ynms.append('%02d' % t)

            n_plots = n_reg + 1
            n_left = n_plots % n_cols
            n_rows = n_plots / n_cols + (1 if n_left else 0)
            wid = 2.5 * n_cols
            ht = 2.0 * n_rows
            reg_t_fig.set_size_inches(wid, ht)

            reg_by_t = []
            p = 0
            while p < n_reg:
                reg = []
                q = 0
                while q < n_year:
                    reg.append(by_region[q][p])
                    q += 1
                reg_by_t.append(reg)
                p += 1

            max_y_reg = -1
            max_y_hem = -1
            q = 0
            while q < n_reg:
                dat = reg_by_t[q]
                p = 0
                while p < n_year:
                    if q < n_reg - 3:
                        max_y_reg = max(max_y_reg, sum(dat[p]))
                    elif q < n_reg - 1:
                        max_y_hem = max(max_y_hem, sum(dat[p]))
                    p += 1
                q += 1

            q = 0
            while q < n_reg:
                dat = reg_by_t[q]

                plt.subplot(n_rows, n_cols, q + 1)
                ax = plt.gca()
                ax.grid(zorder=0)

                # build up a stacked bar chart, each category is a layer
                # copy that cat for all months into a temp array then add
                # it to the plot at the right height and color.
                bot = np.zeros((n_year))
                c = 0
                while c < 6:
                    tmp = []
                    p = 0
                    while p < n_year:
                        tmp.append(dat[p][c])
                        p += 1

                    plt.bar(np.arange(0,n_year,1)-0.375, tmp, width=0.75, bottom=bot, \
                        facecolor=red_cmap[c], edgecolor='k', linewidth=1, \
                        tick_label=ynms, \
                        zorder=3)

                    bot += tmp
                    c += 1

                plt.xticks(np.arange(0, n_year, 1), rotation='vertical')
                if state.rel_axes and q < n_reg - 1:
                    ax.set_ylim([
                        0, 1.05 * (max_y_reg if q < n_reg - 3 else max_y_hem)
                    ])
                if (q % n_cols == 0):
                    plt.ylabel('Count', fontweight='normal', fontsize=10)
                if (q >= (n_reg - n_cols)):
                    plt.xlabel('Year', fontweight='normal', fontsize=10)
                plt.title('%s' % (rnms[q]), fontweight='bold', fontsize=11)
                plt.grid(True)

                q += 1

            plt.suptitle('Regional Trend', fontweight='bold')
            plt.subplots_adjust(wspace=0.3, hspace=0.6, top=0.92)

            # add the color map legend
            plt.subplot(n_rows, n_cols, q + 1)
            ax = plt.gca()
            ax.grid(zorder=0)
            l = plt.legend(handles=red_cmap_pats,
                           loc=2,
                           bbox_to_anchor=(0.0, 1.0))
            plt.axis('off')

            plt.savefig('%s_regional_trend_%d.png'%( \
                state.basename, page_no), dpi=state.dpi)

            if (state.interactive):
                plt.show()

            # restore matplot lib global state
            plt.rcParams['legend.frameon'] = legend_frame_on_orig

            # send data downstream
            return summary
        def execute(port, data_in, req):
            """
            expects a table with track data containing wind radii computed
            along each point of the track. produces statistical plots showing
            the global distribution of wind radii.
            """
            track_table = teca_py.as_teca_table(data_in[0])

            # plot stats
            import matplotlib.pyplot as plt
            import matplotlib.patches as plt_mp
            from matplotlib.colors import LogNorm

            red_cmap = ['#ffd2a3','#ffa749','#ff7c04', \
                '#ea4f00','#c92500','#a80300']

            km_per_deg_lat = 111
            km_s_per_m_hr = 3.6

            fig = plt.figure(figsize=(9.25, 6.75), dpi=state.dpi)

            # scatter
            plt.subplot('331')
            plt.hold('True')

            if not track_table.has_column(state.wind_column):
                sys.stderr.write('ERROR: track table missing %s\n' %
                                 (state.wind_column))
                sys.exit(-1)

            year = track_table.get_column('year').as_array()
            month = track_table.get_column('month').as_array()
            day = track_table.get_column('day').as_array()

            ws = km_s_per_m_hr * track_table.get_column(
                state.wind_column).as_array()

            wr = []
            nwr = 0
            while track_table.has_column('wind_radius_%d' % (nwr)):
                wr.append(km_per_deg_lat *
                          track_table.get_column('wind_radius_%d' %
                                                 (nwr)).as_array())
                nwr += 1

            i = 0
            while i < nwr:
                wc = teca_py.teca_tc_saffir_simpson.get_upper_bound_kmph(i - 1)
                wri = wr[i]
                ii = np.where(wri > 0.0)
                plt.scatter(wri[ii],
                            ws[ii],
                            c=red_cmap[i],
                            alpha=0.25,
                            marker='.',
                            zorder=3 + i)
                i += 1

            plt.ylabel('Wind speed (km/hr)', fontweight='normal', fontsize=10)
            plt.title('R0 - R5 vs Wind speed', fontweight='bold', fontsize=11)
            plt.grid(True)
            ax = plt.gca()
            ax.set_xlim([0.0, 6.0 * km_per_deg_lat])

            # all
            plt.subplot('332')
            plt.hold(True)
            i = 0
            while i < nwr:
                wc = teca_py.teca_tc_saffir_simpson.get_upper_bound_kmph(i - 1)
                wri = wr[i]
                n,bins,pats = plt.hist(wri[np.where(wri > 0.0)], 32, range=[0,6.0*km_per_deg_lat], \
                    facecolor=red_cmap[i], alpha=0.95, edgecolor='black', \
                    linewidth=2, zorder=3+i)
                i += 1
            plt.ylabel('Number', fontweight='normal', fontsize=10)
            plt.title('All R0 - R5', fontweight='bold', fontsize=11)
            plt.grid(True)
            ax = plt.gca()
            ax.set_xlim([0.0, 6.0 * km_per_deg_lat])

            # r0 - r5
            i = 0
            while i < nwr:
                plt.subplot(333 + i)
                wc = teca_py.teca_tc_saffir_simpson.get_upper_bound_kmph(i - 1)
                wri = wr[i]
                wrii = wri[np.where(wri > 0.0)]
                n,bins,pats = plt.hist(wrii, 32, \
                    facecolor=red_cmap[i], alpha=1.00, edgecolor='black', \
                    linewidth=2, zorder=3)
                if ((i % 3) == 1):
                    plt.ylabel('Number', fontweight='normal', fontsize=10)
                if (i >= 3):
                    plt.xlabel('Radius (km)', fontweight='normal', fontsize=10)
                plt.title('R%d (%0.1f km/hr)' % (i, wc),
                          fontweight='bold',
                          fontsize=11)
                plt.grid(True)
                ax = plt.gca()
                ax.set_xlim([np.min(wrii), np.max(wrii)])
                i += 1

            # legend
            plt.subplot('339')
            red_cmap_pats = []
            q = 0
            while q < nwr:
                red_cmap_pats.append( \
                    plt_mp.Patch(color=red_cmap[q], label='R%d'%(q)))
                q += 1
            l = plt.legend(handles=red_cmap_pats,
                           loc=2,
                           bbox_to_anchor=(-0.1, 1.0),
                           fancybox=True)
            plt.axis('off')


            plt.suptitle('Wind Radii %s/%d/%d - %s/%d/%d'%(month[0],day[0],year[0], \
                month[-1],day[-1],year[1]), fontweight='bold', fontsize=12)
            plt.subplots_adjust(hspace=0.35, wspace=0.35, top=0.90)

            plt.savefig(state.output_prefix + 'wind_radii_stats.png')

            fig = plt.figure(figsize=(7.5, 4.0), dpi=100)
            # peak radius
            pr = km_per_deg_lat * track_table.get_column(
                'peak_radius').as_array()
            # peak radius is only valid if one of the other wind radii
            # exist
            kk = wr[0] > 1.0e-6
            q = 1
            while q < nwr:
                kk = np.logical_or(kk, wr[q] > 1.0e-6)
                q += 1
            pr = pr[kk]

            plt.subplot(121)
            n,bins,pats = plt.hist(pr[np.where(pr > 0.0)], 24, \
                facecolor='steelblue', alpha=0.95, edgecolor='black', \
                linewidth=2, zorder=3)
            plt.ylabel('Number', fontweight='normal', fontsize=10)
            plt.xlabel('Radius (km)', fontweight='normal', fontsize=10)
            plt.title('RP (radius at peak wind)',
                      fontweight='bold',
                      fontsize=11)
            plt.grid(True)
            ax = plt.gca()
            ax.set_xlim([0.0, np.max(pr)])

            # scatter
            plt.subplot('122')
            plt.hold('True')
            ii = np.where(pr > 0.0)
            cnts, xe, ye, im = plt.hist2d(pr[ii],
                                          ws[ii],
                                          bins=24,
                                          norm=LogNorm(),
                                          zorder=2)
            plt.ylabel('Wind speed (km/hr)', fontweight='normal', fontsize=10)
            plt.xlabel('Radius (km)', fontweight='normal', fontsize=10)
            plt.title('RP vs Wind speed', fontweight='bold', fontsize=11)
            plt.grid(True)
            ax = plt.gca()
            ax.set_xlim([0.0, np.max(pr)])

            fig.subplots_adjust(right=0.85)
            cbar_ax = fig.add_axes([0.88, 0.35, 0.05, 0.5])
            fig.colorbar(im, cax=cbar_ax)

            plt.suptitle('Wind Radii %s/%d/%d - %s/%d/%d'%(month[0],day[0],year[0], \
                month[-1],day[-1],year[1]), fontweight='bold', fontsize=12)
            plt.subplots_adjust(hspace=0.3, wspace=0.3, top=0.85)

            plt.savefig(state.output_prefix + 'peak_radius_stats.png')

            if state.interactive:
                plt.show()

            # send data downstream
            return track_table