def plotPixels(self, xVals, images, title): figure = matplotlib.figure.Figure((6, 4), dpi = 100, facecolor = (1, 1, 1)) axes = figure.add_subplot(1, 1, 1) axes.set_axis_bgcolor('white') axes.set_title(title) axes.set_ylabel('Individual pixel value') axes.set_xlabel('Exposure time (ms)') # Plot pixels from the centers of each quadrant as well as from # the center of the image and the average of the image as a whole. lines = [] labels = [] for cam in range(images.shape[1]): camImages = images[:, cam] height, width = camImages[0].shape cy = height / 2 cx = width / 2 for color, xOffset, yOffset in [('r', -1, -1), ('g', -1, 1), ('b', 1, 1), ('c', 1, -1), ('m', 0, 0)]: y = cy + yOffset * height / 4 x = cx + xOffset * width / 4 lines.append(axes.plot(xVals, camImages[:, y, x], color)) labels.append("Pixel at %d, %d for cam %d" % (x, y, cam)) lines.append(axes.plot(xVals, map(numpy.mean, camImages), 'k')) labels.append("Average for cam %d" % cam) figure.legend(lines, labels, loc = 'upper left') frame = wx.Frame(None, title = "Linearity plot") canvas = matplotlib.backends.backend_wxagg.FigureCanvasWxAgg( frame, -1, figure) canvas.draw() # Big enough to be visible, but unlikely to run off the edge of # the screen. frame.SetSize((640, 480)) frame.Show()
horizontalalignment='center', size='small') figure.text(0.5, 0.1, 'Normalized X coordinate', horizontalalignment='center', size='small') figure.text(0.06, 0.5, 'Ice Speed (m/a)', rotation='vertical', verticalalignment='center') # Create the (three column) legend prop = matplotlib.font_manager.FontProperties(size='x-small') Line2D = matplotlib.lines.Line2D([], [], color=(0, 0, 0)) figure.legend([Line2D], ['Model Output'], loc=(0.1, 0.05), prop=prop).draw_frame(False) Line2D.set_linestyle(':') Line2D.set_color((1, 0, 0)) Patch = matplotlib.patches.Patch(edgecolor=None, facecolor=(1, 0, 0), alpha=0.25) figure.legend([Line2D, Patch], ['Full Stokes Mean', 'Full Stokes Std. Dev.'], loc=(0.3, 0.02), prop=prop).draw_frame(False) Line2D.set_color((0, 0, 1)) Patch.set_facecolor((0, 0, 1)) figure.legend( [Line2D, Patch], [nonFSmodelType + ' Mean', nonFSmodelType + ' Std. Dev.'], loc=(0.55, 0.02),
def _create_line(plots, labels, plot_info): """\ Given all the data for the metrics, create a line plot. plots: list of dicts containing the plot data. Each dict contains: x: list of x-values for the plot y: list of corresponding y-values errors: errors for each data point, or None if no error information available label: plot title labels: list of x-tick labels plot_info: a MetricsPlot """ # when we're doing any kind of normalization, all series get put into a # single plot single = bool(plot_info.normalize_to) area_data = [] lines = [] if single: plot_height = _SINGLE_PLOT_HEIGHT else: plot_height = _MULTIPLE_PLOT_HEIGHT_PER_PLOT * len(plots) figure, height = _create_figure(plot_height) if single: subplot = figure.add_subplot(1, 1, 1) # Plot all the data for plot_index, (plot, color) in enumerate(zip(plots, _colors(len(plots)))): needs_invert = (plot['label'] in plot_info.inverted_series) # Add a new subplot, if user wants multiple subplots # Also handle axis inversion for subplots here if not single: subplot = figure.add_subplot(len(plots), 1, plot_index + 1) subplot.set_title(plot['label']) if needs_invert: # for separate plots, just invert the y-axis subplot.set_ylim(1, 0) elif needs_invert: # for a shared plot (normalized data), need to invert the y values # manually, since all plots share a y-axis plot['y'] = [-y for y in plot['y']] # Plot the series subplot.set_xticks(range(0, len(labels))) subplot.set_xlim(-1, len(labels)) if single: lines += subplot.plot(plot['x'], plot['y'], label=plot['label'], marker=_MULTIPLE_PLOT_MARKER_TYPE, markersize=_MULTIPLE_PLOT_MARKER_SIZE) error_bar_color = lines[-1].get_color() else: lines += subplot.plot(plot['x'], plot['y'], _SINGLE_PLOT_STYLE, label=plot['label']) error_bar_color = _SINGLE_PLOT_ERROR_BAR_COLOR if plot['errors']: subplot.errorbar(plot['x'], plot['y'], linestyle='None', yerr=plot['errors'], color=error_bar_color) subplot.set_xticklabels([]) # Construct the information for the drilldowns. # We need to do this in a separate loop so that all the data is in # matplotlib before we start calling transform(); otherwise, it will return # incorrect data because it hasn't finished adjusting axis limits. for line in lines: # Get the pixel coordinates of each point on the figure x = line.get_xdata() y = line.get_ydata() label = line.get_label() icoords = line.get_transform().transform(zip(x, y)) # Get the appropriate drilldown query drill = plot_info.query_dict['__' + label + '__'] # Set the title attributes (hover-over tool-tips) x_labels = [labels[x_val] for x_val in x] titles = [ '%s - %s: %f' % (label, x_label, y_val) for x_label, y_val in zip(x_labels, y) ] # Get the appropriate parameters for the drilldown query params = [ dict(query=drill, series=line.get_label(), param=x_label) for x_label in x_labels ] area_data += [ dict(left=ix - 5, top=height - iy - 5, right=ix + 5, bottom=height - iy + 5, title=title, callback=plot_info.drilldown_callback, callback_arguments=param_dict) for (ix, iy), title, param_dict in zip(icoords, titles, params) ] subplot.set_xticklabels(labels, rotation=90, size=_LINE_XTICK_LABELS_SIZE) # Show the legend if there are not multiple subplots if single: font_properties = matplotlib.font_manager.FontProperties( size=_LEGEND_FONT_SIZE) legend = figure.legend(lines, [plot['label'] for plot in plots], prop=font_properties, handlelen=_LEGEND_HANDLE_LENGTH, numpoints=_LEGEND_NUM_POINTS) # Workaround for matplotlib not keeping all line markers in the legend - # it seems if we don't do this, matplotlib won't keep all the line # markers in the legend. for line in legend.get_lines(): line.set_marker(_LEGEND_MARKER_TYPE) return (figure, area_data)
def _create_bar(plots, labels, plot_info): """\ Given all the data for the metrics, create a line plot. plots: list of dicts containing the plot data. x: list of x-values for the plot y: list of corresponding y-values errors: errors for each data point, or None if no error information available label: plot title labels: list of x-tick labels plot_info: a MetricsPlot """ area_data = [] bars = [] figure, height = _create_figure(_SINGLE_PLOT_HEIGHT) # Set up the plot subplot = figure.add_subplot(1, 1, 1) subplot.set_xticks(range(0, len(labels))) subplot.set_xlim(-1, len(labels)) subplot.set_xticklabels(labels, rotation=90, size=_BAR_XTICK_LABELS_SIZE) # draw a bold line at y=0, making it easier to tell if bars are dipping # below the axis or not. subplot.axhline(linewidth=2, color='black') # width here is the width for each bar in the plot. Matplotlib default is # 0.8. width = 0.8 / len(plots) # Plot the data for plot_index, (plot, color) in enumerate(zip(plots, _colors(len(plots)))): # Invert the y-axis if needed if plot['label'] in plot_info.inverted_series: plot['y'] = [-y for y in plot['y']] adjusted_x = _get_adjusted_bar(plot['x'], width, plot_index + 1, len(plots)) bar_data = subplot.bar(adjusted_x, plot['y'], width=width, yerr=plot['errors'], facecolor=color, label=plot['label']) bars.append(bar_data[0]) # Construct the information for the drilldowns. # See comment in _create_line for why we need a separate loop to do this. for plot_index, plot in enumerate(plots): adjusted_x = _get_adjusted_bar(plot['x'], width, plot_index + 1, len(plots)) # Let matplotlib plot the data, so that we can get the data-to-image # coordinate transforms line = subplot.plot(adjusted_x, plot['y'], linestyle='None')[0] label = plot['label'] upper_left_coords = line.get_transform().transform( zip(adjusted_x, plot['y'])) bottom_right_coords = line.get_transform().transform([ (x + width, 0) for x in adjusted_x ]) # Get the drilldown query drill = plot_info.query_dict['__' + label + '__'] # Set the title attributes x_labels = [labels[x] for x in plot['x']] titles = [ '%s - %s: %f' % (plot['label'], label, y) for label, y in zip(x_labels, plot['y']) ] params = [ dict(query=drill, series=plot['label'], param=x_label) for x_label in x_labels ] area_data += [ dict(left=ulx, top=height - uly, right=brx, bottom=height - bry, title=title, callback=plot_info.drilldown_callback, callback_arguments=param_dict) for (ulx, uly), (brx, bry), title, param_dict in zip( upper_left_coords, bottom_right_coords, titles, params) ] figure.legend(bars, [plot['label'] for plot in plots]) return (figure, area_data)
def _create_line(plots, labels, plot_info): """ Given all the data for the metrics, create a line plot. plots: list of dicts containing the plot data. Each dict contains: x: list of x-values for the plot y: list of corresponding y-values errors: errors for each data point, or None if no error information available label: plot title labels: list of x-tick labels plot_info: a MetricsPlot """ # when we're doing any kind of normalization, all series get put into a # single plot single = bool(plot_info.normalize_to) area_data = [] lines = [] if single: plot_height = _SINGLE_PLOT_HEIGHT else: plot_height = _MULTIPLE_PLOT_HEIGHT_PER_PLOT * len(plots) figure, height = _create_figure(plot_height) if single: subplot = figure.add_subplot(1, 1, 1) # Plot all the data for plot_index, (plot, color) in enumerate(zip(plots, _colors(len(plots)))): needs_invert = (plot['label'] in plot_info.inverted_series) # Add a new subplot, if user wants multiple subplots # Also handle axis inversion for subplots here if not single: subplot = figure.add_subplot(len(plots), 1, plot_index + 1) subplot.set_title(plot['label']) if needs_invert: # for separate plots, just invert the y-axis subplot.set_ylim(1, 0) elif needs_invert: # for a shared plot (normalized data), need to invert the y values # manually, since all plots share a y-axis plot['y'] = [-y for y in plot['y']] # Plot the series subplot.set_xticks(range(0, len(labels))) subplot.set_xlim(-1, len(labels)) if single: lines += subplot.plot(plot['x'], plot['y'], label=plot['label'], marker=_MULTIPLE_PLOT_MARKER_TYPE, markersize=_MULTIPLE_PLOT_MARKER_SIZE) error_bar_color = lines[-1].get_color() else: lines += subplot.plot(plot['x'], plot['y'], _SINGLE_PLOT_STYLE, label=plot['label']) error_bar_color = _SINGLE_PLOT_ERROR_BAR_COLOR if plot['errors']: subplot.errorbar(plot['x'], plot['y'], linestyle='None', yerr=plot['errors'], color=error_bar_color) subplot.set_xticklabels([]) # Construct the information for the drilldowns. # We need to do this in a separate loop so that all the data is in # matplotlib before we start calling transform(); otherwise, it will return # incorrect data because it hasn't finished adjusting axis limits. for line in lines: # Get the pixel coordinates of each point on the figure x = line.get_xdata() y = line.get_ydata() label = line.get_label() icoords = line.get_transform().transform(zip(x, y)) # Get the appropriate drilldown query drill = plot_info.query_dict['__' + label + '__'] # Set the title attributes (hover-over tool-tips) x_labels = [labels[x_val] for x_val in x] titles = ['%s - %s: %f' % (label, x_label, y_val) for x_label, y_val in zip(x_labels, y)] # Get the appropriate parameters for the drilldown query params = [dict(query=drill, series=line.get_label(), param=x_label) for x_label in x_labels] area_data += [dict(left=ix - 5, top=height - iy - 5, right=ix + 5, bottom=height - iy + 5, title=title, callback=plot_info.drilldown_callback, callback_arguments=param_dict) for (ix, iy), title, param_dict in zip(icoords, titles, params)] subplot.set_xticklabels(labels, rotation=90, size=_LINE_XTICK_LABELS_SIZE) # Show the legend if there are not multiple subplots if single: font_properties = matplotlib.font_manager.FontProperties( size=_LEGEND_FONT_SIZE) legend = figure.legend(lines, [plot['label'] for plot in plots], prop=font_properties, handlelen=_LEGEND_HANDLE_LENGTH, numpoints=_LEGEND_NUM_POINTS) # Workaround for matplotlib not keeping all line markers in the legend - # it seems if we don't do this, matplotlib won't keep all the line # markers in the legend. for line in legend.get_lines(): line.set_marker(_LEGEND_MARKER_TYPE) return (figure, area_data)
def _create_bar(plots, labels, plot_info): """ Given all the data for the metrics, create a line plot. plots: list of dicts containing the plot data. x: list of x-values for the plot y: list of corresponding y-values errors: errors for each data point, or None if no error information available label: plot title labels: list of x-tick labels plot_info: a MetricsPlot """ area_data = [] bars = [] figure, height = _create_figure(_SINGLE_PLOT_HEIGHT) # Set up the plot subplot = figure.add_subplot(1, 1, 1) subplot.set_xticks(range(0, len(labels))) subplot.set_xlim(-1, len(labels)) subplot.set_xticklabels(labels, rotation=90, size=_BAR_XTICK_LABELS_SIZE) # draw a bold line at y=0, making it easier to tell if bars are dipping # below the axis or not. subplot.axhline(linewidth=2, color='black') # width here is the width for each bar in the plot. Matplotlib default is # 0.8. width = 0.8 / len(plots) # Plot the data for plot_index, (plot, color) in enumerate(zip(plots, _colors(len(plots)))): # Invert the y-axis if needed if plot['label'] in plot_info.inverted_series: plot['y'] = [-y for y in plot['y']] adjusted_x = _get_adjusted_bar(plot['x'], width, plot_index + 1, len(plots)) bar_data = subplot.bar(adjusted_x, plot['y'], width=width, yerr=plot['errors'], facecolor=color, label=plot['label']) bars.append(bar_data[0]) # Construct the information for the drilldowns. # See comment in _create_line for why we need a separate loop to do this. for plot_index, plot in enumerate(plots): adjusted_x = _get_adjusted_bar(plot['x'], width, plot_index + 1, len(plots)) # Let matplotlib plot the data, so that we can get the data-to-image # coordinate transforms line = subplot.plot(adjusted_x, plot['y'], linestyle='None')[0] label = plot['label'] upper_left_coords = line.get_transform().transform(zip(adjusted_x, plot['y'])) bottom_right_coords = line.get_transform().transform( [(x + width, 0) for x in adjusted_x]) # Get the drilldown query drill = plot_info.query_dict['__' + label + '__'] # Set the title attributes x_labels = [labels[x] for x in plot['x']] titles = ['%s - %s: %f' % (plot['label'], label, y) for label, y in zip(x_labels, plot['y'])] params = [dict(query=drill, series=plot['label'], param=x_label) for x_label in x_labels] area_data += [dict(left=ulx, top=height - uly, right=brx, bottom=height - bry, title=title, callback=plot_info.drilldown_callback, callback_arguments=param_dict) for (ulx, uly), (brx, bry), title, param_dict in zip(upper_left_coords, bottom_right_coords, titles, params)] figure.legend(bars, [plot['label'] for plot in plots]) return (figure, area_data)
# Loop over the experiments requested on the command line for experiment in options.experiments: print 'ISMIP-HOM', experiment.upper() # Create the figure on which the plot axes will be placed figure = pyplot.figure(subplotpars=matplotlib.figure.SubplotParams(top=.85,bottom=.15)) figure.text(0.5,0.92,'ISMIP-HOM Experiment '+experiment.upper(),horizontalalignment='center',size='large') if options.subtitle: figure.text(0.5,0.89,options.subtitle,horizontalalignment='center',size='small') figure.text(0.5,0.1,'Normalized X coordinate',horizontalalignment='center',size='small') figure.text(0.06,0.5,'Ice Speed (m/a)',rotation='vertical',verticalalignment='center') # Create the (three column) legend prop = matplotlib.font_manager.FontProperties(size='x-small') Line2D = matplotlib.lines.Line2D([],[],color=(0,0,0)) figure.legend([Line2D],['Model Output'],loc=(0.1,0.05),prop=prop).draw_frame(False) Line2D.set_linestyle(':') Line2D.set_color((1,0,0)) Patch = matplotlib.patches.Patch(edgecolor=None,facecolor=(1,0,0),alpha=0.25) figure.legend([Line2D,Patch],['Full Stokes Mean','Full Stokes Std. Dev.'],loc=(0.3,0.02),prop=prop).draw_frame(False) Line2D.set_color((0,0,1)) Patch.set_facecolor((0,0,1)) figure.legend([Line2D,Patch],[nonFSmodelType+' Mean',nonFSmodelType+' Std. Dev.'],loc=(0.55,0.02),prop=prop).draw_frame(False) # Loop over the sizes requested on the command line for i, size in enumerate(map(int,options.sizes)): try: if experiment == 'f': if size != 100 or len(options.sizes) > 1: print 'NOTE: Experiment f uses a domain size of 100 km only'
def main(): """ Plot the ISMIP-HOM test results. """ if args.all_ps: nonFSmodelType = 'All Partial Stokes' else: nonFSmodelType = 'First Order' print 'NOTE: The category being used for models approximating Full Stokes is: ' + nonFSmodelType print 'For more information, see details of option -a by invoking: python plotISMIP_HOM.py --help \n' # ========================================================= # First generate the standard ascii files defined by ISMIP-HOM # (This used to be in the run script, but is more useful and natural here.) # Note: It is not strictly necessary to generate these files # just to plot the results, but this approach is left in # to use existing code, and on the off-chance it is ever # necessary to generate these standard ascii ISMIP-HOM files... # ========================================================= pattern = get_file_pattern() experiments, sizes = get_experiments_and_sizes(pattern) if args.sizes and (set(args.sizes) <= set(map(int, sizes))): sizes = args.sizes elif args.sizes: print("ERROR: No output files found for that size!") print( " Run `ls " + pattern.replace('-?', '-[a-e]') + "`\non the command line to see the set of output files. (Note, test f's size is ignored.)" ) sys.exit(1) # sort experiments and sizes sizes.sort() experiments.sort() # Loop over the experiments for experiment in experiments: # Loop over the sizes for size in map(int, sizes): try: # Extract the output data for comparison to the other models # NOTE: The script now assumes that uvel_extend and vvel_extend are ALWAYS present. # Those fields contain the ice velocity computed at the upper right corner of each grid cell. # They appear to be on the x1,y1 grid in their metadata but are actually on the x0,y0 grid. # The additional row/column include the first halo value past ewn/nsn. # That value is valid at both 0.0 and 1.0 on the non-dimensional coordinate system. # Matrix manipulations for each test case below are done to create a larger matrix that goes from 0.0 to 1.0, inclusive. # NOTE: The cases below are only writing [x,y,u,v] to the text file. This is the minimum needed to compare to other models. # In the future, the other additional fields specified in section 4 of http://homepages.ulb.ac.be/~fpattyn/ismip/ismiphom.pdf # can be added. wvel and the stresses are on the x1,y1 grid, so they would need to be interpolated to the x0,y0 grid # since we are using that as the coordinate system in the text files. # Open the netCDF file that was written by CISM # Standard filename format used by both scripts if experiment == 'f': size = 100 out_file = pattern.replace('-?', '-' + experiment).replace( '????', str(size).zfill(4)) netCDFfile = NetCDFFile(out_file, 'r') if netCDF_module == 'Scientific.IO.NetCDF': velscale = netCDFfile.variables['uvel_extend'].scale_factor else: velscale = 1.0 if experiment in [ 'f', ]: # Convert CISM output data to the rotated coord system used by the problem setup alpha = -3.0 * pi / 180 # defined in run script if netCDF_module == 'Scientific.IO.NetCDF': thkscale = netCDFfile.variables['thk'].scale_factor wvelscale = netCDFfile.variables[ 'wvel_ho'].scale_factor else: thkscale = 1.0 wvelscale = 1.0 usurf = netCDFfile.variables['usurf'][ -1, :, :] * thkscale # get last time level usurfStag = (usurf[1:, 1:] + usurf[1:, :-1] + usurf[:-1, :-1] + usurf[:-1, :-1]) / 4.0 uvelS = netCDFfile.variables['uvel'][ -1, 0, :, :] * velscale # top level of last time vvelS = netCDFfile.variables['vvel'][ -1, 0, :, :] * velscale # top level of last time wvelS = netCDFfile.variables['wvel_ho'][ -1, 0, :, :] * wvelscale # top level of last time wvelStag = (wvelS[1:, 1:] + wvelS[1:, :-1] + wvelS[:-1, :-1] + wvelS[:-1, :-1]) / 4.0 x0 = netCDFfile.variables['x0'][:] y0 = netCDFfile.variables['y0'][:] # calculate rotated xprime coordinates along the surface - xx, yy are used by code below to write the output xx = x0 * cos(alpha) + (usurfStag[20, :] - 7000.0) * sin(alpha) xx = xx / 1000.0 - 50.0 yy = y0 / 1000.0 - 50.0 # calculate rotated uvel/vvel at surface uvelSprime = uvelS[:, :] * cos( alpha) + wvelStag[:, :] * sin(alpha) wvelSprime = -uvelS[:, :] * sin( alpha) + wvelStag[:, :] * cos(alpha) nan = np.ones( uvelSprime.shape ) * -999.0 # create a dummy matrix for uncalculated values. # =========================================== # optional bit of code to plot out vertical velocity on the rotated grid. # This can be compared to Fig. 14b in the tc-2007-0019-sp3.pdf document figure = pyplot.figure(subplotpars=matplotlib.figure. SubplotParams(top=.85, bottom=.15)) axes = figure.add_subplot(111) pc = axes.pcolor(wvelSprime) pyplot.colorbar(pc) cntr = axes.contour(wvelSprime, [ -0.5, -0.4, -0.3, -0.2, -0.1, 0.0, 0.1, 0.2, 0.3, 0.4 ], colors='k') axes.clabel(cntr) pyplot.title('Compare to Fig. 14b of tc-2007-0019-sp3.pdf') pyplot.draw() pyplot.show() # =========================================== else: # all other tests # Make x,y position arrays that can be used by all test cases. # Want x/y positions to include the periodic edge at both the beginning and end xx = netCDFfile.variables['x0'][:] / (1000.0 * float(size)) xx = np.concatenate(([0.0], xx, [1.0])) yy = netCDFfile.variables['y0'][:] / (1000.0 * float(size)) yy = np.concatenate(([0.0], yy, [1.0])) if experiment in ('b', 'd'): yy = ( yy[len(yy) / 2], ) # for the 2-d experiments, just use the middle y-index # Figure out u,v since all experiments needs at least one of them (avoids duplicate code in each case below # Want to use last time level. Most experiments should only have a single time level, but F may have many in the file. # Apparently some older versions of netCDF4 give an error when using the -1 dimension if the size is 1, hence this bit of seemingly unnecessary logic... if netCDFfile.variables['uvel_extend'][:].shape[0] == 1: t = 0 else: t = -1 us = netCDFfile.variables['uvel_extend'][ t, 0, :, :] * velscale # top level of last time us = np.concatenate( (us[:, -1:], us), axis=1) # copy the column at x=1.0 to x=0.0 us = np.concatenate( (us[-1:, :], us), axis=0) # copy the row at y=1.0 to y=0.0 vs = netCDFfile.variables['vvel_extend'][ t, 0, :, :] * velscale # top level of last time vs = np.concatenate( (vs[:, -1:], vs), axis=1) # copy the column at x=1.0 to x=0.0 vs = np.concatenate( (vs[-1:, :], vs), axis=0) # copy the row at y=1.0 to y=0.0 ub = netCDFfile.variables['uvel_extend'][ t, -1, :, :] * velscale # bottom level of last time ub = np.concatenate( (ub[:, -1:], ub), axis=1) # copy the column at x=1.0 to x=0.0 ub = np.concatenate( (ub[-1:, :], ub), axis=0) # copy the row at y=1.0 to y=0.0 vb = netCDFfile.variables['vvel_extend'][ t, -1, :, :] * velscale # bottom level of last time vb = np.concatenate( (vb[:, -1:], vb), axis=1) # copy the column at x=1.0 to x=0.0 vb = np.concatenate( (vb[-1:, :], vb), axis=0) # copy the row at y=1.0 to y=0.0 #nan = ub*np.NaN # create a dummy matrix for uncalculated values. nan = np.ones( ub.shape ) * -999.0 # create a dummy matrix for uncalculated values. # make arrays of the variables needed for each experiment # the extended-grid velocities have the periodic edge in the last x-position. We also want it in the first x-position. # After building the 2-d array as needed for each variable from the raw file data, then build a list called 'data'. if experiment == 'a': # This is supposed to be: [('uvel',0),('vvel',0),('wvel',0),('tau_xz',-1),('tau_yz',-1),[deltap]] data = (us, vs, nan, nan, nan, nan) elif experiment == 'b': # This is supposed to be: uvel(0), wvel(0), tau_xz(-1), deltaP data = (us, nan, nan, nan) elif experiment == 'c': # This is supposed to be: [uvel',0),('vvel',0),('wvel',0),('uvel',-1),('vvel',-1),('tau_xz',-1),('tau_yz',-1), deltap] data = (us, vs, nan, ub, vb, nan, nan, nan) elif experiment == 'd': # This is supposed to be: [('uvel',0),('wvel',0),('uvel',-1),('tau_xz',-1), deltap] data = (us, nan, nan, nan, nan) elif experiment == 'f': # should be: x, y, zs, vx, vy, vz # This is supposed to be: [('usurf',None),('uvel',0),('vvel',0),('wvel',0)] data = (nan, uvelSprime, vvelS, nan) # Write a "standard" ISMIP-HOM file (example file name: "cis1a020.txt") in the "output" subdirectory ISMIP_HOMfilename = out_file.replace('.out.nc', '.txt') ISMIP_HOMfile = open(ISMIP_HOMfilename, 'w') for i, x in enumerate(xx): for j, y in enumerate(yy): if experiment in ('a', 'c', 'f'): # include x and y positions ISMIP_HOMfile.write('\t'.join( map(str, [x, y] + [v[j, i] for (v) in data])) + '\n') else: # only include x position ISMIP_HOMfile.write('\t'.join( map(str, [x] + [v[j, i] for (v) in data])) + '\n') ISMIP_HOMfile.close() netCDFfile.close() except: print 'Warning: The CISM output file for experiment ' + experiment + ' at size ' + str( size) + ' could NOT be read successfully!' raise # ========================================================= # Now actually analyze the results. # ========================================================= # Loop over the experiments requested on the command line for experiment in experiments: print 'ISMIP-HOM', experiment.upper() # Create the figure on which the plot axes will be placed figure = pyplot.figure( subplotpars=matplotlib.figure.SubplotParams(top=.85, bottom=.15)) figure.text(0.5, 0.92, 'ISMIP-HOM Experiment ' + experiment.upper(), horizontalalignment='center', size='large') if args.subtitle: figure.text(0.5, 0.89, args.subtitle, horizontalalignment='center', size='small') figure.text(0.5, 0.1, 'Normalized X coordinate', horizontalalignment='center', size='small') figure.text(0.06, 0.5, 'Ice Speed (m/a)', rotation='vertical', verticalalignment='center') # Create the (three column) legend prop = matplotlib.font_manager.FontProperties(size='x-small') Line2D = matplotlib.lines.Line2D([], [], color=(0, 0, 0)) figure.legend([Line2D], ['Model Output'], loc=(0.1, 0.05), prop=prop).draw_frame(False) Line2D.set_linestyle(':') Line2D.set_color((1, 0, 0)) Patch = matplotlib.patches.Patch(edgecolor=None, facecolor=(1, 0, 0), alpha=0.25) figure.legend([Line2D, Patch], ['Full Stokes Mean', 'Full Stokes Std. Dev.'], loc=(0.3, 0.02), prop=prop).draw_frame(False) Line2D.set_color((0, 0, 1)) Patch.set_facecolor((0, 0, 1)) figure.legend( [Line2D, Patch], [nonFSmodelType + ' Mean', nonFSmodelType + ' Std. Dev.'], loc=(0.55, 0.02), prop=prop).draw_frame(False) # Loop over the sizes requested on the command line for i, size in enumerate(map(int, sizes)): try: if experiment == 'f': if size != 100 or len(sizes) > 1: print 'NOTE: Experiment f uses a domain size of 100 km only' size = 100 out_file = pattern.replace('-?', '-' + experiment).replace( '????', str(size).zfill(4)) # Create the plot axes for this domain size if len(sizes) == 1: axes = figure.add_subplot(111) else: axes = figure.add_subplot(2, 3, i + 1) for tick in axes.xaxis.get_major_ticks(): tick.label1.set_fontsize('xx-small') for tick in axes.yaxis.get_major_ticks(): tick.label1.set_fontsize('xx-small') axes.set_title('%d km' % size, size='medium') # Get the Glimmer output data glimmerData = read(out_file.replace('.out.nc', '.txt'), experiment) # The Glimmer data is on a staggered grid; # Interpolate to obtain the value at x=0 and x=1 # using periodic boundary conditions #v = (glimmerData[0][1] + glimmerData[-1][1])/2 #glimmerData = [(0.0,v)]+glimmerData+[(1.0,v)] # Plot the Glimmer data axes.plot([row[0] for row in glimmerData], [row[1] for row in glimmerData], color='black') # Get the data from other models for comparison firstOrder = 0 fullStokes = 1 count = [0, 0] sumV = [[0.0 for v in glimmerData], [0.0 for v in glimmerData]] sumV2 = [[0.0 for v in glimmerData], [0.0 for v in glimmerData]] for (path, directories, filenames) in os.walk('ismip_all'): for filename in filenames: modelName = filename[0:4] modelExperiment = filename[4] modelSize = filename[5:8] #print 'name, exp, size', modelName, modelExperiment, modelSize if modelName == 'aas1': # Skip the 'aas1' model because its output files in the tc-2007-0019-sp2.zip file do not follow the proper naming convention. MJH 11/5/13 continue if (modelExperiment != experiment) or (modelExperiment != 'f' and int(modelSize) != size) \ or (not args.all_ps and not modelName in lmlaModels + fullStokesModels): continue # continue next loop iteration if not the size or not the experiment desired or if we just want FO comparison and this model is not FO or FS. elif (modelExperiment == 'f'): if (modelSize == '001'): continue # ignore the sliding version for now if modelName == 'cma1': continue # the cma1 'f' experiments made the x,y coords dimensionless instead of dimensional - ignore for convenience print 'Using data from file:', os.path.join( path, filename) data = read(os.path.join(path, filename), experiment) if modelName in fullStokesModels: index = fullStokes else: index = firstOrder count[index] += 1 #axes.plot([row[0] for row in data], [row[1] for row in data] ) ## OPTIONAL: print out every individual model in its native x-coordinates. # Interpolate onto the x values from the Glimmer model run for (i, target) in enumerate( [row[0] for row in glimmerData]): below = -99999.0 above = 99999.0 for (j, x) in enumerate([row[0] for row in data]): if below < x <= target: b, below = j, x if target <= x < above: a, above = j, x if above == below: v = data[a][1] else: if below == -99999.0: # Use the periodic boundary condition at x = 0 xBelow = data[-1][0] - 1 vBelow = data[-1][1] else: xBelow, vBelow = data[b] if above == 99999.0: # Use the periodic boundary condition at x = 1 xAbove = data[0][0] + 1 vAbove = data[0][1] else: xAbove, vAbove = data[a] if xAbove == xBelow: print 'Surprise!', above, below, xAbove, xBelow, vAbove, vBelow v = (vAbove + vBelow) / 2 else: alpha = (target - xBelow) / (xAbove - xBelow) v = alpha * vAbove + (1 - alpha) * vBelow sumV[index][i] += v sumV2[index][i] += v * v # Calculate statistics of the other model results if sum(count) == 0: print 'To compare with other models you need to download the ISMIP-HOM results from: http://www.the-cryosphere.net/2/95/2008/tc-2-95-2008-supplement.zip and unzip the contained file tc-2007-0019-sp2.zip into a directory named ismip_all. The ismip_all directory must be in the directory from which you are running this script.' else: # Find the mean and standard deviation of the velocities at each x for index in (firstOrder, fullStokes): if count[index] == 0: continue mean = list() standardDeviation = list() for i in range(len(glimmerData)): mean.append(sumV[index][i] / count[index]) standardDeviation.append( sqrt(sumV2[index][i] / count[index] - mean[-1]**2)) # Plot the mean using a dotted line color = ( index, 0, 1 - index ) # blue for first order (index=0); red for full Stokes (index=1) x = [row[0] for row in glimmerData] axes.plot(x, mean, ':', color=color) # Plot a filled polygon showing the mean plus and minus one standard deviation meanMinusSD = [ m - sd for (m, sd) in zip(mean, standardDeviation) ] meanPlusSD = [ m + sd for (m, sd) in zip(mean, standardDeviation) ] x = x + list(reversed(x)) y = meanPlusSD + list(reversed(meanMinusSD)) axes.fill(x, y, facecolor=color, edgecolor=color, alpha=0.25) if index == firstOrder: # Calculate some statistics comparing the Glimmer data with the other models pcterror = [ 100.0 * abs(glimmer - others) / others for (glimmer, others ) in zip([row[1] for row in glimmerData], mean) ] abserror = [ abs(glimmer - others) for (glimmer, others ) in zip([row[1] for row in glimmerData], mean) ] maximum = max(pcterror) position = glimmerData[pcterror.index(maximum)][0] total = sum([e for e in pcterror]) compare = sum([ (s / m) for (s, m) in zip(standardDeviation, mean) ]) n = len(glimmerData) #print '\t'.join([str(size)+' km',str(total/n),str(compare/n),str(position)]) print 'Size=' + str(size) + ' km' print ' Mean percent error along flowline of CISM relative to mean of first-order models=' + str( total / float(n)) + '%' print ' Mean COD (stdev/mean) along flowline of mean of first-order models (excluding CISM)=' + str( compare / float(n) * 100.0) + '%' print ' Max. CISM percent error=' + str( maximum) + '% at x-position ' + str(position) print ' Max. CISM absolute error=' + str( max(abserror)) + ' m/yr at x-position ' + str( glimmerData[abserror.index( max(abserror))][0]) except: print "Error in analyzing/plotting experiment ", experiment, " at size ", size, " km" if savePlotInFile: plot_file = pattern.replace('-?', '-' + experiment).replace( '.????', '').replace('.out.nc', plotType) print 'Writing:', plot_file pyplot.savefig(plot_file) # Experiment f can also have a surface profile plotted if experiment == 'f': # rather than getting the data from the text file, we are going to read it directly. # this is because the velocities and usrf are on different grids, so it is difficult to include them # both in the standard ISMIP-HOM text file format that has a single x,y coord. system size = 100 out_file = pattern.replace('-?', '-' + experiment).replace( '????', str(size).zfill(4)) netCDFfile = NetCDFFile(out_file, 'r') if netCDF_module == 'Scientific.IO.NetCDF': thkscale = netCDFfile.variables['thk'].scale_factor else: thkscale = 1.0 usurf = netCDFfile.variables['usurf'][ -1, :, :] * thkscale # get last time level x1 = netCDFfile.variables['x1'][:] y1 = netCDFfile.variables['y1'][:] # Create the usurf figure ufigure = pyplot.figure( subplotpars=matplotlib.figure.SubplotParams(top=.85, bottom=.15)) ufigure.text(0.5, 0.92, 'ISMIP-HOM Experiment F: Surface elevation', horizontalalignment='center', size='large') if args.subtitle: ufigure.text(0.5, 0.89, args.subtitle, horizontalalignment='center', size='small') ufigure.text(0.5, 0.1, 'X coordinate', horizontalalignment='center', size='small') ufigure.text(0.06, 0.5, 'upper surface (m)', rotation='vertical', verticalalignment='center') # Create the (three column) legend prop = matplotlib.font_manager.FontProperties(size='x-small') Line2D = matplotlib.lines.Line2D([], [], color=(0, 0, 0)) ufigure.legend([Line2D], ['Model Output'], loc=(0.1, 0.05), prop=prop).draw_frame(False) Line2D.set_linestyle(':') Line2D.set_color((1, 0, 0)) Patch = matplotlib.patches.Patch(edgecolor=None, facecolor=(1, 0, 0), alpha=0.25) ufigure.legend([Line2D, Patch], ['Full Stokes Mean', 'Full Stokes Std. Dev.'], loc=(0.3, 0.02), prop=prop).draw_frame(False) Line2D.set_color((0, 0, 1)) Patch.set_facecolor((0, 0, 1)) ufigure.legend( [Line2D, Patch], [nonFSmodelType + ' Mean', nonFSmodelType + ' Std. Dev.'], loc=(0.55, 0.02), prop=prop).draw_frame(False) # Create the plot axes axes2 = ufigure.add_subplot(111) axes2.set_title('%d km' % size, size='medium') # Convert CISM output data to the rotated coord system used by the problem setup alpha = -3.0 * pi / 180 # defined in run script # use integer floor division operator to get an index close to the center TODO should be interpolating if needed... yp = len(y1) // 2 # calculate rotated xprime, zprime coordinates along the surface (this is the coord. sys. used for this test case) xprime = x1 * cos(alpha) + (usurf[yp, :] - 7000.0) * sin(alpha) xprime = xprime / 1000.0 - 50.0 zprime = -x1 * sin(alpha) + (usurf[yp, :] - 7000.0) * cos(alpha) # Plot CISM output axes2.plot(xprime, zprime, color='black') # create glimmerData so we can re-use the code from above glimmerData = list() for i in range(len(xprime)): glimmerData.append(tuple([xprime[i], zprime[i]])) # Now plot the other models - yucky code copied from above # Get the data from other models for comparison firstOrder = 0 fullStokes = 1 count = [0, 0] sumV = [[0.0 for v in glimmerData], [0.0 for v in glimmerData]] sumV2 = [[0.0 for v in glimmerData], [0.0 for v in glimmerData]] for (path, directories, filenames) in os.walk('ismip_all'): for filename in filenames: modelName = filename[0:4] modelExperiment = filename[4] modelSize = filename[5:8] #print 'name, exp, size', modelName, modelExperiment, modelSize if modelName == 'aas1': # Skip the 'aas1' model because its output files in the tc-2007-0019-sp2.zip file do not follow the proper naming convention. MJH 11/5/13 continue if (modelExperiment != experiment) or (modelExperiment != 'f' and int(modelSize) != size) \ or (not args.all_ps and not modelName in lmlaModels + fullStokesModels): continue # continue next loop iteration if not the size or not the experiment desired or if we just want FO comparison and this model is not FO or FS. elif (modelExperiment == 'f'): if (modelSize == '001'): continue # ignore the sliding version for now if modelName == 'cma1': continue # the cma1 'f' experiments made the x,y coords dimensionless instead of dimensional - ignore for convenience print 'Using data from file:', os.path.join(path, filename) data = read(os.path.join(path, filename), experiment='f-elevation') if modelName in fullStokesModels: index = fullStokes else: index = firstOrder count[index] += 1 #axes2.plot([row[0] for row in data], [row[1] for row in data] ) ## OPTIONAL: print out every individual model in its native x-coordinates. # Interpolate onto the x values from the Glimmer model run for (i, target) in enumerate([row[0] for row in glimmerData]): below = -99999.0 above = 99999.0 for (j, x) in enumerate([row[0] for row in data]): if below < x <= target: b, below = j, x if target <= x < above: a, above = j, x if above == below: v = data[a][1] else: if below == -99999.0: # Use the periodic boundary condition at x = 0 xBelow = data[-1][0] - 1 vBelow = data[-1][1] else: xBelow, vBelow = data[b] if above == 99999.0: # Use the periodic boundary condition at x = 1 xAbove = data[0][0] + 1 vAbove = data[0][1] else: xAbove, vAbove = data[a] if xAbove == xBelow: print 'Surprise!', above, below, xAbove, xBelow, vAbove, vBelow v = (vAbove + vBelow) / 2 else: alpha = (target - xBelow) / (xAbove - xBelow) v = alpha * vAbove + (1 - alpha) * vBelow sumV[index][i] += v sumV2[index][i] += v * v # Calculate statistics of the other model results if sum(count) == 0: print 'To compare with other models you need to download the ISMIP-HOM results from: http://www.the-cryosphere.net/2/95/2008/tc-2-95-2008-supplement.zip and unzip the contained file tc-2007-0019-sp2.zip into a directory named ismip_all. The ismip_all directory must be in the directory from which you are running this script.' else: # Find the mean and standard deviation of the velocities at each x for index in (firstOrder, fullStokes): if count[index] == 0: continue mean = list() standardDeviation = list() for i in range(len(glimmerData)): mean.append(sumV[index][i] / count[index]) standardDeviation.append( sqrt(sumV2[index][i] / count[index] - mean[-1]**2)) # Plot the mean using a dotted line color = ( index, 0, 1 - index ) # blue for first order (index=0); red for full Stokes (index=1) x = [row[0] for row in glimmerData] axes2.plot(x, mean, ':', color=color) # Plot a filled polygon showing the mean plus and minus one standard deviation meanMinusSD = [ m - sd for (m, sd) in zip(mean, standardDeviation) ] meanPlusSD = [ m + sd for (m, sd) in zip(mean, standardDeviation) ] x = x + list(reversed(x)) y = meanPlusSD + list(reversed(meanMinusSD)) axes2.fill(x, y, facecolor=color, edgecolor=color, alpha=0.25) if savePlotInFile: plot_file = pattern.replace( '-?', '-' + experiment + '-SurfaceElevation').replace( '.????', '').replace('.out.nc', plotType) print 'Writing:', plot_file pyplot.savefig(plot_file) # Experiment f should be run for one size (100 km) only if experiment == 'f': break if not savePlotInFile: pyplot.show()
horizontalalignment='center', size='small') figure.text(0.5, 0.1, 'Normalized X coordinate', horizontalalignment='center', size='small') figure.text(0.06, 0.5, 'Ice Speed (m/a)', rotation='vertical', verticalalignment='center') # Create the (three column) legend prop = matplotlib.font_manager.FontProperties(size='x-small') Line2D = matplotlib.lines.Line2D([], [], color=(0, 0, 0)) figure.legend([Line2D], ['Model Output'], loc=(0.1, 0.05), prop=prop).draw_frame(False) Line2D.set_linestyle(':') Line2D.set_color((1, 0, 0)) Patch = matplotlib.patches.Patch(edgecolor=None, facecolor=(1, 0, 0), alpha=0.25) figure.legend([Line2D, Patch], ['Full Stokes Mean', 'Full Stokes Std. Dev.'], loc=(0.3, 0.02), prop=prop).draw_frame(False) Line2D.set_color((0, 0, 1)) Patch.set_facecolor((0, 0, 1)) figure.legend([Line2D, Patch], ['First Order Mean', 'First Order Std. Dev.'], loc=(0.55, 0.02), prop=prop).draw_frame(False)
def main(): """ Plot the ISMIP-HOM test results. """ if args.all_ps: nonFSmodelType='All Partial Stokes' else: nonFSmodelType='First Order' print 'NOTE: The category being used for models approximating Full Stokes is: '+nonFSmodelType print 'For more information, see details of option -a by invoking: python plotISMIP_HOM.py --help \n' # ========================================================= # First generate the standard ascii files defined by ISMIP-HOM # (This used to be in the run script, but is more useful and natural here.) # Note: It is not strictly necessary to generate these files # just to plot the results, but this approach is left in # to use existing code, and on the off-chance it is ever # necessary to generate these standard ascii ISMIP-HOM files... # ========================================================= pattern = get_file_pattern() experiments, sizes = get_experiments_and_sizes(pattern) if args.sizes and (set(args.sizes) <= set(map(int,sizes))): sizes = args.sizes elif args.sizes: print("ERROR: No output files found for that size!") print(" Run `ls "+pattern.replace('-?','-[a-e]')+"`\non the command line to see the set of output files. (Note, test f's size is ignored.)") sys.exit(1) # sort experiments and sizes sizes.sort() experiments.sort() # Loop over the experiments for experiment in experiments: # Loop over the sizes for size in map(int,sizes): try: # Extract the output data for comparison to the other models # NOTE: The script now assumes that uvel_extend and vvel_extend are ALWAYS present. # Those fields contain the ice velocity computed at the upper right corner of each grid cell. # They appear to be on the x1,y1 grid in their metadata but are actually on the x0,y0 grid. # The additional row/column include the first halo value past ewn/nsn. # That value is valid at both 0.0 and 1.0 on the non-dimensional coordinate system. # Matrix manipulations for each test case below are done to create a larger matrix that goes from 0.0 to 1.0, inclusive. # NOTE: The cases below are only writing [x,y,u,v] to the text file. This is the minimum needed to compare to other models. # In the future, the other additional fields specified in section 4 of http://homepages.ulb.ac.be/~fpattyn/ismip/ismiphom.pdf # can be added. wvel and the stresses are on the x1,y1 grid, so they would need to be interpolated to the x0,y0 grid # since we are using that as the coordinate system in the text files. # Open the netCDF file that was written by CISM # Standard filename format used by both scripts if experiment == 'f': size = 100 out_file = pattern.replace('-?','-'+experiment).replace('????',str(size).zfill(4)) netCDFfile = NetCDFFile(out_file,'r') if netCDF_module == 'Scientific.IO.NetCDF': velscale = netCDFfile.variables['uvel_extend'].scale_factor else: velscale = 1.0 if experiment in ['f',]: # Convert CISM output data to the rotated coord system used by the problem setup alpha = -3.0 * pi/180 # defined in run script if netCDF_module == 'Scientific.IO.NetCDF': thkscale = netCDFfile.variables['thk'].scale_factor wvelscale = netCDFfile.variables['wvel_ho'].scale_factor else: thkscale = 1.0 wvelscale = 1.0 usurf = netCDFfile.variables['usurf'][-1,:,:] * thkscale # get last time level usurfStag = (usurf[1:,1:] + usurf[1:,:-1] + usurf[:-1,:-1] + usurf[:-1, :-1]) / 4.0 uvelS = netCDFfile.variables['uvel'][-1,0,:,:] * velscale # top level of last time vvelS = netCDFfile.variables['vvel'][-1,0,:,:] * velscale # top level of last time wvelS = netCDFfile.variables['wvel_ho'][-1,0,:,:] * wvelscale # top level of last time wvelStag = (wvelS[1:,1:] + wvelS[1:,:-1] + wvelS[:-1,:-1] + wvelS[:-1, :-1]) / 4.0 x0 = netCDFfile.variables['x0'][:] y0 = netCDFfile.variables['y0'][:] # calculate rotated xprime coordinates along the surface - xx, yy are used by code below to write the output xx = x0 * cos(alpha) + (usurfStag[20,:]-7000.0) * sin(alpha) xx = xx/1000.0 - 50.0 yy = y0/1000.0 - 50.0 # calculate rotated uvel/vvel at surface uvelSprime = uvelS[:,:] * cos(alpha) + wvelStag[:,:] * sin(alpha) wvelSprime = -uvelS[:,:] * sin(alpha) + wvelStag[:,:] * cos(alpha) nan = np.ones(uvelSprime.shape)*-999.0 # create a dummy matrix for uncalculated values. # =========================================== # optional bit of code to plot out vertical velocity on the rotated grid. # This can be compared to Fig. 14b in the tc-2007-0019-sp3.pdf document figure = pyplot.figure(subplotpars=matplotlib.figure.SubplotParams(top=.85,bottom=.15)) axes = figure.add_subplot(111) pc = axes.pcolor(wvelSprime) pyplot.colorbar(pc) cntr=axes.contour(wvelSprime, [-0.5, -0.4, -0.3, -0.2, -0.1, 0.0, 0.1, 0.2, 0.3, 0.4],colors='k') axes.clabel(cntr) pyplot.title('Compare to Fig. 14b of tc-2007-0019-sp3.pdf') pyplot.draw() pyplot.show() # =========================================== else: # all other tests # Make x,y position arrays that can be used by all test cases. # Want x/y positions to include the periodic edge at both the beginning and end xx = netCDFfile.variables['x0'][:]/(1000.0*float(size)) xx = np.concatenate(([0.0],xx,[1.0])) yy = netCDFfile.variables['y0'][:]/(1000.0*float(size)) yy = np.concatenate(([0.0],yy,[1.0])) if experiment in ('b','d'): yy = (yy[len(yy)/2],) # for the 2-d experiments, just use the middle y-index # Figure out u,v since all experiments needs at least one of them (avoids duplicate code in each case below # Want to use last time level. Most experiments should only have a single time level, but F may have many in the file. # Apparently some older versions of netCDF4 give an error when using the -1 dimension if the size is 1, hence this bit of seemingly unnecessary logic... if netCDFfile.variables['uvel_extend'][:].shape[0] == 1: t = 0 else: t = -1 us = netCDFfile.variables['uvel_extend'][t,0,:,:] * velscale # top level of last time us = np.concatenate( (us[:,-1:], us), axis=1) # copy the column at x=1.0 to x=0.0 us = np.concatenate( (us[-1:,:], us), axis=0) # copy the row at y=1.0 to y=0.0 vs = netCDFfile.variables['vvel_extend'][t,0,:,:] * velscale # top level of last time vs = np.concatenate( (vs[:,-1:], vs), axis=1) # copy the column at x=1.0 to x=0.0 vs = np.concatenate( (vs[-1:,:], vs), axis=0) # copy the row at y=1.0 to y=0.0 ub = netCDFfile.variables['uvel_extend'][t,-1,:,:] * velscale # bottom level of last time ub = np.concatenate( (ub[:,-1:], ub), axis=1) # copy the column at x=1.0 to x=0.0 ub = np.concatenate( (ub[-1:,:], ub), axis=0) # copy the row at y=1.0 to y=0.0 vb = netCDFfile.variables['vvel_extend'][t,-1,:,:] * velscale # bottom level of last time vb = np.concatenate( (vb[:,-1:], vb), axis=1) # copy the column at x=1.0 to x=0.0 vb = np.concatenate( (vb[-1:,:], vb), axis=0) # copy the row at y=1.0 to y=0.0 #nan = ub*np.NaN # create a dummy matrix for uncalculated values. nan = np.ones(ub.shape)*-999.0 # create a dummy matrix for uncalculated values. # make arrays of the variables needed for each experiment # the extended-grid velocities have the periodic edge in the last x-position. We also want it in the first x-position. # After building the 2-d array as needed for each variable from the raw file data, then build a list called 'data'. if experiment == 'a': # This is supposed to be: [('uvel',0),('vvel',0),('wvel',0),('tau_xz',-1),('tau_yz',-1),[deltap]] data = (us, vs, nan, nan, nan, nan) elif experiment == 'b': # This is supposed to be: uvel(0), wvel(0), tau_xz(-1), deltaP data = (us, nan, nan, nan) elif experiment == 'c': # This is supposed to be: [uvel',0),('vvel',0),('wvel',0),('uvel',-1),('vvel',-1),('tau_xz',-1),('tau_yz',-1), deltap] data = (us, vs, nan, ub, vb, nan, nan, nan) elif experiment == 'd': # This is supposed to be: [('uvel',0),('wvel',0),('uvel',-1),('tau_xz',-1), deltap] data = (us, nan, nan, nan, nan) elif experiment == 'f': # should be: x, y, zs, vx, vy, vz # This is supposed to be: [('usurf',None),('uvel',0),('vvel',0),('wvel',0)] data = (nan, uvelSprime, vvelS, nan) # Write a "standard" ISMIP-HOM file (example file name: "cis1a020.txt") in the "output" subdirectory ISMIP_HOMfilename = out_file.replace('.out.nc','.txt') ISMIP_HOMfile = open(ISMIP_HOMfilename,'w') for i, x in enumerate(xx): for j, y in enumerate(yy): if experiment in ('a','c','f'): # include x and y positions ISMIP_HOMfile.write('\t'.join(map(str,[x,y]+[v[j,i] for (v) in data]))+'\n') else: # only include x position ISMIP_HOMfile.write('\t'.join(map(str,[x]+[v[j,i] for (v) in data]))+'\n') ISMIP_HOMfile.close() netCDFfile.close() except: print 'Warning: The CISM output file for experiment '+experiment+' at size '+str(size)+' could NOT be read successfully!' raise # ========================================================= # Now actually analyze the results. # ========================================================= # Loop over the experiments requested on the command line for experiment in experiments: print 'ISMIP-HOM', experiment.upper() # Create the figure on which the plot axes will be placed figure = pyplot.figure(subplotpars=matplotlib.figure.SubplotParams(top=.85,bottom=.15)) figure.text(0.5,0.92,'ISMIP-HOM Experiment '+experiment.upper(),horizontalalignment='center',size='large') if args.subtitle: figure.text(0.5,0.89,args.subtitle,horizontalalignment='center',size='small') figure.text(0.5,0.1,'Normalized X coordinate',horizontalalignment='center',size='small') figure.text(0.06,0.5,'Ice Speed (m/a)',rotation='vertical',verticalalignment='center') # Create the (three column) legend prop = matplotlib.font_manager.FontProperties(size='x-small') Line2D = matplotlib.lines.Line2D([],[],color=(0,0,0)) figure.legend([Line2D],['Model Output'],loc=(0.1,0.05),prop=prop).draw_frame(False) Line2D.set_linestyle(':') Line2D.set_color((1,0,0)) Patch = matplotlib.patches.Patch(edgecolor=None,facecolor=(1,0,0),alpha=0.25) figure.legend([Line2D,Patch],['Full Stokes Mean','Full Stokes Std. Dev.'],loc=(0.3,0.02),prop=prop).draw_frame(False) Line2D.set_color((0,0,1)) Patch.set_facecolor((0,0,1)) figure.legend([Line2D,Patch],[nonFSmodelType+' Mean',nonFSmodelType+' Std. Dev.'],loc=(0.55,0.02),prop=prop).draw_frame(False) # Loop over the sizes requested on the command line for i, size in enumerate(map(int,sizes)): try: if experiment == 'f': if size != 100 or len(sizes) > 1: print 'NOTE: Experiment f uses a domain size of 100 km only' size = 100 out_file = pattern.replace('-?','-'+experiment).replace('????',str(size).zfill(4)) # Create the plot axes for this domain size if len(sizes) == 1: axes = figure.add_subplot(111) else: axes = figure.add_subplot(2,3,i+1) for tick in axes.xaxis.get_major_ticks(): tick.label1.set_fontsize('xx-small') for tick in axes.yaxis.get_major_ticks(): tick.label1.set_fontsize('xx-small') axes.set_title('%d km' % size, size='medium') # Get the Glimmer output data glimmerData = read(out_file.replace('.out.nc','.txt'),experiment) # The Glimmer data is on a staggered grid; # Interpolate to obtain the value at x=0 and x=1 # using periodic boundary conditions #v = (glimmerData[0][1] + glimmerData[-1][1])/2 #glimmerData = [(0.0,v)]+glimmerData+[(1.0,v)] # Plot the Glimmer data axes.plot([row[0] for row in glimmerData], [row[1] for row in glimmerData],color='black') # Get the data from other models for comparison firstOrder = 0 fullStokes = 1 count = [0,0] sumV = [[0.0 for v in glimmerData],[0.0 for v in glimmerData]] sumV2 = [[0.0 for v in glimmerData],[0.0 for v in glimmerData]] for (path,directories,filenames) in os.walk('ismip_all'): for filename in filenames: modelName = filename[0:4] modelExperiment = filename[4] modelSize = filename[5:8] #print 'name, exp, size', modelName, modelExperiment, modelSize if modelName == 'aas1': # Skip the 'aas1' model because its output files in the tc-2007-0019-sp2.zip file do not follow the proper naming convention. MJH 11/5/13 continue if (modelExperiment != experiment) or (modelExperiment != 'f' and int(modelSize) != size) \ or (not args.all_ps and not modelName in lmlaModels + fullStokesModels): continue # continue next loop iteration if not the size or not the experiment desired or if we just want FO comparison and this model is not FO or FS. elif (modelExperiment == 'f'): if (modelSize == '001'): continue # ignore the sliding version for now if modelName == 'cma1': continue # the cma1 'f' experiments made the x,y coords dimensionless instead of dimensional - ignore for convenience print 'Using data from file:',os.path.join(path,filename) data = read(os.path.join(path,filename),experiment) if modelName in fullStokesModels: index = fullStokes else: index = firstOrder count[index] += 1 #axes.plot([row[0] for row in data], [row[1] for row in data] ) ## OPTIONAL: print out every individual model in its native x-coordinates. # Interpolate onto the x values from the Glimmer model run for (i,target) in enumerate([row[0] for row in glimmerData]): below = -99999.0 above = 99999.0 for (j,x) in enumerate([row[0] for row in data]): if below < x <= target: b,below = j,x if target <= x < above: a,above = j,x if above == below: v = data[a][1] else: if below == -99999.0: # Use the periodic boundary condition at x = 0 xBelow = data[-1][0] - 1 vBelow = data[-1][1] else: xBelow,vBelow = data[b] if above == 99999.0: # Use the periodic boundary condition at x = 1 xAbove = data[0][0] + 1 vAbove = data[0][1] else: xAbove,vAbove = data[a] if xAbove == xBelow: print 'Surprise!',above,below,xAbove,xBelow,vAbove,vBelow v = (vAbove+vBelow)/2 else: alpha = (target-xBelow)/(xAbove-xBelow) v = alpha*vAbove + (1-alpha)*vBelow sumV [index][i] += v sumV2[index][i] += v*v # Calculate statistics of the other model results if sum(count) == 0: print 'To compare with other models you need to download the ISMIP-HOM results from: http://www.the-cryosphere.net/2/95/2008/tc-2-95-2008-supplement.zip and unzip the contained file tc-2007-0019-sp2.zip into a directory named ismip_all. The ismip_all directory must be in the directory from which you are running this script.' else: # Find the mean and standard deviation of the velocities at each x for index in (firstOrder,fullStokes): if count[index] == 0: continue mean = list() standardDeviation = list() for i in range(len(glimmerData)): mean.append(sumV[index][i]/count[index]) standardDeviation.append(sqrt(sumV2[index][i]/count[index]-mean[-1]**2)) # Plot the mean using a dotted line color = (index,0,1-index) # blue for first order (index=0); red for full Stokes (index=1) x = [row[0] for row in glimmerData] axes.plot(x,mean,':',color=color) # Plot a filled polygon showing the mean plus and minus one standard deviation meanMinusSD = [m-sd for (m,sd) in zip(mean,standardDeviation)] meanPlusSD = [m+sd for (m,sd) in zip(mean,standardDeviation)] x = x + list(reversed(x)) y = meanPlusSD + list(reversed(meanMinusSD)) axes.fill(x,y,facecolor=color,edgecolor=color,alpha=0.25) if index == firstOrder: # Calculate some statistics comparing the Glimmer data with the other models pcterror = [100.0*abs(glimmer-others)/others for (glimmer,others) in zip([row[1] for row in glimmerData],mean)] abserror = [abs(glimmer-others) for (glimmer,others) in zip([row[1] for row in glimmerData],mean)] maximum = max(pcterror) position = glimmerData[pcterror.index(maximum)][0] total = sum([e for e in pcterror]) compare = sum([(s/m) for (s,m) in zip(standardDeviation,mean)]) n = len(glimmerData) #print '\t'.join([str(size)+' km',str(total/n),str(compare/n),str(position)]) print 'Size='+str(size)+' km' print ' Mean percent error along flowline of CISM relative to mean of first-order models='+str(total/float(n))+'%' print ' Mean COD (stdev/mean) along flowline of mean of first-order models (excluding CISM)='+str(compare/float(n)*100.0)+'%' print ' Max. CISM percent error='+str(maximum)+'% at x-position '+str(position) print ' Max. CISM absolute error='+str(max(abserror))+' m/yr at x-position '+str(glimmerData[abserror.index(max(abserror))][0]) except: print "Error in analyzing/plotting experiment ",experiment," at size ",size," km" if savePlotInFile: plot_file = pattern.replace('-?','-'+experiment).replace('.????','').replace('.out.nc',plotType) print 'Writing:', plot_file pyplot.savefig(plot_file) # Experiment f can also have a surface profile plotted if experiment == 'f': # rather than getting the data from the text file, we are going to read it directly. # this is because the velocities and usrf are on different grids, so it is difficult to include them # both in the standard ISMIP-HOM text file format that has a single x,y coord. system size = 100 out_file = pattern.replace('-?','-'+experiment).replace('????',str(size).zfill(4)) netCDFfile = NetCDFFile(out_file,'r') if netCDF_module == 'Scientific.IO.NetCDF': thkscale = netCDFfile.variables['thk'].scale_factor else: thkscale = 1.0 usurf = netCDFfile.variables['usurf'][-1,:,:] * thkscale # get last time level x1 = netCDFfile.variables['x1'][:] y1 = netCDFfile.variables['y1'][:] # Create the usurf figure ufigure = pyplot.figure(subplotpars=matplotlib.figure.SubplotParams(top=.85,bottom=.15)) ufigure.text(0.5,0.92,'ISMIP-HOM Experiment F: Surface elevation',horizontalalignment='center',size='large') if args.subtitle: ufigure.text(0.5,0.89,args.subtitle,horizontalalignment='center',size='small') ufigure.text(0.5,0.1,'X coordinate',horizontalalignment='center',size='small') ufigure.text(0.06,0.5,'upper surface (m)',rotation='vertical',verticalalignment='center') # Create the (three column) legend prop = matplotlib.font_manager.FontProperties(size='x-small') Line2D = matplotlib.lines.Line2D([],[],color=(0,0,0)) ufigure.legend([Line2D],['Model Output'],loc=(0.1,0.05),prop=prop).draw_frame(False) Line2D.set_linestyle(':') Line2D.set_color((1,0,0)) Patch = matplotlib.patches.Patch(edgecolor=None,facecolor=(1,0,0),alpha=0.25) ufigure.legend([Line2D,Patch],['Full Stokes Mean','Full Stokes Std. Dev.'],loc=(0.3,0.02),prop=prop).draw_frame(False) Line2D.set_color((0,0,1)) Patch.set_facecolor((0,0,1)) ufigure.legend([Line2D,Patch],[nonFSmodelType+' Mean',nonFSmodelType+' Std. Dev.'],loc=(0.55,0.02),prop=prop).draw_frame(False) # Create the plot axes axes2 = ufigure.add_subplot(111) axes2.set_title('%d km' % size, size='medium') # Convert CISM output data to the rotated coord system used by the problem setup alpha = -3.0 * pi/180 # defined in run script # use integer floor division operator to get an index close to the center TODO should be interpolating if needed... yp = len(y1)//2 # calculate rotated xprime, zprime coordinates along the surface (this is the coord. sys. used for this test case) xprime = x1 * cos(alpha) + (usurf[yp,:]-7000.0) * sin(alpha) xprime = xprime/1000.0 - 50.0 zprime = -x1 * sin(alpha) + (usurf[yp,:]-7000.0) * cos(alpha) # Plot CISM output axes2.plot(xprime, zprime, color='black') # create glimmerData so we can re-use the code from above glimmerData = list() for i in range(len(xprime)): glimmerData.append(tuple([xprime[i], zprime[i]])) # Now plot the other models - yucky code copied from above # Get the data from other models for comparison firstOrder = 0 fullStokes = 1 count = [0,0] sumV = [[0.0 for v in glimmerData],[0.0 for v in glimmerData]] sumV2 = [[0.0 for v in glimmerData],[0.0 for v in glimmerData]] for (path,directories,filenames) in os.walk('ismip_all'): for filename in filenames: modelName = filename[0:4] modelExperiment = filename[4] modelSize = filename[5:8] #print 'name, exp, size', modelName, modelExperiment, modelSize if modelName == 'aas1': # Skip the 'aas1' model because its output files in the tc-2007-0019-sp2.zip file do not follow the proper naming convention. MJH 11/5/13 continue if (modelExperiment != experiment) or (modelExperiment != 'f' and int(modelSize) != size) \ or (not args.all_ps and not modelName in lmlaModels + fullStokesModels): continue # continue next loop iteration if not the size or not the experiment desired or if we just want FO comparison and this model is not FO or FS. elif (modelExperiment == 'f'): if (modelSize == '001'): continue # ignore the sliding version for now if modelName == 'cma1': continue # the cma1 'f' experiments made the x,y coords dimensionless instead of dimensional - ignore for convenience print 'Using data from file:',os.path.join(path,filename) data = read(os.path.join(path,filename), experiment='f-elevation') if modelName in fullStokesModels: index = fullStokes else: index = firstOrder count[index] += 1 #axes2.plot([row[0] for row in data], [row[1] for row in data] ) ## OPTIONAL: print out every individual model in its native x-coordinates. # Interpolate onto the x values from the Glimmer model run for (i,target) in enumerate([row[0] for row in glimmerData]): below = -99999.0 above = 99999.0 for (j,x) in enumerate([row[0] for row in data]): if below < x <= target: b,below = j,x if target <= x < above: a,above = j,x if above == below: v = data[a][1] else: if below == -99999.0: # Use the periodic boundary condition at x = 0 xBelow = data[-1][0] - 1 vBelow = data[-1][1] else: xBelow,vBelow = data[b] if above == 99999.0: # Use the periodic boundary condition at x = 1 xAbove = data[0][0] + 1 vAbove = data[0][1] else: xAbove,vAbove = data[a] if xAbove == xBelow: print 'Surprise!',above,below,xAbove,xBelow,vAbove,vBelow v = (vAbove+vBelow)/2 else: alpha = (target-xBelow)/(xAbove-xBelow) v = alpha*vAbove + (1-alpha)*vBelow sumV [index][i] += v sumV2[index][i] += v*v # Calculate statistics of the other model results if sum(count) == 0: print 'To compare with other models you need to download the ISMIP-HOM results from: http://www.the-cryosphere.net/2/95/2008/tc-2-95-2008-supplement.zip and unzip the contained file tc-2007-0019-sp2.zip into a directory named ismip_all. The ismip_all directory must be in the directory from which you are running this script.' else: # Find the mean and standard deviation of the velocities at each x for index in (firstOrder,fullStokes): if count[index] == 0: continue mean = list() standardDeviation = list() for i in range(len(glimmerData)): mean.append(sumV[index][i]/count[index]) standardDeviation.append(sqrt(sumV2[index][i]/count[index]-mean[-1]**2)) # Plot the mean using a dotted line color = (index,0,1-index) # blue for first order (index=0); red for full Stokes (index=1) x = [row[0] for row in glimmerData] axes2.plot(x,mean,':',color=color) # Plot a filled polygon showing the mean plus and minus one standard deviation meanMinusSD = [m-sd for (m,sd) in zip(mean,standardDeviation)] meanPlusSD = [m+sd for (m,sd) in zip(mean,standardDeviation)] x = x + list(reversed(x)) y = meanPlusSD + list(reversed(meanMinusSD)) axes2.fill(x,y,facecolor=color,edgecolor=color,alpha=0.25) if savePlotInFile: plot_file = pattern.replace('-?','-'+experiment+'-SurfaceElevation').replace('.????','').replace('.out.nc',plotType) print 'Writing:', plot_file pyplot.savefig(plot_file) # Experiment f should be run for one size (100 km) only if experiment == 'f': break if not savePlotInFile: pyplot.show()
# Loop over the experiments requested on the command line for experiment in options.experiments: print 'ISMIP-HOM', experiment.upper() # Create the figure on which the plot axes will be placed figure = pyplot.figure(subplotpars=matplotlib.figure.SubplotParams(top=.85,bottom=.15)) figure.text(0.5,0.92,'ISMIP-HOM Experiment '+experiment.upper(),horizontalalignment='center',size='large') if options.subtitle: figure.text(0.5,0.89,options.subtitle,horizontalalignment='center',size='small') figure.text(0.5,0.1,'Normalized X coordinate',horizontalalignment='center',size='small') figure.text(0.06,0.5,'Ice Speed (m/a)',rotation='vertical',verticalalignment='center') # Create the (three column) legend prop = matplotlib.font_manager.FontProperties(size='x-small') Line2D = matplotlib.lines.Line2D([],[],color=(0,0,0)) figure.legend([Line2D],['Model Output'],loc=(0.1,0.05),prop=prop).draw_frame(False) Line2D.set_linestyle(':') Line2D.set_color((1,0,0)) Patch = matplotlib.patches.Patch(edgecolor=None,facecolor=(1,0,0),alpha=0.25) figure.legend([Line2D,Patch],['Full Stokes Mean','Full Stokes Std. Dev.'],loc=(0.3,0.02),prop=prop).draw_frame(False) Line2D.set_color((0,0,1)) Patch.set_facecolor((0,0,1)) figure.legend([Line2D,Patch],['First Order Mean','First Order Std. Dev.'],loc=(0.55,0.02),prop=prop).draw_frame(False) # Loop over the sizes requested on the command line for i, size in enumerate(map(int,options.sizes)): if experiment == 'f': if size != 100 or len(options.sizes) > 1: print 'NOTE: Experiment f uses a domain size of 100 km only' size = 100