Esempio n. 1
0
    def _setup_remappers(self, fileName):  # {{{
        """
        Set up the remappers for remapping from observations to the comparison
        grids.

        Parameters
        ----------
        fileName : str
            The name of the observation file used to determine the source grid
        """
        # Authors
        # -------
        # Xylar Asay-Davis

        config = self.config

        sectionName = '{}Observations'.format(self.componentName)

        obsDescriptor = self.get_observation_descriptor(fileName)

        outFilePrefix = self.outFilePrefix
        self.remappers = {}
        for comparisonGridName in self.comparisonGridNames:

            comparisonDescriptor = get_comparison_descriptor(
                config, comparisonGridName=comparisonGridName)

            self.remappers[comparisonGridName] = get_remapper(
                config=config,
                sourceDescriptor=obsDescriptor,
                comparisonDescriptor=comparisonDescriptor,
                mappingFilePrefix='map_obs_{}'.format(outFilePrefix),
                method=config.get(sectionName, 'interpolationMethod'),
                logger=self.logger)
def compute_vel_mag(prefix, inGridName, inDir):
    config = MpasAnalysisConfigParser()
    config.read('mpas_analysis/config.default')

    outDescriptor = get_comparison_descriptor(config, 'antarctic')
    outGridName = outDescriptor.meshName
    description = 'Monthly velocity magnitude climatologies from ' \
                  '2005-2010 average of the Southern Ocean State ' \
                  'Estimate (SOSE)'
    botDescription = 'Monthly velocity magnitude climatologies at sea ' \
                     'floor from 2005-2010 average from SOSE'

    for gridName in [outGridName]:
        outFileName = '{}_vel_mag_{}.nc'.format(prefix, gridName)
        uFileName = '{}_zonal_vel_{}.nc'.format(prefix, gridName)
        vFileName = '{}_merid_vel_{}.nc'.format(prefix, gridName)
        if not os.path.exists(outFileName):
            with xarray.open_dataset(uFileName) as dsU:
                with xarray.open_dataset(vFileName) as dsV:
                    dsVelMag = dsU.drop(['zonalVel', 'botZonalVel'])
                    dsVelMag['velMag'] = xarray.ufuncs.sqrt(dsU.zonalVel**2 +
                                                            dsV.meridVel**2)
                    dsVelMag.velMag.attrs['units'] = 'm s$^{-1}$'
                    dsVelMag.velMag.attrs['description'] = description

                    dsVelMag['botVelMag'] = xarray.ufuncs.sqrt(
                        dsU.botZonalVel**2 + dsV.botMeridVel**2)
                    dsVelMag.botVelMag.attrs['units'] = 'm s$^{-1}$'
                    dsVelMag.botVelMag.attrs['description'] = botDescription

                    write_netcdf(dsVelMag, outFileName)
    def _setup_remappers(self):  # {{{
        """
        Set up the remappers for remapping from the MPAS to the comparison
        grids.
        """
        # Authors
        # -------
        # Xylar Asay-Davis

        config = self.config

        # make reamppers
        mappingFilePrefix = 'map'
        self.remappers = {}
        for comparisonGridName in self.comparisonGridNames:

            comparisonDescriptor = get_comparison_descriptor(
                config, comparisonGridName)
            self.comparisonGridName = comparisonDescriptor.meshName
            mpasDescriptor = MpasMeshDescriptor(self.restartFileName,
                                                meshName=config.get(
                                                    'input', 'mpasMeshName'))
            self.mpasMeshName = mpasDescriptor.meshName

            self.remappers[comparisonGridName] = get_remapper(
                config=config,
                sourceDescriptor=mpasDescriptor,
                comparisonDescriptor=comparisonDescriptor,
                mappingFilePrefix=mappingFilePrefix,
                method=config.get('climatology', 'mpasInterpolationMethod'),
                logger=self.logger)
