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
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
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