diff --git a/docs/api.rst b/docs/api.rst index be61769f3..59bf6a56a 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -57,6 +57,15 @@ Ocean tasks TimeSeriesOHC TimeSeriesSST +.. currentmodule:: mpas_analysis.ocean.plot_climatology_map_subtask + +.. autosummary:: + :toctree: generated/ + + PlotClimatologyMapSubtask + PlotClimatologyMapSubtask.set_plot_info + + Sea ice tasks ------------- @@ -69,10 +78,18 @@ Sea ice tasks ClimatologyMapSeaIceThick TimeSeriesSeaIce +.. currentmodule:: mpas_analysis.sea_ice.plot_climatology_map_subtask + +.. autosummary:: + :toctree: generated/ + + PlotClimatologyMapSubtask + PlotClimatologyMapSubtask.set_plot_info + Configuration ============== -.. currentmodule:: mpas_analysis.configuration.MpasAnalysisConfigParser +.. currentmodule:: mpas_analysis.configuration .. autosummary:: :toctree: generated/ @@ -122,7 +139,6 @@ Climatology get_comparison_descriptor get_antarctic_stereographic_projection get_remapper - get_observation_climatology_file_names compute_monthly_climatology compute_climatology add_years_months_days_in_month @@ -134,6 +150,11 @@ Climatology RemapMpasClimatologySubtask RemapMpasClimatologySubtask.get_file_name + RemapObservedClimatologySubtask + RemapObservedClimatologySubtask.get_observation_descriptor + RemapObservedClimatologySubtask.build_observational_dataset + RemapObservedClimatologySubtask.get_file_name + Time Series ----------- .. currentmodule:: mpas_analysis.shared.time_series diff --git a/mpas_analysis/config.default b/mpas_analysis/config.default index 05dfeb97d..a2e135ca5 100644 --- a/mpas_analysis/config.default +++ b/mpas_analysis/config.default @@ -158,10 +158,14 @@ startYear = 11 # the last year over which to average climatalogies endYear = 20 -# The comparison grid resolution in degrees +# The comparison lat/lon grid resolution in degrees comparisonLatResolution = 0.5 comparisonLonResolution = 0.5 +# The comparison Antarctic polar stereographic grid size and resolution in km +comparisonAntarcticStereoWidth = 6000. +comparisonAntarcticStereoResolution = 10. + # interpolation order for model and observation results. Likely values are # 'bilinear', 'neareststod' (nearest neighbor) or 'conserve' mpasInterpolationMethod = bilinear @@ -524,7 +528,7 @@ colorbarLevelsDifference = [-5, -3, -2, -1, 0, 1, 2, 3, 5] # Nov, Dec, JFM, AMJ, JAS, OND, ANN) seasons = ['JFM', 'JAS', 'ANN'] -# comparison grid(s) ('lonlat', 'anatarctic') on which to plot analysis +# comparison grid(s) ('latlon', 'antarctic') on which to plot analysis comparisonGrids = ['latlon'] [climatologyMapSSS] @@ -549,7 +553,7 @@ colorbarLevelsDifference = [-3, -2, -1, -0.5, 0, 0.5, 1, 2, 3] # Nov, Dec, JFM, AMJ, JAS, OND, ANN) seasons = ['JFM', 'JAS', 'ANN'] -# comparison grid(s) ('lonlat', 'anatarctic') on which to plot analysis +# comparison grid(s) ('latlon', 'antarctic') on which to plot analysis comparisonGrids = ['latlon'] [climatologyMapMLD] @@ -574,7 +578,7 @@ colorbarLevelsDifference = [-150, -80, -30, -10, 0, 10, 30, 80, 150] # Nov, Dec, JFM, AMJ, JAS, OND, ANN) seasons = ['JFM', 'JAS', 'ANN'] -# comparison grid(s) ('lonlat', 'anatarctic') on which to plot analysis +# comparison grid(s) ('latlon', 'antarctic') on which to plot analysis comparisonGrids = ['latlon'] [climatologyMapSeaIceConcNH] @@ -600,7 +604,7 @@ colorbarLevelsDifference = [-1., -0.8, -0.6, -0.4, -0.2, 0, 0.2, 0.4, 0.6, 0.8, # observations are only available for these seasons) seasons = ['JFM', 'JAS'] -# comparison grid(s) ('lonlat', 'anatarctic') on which to plot analysis +# comparison grid(s) ('latlon', 'antarctic') on which to plot analysis comparisonGrids = ['latlon'] # reference lat/lon for sea ice plots in the northern hemisphere @@ -636,7 +640,7 @@ colorbarLevelsDifference = [-1., -0.8, -0.6, -0.4, -0.2, 0, 0.2, 0.4, 0.6, 0.8, # observations are only available for these seasons) seasons = ['DJF', 'JJA'] -# comparison grid(s) ('lonlat', 'anatarctic') on which to plot analysis +# comparison grid(s) ('latlon', 'antarctic') on which to plot analysis comparisonGrids = ['latlon'] # reference lat/lon for sea ice plots in the northern hemisphere @@ -672,7 +676,7 @@ colorbarLevelsDifference = [-3., -2.5, -2, -0.5, -0.1, 0, 0.1, 0.5, 2, 2.5, 3.] # observations are only available for these seasons) seasons = ['FM', 'ON'] -# comparison grid(s) ('lonlat', 'anatarctic') on which to plot analysis +# comparison grid(s) ('latlon', 'antarctic') on which to plot analysis comparisonGrids = ['latlon'] # reference lat/lon for sea ice plots in the northern hemisphere @@ -708,7 +712,7 @@ colorbarLevelsDifference = [-3., -2.5, -2, -0.5, -0.1, 0, 0.1, 0.5, 2, 2.5, 3.] # observations are only available for these seasons) seasons = ['FM', 'ON'] -# comparison grid(s) ('lonlat', 'anatarctic') on which to plot analysis +# comparison grid(s) ('latlon', 'antarctic') on which to plot analysis comparisonGrids = ['latlon'] # reference lat/lon for sea ice plots in the northern hemisphere diff --git a/mpas_analysis/configuration/__init__.py b/mpas_analysis/configuration/__init__.py index c23b4d7fb..114e54cb5 100644 --- a/mpas_analysis/configuration/__init__.py +++ b/mpas_analysis/configuration/__init__.py @@ -1 +1 @@ -from .MpasAnalysisConfigParser import MpasAnalysisConfigParser +from .mpas_analysis_config_parser import MpasAnalysisConfigParser diff --git a/mpas_analysis/configuration/MpasAnalysisConfigParser.py b/mpas_analysis/configuration/mpas_analysis_config_parser.py similarity index 100% rename from mpas_analysis/configuration/MpasAnalysisConfigParser.py rename to mpas_analysis/configuration/mpas_analysis_config_parser.py diff --git a/mpas_analysis/ocean/__init__.py b/mpas_analysis/ocean/__init__.py index 09ceff310..1ad3ab1b5 100644 --- a/mpas_analysis/ocean/__init__.py +++ b/mpas_analysis/ocean/__init__.py @@ -1,7 +1,9 @@ -from .climatology_map import ClimatologyMapSST, ClimatologyMapMLD, \ - ClimatologyMapSSS +from .climatology_map_sst import ClimatologyMapSST +from .climatology_map_mld import ClimatologyMapMLD +from .climatology_map_sss import ClimatologyMapSSS + from .time_series_ohc import TimeSeriesOHC from .time_series_sst import TimeSeriesSST from .index_nino34 import IndexNino34 from .streamfunction_moc import StreamfunctionMOC -from .meridional_heat_transport import MeridionalHeatTransport \ No newline at end of file +from .meridional_heat_transport import MeridionalHeatTransport diff --git a/mpas_analysis/ocean/climatology_map.py b/mpas_analysis/ocean/climatology_map.py deleted file mode 100644 index 9a15151c0..000000000 --- a/mpas_analysis/ocean/climatology_map.py +++ /dev/null @@ -1,729 +0,0 @@ -""" -An analysis tasks for comparison of 2D model fields against observations. -Currently supports sea surface temperature (sst), sea surface salinity -(sss) and mixed layer depth (mld) - -Authors -------- -Luke Van Roekel, Xylar Asay-Davis, Milena Veneziani -""" - -from __future__ import absolute_import, division, print_function, \ - unicode_literals - -import xarray as xr -import datetime -import numpy as np -import os - -from ..shared import AnalysisTask - -from ..shared.plot.plotting import plot_global_comparison, \ - setup_colormap -from ..shared.constants import constants - -from ..shared.io.utility import build_config_full_path, make_directories -from ..shared.html import write_image_xml - -from ..shared.climatology import get_comparison_descriptor, \ - get_remapper, get_observation_climatology_file_names, \ - compute_climatology, remap_and_write_climatology, \ - RemapMpasClimatologySubtask - -from ..shared.grid import LatLonGridDescriptor - -from ..shared.mpas_xarray import mpas_xarray - -from ..shared.interpolation import Remapper - - -class ClimatologyMapOcean(AnalysisTask): # {{{ - """ - An analysis task for comparison of 2D model fields against observations. - - Attributes - ---------- - fieldName : str - A short name of the field being analyzed - - fieldNameInTitle : str - An equivalent name of the field appropriate for figure titles - - mpasFieldName : str - The name of the MPAS timeSeriesStatsMonthly variable to be analyzed - - iselValues : dict - A dictionary of dimensions and indices (or ``None``) used to extract - a slice of the MPAS field. - - obsFileName : str - A file containing observtions from which to construct seasonal - climatologies. - - obsFieldName : str - A variable in the observations file to plot - - observationTitleLabel : str - A label on the subplot showing observations - - outFileLabel : str - A prefix for the resulting image file. - - unitsLabel : str - The units for the field being plotted. - - galleryGroup : str - In the generated website, the name of the group of analysis galleries - to which this analysis belongs - - groupLink : str - the link within the component webpage used to identify the gallery - group (a short version of the gallery group name with no spaces or - punctuation) - - groupSubtitle : str - a subtitle for the gallery group - - galleryName : str, optional - the name of the gallery (or possibly a subtitle for the gallery group - if there is only one gallery in the group) - - remapClimatologySubtask : ``RemapMpasClimatologySubtask`` - The subtask that remaps the climatologies this task will plot. - The ``remapClimatologySubtask`` is needed to determine the file names - of the climatology output. - - mpasClimatologyTask : ``MpasClimatologyTask`` - The task that produced the climatology to be remapped and plotted - - Authors - ------- - Luke Van Roekel, Xylar Asay-Davis, Milena Veneziani - """ - - def __init__(self, config, mpasClimatologyTask, taskName, tags): # {{{ - ''' - Construct one analysis subtask for each plot (i.e. each season and - comparison grid) and a subtask for computing climatologies. - - Parameters - ---------- - config : instance of MpasAnalysisConfigParser - Contains configuration options - - mpasClimatologyTask : ``MpasClimatologyTask`` - The task that produced the climatology to be remapped and plotted - - taskName : str - The name of the task, typically the same as the subclass name - except starting with lowercase (e.g. 'climatologyMapSST' for class - 'ClimatologyMapSST') - - tags : list of str - Tags used to describe the task (e.g. 'climatology', - 'horizontalMap'). These are used to determine which tasks are - generated (e.g. 'all_transect' or 'no_climatology' in the - 'generate' flags) - - Authors - ------- - Xylar Asay-Davis - - ''' - # call the constructor from the base class (AnalysisTask) - super(ClimatologyMapOcean, self).__init__(config=config, - taskName=taskName, - componentName='ocean', - tags=tags) - - self.mpasClimatologyTask = mpasClimatologyTask - - sectionName = self.taskName - - # read in what seasons we want to plot - seasons = config.getExpression(sectionName, 'seasons') - - if len(seasons) == 0: - raise ValueError('config section {} does not contain valid list ' - 'of seasons'.format(sectionName)) - - # comparisonGridNames = config.getExpression(sectionName, - # 'comparisonGrids') - - # if len(comparisonGridNames) == 0: - # raise ValueError('config section {} does not contain valid list ' - # 'of comparison grids'.format(sectionName)) - - # the variable self.mpasFieldName will be added to mpasClimatologyTask - # along with the seasons. - self.remapClimatologySubtask = RemapMpasClimatologySubtask( - mpasClimatologyTask=self.mpasClimatologyTask, - parentTask=self, - climatologyName=self.fieldName, - variableList=[self.mpasFieldName], - # comparisonGridNames=comparisonGridNames, - seasons=seasons, - iselValues=self.iselValues) - - # }}} - - def setup_and_check(self): # {{{ - """ - Perform steps to set up the analysis and check for errors in the setup. - - Authors - ------- - Xylar Asay-Davis - """ - # call setup_and_check from the base class (AnalysisTask), - # which will perform some common setup, including storing: - # self.runDirectory , self.historyDirectory, self.plotsDirectory, - # self.namelist, self.runStreams, self.historyStreams, - # self.calendar - super(ClimatologyMapOcean, self).setup_and_check() - - config = self.config - self.startYear = self.mpasClimatologyTask.startYear - self.startDate = self.mpasClimatologyTask.startDate - self.endYear = self.mpasClimatologyTask.endYear - self.endDate = self.mpasClimatologyTask.endDate - - mainRunName = config.get('runs', 'mainRunName') - seasons = config.getExpression(self.taskName, 'seasons') - - # we set up the remapper here because ESFM_RegridWeightGen seems to - # have trouble if it runs in another process (or in several at once) - self._setup_obs_remapper() - - self.xmlFileNames = [] - self.filePrefixes = {} - - for season in seasons: - filePrefix = '{}_{}_{}_years{:04d}-{:04d}'.format( - self.outFileLabel, mainRunName, - season, self.startYear, self.endYear) - self.xmlFileNames.append('{}/{}.xml'.format(self.plotsDirectory, - filePrefix)) - self.filePrefixes[season] = filePrefix - - # make the mapping directory, because doing so within each process - # seems to be giving ESMF_RegridWeightGen some trouble - mappingSubdirectory = build_config_full_path(config, 'output', - 'mappingSubdirectory') - make_directories(mappingSubdirectory) - - # }}} - - def run_task(self): # {{{ - """ - Plots a comparison of ACME/MPAS output to SST, MLD or SSS observations - - Authors - ------- - Luke Van Roekel, Xylar Asay-Davis, Milena Veneziani - """ - - self.logger.info("\nPlotting 2-d maps of {} climatologies...".format( - self.fieldNameInTitle)) - - # get local versions of member variables for convenience - config = self.config - fieldName = self.fieldName - - try: - restartFileName = self.runStreams.readpath('restart')[0] - except ValueError: - raise IOError('No MPAS-O restart file found: need at least one ' - 'restart file for ocn_modelvsobs calculation') - - mainRunName = self.config.get('runs', 'mainRunName') - - seasons = config.getExpression(self.taskName, 'seasons') - - (colormapResult, colorbarLevelsResult) = setup_colormap( - config, self.taskName, suffix='Result') - (colormapDifference, colorbarLevelsDifference) = setup_colormap( - config, self.taskName, suffix='Difference') - - dsRestart = xr.open_dataset(restartFileName) - dsRestart = mpas_xarray.subset_variables(dsRestart, ['maxLevelCell']) - - dsObs = None - - # Interpolate and compute biases - for season in seasons: - - monthValues = constants.monthDictionary[season] - - remappedFileName = self.remapClimatologySubtask.get_file_name( - season=season, stage='remapped', comparisonGridName='latlon') - - remappedClimatology = xr.open_dataset(remappedFileName) - - modelOutput = \ - remappedClimatology[self.mpasFieldName].values - - lon = remappedClimatology['lon'].values - lat = remappedClimatology['lat'].values - - lonTarg, latTarg = np.meshgrid(lon, lat) - - # now the observations - (climatologyFileName, remappedFileName) = \ - get_observation_climatology_file_names( - config=config, fieldName=fieldName, monthNames=season, - componentName='ocean', remapper=self.obsRemapper) - - if not os.path.exists(remappedFileName): - - if dsObs is None: - # load the observations the first time - dsObs = self._build_observational_dataset() - - seasonalClimatology = compute_climatology( - dsObs, monthValues, maskVaries=True) - - if self.obsRemapper is None: - # no need to remap because the observations are on the - # comparison grid already - remappedClimatology = seasonalClimatology - else: - remappedClimatology = \ - remap_and_write_climatology( - config, seasonalClimatology, climatologyFileName, - remappedFileName, self.obsRemapper, - logger=self.logger) - - else: - - remappedClimatology = xr.open_dataset(remappedFileName) - observations = remappedClimatology[self.obsFieldName].values - - bias = modelOutput - observations - - filePrefix = self.filePrefixes[season] - 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, - observations, - bias, - colormapResult, - colorbarLevelsResult, - colormapDifference, - colorbarLevelsDifference, - fileout=outFileName, - title=title, - modelTitle='{}'.format(mainRunName), - obsTitle=self.observationTitleLabel, - diffTitle='Model-Observations', - cbarlabel=self.unitsLabel) - - 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=season, - imageDescription=caption, - imageCaption=caption) - - # }}} - - def _setup_obs_remapper(self): # {{{ - """ - Set up the remapper for remapping from the MPAS to the comparison - grid. - - Authors - ------- - Xylar Asay-Davis - """ - config = self.config - fieldName = self.fieldName - - seasons = config.getExpression(self.taskName, 'seasons') - - # make reamppers - comparisonDescriptor = get_comparison_descriptor( - config=config, comparisonGridName='latlon') - self.comparisonGridName = comparisonDescriptor.meshName - - obsDescriptor = LatLonGridDescriptor.read(fileName=self.obsFileName, - latVarName='lat', - lonVarName='lon') - - origObsRemapper = Remapper(comparisonDescriptor, obsDescriptor) - - season = seasons[0] - - # now the observations - (climatologyFileName, remappedFileName) = \ - get_observation_climatology_file_names( - config=config, fieldName=fieldName, monthNames=season, - componentName='ocean', remapper=origObsRemapper) - - # make the remapper for the climatology - self.obsRemapper = get_remapper( - config=config, sourceDescriptor=obsDescriptor, - comparisonDescriptor=comparisonDescriptor, - mappingFilePrefix='map_obs_{}'.format(fieldName), - method=config.get('oceanObservations', - 'interpolationMethod'), - logger=self.logger) - - # }}} - # }}} - - -class ClimatologyMapSST(ClimatologyMapOcean): # {{{ - """ - An analysis task for comparison of sea surface temperature (sst) against - observations - - Authors - ------- - Luke Van Roekel, Xylar Asay-Davis, Milena Veneziani - """ - def __init__(self, config, mpasClimatologyTask): # {{{ - """ - Construct the analysis task. - - Parameters - ---------- - config : instance of MpasAnalysisConfigParser - Contains configuration options - - mpasClimatologyTask : ``MpasClimatologyTask`` - The task that produced the climatology to be remapped and plotted - - Authors - ------- - Xylar Asay-Davis - """ - self.fieldName = 'sst' - self.fieldNameInTitle = 'SST' - self.mpasFieldName = 'timeMonthly_avg_activeTracers_temperature' - self.iselValues = {'nVertLevels': 0} - - # call the constructor from the base class (ClimatologyMapOcean) - super(ClimatologyMapSST, self).__init__( - config=config, - mpasClimatologyTask=mpasClimatologyTask, - taskName='climatologyMapSST', - tags=['climatology', 'horizontalMap', self.fieldName]) - - # }}} - - def setup_and_check(self): # {{{ - """ - Perform steps to set up the analysis and check for errors in the setup. - - Authors - ------- - Xylar Asay-Davis - """ - - self.outFileLabel = 'sstHADOI' - - observationsDirectory = build_config_full_path( - self.config, 'oceanObservations', - '{}Subdirectory'.format(self.fieldName)) - - self.obsFileName = \ - "{}/MODEL.SST.HAD187001-198110.OI198111-201203.nc".format( - observationsDirectory) - - self.obsFieldName = 'SST' - - climStartYear = self.config.getint('oceanObservations', - 'sstClimatologyStartYear') - climEndYear = self.config.getint('oceanObservations', - 'sstClimatologyEndYear') - - if climStartYear < 1925: - period = 'pre-industrial' - else: - period = 'present-day' - - # Set appropriate figure labels for SST - self.observationTitleLabel = \ - 'Observations (Hadley/OI, {} {:04d}-{:04d})'.format(period, - climStartYear, - climEndYear) - self.unitsLabel = r'$^o$C' - - # variables for XML and webpages - self.galleryGroup = 'Global Sea Surface Temperature' - self.groupSubtitle = None - self.groupLink = 'sst' - self.galleryName = 'Observations: Hadley-NOAA-OI' - self.imageCaption = 'Mean Sea Surface Temperature' - - # call setup_and_check from the base class (AnalysisTask), - # which will perform some common setup, including storing: - # self.runDirectory , self.historyDirectory, self.plotsDirectory, - # self.namelist, self.runStreams, self.historyStreams, - # self.calendar - super(ClimatologyMapSST, self).setup_and_check() - - # }}} - - def _build_observational_dataset(self): # {{{ - ''' - read in the data sets for observations, and possibly rename some - variables and dimensions - - Authors - ------- - Xylar Asay-Davis - ''' - - climStartYear = self.config.getint('oceanObservations', - 'sstClimatologyStartYear') - climEndYear = self.config.getint('oceanObservations', - 'sstClimatologyEndYear') - timeStart = datetime.datetime(year=climStartYear, month=1, day=1) - timeEnd = datetime.datetime(year=climEndYear, month=12, day=31) - - dsObs = xr.open_mfdataset(self.obsFileName) - dsObs.rename({'time': 'Time'}, inplace=True) - dsObs = dsObs.sel(Time=slice(timeStart, timeEnd)) - dsObs.coords['month'] = dsObs['Time.month'] - dsObs.coords['year'] = dsObs['Time.year'] - - return dsObs # }}} - - # }}} - - -class ClimatologyMapSSS(ClimatologyMapOcean): # {{{ - """ - An analysis task for comparison of sea surface salinity (sss) against - observations - - Authors - ------- - Luke Van Roekel, Xylar Asay-Davis, Milena Veneziani - """ - def __init__(self, config, mpasClimatologyTask): # {{{ - """ - Construct the analysis task. - - Parameters - ---------- - config : instance of MpasAnalysisConfigParser - Contains configuration options - - mpasClimatologyTask : ``MpasClimatologyTask`` - The task that produced the climatology to be remapped and plotted - - Authors - ------- - Xylar Asay-Davis - """ - self.fieldName = 'sss' - self.fieldNameInTitle = 'SSS' - self.mpasFieldName = 'timeMonthly_avg_activeTracers_salinity' - self.iselValues = {'nVertLevels': 0} - - # call the constructor from the base class (ClimatologyMapOcean) - super(ClimatologyMapSSS, self).__init__( - config=config, - mpasClimatologyTask=mpasClimatologyTask, - taskName='climatologyMapSSS', - tags=['climatology', 'horizontalMap', self.fieldName]) - - # }}} - - def setup_and_check(self): # {{{ - """ - Perform steps to set up the analysis and check for errors in the setup. - - Authors - ------- - Xylar Asay-Davis - """ - - self.outFileLabel = 'sssAquarius' - - observationsDirectory = build_config_full_path( - self.config, 'oceanObservations', - '{}Subdirectory'.format(self.fieldName)) - - self.obsFileName = \ - '{}/Aquarius_V3_SSS_Monthly.nc'.format( - observationsDirectory) - - self.obsFieldName = 'SSS' - - self.observationTitleLabel = 'Observations (Aquarius, 2011-2014)' - self.unitsLabel = 'PSU' - - # variables for XML and webpages - self.galleryGroup = 'Global Sea Surface Salinity' - self.groupSubtitle = None - self.groupLink = 'sss' - self.galleryName = 'Observations: Aquarius' - self.imageCaption = 'Mean Sea Surface Salinity' - - # call setup_and_check from the base class (AnalysisTask), - # which will perform some common setup, including storing: - # self.runDirectory , self.historyDirectory, self.plotsDirectory, - # self.namelist, self.runStreams, self.historyStreams, - # self.calendar - super(ClimatologyMapSSS, self).setup_and_check() - - # }}} - - def _build_observational_dataset(self): # {{{ - ''' - read in the data sets for observations, and possibly rename some - variables and dimensions - - Authors - ------- - Xylar Asay-Davis - ''' - - timeStart = datetime.datetime(2011, 8, 1) - timeEnd = datetime.datetime(2014, 12, 31) - - dsObs = xr.open_mfdataset(self.obsFileName) - dsObs.rename({'time': 'Time'}, inplace=True) - dsObs = dsObs.sel(Time=slice(timeStart, timeEnd)) - dsObs.coords['month'] = dsObs['Time.month'] - dsObs.coords['year'] = dsObs['Time.year'] - - return dsObs # }}} - - # }}} - - -class ClimatologyMapMLD(ClimatologyMapOcean): # {{{ - """ - An analysis task for comparison of mixed layer depth (mld) against - observations - - Authors - ------- - Luke Van Roekel, Xylar Asay-Davis, Milena Veneziani - """ - def __init__(self, config, mpasClimatologyTask): # {{{ - """ - Construct the analysis task. - - Parameters - ---------- - config : instance of MpasAnalysisConfigParser - Contains configuration options - - mpasClimatologyTask : ``MpasClimatologyTask`` - The task that produced the climatology to be remapped and plotted - - Authors - ------- - Xylar Asay-Davis - """ - - self.fieldName = 'mld' - self.fieldNameInTitle = 'MLD' - self.mpasFieldName = 'timeMonthly_avg_dThreshMLD' - self.iselValues = None - - # call the constructor from the base class (ClimatologyMapOcean) - super(ClimatologyMapMLD, self).__init__( - config=config, - mpasClimatologyTask=mpasClimatologyTask, - taskName='climatologyMapMLD', - tags=['climatology', 'horizontalMap', self.fieldName]) - - # }}} - - def setup_and_check(self): # {{{ - """ - Perform steps to set up the analysis and check for errors in the setup. - - Authors - ------- - Xylar Asay-Davis - """ - - self.outFileLabel = 'mldHolteTalleyARGO' - - observationsDirectory = build_config_full_path( - self.config, 'oceanObservations', - '{}Subdirectory'.format(self.fieldName)) - - self.obsFileName = \ - '{}/holtetalley_mld_climatology.nc'.format( - observationsDirectory) - - self.obsFieldName = 'mld_dt_mean' - - # Set appropriate MLD figure labels - self.observationTitleLabel = \ - 'Observations (HolteTalley density threshold MLD)' - self.unitsLabel = 'm' - - # variables for XML and webpages - self.galleryGroup = 'Global Mixed-Layer Depth' - self.groupSubtitle = None - self.groupLink = 'mld' - self.galleryName = 'Observations: Holte-Talley ARGO' - self.imageCaption = 'Mean Mixed-Layer Depth' - - # call setup_and_check from the base class (AnalysisTask), - # which will perform some common setup, including storing: - # self.runDirectory , self.historyDirectory, self.plotsDirectory, - # self.namelist, self.runStreams, self.historyStreams, - # self.calendar - super(ClimatologyMapMLD, self).setup_and_check() - - # }}} - - def _build_observational_dataset(self): # {{{ - ''' - read in the data sets for observations, and possibly rename some - variables and dimensions - - Authors - ------- - Xylar Asay-Davis - ''' - - # Load MLD observational data - dsObs = xr.open_mfdataset(self.obsFileName) - - # Increment month value to be consistent with the model output - dsObs.iMONTH.values += 1 - # Rename the dimensions to be consistent with other obs. data sets - dsObs.rename({'month': 'calmonth', 'lat': 'latCoord', - 'lon': 'lonCoord'}, inplace=True) - dsObs.rename({'iMONTH': 'Time', 'iLAT': 'lat', 'iLON': 'lon'}, - inplace=True) - - # set the coordinates now that the dimensions have the same names - dsObs.coords['lat'] = dsObs['latCoord'] - dsObs.coords['lon'] = dsObs['lonCoord'] - dsObs.coords['Time'] = dsObs['calmonth'] - dsObs.coords['month'] = ('Time', np.array(dsObs['calmonth'], int)) - - # no meaningful year since this is already a climatology - dsObs.coords['year'] = ('Time', np.ones(dsObs.dims['Time'], int)) - - dsObs = mpas_xarray.subset_variables(dsObs, [self.obsFieldName, - 'month']) - - return dsObs # }}} - -# vim: foldmethod=marker ai ts=4 sts=4 et sw=4 ft=python diff --git a/mpas_analysis/ocean/climatology_map_mld.py b/mpas_analysis/ocean/climatology_map_mld.py new file mode 100644 index 000000000..89218be6c --- /dev/null +++ b/mpas_analysis/ocean/climatology_map_mld.py @@ -0,0 +1,205 @@ +from __future__ import absolute_import, division, print_function, \ + unicode_literals + +import xarray as xr +import numpy as np + +from ..shared import AnalysisTask + +from ..shared.io.utility import build_config_full_path + +from ..shared.climatology import RemapMpasClimatologySubtask, \ + RemapObservedClimatologySubtask + +from .plot_climatology_map_subtask import PlotClimatologyMapSubtask + +from ..shared.grid import LatLonGridDescriptor + +from ..shared.mpas_xarray import mpas_xarray + + +class ClimatologyMapMLD(AnalysisTask): # {{{ + """ + An analysis task for comparison of mixed layer depth (mld) against + observations + + Authors + ------- + Luke Van Roekel, Xylar Asay-Davis, Milena Veneziani + """ + def __init__(self, config, mpasClimatologyTask): # {{{ + """ + Construct the analysis task. + + Parameters + ---------- + config : instance of MpasAnalysisConfigParser + Contains configuration options + + mpasClimatologyTask : ``MpasClimatologyTask`` + The task that produced the climatology to be remapped and plotted + + Authors + ------- + Xylar Asay-Davis + """ + fieldName = 'mld' + # call the constructor from the base class (AnalysisTask) + super(ClimatologyMapMLD, self).__init__( + config=config, taskName='climatologyMapMLD', + componentName='ocean', + tags=['climatology', 'horizontalMap', fieldName]) + + sectionName = self.taskName + + mpasFieldName = 'timeMonthly_avg_dThreshMLD' + iselValues = None + + observationTitleLabel = \ + 'Observations (HolteTalley density threshold MLD)' + + observationsDirectory = build_config_full_path( + config, 'oceanObservations', + '{}Subdirectory'.format(fieldName)) + + obsFileName = \ + "{}/holtetalley_mld_climatology.nc".format(observationsDirectory) + obsFieldName = 'mld' + + # read in what seasons we want to plot + seasons = config.getExpression(sectionName, 'seasons') + + if len(seasons) == 0: + raise ValueError('config section {} does not contain valid list ' + 'of seasons'.format(sectionName)) + + comparisonGridNames = config.getExpression(sectionName, + 'comparisonGrids') + + if len(comparisonGridNames) == 0: + raise ValueError('config section {} does not contain valid list ' + 'of comparison grids'.format(sectionName)) + + # the variable 'timeMonthly_avg_dThreshMLD' will be added to + # mpasClimatologyTask along with the seasons. + remapClimatologySubtask = RemapMpasClimatologySubtask( + mpasClimatologyTask=mpasClimatologyTask, + parentTask=self, + climatologyName=fieldName, + variableList=[mpasFieldName], + comparisonGridNames=comparisonGridNames, + seasons=seasons, + iselValues=iselValues) + + remapObservationsSubtask = RemapObservedMLDClimatology( + parentTask=self, seasons=seasons, fileName=obsFileName, + outFilePrefix=obsFieldName, + comparisonGridNames=comparisonGridNames) + self.add_subtask(remapObservationsSubtask) + for comparisonGridName in comparisonGridNames: + for season in seasons: + # make a new subtask for this season and comparison grid + subtask = PlotClimatologyMapSubtask(self, season, + comparisonGridName, + remapClimatologySubtask, + remapObservationsSubtask) + + subtask.set_plot_info( + outFileLabel='mldHolteTalleyARGO', + fieldNameInTitle='MLD', + mpasFieldName=mpasFieldName, + obsFieldName=obsFieldName, + observationTitleLabel=observationTitleLabel, + unitsLabel=r'm', + imageCaption='Mean Mixed-Layer Depth', + galleryGroup='Mixed-Layer Depth', + groupSubtitle=None, + groupLink='mld', + galleryName='Observations: Holte-Talley ARGO') + + self.add_subtask(subtask) + # }}} + # }}} + + +class RemapObservedMLDClimatology(RemapObservedClimatologySubtask): # {{{ + """ + A subtask for reading and remapping MLD observations + + Authors + ------- + Luke Van Roekel, Xylar Asay-Davis, Milena Veneziani + """ + + def get_observation_descriptor(self, fileName): # {{{ + ''' + get a MeshDescriptor for the observation grid + + Parameters + ---------- + fileName : str + observation file name describing the source grid + + Returns + ------- + obsDescriptor : ``MeshDescriptor`` + The descriptor for the observation grid + + Authors + ------- + Xylar Asay-Davis + ''' + + # create a descriptor of the observation grid using the lat/lon + # coordinates + obsDescriptor = LatLonGridDescriptor.read(fileName=fileName, + latVarName='lat', + lonVarName='lon') + return obsDescriptor # }}} + + def build_observational_dataset(self, fileName): # {{{ + ''' + read in the data sets for observations, and possibly rename some + variables and dimensions + + Parameters + ---------- + fileName : str + observation file name + + Returns + ------- + dsObs : ``xarray.Dataset`` + The observational dataset + + Authors + ------- + Xylar Asay-Davis + ''' + + # Load MLD observational data + dsObs = xr.open_dataset(fileName) + + # Increment month value to be consistent with the model output + dsObs.iMONTH.values += 1 + # Rename the dimensions to be consistent with other obs. data sets + dsObs.rename({'month': 'calmonth', 'lat': 'latCoord', + 'lon': 'lonCoord', 'mld_dt_mean': 'mld'}, inplace=True) + dsObs.rename({'iMONTH': 'Time', 'iLAT': 'lat', 'iLON': 'lon'}, + inplace=True) + + # set the coordinates now that the dimensions have the same names + dsObs.coords['lat'] = dsObs['latCoord'] + dsObs.coords['lon'] = dsObs['lonCoord'] + dsObs.coords['Time'] = dsObs['calmonth'] + dsObs.coords['month'] = ('Time', np.array(dsObs['calmonth'], int)) + + # no meaningful year since this is already a climatology + dsObs.coords['year'] = ('Time', np.ones(dsObs.dims['Time'], int)) + + dsObs = mpas_xarray.subset_variables(dsObs, ['mld', 'month']) + return dsObs # }}} + + # }}} + +# vim: foldmethod=marker ai ts=4 sts=4 et sw=4 ft=python diff --git a/mpas_analysis/ocean/climatology_map_sss.py b/mpas_analysis/ocean/climatology_map_sss.py new file mode 100644 index 000000000..168a3d59c --- /dev/null +++ b/mpas_analysis/ocean/climatology_map_sss.py @@ -0,0 +1,192 @@ +from __future__ import absolute_import, division, print_function, \ + unicode_literals + +import xarray as xr +import datetime + +from ..shared import AnalysisTask + +from ..shared.io.utility import build_config_full_path + +from ..shared.climatology import RemapMpasClimatologySubtask, \ + RemapObservedClimatologySubtask + +from .plot_climatology_map_subtask import PlotClimatologyMapSubtask + +from ..shared.grid import LatLonGridDescriptor + + +class ClimatologyMapSSS(AnalysisTask): # {{{ + """ + An analysis task for comparison of sea surface salinity (sss) against + observations + + Authors + ------- + Luke Van Roekel, Xylar Asay-Davis, Milena Veneziani + """ + def __init__(self, config, mpasClimatologyTask): # {{{ + """ + Construct the analysis task. + + Parameters + ---------- + config : instance of MpasAnalysisConfigParser + Contains configuration options + + mpasClimatologyTask : ``MpasClimatologyTask`` + The task that produced the climatology to be remapped and plotted + + Authors + ------- + Xylar Asay-Davis + """ + self.fieldName = 'sss' + # call the constructor from the base class (AnalysisTask) + super(ClimatologyMapSSS, self).__init__( + config=config, taskName='climatologyMapSSS', + componentName='ocean', + tags=['climatology', 'horizontalMap', self.fieldName]) + + mpasFieldName = 'timeMonthly_avg_activeTracers_salinity' + iselValues = {'nVertLevels': 0} + + sectionName = self.taskName + + observationTitleLabel = \ + 'Observations (Aquarius, 2011-2014)' + + observationsDirectory = build_config_full_path( + config, 'oceanObservations', + '{}Subdirectory'.format(self.fieldName)) + + obsFileName = \ + "{}/Aquarius_V3_SSS_Monthly.nc".format( + observationsDirectory) + obsFieldName = 'sss' + + # read in what seasons we want to plot + seasons = config.getExpression(sectionName, 'seasons') + + if len(seasons) == 0: + raise ValueError('config section {} does not contain valid list ' + 'of seasons'.format(sectionName)) + + comparisonGridNames = config.getExpression(sectionName, + 'comparisonGrids') + + if len(comparisonGridNames) == 0: + raise ValueError('config section {} does not contain valid list ' + 'of comparison grids'.format(sectionName)) + + # the variable self.mpasFieldName will be added to mpasClimatologyTask + # along with the seasons. + remapClimatologySubtask = RemapMpasClimatologySubtask( + mpasClimatologyTask=mpasClimatologyTask, + parentTask=self, + climatologyName=self.fieldName, + variableList=[mpasFieldName], + comparisonGridNames=comparisonGridNames, + seasons=seasons, + iselValues=iselValues) + + remapObservationsSubtask = RemapObservedSSSClimatology( + parentTask=self, seasons=seasons, fileName=obsFileName, + outFilePrefix=obsFieldName, + comparisonGridNames=comparisonGridNames) + self.add_subtask(remapObservationsSubtask) + for comparisonGridName in comparisonGridNames: + for season in seasons: + # make a new subtask for this season and comparison grid + subtask = PlotClimatologyMapSubtask(self, season, + comparisonGridName, + remapClimatologySubtask, + remapObservationsSubtask) + + subtask.set_plot_info( + outFileLabel='sssAquarius', + fieldNameInTitle='SSS', + mpasFieldName=mpasFieldName, + obsFieldName=obsFieldName, + observationTitleLabel=observationTitleLabel, + unitsLabel=r'PSU', + imageCaption='Mean Sea Surface Salinity', + galleryGroup='Sea Surface Salinity', + groupSubtitle=None, + groupLink='sss', + galleryName='Observations: Aquarius') + + self.add_subtask(subtask) + # }}} + # }}} + + +class RemapObservedSSSClimatology(RemapObservedClimatologySubtask): # {{{ + """ + A subtask for reading and remapping SSS observations + + Authors + ------- + Luke Van Roekel, Xylar Asay-Davis, Milena Veneziani + """ + + def get_observation_descriptor(self, fileName): # {{{ + ''' + get a MeshDescriptor for the observation grid + + Parameters + ---------- + fileName : str + observation file name describing the source grid + + Returns + ------- + obsDescriptor : ``MeshDescriptor`` + The descriptor for the observation grid + + Authors + ------- + Xylar Asay-Davis + ''' + + # create a descriptor of the observation grid using the lat/lon + # coordinates + obsDescriptor = LatLonGridDescriptor.read(fileName=fileName, + latVarName='lat', + lonVarName='lon') + return obsDescriptor # }}} + + def build_observational_dataset(self, fileName): # {{{ + ''' + read in the data sets for observations, and possibly rename some + variables and dimensions + + Parameters + ---------- + fileName : str + observation file name + + Returns + ------- + dsObs : ``xarray.Dataset`` + The observational dataset + + Authors + ------- + Xylar Asay-Davis + ''' + + timeStart = datetime.datetime(2011, 8, 1) + timeEnd = datetime.datetime(2014, 12, 31) + + dsObs = xr.open_dataset(fileName) + dsObs.rename({'time': 'Time', 'SSS': 'sss'}, inplace=True) + dsObs = dsObs.sel(Time=slice(timeStart, timeEnd)) + dsObs.coords['month'] = dsObs['Time.month'] + dsObs.coords['year'] = dsObs['Time.year'] + + return dsObs # }}} + + # }}} + +# vim: foldmethod=marker ai ts=4 sts=4 et sw=4 ft=python diff --git a/mpas_analysis/ocean/climatology_map_sst.py b/mpas_analysis/ocean/climatology_map_sst.py new file mode 100644 index 000000000..db4bcca5a --- /dev/null +++ b/mpas_analysis/ocean/climatology_map_sst.py @@ -0,0 +1,208 @@ +from __future__ import absolute_import, division, print_function, \ + unicode_literals + +import xarray as xr +import datetime + +from ..shared import AnalysisTask + +from ..shared.io.utility import build_config_full_path + +from ..shared.climatology import RemapMpasClimatologySubtask, \ + RemapObservedClimatologySubtask + +from .plot_climatology_map_subtask import PlotClimatologyMapSubtask + +from ..shared.grid import LatLonGridDescriptor + + +class ClimatologyMapSST(AnalysisTask): # {{{ + """ + An analysis task for comparison of sea surface temperature (sst) against + observations + + Authors + ------- + Luke Van Roekel, Xylar Asay-Davis, Milena Veneziani + """ + def __init__(self, config, mpasClimatologyTask): # {{{ + """ + Construct the analysis task. + + Parameters + ---------- + config : instance of MpasAnalysisConfigParser + Contains configuration options + + mpasClimatologyTask : ``MpasClimatologyTask`` + The task that produced the climatology to be remapped and plotted + + Authors + ------- + Xylar Asay-Davis + """ + self.fieldName = 'sst' + # call the constructor from the base class (AnalysisTask) + super(ClimatologyMapSST, self).__init__( + config=config, taskName='climatologyMapSST', + componentName='ocean', + tags=['climatology', 'horizontalMap', self.fieldName]) + + mpasFieldName = 'timeMonthly_avg_activeTracers_temperature' + iselValues = {'nVertLevels': 0} + + sectionName = self.taskName + + climStartYear = config.getint('oceanObservations', + 'sstClimatologyStartYear') + climEndYear = config.getint('oceanObservations', + 'sstClimatologyEndYear') + + if climStartYear < 1925: + period = 'pre-industrial' + else: + period = 'present-day' + + observationTitleLabel = \ + 'Observations (Hadley/OI, {} {:04d}-{:04d})'.format(period, + climStartYear, + climEndYear) + + observationsDirectory = build_config_full_path( + config, 'oceanObservations', + '{}Subdirectory'.format(self.fieldName)) + + obsFileName = \ + "{}/MODEL.SST.HAD187001-198110.OI198111-201203.nc".format( + observationsDirectory) + obsFieldName = 'sst' + + # read in what seasons we want to plot + seasons = config.getExpression(sectionName, 'seasons') + + if len(seasons) == 0: + raise ValueError('config section {} does not contain valid list ' + 'of seasons'.format(sectionName)) + + comparisonGridNames = config.getExpression(sectionName, + 'comparisonGrids') + + if len(comparisonGridNames) == 0: + raise ValueError('config section {} does not contain valid list ' + 'of comparison grids'.format(sectionName)) + + # the variable self.mpasFieldName will be added to mpasClimatologyTask + # along with the seasons. + remapClimatologySubtask = RemapMpasClimatologySubtask( + mpasClimatologyTask=mpasClimatologyTask, + parentTask=self, + climatologyName=self.fieldName, + variableList=[mpasFieldName], + comparisonGridNames=comparisonGridNames, + seasons=seasons, + iselValues=iselValues) + + remapObservationsSubtask = RemapObservedSSTClimatology( + parentTask=self, seasons=seasons, fileName=obsFileName, + outFilePrefix=obsFieldName, + comparisonGridNames=comparisonGridNames) + self.add_subtask(remapObservationsSubtask) + for comparisonGridName in comparisonGridNames: + for season in seasons: + # make a new subtask for this season and comparison grid + subtask = PlotClimatologyMapSubtask(self, season, + comparisonGridName, + remapClimatologySubtask, + remapObservationsSubtask) + + subtask.set_plot_info( + outFileLabel='sstHADOI', + fieldNameInTitle='SST', + mpasFieldName=mpasFieldName, + obsFieldName=obsFieldName, + observationTitleLabel=observationTitleLabel, + unitsLabel=r'$^o$C', + imageCaption='Mean Sea Surface Temperature', + galleryGroup='Sea Surface Temperature', + groupSubtitle=None, + groupLink='sst', + galleryName='Observations: Hadley-NOAA-OI') + + self.add_subtask(subtask) + # }}} + # }}} + + +class RemapObservedSSTClimatology(RemapObservedClimatologySubtask): # {{{ + """ + A subtask for reading and remapping SST observations + + Authors + ------- + Luke Van Roekel, Xylar Asay-Davis, Milena Veneziani + """ + + def get_observation_descriptor(self, fileName): # {{{ + ''' + get a MeshDescriptor for the observation grid + + Parameters + ---------- + fileName : str + observation file name describing the source grid + + Returns + ------- + obsDescriptor : ``MeshDescriptor`` + The descriptor for the observation grid + + Authors + ------- + Xylar Asay-Davis + ''' + + # create a descriptor of the observation grid using the lat/lon + # coordinates + obsDescriptor = LatLonGridDescriptor.read(fileName=fileName, + latVarName='lat', + lonVarName='lon') + return obsDescriptor # }}} + + def build_observational_dataset(self, fileName): # {{{ + ''' + read in the data sets for observations, and possibly rename some + variables and dimensions + + Parameters + ---------- + fileName : str + observation file name + + Returns + ------- + dsObs : ``xarray.Dataset`` + The observational dataset + + Authors + ------- + Xylar Asay-Davis + ''' + + climStartYear = self.config.getint('oceanObservations', + 'sstClimatologyStartYear') + climEndYear = self.config.getint('oceanObservations', + 'sstClimatologyEndYear') + timeStart = datetime.datetime(year=climStartYear, month=1, day=1) + timeEnd = datetime.datetime(year=climEndYear, month=12, day=31) + + dsObs = xr.open_dataset(fileName) + dsObs.rename({'time': 'Time', 'SST': 'sst'}, inplace=True) + dsObs = dsObs.sel(Time=slice(timeStart, timeEnd)) + dsObs.coords['month'] = dsObs['Time.month'] + dsObs.coords['year'] = dsObs['Time.year'] + + return dsObs # }}} + + # }}} + +# vim: foldmethod=marker ai ts=4 sts=4 et sw=4 ft=python diff --git a/mpas_analysis/ocean/plot_climatology_map_subtask.py b/mpas_analysis/ocean/plot_climatology_map_subtask.py new file mode 100644 index 000000000..ae79e1f21 --- /dev/null +++ b/mpas_analysis/ocean/plot_climatology_map_subtask.py @@ -0,0 +1,420 @@ +""" +An analysis subtasks for plotting comparison of 2D model fields against +observations. + +Authors +------- +Luke Van Roekel, Xylar Asay-Davis, Milena Veneziani +""" + +from __future__ import absolute_import, division, print_function, \ + unicode_literals + +import xarray as xr +import numpy as np + +from ..shared import AnalysisTask + +from ..shared.plot.plotting import plot_global_comparison, \ + setup_colormap, plot_polar_projection_comparison + +from ..shared.html import write_image_xml + +from ..shared.grid import interp_extrap_corner + + +class PlotClimatologyMapSubtask(AnalysisTask): # {{{ + """ + An analysis task for plotting 2D model fields against observations. + + Attributes + ---------- + season : str + A season (key in ``shared.constants.monthDictionary``) to be + plotted. + + comparisonGridName : {'latlon', 'antarctic'} + The name of the comparison grid to plot. + + remapMpasClimatologySubtask : ``RemapMpasClimatologySubtask`` + The subtask for remapping the MPAS climatology that this subtask + will plot + + remapObsClimatologySubtask : ``RemapObservedClimatologySubtask`` + The subtask for remapping the observational climatology that this + subtask will plot + + outFileLabel : str + The prefix on each plot and associated XML file + + fieldNameInTitle : str + The name of the field being plotted, as used in the plot title + + mpasFieldName : str + The name of the variable in the MPAS timeSeriesStatsMonthly output + + obsFieldName : str + The name of the variable to use from the observations file + + observationTitleLabel : str + the title of the observations subplot + + unitsLabel : str + the units of the plotted field, to be displayed on color bars + + imageCaption : str + the caption when mousing over the plot or displaying it full + screen + + galleryGroup : str + the name of the group of galleries in which this plot belongs + + groupSubtitle : str + the subtitle of the group in which this plot belongs (or blank + if none) + + groupLink : str + a short name (with no spaces) for the link to the gallery group + + galleryName : str + the name of the gallery in which this plot belongs + + Authors + ------- + Luke Van Roekel, Xylar Asay-Davis, Milena Veneziani + """ + + def __init__(self, parentTask, season, comparisonGridName, + remapMpasClimatologySubtask, remapObsClimatologySubtask): + # {{{ + ''' + Construct one analysis subtask for each plot (i.e. each season and + comparison grid) and a subtask for computing climatologies. + + Parameters + ---------- + parentTask : ``AnalysisTask`` + The parent (master) task for this subtask + + season : str + A season (key in ``shared.constants.monthDictionary``) to be + plotted. + + comparisonGridName : {'latlon', 'antarctic'} + The name of the comparison grid to plot. + + remapMpasClimatologySubtask : ``RemapMpasClimatologySubtask`` + The subtask for remapping the MPAS climatology that this subtask + will plot + + remapObsClimatologySubtask : ``RemapObservedClimatologySubtask`` + The subtask for remapping the observational climatology that this + subtask will plot + + Authors + ------- + Xylar Asay-Davis + + ''' + + self.season = season + self.comparisonGridName = comparisonGridName + self.remapMpasClimatologySubtask = remapMpasClimatologySubtask + self.remapObsClimatologySubtask = remapObsClimatologySubtask + subtaskName = 'plot{}_{}'.format(season, comparisonGridName) + config = parentTask.config + taskName = parentTask.taskName + tags = parentTask.tags + + # call the constructor from the base class (AnalysisTask) + super(PlotClimatologyMapSubtask, self).__init__( + config=config, taskName=taskName, subtaskName=subtaskName, + componentName='ocean', tags=tags) + + # this task should not run until the remapping subtasks are done, since + # it relies on data from those subtasks + self.run_after(remapMpasClimatologySubtask) + self.run_after(remapObsClimatologySubtask) + # }}} + + def set_plot_info(self, outFileLabel, fieldNameInTitle, mpasFieldName, + obsFieldName, observationTitleLabel, unitsLabel, + imageCaption, galleryGroup, groupSubtitle, groupLink, + galleryName): # {{{ + """ + Store attributes related to plots, plot file names and HTML output. + + Parameters + ---------- + outFileLabel : str + The prefix on each plot and associated XML file + + fieldNameInTitle : str + The name of the field being plotted, as used in the plot title + + mpasFieldName : str + The name of the variable in the MPAS timeSeriesStatsMonthly output + + obsFieldName : str + The name of the variable to use from the observations file + + observationTitleLabel : str + the title of the observations subplot + + unitsLabel : str + the units of the plotted field, to be displayed on color bars + + imageCaption : str + the caption when mousing over the plot or displaying it full + screen + + galleryGroup : str + the name of the group of galleries in which this plot belongs + + groupSubtitle : str + the subtitle of the group in which this plot belongs (or blank + if none) + + groupLink : str + a short name (with no spaces) for the link to the gallery group + + galleryName : str + the name of the gallery in which this plot belongs + + Authors + ------- + Xylar Asay-Davis + """ + self.outFileLabel = outFileLabel + self.fieldNameInTitle = fieldNameInTitle + self.mpasFieldName = mpasFieldName + self.obsFieldName = obsFieldName + self.observationTitleLabel = observationTitleLabel + self.unitsLabel = unitsLabel + + # xml/html related variables + self.imageCaption = imageCaption + self.galleryGroup = galleryGroup + self.groupSubtitle = groupSubtitle + self.groupLink = groupLink + self.galleryName = galleryName + # }}} + + def setup_and_check(self): # {{{ + """ + Perform steps to set up the analysis and check for errors in the setup. + + Authors + ------- + Xylar Asay-Davis + """ + # first, call setup_and_check from the base class (AnalysisTask), + # which will perform some common setup, including storing: + # self.runDirectory , self.historyDirectory, self.plotsDirectory, + # self.namelist, self.runStreams, self.historyStreams, + # self.calendar + super(PlotClimatologyMapSubtask, self).setup_and_check() + + config = self.config + self.startYear = config.getint('climatology', 'startYear') + self.endYear = config.getint('climatology', 'endYear') + self.startDate = config.get('climatology', 'startDate') + self.endDate = config.get('climatology', 'endDate') + + mainRunName = config.get('runs', 'mainRunName') + + self.xmlFileNames = [] + self.filePrefixes = {} + + if self.comparisonGridName == 'latlon': + self.filePrefix = '{}_{}_{}_years{:04d}-{:04d}'.format( + self.outFileLabel, mainRunName, + self.season, self.startYear, self.endYear) + else: + self.filePrefix = '{}_{}_{}_{}_years{:04d}-{:04d}'.format( + self.outFileLabel, self.comparisonGridName, + mainRunName, self.season, self.startYear, self.endYear) + self.xmlFileNames.append('{}/{}.xml'.format(self.plotsDirectory, + self.filePrefix)) + # }}} + + def run_task(self): # {{{ + """ + Plots a comparison of ACME/MPAS output to SST, MLD or SSS observations + + Authors + ------- + Luke Van Roekel, Xylar Asay-Davis, Milena Veneziani + """ + + season = self.season + comparisonGridName = self.comparisonGridName + self.logger.info("\nPlotting 2-d maps of {} climatologies for {} on " + "the {} grid...".format(self.fieldNameInTitle, + season, comparisonGridName)) + + # first read the model climatology + remappedFileName = self.remapMpasClimatologySubtask.get_file_name( + season=season, stage='remapped', + comparisonGridName=comparisonGridName) + + remappedModelClimatology = xr.open_dataset(remappedFileName) + + # now the observations + remappedFileName = self.remapObsClimatologySubtask.get_file_name( + stage='remapped', season=season, + comparisonGridName=comparisonGridName) + + remappedObsClimatology = xr.open_dataset(remappedFileName) + + if self.comparisonGridName == 'latlon': + self._plot_latlon(remappedModelClimatology, remappedObsClimatology) + elif self.comparisonGridName == 'antarctic': + self._plot_antarctic(remappedModelClimatology, + remappedObsClimatology) + # }}} + + def _plot_latlon(self, remappedModelClimatology, remappedObsClimatology): + # {{{ + """ plotting a global lat-lon data set """ + + season = self.season + config = self.config + configSectionName = self.taskName + + mainRunName = config.get('runs', 'mainRunName') + + (colormapResult, colorbarLevelsResult) = setup_colormap( + config, configSectionName, suffix='Result') + (colormapDifference, colorbarLevelsDifference) = setup_colormap( + config, configSectionName, suffix='Difference') + + modelOutput = \ + remappedModelClimatology[self.mpasFieldName].values + + lon = remappedModelClimatology['lon'].values + lat = remappedModelClimatology['lat'].values + + lonTarg, latTarg = np.meshgrid(lon, lat) + + observations = remappedObsClimatology[self.obsFieldName].values + + bias = modelOutput - observations + + 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, + observations, + bias, + colormapResult, + colorbarLevelsResult, + colormapDifference, + colorbarLevelsDifference, + fileout=outFileName, + title=title, + modelTitle='{}'.format(mainRunName), + obsTitle=self.observationTitleLabel, + diffTitle='Model-Observations', + 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=season, + imageDescription=caption, + imageCaption=caption) + + # }}} + + def _plot_antarctic(self, remappedModelClimatology, + remappedObsClimatology): # {{{ + """ plotting an Antarctic data set """ + + season = self.season + comparisonGridName = self.comparisonGridName + config = self.config + configSectionName = self.taskName + + 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 = \ + remappedModelClimatology[self.mpasFieldName].values + + observations = remappedObsClimatology[self.obsFieldName].values + + bias = modelOutput - observations + + 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) + + if config.has_option(configSectionName, 'colormapIndicesResult'): + colorMapType = 'indexed' + elif config.has_option(configSectionName, 'normTypeResult'): + colorMapType = 'norm' + else: + raise ValueError('config section {} contains neither the info' + 'for an indexed color map nor for computing a ' + 'norm'.format(configSectionName)) + + plot_polar_projection_comparison( + config, + x, + y, + self.landMask, + modelOutput, + observations, + bias, + fileout=outFileName, + colorMapSectionName=configSectionName, + colorMapType=colorMapType, + title=title, + modelTitle='{}'.format(mainRunName), + obsTitle=self.observationTitleLabel, + diffTitle='Model - Observations', + 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=season, + imageDescription=caption, + imageCaption=caption) + + # }}} + # }}} + + +# vim: foldmethod=marker ai ts=4 sts=4 et sw=4 ft=python diff --git a/mpas_analysis/sea_ice/__init__.py b/mpas_analysis/sea_ice/__init__.py index ff456b4bf..bc745d9d9 100644 --- a/mpas_analysis/sea_ice/__init__.py +++ b/mpas_analysis/sea_ice/__init__.py @@ -1,2 +1,3 @@ -from .climatology_map import ClimatologyMapSeaIceConc, ClimatologyMapSeaIceThick -from .time_series import TimeSeriesSeaIce \ No newline at end of file +from .climatology_map_sea_ice_conc import ClimatologyMapSeaIceConc +from .climatology_map_sea_ice_thick import ClimatologyMapSeaIceThick +from .time_series import TimeSeriesSeaIce diff --git a/mpas_analysis/sea_ice/climatology_map.py b/mpas_analysis/sea_ice/climatology_map.py deleted file mode 100644 index 33a8672aa..000000000 --- a/mpas_analysis/sea_ice/climatology_map.py +++ /dev/null @@ -1,591 +0,0 @@ - -from __future__ import absolute_import, division, print_function, \ - unicode_literals - -import os -import os.path - -import numpy.ma as ma -import numpy as np - -import xarray as xr - -from ..shared.climatology import get_comparison_descriptor, \ - get_remapper, get_observation_climatology_file_names, \ - remap_and_write_climatology, RemapMpasClimatologySubtask - -from ..shared.grid import LatLonGridDescriptor - -from ..shared.plot.plotting import plot_polar_comparison, \ - setup_colormap - -from ..shared.io.utility import build_config_full_path, make_directories -from ..shared.io import write_netcdf -from ..shared.html import write_image_xml - -from .sea_ice_analysis_task import SeaIceAnalysisTask - - -class ClimatologyMapSeaIce(SeaIceAnalysisTask): - """ - General comparison of 2-d model fields against data. Currently only - supports sea ice concentration and sea ice thickness - - Attributes - ---------- - remapClimatologySubtask : ``RemapMpasClimatologySubtask`` - The subtask that remaps the climatologies this task will plot. - The ``remapClimatologySubtask`` is needed to determine the file names - of the climatology output. - - mpasClimatologyTask : ``MpasClimatologyTask`` - The task that produced the climatology to be remapped and plotted - - - Authors - ------- - Xylar Asay-Davis, Milena Veneziani - """ - - def __init__(self, config, mpasClimatologyTask, taskName, tags): # {{{ - ''' - Construct one analysis subtask for each plot (i.e. each season and - comparison grid) and a subtask for computing climatologies. - - Parameters - ---------- - config : instance of MpasAnalysisConfigParser - Contains configuration options - - mpasClimatologyTask : ``MpasClimatologyTask`` - The task that produced the climatology to be remapped and plotted - - taskName : str - The name of the task, typically the same as the subclass name - except starting with lowercase (e.g. 'climatologyMapSST' for class - 'ClimatologyMapSST') - - tags : list of str - Tags used to describe the task (e.g. 'climatology', - 'horizontalMap'). These are used to determine which tasks are - generated (e.g. 'all_transect' or 'no_climatology' in the - 'generate' flags) - - Authors - ------- - Xylar Asay-Davis - - ''' - # call the constructor from the base class (AnalysisTask) - super(ClimatologyMapSeaIce, self).__init__(config=config, - taskName=taskName, - componentName='seaIce', - tags=tags) - - self.mpasClimatologyTask = mpasClimatologyTask - - sectionName = self.taskName - - # read in what seasons we want to plot - seasons = config.getExpression(sectionName, 'seasons') - - if len(seasons) == 0: - raise ValueError('config section {} does not contain valid list ' - 'of seasons'.format(sectionName)) - - # the variable self.mpasFieldName will be added to mpasClimatologyTask - # along with the seasons. - self.remapClimatologySubtask = RemapMpasClimatologySubtask( - mpasClimatologyTask=self.mpasClimatologyTask, - parentTask=self, - climatologyName='{}{}'.format(self.fieldName, self.hemisphere), - variableList=[self.mpasFieldName], - seasons=seasons, - iselValues=self.iselValues) - - # }}} - - def setup_and_check(self): # {{{ - """ - Perform steps to set up the analysis and check for errors in the setup. - - Authors - ------- - Xylar Asay-Davis - """ - # first, call setup_and_check from the base class (SeaIceAnalysisTask), - # which will perform some common setup - super(ClimatologyMapSeaIce, self).setup_and_check() - - self.startYear = self.mpasClimatologyTask.startYear - self.startDate = self.mpasClimatologyTask.startDate - self.endYear = self.mpasClimatologyTask.endYear - self.endDate = self.mpasClimatologyTask.endDate - - mainRunName = self.config.get('runs', 'mainRunName') - - # we set up the remapper here because ESFM_RegridWeightGen seems to - # have trouble if it runs in another process (or in several at once) - self._setup_obs_remapper() - - self.xmlFileNames = [] - - for info in self.obsAndPlotInfo: - season = info['season'] - outFileLabel = info['outFileLabel'] - - filePrefix = '{}_{}_{}_years{:04d}-{:04d}'.format( - outFileLabel, mainRunName, - season, self.startYear, self.endYear) - self.xmlFileNames.append('{}/{}.xml'.format(self.plotsDirectory, - filePrefix)) - info['filePrefix'] = filePrefix - - # make the mapping directory, because doing so within each process - # seems to be giving ESMF_RegridWeightGen some trouble - mappingSubdirectory = \ - build_config_full_path(self.config, 'output', - 'mappingSubdirectory') - make_directories(mappingSubdirectory) - - # }}} - - 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 - """ - - self.logger.info("\nPlotting 2-d maps of {} climatologies...".format( - self.fieldNameInTitle)) - - config = self.config - - mainRunName = config.get('runs', 'mainRunName') - startYear = self.startYear - endYear = self.endYear - - hemisphere = self.hemisphere - sectionName = self.sectionName - vertical = config.getboolean(sectionName, 'vertical') - if hemisphere == 'NH': - plotProjection = 'npstere' - else: - plotProjection = 'spstere' - - for info in self.obsAndPlotInfo: - season = info['season'] - - (colormapResult, colorbarLevelsResult) = setup_colormap( - config, sectionName, suffix='Result') - (colormapDifference, colorbarLevelsDifference) = setup_colormap( - config, sectionName, suffix='Difference') - - referenceLongitude = config.getfloat(sectionName, - 'referenceLongitude') - minimumLatitude = config.getfloat(sectionName, - 'minimumLatitude') - - fieldName = '{}{}'.format(self.mpasFieldName, hemisphere) - - remappedFileName = self.remapClimatologySubtask.get_file_name( - season=season, stage='remapped', comparisonGridName='latlon') - 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) - - obsFileName = info['obsFileName'] - - if not os.path.isfile(obsFileName): - raise OSError('Obs file {} not found.'.format( - obsFileName)) - - fieldName = '{}{}'.format(self.obsFieldName, hemisphere) - - (obsClimatologyFileName, obsRemappedFileName) = \ - get_observation_climatology_file_names( - config=config, fieldName=fieldName, - monthNames=season, componentName=self.componentName, - remapper=self.obsRemapper) - - if not os.path.exists(obsRemappedFileName): - - # load the observations the first time - seasonalClimatology = self._build_observational_dataset( - obsFileName) - write_netcdf(seasonalClimatology, obsClimatologyFileName) - - if self.obsRemapper is None: - # no need to remap because the observations are on the - # comparison grid already - remappedClimatology = seasonalClimatology - else: - remappedClimatology = \ - remap_and_write_climatology( - config, seasonalClimatology, - obsClimatologyFileName, - obsRemappedFileName, self.obsRemapper, - logger=self.logger) - - else: - - remappedClimatology = xr.open_dataset(obsRemappedFileName) - - observations = remappedClimatology[self.obsFieldName].values - if self.maskValue is not None: - observations = ma.masked_values(observations, self.maskValue) - - difference = modelOutput - observations - - startYear = self.startYear - endYear = self.endYear - observationTitleLabel = info['observationTitleLabel'] - filePrefix = info['filePrefix'] - title = '{} ({}, years {:04d}-{:04d})'.format( - self.fieldNameInTitle, season, startYear, endYear) - fileout = '{}/{}.png'.format(self.plotsDirectory, filePrefix) - plot_polar_comparison( - config, - lonTarg, - latTarg, - modelOutput, - observations, - difference, - colormapResult, - colorbarLevelsResult, - colormapDifference, - colorbarLevelsDifference, - title=title, - fileout=fileout, - plotProjection=plotProjection, - latmin=minimumLatitude, - lon0=referenceLongitude, - modelTitle=mainRunName, - obsTitle=observationTitleLabel, - diffTitle='Model-Observations', - cbarlabel=self.unitsLabel, - vertical=vertical) - - galleryName = info['galleryName'] - imageDescription = info['imageDescription'] - imageCaption = info['imageCaption'] - write_image_xml( - config, - filePrefix, - componentName='Sea Ice', - componentSubdirectory='sea_ice', - galleryGroup=self.galleryGroup, - groupSubtitle=self.groupSubtitle, - groupLink=self.groupLink, - gallery=galleryName, - thumbnailDescription=season, - imageDescription=imageDescription, - imageCaption=imageCaption) - # }}} - - def _setup_obs_remapper(self): # {{{ - """ - Set up the remapper for remapping from the MPAS to the comparison - grid. - - Authors - ------- - Xylar Asay-Davis - """ - config = self.config - hemisphere = self.hemisphere - - info = self.obsAndPlotInfo[0] - - fieldName = '{}{}'.format(self.mpasFieldName, hemisphere) - - obsFileName = info['obsFileName'] - - obsDescriptor = LatLonGridDescriptor.read(fileName=obsFileName, - latVarName='t_lat', - lonVarName='t_lon') - - comparisonDescriptor = get_comparison_descriptor( - config=config, comparisonGridName='latlon') - - fieldName = '{}{}'.format(self.obsFieldName, hemisphere) - self.obsRemapper = get_remapper( - config=config, - sourceDescriptor=obsDescriptor, - comparisonDescriptor=comparisonDescriptor, - mappingFilePrefix='map_obs_{}'.format(fieldName), - method=config.get('seaIceObservations', - 'interpolationMethod'), - logger=self.logger) - # }}} - # }}} - - -class ClimatologyMapSeaIceConc(ClimatologyMapSeaIce): # {{{ - """ - An analysis task for comparison of sea ice concentration against - observations - - Authors - ------- - Luke Van Roekel, Xylar Asay-Davis, Milena Veneziani - """ - def __init__(self, config, mpasClimatologyTask, hemisphere): - # {{{ - """ - Construct the analysis task. - - Parameters - ---------- - config : instance of MpasAnalysisConfigParser - Contains configuration options - - mpasClimatologyTask : ``MpasClimatologyTask`` - The task that produced the climatology to be remapped and plotted - - hemisphere : {'NH', 'SH'} - The hemisphere to plot - - Authors - ------- - Xylar Asay-Davis - """ - taskName = 'climatologyMapSeaIceConc{}'.format(hemisphere) - - self.sectionName = taskName - - self.fieldName = 'seaIceConc' - self.hemisphere = hemisphere - self.fieldNameInTitle = 'Sea ice concentration' - self.seasons = config.getExpression(self.sectionName, 'seasons') - self.observationPrefixes = config.getExpression(self.sectionName, - 'observationPrefixes') - - self.mpasFieldName = 'timeMonthly_avg_iceAreaCell' - self.iselValues = None - - tags = ['climatology', 'horizontalMap', self.fieldName] - - # call the constructor from the base class (AnalysisTask) - super(ClimatologyMapSeaIceConc, self).__init__( - config=config, mpasClimatologyTask=mpasClimatologyTask, - taskName=taskName, tags=tags) - - # }}} - - def setup_and_check(self): # {{{ - """ - Perform steps to set up the analysis and check for errors in the setup. - - Authors - ------- - Xylar Asay-Davis - """ - hemisphere = self.hemisphere - if hemisphere == 'NH': - hemisphereLong = 'Northern' - else: - hemisphereLong = 'Southern' - - self.obsAndPlotInfo = [] - for prefix in self.observationPrefixes: - for season in self.seasons: - localDict = {} - localDict['season'] = season - localDict['obsFileName'] = \ - build_config_full_path( - self.config, 'seaIceObservations', - 'concentration{}{}_{}'.format(prefix, - hemisphere, - season)) - localDict['observationTitleLabel'] = \ - 'Observations (SSM/I {})'.format(prefix) - localDict['outFileLabel'] = \ - 'iceconc{}{}'.format(prefix, hemisphere) - - localDict['galleryName'] = \ - 'Observations: SSM/I {}'.format(prefix) - - localDict['imageDescription'] = \ - '{} Climatology Map of {}-Hemisphere Sea-Ice ' \ - 'Concentration'.format(season, hemisphereLong) - - localDict['imageCaption'] = \ - '{}.
Observations: SSM/I {}'.format( - localDict['imageDescription'], prefix) - - self.obsAndPlotInfo.append(localDict) - - self.obsFieldName = 'AICE' - - self.unitsLabel = 'fraction' - - self.maskValue = None - - # variables for XML and webpages - self.galleryGroup = '{}-Hemisphere Sea-Ice Concentration'.format( - hemisphereLong) - self.groupSubtitle = None - self.groupLink = '{}_conc'.format(hemisphere.lower()) - - # call setup_and_check from the base class - # (ClimatologyMapSeaIce), which will perform some common setup, - # including storing: - # self.runDirectory , self.historyDirectory, self.plotsDirectory, - # self.namelist, self.runStreams, self.historyStreams, - # self.calendar, self.namelistMap, self.streamMap, self.variableMap - super(ClimatologyMapSeaIceConc, self).setup_and_check() - - # }}} - - def _build_observational_dataset(self, obsFileName): # {{{ - ''' - read in the data sets for observations, and possibly rename some - variables and dimensions - - Authors - ------- - Xylar Asay-Davis - ''' - - dsObs = xr.open_mfdataset(obsFileName) - - return dsObs # }}} - - # }}} - - -class ClimatologyMapSeaIceThick(ClimatologyMapSeaIce): # {{{ - """ - An analysis task for comparison of sea ice thickness against observations - - Authors - ------- - Luke Van Roekel, Xylar Asay-Davis, Milena Veneziani - """ - def __init__(self, config, mpasClimatologyTask, hemisphere): - # {{{ - """ - Construct the analysis task. - - Parameters - ---------- - config : instance of MpasAnalysisConfigParser - Contains configuration options - - mpasClimatologyTask : ``MpasClimatologyTask`` - The task that produced the climatology to be remapped and plotted - - hemisphere : {'NH', 'SH'} - The hemisphere to plot - - Authors - ------- - Xylar Asay-Davis - """ - taskName = 'climatologyMapSeaIceThick{}'.format(hemisphere) - - self.sectionName = taskName - - self.fieldName = 'seaIceThick' - self.hemisphere = hemisphere - self.fieldNameInTitle = 'Sea ice thickness' - self.seasons = config.getExpression(self.sectionName, 'seasons') - - self.mpasFieldName = 'timeMonthly_avg_iceVolumeCell' - self.iselValues = None - - tags = ['climatology', 'horizontalMap', self.fieldName] - - # call the constructor from the base class (AnalysisTask) - super(ClimatologyMapSeaIceThick, self).__init__( - config=config, mpasClimatologyTask=mpasClimatologyTask, - taskName=taskName, tags=tags) - - # }}} - - def setup_and_check(self): # {{{ - """ - Perform steps to set up the analysis and check for errors in the setup. - - Authors - ------- - Xylar Asay-Davis - """ - hemisphere = self.hemisphere - - if hemisphere == 'NH': - hemisphereLong = 'Northern' - else: - hemisphereLong = 'Southern' - - self.obsAndPlotInfo = [] - for season in self.seasons: - localDict = {} - localDict['season'] = season - localDict['obsFileName'] = \ - build_config_full_path( - self.config, 'seaIceObservations', - 'thickness{}_{}'.format(hemisphere, season)) - localDict['observationTitleLabel'] = 'Observations (ICESat)' - localDict['outFileLabel'] = 'icethick{}'.format(hemisphere) - - localDict['galleryName'] = 'Observations: ICESat' - - localDict['imageDescription'] = \ - '{} Climatology Map of {}-Hemisphere Sea-Ice ' \ - 'Thickness.'.format(season, hemisphereLong) - - localDict['imageCaption'] = localDict['imageDescription'] - - self.obsAndPlotInfo.append(localDict) - - self.obsFieldName = 'HI' - - self.unitsLabel = 'm' - - self.maskValue = 0 - - # variables for XML and webpages - self.galleryGroup = '{}-Hemisphere Sea-Ice Thicknesss'.format( - hemisphereLong) - self.groupSubtitle = None - self.groupLink = '{}_thick'.format(hemisphere.lower()) - - # call setup_and_check from the base class - # (ClimatologyMapSeaIce), which will perform some common setup, - # including storing: - # self.runDirectory , self.historyDirectory, self.plotsDirectory, - # self.namelist, self.runStreams, self.historyStreams, - # self.calendar, self.namelistMap, self.streamMap, self.variableMap - super(ClimatologyMapSeaIceThick, self).setup_and_check() - - # }}} - - def _build_observational_dataset(self, obsFileName): # {{{ - ''' - read in the data sets for observations, and possibly rename some - variables and dimensions - - Authors - ------- - Xylar Asay-Davis - ''' - - dsObs = xr.open_mfdataset(obsFileName) - - return dsObs # }}} - - # }}} - -# vim: foldmethod=marker ai ts=4 sts=4 et sw=4 ft=python diff --git a/mpas_analysis/sea_ice/climatology_map_sea_ice_conc.py b/mpas_analysis/sea_ice/climatology_map_sea_ice_conc.py new file mode 100644 index 000000000..9f26dfc70 --- /dev/null +++ b/mpas_analysis/sea_ice/climatology_map_sea_ice_conc.py @@ -0,0 +1,215 @@ + +from __future__ import absolute_import, division, print_function, \ + unicode_literals + +import xarray as xr + +from ..shared import AnalysisTask + +from ..shared.climatology import RemapMpasClimatologySubtask, \ + RemapObservedClimatologySubtask + +from .plot_climatology_map_subtask import PlotClimatologyMapSubtask + +from ..shared.io.utility import build_config_full_path + +from ..shared.grid import LatLonGridDescriptor + + +class ClimatologyMapSeaIceConc(AnalysisTask): # {{{ + """ + An analysis task for comparison of sea ice concentration against + observations + + Authors + ------- + Luke Van Roekel, Xylar Asay-Davis, Milena Veneziani + """ + def __init__(self, config, mpasClimatologyTask, hemisphere): + # {{{ + """ + Construct the analysis task. + + Parameters + ---------- + config : instance of MpasAnalysisConfigParser + Contains configuration options + + mpasClimatologyTask : ``MpasClimatologyTask`` + The task that produced the climatology to be remapped and plotted + + hemisphere : {'NH', 'SH'} + The hemisphere to plot + + Authors + ------- + Xylar Asay-Davis + """ + taskName = 'climatologyMapSeaIceConc{}'.format(hemisphere) + + self.fieldName = 'seaIceConc' + # call the constructor from the base class (AnalysisTask) + super(ClimatologyMapSeaIceConc, self).__init__( + config=config, taskName=taskName, + componentName='seaIce', + tags=['climatology', 'horizontalMap', self.fieldName]) + + mpasFieldName = 'timeMonthly_avg_iceAreaCell' + iselValues = None + + sectionName = taskName + + if hemisphere == 'NH': + hemisphereLong = 'Northern' + else: + hemisphereLong = 'Southern' + + obsFieldName = 'seaIceConc' + + # read in what seasons we want to plot + seasons = config.getExpression(sectionName, 'seasons') + + if len(seasons) == 0: + raise ValueError('config section {} does not contain valid list ' + 'of seasons'.format(sectionName)) + + comparisonGridNames = config.getExpression(sectionName, + 'comparisonGrids') + + if len(comparisonGridNames) == 0: + raise ValueError('config section {} does not contain valid list ' + 'of comparison grids'.format(sectionName)) + + # the variable self.mpasFieldName will be added to mpasClimatologyTask + # along with the seasons. + remapClimatologySubtask = RemapMpasClimatologySubtask( + mpasClimatologyTask=mpasClimatologyTask, + parentTask=self, + climatologyName='{}{}'.format(self.fieldName, hemisphere), + variableList=[mpasFieldName], + comparisonGridNames=comparisonGridNames, + seasons=seasons, + iselValues=iselValues) + + observationPrefixes = config.getExpression(sectionName, + 'observationPrefixes') + for prefix in observationPrefixes: + for season in seasons: + observationTitleLabel = \ + 'Observations (SSM/I {})'.format(prefix) + + obsFileName = build_config_full_path( + config, 'seaIceObservations', + 'concentration{}{}_{}'.format(prefix, + hemisphere, + season)) + + remapObservationsSubtask = RemapObservedConcClimatology( + parentTask=self, seasons=[season], + fileName=obsFileName, + outFilePrefix='{}{}{}_{}'.format(obsFieldName, prefix, + hemisphere, season), + comparisonGridNames=comparisonGridNames, + subtaskName='remapObservations_{}{}'.format(prefix, + season)) + self.add_subtask(remapObservationsSubtask) + for comparisonGridName in comparisonGridNames: + + imageDescription = \ + '{} Climatology Map of {}-Hemisphere Sea-Ice ' \ + 'Concentration'.format(season, hemisphereLong) + imageCaption = '{}.
Observations: SSM/I {}'.format( + imageDescription, prefix) + galleryGroup = \ + '{}-Hemisphere Sea-Ice Concentration'.format( + hemisphereLong) + # make a new subtask for this season and comparison grid + subtask = PlotClimatologyMapSubtask( + self, hemisphere, season, comparisonGridName, + remapClimatologySubtask, remapObservationsSubtask, + subtaskSuffix=prefix) + + subtask.set_plot_info( + outFileLabel='iceconc{}{}'.format(prefix, + hemisphere), + fieldNameInTitle='Sea ice concentration', + mpasFieldName=mpasFieldName, + obsFieldName=obsFieldName, + observationTitleLabel=observationTitleLabel, + unitsLabel=r'fraction', + imageDescription=imageDescription, + imageCaption=imageCaption, + galleryGroup=galleryGroup, + groupSubtitle=None, + groupLink='{}_conc'.format(hemisphere.lower()), + galleryName='Observations: SSM/I {}'.format( + prefix)) + + self.add_subtask(subtask) + # }}} + # }}} + + +class RemapObservedConcClimatology(RemapObservedClimatologySubtask): # {{{ + """ + A subtask for reading and remapping sea ice concentration observations + + Authors + ------- + Xylar Asay-Davis + """ + + def get_observation_descriptor(self, fileName): # {{{ + ''' + get a MeshDescriptor for the observation grid + + Parameters + ---------- + fileName : str + observation file name describing the source grid + + Returns + ------- + obsDescriptor : ``MeshDescriptor`` + The descriptor for the observation grid + + Authors + ------- + Xylar Asay-Davis + ''' + + # create a descriptor of the observation grid using the lat/lon + # coordinates + obsDescriptor = LatLonGridDescriptor.read(fileName=fileName, + latVarName='t_lat', + lonVarName='t_lon') + return obsDescriptor # }}} + + def build_observational_dataset(self, fileName): # {{{ + ''' + read in the data sets for observations, and possibly rename some + variables and dimensions + + Parameters + ---------- + fileName : str + observation file name + + Returns + ------- + dsObs : ``xarray.Dataset`` + The observational dataset + + Authors + ------- + Xylar Asay-Davis + ''' + + dsObs = xr.open_dataset(fileName) + dsObs.rename({'AICE': 'seaIceConc'}, inplace=True) + return dsObs + # }}} + # }}} + + +# vim: foldmethod=marker ai ts=4 sts=4 et sw=4 ft=python diff --git a/mpas_analysis/sea_ice/climatology_map_sea_ice_thick.py b/mpas_analysis/sea_ice/climatology_map_sea_ice_thick.py new file mode 100644 index 000000000..09719cef1 --- /dev/null +++ b/mpas_analysis/sea_ice/climatology_map_sea_ice_thick.py @@ -0,0 +1,204 @@ + +from __future__ import absolute_import, division, print_function, \ + unicode_literals + +import xarray as xr + +from ..shared import AnalysisTask + +from ..shared.climatology import RemapMpasClimatologySubtask, \ + RemapObservedClimatologySubtask + +from .plot_climatology_map_subtask import PlotClimatologyMapSubtask + +from ..shared.io.utility import build_config_full_path + +from ..shared.grid import LatLonGridDescriptor + + +class ClimatologyMapSeaIceThick(AnalysisTask): # {{{ + """ + An analysis task for comparison of sea ice thickness against + observations + + Authors + ------- + Luke Van Roekel, Xylar Asay-Davis, Milena Veneziani + """ + def __init__(self, config, mpasClimatologyTask, hemisphere): + # {{{ + """ + Construct the analysis task. + + Parameters + ---------- + config : instance of MpasAnalysisConfigParser + Contains configuration options + + mpasClimatologyTask : ``MpasClimatologyTask`` + The task that produced the climatology to be remapped and plotted + + hemisphere : {'NH', 'SH'} + The hemisphere to plot + + Authors + ------- + Xylar Asay-Davis + """ + taskName = 'climatologyMapSeaIceThick{}'.format(hemisphere) + + self.fieldName = 'seaIceThick' + # call the constructor from the base class (AnalysisTask) + super(ClimatologyMapSeaIceThick, self).__init__( + config=config, taskName=taskName, + componentName='seaIce', + tags=['climatology', 'horizontalMap', self.fieldName]) + + mpasFieldName = 'timeMonthly_avg_iceVolumeCell' + iselValues = None + + sectionName = taskName + + if hemisphere == 'NH': + hemisphereLong = 'Northern' + else: + hemisphereLong = 'Southern' + + obsFieldName = 'seaIceThick' + + # read in what seasons we want to plot + seasons = config.getExpression(sectionName, 'seasons') + + if len(seasons) == 0: + raise ValueError('config section {} does not contain valid list ' + 'of seasons'.format(sectionName)) + + comparisonGridNames = config.getExpression(sectionName, + 'comparisonGrids') + + if len(comparisonGridNames) == 0: + raise ValueError('config section {} does not contain valid list ' + 'of comparison grids'.format(sectionName)) + + # the variable self.mpasFieldName will be added to mpasClimatologyTask + # along with the seasons. + remapClimatologySubtask = RemapMpasClimatologySubtask( + mpasClimatologyTask=mpasClimatologyTask, + parentTask=self, + climatologyName='{}{}'.format(self.fieldName, hemisphere), + variableList=[mpasFieldName], + comparisonGridNames=comparisonGridNames, + seasons=seasons, + iselValues=iselValues) + + for season in seasons: + + obsFileName = build_config_full_path( + config, 'seaIceObservations', + 'thickness{}_{}'.format(hemisphere, season)) + + remapObservationsSubtask = RemapObservedThickClimatology( + parentTask=self, seasons=[season], + fileName=obsFileName, + outFilePrefix='{}{}_{}'.format(obsFieldName, hemisphere, + season), + comparisonGridNames=comparisonGridNames, + subtaskName='remapObservations{}'.format(season)) + self.add_subtask(remapObservationsSubtask) + for comparisonGridName in comparisonGridNames: + + imageDescription = \ + '{} Climatology Map of {}-Hemisphere Sea-Ice ' \ + 'Thickness.'.format(season, hemisphereLong) + imageCaption = imageDescription + galleryGroup = \ + '{}-Hemisphere Sea-Ice Concentration'.format( + hemisphereLong) + # make a new subtask for this season and comparison grid + subtask = PlotClimatologyMapSubtask( + self, hemisphere, season, comparisonGridName, + remapClimatologySubtask, remapObservationsSubtask) + + subtask.set_plot_info( + outFileLabel='icethick{}'.format(hemisphere), + fieldNameInTitle='Sea ice thickness', + mpasFieldName=mpasFieldName, + obsFieldName=obsFieldName, + observationTitleLabel='Observations (ICESat)', + unitsLabel=r'm', + imageDescription=imageDescription, + imageCaption=imageCaption, + galleryGroup=galleryGroup, + groupSubtitle=None, + groupLink='{}_thick'.format(hemisphere.lower()), + galleryName='Observations: ICESat', + maskValue=0) + + self.add_subtask(subtask) + # }}} + # }}} + + +class RemapObservedThickClimatology(RemapObservedClimatologySubtask): # {{{ + """ + A subtask for reading and remapping sea ice thickness observations + + Authors + ------- + Xylar Asay-Davis + """ + + def get_observation_descriptor(self, fileName): # {{{ + ''' + get a MeshDescriptor for the observation grid + + Parameters + ---------- + fileName : str + observation file name describing the source grid + + Returns + ------- + obsDescriptor : ``MeshDescriptor`` + The descriptor for the observation grid + + Authors + ------- + Xylar Asay-Davis + ''' + + # create a descriptor of the observation grid using the lat/lon + # coordinates + obsDescriptor = LatLonGridDescriptor.read(fileName=fileName, + latVarName='t_lat', + lonVarName='t_lon') + return obsDescriptor # }}} + + def build_observational_dataset(self, fileName): # {{{ + ''' + read in the data sets for observations, and possibly rename some + variables and dimensions + + Parameters + ---------- + fileName : str + observation file name + + Returns + ------- + dsObs : ``xarray.Dataset`` + The observational dataset + + Authors + ------- + Xylar Asay-Davis + ''' + + dsObs = xr.open_dataset(fileName) + dsObs.rename({'HI': 'seaIceThick'}, inplace=True) + return dsObs + # }}} + # }}} + + +# vim: foldmethod=marker ai ts=4 sts=4 et sw=4 ft=python diff --git a/mpas_analysis/sea_ice/plot_climatology_map_subtask.py b/mpas_analysis/sea_ice/plot_climatology_map_subtask.py new file mode 100644 index 000000000..acbf7fdbb --- /dev/null +++ b/mpas_analysis/sea_ice/plot_climatology_map_subtask.py @@ -0,0 +1,359 @@ + +from __future__ import absolute_import, division, print_function, \ + unicode_literals + +import numpy.ma as ma +import numpy as np + +import xarray as xr + +from ..shared import AnalysisTask + +from ..shared.plot.plotting import plot_polar_comparison, \ + setup_colormap + +from ..shared.html import write_image_xml + + +class PlotClimatologyMapSubtask(AnalysisTask): # {{{ + """ + An analysis task for plotting 2D model fields against observations. + + Attributes + ---------- + hemisphere : str + The hemisphere to plot + + season : str + A season (key in ``shared.constants.monthDictionary``) to be + plotted. + + comparisonGridName : {'latlon', 'antarctic'} + The name of the comparison grid to plot. + + remapMpasClimatologySubtask : ``RemapMpasClimatologySubtask`` + The subtask for remapping the MPAS climatology that this subtask + will plot + + remapObsClimatologySubtask : ``RemapObservedClimatologySubtask`` + The subtask for remapping the observational climatology that this + subtask will plot + + outFileLabel : str + The prefix on each plot and associated XML file + + fieldNameInTitle : str + The name of the field being plotted, as used in the plot title + + mpasFieldName : str + The name of the variable in the MPAS timeSeriesStatsMonthly output + + obsFieldName : str + The name of the variable to use from the observations file + + observationTitleLabel : str + the title of the observations subplot + + unitsLabel : str + the units of the plotted field, to be displayed on color bars + + imageCaption : str + the caption when mousing over the plot or displaying it full + screen + + galleryGroup : str + the name of the group of galleries in which this plot belongs + + groupSubtitle : str + the subtitle of the group in which this plot belongs (or blank + if none) + + groupLink : str + a short name (with no spaces) for the link to the gallery group + + galleryName : str + the name of the gallery in which this plot belongs + + Authors + ------- + Xylar Asay-Davis, Milena Veneziani + """ + + def __init__(self, parentTask, hemisphere, season, comparisonGridName, + remapMpasClimatologySubtask, remapObsClimatologySubtask, + subtaskSuffix=None): # {{{ + ''' + Construct one analysis subtask for each plot (i.e. each season and + comparison grid) and a subtask for computing climatologies. + + Parameters + ---------- + parentTask : ``AnalysisTask`` + The parent (master) task for this subtask + + hemisphere : str + The hemisphere to plot + + season : str + A season (key in ``shared.constants.monthDictionary``) to be + plotted. + + comparisonGridName : {'latlon'} + The name of the comparison grid to plot. + + remapMpasClimatologySubtask : ``RemapMpasClimatologySubtask`` + The subtask for remapping the MPAS climatology that this subtask + will plot + + remapObsClimatologySubtask : ``RemapObservedClimatologySubtask`` + The subtask for remapping the observational climatology that this + subtask will plot + + subtaskSuffix : str, optional + A suffix on the subtask to ensure that it is unique (e.g. the + observations being plotted) + + Authors + ------- + Xylar Asay-Davis + + ''' + + self.hemisphere = hemisphere + self.season = season + self.comparisonGridName = comparisonGridName + self.remapMpasClimatologySubtask = remapMpasClimatologySubtask + self.remapObsClimatologySubtask = remapObsClimatologySubtask + + subtaskName = 'plot{}'.format(season) + if subtaskSuffix is not None: + subtaskName = '{}_{}'.format(subtaskName, subtaskSuffix) + + config = parentTask.config + taskName = parentTask.taskName + tags = parentTask.tags + + # call the constructor from the base class (AnalysisTask) + super(PlotClimatologyMapSubtask, self).__init__( + config=config, taskName=taskName, subtaskName=subtaskName, + componentName='ocean', tags=tags) + + # this task should not run until the remapping subtasks are done, since + # it relies on data from those subtasks + self.run_after(remapMpasClimatologySubtask) + self.run_after(remapObsClimatologySubtask) + # }}} + + def set_plot_info(self, outFileLabel, fieldNameInTitle, mpasFieldName, + obsFieldName, observationTitleLabel, unitsLabel, + imageDescription, imageCaption, galleryGroup, + groupSubtitle, groupLink, galleryName, maskValue=None): + # {{{ + """ + Store attributes related to plots, plot file names and HTML output. + + Parameters + ---------- + outFileLabel : str + The prefix on each plot and associated XML file + + fieldNameInTitle : str + The name of the field being plotted, as used in the plot title + + mpasFieldName : str + The name of the variable in the MPAS timeSeriesStatsMonthly output + + obsFieldName : str + The name of the variable to use from the observations file + + observationTitleLabel : str + the title of the observations subplot + + unitsLabel : str + the units of the plotted field, to be displayed on color bars + + imageDescription : str + the caption when mousing over the plot + + imageCaption : str + the caption when displaying the plot full screen + + galleryGroup : str + the name of the group of galleries in which this plot belongs + + groupSubtitle : str + the subtitle of the group in which this plot belongs (or blank + if none) + + groupLink : str + a short name (with no spaces) for the link to the gallery group + + galleryName : str + the name of the gallery in which this plot belongs + + maskValue : float, optional + a value to mask out in plots + + Authors + ------- + Xylar Asay-Davis + """ + self.outFileLabel = outFileLabel + self.fieldNameInTitle = fieldNameInTitle + self.mpasFieldName = mpasFieldName + self.obsFieldName = obsFieldName + self.observationTitleLabel = observationTitleLabel + self.unitsLabel = unitsLabel + + # xml/html related variables + self.imageDescription = imageDescription + self.imageCaption = imageCaption + self.galleryGroup = galleryGroup + self.groupSubtitle = groupSubtitle + self.groupLink = groupLink + self.galleryName = galleryName + + self.maskValue = maskValue + # }}} + + def setup_and_check(self): # {{{ + """ + Perform steps to set up the analysis and check for errors in the setup. + + Authors + ------- + Xylar Asay-Davis + """ + # first, call setup_and_check from the base class (AnalysisTask), + # which will perform some common setup + super(PlotClimatologyMapSubtask, self).setup_and_check() + + config = self.config + self.startYear = config.getint('climatology', 'startYear') + self.endYear = config.getint('climatology', 'endYear') + self.startDate = config.get('climatology', 'startDate') + self.endDate = config.get('climatology', 'endDate') + + mainRunName = config.get('runs', 'mainRunName') + + self.xmlFileNames = [] + + self.filePrefix = '{}_{}_{}_years{:04d}-{:04d}'.format( + self.outFileLabel, mainRunName, + self.season, self.startYear, self.endYear) + self.xmlFileNames.append('{}/{}.xml'.format(self.plotsDirectory, + self.filePrefix)) + + # }}} + + 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 " + "{} from {} observations...".format( + self.fieldNameInTitle, + season, self.observationTitleLabel)) + + 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' + + (colormapResult, colorbarLevelsResult) = setup_colormap( + config, sectionName, suffix='Result') + (colormapDifference, colorbarLevelsDifference) = setup_colormap( + config, sectionName, suffix='Difference') + + referenceLongitude = config.getfloat(sectionName, + 'referenceLongitude') + minimumLatitude = config.getfloat(sectionName, + 'minimumLatitude') + + remappedFileName = self.remapMpasClimatologySubtask.get_file_name( + season=season, stage='remapped', + 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) + + remappedFileName = self.remapObsClimatologySubtask.get_file_name( + stage='remapped', season=season, + comparisonGridName=comparisonGridName) + + remappedObsClimatology = xr.open_dataset(remappedFileName) + + observations = remappedObsClimatology[self.obsFieldName].values + if self.maskValue is not None: + observations = ma.masked_values(observations, self.maskValue) + + difference = modelOutput - observations + + startYear = self.startYear + endYear = self.endYear + observationTitleLabel = self.observationTitleLabel + 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, + observations, + difference, + colormapResult, + colorbarLevelsResult, + colormapDifference, + colorbarLevelsDifference, + title=title, + fileout=fileout, + plotProjection=plotProjection, + latmin=minimumLatitude, + lon0=referenceLongitude, + modelTitle=mainRunName, + obsTitle=observationTitleLabel, + diffTitle='Model-Observations', + 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) + # }}} + + +# vim: foldmethod=marker ai ts=4 sts=4 et sw=4 ft=python diff --git a/mpas_analysis/sea_ice/sea_ice_analysis_task.py b/mpas_analysis/sea_ice/sea_ice_analysis_task.py deleted file mode 100644 index 6d05af1c1..000000000 --- a/mpas_analysis/sea_ice/sea_ice_analysis_task.py +++ /dev/null @@ -1,75 +0,0 @@ - -from __future__ import absolute_import, division, print_function, \ - unicode_literals - -from ..shared.analysis_task import AnalysisTask - -from ..shared.io import StreamsFile -from ..shared.io.utility import build_config_full_path - -from ..shared.timekeeping.utility import get_simulation_start_time - - -class SeaIceAnalysisTask(AnalysisTask): # {{{ - ''' - A base class for sea-ice analysis tasks that implements common setup - - Authors - ------- - Xylar Asay-Davis - ''' - - def setup_and_check(self): # {{{ - ''' - Perform steps to set up common to sea ice analysis, including - finding the simulation start time and an appropriate restart file - (from MPAS-SeaIce if possible, but from MPAS-Ocean as a fallback) - - Authors - ------- - Xylar Asay-Davis - ''' - - # first, call setup_and_check from the base class (AnalysisTask), - # which will perform some common setup, including storing: - # self.runDirectory , self.historyDirectory, self.plotsDirectory, - # self.namelist, self.runStreams, self.historyStreams, - # self.calendar - super(SeaIceAnalysisTask, self).setup_and_check() - - try: - self.simulationStartTime = get_simulation_start_time( - self.runStreams) - except IOError: - # try the ocean stream instead - runDirectory = build_config_full_path(self.config, 'input', - 'runSubdirectory') - oceanStreamsFileName = build_config_full_path( - self.config, 'input', 'oceanStreamsFileName') - oceanStreams = StreamsFile(oceanStreamsFileName, - streamsdir=runDirectory) - self.simulationStartTime = get_simulation_start_time(oceanStreams) - - try: - self.restartFileName = self.runStreams.readpath('restart')[0] - except ValueError: - # get an ocean restart file, since no sea-ice restart exists - try: - runDirectory = build_config_full_path(self.config, 'input', - 'runSubdirectory') - oceanStreamsFileName = build_config_full_path( - self.config, 'input', 'oceanStreamsFileName') - oceanStreams = StreamsFile(oceanStreamsFileName, - streamsdir=runDirectory) - self.restartFileName = oceanStreams.readpath('restart')[0] - except ValueError: - raise IOError('No MPAS-O or MPAS-Seaice restart file found: ' - 'need at least one restart file \n' - 'for sea ice analysis tasks') - - # }}} - - -# }}} - -# vim: foldmethod=marker ai ts=4 sts=4 et sw=4 ft=python diff --git a/mpas_analysis/sea_ice/time_series.py b/mpas_analysis/sea_ice/time_series.py index 5685438a4..d045daed1 100644 --- a/mpas_analysis/sea_ice/time_series.py +++ b/mpas_analysis/sea_ice/time_series.py @@ -4,7 +4,7 @@ import xarray as xr -from .sea_ice_analysis_task import SeaIceAnalysisTask +from ..shared import AnalysisTask from ..shared.plot.plotting import timeseries_analysis_plot, \ timeseries_analysis_plot_polar @@ -13,7 +13,7 @@ make_directories from ..shared.timekeeping.utility import date_to_days, days_to_datetime, \ - datetime_to_days + datetime_to_days, get_simulation_start_time from ..shared.timekeeping.MpasRelativeDelta import MpasRelativeDelta from ..shared.generalized_reader import open_multifile_dataset @@ -23,7 +23,7 @@ from ..shared.html import write_image_xml -class TimeSeriesSeaIce(SeaIceAnalysisTask): +class TimeSeriesSeaIce(AnalysisTask): """ Performs analysis of time series of sea-ice properties. @@ -54,7 +54,7 @@ def __init__(self, config, mpasTimeSeriesTask): # {{{ ------- Xylar Asay-Davis """ - # first, call the constructor from the base class (SeaIceAnalysisTask) + # first, call the constructor from the base class (AnalysisTask) super(TimeSeriesSeaIce, self).__init__( config=config, taskName='timeSeriesSeaIceAreaVol', @@ -80,7 +80,7 @@ def setup_and_check(self): # {{{ ------- Xylar Asay-Davis """ - # first, call setup_and_check from the base class (SeaIceAnalysisTask), + # first, call setup_and_check from the base class (AnalysisTask), # which will perform some common setup, including storing: # self.runDirectory , self.historyDirectory, self.plotsDirectory, # self.namelist, self.runStreams, self.historyStreams, @@ -102,6 +102,31 @@ def setup_and_check(self): # {{{ check_path_exists(config.get('seaIcePreprocessedReference', 'baseDirectory')) + # get a list of timeSeriesStatsMonthly output files from the streams + # file, reading only those that are between the start and end dates + streamName = 'timeSeriesStatsMonthlyOutput' + self.startDate = config.get('timeSeries', 'startDate') + self.endDate = config.get('timeSeries', 'endDate') + self.inputFiles = \ + self.historyStreams.readpath(streamName, + startDate=self.startDate, + endDate=self.endDate, + calendar=self.calendar) + + if len(self.inputFiles) == 0: + raise IOError('No files were found in stream {} between {} and ' + '{}.'.format(streamName, self.startDate, + self.endDate)) + + self.simulationStartTime = get_simulation_start_time(self.runStreams) + + try: + self.restartFileName = self.runStreams.readpath('restart')[0] + except ValueError: + raise IOError('No MPAS-SeaIce restart file found: need at least ' + 'one restart file to perform remapping of ' + 'climatologies.') + # these are redundant for now. Later cleanup is needed where these # file names are reused in run() self.xmlFileNames = [] @@ -284,8 +309,8 @@ def run_task(self): # {{{ if compareWithObservations: key = (hemisphere, 'iceArea') - title[key] = '{}\nSSM/I observations, annual cycle (blue)'.format( - title[key]) + title[key] = '{}\nSSM/I observations, annual cycle ' \ + '(blue)'.format(title[key]) if hemisphere == 'NH': key = (hemisphere, 'iceVolume') title[key] = \ @@ -440,9 +465,8 @@ def run_task(self): # {{{ '{}/{}.{}_polar.png'.format(self.plotsDirectory, mainRunName, variableName) - title = \ - '{}, NH (black), SH (blue)\n{}'.format(plotTitles[variableName], - mainRunName) + title = '{}, NH (black), SH (blue)\n' \ + '{}'.format(plotTitles[variableName], mainRunName) varList = [plotVars[('NH', variableName)], plotVars[('SH', variableName)]] timeseries_analysis_plot(config, varList, diff --git a/mpas_analysis/shared/climatology/__init__.py b/mpas_analysis/shared/climatology/__init__.py index a6fe39aa3..279616e8d 100644 --- a/mpas_analysis/shared/climatology/__init__.py +++ b/mpas_analysis/shared/climatology/__init__.py @@ -1,9 +1,9 @@ from .climatology import get_remapper, \ - get_observation_climatology_file_names, \ compute_monthly_climatology, compute_climatology, \ add_years_months_days_in_month, remap_and_write_climatology from .mpas_climatology_task import MpasClimatologyTask from .remap_mpas_climatology_subtask import RemapMpasClimatologySubtask +from .remap_observed_climatology_subtask import RemapObservedClimatologySubtask from .comparison_descriptors import get_comparison_descriptor, \ get_antarctic_stereographic_projection diff --git a/mpas_analysis/shared/climatology/climatology.py b/mpas_analysis/shared/climatology/climatology.py index d160a03de..9601e3f39 100644 --- a/mpas_analysis/shared/climatology/climatology.py +++ b/mpas_analysis/shared/climatology/climatology.py @@ -166,9 +166,7 @@ def get_observation_climatology_file_names(config, fieldName, monthNames, make_directories(climatologyDirectory) - if not _matches_comparison(remapper.sourceDescriptor, - remapper.destinationDescriptor): - make_directories(remappedDirectory) + make_directories(remappedDirectory) return (climatologyFileName, remappedFileName) # }}} @@ -336,7 +334,7 @@ def add_years_months_days_in_month(ds, calendar=None): # {{{ 'will be computed with\n' 'month durations ignoring leap years.') - daysInMonth = numpy.array([constants.daysInMonth[month-1] for + daysInMonth = numpy.array([constants.daysInMonth[int(month)-1] for month in ds.month.values], float) ds.coords['daysInMonth'] = ('Time', daysInMonth) @@ -393,6 +391,11 @@ def remap_and_write_climatology(config, climatologyDataSet, """ useNcremap = config.getboolean('climatology', 'useNcremap') + if (isinstance(remapper.sourceDescriptor, ProjectionGridDescriptor) or + isinstance(remapper.destinationDescriptor, ProjectionGridDescriptor)): + # ncremap doesn't support projection grids + useNcremap = False + if remapper.mappingFileName is None: # no remapping is needed remappedClimatology = climatologyDataSet diff --git a/mpas_analysis/shared/climatology/remap_mpas_climatology_subtask.py b/mpas_analysis/shared/climatology/remap_mpas_climatology_subtask.py index 09785f56a..9fca4e893 100644 --- a/mpas_analysis/shared/climatology/remap_mpas_climatology_subtask.py +++ b/mpas_analysis/shared/climatology/remap_mpas_climatology_subtask.py @@ -3,6 +3,7 @@ unicode_literals import xarray as xr +import numpy import os from ..analysis_task import AnalysisTask @@ -89,7 +90,7 @@ def __init__(self, mpasClimatologyTask, parentTask, climatologyName, to be computed or ['none'] (not ``None``) if only monthly climatologies are needed. - comparisonGridNames : list of {'latlon', 'antarctic'}, optinal + comparisonGridNames : list of {'latlon', 'antarctic'}, optional The name(s) of the comparison grid to use for remapping. iselValues : dict, optional @@ -390,6 +391,10 @@ def _mask_climatologies(self, season, dsMask): # {{{ # slice climatology = climatology.isel(**iselValues) + # add valid mask as a variable, useful for remapping later + climatology['validMask'] = \ + xr.DataArray(numpy.ones(climatology.dims['nCells']), + dims=['nCells']) # mask the data set for variableName in self.variableList: climatology[variableName] = \ @@ -431,8 +436,8 @@ def _remap(self, inFileName, outFileName, remapper, comparisonGridName): useNcremap = self.config.getboolean('climatology', 'useNcremap') - if comparisonGridName == 'antarctic': - # ncremap doesn't support polar stereographic grids + if comparisonGridName != 'latlon': + # ncremap doesn't support grids other than lat/lon useNcremap = False renormalizationThreshold = self.config.getfloat( diff --git a/mpas_analysis/shared/climatology/remap_observed_climatology_subtask.py b/mpas_analysis/shared/climatology/remap_observed_climatology_subtask.py new file mode 100644 index 000000000..ac4c14af6 --- /dev/null +++ b/mpas_analysis/shared/climatology/remap_observed_climatology_subtask.py @@ -0,0 +1,346 @@ +from __future__ import absolute_import, division, print_function, \ + unicode_literals + +import os +import os.path +import xarray as xr + +from ..analysis_task import AnalysisTask + +from ..constants import constants + +from ..io.utility import build_config_full_path, make_directories +from ..io import write_netcdf + +from .climatology import get_remapper, remap_and_write_climatology, \ + compute_climatology + +from .comparison_descriptors import get_comparison_descriptor + + +class RemapObservedClimatologySubtask(AnalysisTask): # {{{ + """ + An analysis task for comparison of 2D model fields against observations. + + Attributes + ---------- + seasons : list of str + A list of seasons (keys in ``constants.monthDictionary``) over + which the climatology should be computed. + + fileName : str + The name of the observation file + + outFilePrefix : str + The prefix in front of output files and mapping files, typically the + name of the field being remapped + + comparisonGridNames : list of {'latlon', 'antarctic'} + The name(s) of the comparison grid to use for remapping. + Authors + ------- + Xylar Asay-Davis + """ + + def __init__(self, parentTask, seasons, fileName, outFilePrefix, + comparisonGridNames=['latlon'], + subtaskName='remapObservations'): + # {{{ + ''' + Construct one analysis subtask for each plot (i.e. each season and + comparison grid) and a subtask for computing climatologies. + + Parameters + ---------- + parentTask : ``AnalysisTask`` + The parent (master) task for this subtask + + seasons : list of str + A list of seasons (keys in ``constants.monthDictionary``) over + which the climatology should be computed. + + fileName : str + The name of the observation file + + outFilePrefix : str + The prefix in front of output files and mapping files, typically + the name of the field being remapped + + comparisonGridNames : list of {'latlon', 'antarctic'}, optional + The name(s) of the comparison grid to use for remapping. + + subtaskName : str, optional + The name of the subtask + + Authors + ------- + Xylar Asay-Davis + + ''' + + self.seasons = seasons + self.fileName = fileName + self.outFilePrefix = outFilePrefix + self.comparisonGridNames = comparisonGridNames + + config = parentTask.config + taskName = parentTask.taskName + tags = parentTask.tags + componentName = parentTask.componentName + + # call the constructor from the base class (AnalysisTask) + super(RemapObservedClimatologySubtask, self).__init__( + config=config, taskName=taskName, subtaskName=subtaskName, + componentName=componentName, tags=tags) + # }}} + + def setup_and_check(self): # {{{ + """ + Perform steps to set up the analysis and check for errors in the setup. + + Authors + ------- + Xylar Asay-Davis + """ + # call setup_and_check from the base class (AnalysisTask), + # which will perform some common setup, including storing: + # self.runDirectory , self.historyDirectory, self.plotsDirectory, + # self.namelist, self.runStreams, self.historyStreams, + # self.calendar + super(RemapObservedClimatologySubtask, self).setup_and_check() + + # we set up the remappers here because ESFM_RegridWeightGen seems to + # have trouble if it runs in another process (or in several at once) + self._setup_remappers(self.fileName) + + # build the observational data set and write it out to a file, to + # be read back in during the run_task() phase + obsFileName = self.get_file_name(stage='original') + if not os.path.exists(obsFileName): + ds = self.build_observational_dataset(self.fileName) + write_netcdf(ds, obsFileName) + + # }}} + + def run_task(self): # {{{ + """ + Performs remapping of obsrevations to the comparsion grid + + Authors + ------- + Xylar Asay-Davis + """ + + config = self.config + + obsFileName = self.get_file_name(stage='original') + if not os.path.isfile(obsFileName): + raise OSError('Obs file {} not found.'.format( + obsFileName)) + + for comparisonGridName in self.comparisonGridNames: + for season in self.seasons: + + remappedFileName = self.get_file_name( + stage='remapped', + season=season, + comparisonGridName=comparisonGridName) + + if not os.path.exists(remappedFileName): + + ds = xr.open_dataset(obsFileName) + + climatologyFileName = self.get_file_name( + stage='climatology', + season=season, + comparisonGridName=comparisonGridName) + if 'month' in ds.variables.keys() and \ + 'year' in ds.variables.keys(): + # this data set is not yet a climatology, so compute + # the climatology + monthValues = constants.monthDictionary[season] + seasonalClimatology = compute_climatology( + ds, monthValues, maskVaries=True) + else: + # We don't have month or year arrays to compute a + # climatology so assume this already is one + seasonalClimatology = ds + + write_netcdf(seasonalClimatology, climatologyFileName) + + remapper = self.remappers[comparisonGridName] + + if remapper.mappingFileName is None: + # no need to remap because the observations are on the + # comparison grid already + os.symlink(climatologyFileName, remappedFileName) + else: + remap_and_write_climatology( + config, seasonalClimatology, + climatologyFileName, + remappedFileName, remapper, + logger=self.logger) + + # }}} + + def get_observation_descriptor(self, fileName): # {{{ + ''' + get a MeshDescriptor for the observation grid. A subclass derived from + this class must override this method to create the appropriate + descriptor + + Parameters + ---------- + fileName : str + observation file name describing the source grid + + Returns + ------- + obsDescriptor : ``MeshDescriptor`` + The descriptor for the observation grid + + Authors + ------- + Xylar Asay-Davis + ''' + + return None # }}} + + def build_observational_dataset(self, fileName): # {{{ + ''' + read in the data sets for observations, and possibly rename some + variables and dimensions. A subclass derived from this class must + override this method to create the appropriate data set + + Parameters + ---------- + fileName : str + observation file name + + Returns + ------- + dsObs : ``xarray.Dataset`` + The observational dataset + + Authors + ------- + Xylar Asay-Davis + ''' + + return None # }}} + + # }}} + + def get_file_name(self, stage, season=None, comparisonGridName=None): + # {{{ + """ + Given config options, the name of a field and a string identifying the + months in a seasonal climatology, returns the full path for MPAS + climatology files before and after remapping. + + Parameters + ---------- + stage : {'original', 'climatology', 'remapped'} + The stage of the masking and remapping process + + season : str, optional + One of the seasons in ``constants.monthDictionary`` + + comparisonGridName : {'latlon', 'antarctic'}, optional + The name of the comparison grid to use for remapping. + + Returns + ------- + fileName : str + The path to the climatology file for the specified season. + + Authors + ------- + Xylar Asay-Davis + """ + + config = self.config + obsSection = '{}Observations'.format(self.componentName) + if comparisonGridName is None: + # just needed for getting the obs. grid name, so doesn't matter + # which comparison grid + remapper = self.remappers[self.comparisonGridNames[0]] + else: + remapper = self.remappers[comparisonGridName] + + obsGridName = remapper.sourceDescriptor.meshName + + outFilePrefix = self.outFilePrefix + + if stage in ['original', 'climatology']: + climatologyDirectory = build_config_full_path( + config=config, section='output', + relativePathOption='climatologySubdirectory', + relativePathSection=obsSection) + + make_directories(climatologyDirectory) + + if stage == 'original': + fileName = '{}/{}_{}.nc'.format( + climatologyDirectory, outFilePrefix, obsGridName) + else: + fileName = '{}/{}_{}_{}.nc'.format( + climatologyDirectory, outFilePrefix, obsGridName, season) + + elif stage == 'remapped': + remappedDirectory = build_config_full_path( + config=config, section='output', + relativePathOption='remappedClimSubdirectory', + relativePathSection=obsSection) + + make_directories(remappedDirectory) + + comparisonGridName = remapper.destinationDescriptor.meshName + fileName = '{}/{}_{}_to_{}_{}.nc'.format( + remappedDirectory, outFilePrefix, obsGridName, + comparisonGridName, season) + + else: + raise ValueError('Unknown stage {}'.format(stage)) + + return fileName # }}} + + 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) + # }}} + # }}} + +# vim: foldmethod=marker ai ts=4 sts=4 et sw=4 ft=python diff --git a/mpas_analysis/shared/grid/__init__.py b/mpas_analysis/shared/grid/__init__.py index 7fae57fb3..beffc841f 100644 --- a/mpas_analysis/shared/grid/__init__.py +++ b/mpas_analysis/shared/grid/__init__.py @@ -1,2 +1,2 @@ from .grid import MpasMeshDescriptor, LatLonGridDescriptor, \ - ProjectionGridDescriptor + ProjectionGridDescriptor, interp_extrap_corner diff --git a/mpas_analysis/shared/grid/grid.py b/mpas_analysis/shared/grid/grid.py index 81e46abfd..ba7c4096d 100644 --- a/mpas_analysis/shared/grid/grid.py +++ b/mpas_analysis/shared/grid/grid.py @@ -189,7 +189,7 @@ def to_scrip(self, scripFileName): # {{{ inFile.close() outFile.close() # }}} -# }}} + # }}} class LatLonGridDescriptor(MeshDescriptor): # {{{ @@ -259,8 +259,8 @@ def read(cls, fileName=None, ds=None, latVarName='lat', ds[lonVarName].dims[0]) # interp/extrap corners - descriptor.lonCorner = _interp_extrap_corner(descriptor.lon) - descriptor.latCorner = _interp_extrap_corner(descriptor.lat) + descriptor.lonCorner = interp_extrap_corner(descriptor.lon) + descriptor.latCorner = interp_extrap_corner(descriptor.lat) if 'history' in ds.attrs: descriptor.history = '\n'.join([ds.attrs['history'], @@ -368,6 +368,7 @@ def _set_coords(self, latVarName, lonVarName, latDimName, if self.meshName is None: self.meshName = '{}x{}{}'.format(abs(dLat), abs(dLon), units) # }}} + # }}} class ProjectionGridDescriptor(MeshDescriptor): # {{{ @@ -446,8 +447,8 @@ def read(cls, projection, fileName, meshName=None, xVarName='x', ds[yVarName].dims[0]) # interp/extrap corners - descriptor.xCorner = _interp_extrap_corner(descriptor.x) - descriptor.yCorner = _interp_extrap_corner(descriptor.y) + descriptor.xCorner = interp_extrap_corner(descriptor.x) + descriptor.yCorner = interp_extrap_corner(descriptor.y) # Update history attribute of netCDF file if 'history' in ds.attrs: @@ -487,8 +488,8 @@ def create(cls, projection, x, y, meshName): # {{{ descriptor._set_coords('x', 'y', 'x', 'y') # interp/extrap corners - descriptor.xCorner = _interp_extrap_corner(descriptor.x) - descriptor.yCorner = _interp_extrap_corner(descriptor.y) + descriptor.xCorner = interp_extrap_corner(descriptor.x) + descriptor.yCorner = interp_extrap_corner(descriptor.y) descriptor.history = sys.argv[:] return descriptor # }}} @@ -587,6 +588,19 @@ def _set_coords(self, xVarName, yVarName, xDimName, yDimName): # {{{ self.dimSize = [len(self.x), len(self.y)] # }}} + # }}} + + +def interp_extrap_corner(inField): # {{{ + '''Interpolate/extrapolate a 1D field from grid centers to grid corners''' + + outField = numpy.zeros(len(inField)+1) + outField[1:-1] = 0.5*(inField[0:-1] + inField[1:]) + # extrapolate the ends + outField[0] = 1.5*inField[0] - 0.5*inField[1] + outField[-1] = 1.5*inField[-1] - 0.5*inField[-2] + return outField # }}} + def _create_scrip(outFile, grid_size, grid_corners, grid_rank, units, meshName): # {{{ @@ -645,17 +659,6 @@ def _create_scrip(outFile, grid_size, grid_corners, grid_rank, units, # }}} -def _interp_extrap_corner(inField): # {{{ - '''Interpolate/extrapolate a 1D field from grid centers to grid corners''' - - outField = numpy.zeros(len(inField)+1) - outField[1:-1] = 0.5*(inField[0:-1] + inField[1:]) - # extrapolate the ends - outField[0] = 1.5*inField[0] - 0.5*inField[1] - outField[-1] = 1.5*inField[-1] - 0.5*inField[-2] - return outField # }}} - - def _unwrap_corners(inField): '''Turn a 2D array of corners into an array of rectangular mesh elements''' outField = numpy.zeros(((inField.shape[0]-1)*(inField.shape[1]-1), 4)) diff --git a/mpas_analysis/shared/plot/plotting.py b/mpas_analysis/shared/plot/plotting.py index 3c6d141fd..a71edf5c0 100644 --- a/mpas_analysis/shared/plot/plotting.py +++ b/mpas_analysis/shared/plot/plotting.py @@ -423,7 +423,7 @@ def plot_global_comparison( diffTitle='Model-Observations', cbarlabel='units', titleFontSize=None, - figsize=(8, 12), + figsize=(8, 13), dpi=None): """ @@ -558,13 +558,203 @@ def plot_global_comparison( plt.close() -def _date_tick(days, pos, calendar='gregorian', includeMonth=True): - days = np.maximum(days, 0.) - date = days_to_datetime(days, calendar) - if includeMonth: - return '{:04d}-{:02d}'.format(date.year, date.month) +def plot_polar_projection_comparison( + config, + x, + y, + landMask, + modelArray, + obsArray, + diffArray, + fileout, + colorMapSectionName, + colorMapType='norm', + title=None, + modelTitle='Model', + obsTitle='Observations', + diffTitle='Model-Observations', + cbarlabel='units', + titleFontSize=None, + figsize=None, + dpi=None, + vertical=False): + + """ + Plots a data set as a longitude/latitude map. + + Parameters + ---------- + config : instance of ConfigParser + the configuration, containing a [plot] section with options that + control plotting + + x, y : numpy ndarrays + 1D x and y arrays defining the projection grid + + landMask : numpy ndarrays + model and observational data sets + + modelArray, obsArray : numpy ndarrays + model and observational data sets + + diffArray : float array + difference between modelArray and obsArray + + fileout : str + the file name to be written + + colorMapSectionName : str + section name in ``config`` where color map info can be found. + + colorMapType : {'norm', 'indexed'}, optional + The type of color map, either a matplotlib norm or indices into a color + map. + + If 'norm', the following options must be defined for suffixes + ``Result`` and ``Difference``: + ``colormapName``, ``normType``, + ``normArgs``, ``colorbarTicks`` + + If 'indexed', these options are required: + ``colormapName``, ``colormapIndices``, + ``colorbarLevels`` + + The colorbar for each panel will be constructed from these options + + + title : str, optional + the subtitle of the plot + + plotProjection : str, optional + Basemap projection for the plot + + modelTitle : str, optional + title of the model panel + + obsTitle : str, optional + title of the observations panel + + diffTitle : str, optional + title of the difference (bias) panel + + cbarlabel : str, optional + label on the colorbar + + titleFontSize : int, optional + size of the title font + + figsize : tuple of float, optional + the size of the figure in inches. If ``None``, the figure size is + ``(8, 22)`` if ``vertical == True`` and ``(22, 8)`` otherwise. + + dpi : int, optional + the number of dots per inch of the figure, taken from section ``plot`` + option ``dpi`` in the config file by default + + vertical : bool, optional + whether the subplots should be stacked vertically rather than + horizontally + + Authors + ------- + Xylar Asay-Davis + """ + + def plot_panel(ax, title, array, cmap, norm, ticks): + plt.title(title, y=1.06, **axis_font) + + mesh = plt.pcolormesh(x, y, array, cmap=cmap, norm=norm) + + plt.pcolormesh(x, y, landMask, cmap=landColorMap) + plt.contour(xCenter, yCenter, landMask.mask, (0.5,), colors='k', + linewidths=0.5) + + # create an axes on the right side of ax. The width of cax will be 5% + # of ax and the padding between cax and ax will be fixed at 0.05 inch. + divider = make_axes_locatable(ax) + cax = divider.append_axes("right", size="5%", pad=0.05) + + cbar = plt.colorbar(mesh, cax=cax) + cbar.set_label(cbarlabel) + if ticks is not None: + cbar.set_ticks(ticks) + cbar.set_ticklabels(['{}'.format(tick) for tick in ticks]) + + ax.axis('off') + ax.set_aspect('equal') + ax.autoscale(tight=True) + + # set up figure + if dpi is None: + dpi = config.getint('plot', 'dpi') + + if vertical: + if figsize is None: + figsize = (8, 22) + subplots = [311, 312, 313] else: - return '{:04d}'.format(date.year) + if figsize is None: + figsize = (22, 7.5) + subplots = [131, 132, 133] + + if colorMapType == 'norm': + (cmapModelObs, normModelObs) = _setup_colormap_and_norm( + config, colorMapSectionName, suffix='Result') + (cmapDiff, normDiff) = _setup_colormap_and_norm( + config, colorMapSectionName, suffix='Difference') + + colorbarTicksResult = config.getExpression(colorMapSectionName, + 'colorbarTicksResult') + colorbarTicksDifference = config.getExpression( + colorMapSectionName, 'colorbarTicksDifference') + elif colorMapType == 'indexed': + + (cmapModelObs, colorbarTicksResult) = setup_colormap( + config, colorMapSectionName, suffix='Result') + (cmapDiff, colorbarTicksDifference) = setup_colormap( + config, colorMapSectionName, suffix='Difference') + + normModelObs = cols.BoundaryNorm(colorbarTicksResult, cmapModelObs.N) + normDiff = cols.BoundaryNorm(colorbarTicksDifference, cmapDiff.N) + else: + raise ValueError('colorMapType must be one of {norm, indexed}') + + # set up figure + fig = plt.figure(figsize=figsize, dpi=dpi) + if (title is not None): + if titleFontSize is None: + titleFontSize = config.get('plot', 'titleFontSize') + title_font = {'size': titleFontSize, + 'color': config.get('plot', 'titleFontColor'), + 'weight': config.get('plot', 'titleFontWeight')} + fig.suptitle(title, y=0.95, **title_font) + axis_font = {'size': config.get('plot', 'axisFontSize')} + + # set up land colormap + colorList = [(0.8, 0.8, 0.8), (0.8, 0.8, 0.8)] + landColorMap = cols.LinearSegmentedColormap.from_list('land', colorList) + + # locations of centers for land contour + xCenter = 0.5*(x[1:] + x[0:-1]) + yCenter = 0.5*(y[1:] + y[0:-1]) + + ax = plt.subplot(subplots[0]) + plot_panel(ax, modelTitle, modelArray, cmapModelObs, normModelObs, + colorbarTicksResult) + + ax = plt.subplot(subplots[1]) + plot_panel(ax, obsTitle, obsArray, cmapModelObs, normModelObs, + colorbarTicksResult) + + ax = plt.subplot(subplots[2]) + plot_panel(ax, diffTitle, diffArray, cmapDiff, normDiff, + colorbarTicksDifference) + + if (fileout is not None): + plt.savefig(fileout, dpi=dpi, bbox_inches='tight', pad_inches=0.1) + + if not config.getboolean('plot', 'displayToScreen'): + plt.close() def plot_1D(config, xArrays, fieldArrays, errArrays, @@ -905,6 +1095,8 @@ def setup_colormap(config, configSectionName, suffix=''): Xylar Asay-Davis, Milena Veneziani, Greg Streletz ''' + _register_custom_colormaps() + colormap = plt.get_cmap(config.get(configSectionName, 'colormapName{}'.format(suffix))) @@ -1022,5 +1214,183 @@ def plot_xtick_format(plt, calendar, minDays, maxDays, maxXTicks): plt.autoscale(enable=True, axis='x', tight=True) +def _setup_colormap_and_norm(config, configSectionName, suffix=''): + + ''' + Set up a colormap from the registry + + Parameters + ---------- + config : instance of ConfigParser + the configuration, containing a [plot] section with options that + control plotting + + configSectionName : str + name of config section + + suffix: str, optional + suffix of colormap related options + + Returns + ------- + colormap : srt + new colormap + + norm : ``SymLogNorm`` object + the norm used to normalize the colormap + + Authors + ------- + Xylar Asay-Davis + ''' + + _register_custom_colormaps() + + colormap = plt.get_cmap(config.get(configSectionName, + 'colormapName{}'.format(suffix))) + + normType = config.get(configSectionName, 'normType{}'.format(suffix)) + + kwargs = config.getExpression(configSectionName, + 'normArgs{}'.format(suffix)) + + if normType == 'symLog': + norm = cols.SymLogNorm(**kwargs) + elif normType == 'linear': + norm = cols.Normalize(**kwargs) + else: + raise ValueError('Unsupported norm type {} in section {}'.format( + normType, configSectionName)) + + return (colormap, norm) + + +def _date_tick(days, pos, calendar='gregorian', includeMonth=True): + days = np.maximum(days, 0.) + date = days_to_datetime(days, calendar) + if includeMonth: + return '{:04d}-{:02d}'.format(date.year, date.month) + else: + return '{:04d}'.format(date.year) + + +def _register_custom_colormaps(): + name = 'ferret' + backgroundColor = (0.9, 0.9, 0.9) + + red = np.array([[0, 0.6], + [0.15, 1], + [0.35, 1], + [0.65, 0], + [0.8, 0], + [1, 0.75]]) + + green = np.array([[0, 0], + [0.1, 0], + [0.35, 1], + [1, 0]]) + + blue = np.array([[0, 0], + [0.5, 0], + [0.9, 0.9], + [1, 0.9]]) + + colorCount = 21 + colorList = np.ones((colorCount, 4), float) + colorList[:, 0] = np.interp(np.linspace(0, 1, colorCount), + red[:, 0], red[:, 1]) + colorList[:, 1] = np.interp(np.linspace(0, 1, colorCount), + green[:, 0], green[:, 1]) + colorList[:, 2] = np.interp(np.linspace(0, 1, colorCount), + blue[:, 0], blue[:, 1]) + colorList = colorList[::-1, :] + + colorMap = cols.LinearSegmentedColormap.from_list( + name, colorList, N=255) + + colorMap.set_bad(backgroundColor) + plt.register_cmap(name, colorMap) + + name = 'erdc_iceFire_H' + + colorArray = np.array([ + [-1, 4.05432e-07, 0, 5.90122e-06], + [-0.87451, 0, 0.120401, 0.302675], + [-0.74902, 0, 0.216583, 0.524574], + [-0.623529, 0.0552475, 0.345025, 0.6595], + [-0.498039, 0.128047, 0.492588, 0.720288], + [-0.372549, 0.188955, 0.641309, 0.792092], + [-0.247059, 0.327673, 0.784935, 0.873434], + [-0.121569, 0.60824, 0.892164, 0.935547], + [0.00392157, 0.881371, 0.912178, 0.818099], + [0.129412, 0.951407, 0.835621, 0.449279], + [0.254902, 0.904481, 0.690489, 0], + [0.380392, 0.85407, 0.510864, 0], + [0.505882, 0.777093, 0.33018, 0.00088199], + [0.631373, 0.672862, 0.139087, 0.00269398], + [0.756863, 0.508815, 0, 0], + [0.882353, 0.299417, 0.000366289, 0.000547829], + [1, 0.0157519, 0.00332021, 4.55569e-08]], float) + + colorCount = 255 + colorList = np.ones((colorCount, 4), float) + x = colorArray[:, 0] + for cIndex in range(3): + colorList[:, cIndex] = np.interp( + np.linspace(-1., 1., colorCount), + x, colorArray[:, cIndex+1]) + + colorMap = cols.LinearSegmentedColormap.from_list( + name, colorList, N=255) + + plt.register_cmap(name, colorMap) + + name = 'erdc_iceFire_L' + + colorArray = np.array([ + [-1, 0.870485, 0.913768, 0.832905], + [-0.87451, 0.586919, 0.887865, 0.934003], + [-0.74902, 0.31583, 0.776442, 0.867858], + [-0.623529, 0.18302, 0.632034, 0.787722], + [-0.498039, 0.117909, 0.484134, 0.713825], + [-0.372549, 0.0507239, 0.335979, 0.654741], + [-0.247059, 0, 0.209874, 0.511832], + [-0.121569, 0, 0.114689, 0.28935], + [0.00392157, 0.0157519, 0.00332021, 4.55569e-08], + [0.129412, 0.312914, 0, 0], + [0.254902, 0.520865, 0, 0], + [0.380392, 0.680105, 0.15255, 0.0025996], + [0.505882, 0.785109, 0.339479, 0.000797922], + [0.631373, 0.857354, 0.522494, 0], + [0.756863, 0.910974, 0.699774, 0], + [0.882353, 0.951921, 0.842817, 0.478545], + [1, 0.881371, 0.912178, 0.818099]], float) + + colorCount = 255 + colorList = np.ones((colorCount, 4), float) + x = colorArray[:, 0] + for cIndex in range(3): + colorList[:, cIndex] = np.interp( + np.linspace(-1., 1., colorCount), + x, colorArray[:, cIndex+1]) + + colorMap = cols.LinearSegmentedColormap.from_list( + name, colorList, N=255) + + plt.register_cmap(name, colorMap) + + name = 'BuOr' + colors1 = plt.cm.PuOr(np.linspace(0., 1, 256)) + colors2 = plt.cm.RdBu(np.linspace(0, 1, 256)) + + # combine them and build a new colormap, just the orange from the first + # and the blue from the second + colorList = np.vstack((colors1[0:128, :], colors2[128:256, :])) + # reverse the order + colorList = colorList[::-1, :] + colorMap = cols.LinearSegmentedColormap.from_list(name, colorList) + + plt.register_cmap(name, colorMap) + # vim: foldmethod=marker ai ts=4 sts=4 et sw=4 ft=python diff --git a/mpas_analysis/test/test_analysis_task.py b/mpas_analysis/test/test_analysis_task.py index c0cfa9c99..e6eeab82e 100644 --- a/mpas_analysis/test/test_analysis_task.py +++ b/mpas_analysis/test/test_analysis_task.py @@ -10,8 +10,7 @@ import pytest from mpas_analysis.test import TestCase from mpas_analysis.shared.analysis_task import AnalysisTask -from mpas_analysis.configuration.MpasAnalysisConfigParser \ - import MpasAnalysisConfigParser +from mpas_analysis.configuration import MpasAnalysisConfigParser class TestAnalysisTask(TestCase): diff --git a/mpas_analysis/test/test_climatology.py b/mpas_analysis/test/test_climatology.py index ce7948441..087ccd7ca 100644 --- a/mpas_analysis/test/test_climatology.py +++ b/mpas_analysis/test/test_climatology.py @@ -18,11 +18,9 @@ from mpas_analysis.test import TestCase, loaddatadir from mpas_analysis.shared.generalized_reader.generalized_reader \ import open_multifile_dataset -from mpas_analysis.configuration.MpasAnalysisConfigParser \ - import MpasAnalysisConfigParser +from mpas_analysis.configuration import MpasAnalysisConfigParser from mpas_analysis.shared.climatology import \ get_comparison_descriptor, get_remapper, \ - get_observation_climatology_file_names, \ add_years_months_days_in_month, compute_climatology, \ compute_monthly_climatology from mpas_analysis.shared.grid import MpasMeshDescriptor, LatLonGridDescriptor @@ -195,26 +193,6 @@ def test_get_observations_remapper(self): shutil.copyfile(defaultMappingFileName, explicitMappingFileName) - def test_get_observation_climatology_file_names(self): - config = self.setup_config() - fieldName = 'sst' - monthNames = 'JFM' - componentName = 'ocean' - - remapper = self.setup_obs_remapper(config, fieldName) - - (climatologyFileName, remappedFileName) = \ - get_observation_climatology_file_names( - config, fieldName, monthNames, componentName, remapper) - expectedClimatologyFileName = '{}/clim/obs/sst_1.0x1.0degree_' \ - 'JFM.nc'.format(self.test_dir) - self.assertEqual(climatologyFileName, expectedClimatologyFileName) - - expectedRemappedFileName = '{}/clim/obs/remap/sst_1.0x1.0degree_' \ - 'to_0.5x0.5degree_' \ - 'JFM.nc'.format(self.test_dir) - self.assertEqual(remappedFileName, expectedRemappedFileName) - def test_compute_climatology(self): config = self.setup_config() calendar = 'gregorian_noleap' diff --git a/mpas_analysis/test/test_generalized_reader.py b/mpas_analysis/test/test_generalized_reader.py index d7d24b4db..1bc362831 100644 --- a/mpas_analysis/test/test_generalized_reader.py +++ b/mpas_analysis/test/test_generalized_reader.py @@ -12,8 +12,7 @@ from mpas_analysis.test import TestCase, loaddatadir from mpas_analysis.shared.generalized_reader.generalized_reader \ import open_multifile_dataset -from mpas_analysis.configuration.MpasAnalysisConfigParser \ - import MpasAnalysisConfigParser +from mpas_analysis.configuration import MpasAnalysisConfigParser @pytest.mark.usefixtures("loaddatadir") diff --git a/mpas_analysis/test/test_interpolate.py b/mpas_analysis/test/test_interpolate.py index 5e6088232..0b44d0a42 100644 --- a/mpas_analysis/test/test_interpolate.py +++ b/mpas_analysis/test/test_interpolate.py @@ -20,8 +20,7 @@ from mpas_analysis.shared.grid import MpasMeshDescriptor, \ LatLonGridDescriptor, ProjectionGridDescriptor from mpas_analysis.test import TestCase, loaddatadir -from mpas_analysis.configuration.MpasAnalysisConfigParser \ - import MpasAnalysisConfigParser +from mpas_analysis.configuration import MpasAnalysisConfigParser @pytest.mark.usefixtures('loaddatadir') diff --git a/mpas_analysis/test/test_mpas_config_parser.py b/mpas_analysis/test/test_mpas_config_parser.py index b27eaba1b..38944f287 100644 --- a/mpas_analysis/test/test_mpas_config_parser.py +++ b/mpas_analysis/test/test_mpas_config_parser.py @@ -12,8 +12,7 @@ import six from six.moves import configparser from mpas_analysis.test import TestCase, loaddatadir -from mpas_analysis.configuration.MpasAnalysisConfigParser \ - import MpasAnalysisConfigParser +from mpas_analysis.configuration import MpasAnalysisConfigParser from . import requires_numpy diff --git a/mpas_analysis/test/test_remap_obs_clim_subtask.py b/mpas_analysis/test/test_remap_obs_clim_subtask.py new file mode 100644 index 000000000..d0e0e55dc --- /dev/null +++ b/mpas_analysis/test/test_remap_obs_clim_subtask.py @@ -0,0 +1,169 @@ +""" +Unit test infrastructure for MpasClimatologyTask. + +Xylar Asay-Davis +""" + +from __future__ import absolute_import, division, print_function, \ + unicode_literals + +import pytest +import tempfile +import shutil +import os +import xarray + +from mpas_analysis.test import TestCase, loaddatadir +from mpas_analysis.configuration import MpasAnalysisConfigParser +from mpas_analysis.shared.climatology import RemapObservedClimatologySubtask +from mpas_analysis.shared import AnalysisTask +from mpas_analysis.shared.io.utility import build_config_full_path, \ + make_directories +from mpas_analysis.shared.grid import LatLonGridDescriptor + + +class RemapObservedMLDClimatology(RemapObservedClimatologySubtask): # {{{ + """ + A subtask for reading and remapping MLD observations + + Authors + ------- + Xylar Asay-Davis + """ + + def get_observation_descriptor(self, fileName): # {{{ + ''' + get a MeshDescriptor for the observation grid + + Parameters + ---------- + fileName : str + observation file name describing the source grid + + Returns + ------- + obsDescriptor : ``MeshDescriptor`` + The descriptor for the observation grid + + Authors + ------- + Xylar Asay-Davis + ''' + + # create a descriptor of the observation grid using the lat/lon + # coordinates + obsDescriptor = LatLonGridDescriptor.read(fileName=fileName, + latVarName='lat', + lonVarName='lon') + return obsDescriptor # }}} + + def build_observational_dataset(self, fileName): # {{{ + ''' + read in the data sets for observations, and possibly rename some + variables and dimensions + + Parameters + ---------- + fileName : str + observation file name + + Returns + ------- + dsObs : ``xarray.Dataset`` + The observational dataset + + Authors + ------- + Xylar Asay-Davis + ''' + + # Load MLD observational data + dsObs = xarray.open_dataset(fileName) + + dsObs.coords['month'] = dsObs['month'] + dsObs.coords['year'] = dsObs['year'] + + return dsObs # }}} + + # }}} + + +@pytest.mark.usefixtures("loaddatadir") +class TestRemapObsClimSubtask(TestCase): + def setUp(self): + # Create a temporary directory + self.test_dir = tempfile.mkdtemp() + + def tearDown(self): + # Remove the directory after the test + shutil.rmtree(self.test_dir) + + def setup_config(self): + configPath = self.datadir.join('config.remap_obs') + config = MpasAnalysisConfigParser() + config.read(str(configPath)) + config.set('input', 'baseDirectory', str(self.datadir)) + config.set('oceanObservations', 'baseDirectory', str(self.datadir)) + config.set('output', 'baseDirectory', str(self.test_dir)) + return config + + def setup_subtask(self): + config = self.setup_config() + parent = AnalysisTask(config, 'taskName', 'ocean') + obsFileName = '{}/mld_4.0x4.0degree.nc'.format(str(self.datadir)) + remapObsTask = RemapObservedMLDClimatology( + parentTask=parent, + seasons=['ANN'], fileName=obsFileName, + outFilePrefix='mld', + comparisonGridNames=['latlon', 'antarctic']) + + remapObsTask.setup_and_check() + return remapObsTask + + def test_subtask_run_analysis(self): + remapSubtask = self.setup_subtask() + + config = remapSubtask.config + logsDirectory = build_config_full_path(config, 'output', + 'logsSubdirectory') + make_directories(logsDirectory) + make_directories('{}/configs/'.format(logsDirectory)) + + remapSubtask.run(writeLogFile=False) + + for comparisonGridName in remapSubtask.comparisonGridNames: + for season in remapSubtask.seasons: + for stage in ['original', 'climatology', 'remapped']: + fileName = remapSubtask.get_file_name( + season=season, stage=stage, + comparisonGridName=comparisonGridName) + assert(os.path.exists(fileName)) + + def test_subtask_get_file_name(self): + remapSubtask = self.setup_subtask() + + for comparisonGridName in ['latlon', 'antarctic', None]: + fileName = remapSubtask.get_file_name( + stage='original', comparisonGridName=comparisonGridName) + assert(fileName == '{}/clim/obs/mld_4.0x4.0degree.nc'.format( + str(self.test_dir))) + + stage = 'climatology' + for comparisonGridName in ['latlon', 'antarctic', None]: + fileName = remapSubtask.get_file_name(stage=stage, + season='JFM', + comparisonGridName='latlon') + assert(fileName == '{}/clim/obs/mld_4.0x4.0degree_JFM.nc'.format( + str(self.test_dir))) + + stage = 'remapped' + fileName = remapSubtask.get_file_name(stage=stage, season='JFM', + comparisonGridName='latlon') + assert(fileName == '{}/clim/obs/remapped/mld_4.0x4.0degree_to_' + '0.5x0.5degree_JFM.nc'.format(str(self.test_dir))) + + fileName = remapSubtask.get_file_name(stage=stage, season='JFM', + comparisonGridName='antarctic') + assert(fileName == '{}/clim/obs/remapped/mld_4.0x4.0degree_to_' + '6000.0x6000.0km_10.0km_Antarctic_stereo_JFM.nc'.format( + str(self.test_dir))) diff --git a/mpas_analysis/test/test_remap_obs_clim_subtask/config.remap_obs b/mpas_analysis/test/test_remap_obs_clim_subtask/config.remap_obs new file mode 100644 index 000000000..1e6c43e8f --- /dev/null +++ b/mpas_analysis/test/test_remap_obs_clim_subtask/config.remap_obs @@ -0,0 +1,28 @@ +[input] +baseDirectory = /dir/for/model/output +runSubdirectory = . +oceanHistorySubdirectory = . +oceanNamelistFileName = mpas-o_in +oceanStreamsFileName = streams.ocean + +[output] +baseDirectory = /dir/for/analysis/output +plotsSubdirectory = plots +logsSubdirectory = logs +mappingSubdirectory = mapping + +[climatology] +startYear = 2 +endYear = 2 +comparisonLatResolution = 0.5 +comparisonLonResolution = 0.5 +comparisonAntarcticStereoWidth = 6000. +comparisonAntarcticStereoResolution = 10. +useNcremap = True +renormalizationThreshold = 0.01 + +[oceanObservations] +baseDirectory = /dir/to/ocean/observations +interpolationMethod = bilinear +climatologySubdirectory = clim/obs +remappedClimSubdirectory = clim/obs/remapped diff --git a/mpas_analysis/test/test_remap_obs_clim_subtask/mld_4.0x4.0degree.nc b/mpas_analysis/test/test_remap_obs_clim_subtask/mld_4.0x4.0degree.nc new file mode 100644 index 000000000..e463860b0 Binary files /dev/null and b/mpas_analysis/test/test_remap_obs_clim_subtask/mld_4.0x4.0degree.nc differ diff --git a/mpas_analysis/test/test_remap_obs_clim_subtask/mpas-o_in b/mpas_analysis/test/test_remap_obs_clim_subtask/mpas-o_in new file mode 100644 index 000000000..0d8ba89a3 --- /dev/null +++ b/mpas_analysis/test/test_remap_obs_clim_subtask/mpas-o_in @@ -0,0 +1,1092 @@ +&run_modes + config_ocean_run_mode = 'forward' +/ +&time_management + config_calendar_type = 'gregorian_noleap' + config_do_restart = .true. + config_restart_timestamp_name = 'rpointer.ocn' + config_start_time = 'file' +/ +&io + config_pio_num_iotasks = 0 + config_pio_stride = 1 + config_write_output_on_startup = .true. +/ +&decomposition + config_block_decomp_file_prefix = '/project/projectdirs/acme/inputdata/ocn/mpas-o/oQU240/mpas-o.graph.info.151209.part.' + config_explicit_proc_decomp = .false. + config_num_halos = 3 + config_number_of_blocks = 0 + config_proc_decomp_file_prefix = 'graph.info.part.' +/ +&init_setup + config_expand_sphere = .false. + config_init_configuration = 'none' + config_realistic_coriolis_parameter = .false. + config_vert_levels = -1 + config_vertical_grid = 'uniform' + config_write_cull_cell_mask = .true. +/ +&cvtgenerator + config_1dcvtgenerator_dzseed = 1.2 + config_1dcvtgenerator_stretch1 = 1.0770 + config_1dcvtgenerator_stretch2 = 1.0275 +/ +&init_ssh_and_landicepressure + config_iterative_init_variable = 'landIcePressure' +/ +&time_integration + config_dt = '01:00:00' + config_time_integrator = 'split_explicit' +/ +&ale_vertical_grid + config_dzdk_positive = .false. + config_max_thickness_factor = 6.0 + config_min_thickness = 1.0 + config_use_min_max_thickness = .false. + config_vert_coord_movement = 'uniform_stretching' +/ +&ale_frequency_filtered_thickness + config_highfreqthick_del2 = 100.0 + config_highfreqthick_restore_time = 30.0 + config_thickness_filter_timescale = 5.0 + config_use_freq_filtered_thickness = .false. + config_use_highfreqthick_del2 = .false. + config_use_highfreqthick_restore = .false. +/ +&partial_bottom_cells + config_alter_ics_for_pbcs = .false. + config_min_pbc_fraction = 0.10 + config_pbc_alteration_type = 'full_cell' +/ +&hmix + config_apvm_scale_factor = 0.0 + config_hmix_scalewithmesh = .false. + config_maxmeshdensity = -1.0 +/ +&hmix_del2 + config_mom_del2 = 10.0 + config_tracer_del2 = 10.0 + config_use_mom_del2 = .false. + config_use_tracer_del2 = .false. +/ +&hmix_del4 + config_mom_del4 = 2.0e14 + config_mom_del4_div_factor = 1.0 + config_tracer_del4 = 0.0 + config_use_mom_del4 = .true. + config_use_tracer_del4 = .false. +/ +&hmix_leith + config_leith_dx = 15000.0 + config_leith_parameter = 1.0 + config_leith_visc2_max = 2.5e3 + config_use_leith_del2 = .false. +/ +&mesoscale_eddy_parameterization + config_gravwavespeed_trunc = 0.3 + config_max_relative_slope = 0.01 + config_redi_bottom_layer_tapering_depth = 0.0 + config_redi_kappa = 0.0 + config_redi_surface_layer_tapering_extent = 0.0 + config_standardgm_tracer_kappa = 600.0 + config_use_redi_bottom_layer_tapering = .false. + config_use_redi_surface_layer_tapering = .false. + config_use_standardgm = .true. +/ +&hmix_del2_tensor + config_mom_del2_tensor = 10.0 + config_use_mom_del2_tensor = .false. +/ +&hmix_del4_tensor + config_mom_del4_tensor = 5.0e13 + config_use_mom_del4_tensor = .false. +/ +&rayleigh_damping + config_rayleigh_damping_coeff = 0.0 + config_rayleigh_friction = .false. +/ +&vmix + config_convective_diff = 1.0 + config_convective_visc = 1.0 +/ +&vmix_const + config_use_const_diff = .false. + config_use_const_visc = .false. + config_vert_diff = 1.0e-5 + config_vert_visc = 1.0e-4 +/ +&vmix_rich + config_bkrd_vert_diff = 1.0e-5 + config_bkrd_vert_visc = 1.0e-4 + config_rich_mix = 0.005 + config_use_rich_diff = .false. + config_use_rich_visc = .false. +/ +&vmix_tanh + config_max_diff_tanh = 2.5e-2 + config_max_visc_tanh = 2.5e-1 + config_min_diff_tanh = 1.0e-5 + config_min_visc_tanh = 1.0e-4 + config_use_tanh_diff = .false. + config_use_tanh_visc = .false. + config_zmid_tanh = -100 + config_zwidth_tanh = 100 +/ +&cvmix + config_cvmix_background_diffusion = 1.0e-5 + config_cvmix_background_viscosity = 1.0e-4 + config_cvmix_convective_basedonbvf = .true. + config_cvmix_convective_diffusion = 1.0 + config_cvmix_convective_triggerbvf = 0.0 + config_cvmix_convective_viscosity = 1.0 + config_cvmix_kpp_boundary_layer_depth = 30.0 + config_cvmix_kpp_criticalbulkrichardsonnumber = 0.25 + config_cvmix_kpp_ekmanobl = .false. + config_cvmix_kpp_interpolationomltype = 'quadratic' + config_cvmix_kpp_matching = 'SimpleShapes' + config_cvmix_kpp_monobobl = .false. + config_cvmix_kpp_stop_obl_search = 100.0 + config_cvmix_kpp_surface_layer_averaging = 5.0 + config_cvmix_kpp_surface_layer_extent = 0.1 + config_cvmix_kpp_use_enhanced_diff = .true. + config_cvmix_num_ri_smooth_loops = 2 + config_cvmix_prandtl_number = 1.0 + config_cvmix_shear_kpp_exp = 3 + config_cvmix_shear_kpp_nu_zero = 0.005 + config_cvmix_shear_kpp_ri_zero = 0.7 + config_cvmix_shear_mixing_scheme = 'KPP' + config_cvmix_shear_pp_alpha = 5.0 + config_cvmix_shear_pp_exp = 2.0 + config_cvmix_shear_pp_nu_zero = 0.005 + config_use_cvmix = .true. + config_use_cvmix_background = .true. + config_use_cvmix_convection = .true. + config_use_cvmix_double_diffusion = .false. + config_use_cvmix_fixed_boundary_layer = .false. + config_use_cvmix_kpp = .true. + config_use_cvmix_shear = .true. + config_use_cvmix_tidal_mixing = .false. + configure_cvmix_kpp_minimum_obl_under_sea_ice = 10.0 +/ +&forcing + config_flux_attenuation_coefficient = 0.001 + config_flux_attenuation_coefficient_runoff = 10.0 + config_use_bulk_thickness_flux = .true. + config_use_bulk_wind_stress = .true. +/ +&coupling + config_ssh_grad_relax_timescale = 86400.0 +/ +&shortwaveradiation + config_forcing_restart_file = 'Restart_forcing_time_stamp' + config_jerlov_water_type = 3 + config_surface_buoyancy_depth = 1 + config_sw_absorption_type = 'jerlov' +/ +&frazil_ice + config_frazil_fractional_thickness_limit = 0.1 + config_frazil_heat_of_fusion = 3.337e5 + config_frazil_ice_density = 1000.0 + config_frazil_in_open_ocean = .true. + config_frazil_land_ice_reference_salinity = 0.0 + config_frazil_maximum_depth = 100.0 + config_frazil_maximum_freezing_temperature = 0.0 + config_frazil_sea_ice_reference_salinity = 4.0 + config_frazil_under_land_ice = .true. + config_frazil_use_surface_pressure = .false. + config_specific_heat_sea_water = 3.996e3 + config_use_frazil_ice_formation = .true. +/ +&land_ice_fluxes + config_land_ice_flux_attenuation_coefficient = 10.0 + config_land_ice_flux_boundarylayerneighborweight = 0.0 + config_land_ice_flux_boundarylayerthickness = 10.0 + config_land_ice_flux_cp_ice = 2.009e3 + config_land_ice_flux_formulation = 'Jenkins' + config_land_ice_flux_isomip_gammat = 1e-4 + config_land_ice_flux_jenkins_heat_transfer_coefficient = 0.011 + config_land_ice_flux_jenkins_salt_transfer_coefficient = 3.1e-4 + config_land_ice_flux_mode = 'off' + config_land_ice_flux_rho_ice = 918 + config_land_ice_flux_rms_tidal_velocity = 5e-2 + config_land_ice_flux_topdragcoeff = 2.5e-3 + config_land_ice_flux_usehollandjenkinsadvdiff = .false. +/ +&advection + config_coef_3rd_order = 0.25 + config_horiz_tracer_adv_order = 3 + config_monotonic = .true. + config_vert_tracer_adv = 'stencil' + config_vert_tracer_adv_order = 3 +/ +&bottom_drag + config_bottom_drag_coeff = 1.0e-3 +/ +&ocean_constants + config_density0 = 1026.0 +/ +&pressure_gradient + config_common_level_weight = 0.5 + config_pressure_gradient_type = 'Jacobian_from_TS' +/ +&eos + config_eos_type = 'jm' + config_land_ice_cavity_freezing_temperature_coeff_0 = 6.22e-2 + config_land_ice_cavity_freezing_temperature_coeff_p = -7.43e-8 + config_land_ice_cavity_freezing_temperature_coeff_ps = -1.74e-10 + config_land_ice_cavity_freezing_temperature_coeff_s = -5.63e-2 + config_land_ice_cavity_freezing_temperature_reference_pressure = 0.0 + config_open_ocean_freezing_temperature_coeff_0 = -1.8 + config_open_ocean_freezing_temperature_coeff_p = 0.0 + config_open_ocean_freezing_temperature_coeff_ps = 0.0 + config_open_ocean_freezing_temperature_coeff_s = 0.0 + config_open_ocean_freezing_temperature_reference_pressure = 0.0 +/ +&eos_linear + config_eos_linear_alpha = 0.2 + config_eos_linear_beta = 0.8 + config_eos_linear_densityref = 1000.0 + config_eos_linear_sref = 35.0 + config_eos_linear_tref = 5.0 +/ +&split_explicit_ts + config_btr_dt = '0000_00:03:00' + config_btr_gam1_velwt1 = 0.5 + config_btr_gam2_sshwt1 = 1.0 + config_btr_gam3_velwt2 = 1.0 + config_btr_solve_ssh2 = .false. + config_btr_subcycle_loop_factor = 2 + config_n_bcl_iter_beg = 1 + config_n_bcl_iter_end = 2 + config_n_bcl_iter_mid = 2 + config_n_btr_cor_iter = 2 + config_n_ts_iter = 2 + config_vel_correction = .true. +/ +&testing + config_conduct_tests = .false. + config_tensor_test_function = 'sph_uCosCos' + config_test_tensors = .false. +/ +&debug + config_check_ssh_consistency = .true. + config_check_tracer_monotonicity = .false. + config_check_zlevel_consistency = .false. + config_disable_redi_horizontal_term1 = .false. + config_disable_redi_horizontal_term2 = .false. + config_disable_redi_horizontal_term3 = .false. + config_disable_redi_k33 = .false. + config_disable_thick_all_tend = .false. + config_disable_thick_hadv = .false. + config_disable_thick_sflux = .false. + config_disable_thick_vadv = .false. + config_disable_tr_adv = .false. + config_disable_tr_all_tend = .false. + config_disable_tr_hmix = .false. + config_disable_tr_nonlocalflux = .false. + config_disable_tr_sflux = .false. + config_disable_tr_vmix = .false. + config_disable_vel_all_tend = .false. + config_disable_vel_coriolis = .false. + config_disable_vel_hmix = .false. + config_disable_vel_pgrad = .false. + config_disable_vel_surface_stress = .false. + config_disable_vel_vadv = .false. + config_disable_vel_vmix = .false. + config_filter_btr_mode = .false. + config_include_ke_vertex = .false. + config_prescribe_thickness = .false. + config_prescribe_velocity = .false. + config_read_nearest_restart = .false. +/ +&constrain_haney_number + config_rx1_horiz_smooth_open_ocean_cells = 20 + config_rx1_horiz_smooth_weight = 1.0 + config_rx1_init_inner_weight = 0.1 + config_rx1_inner_iter_count = 10 + config_rx1_max = 5.0 + config_rx1_min_layer_thickness = 1.0 + config_rx1_min_levels = 3 + config_rx1_outer_iter_count = 20 + config_rx1_slope_weight = 1e-1 + config_rx1_vert_smooth_weight = 1.0 + config_rx1_zstar_weight = 1.0 + config_use_rx1_constraint = .false. +/ +&baroclinic_channel + config_baroclinic_channel_bottom_depth = 1000.0 + config_baroclinic_channel_bottom_temperature = 10.1 + config_baroclinic_channel_coriolis_parameter = -1.2e-4 + config_baroclinic_channel_gradient_width_dist = 40e3 + config_baroclinic_channel_gradient_width_frac = 0.08 + config_baroclinic_channel_salinity = 35.0 + config_baroclinic_channel_surface_temperature = 13.1 + config_baroclinic_channel_temperature_difference = 1.2 + config_baroclinic_channel_use_distances = .false. + config_baroclinic_channel_vert_levels = 20 +/ +&lock_exchange + config_lock_exchange_bottom_depth = 20.0 + config_lock_exchange_cold_temperature = 5.0 + config_lock_exchange_direction = 'y' + config_lock_exchange_isopycnal_min_thickness = 0.01 + config_lock_exchange_layer_type = 'z-level' + config_lock_exchange_salinity = 35.0 + config_lock_exchange_vert_levels = 20 + config_lock_exchange_warm_temperature = 30.0 +/ +&internal_waves + config_internal_waves_amplitude_width_dist = 50e3 + config_internal_waves_amplitude_width_frac = 0.33 + config_internal_waves_bottom_depth = 500.0 + config_internal_waves_bottom_temperature = 10.1 + config_internal_waves_isopycnal_displacement = 125.0 + config_internal_waves_layer_type = 'z-level' + config_internal_waves_salinity = 35.0 + config_internal_waves_surface_temperature = 20.1 + config_internal_waves_temperature_difference = 2.0 + config_internal_waves_use_distances = false + config_internal_waves_vert_levels = 20 +/ +&overflow + config_overflow_bottom_depth = 2000.0 + config_overflow_domain_temperature = 20.0 + config_overflow_isopycnal_min_thickness = 0.01 + config_overflow_layer_type = 'z-level' + config_overflow_plug_temperature = 10.0 + config_overflow_plug_width_dist = 20e3 + config_overflow_plug_width_frac = 0.10 + config_overflow_ridge_depth = 500.0 + config_overflow_salinity = 35.0 + config_overflow_slope_center_dist = 40e3 + config_overflow_slope_center_frac = 0.20 + config_overflow_slope_width_dist = 7e3 + config_overflow_slope_width_frac = 0.05 + config_overflow_use_distances = false + config_overflow_vert_levels = 100 +/ +&global_ocean + config_global_ocean_chlorophyll_varname = 'none' + config_global_ocean_clearsky_varname = 'none' + config_global_ocean_cull_inland_seas = .true. + config_global_ocean_deepen_critical_passages = .true. + config_global_ocean_depress_by_land_ice = .false. + config_global_ocean_depth_conversion_factor = 1.0 + config_global_ocean_depth_dimname = 'none' + config_global_ocean_depth_file = 'none' + config_global_ocean_depth_varname = 'none' + config_global_ocean_ecosys_depth_conversion_factor = 1.0 + config_global_ocean_ecosys_depth_varname = 'none' + config_global_ocean_ecosys_file = 'unknown' + config_global_ocean_ecosys_forcing_file = 'unknown' + config_global_ocean_ecosys_forcing_time_dimname = 'none' + config_global_ocean_ecosys_lat_varname = 'none' + config_global_ocean_ecosys_latlon_degrees = .true. + config_global_ocean_ecosys_lon_varname = 'none' + config_global_ocean_ecosys_method = 'bilinear_interpolation' + config_global_ocean_ecosys_ndepth_dimname = 'none' + config_global_ocean_ecosys_nlat_dimname = 'none' + config_global_ocean_ecosys_nlon_dimname = 'none' + config_global_ocean_ecosys_vert_levels = -1 + config_global_ocean_interior_restore_rate = 1.0e-7 + config_global_ocean_land_ice_topo_draft_varname = 'none' + config_global_ocean_land_ice_topo_file = 'none' + config_global_ocean_land_ice_topo_grounded_frac_varname = 'none' + config_global_ocean_land_ice_topo_ice_frac_varname = 'none' + config_global_ocean_land_ice_topo_lat_varname = 'none' + config_global_ocean_land_ice_topo_latlon_degrees = .true. + config_global_ocean_land_ice_topo_lon_varname = 'none' + config_global_ocean_land_ice_topo_nlat_dimname = 'none' + config_global_ocean_land_ice_topo_nlon_dimname = 'none' + config_global_ocean_land_ice_topo_thickness_varname = 'none' + config_global_ocean_minimum_depth = 15 + config_global_ocean_piston_velocity = 5.0e-5 + config_global_ocean_salinity_file = 'none' + config_global_ocean_salinity_varname = 'none' + config_global_ocean_smooth_ecosys_iterations = 0 + config_global_ocean_smooth_topography = .true. + config_global_ocean_smooth_ts_iterations = 0 + config_global_ocean_swdata_file = 'none' + config_global_ocean_swdata_lat_varname = 'none' + config_global_ocean_swdata_latlon_degrees = .true. + config_global_ocean_swdata_lon_varname = 'none' + config_global_ocean_swdata_method = 'bilinear_interpolation' + config_global_ocean_swdata_nlat_dimname = 'none' + config_global_ocean_swdata_nlon_dimname = 'none' + config_global_ocean_temperature_file = 'none' + config_global_ocean_temperature_varname = 'none' + config_global_ocean_topography_file = 'none' + config_global_ocean_topography_has_ocean_frac = .false. + config_global_ocean_topography_lat_varname = 'none' + config_global_ocean_topography_latlon_degrees = .true. + config_global_ocean_topography_lon_varname = 'none' + config_global_ocean_topography_method = 'bilinear_interpolation' + config_global_ocean_topography_nlat_dimname = 'none' + config_global_ocean_topography_nlon_dimname = 'none' + config_global_ocean_topography_ocean_frac_varname = 'none' + config_global_ocean_topography_varname = 'none' + config_global_ocean_tracer_depth_conversion_factor = 1.0 + config_global_ocean_tracer_depth_varname = 'none' + config_global_ocean_tracer_lat_varname = 'none' + config_global_ocean_tracer_latlon_degrees = .true. + config_global_ocean_tracer_lon_varname = 'none' + config_global_ocean_tracer_method = 'bilinear_interpolation' + config_global_ocean_tracer_ndepth_dimname = 'none' + config_global_ocean_tracer_nlat_dimname = 'none' + config_global_ocean_tracer_nlon_dimname = 'none' + config_global_ocean_tracer_vert_levels = -1 + config_global_ocean_windstress_conversion_factor = 1 + config_global_ocean_windstress_file = 'none' + config_global_ocean_windstress_lat_varname = 'none' + config_global_ocean_windstress_latlon_degrees = .true. + config_global_ocean_windstress_lon_varname = 'none' + config_global_ocean_windstress_meridional_varname = 'none' + config_global_ocean_windstress_method = 'bilinear_interpolation' + config_global_ocean_windstress_nlat_dimname = 'none' + config_global_ocean_windstress_nlon_dimname = 'none' + config_global_ocean_windstress_zonal_varname = 'none' + config_global_ocean_zenithangle_varname = 'none' +/ +&cvmix_wswsbf + config_cvmix_wswsbf_bottom_depth = 400.0 + config_cvmix_wswsbf_coriolis_parameter = 1.0e-4 + config_cvmix_wswsbf_evaporation_flux = 0.0 + config_cvmix_wswsbf_interior_salinity_restoring_rate = 1.0e-6 + config_cvmix_wswsbf_interior_temperature_restoring_rate = 1.0e-6 + config_cvmix_wswsbf_latent_heat_flux = 0.0 + config_cvmix_wswsbf_max_windstress = 0.10 + config_cvmix_wswsbf_mixed_layer_depth_salinity = 0.0 + config_cvmix_wswsbf_mixed_layer_depth_temperature = 0.0 + config_cvmix_wswsbf_mixed_layer_salinity_change = 0.0 + config_cvmix_wswsbf_mixed_layer_temperature_change = 0.0 + config_cvmix_wswsbf_rain_flux = 0.0 + config_cvmix_wswsbf_salinity_gradient = 0.0 + config_cvmix_wswsbf_salinity_gradient_mixed_layer = 0.0 + config_cvmix_wswsbf_salinity_piston_velocity = 4.0e-6 + config_cvmix_wswsbf_sensible_heat_flux = 0.0 + config_cvmix_wswsbf_shortwave_heat_flux = 0.0 + config_cvmix_wswsbf_surface_restoring_salinity = 35.0 + config_cvmix_wswsbf_surface_restoring_temperature = 15.0 + config_cvmix_wswsbf_surface_salinity = 35.0 + config_cvmix_wswsbf_surface_temperature = 15.0 + config_cvmix_wswsbf_temperature_gradient = 0.01 + config_cvmix_wswsbf_temperature_gradient_mixed_layer = 0.0 + config_cvmix_wswsbf_temperature_piston_velocity = 4.0e-6 + config_cvmix_wswsbf_vert_levels = 100 + config_cvmix_wswsbf_vertical_grid = 'uniform' +/ +&iso + config_iso_acc_wind = 0.2 + config_iso_asf_wind = -0.05 + config_iso_cont_slope_flag = .true. + config_iso_depression_center_lon = 60 + config_iso_depression_depth = 800 + config_iso_depression_flag = .true. + config_iso_depression_north_lat = -65 + config_iso_depression_south_lat = -72 + config_iso_depression_width = 480000 + config_iso_embayment_center_lat = -71 + config_iso_embayment_center_lon = 60 + config_iso_embayment_depth = 2000 + config_iso_embayment_flag = .true. + config_iso_embayment_radius = 500000 + config_iso_heat_flux_lat_mn = -53 + config_iso_heat_flux_lat_sm = -65 + config_iso_heat_flux_lat_ss = -70 + config_iso_heat_flux_middle = 10 + config_iso_heat_flux_north = -5 + config_iso_heat_flux_region1 = -5 + config_iso_heat_flux_region1_flag = false + config_iso_heat_flux_region1_radius = 300000 + config_iso_heat_flux_region2 = -5 + config_iso_heat_flux_region2_flag = false + config_iso_heat_flux_region2_radius = 240000 + config_iso_heat_flux_south = -5 + config_iso_initial_temp_h0 = 1200 + config_iso_initial_temp_h1 = 500 + config_iso_initial_temp_latn = -50 + config_iso_initial_temp_lats = -75 + config_iso_initial_temp_mt = 0.000075 + config_iso_initial_temp_t1 = 3.5 + config_iso_initial_temp_t2 = 4.0 + config_iso_main_channel_depth = 4000.0 + config_iso_max_cont_slope = 0.01 + config_iso_north_wall_lat = -50 + config_iso_plateau_center_lat = -58 + config_iso_plateau_center_lon = 300 + config_iso_plateau_flag = .true. + config_iso_plateau_height = 2000 + config_iso_plateau_radius = 200000 + config_iso_plateau_slope_width = 1000000 + config_iso_region1_center_lat = -75 + config_iso_region1_center_lon = 60 + config_iso_region2_center_lat = -71 + config_iso_region2_center_lon = 150 + config_iso_region3_center_lat = -71 + config_iso_region3_center_lon = 240 + config_iso_region4_center_lat = -71 + config_iso_region4_center_lon = 330 + config_iso_ridge_center_lon = 180 + config_iso_ridge_flag = .true. + config_iso_ridge_height = 2000.0 + config_iso_ridge_width = 2000000 + config_iso_salinity = 35.0 + config_iso_shelf_depth = 500 + config_iso_shelf_flag = .true. + config_iso_shelf_width = 120000 + config_iso_south_wall_lat = -70 + config_iso_surface_temperature_piston_velocity = 5.787e-5 + config_iso_temperature_restore_lcx1 = 600000 + config_iso_temperature_restore_lcx2 = 600000 + config_iso_temperature_restore_lcx3 = 600000 + config_iso_temperature_restore_lcx4 = 600000 + config_iso_temperature_restore_lcy1 = 600000 + config_iso_temperature_restore_lcy2 = 250000 + config_iso_temperature_restore_lcy3 = 250000 + config_iso_temperature_restore_lcy4 = 250000 + config_iso_temperature_restore_region1_flag = .true. + config_iso_temperature_restore_region2_flag = .true. + config_iso_temperature_restore_region3_flag = .true. + config_iso_temperature_restore_region4_flag = .true. + config_iso_temperature_restore_t1 = -1 + config_iso_temperature_restore_t2 = -1 + config_iso_temperature_restore_t3 = -1 + config_iso_temperature_restore_t4 = -1 + config_iso_temperature_sponge_h1 = 1000 + config_iso_temperature_sponge_l1 = 120000 + config_iso_temperature_sponge_t1 = 10 + config_iso_temperature_sponge_tau1 = 10.0 + config_iso_vert_levels = 100 + config_iso_wind_stress_max = 0.01 + config_iso_wind_trans = -65 +/ +&soma + config_soma_bottom_depth = 2500.0 + config_soma_center_latitude = 35.0 + config_soma_center_longitude = 0.0 + config_soma_density_difference = 4.0 + config_soma_density_difference_linear = 0.05 + config_soma_domain_width = 1.25e6 + config_soma_phi = 0.1 + config_soma_ref_density = 1000.0 + config_soma_restoring_temp_piston_vel = 1.0e-5 + config_soma_shelf_depth = 100.0 + config_soma_shelf_width = -0.4 + config_soma_surface_salinity = 33.0 + config_soma_surface_temp_restoring_at_center_latitude = 7.5 + config_soma_surface_temp_restoring_latitude_gradient = 0.5 + config_soma_surface_temperature = 20.0 + config_soma_thermocline_depth = 300.0 + config_soma_use_surface_temp_restoring = false + config_soma_vert_levels = 100 +/ +&ziso + config_ziso_add_easterly_wind_stress_asf = false + config_ziso_antarctic_shelf_front_width = 600000 + config_ziso_bottom_depth = 2500.0 + config_ziso_coriolis_gradient = 1e-11 + config_ziso_frazil_enable = false + config_ziso_frazil_temperature_anomaly = -3.0 + config_ziso_initial_temp_h1 = 300.0 + config_ziso_initial_temp_mt = 7.5e-5 + config_ziso_initial_temp_t1 = 6.0 + config_ziso_initial_temp_t2 = 3.6 + config_ziso_mean_restoring_temp = 3.0 + config_ziso_meridional_extent = 2.0e6 + config_ziso_reference_coriolis = -1e-4 + config_ziso_restoring_sponge_l = 8.0e4 + config_ziso_restoring_temp_dev_ta = 2.0 + config_ziso_restoring_temp_dev_tb = 2.0 + config_ziso_restoring_temp_piston_vel = 1.93e-5 + config_ziso_restoring_temp_tau = 30.0 + config_ziso_restoring_temp_ze = 1250.0 + config_ziso_shelf_depth = 500.0 + config_ziso_slope_center_position = 5.0e5 + config_ziso_slope_half_width = 1.0e5 + config_ziso_use_slopping_bathymetry = false + config_ziso_vert_levels = 100 + config_ziso_wind_stress_max = 0.2 + config_ziso_wind_stress_shelf_front_max = -0.05 + config_ziso_wind_transition_position = 800000.0 + config_ziso_zonal_extent = 1.0e6 +/ +&sub_ice_shelf_2d + config_sub_ice_shelf_2d_bottom_depth = 2000.0 + config_sub_ice_shelf_2d_bottom_salinity = 34.7 + config_sub_ice_shelf_2d_cavity_thickness = 25.0 + config_sub_ice_shelf_2d_edge_width = 15.0e3 + config_sub_ice_shelf_2d_slope_height = 500.0 + config_sub_ice_shelf_2d_surface_salinity = 34.5 + config_sub_ice_shelf_2d_temperature = 1.0 + config_sub_ice_shelf_2d_vert_levels = 20 + config_sub_ice_shelf_2d_y1 = 30.0e3 + config_sub_ice_shelf_2d_y2 = 60.0e3 +/ +&periodic_planar + config_periodic_planar_bottom_depth = 2500.0 + config_periodic_planar_velocity_strength = 1.0 + config_periodic_planar_vert_levels = 100 +/ +&ecosys_column + config_ecosys_column_bottom_depth = 6000.0 + config_ecosys_column_ecosys_filename = 'unknown' + config_ecosys_column_ts_filename = 'unknown' + config_ecosys_column_vert_levels = 100 + config_ecosys_column_vertical_grid = '100layerACMEv1' +/ +&sea_mount + config_sea_mount_bottom_depth = 5000.0 + config_sea_mount_coriolis_parameter = -1.0e-4 + config_sea_mount_density_alpha = 0.2 + config_sea_mount_density_coef_exp = 1028 + config_sea_mount_density_coef_linear = 1024 + config_sea_mount_density_depth_exp = 500 + config_sea_mount_density_depth_linear = 4500 + config_sea_mount_density_gradient_exp = 3.0 + config_sea_mount_density_gradient_linear = 0.1 + config_sea_mount_density_ref = 1028 + config_sea_mount_density_tref = 5.0 + config_sea_mount_height = 4500.0 + config_sea_mount_layer_type = 'sigma' + config_sea_mount_radius = 10.0e3 + config_sea_mount_salinity = 35.0 + config_sea_mount_stratification_type = 'exponential' + config_sea_mount_vert_levels = 10 + config_sea_mount_width = 40.0e3 +/ +&isomip + config_isomip_bottom_depth = -900.0 + config_isomip_coriolis_parameter = -1.4e-4 + config_isomip_eastern_boundary = 500e3 + config_isomip_ice_fraction1 = 1.0 + config_isomip_ice_fraction2 = 1.0 + config_isomip_ice_fraction3 = 1.0 + config_isomip_northern_boundary = 1000e3 + config_isomip_restoring_salinity = 34.4 + config_isomip_restoring_temperature = -1.9 + config_isomip_salinity = 34.4 + config_isomip_salinity_piston_velocity = 1.157e-5 + config_isomip_southern_boundary = 0.0 + config_isomip_temperature = -1.9 + config_isomip_temperature_piston_velocity = 1.157e-5 + config_isomip_vert_levels = 30 + config_isomip_vertical_level_distribution = 'constant' + config_isomip_western_boundary = 0.0 + config_isomip_y1 = 0.0 + config_isomip_y2 = 400e3 + config_isomip_y3 = 1000e3 + config_isomip_z1 = -700.0 + config_isomip_z2 = -200.0 + config_isomip_z3 = -200.0 +/ +&isomip_plus + config_isomip_plus_coriolis_parameter = -1.409e-4 + config_isomip_plus_effective_density = 1026. + config_isomip_plus_init_bot_sal = 34.5 + config_isomip_plus_init_bot_temp = -1.9 + config_isomip_plus_init_top_sal = 33.8 + config_isomip_plus_init_top_temp = -1.9 + config_isomip_plus_max_bottom_depth = -720.0 + config_isomip_plus_min_column_thickness = 10.0 + config_isomip_plus_min_ocean_fraction = 0.5 + config_isomip_plus_minimum_levels = 3 + config_isomip_plus_restore_bot_sal = 34.7 + config_isomip_plus_restore_bot_temp = 1.0 + config_isomip_plus_restore_evap_rate = 200 + config_isomip_plus_restore_rate = 10.0 + config_isomip_plus_restore_top_sal = 33.8 + config_isomip_plus_restore_top_temp = -1.9 + config_isomip_plus_restore_xmax = 800.0e3 + config_isomip_plus_restore_xmin = 790.0e3 + config_isomip_plus_topography_file = 'input_geometry_processed.nc' + config_isomip_plus_vert_levels = 36 + config_isomip_plus_vertical_level_distribution = 'constant' +/ +&tracer_forcing_activetracers + config_salinity_restoring_constant_piston_velocity = 0.0 + config_salinity_restoring_max_difference = 0.5 + config_use_activetracers = .true. + config_use_activetracers_exponential_decay = .false. + config_use_activetracers_idealage_forcing = .false. + config_use_activetracers_interior_restoring = .false. + config_use_activetracers_surface_bulk_forcing = .true. + config_use_activetracers_surface_restoring = .false. + config_use_activetracers_ttd_forcing = .false. + config_use_surface_salinity_monthly_restoring = .false. +/ +&tracer_forcing_debugtracers + config_use_debugtracers = .false. + config_use_debugtracers_exponential_decay = .false. + config_use_debugtracers_idealage_forcing = .false. + config_use_debugtracers_interior_restoring = .false. + config_use_debugtracers_surface_bulk_forcing = .false. + config_use_debugtracers_surface_restoring = .false. + config_use_debugtracers_ttd_forcing = .false. +/ +&tracer_forcing_ecosystracers + config_use_ecosystracers = .false. + config_use_ecosystracers_exponential_decay = .false. + config_use_ecosystracers_idealage_forcing = .false. + config_use_ecosystracers_interior_restoring = .false. + config_use_ecosystracers_sea_ice_coupling = .false. + config_use_ecosystracers_surface_bulk_forcing = .false. + config_use_ecosystracers_surface_restoring = .false. + config_use_ecosystracers_surface_value = .false. + config_use_ecosystracers_ttd_forcing = .false. +/ +&tracer_forcing_dmstracers + config_use_dmstracers = .false. + config_use_dmstracers_exponential_decay = .false. + config_use_dmstracers_idealage_forcing = .false. + config_use_dmstracers_interior_restoring = .false. + config_use_dmstracers_sea_ice_coupling = .false. + config_use_dmstracers_surface_bulk_forcing = .false. + config_use_dmstracers_surface_restoring = .false. + config_use_dmstracers_surface_value = .false. + config_use_dmstracers_ttd_forcing = .false. +/ +&tracer_forcing_macromoleculestracers + config_use_macromoleculestracers = .false. + config_use_macromoleculestracers_exponential_decay = .false. + config_use_macromoleculestracers_idealage_forcing = .false. + config_use_macromoleculestracers_interior_restoring = .false. + config_use_macromoleculestracers_sea_ice_coupling = .false. + config_use_macromoleculestracers_surface_bulk_forcing = .false. + config_use_macromoleculestracers_surface_restoring = .false. + config_use_macromoleculestracers_surface_value = .false. + config_use_macromoleculestracers_ttd_forcing = .false. +/ +&am_globalstats + config_am_globalstats_compute_interval = 'output_interval' + config_am_globalstats_compute_on_startup = .true. + config_am_globalstats_directory = 'analysis_members' + config_am_globalstats_enable = .true. + config_am_globalstats_output_stream = 'globalStatsOutput' + config_am_globalstats_text_file = .false. + config_am_globalstats_write_on_startup = .true. +/ +&am_surfaceareaweightedaverages + config_am_surfaceareaweightedaverages_compute_interval = '0000-00-00_01:00:00' + config_am_surfaceareaweightedaverages_compute_on_startup = .true. + config_am_surfaceareaweightedaverages_enable = .true. + config_am_surfaceareaweightedaverages_output_stream = 'surfaceAreaWeightedAveragesOutput' + config_am_surfaceareaweightedaverages_write_on_startup = .true. +/ +&am_watermasscensus + config_am_watermasscensus_compute_interval = '0000-00-00_01:00:00' + config_am_watermasscensus_compute_on_startup = .true. + config_am_watermasscensus_enable = .false. + config_am_watermasscensus_maxsalinity = 37.0 + config_am_watermasscensus_maxtemperature = 30.0 + config_am_watermasscensus_minsalinity = 32.0 + config_am_watermasscensus_mintemperature = -2.0 + config_am_watermasscensus_output_stream = 'waterMassCensusOutput' + config_am_watermasscensus_write_on_startup = .true. +/ +&am_layervolumeweightedaverage + config_am_layervolumeweightedaverage_compute_interval = '0000-00-00_01:00:00' + config_am_layervolumeweightedaverage_compute_on_startup = .true. + config_am_layervolumeweightedaverage_enable = .true. + config_am_layervolumeweightedaverage_output_stream = 'layerVolumeWeightedAverageOutput' + config_am_layervolumeweightedaverage_write_on_startup = .true. +/ +&am_zonalmean + config_am_zonalmean_compute_interval = '0000-00-00_01:00:00' + config_am_zonalmean_compute_on_startup = .true. + config_am_zonalmean_enable = .false. + config_am_zonalmean_max_bin = -1.0e34 + config_am_zonalmean_min_bin = -1.0e34 + config_am_zonalmean_num_bins = 180 + config_am_zonalmean_output_stream = 'zonalMeanOutput' + config_am_zonalmean_write_on_startup = .true. +/ +&am_okuboweiss + config_am_okuboweiss_compute_eddy_census = .true. + config_am_okuboweiss_compute_interval = '0000-00-00_01:00:00' + config_am_okuboweiss_compute_on_startup = .true. + config_am_okuboweiss_directory = 'analysis_members' + config_am_okuboweiss_eddy_min_cells = 20 + config_am_okuboweiss_enable = .false. + config_am_okuboweiss_lambda2_normalization = 1e-10 + config_am_okuboweiss_normalization = 1e-10 + config_am_okuboweiss_output_stream = 'okuboWeissOutput' + config_am_okuboweiss_threshold_value = -0.2 + config_am_okuboweiss_use_lat_lon_coords = .true. + config_am_okuboweiss_write_on_startup = .true. +/ +&am_meridionalheattransport + config_am_meridionalheattransport_compute_interval = '0000-00-00_01:00:00' + config_am_meridionalheattransport_compute_on_startup = .true. + config_am_meridionalheattransport_enable = .true. + config_am_meridionalheattransport_max_bin = -1.0e34 + config_am_meridionalheattransport_min_bin = -1.0e34 + config_am_meridionalheattransport_num_bins = 180 + config_am_meridionalheattransport_output_stream = 'meridionalHeatTransportOutput' + config_am_meridionalheattransport_region_group = '' + config_am_meridionalheattransport_write_on_startup = .true. +/ +&am_testcomputeinterval + config_am_testcomputeinterval_compute_interval = '00-00-01_00:00:00' + config_am_testcomputeinterval_compute_on_startup = .true. + config_am_testcomputeinterval_enable = .false. + config_am_testcomputeinterval_output_stream = 'testComputeIntervalOutput' + config_am_testcomputeinterval_write_on_startup = .true. +/ +&am_highfrequencyoutput + config_am_highfrequencyoutput_compute_interval = 'output_interval' + config_am_highfrequencyoutput_compute_on_startup = .false. + config_am_highfrequencyoutput_enable = .true. + config_am_highfrequencyoutput_output_stream = 'highFrequencyOutput' + config_am_highfrequencyoutput_write_on_startup = .false. +/ +&am_timefilters + config_am_timefilters_compute_cell_centered_values = .true. + config_am_timefilters_compute_interval = 'dt' + config_am_timefilters_compute_on_startup = .true. + config_am_timefilters_enable = .false. + config_am_timefilters_initialize_filters = .true. + config_am_timefilters_output_stream = 'timeFiltersOutput' + config_am_timefilters_restart_stream = 'timeFiltersRestart' + config_am_timefilters_tau = '90_00:00:00' + config_am_timefilters_write_on_startup = .true. +/ +&am_lagrparttrack + config_am_lagrparttrack_compute_interval = 'dt' + config_am_lagrparttrack_compute_on_startup = .false. + config_am_lagrparttrack_enable = .false. + config_am_lagrparttrack_filter_number = 0 + config_am_lagrparttrack_input_stream = 'lagrPartTrackInput' + config_am_lagrparttrack_output_stream = 'lagrPartTrackOutput' + config_am_lagrparttrack_region_stream = 'lagrPartTrackRegions' + config_am_lagrparttrack_reset_criteria = 'none' + config_am_lagrparttrack_reset_global_timestamp = '0000_00:00:00' + config_am_lagrparttrack_reset_if_inside_region = .false. + config_am_lagrparttrack_reset_if_outside_region = .false. + config_am_lagrparttrack_restart_stream = 'lagrPartTrackRestart' + config_am_lagrparttrack_write_on_startup = .true. +/ +&am_eliassenpalm + config_am_eliassenpalm_compute_interval = 'output_interval' + config_am_eliassenpalm_compute_on_startup = .true. + config_am_eliassenpalm_debug = .false. + config_am_eliassenpalm_enable = .false. + config_am_eliassenpalm_nbuoyancylayers = 45 + config_am_eliassenpalm_output_stream = 'eliassenPalmOutput' + config_am_eliassenpalm_restart_stream = 'eliassenPalmRestart' + config_am_eliassenpalm_rhomax_buoycoor = 1080 + config_am_eliassenpalm_rhomin_buoycoor = 900 + config_am_eliassenpalm_write_on_startup = .true. +/ +&am_mixedlayerdepths + config_am_mixedlayerdepths_compute_interval = '0000-00-00_01:00:00' + config_am_mixedlayerdepths_compute_on_startup = .true. + config_am_mixedlayerdepths_crit_dens_threshold = 0.03 + config_am_mixedlayerdepths_crit_temp_threshold = 0.2 + config_am_mixedlayerdepths_den_gradient_threshold = 5E-8 + config_am_mixedlayerdepths_dgradient = .true. + config_am_mixedlayerdepths_dthreshold = .true. + config_am_mixedlayerdepths_enable = .true. + config_am_mixedlayerdepths_interp_method = 1 + config_am_mixedlayerdepths_output_stream = 'mixedLayerDepthsOutput' + config_am_mixedlayerdepths_reference_pressure = 1.0E5 + config_am_mixedlayerdepths_temp_gradient_threshold = 5E-7 + config_am_mixedlayerdepths_tgradient = .true. + config_am_mixedlayerdepths_tthreshold = .true. + config_am_mixedlayerdepths_write_on_startup = .true. +/ +&am_regionalstatsdaily + config_am_regionalstatsdaily_1d_weighting_field = 'areaCell' + config_am_regionalstatsdaily_1d_weighting_function = 'mul' + config_am_regionalstatsdaily_2d_weighting_field = 'volumeCell' + config_am_regionalstatsdaily_2d_weighting_function = 'mul' + config_am_regionalstatsdaily_compute_interval = 'output_interval' + config_am_regionalstatsdaily_compute_on_startup = .false. + config_am_regionalstatsdaily_enable = .false. + config_am_regionalstatsdaily_input_stream = 'regionalMasksInput' + config_am_regionalstatsdaily_operation = 'avg' + config_am_regionalstatsdaily_output_stream = 'regionalStatsDailyOutput' + config_am_regionalstatsdaily_region_group = 'all' + config_am_regionalstatsdaily_region_type = 'cell' + config_am_regionalstatsdaily_restart_stream = 'regionalMasksInput' + config_am_regionalstatsdaily_vertical_dimension = 'nVertLevels' + config_am_regionalstatsdaily_vertical_mask = 'cellMask' + config_am_regionalstatsdaily_write_on_startup = .false. +/ +&am_regionalstatsweekly + config_am_regionalstatsweekly_1d_weighting_field = 'areaCell' + config_am_regionalstatsweekly_1d_weighting_function = 'mul' + config_am_regionalstatsweekly_2d_weighting_field = 'volumeCell' + config_am_regionalstatsweekly_2d_weighting_function = 'mul' + config_am_regionalstatsweekly_compute_interval = 'output_interval' + config_am_regionalstatsweekly_compute_on_startup = .false. + config_am_regionalstatsweekly_enable = .false. + config_am_regionalstatsweekly_input_stream = 'regionalMasksInput' + config_am_regionalstatsweekly_operation = 'avg' + config_am_regionalstatsweekly_output_stream = 'regionalStatsWeeklyOutput' + config_am_regionalstatsweekly_region_group = 'all' + config_am_regionalstatsweekly_region_type = 'cell' + config_am_regionalstatsweekly_restart_stream = 'regionalMasksInput' + config_am_regionalstatsweekly_vertical_dimension = 'nVertLevels' + config_am_regionalstatsweekly_vertical_mask = 'cellMask' + config_am_regionalstatsweekly_write_on_startup = .false. +/ +&am_regionalstatsmonthly + config_am_regionalstatsmonthly_1d_weighting_field = 'areaCell' + config_am_regionalstatsmonthly_1d_weighting_function = 'mul' + config_am_regionalstatsmonthly_2d_weighting_field = 'volumeCell' + config_am_regionalstatsmonthly_2d_weighting_function = 'mul' + config_am_regionalstatsmonthly_compute_interval = 'output_interval' + config_am_regionalstatsmonthly_compute_on_startup = .false. + config_am_regionalstatsmonthly_enable = .false. + config_am_regionalstatsmonthly_input_stream = 'regionalMasksInput' + config_am_regionalstatsmonthly_operation = 'avg' + config_am_regionalstatsmonthly_output_stream = 'regionalStatsMonthlyOutput' + config_am_regionalstatsmonthly_region_group = 'all' + config_am_regionalstatsmonthly_region_type = 'cell' + config_am_regionalstatsmonthly_restart_stream = 'regionalMasksInput' + config_am_regionalstatsmonthly_vertical_dimension = 'nVertLevels' + config_am_regionalstatsmonthly_vertical_mask = 'cellMask' + config_am_regionalstatsmonthly_write_on_startup = .false. +/ +&am_regionalstatscustom + config_am_regionalstatscustom_1d_weighting_field = 'areaCell' + config_am_regionalstatscustom_1d_weighting_function = 'mul' + config_am_regionalstatscustom_2d_weighting_field = 'volumeCell' + config_am_regionalstatscustom_2d_weighting_function = 'mul' + config_am_regionalstatscustom_compute_interval = 'output_interval' + config_am_regionalstatscustom_compute_on_startup = .false. + config_am_regionalstatscustom_enable = .false. + config_am_regionalstatscustom_input_stream = 'regionalMasksInput' + config_am_regionalstatscustom_operation = 'avg' + config_am_regionalstatscustom_output_stream = 'regionalStatsCustomOutput' + config_am_regionalstatscustom_region_group = 'all' + config_am_regionalstatscustom_region_type = 'cell' + config_am_regionalstatscustom_restart_stream = 'regionalMasksInput' + config_am_regionalstatscustom_vertical_dimension = 'nVertLevels' + config_am_regionalstatscustom_vertical_mask = 'cellMask' + config_am_regionalstatscustom_write_on_startup = .false. +/ +&am_timeseriesstatsdaily + config_am_timeseriesstatsdaily_backward_output_offset = '00-00-01_00:00:00' + config_am_timeseriesstatsdaily_compute_interval = '00-00-00_01:00:00' + config_am_timeseriesstatsdaily_compute_on_startup = .false. + config_am_timeseriesstatsdaily_duration_intervals = 'repeat_interval' + config_am_timeseriesstatsdaily_enable = .false. + config_am_timeseriesstatsdaily_operation = 'avg' + config_am_timeseriesstatsdaily_output_stream = 'timeSeriesStatsDailyOutput' + config_am_timeseriesstatsdaily_reference_times = 'initial_time' + config_am_timeseriesstatsdaily_repeat_intervals = 'reset_interval' + config_am_timeseriesstatsdaily_reset_intervals = '00-00-01_00:00:00' + config_am_timeseriesstatsdaily_restart_stream = 'timeSeriesStatsDailyRestart' + config_am_timeseriesstatsdaily_write_on_startup = .false. +/ +&am_timeseriesstatsmonthly + config_am_timeseriesstatsmonthly_backward_output_offset = '00-01-00_00:00:00' + config_am_timeseriesstatsmonthly_compute_interval = '00-00-00_01:00:00' + config_am_timeseriesstatsmonthly_compute_on_startup = .false. + config_am_timeseriesstatsmonthly_duration_intervals = 'repeat_interval' + config_am_timeseriesstatsmonthly_enable = .true. + config_am_timeseriesstatsmonthly_operation = 'avg' + config_am_timeseriesstatsmonthly_output_stream = 'timeSeriesStatsMonthlyOutput' + config_am_timeseriesstatsmonthly_reference_times = 'initial_time' + config_am_timeseriesstatsmonthly_repeat_intervals = 'reset_interval' + config_am_timeseriesstatsmonthly_reset_intervals = '00-01-00_00:00:00' + config_am_timeseriesstatsmonthly_restart_stream = 'timeSeriesStatsMonthlyRestart' + config_am_timeseriesstatsmonthly_write_on_startup = .false. +/ +&am_timeseriesstatsclimatology + config_am_timeseriesstatsclimatology_backward_output_offset = '00-03-00_00:00:00' + config_am_timeseriesstatsclimatology_compute_interval = '00-00-00_01:00:00' + config_am_timeseriesstatsclimatology_compute_on_startup = .false. + config_am_timeseriesstatsclimatology_duration_intervals = '00-03-00_00:00:00;00-03-00_00:00:00;00-03-00_00:00:00;00-03-00_00:00:00' + config_am_timeseriesstatsclimatology_enable = .false. + config_am_timeseriesstatsclimatology_operation = 'avg' + config_am_timeseriesstatsclimatology_output_stream = 'timeSeriesStatsClimatologyOutput' + config_am_timeseriesstatsclimatology_reference_times = '00-03-01_00:00:00;00-06-01_00:00:00;00-09-01_00:00:00;00-12-01_00:00:00' + config_am_timeseriesstatsclimatology_repeat_intervals = '01-00-00_00:00:00;01-00-00_00:00:00;01-00-00_00:00:00;01-00-00_00:00:00' + config_am_timeseriesstatsclimatology_reset_intervals = '1000-00-00_00:00:00;1000-00-00_00:00:00;1000-00-00_00:00:00;1000-00-00_00:00:00' + config_am_timeseriesstatsclimatology_restart_stream = 'timeSeriesStatsClimatologyRestart' + config_am_timeseriesstatsclimatology_write_on_startup = .false. +/ +&am_timeseriesstatscustom + config_am_timeseriesstatscustom_backward_output_offset = '00-00-01_00:00:00' + config_am_timeseriesstatscustom_compute_interval = '00-00-00_01:00:00' + config_am_timeseriesstatscustom_compute_on_startup = .false. + config_am_timeseriesstatscustom_duration_intervals = 'repeat_interval' + config_am_timeseriesstatscustom_enable = .false. + config_am_timeseriesstatscustom_operation = 'avg' + config_am_timeseriesstatscustom_output_stream = 'timeSeriesStatsCustomOutput' + config_am_timeseriesstatscustom_reference_times = 'initial_time' + config_am_timeseriesstatscustom_repeat_intervals = 'reset_interval' + config_am_timeseriesstatscustom_reset_intervals = '00-00-07_00:00:00' + config_am_timeseriesstatscustom_restart_stream = 'timeSeriesStatsCustomRestart' + config_am_timeseriesstatscustom_write_on_startup = .false. +/ +&am_pointwisestats + config_am_pointwisestats_compute_interval = 'output_interval' + config_am_pointwisestats_compute_on_startup = .true. + config_am_pointwisestats_enable = .false. + config_am_pointwisestats_output_stream = 'pointwiseStatsOutput' + config_am_pointwisestats_write_on_startup = .true. +/ +&am_debugdiagnostics + config_am_debugdiagnostics_check_state = .true. + config_am_debugdiagnostics_compute_interval = 'dt' + config_am_debugdiagnostics_compute_on_startup = .true. + config_am_debugdiagnostics_enable = .false. + config_am_debugdiagnostics_output_stream = 'debugDiagnosticsOutput' + config_am_debugdiagnostics_write_on_startup = .false. +/ +&am_rpncalculator + config_am_rpncalculator_compute_interval = '0010-00-00_00:00:00' + config_am_rpncalculator_compute_on_startup = .true. + config_am_rpncalculator_enable = .false. + config_am_rpncalculator_expression_1 = 'a b *' + config_am_rpncalculator_expression_2 = 'none' + config_am_rpncalculator_expression_3 = 'none' + config_am_rpncalculator_expression_4 = 'none' + config_am_rpncalculator_output_name_1 = 'volumeCell' + config_am_rpncalculator_output_name_2 = 'none' + config_am_rpncalculator_output_name_3 = 'none' + config_am_rpncalculator_output_name_4 = 'none' + config_am_rpncalculator_output_stream = 'none' + config_am_rpncalculator_variable_a = 'layerThickness' + config_am_rpncalculator_variable_b = 'areaCell' + config_am_rpncalculator_variable_c = 'none' + config_am_rpncalculator_variable_d = 'none' + config_am_rpncalculator_variable_e = 'none' + config_am_rpncalculator_variable_f = 'none' + config_am_rpncalculator_variable_g = 'none' + config_am_rpncalculator_variable_h = 'none' + config_am_rpncalculator_write_on_startup = .false. +/ +&am_transecttransport + config_am_transecttransport_compute_interval = 'output_interval' + config_am_transecttransport_compute_on_startup = .true. + config_am_transecttransport_enable = .false. + config_am_transecttransport_output_stream = 'transectTransportOutput' + config_am_transecttransport_transect_group = 'all' + config_am_transecttransport_write_on_startup = .true. +/ +&am_eddyproductvariables + config_am_eddyproductvariables_compute_interval = 'dt' + config_am_eddyproductvariables_compute_on_startup = .true. + config_am_eddyproductvariables_enable = .false. + config_am_eddyproductvariables_output_stream = 'eddyProductVariablesOutput' + config_am_eddyproductvariables_write_on_startup = .false. +/ +&am_mocstreamfunction + config_am_mocstreamfunction_compute_interval = 'output_interval' + config_am_mocstreamfunction_compute_on_startup = .true. + config_am_mocstreamfunction_enable = .false. + config_am_mocstreamfunction_max_bin = -1.0e34 + config_am_mocstreamfunction_min_bin = -1.0e34 + config_am_mocstreamfunction_normal_velocity_value = 'normalVelocity' + config_am_mocstreamfunction_num_bins = 180 + config_am_mocstreamfunction_output_stream = 'mocStreamfunctionOutput' + config_am_mocstreamfunction_region_group = 'all' + config_am_mocstreamfunction_transect_group = 'all' + config_am_mocstreamfunction_vertical_velocity_value = 'vertVelocityTop' + config_am_mocstreamfunction_write_on_startup = .true. +/ diff --git a/mpas_analysis/test/test_remap_obs_clim_subtask/remap_mld_obs.py b/mpas_analysis/test/test_remap_obs_clim_subtask/remap_mld_obs.py new file mode 100644 index 000000000..ff7ce2a20 --- /dev/null +++ b/mpas_analysis/test/test_remap_obs_clim_subtask/remap_mld_obs.py @@ -0,0 +1,31 @@ +import numpy + +from mpas_analysis.shared.interpolation import Remapper +from mpas_analysis.shared.grid import LatLonGridDescriptor +from mpas_analysis.shared.constants import constants + +inputFileName = '/media/xylar/extra_data/analysis/output/GMPAS-QU240/' \ + 'remap_obs/clim/obs/mld_1.0x1.0degree.nc' + +obsDescriptor = LatLonGridDescriptor.read(fileName=inputFileName, + latVarName='lat', + lonVarName='lon') + +comparisonLatRes = 4. +comparisonLonRes = 4. + +nLat = int((constants.latmax-constants.latmin)/comparisonLatRes)+1 +nLon = int((constants.lonmax-constants.lonmin)/comparisonLonRes)+1 +lat = numpy.linspace(constants.latmin, constants.latmax, nLat) +lon = numpy.linspace(constants.lonmin, constants.lonmax, nLon) + +comparisonDescriptor = LatLonGridDescriptor.create(lat, lon, units='degrees') + +remapper = Remapper(obsDescriptor, comparisonDescriptor, + mappingFileName='map.nc') + +remapper.build_mapping_file() + +remapper.remap_file(inputFileName, 'mld_4.0x4.0degree.nc', + ['mld', 'month', 'year'], + renormalize=0.05) diff --git a/mpas_analysis/test/test_remap_obs_clim_subtask/streams.ocean b/mpas_analysis/test/test_remap_obs_clim_subtask/streams.ocean new file mode 100644 index 000000000..1a025681b --- /dev/null +++ b/mpas_analysis/test/test_remap_obs_clim_subtask/streams.ocean @@ -0,0 +1,604 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/run_mpas_analysis b/run_mpas_analysis index e69887bd7..f6b1ee5df 100755 --- a/run_mpas_analysis +++ b/run_mpas_analysis @@ -9,7 +9,8 @@ Authors Xylar Asay-Davis, Phillip J. Wolfram """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import absolute_import, division, print_function, \ + unicode_literals import matplotlib as mpl import argparse @@ -191,7 +192,7 @@ def add_task_and_subtasks(analysisTask, analysesToGenerate, print("ERROR: prerequisite task {} of analysis task {}" " failed during check,\n" " so this task will not be run".format( - prereq.printTaskName, taskTitle)) + prereq.printTaskName, taskTitle)) analysisTask._setupStatus = 'fail' return @@ -333,7 +334,8 @@ def run_analysis(config, analyses): # {{{ taskTitle = analysisTask.printTaskName if analysisTask._runStatus.value == AnalysisTask.SUCCESS: - print(" Task {} has finished successfully.".format(taskTitle)) + print(" Task {} has finished successfully.".format( + taskTitle)) elif analysisTask._runStatus.value == AnalysisTask.FAIL: print("ERROR in task {}. See log file {} for details".format( taskTitle, analysisTask._logFileName))