def compute_pot_density(prefix, inGridName, inDir):
    config = MpasAnalysisConfigParser()
    config.read('mpas_analysis/config.default')

    outDescriptor = get_comparison_descriptor(config, 'antarctic')
    outGridName = outDescriptor.meshName
    description = 'Monthly potential density climatologies from ' \
                  '2005-2010 average of the Southern Ocean State ' \
                  'Estimate (SOSE)'
    botDescription = 'Monthly potential density climatologies at sea ' \
                     'floor from 2005-2010 average from SOSE'

    for gridName in [inGridName, outGridName]:
        outFileName = '{}_pot_den_{}.nc'.format(prefix, gridName)
        TFileName = '{}_pot_temp_{}.nc'.format(prefix, gridName)
        SFileName = '{}_salinity_{}.nc'.format(prefix, gridName)
        if not os.path.exists(outFileName):
            with xarray.open_dataset(TFileName) as dsT:
                with xarray.open_dataset(SFileName) as dsS:
                    dsPotDensity = dsT.drop(['theta', 'botTheta'])

                    lat, lon, z = xarray.broadcast(dsS.lat, dsS.lon, dsS.z)
                    pressure = gsw.p_from_z(z.values, lat.values)
                    SA = gsw.SA_from_SP(dsS.salinity.values, pressure,
                                        lon.values, lat.values)
                    CT = gsw.CT_from_pt(SA, dsT.theta.values)
                    dsPotDensity['potentialDensity'] = (dsS.salinity.dims,
                                                        gsw.rho(SA, CT, 0.))
                    dsPotDensity.potentialDensity.attrs['units'] = \
                        'kg m$^{-3}$'
                    dsPotDensity.potentialDensity.attrs['description'] = \
                        description

                    lat, lon, z = xarray.broadcast(dsS.lat, dsS.lon, dsS.zBot)
                    pressure = gsw.p_from_z(z.values, lat.values)
                    SA = gsw.SA_from_SP(dsS.botSalinity.values, pressure,
                                        lon.values, lat.values)
                    CT = gsw.CT_from_pt(SA, dsT.botTheta.values)
                    dsPotDensity['botPotentialDensity'] = \
                        (dsS.botSalinity.dims, gsw.rho(SA, CT, 0.))
                    dsPotDensity.botPotentialDensity.attrs['units'] = \
                        'kg m$^{-3}$'
                    dsPotDensity.botPotentialDensity.attrs['description'] = \
                        botDescription

                    write_netcdf(dsPotDensity, outFileName)
def remap_v(prefix, inGridName, inGridFileName, inDir, inVPrefix):
    cacheVFileName = '{}_merid_vel_{}.nc'.format(prefix, inGridName)

    config = MpasAnalysisConfigParser()
    config.read('mpas_analysis/config.default')

    matGrid = loadmat(inGridFileName)
    # lat/lon is a tensor grid so we can use 1-D arrays
    lon = matGrid['XC'][:, 0]
    lat = matGrid['YG'][0, :]
    z = matGrid['RC'][:, 0]
    cellFraction = matGrid['hFacS']

    botIndices = get_bottom_indices(cellFraction)

    with sose_v_to_nc('{}/{}'.format(inDir, inVPrefix),
                      cacheVFileName, lon, lat, z, cellFraction, botIndices) \
            as dsV:
        inDescriptor = LatLonGridDescriptor()

        inDescriptor = LatLonGridDescriptor.read(cacheVFileName,
                                                 latVarName='lat',
                                                 lonVarName='lon')

        outDescriptor = get_comparison_descriptor(config, 'antarctic')
        outGridName = outDescriptor.meshName

        outVFileName = '{}_merid_vel_{}.nc'.format(prefix, outGridName)

        mappingFileName = '{}/map_V_{}_to_{}.nc'.format(
            inDir, inGridName, outGridName)

        remapper = Remapper(inDescriptor, outDescriptor, mappingFileName)

        remapper.build_mapping_file(method='bilinear')

        if not os.path.exists(outVFileName):
            print('Remapping meridional velocity...')
            with remapper.remap(dsV, renormalizationThreshold=0.01) \
                    as remappedV:
                print('Done.')
                remappedV.attrs['history'] = ' '.join(sys.argv)
                write_netcdf(remappedV, outVFileName)
