def _plot_latlon(self, remappedModelClimatology, remappedRefClimatology): # {{{ """ plotting a global lat-lon data set """ season = self.season config = self.config configSectionName = self.taskName mainRunName = config.get('runs', 'mainRunName') modelOutput = nans_to_numpy_mask( remappedModelClimatology[self.mpasFieldName].values) lon = remappedModelClimatology['lon'].values lat = remappedModelClimatology['lat'].values lonTarg, latTarg = np.meshgrid(lon, lat) if remappedRefClimatology is None: refOutput = None bias = None else: refOutput = nans_to_numpy_mask( remappedRefClimatology[self.refFieldName].values) bias = modelOutput - refOutput filePrefix = self.filePrefix outFileName = '{}/{}.png'.format(self.plotsDirectory, filePrefix) title = '{} ({}, years {:04d}-{:04d})'.format( self.fieldNameInTitle, season, self.startYear, self.endYear) plot_global_comparison(config, lonTarg, latTarg, modelOutput, refOutput, bias, configSectionName, fileout=outFileName, title=title, modelTitle='{}'.format(mainRunName), refTitle=self.refTitleLabel, diffTitle=self.diffTitleLabel, cbarlabel=self.unitsLabel) caption = '{} {}'.format(season, self.imageCaption) write_image_xml( config, filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup='Global {}'.format(self.galleryGroup), groupSubtitle=self.groupSubtitle, groupLink=self.groupLink, gallery=self.galleryName, thumbnailDescription=self.thumbnailDescription, imageDescription=caption, imageCaption=caption)
def _make_plot(self, plotParameter, optionalArgument=None): # {{{ ''' Make a simple plot Parameters ---------- plotParameter : str The name of a parameter that is specific to this plot optionalArgument : <type_goes_here>, optional An optional argument <Performs my favorite subtask> ''' # perform the task # self.myArg is a copy of the argument we passed in to __init__ when we # built the task. It is available in any method after that for us to # use as needed. print('myArg: {}'.format(self.myArg)) print('plotParameter: {}'.format(plotParameter)) if optionalArgument is not None: print('optionalArgument: {}'.format(optionalArgument)) # get the file name based on the plot parameter filePrefix = self.filePrefixes[plotParameter] outFileName = '{}/{}.png'.format(self.plotsDirectory, filePrefix) # make the plot x = numpy.linspace(0, 1, 1000) plt.plot(x, x**2) # save the plot to the output file plt.savefig(outFileName) # here's an example of how you would create an XML file for this plot # with the appropriate entries. Some notes: # * Gallery groups typically represent all the analysis from a task, # or sometimes from multiple tasks # * A gallery might be for just for one set of observations, one # season, etc., depending on what makes sense # * Within each gallery, there is one plot for each value in # 'plotParameters', with a corresponding caption and short thumbnail # description caption = 'Plot of x^2 with plotParamter: {}'.format(plotParameter) write_image_xml( self.config, filePrefix, componentName='Ocean', # 'Ocean', 'Sea Ice', etc. componentSubdirectory='ocean', # 'ocean', 'sea_ice', etc. galleryGroup='Title of My Gallery Group', groupSubtitle='Observations: totally made up', groupLink='my_grp', # a short link name for the gallery group gallery='Name of Gallery', thumbnailDescription=plotParameter, imageDescription=caption, imageCaption=caption)
def _write_xml(self, filePrefix): # {{{ caption = 'Meridional Heat Transport' write_image_xml(config=self.config, filePrefix=filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup='Meridional Heat Transport', groupLink='mht', imageDescription=caption, imageCaption=caption) # }}}
def _write_xml(self, filePrefix, plotType): # {{{ caption = u'{} of El Niño 3.4 Climate Index'.format(plotType) write_image_xml(config=self.config, filePrefix=filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup=u'El Niño 3.4 Climate Index', groupLink='nino34', thumbnailDescription=plotType, imageDescription=caption, imageCaption=caption) # }}}
def run_task(self): # {{{ """ Plots time-series output of Antarctic sub-ice-shelf melt rates. """ # Authors # ------- # Xylar Asay-Davis, Stephen Price self.logger.info("\nPlotting Antarctic melt rate time series for " "{}...".format(self.iceShelf)) self.logger.info(' Load melt rate data...') config = self.config calendar = self.calendar iceShelfMasksFile = self.iceShelfMasksFile fcAll = read_feature_collection(iceShelfMasksFile) fc = FeatureCollection() for feature in fcAll.features: if feature['properties']['name'] == self.iceShelf: fc.add_feature(feature) break totalMeltFlux, meltRates = self._load_ice_shelf_fluxes(config) plotControl = self.controlConfig is not None if plotControl: controlRunName = self.controlConfig.get('runs', 'mainRunName') refTotalMeltFlux, refMeltRates = \ self._load_ice_shelf_fluxes(self.controlConfig) # Load observations from multiple files and put in dictionary based # on shelf keyname observationsDirectory = build_obs_path(config, 'ocean', 'meltSubdirectory') obsFileNameDict = {'Rignot et al. (2013)': 'Rignot_2013_melt_rates_20200623.csv', 'Rignot et al. (2013) SS': 'Rignot_2013_melt_rates_SS_20200623.csv'} obsDict = {} # dict for storing dict of obs data for obsName in obsFileNameDict: obsFileName = '{}/{}'.format(observationsDirectory, obsFileNameDict[obsName]) obsDict[obsName] = {} obsFile = csv.reader(open(obsFileName, 'rU')) next(obsFile, None) # skip the header line for line in obsFile: # some later useful values commented out shelfName = line[0] if shelfName != self.iceShelf: continue # surveyArea = line[1] meltFlux = float(line[2]) meltFluxUncertainty = float(line[3]) meltRate = float(line[4]) meltRateUncertainty = float(line[5]) # actualArea = float( line[6] ) # actual area here is in sq km # build dict of obs. keyed to filename description # (which will be used for plotting) obsDict[obsName] = { 'meltFlux': meltFlux, 'meltFluxUncertainty': meltFluxUncertainty, 'meltRate': meltRate, 'meltRateUncertainty': meltRateUncertainty} break # If areas from obs file used need to be converted from sq km to sq m mainRunName = config.get('runs', 'mainRunName') movingAverageMonths = config.getint('timeSeriesAntarcticMelt', 'movingAverageMonths') outputDirectory = build_config_full_path(config, 'output', 'timeseriesSubdirectory') make_directories(outputDirectory) self.logger.info(' Make plots...') # get obs melt flux and unc. for shelf (similar for rates) obsMeltFlux = [] obsMeltFluxUnc = [] obsMeltRate = [] obsMeltRateUnc = [] for obsName in obsDict: if len(obsDict[obsName]) > 0: obsMeltFlux.append( obsDict[obsName]['meltFlux']) obsMeltFluxUnc.append( obsDict[obsName]['meltFluxUncertainty']) obsMeltRate.append( obsDict[obsName]['meltRate']) obsMeltRateUnc.append( obsDict[obsName]['meltRateUncertainty']) else: # append NaN so this particular obs won't plot self.logger.warning('{} observations not available for ' '{}'.format(obsName, self.iceShelf)) obsMeltFlux.append(None) obsMeltFluxUnc.append(None) obsMeltRate.append(None) obsMeltRateUnc.append(None) title = self.iceShelf.replace('_', ' ') xLabel = 'Time (yr)' yLabel = 'Melt Flux (GT/yr)' timeSeries = totalMeltFlux.isel(nRegions=self.regionIndex) filePrefix = 'melt_flux_{}'.format(self.iceShelf.replace(' ', '_')) outFileName = '{}/{}.png'.format(self.plotsDirectory, filePrefix) fields = [timeSeries] lineColors = ['k'] lineWidths = [2.5] legendText = [mainRunName] if plotControl: fields.append(refTotalMeltFlux.isel(nRegions=self.regionIndex)) lineColors.append('r') lineWidths.append(1.2) legendText.append(controlRunName) fig = timeseries_analysis_plot(config, fields, calendar=calendar, title=title, xlabel=xLabel, ylabel=yLabel, movingAveragePoints=movingAverageMonths, lineColors=lineColors, lineWidths=lineWidths, legendText=legendText, obsMean=obsMeltFlux, obsUncertainty=obsMeltFluxUnc, obsLegend=list(obsDict.keys())) # do this before the inset because otherwise it moves the inset # and cartopy doesn't play too well with tight_layout anyway plt.tight_layout() add_inset(fig, fc, width=2.0, height=2.0) savefig(outFileName) caption = 'Running Mean of Total Melt Flux under Ice ' \ 'Shelves in the {} Region'.format(title) write_image_xml( config=config, filePrefix=filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup='Antarctic Melt Time Series', groupLink='antmelttime', gallery='Total Melt Flux', thumbnailDescription=title, imageDescription=caption, imageCaption=caption) xLabel = 'Time (yr)' yLabel = 'Melt Rate (m/yr)' timeSeries = meltRates.isel(nRegions=self.regionIndex) filePrefix = 'melt_rate_{}'.format(self.iceShelf.replace(' ', '_')) outFileName = '{}/{}.png'.format(self.plotsDirectory, filePrefix) fields = [timeSeries] lineColors = ['k'] lineWidths = [2.5] legendText = [mainRunName] if plotControl: fields.append(refMeltRates.isel(nRegions=self.regionIndex)) lineColors.append('r') lineWidths.append(1.2) legendText.append(controlRunName) if config.has_option(self.taskName, 'firstYearXTicks'): firstYearXTicks = config.getint(self.taskName, 'firstYearXTicks') else: firstYearXTicks = None if config.has_option(self.taskName, 'yearStrideXTicks'): yearStrideXTicks = config.getint(self.taskName, 'yearStrideXTicks') else: yearStrideXTicks = None fig = timeseries_analysis_plot(config, fields, calendar=calendar, title=title, xlabel=xLabel, ylabel=yLabel, movingAveragePoints=movingAverageMonths, lineColors=lineColors, lineWidths=lineWidths, legendText=legendText, firstYearXTicks=firstYearXTicks, yearStrideXTicks=yearStrideXTicks, obsMean=obsMeltRate, obsUncertainty=obsMeltRateUnc, obsLegend=list(obsDict.keys())) # do this before the inset because otherwise it moves the inset # and cartopy doesn't play too well with tight_layout anyway plt.tight_layout() add_inset(fig, fc, width=2.0, height=2.0) savefig(outFileName) caption = 'Running Mean of Area-averaged Melt Rate under Ice ' \ 'Shelves in the {} Region'.format(title) write_image_xml( config=config, filePrefix=filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup='Antarctic Melt Time Series', groupLink='antmelttime', gallery='Area-averaged Melt Rate', thumbnailDescription=title, imageDescription=caption, imageCaption=caption)
def run_task(self): # {{{ """ Compute vertical agregates of the data and plot the time series """ # Authors # ------- # Xylar Asay-Davis, Milena Veneziani, Greg Streletz self.logger.info("\nPlotting depth-integrated time series of " "{}...".format(self.fieldNameInTitle)) config = self.config calendar = self.calendar mainRunName = config.get('runs', 'mainRunName') plotTitles = config.getExpression('regions', 'plotTitles') allRegionNames = config.getExpression('regions', 'regions') regionIndex = allRegionNames.index(self.regionName) regionNameInTitle = plotTitles[regionIndex] startDate = config.get('timeSeries', 'startDate') endDate = config.get('timeSeries', 'endDate') # Load data self.logger.info(' Load ocean data...') ds = open_mpas_dataset(fileName=self.inFileName, calendar=calendar, variableList=[self.mpasFieldName, 'depth'], timeVariableNames=None, startDate=startDate, endDate=endDate) ds = ds.isel(nOceanRegionsTmp=regionIndex) depths = ds.depth.values divisionDepths = config.getExpression(self.sectionName, 'depths') # for each depth interval to plot, determine the top and bottom depth topDepths = [0, 0] + divisionDepths bottomDepths = [depths[-1]] + divisionDepths + [depths[-1]] legends = [] for top, bottom in zip(topDepths, bottomDepths): if bottom == depths[-1]: legends.append('{}m-bottom'.format(top)) else: legends.append('{}m-{}m'.format(top, bottom)) # more possible symbols than we typically use lines = ['-', '-', '--', None, None, None, None] markers = [None, None, None, '+', 'o', '^', 'v'] widths = [5, 3, 3, 3, 3, 3, 3] points = [None, None, None, 300, 300, 300, 300] color = 'k' xLabel = 'Time [years]' yLabel = self.yAxisLabel title = '{}, {} \n {} (black)'.format(self.fieldNameInTitle, regionNameInTitle, mainRunName) outFileName = '{}/{}.png'.format(self.plotsDirectory, self.filePrefix) timeSeries = [] lineColors = [] lineStyles = [] lineMarkers = [] lineWidths = [] maxPoints = [] legendText = [] for rangeIndex in range(len(topDepths)): top = topDepths[rangeIndex] bottom = bottomDepths[rangeIndex] field = ds[self.mpasFieldName].where(ds.depth > top) field = field.where(ds.depth <= bottom) timeSeries.append(field.sum('nVertLevels')) lineColors.append(color) lineStyles.append(lines[rangeIndex]) lineMarkers.append(markers[rangeIndex]) lineWidths.append(widths[rangeIndex]) maxPoints.append(points[rangeIndex]) legendText.append(legends[rangeIndex]) preprocessedReferenceRunName = config.get( 'runs', 'preprocessedReferenceRunName') if preprocessedReferenceRunName != 'None': preprocessedInputDirectory = config.get( 'oceanPreprocessedReference', 'baseDirectory') self.logger.info(' Load in preprocessed reference data...') preprocessedFilePrefix = config.get(self.sectionName, 'preprocessedFilePrefix') inFilesPreprocessed = '{}/{}.{}.year*.nc'.format( preprocessedInputDirectory, preprocessedFilePrefix, preprocessedReferenceRunName) combine_time_series_with_ncrcat( inFilesPreprocessed, self.preprocessedIntermediateFileName, logger=self.logger) dsPreprocessed = open_mpas_dataset( fileName=self.preprocessedIntermediateFileName, calendar=calendar, timeVariableNames='xtime') yearStart = days_to_datetime(ds.Time.min(), calendar=calendar).year yearEnd = days_to_datetime(ds.Time.max(), calendar=calendar).year timeStart = date_to_days(year=yearStart, month=1, day=1, calendar=calendar) timeEnd = date_to_days(year=yearEnd, month=12, day=31, calendar=calendar) yearEndPreprocessed = days_to_datetime(dsPreprocessed.Time.max(), calendar=calendar).year if yearStart <= yearEndPreprocessed: dsPreprocessed = dsPreprocessed.sel( Time=slice(timeStart, timeEnd)) else: self.logger.warning('Warning: Preprocessed time series ends ' 'before the timeSeries startYear and will ' 'not be plotted.') preprocessedReferenceRunName = 'None' # rolling mean seems to have trouble with dask data sets so we # write out the data set and read it back as a single-file data set # (without dask) dsPreprocessed = dsPreprocessed.drop('xtime') write_netcdf(dsPreprocessed, self.preprocessedFileName) dsPreprocessed = xarray.open_dataset(self.preprocessedFileName) if preprocessedReferenceRunName != 'None': color = 'purple' title = '{} \n {} (purple)'.format(title, preprocessedReferenceRunName) preprocessedFieldPrefix = config.get(self.sectionName, 'preprocessedFieldPrefix') movingAveragePoints = config.getint(self.sectionName, 'movingAveragePoints') suffixes = ['tot' ] + ['{}m'.format(depth) for depth in divisionDepths] + ['btm'] # these preprocessed data are already anomalies dsPreprocessed = compute_moving_avg(dsPreprocessed, movingAveragePoints) for rangeIndex in range(len(suffixes)): variableName = '{}_{}'.format(preprocessedFieldPrefix, suffixes[rangeIndex]) if variableName in list(dsPreprocessed.data_vars.keys()): timeSeries.append(dsPreprocessed[variableName]) else: self.logger.warning( 'Warning: Preprocessed variable {} ' 'not found. Skipping.'.format(variableName)) timeSeries.extend(None) lineColors.append(color) lineStyles.append(lines[rangeIndex]) lineMarkers.append(markers[rangeIndex]) lineWidths.append(widths[rangeIndex]) maxPoints.append(points[rangeIndex]) legendText.append(None) if self.controlConfig is not None: controlRunName = self.controlConfig.get('runs', 'mainRunName') title = '{} \n {} (red)'.format(title, controlRunName) self.logger.info(' Load ocean data from control run...') controlStartYear = self.controlConfig.getint( 'timeSeries', 'startYear') controlEndYear = self.controlConfig.getint('timeSeries', 'endYear') controlStartDate = '{:04d}-01-01_00:00:00'.format(controlStartYear) controlEndDate = '{:04d}-12-31_23:59:59'.format(controlEndYear) dsRef = open_mpas_dataset( fileName=self.refFileName, calendar=calendar, variableList=[self.mpasFieldName, 'depth'], timeVariableNames=None, startDate=controlStartDate, endDate=controlEndDate) dsRef = dsRef.isel(nOceanRegionsTmp=regionIndex) color = 'r' for rangeIndex in range(len(topDepths)): top = topDepths[rangeIndex] bottom = bottomDepths[rangeIndex] field = dsRef[self.mpasFieldName].where(dsRef.depth > top) field = field.where(dsRef.depth <= bottom) timeSeries.append(field.sum('nVertLevels')) lineColors.append(color) lineStyles.append(lines[rangeIndex]) lineMarkers.append(markers[rangeIndex]) lineWidths.append(widths[rangeIndex]) maxPoints.append(points[rangeIndex]) legendText.append(None) if config.has_option(self.taskName, 'firstYearXTicks'): firstYearXTicks = config.getint(self.taskName, 'firstYearXTicks') else: firstYearXTicks = None if config.has_option(self.taskName, 'yearStrideXTicks'): yearStrideXTicks = config.getint(self.taskName, 'yearStrideXTicks') else: yearStrideXTicks = None timeseries_analysis_plot(config=config, dsvalues=timeSeries, calendar=calendar, title=title, xlabel=xLabel, ylabel=yLabel, movingAveragePoints=None, lineColors=lineColors, lineStyles=lineStyles, markers=lineMarkers, lineWidths=lineWidths, legendText=legendText, maxPoints=maxPoints, firstYearXTicks=firstYearXTicks, yearStrideXTicks=yearStrideXTicks) savefig(outFileName) write_image_xml(config=config, filePrefix=self.filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup=self.galleryGroup, groupLink=self.groupLink, gallery=self.galleryName, thumbnailDescription='{} {}'.format( self.regionName, self.thumbnailSuffix), imageDescription=self.imageCaption, imageCaption=self.imageCaption)
def run_task(self): # {{{ """ Make the Hovmoller plot from the time series. """ # Authors # ------- # Xylar Asay-Davis, Milena Veneziani, Greg Streletz self.logger.info("\nPlotting {} trends vs. depth...".format( self.fieldNameInTitle)) config = self.config mainRunName = config.get('runs', 'mainRunName') plotTitles = config.getExpression('regions', 'plotTitles') allRegionNames = config.getExpression('regions', 'regions') regionIndex = allRegionNames.index(self.regionName) regionNameInTitle = plotTitles[regionIndex] startDate = self.config.get('timeSeries', 'startDate') endDate = self.config.get('timeSeries', 'endDate') # Load data self.logger.info(' Load ocean data...') ds = open_mpas_dataset(fileName=self.inFileName, calendar=self.calendar, variableList=[self.mpasFieldName], timeVariableNames=None, startDate=startDate, endDate=endDate) ds = ds.isel(nOceanRegionsTmp=regionIndex) # Note: restart file, not a mesh file because we need refBottomDepth, # not in a mesh file try: restartFile = self.runStreams.readpath('restart')[0] except ValueError: raise IOError('No MPAS-O restart file found: need at least one ' 'restart file for OHC calculation') # Define/read in general variables self.logger.info(' Read in depth...') with xr.open_dataset(restartFile) as dsRestart: # reference depth [m] depth = dsRestart.refBottomDepth.values Time = ds.Time.values field = ds[self.mpasFieldName].values.transpose() xLabel = 'Time [years]' yLabel = 'Depth [m]' title = '{}, {} \n {}'.format(self.fieldNameInTitle, regionNameInTitle, mainRunName) figureName = '{}/{}.png'.format(self.plotsDirectory, self.filePrefix) if config.has_option(self.sectionName, 'firstYearXTicks'): firstYearXTicks = config.getint(self.sectionName, 'firstYearXTicks') else: firstYearXTicks = None if config.has_option(self.sectionName, 'yearStrideXTicks'): yearStrideXTicks = config.getint(self.sectionName, 'yearStrideXTicks') else: yearStrideXTicks = None plot_vertical_section(config, Time, depth, field, self.sectionName, suffix='', colorbarLabel=self.unitsLabel, title=title, xlabel=xLabel, ylabel=yLabel, fileout=figureName, linewidths=1, xArrayIsTime=True, calendar=self.calendar, firstYearXTicks=firstYearXTicks, yearStrideXTicks=yearStrideXTicks) write_image_xml(config=config, filePrefix=self.filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup=self.galleryGroup, groupLink=self.groupLink, galleryName=self.galleryName, thumbnailDescription='{} {}'.format( self.regionName, self.thumbnailSuffix), imageDescription=self.imageCaption, imageCaption=self.imageCaption)
def run_task(self): # {{{ ''' Process MOC analysis member data if available, or compute MOC at post-processing if not. Plots streamfunction climatolgoical sections as well as time series of max Atlantic MOC at 26.5N (latitude of RAPID MOC Array). ''' # Authors # ------- # Milena Veneziani, Mark Petersen, Phillip J. Wolfram, Xylar Asay-Davis self.logger.info("\nPlotting streamfunction of Meridional Overturning " "Circulation (MOC)...") config = self.config # **** Compute MOC **** if not self.usePostprocessing and self.mocAnalysisMemberEnabled: self._compute_moc_climo_analysismember() dsMOCTimeSeries = self._compute_moc_time_series_analysismember() else: self._compute_moc_climo_postprocess() dsMOCTimeSeries = self._compute_moc_time_series_postprocess() # **** Plot MOC **** # Define plotting variables mainRunName = config.get('runs', 'mainRunName') movingAveragePoints = config.getint(self.sectionName, 'movingAveragePoints') movingAveragePointsClimatological = config.getint( self.sectionName, 'movingAveragePointsClimatological') colorbarLabel = '[Sv]' xLabel = 'latitude [deg]' yLabel = 'depth [m]' for region in self.regionNames: self.logger.info(' Plot climatological {} MOC...'.format(region)) title = '{} MOC (ANN, years {:04d}-{:04d})\n {}'.format( region, self.startYearClimo, self.endYearClimo, mainRunName) filePrefix = self.filePrefixes[region] figureName = '{}/{}.png'.format(self.plotsDirectory, filePrefix) x = self.lat[region] y = self.depth z = self.moc[region] # Subset lat range minLat = config.getExpression(self.sectionName, 'latBinMin{}'.format(region)) maxLat = config.getExpression(self.sectionName, 'latBinMax{}'.format(region)) indLat = np.logical_and(x >= minLat, x <= maxLat) x = x[indLat] z = z[:, indLat] plot_vertical_section(config, x, y, z, self.sectionName, suffix=region, colorbarLabel=colorbarLabel, title=title, xlabel=xLabel, ylabel=yLabel, fileout=figureName, N=movingAveragePointsClimatological) caption = '{} Meridional Overturning Streamfunction'.format(region) write_image_xml( config=config, filePrefix=filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup='Meridional Overturning Streamfunction', groupLink='moc', thumbnailDescription=region, imageDescription=caption, imageCaption=caption) # }}} # Plot time series self.logger.info(' Plot time series of max Atlantic MOC at 26.5N...') xLabel = 'Time [years]' yLabel = '[Sv]' title = 'Max Atlantic MOC at $26.5\degree$N\n {}'.format(mainRunName) filePrefix = self.filePrefixes['timeSeries'] figureName = '{}/{}.png'.format(self.plotsDirectory, filePrefix) if config.has_option(self.taskName, 'firstYearXTicks'): firstYearXTicks = config.getint(self.taskName, 'firstYearXTicks') else: firstYearXTicks = None if config.has_option(self.taskName, 'yearStrideXTicks'): yearStrideXTicks = config.getint(self.taskName, 'yearStrideXTicks') else: yearStrideXTicks = None fields = [dsMOCTimeSeries.mocAtlantic26] lineColors = ['k'] lineWidths = [2] legendText = [mainRunName] if self.refConfig is not None: refDirectory = build_config_full_path(self.refConfig, 'output', 'timeseriesSubdirectory') refStartYear = self.refConfig.getint('timeSeries', 'startYear') refEndYear = self.refConfig.getint('timeSeries', 'endYear') refStartDate = '{:04d}-01-01_00:00:00'.format(refStartYear) refEndDate = '{:04d}-12-31_23:59:59'.format(refEndYear) refFileName = '{}/mocTimeSeries.nc'.format(refDirectory) self.logger.info(' Read in reference run MOC time series') dsRefMOC = open_mpas_dataset(fileName=refFileName, calendar=self.calendar, timeVariableNames=None, variableList=['mocAtlantic26'], startDate=refStartDate, endDate=refEndDate) fields.append(dsRefMOC.mocAtlantic26) lineColors.append('r') lineWidths.append(2) refRunName = self.refConfig.get('runs', 'mainRunName') legendText.append(refRunName) timeseries_analysis_plot(config, fields, movingAveragePoints, title, xLabel, yLabel, figureName, calendar=self.calendar, lineColors=lineColors, lineWidths=lineWidths, legendText=legendText, firstYearXTicks=firstYearXTicks, yearStrideXTicks=yearStrideXTicks) caption = u'Time Series of maximum Meridional Overturning ' \ u'Circulation at 26.5°N' write_image_xml(config=config, filePrefix=filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup='Meridional Overturning Streamfunction', groupLink='moc', thumbnailDescription='Time Series', imageDescription=caption, imageCaption=caption) # }}}
def _plot_transect(self, remappedModelClimatology, remappedRefClimatology): # {{{ """ plotting the transect """ season = self.season config = self.config configSectionName = self.configSectionName mainRunName = config.get('runs', 'mainRunName') # broadcast x and z to have the same dimensions x, z = xr.broadcast(remappedModelClimatology.x, remappedModelClimatology.z) # set lat and lon in case we want to plot versus these quantities lat = remappedModelClimatology.lat lon = remappedModelClimatology.lon # convert x, z, lat, and lon to numpy arrays; make a copy because # they are sometimes read-only (not sure why) x = x.values.copy().transpose() z = z.values.copy().transpose() lat = lat.values.copy().transpose() lon = lon.values.copy().transpose() self.lat = lat self.lon = lon # z is masked out with NaNs in some locations (where there is land) but # this makes pcolormesh unhappy so we'll zero out those locations z[numpy.isnan(z)] = 0. modelOutput = nans_to_numpy_mask( remappedModelClimatology[self.mpasFieldName].values) modelOutput = modelOutput.transpose() if remappedRefClimatology is None: refOutput = None bias = None else: refOutput = remappedRefClimatology[self.refFieldName] dims = refOutput.dims refOutput = nans_to_numpy_mask(refOutput.values) if dims[1] != 'nPoints': assert (dims[0] == 'nPoints') refOutput = refOutput.transpose() bias = modelOutput - refOutput filePrefix = self.filePrefix outFileName = '{}/{}.png'.format(self.plotsDirectory, filePrefix) title = '{}\n({}, years {:04d}-{:04d})'.format(self.fieldNameInTitle, season, self.startYear, self.endYear) xLabel = 'Distance [km]' yLabel = 'Depth [m]' # define the axis labels and the data to use for the upper # x axis or axes, if such additional axes have been requested upperXAxes = config.get('transects', 'upperXAxes') numUpperTicks = config.getint('transects', 'numUpperTicks') upperXAxisTickLabelPrecision = config.getint( 'transects', 'upperXAxisTickLabelPrecision') self._set_third_x_axis_to_none() if upperXAxes == 'neither': self._set_second_x_axis_to_none() elif upperXAxes == 'lat': self._set_second_x_axis_to_latitude() elif upperXAxes == 'lon': self._set_second_x_axis_to_longitude() elif upperXAxes == 'both': self._set_second_x_axis_to_longitude() self._set_third_x_axis_to_latitude() elif upperXAxes == 'greatestExtent': if self._greatest_extent(lat, lon): self._set_second_x_axis_to_latitude() else: self._set_second_x_axis_to_longitude() elif upperXAxes == 'strictlyMonotonic': if self._strictly_monotonic(lat, lon): self._set_second_x_axis_to_latitude() else: self._set_second_x_axis_to_longitude() elif upperXAxes == 'mostMonotonic': if self._most_monotonic(lat, lon): self._set_second_x_axis_to_latitude() else: self._set_second_x_axis_to_longitude() elif upperXAxes == 'mostStepsInSameDirection': if self._most_steps_in_same_direction(lat, lon): self._set_second_x_axis_to_latitude() else: self._set_second_x_axis_to_longitude() elif upperXAxes == 'fewestDirectionChanges': if self._fewest_direction_changes(lat, lon): self._set_second_x_axis_to_latitude() else: self._set_second_x_axis_to_longitude() else: raise ValueError('invalid option for upperXAxes') # get the parameters determining what type of plot to use, # what line styles and line colors to use, and whether and how # to label contours compareAsContours = config.getboolean('transects', 'compareAsContoursOnSinglePlot') contourLineStyle = config.get('transects', 'contourLineStyle') contourLineColor = config.get('transects', 'contourLineColor') comparisonContourLineStyle = config.get('transects', 'comparisonContourLineStyle') comparisonContourLineColor = config.get('transects', 'comparisonContourLineColor') if compareAsContours: labelContours = config.getboolean( 'transects', 'labelContoursOnContourComparisonPlots') else: labelContours = config.getboolean('transects', 'labelContoursOnHeatmaps') contourLabelPrecision = config.getint('transects', 'contourLabelPrecision') # construct a three-panel comparison plot for the transect, or a # single-panel contour comparison plot if compareAsContours is True plot_vertical_section_comparison( config, x, z, modelOutput, refOutput, bias, outFileName, configSectionName, cbarLabel=self.unitsLabel, xlabel=xLabel, ylabel=yLabel, title=title, modelTitle='{}'.format(mainRunName), refTitle=self.refTitleLabel, diffTitle=self.diffTitleLabel, secondXAxisData=self.secondXAxisData, secondXAxisLabel=self.secondXAxisLabel, thirdXAxisData=self.thirdXAxisData, thirdXAxisLabel=self.thirdXAxisLabel, numUpperTicks=numUpperTicks, upperXAxisTickLabelPrecision=upperXAxisTickLabelPrecision, invertYAxis=False, backgroundColor='#918167', compareAsContours=compareAsContours, lineStyle=contourLineStyle, lineColor=contourLineColor, comparisonContourLineStyle=comparisonContourLineStyle, comparisonContourLineColor=comparisonContourLineColor, labelContours=labelContours, contourLabelPrecision=contourLabelPrecision) caption = '{} {}'.format(season, self.imageCaption) write_image_xml(config, filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup=self.galleryGroup, groupSubtitle=self.groupSubtitle, groupLink=self.groupLink, gallery=self.galleryName, thumbnailDescription=self.thumbnailDescription, imageDescription=caption, imageCaption=caption)
def run_task(self): # {{{ """ Performs analysis of the time-series output of Antarctic sub-ice-shelf melt rates. """ # Authors # ------- # Xylar Asay-Davis, Stephen Price self.logger.info("\nPlotting Antarctic melt rate time series...") self.logger.info(' Load melt rate data...') config = self.config calendar = self.calendar totalMeltFlux, meltRates = self._compute_ice_shelf_fluxes() plotRef = self.refConfig is not None if plotRef: refRunName = self.refConfig.get('runs', 'mainRunName') refTotalMeltFlux, refMeltRates = \ self._load_ice_shelf_fluxes(self.refConfig) # Load observations from multiple files and put in dictionary based # on shelf keyname observationsDirectory = build_config_full_path(config, 'oceanObservations', 'meltSubdirectory') obsFileNameDict = { 'Rignot et al. (2013)': 'Rignot_2013_melt_rates.csv', 'Rignot et al. (2013) SS': 'Rignot_2013_melt_rates_SS.csv' } obsDict = {} # dict for storing dict of obs data for obsName in obsFileNameDict: obsFileName = '{}/{}'.format(observationsDirectory, obsFileNameDict[obsName]) obsDict[obsName] = {} obsFile = csv.reader(open(obsFileName, 'rU')) next(obsFile, None) # skip the header line for line in obsFile: # some later useful values commented out shelfName = line[0] # surveyArea = line[1] meltFlux = float(line[2]) meltFluxUncertainty = float(line[3]) meltRate = float(line[4]) meltRateUncertainty = float(line[5]) # actualArea = float( line[6] ) # actual area here is in sq km # build dict of obs. keyed to filename description # (which will be used for plotting) obsDict[obsName][shelfName] = { 'meltFlux': meltFlux, 'meltFluxUncertainty': meltFluxUncertainty, 'meltRate': meltRate, 'meltRateUncertainty': meltRateUncertainty } # If areas from obs file used need to be converted from sq km to sq m mainRunName = config.get('runs', 'mainRunName') movingAverageMonths = config.getint('timeSeriesAntarcticMelt', 'movingAverageMonths') nRegions = totalMeltFlux.sizes['nRegions'] outputDirectory = build_config_full_path(config, 'output', 'timeseriesSubdirectory') make_directories(outputDirectory) self.logger.info(' Make plots...') for iRegion in range(nRegions): regionName = self.iceShelvesToPlot[iRegion] # get obs melt flux and unc. for shelf (similar for rates) obsMeltFlux = [] obsMeltFluxUnc = [] obsMeltRate = [] obsMeltRateUnc = [] for obsName in obsDict: if regionName in obsDict[obsName]: obsMeltFlux.append( obsDict[obsName][regionName]['meltFlux']) obsMeltFluxUnc.append( obsDict[obsName][regionName]['meltFluxUncertainty']) obsMeltRate.append( obsDict[obsName][regionName]['meltRate']) obsMeltRateUnc.append( obsDict[obsName][regionName]['meltRateUncertainty']) else: # append NaN so this particular obs won't plot self.logger.warning('{} observations not available for ' '{}'.format(obsName, regionName)) obsMeltFlux.append(None) obsMeltFluxUnc.append(None) obsMeltRate.append(None) obsMeltRateUnc.append(None) title = regionName.replace('_', ' ') regionName = regionName.replace(' ', '_') xLabel = 'Time (yr)' yLabel = 'Melt Flux (GT/yr)' timeSeries = totalMeltFlux.isel(nRegions=iRegion) filePrefix = 'melt_flux_{}'.format(regionName) figureName = '{}/{}.png'.format(self.plotsDirectory, filePrefix) fields = [timeSeries] lineColors = ['k'] lineWidths = [2.5] legendText = [mainRunName] if plotRef: fields.append(refTotalMeltFlux.isel(nRegions=iRegion)) lineColors.append('r') lineWidths.append(1.2) legendText.append(refRunName) timeseries_analysis_plot(config, fields, movingAverageMonths, title, xLabel, yLabel, figureName, calendar=calendar, lineColors=lineColors, lineWidths=lineWidths, legendText=legendText, obsMean=obsMeltFlux, obsUncertainty=obsMeltFluxUnc, obsLegend=list(obsDict.keys())) caption = 'Running Mean of Total Melt Flux under Ice ' \ 'Shelves in the {} Region'.format(title) write_image_xml(config=config, filePrefix=filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup='Antarctic Melt Time Series', groupLink='antmelttime', gallery='Total Melt Flux', thumbnailDescription=title, imageDescription=caption, imageCaption=caption) xLabel = 'Time (yr)' yLabel = 'Melt Rate (m/yr)' timeSeries = meltRates.isel(nRegions=iRegion) filePrefix = 'melt_rate_{}'.format(regionName) figureName = '{}/{}.png'.format(self.plotsDirectory, filePrefix) fields = [timeSeries] lineColors = ['k'] lineWidths = [2.5] legendText = [mainRunName] if plotRef: fields.append(refMeltRates.isel(nRegions=iRegion)) lineColors.append('r') lineWidths.append(1.2) legendText.append(refRunName) if config.has_option(self.taskName, 'firstYearXTicks'): firstYearXTicks = config.getint(self.taskName, 'firstYearXTicks') else: firstYearXTicks = None if config.has_option(self.taskName, 'yearStrideXTicks'): yearStrideXTicks = config.getint(self.taskName, 'yearStrideXTicks') else: yearStrideXTicks = None timeseries_analysis_plot(config, fields, movingAverageMonths, title, xLabel, yLabel, figureName, calendar=calendar, lineColors=lineColors, lineWidths=lineWidths, legendText=legendText, obsMean=obsMeltRate, obsUncertainty=obsMeltRateUnc, obsLegend=list(obsDict.keys()), firstYearXTicks=firstYearXTicks, yearStrideXTicks=yearStrideXTicks) caption = 'Running Mean of Area-averaged Melt Rate under Ice ' \ 'Shelves in the {} Region'.format(title) write_image_xml(config=config, filePrefix=filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup='Antarctic Melt Time Series', groupLink='antmelttime', gallery='Area-averaged Melt Rate', thumbnailDescription=title, imageDescription=caption, imageCaption=caption)
def _plot_antarctic(self, remappedModelClimatology, remappedRefClimatology): # {{{ """ plotting an Antarctic data set """ season = self.season comparisonGridName = self.comparisonGridName config = self.config configSectionName = self.configSectionName mainRunName = config.get('runs', 'mainRunName') oceanMask = remappedModelClimatology['validMask'].values self.landMask = np.ma.masked_array(np.ones(oceanMask.shape), mask=np.logical_not( np.isnan(oceanMask))) modelOutput = nans_to_numpy_mask( remappedModelClimatology[self.mpasFieldName].values) if remappedRefClimatology is None: refOutput = None bias = None else: refOutput = nans_to_numpy_mask( remappedRefClimatology[self.refFieldName].values) bias = modelOutput - refOutput x = interp_extrap_corner(remappedModelClimatology['x'].values) y = interp_extrap_corner(remappedModelClimatology['y'].values) filePrefix = self.filePrefix outFileName = '{}/{}.png'.format(self.plotsDirectory, filePrefix) title = '{} ({}, years {:04d}-{:04d})'.format(self.fieldNameInTitle, season, self.startYear, self.endYear) plot_polar_projection_comparison(config, x, y, self.landMask, modelOutput, refOutput, bias, fileout=outFileName, colorMapSectionName=configSectionName, title=title, modelTitle='{}'.format(mainRunName), refTitle=self.refTitleLabel, diffTitle=self.diffTitleLabel, cbarlabel=self.unitsLabel) upperGridName = comparisonGridName[0].upper() + comparisonGridName[1:] caption = '{} {}'.format(season, self.imageCaption) write_image_xml(config, filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup='{} {}'.format(upperGridName, self.galleryGroup), groupSubtitle=self.groupSubtitle, groupLink=self.groupLink, gallery=self.galleryName, thumbnailDescription=self.thumbnailDescription, imageDescription=caption, imageCaption=caption)
def run_task(self): # {{{ ''' Process MOC analysis member data if available, or compute MOC at post-processing if not. Plots streamfunction climatolgoical sections as well as time series of max Atlantic MOC at 26.5N (latitude of RAPID MOC Array). ''' # Authors # ------- # Milena Veneziani, Mark Petersen, Phillip J. Wolfram, Xylar Asay-Davis self.logger.info("\nPlotting streamfunction of Meridional Overturning " "Circulation (MOC)...") config = self.config # **** Compute MOC **** # Check whether MOC Analysis Member is enabled if self.mocAnalysisMemberEnabled: # Add a moc_analisysMember_processing self.logger.info('*** MOC Analysis Member is on ***') # (mocDictClimo, mocDictTseries) = \ # self._compute_moc_analysismember(config, streams, calendar, # sectionName, dictClimo, # dictTseries) # delete the following 3 lines after analysis of the MOC AM is # supported self.logger.info('...but not yet supported. Using offline MOC') self._compute_moc_climo_postprocess() dsMOCTimeSeries = self._compute_moc_time_series_postprocess() else: self._compute_moc_climo_postprocess() dsMOCTimeSeries = self._compute_moc_time_series_postprocess() # **** Plot MOC **** # Define plotting variables mainRunName = config.get('runs', 'mainRunName') movingAveragePoints = config.getint(self.sectionName, 'movingAveragePoints') movingAveragePointsClimatological = config.getint( self.sectionName, 'movingAveragePointsClimatological') colorbarLabel = '[Sv]' xLabel = 'latitude [deg]' yLabel = 'depth [m]' for region in self.regionNames: self.logger.info(' Plot climatological {} MOC...'.format(region)) title = '{} MOC (ANN, years {:04d}-{:04d})\n {}'.format( region, self.startYearClimo, self.endYearClimo, mainRunName) filePrefix = self.filePrefixes[region] figureName = '{}/{}.png'.format(self.plotsDirectory, filePrefix) x = self.lat[region] y = self.depth z = self.moc[region] plot_vertical_section(config, x, y, z, self.sectionName, suffix=region, colorbarLabel=colorbarLabel, title=title, xlabel=xLabel, ylabel=yLabel, fileout=figureName, N=movingAveragePointsClimatological) caption = '{} Meridional Overturning Streamfunction'.format(region) write_image_xml( config=config, filePrefix=filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup='Meridional Overturning Streamfunction', groupLink='moc', thumbnailDescription=region, imageDescription=caption, imageCaption=caption) # }}} # Plot time series self.logger.info(' Plot time series of max Atlantic MOC at 26.5N...') xLabel = 'Time [years]' yLabel = '[Sv]' title = 'Max Atlantic MOC at $26.5^\circ$N\n {}'.format(mainRunName) filePrefix = self.filePrefixes['timeSeries'] figureName = '{}/{}.png'.format(self.plotsDirectory, filePrefix) if config.has_option(self.taskName, 'firstYearXTicks'): firstYearXTicks = config.getint(self.taskName, 'firstYearXTicks') else: firstYearXTicks = None if config.has_option(self.taskName, 'yearStrideXTicks'): yearStrideXTicks = config.getint(self.taskName, 'yearStrideXTicks') else: yearStrideXTicks = None timeseries_analysis_plot(config, [dsMOCTimeSeries.mocAtlantic26], movingAveragePoints, title, xLabel, yLabel, figureName, lineStyles=['k-'], lineWidths=[2], legendText=[None], calendar=self.calendar, firstYearXTicks=firstYearXTicks, yearStrideXTicks=yearStrideXTicks) caption = u'Time Series of maximum Meridional Overturning ' \ u'Circulation at 26.5°N' write_image_xml(config=config, filePrefix=filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup='Meridional Overturning Streamfunction', groupLink='moc', thumbnailDescription='Time Series', imageDescription=caption, imageCaption=caption) # }}}
def run_task(self): # {{{ """ Performs analysis of sea-ice properties by comparing with previous model results and/or observations. """ # Authors # ------- # Xylar Asay-Davis, Milena Veneziani config = self.config season = self.season comparisonGridName = self.comparisonGridName self.logger.info("\nPlotting 2-d maps of {} climatologies for " "{} against {}...".format( self.fieldNameInTitle, season, self.refTitleLabel)) mainRunName = config.get('runs', 'mainRunName') startYear = self.startYear endYear = self.endYear hemisphere = self.hemisphere sectionName = self.taskName vertical = config.getboolean(sectionName, 'vertical') if hemisphere == 'NH': plotProjection = 'npstere' else: plotProjection = 'spstere' referenceLongitude = config.getfloat(sectionName, 'referenceLongitude') minimumLatitude = config.getfloat(sectionName, 'minimumLatitude') remappedFileName = \ self.remapMpasClimatologySubtask.get_remapped_file_name( season=season, comparisonGridName=comparisonGridName) remappedClimatology = xr.open_dataset(remappedFileName) modelOutput = remappedClimatology[self.mpasFieldName].values if self.maskValue is not None: modelOutput = ma.masked_values(modelOutput, self.maskValue) lon = remappedClimatology['lon'].values lat = remappedClimatology['lat'].values lonTarg, latTarg = np.meshgrid(lon, lat) if self.remapObsClimatologySubtask is not None: remappedFileName = self.remapObsClimatologySubtask.get_file_name( stage='remapped', season=season, comparisonGridName=comparisonGridName) remappedRefClimatology = xr.open_dataset(remappedFileName) refOutput = remappedRefClimatology[self.refFieldName].values if self.maskValue is not None: refOutput = ma.masked_values(refOutput, self.maskValue) difference = modelOutput - refOutput elif self.refConfig is not None: climatologyName = self.remapMpasClimatologySubtask.climatologyName remappedFileName = \ get_remapped_mpas_climatology_file_name( self.refConfig, season=season, componentName=self.componentName, climatologyName=climatologyName, comparisonGridName=comparisonGridName) remappedRefClimatology = xr.open_dataset(remappedFileName) refStartYear = self.refConfig.getint('climatology', 'startYear') refEndYear = self.refConfig.getint('climatology', 'endYear') if refStartYear != self.startYear or refEndYear != self.endYear: self.refTitleLabel = '{}\n(years {:04d}-{:04d})'.format( self.refTitleLabel, refStartYear, refEndYear) else: remappedRefClimatology = None if remappedRefClimatology is None: refOutput = None difference = None else: refOutput = remappedRefClimatology[self.refFieldName].values if self.maskValue is not None: refOutput = ma.masked_values(refOutput, self.maskValue) difference = modelOutput - refOutput startYear = self.startYear endYear = self.endYear filePrefix = self.filePrefix title = '{} ({}, years {:04d}-{:04d})'.format( self.fieldNameInTitle, season, startYear, endYear) fileout = '{}/{}.png'.format(self.plotsDirectory, filePrefix) plot_polar_comparison( config, lonTarg, latTarg, modelOutput, refOutput, difference, sectionName, title=title, fileout=fileout, plotProjection=plotProjection, latmin=minimumLatitude, lon0=referenceLongitude, modelTitle=mainRunName, refTitle=self.refTitleLabel, diffTitle=self.diffTitleLabel, cbarlabel=self.unitsLabel, vertical=vertical) write_image_xml( config, filePrefix, componentName='Sea Ice', componentSubdirectory='sea_ice', galleryGroup=self.galleryGroup, groupSubtitle=self.groupSubtitle, groupLink=self.groupLink, gallery=self.galleryName, thumbnailDescription=season, imageDescription=self.imageDescription, imageCaption=self.imageCaption)
def run_task(self): # {{{ """ Plots time-series output of transport through transects. """ # Authors # ------- # Xylar Asay-Davis, Stephen Price self.logger.info("\nPlotting time series of transport through " "{}...".format(self.transect)) self.logger.info(' Load transport data...') obsDict = { 'Drake Passage': [120, 175], 'Tasmania-Ant': [147, 167], 'Africa-Ant': None, 'Antilles Inflow': [-23.1, -13.7], 'Mona Passage': [-3.8, -1.4], 'Windward Passage': [-7.2, -6.8], 'Florida-Cuba': [30, 33], 'Florida-Bahamas': [30, 33], 'Indonesian Throughflow': [-21, -11], 'Agulhas': [-90, -50], 'Mozambique Channel': [-20, -8], 'Bering Strait': [0.6, 1.0], 'Lancaster Sound': [-1.0, -0.5], 'Fram Strait': [-4.7, 0.7], 'Davis Strait': [-1.6, -3.6], 'Barents Sea Opening': [1.4, 2.6], 'Nares Strait': [-1.8, 0.2] } config = self.config calendar = self.calendar fcAll = read_feature_collection(self.transportTransectFileName) fc = FeatureCollection() for feature in fcAll.features: if feature['properties']['name'] == self.transect: fc.add_feature(feature) break transport, trans_mean, trans_std = self._load_transport(config) if self.transect in obsDict: bounds = obsDict[self.transect] else: bounds = None plotControl = self.controlConfig is not None mainRunName = config.get('runs', 'mainRunName') movingAverageMonths = config.getint('timeSeriesTransport', 'movingAverageMonths') self.logger.info(' Plotting...') transectName = self.transect.replace('_', ' ') title = transectName thumbnailDescription = transectName xLabel = 'Time (yr)' yLabel = 'Transport (Sv)' filePrefix = 'transport_{}'.format(self.transect.replace(' ', '_')) outFileName = '{}/{}.png'.format(self.plotsDirectory, filePrefix) fields = [transport] lineColors = ['k'] lineWidths = [2.5] meanString = 'mean={:.2f} $\pm$ {:.2f}'.format(trans_mean, trans_std) if plotControl: controlRunName = self.controlConfig.get('runs', 'mainRunName') ref_transport, ref_mean, ref_std = \ self._load_transport(self.controlConfig) refMeanString = 'mean={:.2f} $\pm$ {:.2f}'.format( ref_mean, ref_std) fields.append(ref_transport) lineColors.append('r') lineWidths.append(1.2) legendText = [ '{} ({})'.format(mainRunName, meanString), '{} ({})'.format(controlRunName, refMeanString) ] else: legendText = [mainRunName] title = '{} ({})'.format(title, meanString) if config.has_option(self.taskName, 'firstYearXTicks'): firstYearXTicks = config.getint(self.taskName, 'firstYearXTicks') else: firstYearXTicks = None if config.has_option(self.taskName, 'yearStrideXTicks'): yearStrideXTicks = config.getint(self.taskName, 'yearStrideXTicks') else: yearStrideXTicks = None fig = timeseries_analysis_plot(config, fields, calendar=calendar, title=title, xlabel=xLabel, ylabel=yLabel, movingAveragePoints=movingAverageMonths, lineColors=lineColors, lineWidths=lineWidths, legendText=legendText, firstYearXTicks=firstYearXTicks, yearStrideXTicks=yearStrideXTicks) if bounds is not None: t = transport.Time.values plt.gca().fill_between(t, bounds[0] * numpy.ones_like(t), bounds[1] * numpy.ones_like(t), alpha=0.3, label='observations') plt.legend(loc='lower left') # do this before the inset because otherwise it moves the inset # and cartopy doesn't play too well with tight_layout anyway plt.tight_layout() add_inset(fig, fc, width=2.0, height=2.0) savefig(outFileName) caption = 'Transport through the {} Transect'.format(transectName) write_image_xml(config=config, filePrefix=filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup='Transport Time Series', groupLink='transporttime', thumbnailDescription=thumbnailDescription, imageDescription=caption, imageCaption=caption)
def _plot_transect(self, remappedModelClimatology, remappedRefClimatology): # {{{ """ plotting the transect """ season = self.season config = self.config configSectionName = self.configSectionName mainRunName = config.get('runs', 'mainRunName') # broadcast x and z to have the same dimensions x, z = xr.broadcast(remappedModelClimatology.x, remappedModelClimatology.z) # set lat and lon in case we want to plot versus these quantities lat = remappedModelClimatology.lat lon = remappedModelClimatology.lon # convert x, z, lat, and lon to numpy arrays; make a copy because # they are sometimes read-only (not sure why) x = x.values.copy().transpose() z = z.values.copy().transpose() lat = lat.values.copy().transpose() lon = lon.values.copy().transpose() self.lat = lat self.lon = lon # This will do strange things at the antemeridian but there's little # we can do about that. lon_pm180 = numpy.mod(lon + 180., 360.) - 180. if self.horizontalBounds is not None: mask = numpy.logical_and( remappedModelClimatology.x.values >= self.horizontalBounds[0], remappedModelClimatology.x.values <= self.horizontalBounds[1]) inset_lon = lon_pm180[mask] inset_lat = lat[mask] else: inset_lon = lon_pm180 inset_lat = lat fc = FeatureCollection() fc.add_feature({ "type": "Feature", "properties": { "name": self.transectName, "author": 'Xylar Asay-Davis', "object": 'transect', "component": 'ocean', "tags": '' }, "geometry": { "type": "LineString", "coordinates": list(map(list, zip(inset_lon, inset_lat))) } }) # z is masked out with NaNs in some locations (where there is land) but # this makes pcolormesh unhappy so we'll zero out those locations z[numpy.isnan(z)] = 0. modelOutput = nans_to_numpy_mask( remappedModelClimatology[self.mpasFieldName].values) modelOutput = modelOutput.transpose() if remappedRefClimatology is None: refOutput = None bias = None else: refOutput = remappedRefClimatology[self.refFieldName] dims = refOutput.dims refOutput = nans_to_numpy_mask(refOutput.values) if dims[1] != 'nPoints': assert (dims[0] == 'nPoints') refOutput = refOutput.transpose() bias = modelOutput - refOutput filePrefix = self.filePrefix outFileName = '{}/{}.png'.format(self.plotsDirectory, filePrefix) title = '{}\n({}, years {:04d}-{:04d})'.format(self.fieldNameInTitle, season, self.startYear, self.endYear) xLabel = 'Distance [km]' yLabel = 'Depth [m]' # define the axis labels and the data to use for the upper # x axis or axes, if such additional axes have been requested upperXAxes = config.get('transects', 'upperXAxes') numUpperTicks = config.getint('transects', 'numUpperTicks') upperXAxisTickLabelPrecision = config.getint( 'transects', 'upperXAxisTickLabelPrecision') self._set_third_x_axis_to_none() if upperXAxes == 'neither': self._set_second_x_axis_to_none() elif upperXAxes == 'lat': self._set_second_x_axis_to_latitude() elif upperXAxes == 'lon': self._set_second_x_axis_to_longitude() elif upperXAxes == 'both': self._set_second_x_axis_to_longitude() self._set_third_x_axis_to_latitude() elif upperXAxes == 'greatestExtent': if self._greatest_extent(lat, lon): self._set_second_x_axis_to_latitude() else: self._set_second_x_axis_to_longitude() elif upperXAxes == 'strictlyMonotonic': if self._strictly_monotonic(lat, lon): self._set_second_x_axis_to_latitude() else: self._set_second_x_axis_to_longitude() elif upperXAxes == 'mostMonotonic': if self._most_monotonic(lat, lon): self._set_second_x_axis_to_latitude() else: self._set_second_x_axis_to_longitude() elif upperXAxes == 'mostStepsInSameDirection': if self._most_steps_in_same_direction(lat, lon): self._set_second_x_axis_to_latitude() else: self._set_second_x_axis_to_longitude() elif upperXAxes == 'fewestDirectionChanges': if self._fewest_direction_changes(lat, lon): self._set_second_x_axis_to_latitude() else: self._set_second_x_axis_to_longitude() else: raise ValueError('invalid option for upperXAxes') # get the parameters determining what type of plot to use, # what line styles and line colors to use, and whether and how # to label contours compareAsContours = config.getboolean('transects', 'compareAsContoursOnSinglePlot') contourLineStyle = config.get('transects', 'contourLineStyle') contourLineColor = config.get('transects', 'contourLineColor') comparisonContourLineStyle = config.get('transects', 'comparisonContourLineStyle') comparisonContourLineColor = config.get('transects', 'comparisonContourLineColor') if compareAsContours: labelContours = config.getboolean( 'transects', 'labelContoursOnContourComparisonPlots') else: labelContours = config.getboolean('transects', 'labelContoursOnHeatmaps') contourLabelPrecision = config.getint('transects', 'contourLabelPrecision') # construct a three-panel comparison plot for the transect, or a # single-panel contour comparison plot if compareAsContours is True fig, axes, suptitle = plot_vertical_section_comparison( config, x, z, modelOutput, refOutput, bias, configSectionName, cbarLabel=self.unitsLabel, xlabel=xLabel, ylabel=yLabel, title=title, modelTitle='{}'.format(mainRunName), refTitle=self.refTitleLabel, diffTitle=self.diffTitleLabel, secondXAxisData=self.secondXAxisData, secondXAxisLabel=self.secondXAxisLabel, thirdXAxisData=self.thirdXAxisData, thirdXAxisLabel=self.thirdXAxisLabel, numUpperTicks=numUpperTicks, upperXAxisTickLabelPrecision=upperXAxisTickLabelPrecision, invertYAxis=False, backgroundColor='#918167', xLim=self.horizontalBounds, compareAsContours=compareAsContours, lineStyle=contourLineStyle, lineColor=contourLineColor, comparisonContourLineStyle=comparisonContourLineStyle, comparisonContourLineColor=comparisonContourLineColor, labelContours=labelContours, contourLabelPrecision=contourLabelPrecision) # shift the super-title a little to the left to make room for the inset pos = suptitle.get_position() suptitle.set_position((pos[0] - 0.05, pos[1])) # make a red start axis and green end axis to correspond to the dots # in the inset for ax in axes: ax.spines['left'].set_color('red') ax.spines['right'].set_color('green') ax.spines['left'].set_linewidth(4) ax.spines['right'].set_linewidth(4) add_inset(fig, fc, width=1.5, height=1.5, xbuffer=0.1, ybuffer=0.1) savefig(outFileName, tight=False) caption = '{} {}'.format(season, self.imageCaption) write_image_xml(config, filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup=self.galleryGroup, groupSubtitle=self.groupSubtitle, groupLink=self.groupLink, gallery=self.galleryName, thumbnailDescription=self.thumbnailDescription, imageDescription=caption, imageCaption=caption)
def run_task(self): # {{{ """ Make the Hovmoller plot from the time series. """ # Authors # ------- # Xylar Asay-Davis, Milena Veneziani, Greg Streletz self.logger.info("\nPlotting {} time series vs. depth...".format( self.fieldNameInTitle)) config = self.config mainRunName = config.get('runs', 'mainRunName') self.logger.info(' Load ocean data...') ds = xr.open_dataset(self.inFileName) if 'regionNames' in ds.coords: allRegionNames = decode_strings(ds.regionNames) regionIndex = allRegionNames.index(self.regionName) regionNameInTitle = self.regionName.replace('_', ' ') regionDim = ds.regionNames.dims[0] else: plotTitles = config.getExpression('regions', 'plotTitles') allRegionNames = config.getExpression('regions', 'regions') regionIndex = allRegionNames.index(self.regionName) regionNameInTitle = plotTitles[regionIndex] regionDim = 'nOceanRegionsTmp' ds = ds.isel(**{regionDim: regionIndex}) # Note: restart file, not a mesh file because we need refBottomDepth, # not in a mesh file try: restartFile = self.runStreams.readpath('restart')[0] except ValueError: raise IOError('No MPAS-O restart file found: need at least one ' 'restart file for plotting time series vs. depth') # Define/read in general variables self.logger.info(' Read in depth...') with xr.open_dataset(restartFile) as dsRestart: # reference depth [m] depths = dsRestart.refBottomDepth.values z = np.zeros(depths.shape) z[0] = -0.5 * depths[0] z[1:] = -0.5 * (depths[0:-1] + depths[1:]) Time = ds.Time.values field = ds[self.mpasFieldName].values.transpose() xLabel = 'Time (years)' yLabel = 'Depth (m)' title = '{}, {} \n {}'.format(self.fieldNameInTitle, regionNameInTitle, mainRunName) outFileName = '{}/{}.png'.format(self.plotsDirectory, self.filePrefix) if config.has_option(self.sectionName, 'firstYearXTicks'): firstYearXTicks = config.getint(self.sectionName, 'firstYearXTicks') else: firstYearXTicks = None if config.has_option(self.sectionName, 'yearStrideXTicks'): yearStrideXTicks = config.getint(self.sectionName, 'yearStrideXTicks') else: yearStrideXTicks = None if config.has_option(self.sectionName, 'yLim'): yLim = config.getExpression(self.sectionName, 'yLim') else: yLim = None plot_vertical_section(config, Time, z, field, self.sectionName, suffix='', colorbarLabel=self.unitsLabel, title=title, xlabel=xLabel, ylabel=yLabel, lineWidth=1, xArrayIsTime=True, calendar=self.calendar, firstYearXTicks=firstYearXTicks, yearStrideXTicks=yearStrideXTicks, yLim=yLim, invertYAxis=False) savefig(outFileName) write_image_xml(config=config, filePrefix=self.filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup=self.galleryGroup, groupSubtitle=self.groupSubtitle, groupLink=self.groupLink, gallery=self.galleryName, thumbnailDescription='{} {}'.format( regionNameInTitle, self.thumbnailSuffix), imageDescription=self.imageCaption, imageCaption=self.imageCaption)
def run_task(self): # {{{ """ Plot a depth profile with variability """ # Authors # ------- # Xylar Asay-Davis config = self.config startYear = self.startYear endYear = self.endYear regionMaskFile = self.masksSubtask.geojsonFileName fcAll = read_feature_collection(regionMaskFile) fc = FeatureCollection() for feature in fcAll.features: if feature['properties']['name'] == self.regionName: fc.add_feature(feature) break inDirectory = build_config_full_path(config, 'output', 'profilesSubdirectory') timeSeriesName = self.timeSeriesName inFileName = '{}/{}_{}_{:04d}-{:04d}.nc'.format( inDirectory, timeSeriesName, self.season, self.startYear, self.endYear) regionGroup = self.masksSubtask.regionGroup regionGroupSection = 'profiles{}'.format( regionGroup.replace(' ', '')) ds = xr.open_dataset(inFileName) allRegionNames = decode_strings(ds.regionNames) regionIndex = allRegionNames.index(self.regionName) ds = ds.isel(nRegions=regionIndex) meanFieldName = '{}_mean'.format(self.field['prefix']) stdFieldName = '{}_std'.format(self.field['prefix']) mainRunName = config.get('runs', 'mainRunName') profileGalleryGroup = config.get(regionGroupSection, 'profileGalleryGroup') titleFieldName = self.field['titleName'] regionName = self.regionName.replace('_', ' ') xLabel = '{} ({})'.format(titleFieldName, self.field['units']) yLabel = 'depth (m)' outFileName = '{}/{}.png'.format(self.plotsDirectory, self.filePrefix) lineColors = ['k'] lineWidths = [1.6] zArrays = [ds.z.values] fieldArrays = [ds[meanFieldName].values] errArrays = [ds[stdFieldName].values] if self.controlConfig is None: title = '{} {}, years {:04d}-{:04d}\n{}'.format( regionName, self.season, startYear, endYear, mainRunName) legendText = [None] else: controlStartYear = self.controlConfig.getint('climatology', 'startYear') controlEndYear = self.controlConfig.getint('climatology', 'endYear') controlRunName = self.controlConfig.get('runs', 'mainRunName') if controlStartYear == startYear and controlEndYear == endYear: title = '{} {}, years {:04d}-{:04d}'.format( regionName, self.season, startYear, endYear) legendText = [mainRunName, controlRunName] elif mainRunName == controlRunName: title = '{} {}\n{}'.format( regionName, self.season, mainRunName) legendText = ['{:04d}-{:04d}'.format(startYear, endYear), '{:04d}-{:04d}'.format(controlStartYear, controlEndYear)] else: title = '{} {} '.format(regionName, self.season) legendText = ['{} {:04d}-{:04d}'.format(mainRunName, startYear, endYear), '{} {:04d}-{:04d}'.format(controlRunName, controlStartYear, controlEndYear)] controlDirectory = build_config_full_path( self.controlConfig, 'output', 'profilesSubdirectory') controlFileName = \ '{}/{}_{}_{:04d}-{:04d}.nc'.format( controlDirectory, timeSeriesName, self.season, controlStartYear, controlEndYear) dsControl = xr.open_dataset(controlFileName) allRegionNames = decode_strings(dsControl.regionNames) regionIndex = allRegionNames.index(self.regionName) dsControl = dsControl.isel(nRegions=regionIndex) lineColors.append('r') lineWidths.append(1.2) zArrays.append(dsControl.z.values) fieldArrays.append(dsControl[meanFieldName].values) errArrays.append(dsControl[stdFieldName].values) depthRange = config.getExpression(regionGroupSection, 'depthRange') if len(depthRange) == 0: depthRange = None fig = self.plot(zArrays, fieldArrays, errArrays, lineColors=lineColors, lineWidths=lineWidths, legendText=legendText, title=title, xLabel=xLabel, yLabel=yLabel, yLim=depthRange) # do this before the inset because otherwise it moves the inset # and cartopy doesn't play too well with tight_layout anyway plt.tight_layout() add_inset(fig, fc, width=1.0, height=1.0) savefig(outFileName, tight=False) caption = '{} {} vs depth'.format(regionName, titleFieldName) write_image_xml( config=config, filePrefix=self.filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup=profileGalleryGroup, groupLink='ocnregprofs', imageDescription=caption, imageCaption=caption, gallery=titleFieldName, thumbnailDescription='{} {}'.format(regionName, self.season))
def run_task(self): # {{{ """ Performs analysis of time series of sea-ice properties. """ # Authors # ------- # Xylar Asay-Davis, Milena Veneziani self.logger.info("\nPlotting sea-ice area and volume time series...") config = self.config calendar = self.calendar sectionName = self.taskName plotTitles = {'iceArea': 'Sea-ice area', 'iceVolume': 'Sea-ice volume', 'iceThickness': 'Sea-ice mean thickness'} units = {'iceArea': '[km$^2$]', 'iceVolume': '[10$^3$ km$^3$]', 'iceThickness': '[m]'} obsFileNames = { 'iceArea': {'NH': build_obs_path( config, 'seaIce', relativePathOption='areaNH', relativePathSection=sectionName), 'SH': build_obs_path( config, 'seaIce', relativePathOption='areaSH', relativePathSection=sectionName)}, 'iceVolume': {'NH': build_obs_path( config, 'seaIce', relativePathOption='volNH', relativePathSection=sectionName), 'SH': build_obs_path( config, 'seaIce', relativePathOption='volSH', relativePathSection=sectionName)}} # Some plotting rules titleFontSize = config.get('timeSeriesSeaIceAreaVol', 'titleFontSize') mainRunName = config.get('runs', 'mainRunName') preprocessedReferenceRunName = \ config.get('runs', 'preprocessedReferenceRunName') preprocessedReferenceDirectory = \ config.get('seaIcePreprocessedReference', 'baseDirectory') compareWithObservations = config.getboolean('timeSeriesSeaIceAreaVol', 'compareWithObservations') movingAveragePoints = config.getint('timeSeriesSeaIceAreaVol', 'movingAveragePoints') polarPlot = config.getboolean('timeSeriesSeaIceAreaVol', 'polarPlot') outputDirectory = build_config_full_path(config, 'output', 'timeseriesSubdirectory') make_directories(outputDirectory) self.logger.info(' Load sea-ice data...') # Load mesh dsTimeSeries = self._compute_area_vol() yearStart = days_to_datetime(dsTimeSeries['NH'].Time.min(), calendar=calendar).year yearEnd = days_to_datetime(dsTimeSeries['NH'].Time.max(), calendar=calendar).year timeStart = date_to_days(year=yearStart, month=1, day=1, calendar=calendar) timeEnd = date_to_days(year=yearEnd, month=12, day=31, calendar=calendar) if preprocessedReferenceRunName != 'None': # determine if we're beyond the end of the preprocessed data # (and go ahead and cache the data set while we're checking) outFolder = '{}/preprocessed'.format(outputDirectory) make_directories(outFolder) inFilesPreprocessed = '{}/icevol.{}.year*.nc'.format( preprocessedReferenceDirectory, preprocessedReferenceRunName) outFileName = '{}/iceVolume.nc'.format(outFolder) combine_time_series_with_ncrcat(inFilesPreprocessed, outFileName, logger=self.logger) dsPreprocessed = open_mpas_dataset(fileName=outFileName, calendar=calendar, timeVariableNames='xtime') preprocessedYearEnd = days_to_datetime(dsPreprocessed.Time.max(), calendar=calendar).year if yearStart <= preprocessedYearEnd: dsPreprocessedTimeSlice = \ dsPreprocessed.sel(Time=slice(timeStart, timeEnd)) else: self.logger.warning('Preprocessed time series ends before the ' 'timeSeries startYear and will not be ' 'plotted.') preprocessedReferenceRunName = 'None' if self.controlConfig is not None: dsTimeSeriesRef = {} baseDirectory = build_config_full_path( self.controlConfig, 'output', 'timeSeriesSubdirectory') controlRunName = self.controlConfig.get('runs', 'mainRunName') for hemisphere in ['NH', 'SH']: inFileName = '{}/seaIceAreaVol{}.nc'.format(baseDirectory, hemisphere) dsTimeSeriesRef[hemisphere] = xr.open_dataset(inFileName) norm = {'iceArea': 1e-6, # m^2 to km^2 'iceVolume': 1e-12, # m^3 to 10^3 km^3 'iceThickness': 1.} xLabel = 'Time [years]' galleryGroup = 'Time Series' groupLink = 'timeseries' obs = {} preprocessed = {} figureNameStd = {} figureNamePolar = {} title = {} plotVars = {} obsLegend = {} plotVarsRef = {} for hemisphere in ['NH', 'SH']: self.logger.info(' Make {} plots...'.format(hemisphere)) for variableName in ['iceArea', 'iceVolume']: key = (hemisphere, variableName) # apply the norm to each variable plotVars[key] = (norm[variableName] * dsTimeSeries[hemisphere][variableName]) if self.controlConfig is not None: plotVarsRef[key] = norm[variableName] * \ dsTimeSeriesRef[hemisphere][variableName] prefix = '{}/{}{}_{}'.format(self.plotsDirectory, variableName, hemisphere, mainRunName) figureNameStd[key] = '{}.png'.format(prefix) figureNamePolar[key] = '{}_polar.png'.format(prefix) title[key] = '{} ({})'.format(plotTitles[variableName], hemisphere) if compareWithObservations: key = (hemisphere, 'iceArea') obsLegend[key] = 'SSM/I observations, annual cycle ' if hemisphere == 'NH': key = (hemisphere, 'iceVolume') obsLegend[key] = 'PIOMAS, annual cycle (blue)' if preprocessedReferenceRunName != 'None': for variableName in ['iceArea', 'iceVolume']: key = (hemisphere, variableName) if compareWithObservations: outFolder = '{}/obs'.format(outputDirectory) make_directories(outFolder) outFileName = '{}/iceArea{}.nc'.format(outFolder, hemisphere) combine_time_series_with_ncrcat( obsFileNames['iceArea'][hemisphere], outFileName, logger=self.logger) dsObs = open_mpas_dataset(fileName=outFileName, calendar=calendar, timeVariableNames='xtime') key = (hemisphere, 'iceArea') obs[key] = self._replicate_cycle(plotVars[key], dsObs.IceArea, calendar) key = (hemisphere, 'iceVolume') if hemisphere == 'NH': outFileName = '{}/iceVolume{}.nc'.format(outFolder, hemisphere) combine_time_series_with_ncrcat( obsFileNames['iceVolume'][hemisphere], outFileName, logger=self.logger) dsObs = open_mpas_dataset(fileName=outFileName, calendar=calendar, timeVariableNames='xtime') obs[key] = self._replicate_cycle(plotVars[key], dsObs.IceVol, calendar) else: obs[key] = None if preprocessedReferenceRunName != 'None': outFolder = '{}/preprocessed'.format(outputDirectory) inFilesPreprocessed = '{}/icearea.{}.year*.nc'.format( preprocessedReferenceDirectory, preprocessedReferenceRunName) outFileName = '{}/iceArea.nc'.format(outFolder) combine_time_series_with_ncrcat(inFilesPreprocessed, outFileName, logger=self.logger) dsPreprocessed = open_mpas_dataset(fileName=outFileName, calendar=calendar, timeVariableNames='xtime') dsPreprocessedTimeSlice = dsPreprocessed.sel( Time=slice(timeStart, timeEnd)) key = (hemisphere, 'iceArea') preprocessed[key] = dsPreprocessedTimeSlice[ 'icearea_{}'.format(hemisphere.lower())] inFilesPreprocessed = '{}/icevol.{}.year*.nc'.format( preprocessedReferenceDirectory, preprocessedReferenceRunName) outFileName = '{}/iceVolume.nc'.format(outFolder) combine_time_series_with_ncrcat(inFilesPreprocessed, outFileName, logger=self.logger) dsPreprocessed = open_mpas_dataset(fileName=outFileName, calendar=calendar, timeVariableNames='xtime') dsPreprocessedTimeSlice = dsPreprocessed.sel( Time=slice(timeStart, timeEnd)) key = (hemisphere, 'iceVolume') preprocessed[key] = dsPreprocessedTimeSlice[ 'icevolume_{}'.format(hemisphere.lower())] for variableName in ['iceArea', 'iceVolume']: key = (hemisphere, variableName) dsvalues = [plotVars[key]] legendText = [mainRunName] lineColors = ['k'] lineWidths = [3] if compareWithObservations and key in obsLegend.keys(): dsvalues.append(obs[key]) legendText.append(obsLegend[key]) lineColors.append('b') lineWidths.append(1.2) if preprocessedReferenceRunName != 'None': dsvalues.append(preprocessed[key]) legendText.append(preprocessedReferenceRunName) lineColors.append('purple') lineWidths.append(1.2) if self.controlConfig is not None: dsvalues.append(plotVarsRef[key]) legendText.append(controlRunName) lineColors.append('r') lineWidths.append(1.2) if config.has_option(sectionName, 'firstYearXTicks'): firstYearXTicks = config.getint(sectionName, 'firstYearXTicks') else: firstYearXTicks = None if config.has_option(sectionName, 'yearStrideXTicks'): yearStrideXTicks = config.getint(sectionName, 'yearStrideXTicks') else: yearStrideXTicks = None # separate plots for nothern and southern hemispheres timeseries_analysis_plot(config, dsvalues, movingAveragePoints, title[key], xLabel, units[variableName], calendar=calendar, lineColors=lineColors, lineWidths=lineWidths, legendText=legendText, titleFontSize=titleFontSize, firstYearXTicks=firstYearXTicks, yearStrideXTicks=yearStrideXTicks) savefig(figureNameStd[key]) filePrefix = '{}{}_{}'.format(variableName, hemisphere, mainRunName) thumbnailDescription = '{} {}'.format( hemisphere, plotTitles[variableName]) caption = 'Running mean of {}'.format( thumbnailDescription) write_image_xml( config, filePrefix, componentName='Sea Ice', componentSubdirectory='sea_ice', galleryGroup=galleryGroup, groupLink=groupLink, thumbnailDescription=thumbnailDescription, imageDescription=caption, imageCaption=caption) if (polarPlot): timeseries_analysis_plot_polar( config, dsvalues, movingAveragePoints, title[key], lineColors=lineColors, lineWidths=lineWidths, legendText=legendText, titleFontSize=titleFontSize) savefig(figureNamePolar[key]) filePrefix = '{}{}_{}_polar'.format(variableName, hemisphere, mainRunName) write_image_xml( config, filePrefix, componentName='Sea Ice', componentSubdirectory='sea_ice', galleryGroup=galleryGroup, groupLink=groupLink, thumbnailDescription=thumbnailDescription, imageDescription=caption, imageCaption=caption)
def run_task(self): # {{{ """ Plots time-series output of properties in an ocean region. """ # Authors # ------- # Xylar Asay-Davis self.logger.info("\nPlotting time series of ocean properties of {}" "...".format(self.regionName)) self.logger.info(' Load time series...') config = self.config calendar = self.calendar regionMaskSuffix = config.getExpression(self.sectionName, 'regionMaskSuffix') regionMaskFile = get_region_mask(config, '{}.geojson'.format(regionMaskSuffix)) fcAll = read_feature_collection(regionMaskFile) fc = FeatureCollection() for feature in fcAll.features: if feature['properties']['name'] == self.regionName: fc.add_feature(feature) break baseDirectory = build_config_full_path(config, 'output', 'timeSeriesSubdirectory') startYear = config.getint('timeSeries', 'startYear') endYear = config.getint('timeSeries', 'endYear') regionGroup = self.regionGroup timeSeriesName = regionGroup[0].lower() + \ regionGroup[1:].replace(' ', '') inFileName = '{}/{}/{}_{:04d}-{:04d}.nc'.format( baseDirectory, timeSeriesName, timeSeriesName, startYear, endYear) dsIn = xarray.open_dataset(inFileName).isel(nRegions=self.regionIndex) zbounds = dsIn.zbounds.values controlConfig = self.controlConfig plotControl = controlConfig is not None if plotControl: controlRunName = controlConfig.get('runs', 'mainRunName') baseDirectory = build_config_full_path(controlConfig, 'output', 'timeSeriesSubdirectory') startYear = controlConfig.getint('timeSeries', 'startYear') endYear = controlConfig.getint('timeSeries', 'endYear') inFileName = '{}/{}/{}_{:04d}-{:04d}.nc'.format( baseDirectory, timeSeriesName, timeSeriesName, startYear, endYear) dsRef = xarray.open_dataset(inFileName).isel( nRegions=self.regionIndex) zboundsRef = dsRef.zbounds.values mainRunName = config.get('runs', 'mainRunName') movingAverageMonths = 1 self.logger.info(' Make plots...') groupLink = self.regionGroup[0].lower() + \ self.regionGroup[1:].replace(' ', '') for var in self.variables: varName = var['name'] mainArray = dsIn[varName] is3d = mainArray.attrs['is3d'] == 'True' if is3d: title = 'Volume-Mean {} in {}'.format(var['title'], self.regionName) else: title = 'Area-Mean {} in {}'.format(var['title'], self.regionName) if plotControl: refArray = dsRef[varName] xLabel = 'Time (yr)' yLabel = '{} ({})'.format(var['title'], var['units']) filePrefix = '{}_{}'.format(self.prefix, varName) outFileName = '{}/{}.png'.format(self.plotsDirectory, filePrefix) fields = [mainArray] lineColors = ['k'] lineWidths = [2.5] legendText = [mainRunName] if plotControl: fields.append(refArray) lineColors.append('r') lineWidths.append(1.2) legendText.append(controlRunName) if is3d: if not plotControl or numpy.all(zbounds == zboundsRef): title = '{} ({} < z < {} m)'.format( title, zbounds[0], zbounds[1]) else: legendText[0] = '{} ({} < z < {} m)'.format( legendText[0], zbounds[0], zbounds[1]) legendText[1] = '{} ({} < z < {} m)'.format( legendText[1], zboundsRef[0], zboundsRef[1]) fig = timeseries_analysis_plot( config, fields, calendar=calendar, title=title, xlabel=xLabel, ylabel=yLabel, movingAveragePoints=movingAverageMonths, lineColors=lineColors, lineWidths=lineWidths, legendText=legendText) # do this before the inset because otherwise it moves the inset # and cartopy doesn't play too well with tight_layout anyway plt.tight_layout() add_inset(fig, fc, width=2.0, height=2.0) savefig(outFileName, tight=False) caption = 'Regional mean of {}'.format(title) write_image_xml(config=config, filePrefix=filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup='{} Time Series'.format( self.regionGroup), groupLink=groupLink, gallery=var['title'], thumbnailDescription=self.regionName, imageDescription=caption, imageCaption=caption)
def run_task(self): # {{{ """ Performs analysis of sea-ice properties by comparing with previous model results and/or observations. """ # Authors # ------- # Xylar Asay-Davis, Milena Veneziani config = self.config season = self.season comparisonGridName = self.comparisonGridName self.logger.info("\nPlotting 2-d maps of {} climatologies for " "{} against {}...".format(self.fieldNameInTitle, season, self.refTitleLabel)) mainRunName = config.get('runs', 'mainRunName') startYear = self.startYear endYear = self.endYear hemisphere = self.hemisphere sectionName = self.taskName vertical = config.getboolean(sectionName, 'vertical') if hemisphere == 'NH': plotProjection = 'npstere' else: plotProjection = 'spstere' referenceLongitude = config.getfloat(sectionName, 'referenceLongitude') minimumLatitude = config.getfloat(sectionName, 'minimumLatitude') remappedFileName = \ self.remapMpasClimatologySubtask.get_remapped_file_name( season=season, comparisonGridName=comparisonGridName) remappedClimatology = xr.open_dataset(remappedFileName) modelOutput = remappedClimatology[self.mpasFieldName].values # mask nans modelOutput = np.ma.masked_array(modelOutput, np.isnan(modelOutput)) lon = remappedClimatology['lon'].values lat = remappedClimatology['lat'].values if self.remapObsClimatologySubtask is not None: remappedFileName = self.remapObsClimatologySubtask.get_file_name( stage='remapped', season=season, comparisonGridName=comparisonGridName) remappedRefClimatology = xr.open_dataset(remappedFileName) elif self.controlConfig is not None: climatologyName = self.remapMpasClimatologySubtask.climatologyName remappedFileName = \ get_remapped_mpas_climatology_file_name( self.controlConfig, season=season, componentName=self.componentName, climatologyName=climatologyName, comparisonGridName=comparisonGridName, op=self.remapMpasClimatologySubtask.op) remappedRefClimatology = xr.open_dataset(remappedFileName) controlStartYear = self.controlConfig.getint( 'climatology', 'startYear') controlEndYear = self.controlConfig.getint('climatology', 'endYear') if controlStartYear != self.startYear or \ controlEndYear != self.endYear: self.refTitleLabel = '{}\n(years {:04d}-{:04d})'.format( self.refTitleLabel, controlStartYear, controlEndYear) else: remappedRefClimatology = None if remappedRefClimatology is None: refOutput = None difference = None else: refOutput = remappedRefClimatology[self.refFieldName].values # mask nans refOutput = np.ma.masked_array(refOutput, np.isnan(refOutput)) difference = modelOutput - refOutput # mask with maskValue only after taking the diff if self.maskValue is not None: mask = np.logical_or(refOutput.mask, refOutput == self.maskValue) refOutput = np.ma.masked_array(refOutput, mask) difference = np.ma.masked_array(difference, mask) # mask with maskValue only after taking the diff if self.maskValue is not None: mask = np.logical_or(modelOutput.mask, modelOutput == self.maskValue) modelOutput = np.ma.masked_array(modelOutput, mask) # for log plots, make sure the data is all positive to avoid masking if config.has_option(sectionName, 'normTypeResult'): normType = config.get(sectionName, 'normTypeResult') normArgs = config.getExpression(sectionName, 'normArgsResult') if normType == 'log': epsilon = 1e-2 * normArgs['vmin'] modelOutput = np.maximum(modelOutput, epsilon) if refOutput is not None: refOutput = np.maximum(refOutput, epsilon) startYear = self.startYear endYear = self.endYear filePrefix = self.filePrefix title = '{} ({}, years {:04d}-{:04d})'.format(self.fieldNameInTitle, season, startYear, endYear) fileout = '{}/{}.png'.format(self.plotsDirectory, filePrefix) plot_polar_comparison(config, lon, lat, modelOutput, refOutput, difference, sectionName, title=title, fileout=fileout, plotProjection=plotProjection, latmin=minimumLatitude, lon0=referenceLongitude, modelTitle=mainRunName, refTitle=self.refTitleLabel, diffTitle=self.diffTitleLabel, cbarlabel=self.unitsLabel, vertical=vertical) write_image_xml(config, filePrefix, componentName='Sea Ice', componentSubdirectory='sea_ice', galleryGroup=self.galleryGroup, groupSubtitle=self.groupSubtitle, groupLink=self.groupLink, gallery=self.galleryName, thumbnailDescription=season, imageDescription=self.imageDescription, imageCaption=self.imageCaption)
def run_task(self): # {{{ """ Plots time-series output of properties in an ocean region. """ # Authors # ------- # Xylar Asay-Davis self.logger.info("\nPlotting TS diagram for {}" "...".format(self.regionName)) register_custom_colormaps() config = self.config sectionName = self.sectionName startYear = self.mpasClimatologyTask.startYear endYear = self.mpasClimatologyTask.endYear regionMaskSuffix = config.getExpression(sectionName, 'regionMaskSuffix') regionMaskFile = get_region_mask(config, '{}.geojson'.format(regionMaskSuffix)) fcAll = read_feature_collection(regionMaskFile) fc = FeatureCollection() for feature in fcAll.features: if feature['properties']['name'] == self.regionName: fc.add_feature(feature) break self.logger.info(' Make plots...') groupLink = 'tsDiag' + self.regionGroup[0].lower() + \ self.regionGroup[1:].replace(' ', '') nSubplots = 1 + len(self.obsDicts) if self.controlConfig is not None: nSubplots += 1 if nSubplots == 4: nCols = 2 nRows = 2 else: nCols = min(nSubplots, 3) nRows = (nSubplots - 1) // 3 + 1 axisIndices = numpy.reshape(numpy.arange(nRows * nCols), (nRows, nCols))[::-1, :].ravel() titleFontSize = config.get('plot', 'titleFontSize') axis_font = {'size': config.get('plot', 'axisFontSize')} title_font = { 'size': titleFontSize, 'color': config.get('plot', 'titleFontColor'), 'weight': config.get('plot', 'titleFontWeight') } width = 3 + 4.5 * nCols height = 2 + 4 * nRows # noinspection PyTypeChecker fig, axarray = plt.subplots(nrows=nRows, ncols=nCols, sharey=True, figsize=(width, height)) if nSubplots == 1: axarray = numpy.array(axarray) if nRows == 1: axarray = axarray.reshape((nRows, nCols)) T, S, zMid, volume, zmin, zmax = self._get_mpas_t_s(self.config) mainRunName = config.get('runs', 'mainRunName') plotFields = [{ 'S': S, 'T': T, 'z': zMid, 'vol': volume, 'title': mainRunName }] if self.controlConfig is not None: T, S, zMid, volume, _, _ = self._get_mpas_t_s(self.controlConfig) controlRunName = self.controlConfig.get('runs', 'mainRunName') plotFields.append({ 'S': S, 'T': T, 'z': zMid, 'vol': volume, 'title': 'Control: {}'.format(controlRunName) }) for obsName in self.obsDicts: obsT, obsS, obsZ, obsVol = self._get_obs_t_s( self.obsDicts[obsName]) plotFields.append({ 'S': obsS, 'T': obsT, 'z': obsZ, 'vol': obsVol, 'title': obsName }) Tbins = config.getExpression(sectionName, 'Tbins', usenumpyfunc=True) Sbins = config.getExpression(sectionName, 'Sbins', usenumpyfunc=True) normType = config.get(sectionName, 'normType') PT, SP = numpy.meshgrid(Tbins, Sbins) SA = gsw.SA_from_SP(SP, p=0., lon=0., lat=-75.) CT = gsw.CT_from_t(SA, PT, p=0.) neutralDensity = sigma0(SA, CT) rhoInterval = config.getfloat(sectionName, 'rhoInterval') contours = numpy.arange(24., 29. + rhoInterval, rhoInterval) diagramType = config.get(sectionName, 'diagramType') if diagramType not in ['volumetric', 'scatter']: raise ValueError('Unexpected diagramType {}'.format(diagramType)) lastPanel = None volMinMpas = None volMaxMpas = None for index in range(len(axisIndices)): panelIndex = axisIndices[index] row = nRows - 1 - index // nCols col = numpy.mod(index, nCols) if panelIndex >= nSubplots: plt.delaxes(axarray[row, col]) continue plt.sca(axarray[row, col]) T = plotFields[index]['T'] S = plotFields[index]['S'] z = plotFields[index]['z'] volume = plotFields[index]['vol'] title = plotFields[index]['title'] CS = plt.contour(SP, PT, neutralDensity, contours, linewidths=1., colors='k', zorder=2) plt.clabel(CS, fontsize=12, inline=1, fmt='%4.2f') if diagramType == 'volumetric': lastPanel, volMin, volMax = \ self._plot_volumetric_panel(T, S, volume) if index == 0: volMinMpas = volMin volMaxMpas = volMax if normType == 'linear': norm = colors.Normalize(vmin=0., vmax=volMaxMpas) elif normType == 'log': if volMinMpas is None or volMaxMpas is None: norm = None else: norm = colors.LogNorm(vmin=volMinMpas, vmax=volMaxMpas) else: raise ValueError( 'Unsupported normType {}'.format(normType)) if norm is not None: lastPanel.set_norm(norm) else: lastPanel = self._plot_scatter_panel(T, S, z, zmin, zmax) CTFreezing = freezing.CT_freezing(Sbins, 0, 1) PTFreezing = gsw.t_from_CT(gsw.SA_from_SP(Sbins, p=0., lon=0., lat=-75.), CTFreezing, p=0.) plt.plot(Sbins, PTFreezing, linestyle='--', linewidth=1., color='k') plt.ylim([Tbins[0], Tbins[-1]]) plt.xlim([Sbins[0], Sbins[-1]]) plt.xlabel('Salinity (PSU)', **axis_font) if col == 0: plt.ylabel(r'Potential temperature ($^\circ$C)', **axis_font) plt.title(title) # do this before the inset because otherwise it moves the inset # and cartopy doesn't play too well with tight_layout anyway plt.tight_layout() fig.subplots_adjust(right=0.91) if nRows == 1: fig.subplots_adjust(top=0.85) else: fig.subplots_adjust(top=0.88) suptitle = 'T-S diagram for {} ({}, {:04d}-{:04d})\n' \ ' {} m < z < {} m'.format(self.regionName, self.season, startYear, endYear, zmin, zmax) fig.text(0.5, 0.9, suptitle, horizontalalignment='center', **title_font) inset = add_inset(fig, fc, width=1.5, height=1.5) # move the color bar down a little ot avoid the inset pos0 = inset.get_position() pos1 = axarray[-1, -1].get_position() pad = 0.04 top = pos0.y0 - pad height = top - pos1.y0 cbar_ax = fig.add_axes([0.92, pos1.y0, 0.02, height]) cbar = fig.colorbar(lastPanel, cax=cbar_ax) if diagramType == 'volumetric': cbar.ax.get_yaxis().labelpad = 15 cbar.ax.set_ylabel(r'volume (m$^3$)', rotation=270) else: cbar.ax.set_ylabel('depth (m)', rotation=270) outFileName = '{}/TS_diagram_{}_{}.png'.format(self.plotsDirectory, self.prefix, self.season) savefig(outFileName, tight=False) caption = 'Regional mean of {}'.format(suptitle) write_image_xml(config=config, filePrefix='TS_diagram_{}_{}'.format( self.prefix, self.season), componentName='Ocean', componentSubdirectory='ocean', galleryGroup='T-S Diagrams', groupLink=groupLink, gallery=self.regionGroup, thumbnailDescription=self.regionName, imageDescription=caption, imageCaption=caption)
def run_task(self): # {{{ """ Make the Hovmoller plot from the time series. """ # Authors # ------- # Xylar Asay-Davis, Milena Veneziani, Greg Streletz self.logger.info("\nPlotting {} time series vs. depth...".format( self.fieldNameInTitle)) config = self.config mainRunName = config.get('runs', 'mainRunName') self.logger.info(' Load ocean data...') ds = xr.open_dataset(self.inFileName) if 'regionNames' in ds.coords: allRegionNames = decode_strings(ds.regionNames) regionIndex = allRegionNames.index(self.regionName) regionNameInTitle = self.regionName.replace('_', ' ') regionDim = ds.regionNames.dims[0] else: plotTitles = config.getExpression('regions', 'plotTitles') allRegionNames = config.getExpression('regions', 'regions') regionIndex = allRegionNames.index(self.regionName) regionNameInTitle = plotTitles[regionIndex] regionDim = 'nOceanRegionsTmp' ds = ds.isel(**{regionDim: regionIndex}) # Note: restart file, not a mesh file because we need refBottomDepth, # not in a mesh file try: restartFile = self.runStreams.readpath('restart')[0] except ValueError: raise IOError('No MPAS-O restart file found: need at least one ' 'restart file for plotting time series vs. depth') # Define/read in general variables self.logger.info(' Read in depth...') with xr.open_dataset(restartFile) as dsRestart: # reference depth [m] depths = dsRestart.refBottomDepth.values z = np.zeros(depths.shape) z[0] = -0.5 * depths[0] z[1:] = -0.5 * (depths[0:-1] + depths[1:]) Time = ds.Time.values field = ds[self.mpasFieldName].values.transpose() xLabel = 'Time (years)' yLabel = 'Depth (m)' title = '{}, {}'.format(self.fieldNameInTitle, regionNameInTitle) outFileName = '{}/{}.png'.format(self.plotsDirectory, self.filePrefix) if config.has_option(self.sectionName, 'firstYearXTicks'): firstYearXTicks = config.getint(self.sectionName, 'firstYearXTicks') else: firstYearXTicks = None if config.has_option(self.sectionName, 'yearStrideXTicks'): yearStrideXTicks = config.getint(self.sectionName, 'yearStrideXTicks') else: yearStrideXTicks = None movingAverageMonths = config.getWithDefault(self.sectionName, 'movingAverageMonths', 1) if config.has_option(self.sectionName, 'yLim'): yLim = config.getExpression(self.sectionName, 'yLim') else: yLim = None if self.controlConfig is None: refField = None diff = None refTitle = None diffTitle = None else: controlConfig = self.controlConfig dsRef = xr.open_dataset(self.controlFileName) if 'regionNames' in dsRef.coords: allRegionNames = decode_strings(dsRef.regionNames) regionIndex = allRegionNames.index(self.regionName) regionNameInTitle = self.regionName.replace('_', ' ') regionDim = dsRef.regionNames.dims[0] else: plotTitles = controlConfig.getExpression( 'regions', 'plotTitles') allRegionNames = controlConfig.getExpression( 'regions', 'regions') regionIndex = allRegionNames.index(self.regionName) regionNameInTitle = plotTitles[regionIndex] regionDim = 'nOceanRegionsTmp' dsRef = dsRef.isel(**{regionDim: regionIndex}) refField = dsRef[self.mpasFieldName].values.transpose() assert (field.shape == refField.shape) diff = field - refField refTitle = self.controlConfig.get('runs', 'mainRunName') diffTitle = 'Main - Control' fig, _, suptitle = plot_vertical_section_comparison( config, Time, z, field, refField, diff, self.sectionName, colorbarLabel=self.unitsLabel, title=title, modelTitle=mainRunName, refTitle=refTitle, diffTitle=diffTitle, xlabel=xLabel, ylabel=yLabel, lineWidth=1, xArrayIsTime=True, movingAveragePoints=movingAverageMonths, calendar=self.calendar, firstYearXTicks=firstYearXTicks, yearStrideXTicks=yearStrideXTicks, yLim=yLim, invertYAxis=False) if self.regionMaskFile is not None: # shift the super-title a little to the left to make room for the # inset pos = suptitle.get_position() suptitle.set_position((pos[0] - 0.05, pos[1])) fcAll = read_feature_collection(self.regionMaskFile) fc = FeatureCollection() for feature in fcAll.features: if feature['properties']['name'] == self.regionName: fc.add_feature(feature) break add_inset(fig, fc, width=1.0, height=1.0, xbuffer=0.1, ybuffer=0.1) savefig(outFileName, tight=False) else: savefig(outFileName) write_image_xml(config=config, filePrefix=self.filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup=self.galleryGroup, groupSubtitle=self.groupSubtitle, groupLink=self.groupLink, gallery=self.galleryName, thumbnailDescription='{} {}'.format( regionNameInTitle, self.thumbnailSuffix), imageDescription=self.imageCaption, imageCaption=self.imageCaption)
def _plot_transect(self, remappedModelClimatology, remappedRefClimatology): # {{{ """ plotting the transect """ season = self.season config = self.config configSectionName = self.configSectionName mainRunName = config.get('runs', 'mainRunName') # broadcast x and z to have the same dimensions x, z = xr.broadcast(remappedModelClimatology.x, remappedModelClimatology.z) # convert x and z to numpy arrays, make a copy because they are # sometimes read-only (not sure why) x = x.values.copy().transpose() z = z.values.copy().transpose() # z is masked out with NaNs in some locations (where there is land) but # this makes pcolormesh unhappy so we'll zero out those locations z[numpy.isnan(z)] = 0. modelOutput = nans_to_numpy_mask( remappedModelClimatology[self.mpasFieldName].values) modelOutput = modelOutput.transpose() if remappedRefClimatology is None: refOutput = None bias = None else: refOutput = remappedRefClimatology[self.refFieldName] dims = refOutput.dims refOutput = nans_to_numpy_mask(refOutput.values) if dims[1] != 'nPoints': assert (dims[0] == 'nPoints') refOutput = refOutput.transpose() bias = modelOutput - refOutput filePrefix = self.filePrefix outFileName = '{}/{}.png'.format(self.plotsDirectory, filePrefix) title = '{}\n({}, years {:04d}-{:04d})'.format(self.fieldNameInTitle, season, self.startYear, self.endYear) # construct a three-panel comparison plot for the transect xLabel = 'Distance [km]' yLabel = 'Depth [m]' plot_vertical_section_comparison(config, x, z, modelOutput, refOutput, bias, outFileName, configSectionName, cbarLabel=self.unitsLabel, xlabel=xLabel, ylabel=yLabel, title=title, modelTitle='{}'.format(mainRunName), refTitle=self.refTitleLabel, diffTitle=self.diffTitleLabel, invertYAxis=False, backgroundColor='#918167') caption = '{} {}'.format(season, self.imageCaption) write_image_xml(config, filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup=self.galleryGroup, groupSubtitle=self.groupSubtitle, groupLink=self.groupLink, gallery=self.galleryName, thumbnailDescription=self.thumbnailDescription, imageDescription=caption, imageCaption=caption)
def run_task(self): # {{{ """ Performs analysis of the time-series output of sea-surface temperature (SST). """ # Authors # ------- # Xylar Asay-Davis, Milena Veneziani self.logger.info("\nPlotting SST time series...") self.logger.info(' Load SST data...') config = self.config calendar = self.calendar mainRunName = config.get('runs', 'mainRunName') preprocessedReferenceRunName = \ config.get('runs', 'preprocessedReferenceRunName') preprocessedInputDirectory = config.get('oceanPreprocessedReference', 'baseDirectory') movingAveragePoints = config.getint('timeSeriesSST', 'movingAveragePoints') regions = config.getExpression('regions', 'regions') plotTitles = config.getExpression('regions', 'plotTitles') regionsToPlot = config.getExpression('timeSeriesSST', 'regions') regionIndicesToPlot = [ regions.index(region) for region in regionsToPlot ] outputDirectory = build_config_full_path(config, 'output', 'timeseriesSubdirectory') make_directories(outputDirectory) dsSST = open_mpas_dataset(fileName=self.inputFile, calendar=calendar, variableList=self.variableList, startDate=self.startDate, endDate=self.endDate) yearStart = days_to_datetime(dsSST.Time.min(), calendar=calendar).year yearEnd = days_to_datetime(dsSST.Time.max(), calendar=calendar).year timeStart = date_to_days(year=yearStart, month=1, day=1, calendar=calendar) timeEnd = date_to_days(year=yearEnd, month=12, day=31, calendar=calendar) if self.refConfig is not None: baseDirectory = build_config_full_path(self.refConfig, 'output', 'timeSeriesSubdirectory') refFileName = '{}/{}.nc'.format( baseDirectory, self.mpasTimeSeriesTask.fullTaskName) refStartYear = self.refConfig.getint('timeSeries', 'startYear') refEndYear = self.refConfig.getint('timeSeries', 'endYear') refStartDate = '{:04d}-01-01_00:00:00'.format(refStartYear) refEndDate = '{:04d}-12-31_23:59:59'.format(refEndYear) dsRefSST = open_mpas_dataset(fileName=refFileName, calendar=calendar, variableList=self.variableList, startDate=refStartDate, endDate=refEndDate) else: dsRefSST = None if preprocessedReferenceRunName != 'None': self.logger.info(' Load in SST for a preprocesses reference ' 'run...') inFilesPreprocessed = '{}/SST.{}.year*.nc'.format( preprocessedInputDirectory, preprocessedReferenceRunName) outFolder = '{}/preprocessed'.format(outputDirectory) make_directories(outFolder) outFileName = '{}/sst.nc'.format(outFolder) combine_time_series_with_ncrcat(inFilesPreprocessed, outFileName, logger=self.logger) dsPreprocessed = open_mpas_dataset(fileName=outFileName, calendar=calendar, timeVariableNames='xtime') yearEndPreprocessed = days_to_datetime(dsPreprocessed.Time.max(), calendar=calendar).year if yearStart <= yearEndPreprocessed: dsPreprocessedTimeSlice = \ dsPreprocessed.sel(Time=slice(timeStart, timeEnd)) else: self.logger.warning('Preprocessed time series ends before the ' 'timeSeries startYear and will not be ' 'plotted.') preprocessedReferenceRunName = 'None' self.logger.info(' Make plots...') for regionIndex in regionIndicesToPlot: region = regions[regionIndex] title = '{} SST'.format(plotTitles[regionIndex]) xLabel = 'Time [years]' yLabel = '[$\degree$C]' varName = self.variableList[0] SST = dsSST[varName].isel(nOceanRegions=regionIndex) filePrefix = self.filePrefixes[region] figureName = '{}/{}.png'.format(self.plotsDirectory, filePrefix) lineColors = ['k'] lineWidths = [3] fields = [SST] legendText = [mainRunName] if dsRefSST is not None: refSST = dsRefSST[varName].isel(nOceanRegions=regionIndex) fields.append(refSST) lineColors.append('r') lineWidths.append(1.5) refRunName = self.refConfig.get('runs', 'mainRunName') legendText.append(refRunName) if preprocessedReferenceRunName != 'None': SST_v0 = dsPreprocessedTimeSlice.SST fields.append(SST_v0) lineColors.append('purple') lineWidths.append(1.5) legendText.append(preprocessedReferenceRunName) if config.has_option(self.taskName, 'firstYearXTicks'): firstYearXTicks = config.getint(self.taskName, 'firstYearXTicks') else: firstYearXTicks = None if config.has_option(self.taskName, 'yearStrideXTicks'): yearStrideXTicks = config.getint(self.taskName, 'yearStrideXTicks') else: yearStrideXTicks = None timeseries_analysis_plot(config, fields, movingAveragePoints, title, xLabel, yLabel, figureName, calendar=calendar, lineColors=lineColors, lineWidths=lineWidths, legendText=legendText, firstYearXTicks=firstYearXTicks, yearStrideXTicks=yearStrideXTicks) caption = 'Running Mean of {} Sea Surface Temperature'.format( region) write_image_xml(config=config, filePrefix=filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup='Time Series', groupLink='timeseries', thumbnailDescription='{} SST'.format(region), imageDescription=caption, imageCaption=caption)