Example #1
0
    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)
Example #3
0
 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)  # }}}
Example #4
0
 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)  # }}}
Example #5
0
    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)
Example #7
0
    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)
Example #11
0
    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)
Example #15
0
    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)
Example #17
0
    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))
Example #18
0
    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)
Example #22
0
    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)