def remap(inDir, outDir):

    inGridName = 'SouthernOcean_0.25x0.125degree'
    inFileName = '{}/Schmidtko_et_al_2014_bottom_PT_S_PD_{}.nc'.format(
        inDir, inGridName)

    config = MpasAnalysisConfigParser()
    config.read('mpas_analysis/config.default')

    inDescriptor = LatLonGridDescriptor()

    inDescriptor = LatLonGridDescriptor.read(inFileName,
                                             latVarName='lat',
                                             lonVarName='lon')

    outDescriptor = get_comparison_descriptor(config, 'antarctic')
    outGridName = outDescriptor.meshName

    outFileName = '{}/Schmidtko_et_al_2014_bottom_PT_S_PD_{}.nc'.format(
        outDir, outGridName)

    mappingFileName = '{}/map_{}_to_{}.nc'.format(inDir, inGridName,
                                                  outGridName)

    remapper = Remapper(inDescriptor, outDescriptor, mappingFileName)

    remapper.build_mapping_file(method='bilinear')

    if not os.path.exists(outFileName):
        print('Remapping...')
        with xarray.open_dataset(inFileName) as dsIn:
            with remapper.remap(dsIn, renormalizationThreshold=0.01) \
                    as remappedMLD:
                print('Done.')
                remappedMLD.attrs['history'] = ' '.join(sys.argv)
                write_netcdf(remappedMLD, outFileName)
