diff --git a/docs/developers_guide/api.rst b/docs/developers_guide/api.rst index e23a0e839..c6cb11fd6 100644 --- a/docs/developers_guide/api.rst +++ b/docs/developers_guide/api.rst @@ -94,6 +94,9 @@ Ocean tasks WoaTransects WoceTransects +Ocean subtasks +-------------- + .. currentmodule:: mpas_analysis.ocean.compute_anomaly_subtask .. autosummary:: @@ -116,6 +119,19 @@ Ocean tasks PlotHovmollerSubtask +Ocean utilities +--------------- + +.. currentmodule:: mpas_analysis.ocean.utility + +.. autosummary:: + :toctree: generated/ + + add_standard_regions_and_subset + get_standard_region_names + compute_zmid + + Sea ice tasks ------------- diff --git a/docs/users_guide/config/regions.rst b/docs/users_guide/config/regions.rst index aa2a25729..e41fe60aa 100644 --- a/docs/users_guide/config/regions.rst +++ b/docs/users_guide/config/regions.rst @@ -15,10 +15,10 @@ within MPAS-Analysis using region mask files:: # list of region names (needs to be in the same order as region indices in # time-series stats) - regions = ['arctic', 'equatorial', 'so', 'nino3', 'nino4', 'nino3.4', 'global'] - # list of plot titles (needs to be in the same order as region indices in - # time-series stats) - plotTitles = ['Arctic', 'Equatorial (15S-15N)', 'Southern Ocean', 'Nino 3', + regionShortNames = ['arctic', 'equatorial', 'so', 'nino3', 'nino4', 'nino3.4', + 'global'] + # list of full names (e.g. for plot titles) same order as regionShortNames + regionNames = ['Arctic', 'Equatorial (15S-15N)', 'Southern Ocean', 'Nino 3', 'Nino 4', 'Nino 3.4', 'Global Ocean'] diff --git a/docs/users_guide/tasks/timeSeriesOHCAnomaly.rst b/docs/users_guide/tasks/timeSeriesOHCAnomaly.rst index 4ec726ef8..35ea82255 100644 --- a/docs/users_guide/tasks/timeSeriesOHCAnomaly.rst +++ b/docs/users_guide/tasks/timeSeriesOHCAnomaly.rst @@ -21,8 +21,8 @@ The following configuration options are available for this task:: ## options related to plotting time series of ocean heat content (OHC) ## anomalies from year 1 - # list of regions to plot from the region list in [regions] below - regions = ['global'] + # list of region shrot names to plot from the region list in [regions] above + regionShortNames = ['global'] # approximate depths (m) separating plots of the upper, middle and lower ocean depths = [700, 2000] diff --git a/docs/users_guide/tasks/timeSeriesSST.rst b/docs/users_guide/tasks/timeSeriesSST.rst index 3ba29f8c4..19a6dffcc 100644 --- a/docs/users_guide/tasks/timeSeriesSST.rst +++ b/docs/users_guide/tasks/timeSeriesSST.rst @@ -20,8 +20,8 @@ The following configuration options are available for this task:: [timeSeriesSST] ## options related to plotting time series of sea surface temperature (SST) - # list of regions to plot from the region list in [regions] below - regions = ['global'] + # list of region shrot names to plot from the region list in [regions] above + regionShortNames = ['global'] # Number of points over which to compute moving average (e.g., for monthly # output, movingAveragePoints=12 corresponds to a 12-month moving average diff --git a/docs/users_guide/tasks/timeSeriesSalinityAnomaly.rst b/docs/users_guide/tasks/timeSeriesSalinityAnomaly.rst index 8ad608e82..6b0cb597f 100644 --- a/docs/users_guide/tasks/timeSeriesSalinityAnomaly.rst +++ b/docs/users_guide/tasks/timeSeriesSalinityAnomaly.rst @@ -20,8 +20,8 @@ The following configuration options are available for this task:: [hovmollerSalinityAnomaly] ## options related to plotting time series of salinity vs. depth - # list of regions to plot from the region list in [regions] below - regions = ['global'] + # list of region shrot names to plot from the region list in [regions] above + regionShortNames = ['global'] # Number of points over which to compute moving average(e.g., for monthly # output, movingAveragePoints=12 corresponds to a 12-month moving average diff --git a/docs/users_guide/tasks/timeSeriesTemperatureAnomaly.rst b/docs/users_guide/tasks/timeSeriesTemperatureAnomaly.rst index 4251efae6..a30827584 100644 --- a/docs/users_guide/tasks/timeSeriesTemperatureAnomaly.rst +++ b/docs/users_guide/tasks/timeSeriesTemperatureAnomaly.rst @@ -20,8 +20,8 @@ The following configuration options are available for this task:: [hovmollerTemperatureAnomaly] ## options related to plotting time series of potential temperature vs. depth - # list of regions to plot from the region list in [regions] below - regions = ['global'] + # list of region shrot names to plot from the region list in [regions] above + regionShortNames = ['global'] # Number of points over which to compute moving average(e.g., for monthly # output, movingAveragePoints=12 corresponds to a 12-month moving average diff --git a/mpas_analysis/default.cfg b/mpas_analysis/default.cfg index 1d347057f..2565c1731 100755 --- a/mpas_analysis/default.cfg +++ b/mpas_analysis/default.cfg @@ -358,11 +358,11 @@ endYear = 20 # list of region names (needs to be in the same order as region indices in # time-series stats) -regions = ['arctic', 'equatorial', 'so', 'nino3', 'nino4', 'nino3.4', 'global'] -# list of plot titles (needs to be in the same order as region indices in -# time-series stats) -plotTitles = ['Arctic', 'Equatorial (15S-15N)', 'Southern Ocean', 'Nino 3', - 'Nino 4', 'Nino 3.4', 'Global Ocean'] +regionShortNames = ['arctic', 'equatorial', 'so', 'nino3', 'nino4', 'nino3.4', + 'global'] +# list of full names (e.g. for plot titles) same order as regionShortNames +regionNames = ['Arctic', 'Equatorial (15S-15N)', 'Southern Ocean', 'Nino 3', + 'Nino 4', 'Nino 3.4', 'Global Ocean'] [plot] @@ -608,8 +608,8 @@ concentrationAltibergSH = Altiberg/Altiberg_1991-2017_20180308.nc ## options related to plotting time series of ocean heat content (OHC) ## anomalies from year 1 -# list of regions to plot from the region list in [regions] below -regions = ['global'] +# list of region short names to plot from the region list in [regions] above +regionShortNames = ['global'] # approximate depths (m) separating plots of the upper, middle and lower ocean depths = [700, 2000] @@ -672,8 +672,8 @@ movingAveragePoints = 12 [hovmollerTemperatureAnomaly] ## options related to plotting time series of potential temperature vs. depth -# list of regions to plot from the region list in [regions] below -regions = ['global'] +# list of region short names to plot from the region list in [regions] above +regionShortNames = ['global'] # Number of points over which to compute moving average(e.g., for monthly # output, movingAveragePoints=12 corresponds to a 12-month moving average @@ -706,8 +706,8 @@ contourLevelsResult = np.arange(-5.0, 5.51, 0.5) [hovmollerSalinityAnomaly] ## options related to plotting time series of salinity vs. depth -# list of regions to plot from the region list in [regions] below -regions = ['global'] +# list of region short names to plot from the region list in [regions] above +regionShortNames = ['global'] # Number of points over which to compute moving average(e.g., for monthly # output, movingAveragePoints=12 corresponds to a 12-month moving average @@ -743,7 +743,7 @@ contourLevelsResult = np.arange(-2., 2.01, 0.2) ## options related to plotting time series of sea surface temperature (SST) # list of regions to plot from the region list in [regions] below -regions = ['global'] +regionShortNames = ['global'] # Number of points over which to compute moving average (e.g., for monthly # output, movingAveragePoints=12 corresponds to a 12-month moving average diff --git a/mpas_analysis/ocean/compute_anomaly_subtask.py b/mpas_analysis/ocean/compute_anomaly_subtask.py index 6bcdfc357..00d9fabee 100644 --- a/mpas_analysis/ocean/compute_anomaly_subtask.py +++ b/mpas_analysis/ocean/compute_anomaly_subtask.py @@ -28,6 +28,8 @@ from mpas_analysis.shared.time_series import \ compute_moving_avg_anomaly_from_start +from mpas_analysis.ocean.utility import add_standard_regions_and_subset + class ComputeAnomalySubtask(AnalysisTask): """ @@ -184,6 +186,9 @@ def run_task(self): movingAveragePoints=self.movingAveragePoints, alter_dataset=self.alter_dataset) + if 'nOceanRegions' in ds.dims or 'nOceanRegionsTmp' in ds.dims: + ds = add_standard_regions_and_subset(ds, config) + outFileName = self.outFileName if not os.path.isabs(outFileName): baseDirectory = build_config_full_path( diff --git a/mpas_analysis/ocean/index_nino34.py b/mpas_analysis/ocean/index_nino34.py index e79a538f7..d4b018e70 100644 --- a/mpas_analysis/ocean/index_nino34.py +++ b/mpas_analysis/ocean/index_nino34.py @@ -38,6 +38,8 @@ from mpas_analysis.shared import AnalysisTask from mpas_analysis.shared.html import write_image_xml +from mpas_analysis.ocean.utility import add_standard_regions_and_subset + class IndexNino34(AnalysisTask): """ @@ -129,7 +131,7 @@ def setup_and_check(self): regionToPlot = config.get('indexNino34', 'region') if regionToPlot not in ['nino3.4', 'nino3', 'nino4']: - raise ValueError('Unexpectes El Nino Index region {}'.format( + raise ValueError('Unexpected El Nino Index region {}'.format( regionToPlot)) ninoIndexNumber = regionToPlot[4:] @@ -193,9 +195,7 @@ def run_task(self): # regionIndex should correspond to NINO34 in surface weighted Average # AM - regions = config.getexpression('regions', 'regions') regionToPlot = config.get('indexNino34', 'region') - regionIndex = regions.index(regionToPlot) # Load data: ds = open_mpas_dataset(fileName=self.inputFile, @@ -204,6 +204,11 @@ def run_task(self): startDate=startDate, endDate=endDate) + ds = add_standard_regions_and_subset(ds, config, + regionShortNames=[regionToPlot]) + # we want to collapse the nOceanRegions dimension + ds = ds.isel(nOceanRegions=0) + # Observations have been processed to the nino34Index prior to reading dsObs = xr.open_dataset(dataPath, decode_cf=False, decode_times=False) # add the days between 0001-01-01 and the refDate so we have a new @@ -215,7 +220,7 @@ def run_task(self): self.logger.info(' Compute El Nino {} Index...'.format( ninoIndexNumber)) varName = self.variableList[0] - regionSST = ds[varName].isel(nOceanRegions=regionIndex) + regionSST = ds[varName] nino34Main = self._compute_nino34_index(regionSST, calendar) # Compute the observational index over the entire time range @@ -262,7 +267,10 @@ def run_task(self): calendar=calendar, variableList=self.variableList) - regionSSTRef = dsRef[varName].isel(nOceanRegions=regionIndex) + dsRef = add_standard_regions_and_subset( + dsRef, self.controlConfig, regionShortNames=[regionToPlot]) + + regionSSTRef = dsRef[varName] nino34Ref = self._compute_nino34_index(regionSSTRef, calendar) nino34s = [nino34Subset, nino34Main[2:-3], nino34Ref[2:-3]] diff --git a/mpas_analysis/ocean/plot_depth_integrated_time_series_subtask.py b/mpas_analysis/ocean/plot_depth_integrated_time_series_subtask.py index 4b2f0a9be..57d18df16 100644 --- a/mpas_analysis/ocean/plot_depth_integrated_time_series_subtask.py +++ b/mpas_analysis/ocean/plot_depth_integrated_time_series_subtask.py @@ -31,6 +31,8 @@ from mpas_analysis.shared.time_series import compute_moving_avg, \ combine_time_series_with_ncrcat +from mpas_analysis.ocean.utility import add_standard_regions_and_subset + class PlotDepthIntegratedTimeSeriesSubtask(AnalysisTask): """ @@ -157,7 +159,8 @@ def __init__(self, parentTask, regionName, inFileName, outFileLabel, if subtaskName is None: suffix = regionName[0].upper() + regionName[1:] - subtaskName = 'plotDepthIntegratedTimeSeries{}'.format(suffix) + suffix = suffix.replace(' ', '_') + subtaskName = f'plotDepthIntegratedTimeSeries{suffix}' # first, call the constructor from the base class (AnalysisTask) super(PlotDepthIntegratedTimeSeriesSubtask, self).__init__( @@ -205,7 +208,7 @@ def setup_and_check(self): if self.controlConfig is not None: # we need to know what file to read from the control run so # an absolute path won't work - assert(not os.path.isabs(self.inFileName)) + assert not os.path.isabs(self.inFileName) baseDirectory = build_config_full_path( self.controlConfig, 'output', 'timeSeriesSubdirectory') @@ -217,7 +220,7 @@ def setup_and_check(self): 'runs', 'preprocessedReferenceRunName') if preprocessedReferenceRunName != 'None': - assert(not os.path.isabs(self.inFileName)) + assert not os.path.isabs(self.inFileName) baseDirectory = build_config_full_path( config, 'output', 'timeSeriesSubdirectory') @@ -263,11 +266,6 @@ def run_task(self): mainRunName = config.get('runs', 'mainRunName') - plotTitles = config.getexpression('regions', 'plotTitles') - allRegionNames = config.getexpression('regions', 'regions') - regionIndex = allRegionNames.index(self.regionName) - regionNameInTitle = plotTitles[regionIndex] - startDate = config.get('timeSeries', 'startDate') endDate = config.get('timeSeries', 'endDate') @@ -279,7 +277,17 @@ def run_task(self): timeVariableNames=None, startDate=startDate, endDate=endDate) - ds = ds.isel(nOceanRegionsTmp=regionIndex) + if 'regionNames' in ds.coords: + # we added region names already + ds = ds.set_xindex('regionNames') + ds = ds.sel(regionNames=self.regionName) + regionNameInTitle = ds.regionNames.values + else: + # we need to add region names and select the right region by short + # name + ds = add_standard_regions_and_subset( + ds, config, regionShortNames=[self.regionName]) + regionNameInTitle = ds.regionNames.values[0] depths = ds.depth.values @@ -436,7 +444,17 @@ def run_task(self): timeVariableNames=None, startDate=controlStartDate, endDate=controlEndDate) - dsRef = dsRef.isel(nOceanRegionsTmp=regionIndex) + + if 'regionNames' in dsRef.coords: + # we added region names already + dsRef = dsRef.set_xindex('regionNames') + dsRef = dsRef.sel(regionNames=self.regionName) + else: + # we need to add region names and select the right region by + # short name + dsRef = add_standard_regions_and_subset( + dsRef, self.controlConfig, + regionShortNames=[self.regionName]) color = config.get('timeSeries', 'controlColor') @@ -468,8 +486,9 @@ def run_task(self): fig = timeseries_analysis_plot( config=config, dsvalues=timeSeries, calendar=calendar, - title=title, xlabel=xLabel, ylabel=yLabel, movingAveragePoints=None, - lineColors=lineColors, lineStyles=lineStyles, markers=lineMarkers, + title=title, xlabel=xLabel, ylabel=yLabel, + movingAveragePoints=None, lineColors=lineColors, + lineStyles=lineStyles, markers=lineMarkers, lineWidths=lineWidths, legendText=legendText, maxPoints=maxPoints, firstYearXTicks=firstYearXTicks, yearStrideXTicks=yearStrideXTicks) diff --git a/mpas_analysis/ocean/plot_hovmoller_subtask.py b/mpas_analysis/ocean/plot_hovmoller_subtask.py index 419ce2910..b590263bb 100644 --- a/mpas_analysis/ocean/plot_hovmoller_subtask.py +++ b/mpas_analysis/ocean/plot_hovmoller_subtask.py @@ -161,6 +161,7 @@ def __init__(self, parentTask, regionName, inFileName, outFileLabel, if subtaskName is None: suffix = regionName[0].upper() + regionName[1:] + suffix = suffix.replace(' ', '_') subtaskName = 'plotHovmoller{}'.format(suffix) # first, call the constructor from the base class (AnalysisTask) @@ -208,7 +209,7 @@ def setup_and_check(self): config = self.config if self.controlConfig is not None: - assert(not os.path.isabs(self.inFileName)) + assert not os.path.isabs(self.inFileName) baseDirectory = build_config_full_path( self.controlConfig, 'output', 'timeSeriesSubdirectory') @@ -251,21 +252,12 @@ def run_task(self): self.logger.info(' Load ocean data...') ds = xr.open_dataset(self.inFileName) + print(self.inFileName) - if 'regionNames' in ds.coords: + regionNameInTitle = self.regionName.replace('_', ' ') - allRegionNames = decode_strings(ds.regionNames) - regionIndex = allRegionNames.index(self.regionName) - regionNameInTitle = self.regionName.replace('_', ' ') - regionDim = ds.regionNames.dims[0] - else: - plotTitles = config.getexpression('regions', 'plotTitles') - allRegionNames = config.getexpression('regions', 'regions') - regionIndex = allRegionNames.index(self.regionName) - regionNameInTitle = plotTitles[regionIndex] - regionDim = 'nOceanRegionsTmp' - - ds = ds.isel(**{regionDim: regionIndex}) + ds = ds.set_xindex('regionNames') + ds = ds.sel(regionNames=self.regionName) # Note: restart file, not a mesh file because we need refBottomDepth, # not in a mesh file @@ -290,6 +282,7 @@ def run_task(self): # drop any NaN values, because this causes issues with rolling averages mask = field.notnull().all(dim='Time') + print(mask) xLabel = 'Time (years)' yLabel = 'Depth (m)' @@ -328,21 +321,9 @@ def run_task(self): controlConfig = self.controlConfig dsRef = xr.open_dataset(self.controlFileName) - if 'regionNames' in dsRef.coords: - allRegionNames = decode_strings(dsRef.regionNames) - regionIndex = allRegionNames.index(self.regionName) - regionNameInTitle = self.regionName.replace('_', ' ') - regionDim = dsRef.regionNames.dims[0] - else: - plotTitles = controlConfig.getexpression('regions', - 'plotTitles') - allRegionNames = controlConfig.getexpression('regions', - 'regions') - regionIndex = allRegionNames.index(self.regionName) - regionNameInTitle = plotTitles[regionIndex] - regionDim = 'nOceanRegionsTmp' - - dsRef = dsRef.isel(**{regionDim: regionIndex}) + dsRef = dsRef.set_xindex('regionNames') + dsRef = dsRef.sel(regionNames=self.regionName) + refField = dsRef[self.mpasFieldName] # drop any NaN values, because this causes issues with rolling # averages @@ -352,16 +333,16 @@ def run_task(self): z = z.where(mask, drop=True) field = field.where(mask, drop=True) refField = refField.where(mask, drop=True) - assert (field.shape == refField.shape) + assert field.shape == refField.shape # make sure the start and end time sare the same - assert(int(field.Time.values[0]) == int(refField.Time.values[0])) - assert(int(field.Time.values[-1]) == int(refField.Time.values[-1])) + assert int(field.Time.values[0]) == int(refField.Time.values[0]) + assert int(field.Time.values[-1]) == int(refField.Time.values[-1]) # we're seeing issues with slightly different times between runs # so let's copy them refField['Time'] = field.Time diff = field - refField assert (field.shape == diff.shape) - refTitle = self.controlConfig.get('runs', 'mainRunName') + refTitle = controlConfig.get('runs', 'mainRunName') diffTitle = 'Main - Control' if config.has_option(sectionName, 'titleFontSize'): diff --git a/mpas_analysis/ocean/time_series_ohc_anomaly.py b/mpas_analysis/ocean/time_series_ohc_anomaly.py index ac258bcfe..7a8de35f8 100644 --- a/mpas_analysis/ocean/time_series_ohc_anomaly.py +++ b/mpas_analysis/ocean/time_series_ohc_anomaly.py @@ -25,6 +25,7 @@ PlotDepthIntegratedTimeSeriesSubtask from mpas_analysis.shared.constants import constants as mpas_constants +from mpas_analysis.ocean.utility import get_standard_region_names class TimeSeriesOHCAnomaly(AnalysisTask): @@ -63,7 +64,8 @@ def __init__(self, config, mpasTimeSeriesTask, controlConfig=None): tags=['timeSeries', 'ohc', 'publicObs', 'anomaly']) sectionName = 'timeSeriesOHCAnomaly' - regionNames = config.getexpression(sectionName, 'regions') + regionShortNames = config.getexpression(sectionName, + 'regionShortNames') movingAveragePoints = config.getint(sectionName, 'movingAveragePoints') self.variableDict = {} @@ -85,6 +87,7 @@ def __init__(self, config, mpasTimeSeriesTask, controlConfig=None): alter_dataset=self._compute_ohc) self.add_subtask(anomalyTask) + regionNames = get_standard_region_names(config, regionShortNames) for regionName in regionNames: caption = 'Trend of {} OHC Anomaly vs depth'.format( regionName) @@ -133,10 +136,6 @@ def _compute_ohc(self, ds): """ Compute the OHC time series. """ - - # regionNames = self.config.getexpression('regions', 'regions') - # ds['regionNames'] = ('nOceanRegionsTmp', regionNames) - # for convenience, rename the variables to simpler, shorter names ds = ds.rename(self.variableDict) diff --git a/mpas_analysis/ocean/time_series_salinity_anomaly.py b/mpas_analysis/ocean/time_series_salinity_anomaly.py index dd1929dd0..52290e897 100644 --- a/mpas_analysis/ocean/time_series_salinity_anomaly.py +++ b/mpas_analysis/ocean/time_series_salinity_anomaly.py @@ -16,6 +16,8 @@ from mpas_analysis.ocean.compute_anomaly_subtask import ComputeAnomalySubtask from mpas_analysis.ocean.plot_hovmoller_subtask import PlotHovmollerSubtask +from mpas_analysis.ocean.utility import get_standard_region_names + class TimeSeriesSalinityAnomaly(AnalysisTask): """ @@ -50,7 +52,8 @@ def __init__(self, config, mpasTimeSeriesTask): tags=['timeSeries', 'salinity', 'publicObs', 'anomaly']) sectionName = 'hovmollerSalinityAnomaly' - regionNames = config.getexpression(sectionName, 'regions') + regionShortNames = config.getexpression(sectionName, + 'regionShortNames') movingAveragePoints = config.getint(sectionName, 'movingAveragePoints') mpasFieldName = 'timeMonthly_avg_avgValueWithinOceanLayerRegion_' \ @@ -66,9 +69,10 @@ def __init__(self, config, mpasTimeSeriesTask): movingAveragePoints=movingAveragePoints) self.add_subtask(anomalyTask) + regionNames = get_standard_region_names(config, regionShortNames) + for regionName in regionNames: - caption = 'Trend of {} Salinity Anomaly vs depth'.format( - regionName) + caption = f'Trend of {regionName} Salinity Anomaly vs depth' plotTask = PlotHovmollerSubtask( parentTask=self, regionName=regionName, diff --git a/mpas_analysis/ocean/time_series_sst.py b/mpas_analysis/ocean/time_series_sst.py index d67922ac5..d1fb3b649 100644 --- a/mpas_analysis/ocean/time_series_sst.py +++ b/mpas_analysis/ocean/time_series_sst.py @@ -23,6 +23,11 @@ make_directories, check_path_exists from mpas_analysis.shared.html import write_image_xml +from mpas_analysis.ocean.utility import ( + add_standard_regions_and_subset, + get_standard_region_names +) + class TimeSeriesSST(AnalysisTask): """ @@ -109,13 +114,16 @@ def setup_and_check(self): self.inputFile = self.mpasTimeSeriesTask.outputFile mainRunName = config.get('runs', 'mainRunName') - regions = config.getexpression('timeSeriesSST', 'regions') + regionShortNames = config.getexpression('timeSeriesSST', + 'regionShortNames') self.xmlFileNames = [] self.filePrefixes = {} + regions = get_standard_region_names(config, regionShortNames) for region in regions: - filePrefix = 'sst_{}_{}'.format(region, mainRunName) + regionSuffix = region.replace(' ', '_') + filePrefix = 'sst_{}_{}'.format(regionSuffix, mainRunName) self.xmlFileNames.append('{}/{}.xml'.format(self.plotsDirectory, filePrefix)) self.filePrefixes[region] = filePrefix @@ -147,12 +155,8 @@ def run_task(self): movingAveragePoints = config.getint('timeSeriesSST', 'movingAveragePoints') - regions = config.getexpression('regions', 'regions') - plotTitles = config.getexpression('regions', 'plotTitles') - regionsToPlot = config.getexpression('timeSeriesSST', 'regions') - - regionIndicesToPlot = [regions.index(region) for region in - regionsToPlot] + regionShortNames = config.getexpression('timeSeriesSST', + 'regionShortNames') outputDirectory = build_config_full_path(config, 'output', 'timeseriesSubdirectory') @@ -165,6 +169,9 @@ def run_task(self): startDate=self.startDate, endDate=self.endDate) + dsSST = add_standard_regions_and_subset( + dsSST, config, regionShortNames=regionShortNames) + yearStart = days_to_datetime(dsSST.Time.min(), calendar=calendar).year yearEnd = days_to_datetime(dsSST.Time.max(), calendar=calendar).year timeStart = date_to_days(year=yearStart, month=1, day=1, @@ -191,6 +198,9 @@ def run_task(self): variableList=self.variableList, startDate=controlStartDate, endDate=controlEndDate) + dsRefSST = add_standard_regions_and_subset( + dsRefSST, self.controlConfig, + regionShortNames=regionShortNames) else: dsRefSST = None @@ -221,17 +231,16 @@ def run_task(self): preprocessedReferenceRunName = 'None' self.logger.info(' Make plots...') - for regionIndex in regionIndicesToPlot: - region = regions[regionIndex] - - title = '{} SST'.format(plotTitles[regionIndex]) + for regionName in dsSST.regionNames.values: + title = f'{regionName} SST' xLabel = 'Time [years]' yLabel = r'[$\degree$C]' varName = self.variableList[0] - SST = dsSST[varName].isel(nOceanRegions=regionIndex) + dsSST = dsSST.set_xindex('regionNames') + SST = dsSST[varName].sel(regionNames=regionName) - filePrefix = self.filePrefixes[region] + filePrefix = self.filePrefixes[regionName] outFileName = '{}/{}.png'.format(self.plotsDirectory, filePrefix) @@ -242,7 +251,9 @@ def run_task(self): legendText = [mainRunName] if dsRefSST is not None: - refSST = dsRefSST[varName].isel(nOceanRegions=regionIndex) + + dsRefSST = dsRefSST.set_xindex('regionNames') + refSST = dsRefSST[varName].sel(regionNames=regionName) fields.append(refSST) lineColors.append(config.get('timeSeries', 'controlColor')) lineWidths.append(1.5) @@ -279,8 +290,7 @@ def run_task(self): savefig(outFileName, config) - caption = 'Running Mean of {} Sea Surface Temperature'.format( - region) + caption = f'Running Mean of {regionName} Sea Surface Temperature' write_image_xml( config=config, filePrefix=filePrefix, @@ -288,7 +298,7 @@ def run_task(self): componentSubdirectory='ocean', galleryGroup='Time Series', groupLink='timeseries', - thumbnailDescription='{} SST'.format(region), + thumbnailDescription=f'{regionName} SST', imageDescription=caption, imageCaption=caption) diff --git a/mpas_analysis/ocean/time_series_temperature_anomaly.py b/mpas_analysis/ocean/time_series_temperature_anomaly.py index 73b74c39e..6dbd43f65 100644 --- a/mpas_analysis/ocean/time_series_temperature_anomaly.py +++ b/mpas_analysis/ocean/time_series_temperature_anomaly.py @@ -16,6 +16,8 @@ from mpas_analysis.ocean.compute_anomaly_subtask import ComputeAnomalySubtask from mpas_analysis.ocean.plot_hovmoller_subtask import PlotHovmollerSubtask +from mpas_analysis.ocean.utility import get_standard_region_names + class TimeSeriesTemperatureAnomaly(AnalysisTask): """ @@ -50,7 +52,8 @@ def __init__(self, config, mpasTimeSeriesTask): tags=['timeSeries', 'temperature', 'publicObs', 'anomaly']) sectionName = 'hovmollerTemperatureAnomaly' - regionNames = config.getexpression(sectionName, 'regions') + regionShortNames = config.getexpression(sectionName, + 'regionShortNames') movingAveragePoints = config.getint(sectionName, 'movingAveragePoints') mpasFieldName = 'timeMonthly_avg_avgValueWithinOceanLayerRegion_' \ @@ -66,9 +69,11 @@ def __init__(self, config, mpasTimeSeriesTask): movingAveragePoints=movingAveragePoints) self.add_subtask(anomalyTask) + regionNames = get_standard_region_names(config, regionShortNames) + for regionName in regionNames: - caption = 'Trend of {} Potential Temperature Anomaly vs ' \ - 'Depth'.format(regionName) + caption = \ + f'Trend of {regionName} Potential Temperature Anomaly vs Depth' plotTask = PlotHovmollerSubtask( parentTask=self, regionName=regionName, diff --git a/mpas_analysis/ocean/utility.py b/mpas_analysis/ocean/utility.py index f615fd507..a96c7c3fa 100644 --- a/mpas_analysis/ocean/utility.py +++ b/mpas_analysis/ocean/utility.py @@ -19,6 +19,70 @@ import xarray +def add_standard_regions_and_subset(ds, config, regionShortNames=None): + """ + Add standard region names (``regionNames`` coordinate) to a dataset and + rename ``nOceanRegionsTmp`` dimension to ``nOceanRegions`` (if present). + Shorter standard region names are in ``regionNamesShort``. + + Parameters + ---------- + ds : xarray.Dataset + the dataset to which region names should be added + + config : mpas_tools.config.MpasConfigParser + Configuration options + + regionShortNames : list of str, optional + A list of a subset of the short region names to use to subset the + dataset + + Returns + ------- + ds : xarray.Dataset + the dataset with region names added and possibly subsetted + """ + ds = ds.copy() + if 'nOceanRegionsTmp' in ds.dims: + ds = ds.rename({'nOceanRegionsTmp': 'nOceanRegions'}) + + allShortNames = config.getexpression('regions', 'regionShortNames') + regionNames = config.getexpression('regions', 'regionNames') + ds.coords['regionShortNames'] = ('nOceanRegions', allShortNames) + ds.coords['regionNames'] = ('nOceanRegions', regionNames) + if regionShortNames is not None: + regionIndices = \ + [allShortNames.index(name) for name in regionShortNames] + ds = ds.isel(nOceanRegions=regionIndices) + return ds + + +def get_standard_region_names(config, regionShortNames): + """ + Add standard region names from the short names + + Parameters + ---------- + config : mpas_tools.config.MpasConfigParser + Configuration options + + regionShortNames : list of str + A list of short region names + + Returns + ------- + regionNames : list of str + A list of full standard region names + """ + allShortNames = config.getexpression('regions', 'regionShortNames') + regionNames = config.getexpression('regions', 'regionNames') + regionNameMap = {shortName: regionName for shortName, regionName in + zip(allShortNames, regionNames)} + regionNames = [regionNameMap[shortName] for shortName in regionShortNames] + + return regionNames + + def compute_zmid(bottomDepth, maxLevelCell, layerThickness): """ Computes zMid given data arrays for bottomDepth, maxLevelCell and