def remap_pt_s(prefix, inGridName, inGridFileName, inDir, inTPrefix, inSPrefix,
               inGammaNPrefix):
    cacheTFileName = '{}_pot_temp_{}.nc'.format(prefix, inGridName)
    cacheSFileName = '{}_salinity_{}.nc'.format(prefix, inGridName)
    cacheGammaNFileName = '{}_neut_den_{}.nc'.format(prefix, inGridName)

    config = MpasAnalysisConfigParser()
    config.read('mpas_analysis/config.default')

    matGrid = loadmat(inGridFileName)
    # lat/lon is a tensor grid so we can use 1-D arrays
    lon = matGrid['XC'][:, 0]
    lat = matGrid['YC'][0, :]
    z = matGrid['RC'][:, 0]
    cellFraction = matGrid['hFacC']

    botIndices = get_bottom_indices(cellFraction)

    with sose_pt_to_nc('{}/{}'.format(inDir, inTPrefix),
                       cacheTFileName, lon, lat, z, cellFraction, botIndices) \
            as dsT:
        inDescriptor = LatLonGridDescriptor()

        inDescriptor = LatLonGridDescriptor.read(cacheTFileName,
                                                 latVarName='lat',
                                                 lonVarName='lon')

        outDescriptor = get_comparison_descriptor(config, 'antarctic')
        outGridName = outDescriptor.meshName

        outTFileName = '{}_pot_temp_{}.nc'.format(prefix, outGridName)
        outSFileName = '{}_salinity_{}.nc'.format(prefix, outGridName)
        outGammaNFileName = '{}_neut_den_{}.nc'.format(prefix, outGridName)

        mappingFileName = '{}/map_C_{}_to_{}.nc'.format(
            inDir, inGridName, outGridName)

        remapper = Remapper(inDescriptor, outDescriptor, mappingFileName)

        remapper.build_mapping_file(method='bilinear')

        if not os.path.exists(outTFileName):
            dsT.reset_coords(names='zBot', inplace=True)
            print('Remapping potential temperature...')
            with remapper.remap(dsT, renormalizationThreshold=0.01) \
                    as remappedT:
                print('Done.')
                remappedT.attrs['history'] = ' '.join(sys.argv)
                remappedT.set_coords(names='zBot', inplace=True)
                write_netcdf(remappedT, outTFileName)

    with sose_s_to_nc('{}/{}'.format(inDir, inSPrefix),
                      cacheSFileName, lon, lat, z, cellFraction, botIndices) \
            as dsS:
        if not os.path.exists(outSFileName):
            dsS.reset_coords(names='zBot', inplace=True)
            print('Remapping salinity...')
            with remapper.remap(dsS, renormalizationThreshold=0.01) \
                    as remappedS:
                print('Done.')
                remappedS.attrs['history'] = ' '.join(sys.argv)
                remappedS.set_coords(names='zBot', inplace=True)
                write_netcdf(remappedS, outSFileName)

    with sose_gammaN_to_nc('{}/{}'.format(inDir, inGammaNPrefix),
                           cacheGammaNFileName, lon, lat, z, cellFraction,
                           botIndices) \
            as dsGammaN:
        if not os.path.exists(outGammaNFileName):
            dsGammaN.reset_coords(names='zBot', inplace=True)
            print('Remapping neutral density...')
            with remapper.remap(dsGammaN, renormalizationThreshold=0.01) \
                    as remappedGammaN:
                print('Done.')
                remappedGammaN.attrs['history'] = ' '.join(sys.argv)
                remappedGammaN.set_coords(names='zBot', inplace=True)
                write_netcdf(remappedGammaN, outGammaNFileName)
    def __init__(self, mpasClimatologyTask, parentTask, climatologyName,
                 variableList, seasons, comparisonGridNames=None,
                 iselValues=None, subtaskName='remapMpasClimatology',
                 useNcremap=None):
        # {{{
        '''
        Construct the analysis task and adds it as a subtask of the
        ``parentTask``.

        Parameters
        ----------
        mpasClimatologyTask : ``MpasClimatologyTask``
            The task that produced the climatology to be remapped

        parentTask :  ``AnalysisTask``
            The parent task, used to get the ``taskName``, ``config`` and
            ``componentName``

        climatologyName : str
            A name that describes the climatology (e.g. a short version of
            the important field(s) in the climatology) used to name the
            subdirectories for each stage of the climatology

        variableList : list of str
            A list of variable names in ``timeSeriesStatsMonthly`` to be
            included in the climatologies

        seasons : list of str
            A list of seasons (keys in ``shared.constants.monthDictionary``)
            to be computed or ['none'] (not ``None``) if only monthly
            climatologies are needed.

        comparisonGridNames : list of {'latlon', 'antarctic'}, optional
            The name(s) of the comparison grid to use for remapping.  If none
            is supplied, `add_comparison_descriptor()` must be called to add
            one or more comparison grids.

        iselValues : dict, optional
            A dictionary of dimensions and indices (or ``None``) used to
            extract a slice of the MPAS field(s).

        subtaskName : str, optional
            The name of the subtask

        useNcremap : bool, optional
            Whether to use ncremap to do the remapping (the other option being
            an internal python code that handles more grid types and extra
            dimensions).  This defaults to the config option ``useNcremap``
            if it is not explicitly given.  If a comparison grid other than
            ``latlon`` is given, ncremap is not supported so this flag is set
            to ``False``.
        '''
        # Authors
        # -------
        # Xylar Asay-Davis

        tags = ['climatology']

        # call the constructor from the base class (AnalysisTask)
        super(RemapMpasClimatologySubtask, self).__init__(
            config=mpasClimatologyTask.config,
            taskName=parentTask.taskName,
            subtaskName=subtaskName,
            componentName=parentTask.componentName,
            tags=tags)

        self.variableList = variableList
        self.seasons = seasons
        self.comparisonDescriptors = {}
        if comparisonGridNames is not None:
            for comparisonGridName in comparisonGridNames:
                comparisonDescriptor = get_comparison_descriptor(
                    self.config, comparisonGridName)
                self.comparisonDescriptors[comparisonGridName] = \
                    comparisonDescriptor

        self.iselValues = iselValues
        self.climatologyName = climatologyName
        self.mpasClimatologyTask = mpasClimatologyTask

        self.run_after(mpasClimatologyTask)

        parentTask.add_subtask(self)

        # this is a stopgap until MPAS implements the _FillValue attribute
        # correctly
        self._fillValue = -9.99999979021476795361e+33

        if useNcremap is None:
            self.useNcremap = self.config.getboolean('climatology',
                                                     'useNcremap')
        else:
            self.useNcremap = useNcremap
    def _setup_file_names(self):  # {{{
        """
        Create a dictionary of file names and directories for this climatology
        """
        # Authors
        # -------
        # Xylar Asay-Davis

        config = self.config
        climatologyBaseDirectory = build_config_full_path(
            config, 'output', 'mpasClimatologySubdirectory')

        mpasMeshName = config.get('input', 'mpasMeshName')

        comparisonFullMeshNames = {}
        for comparisonGridName in self.comparisonGridNames:
            comparisonDescriptor = get_comparison_descriptor(
                config, comparisonGridName)
            comparisonFullMeshNames[comparisonGridName] = \
                comparisonDescriptor.meshName

        keys = []
        for season in self.seasons:
            stage = 'masked'
            keys.append((season, stage))
            stage = 'remapped'
            for comparisonGridName in self.comparisonGridNames:
                keys.append((season, stage, comparisonGridName))

        self._outputDirs = {}
        self._outputFiles = {}

        for key in keys:
            season = key[0]
            stage = key[1]
            if stage == 'remapped':
                comparisonGridName = key[2]

            stageDirectory = '{}/{}'.format(climatologyBaseDirectory, stage)

            if stage == 'masked':
                directory = '{}/{}_{}'.format(stageDirectory,
                                              self.climatologyName,
                                              mpasMeshName)
            elif stage == 'remapped':
                directory = '{}/{}_{}_to_{}'.format(
                    stageDirectory, self.climatologyName, mpasMeshName,
                    comparisonFullMeshNames[comparisonGridName])

            make_directories(directory)

            monthValues = sorted(constants.monthDictionary[season])
            startMonth = monthValues[0]
            endMonth = monthValues[-1]

            suffix = '{:04d}{:02d}_{:04d}{:02d}_climo'.format(
                self.mpasClimatologyTask.startYear, startMonth,
                self.mpasClimatologyTask.endYear, endMonth)

            if season in constants.abrevMonthNames:
                season = '{:02d}'.format(monthValues[0])
            fileName = '{}/{}_{}_{}.nc'.format(
                directory, self.mpasClimatologyTask.ncclimoModel, season,
                suffix)

            self._outputDirs[key] = directory
            self._outputFiles[key] = fileName
Esempio n. 10
0
def get_remapped_mpas_climatology_file_name(config, season, componentName,
                                            climatologyName,
                                            comparisonGridName,
                                            op='avg'):  # {{{
    """
    Get the file name for a masked MPAS climatology

    Parameters
    ----------
    config :  ``MpasAnalysisConfigParser``
        Configuration options

    season : str
        One of the seasons in ``constants.monthDictionary``

    componentName : {'ocean', 'seaIce'}
        The MPAS component for which the climatology is being computed

    climatologyName : str
        The name of the climatology (typically the name of a field to mask
        and later remap)

    comparisonGridName : str
        The name of the comparison grid to use for remapping.  If it is one
        of the default comparison grid names ``{'latlon', 'antarctic',
        'arctic'}``, the full grid name is looked up via
        get_comparison_descriptor

    op : {'avg', 'min', 'max'}
         operator for monthly stats
    """
    # Authors
    # -------
    # Xylar Asay-Davis

    startYear = config.getint('climatology', 'startYear')
    endYear = config.getint('climatology', 'endYear')
    mpasMeshName = config.get('input', 'mpasMeshName')

    if componentName == 'ocean':
        ncclimoModel = 'mpaso'
    elif componentName == 'seaIce':
        ncclimoModel = 'mpascice'
    else:
        raise ValueError('component {} is not supported by ncclimo.\n'
                         'Check with Charlie Zender and Xylar Asay-Davis\n'
                         'about getting it added'.format(componentName))

    climatologyOpDirectory = get_climatology_op_directory(config, op)

    if comparisonGridName in ['latlon', 'antarctic', 'arctic']:
        comparisonDescriptor = get_comparison_descriptor(config,
                                                         comparisonGridName)
        comparisonFullMeshName = comparisonDescriptor.meshName
    else:
        comparisonFullMeshName = comparisonGridName

    stageDirectory = '{}/remapped'.format(climatologyOpDirectory)

    directory = '{}/{}_{}_to_{}'.format(stageDirectory, climatologyName,
                                        mpasMeshName, comparisonFullMeshName)

    make_directories(directory)

    monthValues = sorted(constants.monthDictionary[season])
    startMonth = monthValues[0]
    endMonth = monthValues[-1]

    suffix = '{:04d}{:02d}_{:04d}{:02d}_climo'.format(
        startYear, startMonth, endYear, endMonth)

    if season in constants.abrevMonthNames:
        season = '{:02d}'.format(monthValues[0])
    fileName = '{}/{}_{}_{}.nc'.format(
        directory, ncclimoModel, season, suffix)

    return fileName  # }}}
def main():
    parser = argparse.ArgumentParser(
        description=__doc__, formatter_class=argparse.RawTextHelpFormatter)
    parser.add_argument("-i",
                        "--inDir",
                        dest="inDir",
                        required=True,
                        help="Directory where intermediate files used in "
                        "processing should be downloaded")
    parser.add_argument("-o",
                        "--outDir",
                        dest="outDir",
                        required=True,
                        help="Directory where final preprocessed observation "
                        "are stored")
    args = parser.parse_args()

    date = '20190603'

    inGridName = 'SouthernOcean_0.167x0.167degree_{}'.format(date)

    inTPrefix = 'THETA_mnthlyBar.0000000100'
    inSPrefix = 'SALT_mnthlyBar.0000000100'
    inMLDPrefix = 'MLD_mnthlyBar.0000000100'
    inUPrefix = 'UVEL_mnthlyBar.0000000100'
    inVPrefix = 'VVEL_mnthlyBar.0000000100'
    inGammaNPrefix = 'GAMMA_mnthlyBar.0000000100'

    # size in km of the polar stereographic grid
    antarcticStereoWidth = 10000

    config = MpasAnalysisConfigParser()
    config.read('mpas_analysis/config.default')
    config.set('climatology', 'comparisonAntarcticStereoWidth',
               '{}'.format(antarcticStereoWidth))

    outDescriptor = get_comparison_descriptor(config, 'antarctic')
    outGridName = '{}_{}'.format(outDescriptor.meshName, date)

    inPrefixes = [
        inTPrefix, inSPrefix, inMLDPrefix, inUPrefix, inVPrefix, inGammaNPrefix
    ]

    inGridFileName = '{}/grid.mat'.format(args.inDir)

    try:
        os.makedirs(args.inDir)
    except OSError:
        pass

    try:
        os.makedirs(args.outDir)
    except OSError:
        pass

    # dowload the desired file
    download_files(['GRID_README.txt'],
                   urlBase='http://sose.ucsd.edu/DATA',
                   outDir=args.inDir)

    urlBase = 'http://sose.ucsd.edu/DATA/SO6_V2'

    fileList = ['grid.mat']
    for prefix in inPrefixes:
        fileList.append('{}.data.gz'.format(prefix))
        fileList.append('{}.meta'.format(prefix))

    download_files(fileList, urlBase, args.inDir)
    unzip_sose_data(inPrefixes, args.inDir)

    prefix = '{}/SOSE'.format(args.outDir)

    sose_volume_to_nc(prefix, inGridName, inGridFileName, args.inDir)

    prefix = '{}/SOSE_2005-2010_monthly'.format(args.outDir)

    remap_pt_s(prefix, inGridName, inGridFileName, args.inDir, inTPrefix,
               inSPrefix, inGammaNPrefix, outDescriptor, outGridName)

    remap_mld(prefix, inGridName, inGridFileName, args.inDir, inMLDPrefix,
              outDescriptor, outGridName)

    remap_u(prefix, inGridName, inGridFileName, args.inDir, inUPrefix,
            outDescriptor, outGridName)
    remap_v(prefix, inGridName, inGridFileName, args.inDir, inVPrefix,
            outDescriptor, outGridName)

    compute_vel_mag(prefix, inGridName, args.inDir, outGridName)

    compute_pot_density(prefix, inGridName, args.inDir, outGridName)
Esempio n. 12
0
    def _plot_polar(self, remappedModelClimatology,
                    remappedRefClimatology):  # {{{
        """ plotting an Arctic or 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

        comparisonDescriptor = get_comparison_descriptor(
            config, comparisonGridName)
        x = comparisonDescriptor.xCorner
        y = comparisonDescriptor.yCorner

        filePrefix = self.filePrefix
        outFileName = '{}/{}.png'.format(self.plotsDirectory, filePrefix)
        title = '{} ({}, years {:04d}-{:04d})'.format(self.fieldNameInTitle,
                                                      season, self.startYear,
                                                      self.endYear)

        if self.comparisonGridName == 'antarctic':
            hemisphere = 'south'
        else:
            # arctic
            hemisphere = 'north'

        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,
                                         hemisphere=hemisphere)

        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='{}_{}'.format(comparisonGridName,
                                                 self.groupLink),
                        gallery=self.galleryName,
                        thumbnailDescription=self.thumbnailDescription,
                        imageDescription=caption,
                        imageCaption=caption)