Source code for mpas_analysis.ocean.climatology_map_antarctic_melt
sectionName =self.taskName
- mpasFieldName='timeMonthly_avg_landIceFreshwaterFlux'iselValues=None# read in what seasons we want to plot
@@ -212,7 +211,7 @@
Source code for mpas_analysis.ocean.climatology_map_antarctic_melt
Source code for mpas_analysis.ocean.climatology_map_antarctic_melt
else:remapObservationsSubtask=NonecontrolRunName=controlConfig.get('runs','mainRunName')
- galleryName=NonerefTitleLabel=f'Control: {controlRunName}'
+ diffTitleLabel='Main - Control'
+
+ totalFluxVar='timeMonthly_avg_landIceFreshwaterFluxTotal'
+ landIceFluxVar='timeMonthly_avg_landIceFreshwaterFlux'
+ frazilFluxVar='timeMonthly_avg_frazilIceFreshwaterFlux'
+ mpasFieldName=totalFluxVar
+
+ ifcontrolConfigisNone:
+ refFieldName='meltRate'
+ else:refFieldName=mpasFieldName
- outFileLabel='melt'
- diffTitleLabel='Main - Control'forcomparisonGridNameincomparisonGridNames:forseasoninseasons:# make a new subtask for this season and comparison grid
- subtask=PlotClimatologyMapSubtask(
+ subtaskName=f'plot_total_melt_{season}_{comparisonGridName}'
+ subtask=PlotAntarcticMeltSubtask(self,season,comparisonGridName,remapClimatologySubtask,
- remapObservationsSubtask,controlConfig=controlConfig)
+ remapObservationsSubtask,controlConfig=controlConfig,
+ subtaskName=subtaskName)subtask.set_plot_info(
- outFileLabel=outFileLabel,
- fieldNameInTitle='Melt Rate',
+ outFileLabel='antMeltTotal',
+ fieldNameInTitle='Total Melt Flux',mpasFieldName=mpasFieldName,refFieldName=refFieldName,refTitleLabel=refTitleLabel,diffTitleLabel=diffTitleLabel,
- unitsLabel=r'm a$^{-1}$',
- imageCaption='Antarctic Melt Rate',
+ unitsLabel=r'm a$^{-1}$ freshwater equiv.',
+ imageCaption='Antarctic Total Melt Flux',galleryGroup='Melt Rate',groupSubtitle=None,groupLink='antarctic_melt',
- galleryName=galleryName)
+ galleryName='Total Melt Flux')
+
+ self.add_subtask(subtask)
+
+ mpasFieldName=landIceFluxVar
+
+ ifcontrolConfigisNone:
+ refFieldName='meltRate'
+ else:
+ refFieldName=mpasFieldName
+
+ forcomparisonGridNameincomparisonGridNames:
+ forseasoninseasons:
+ # make a new subtask for this season and comparison grid
+ subtaskName= \
+ f'plot_interface_melt_{season}_{comparisonGridName}'
+ subtask=PlotAntarcticMeltSubtask(
+ self,season,comparisonGridName,remapClimatologySubtask,
+ remapObservationsSubtask,controlConfig=controlConfig,
+ subtaskName=subtaskName)
+
+ # In PlotAntarcticMeltSubtask, we will remove the obs from
+ # these plots if totalFluxVar is present so we only compare one
+ # field with obs
+
+ subtask.set_plot_info(
+ outFileLabel='antMeltInterface',
+ fieldNameInTitle='Melt Rate at Interface',
+ mpasFieldName=mpasFieldName,
+ refFieldName=refFieldName,
+ refTitleLabel=refTitleLabel,
+ diffTitleLabel=diffTitleLabel,
+ unitsLabel=r'm a$^{-1}$ freshwater equiv.',
+ imageCaption='Antarctic Melt Rate at Interface',
+ galleryGroup='Melt Rate',
+ groupSubtitle=None,
+ groupLink='antarctic_melt_int',
+ galleryName='Melt Rate at the Ice-ocean Interface')
+
+ self.add_subtask(subtask)
+
+ mpasFieldName=frazilFluxVar
+
+ ifcontrolConfigisNone:
+ refTitleLabel=None
+ refFieldName=None
+ diffTitleLabel=None
+
+ else:
+ controlRunName=controlConfig.get('runs','mainRunName')
+ refTitleLabel=f'Control: {controlRunName}'
+ refFieldName=mpasFieldName
+ diffTitleLabel='Main - Control'
+
+ forcomparisonGridNameincomparisonGridNames:
+ forseasoninseasons:
+ # make a new subtask for this season and comparison grid
+ subtaskName= \
+ f'plot_interface_frazil_{season}_{comparisonGridName}'
+ subtask=PlotAntarcticMeltSubtask(
+ self,season,comparisonGridName,remapClimatologySubtask,
+ controlConfig=controlConfig,subtaskName=subtaskName)
+
+ subtask.set_plot_info(
+ outFileLabel='antFrazil',
+ fieldNameInTitle='Frazil Accretion Rate, neg. upward',
+ mpasFieldName=mpasFieldName,
+ refFieldName=refFieldName,
+ refTitleLabel=refTitleLabel,
+ diffTitleLabel=diffTitleLabel,
+ unitsLabel=r'm a$^{-1}$ freshwater equiv.',
+ imageCaption='Antarctic Accretion Rate',
+ galleryGroup='Melt Rate',
+ groupSubtitle=None,
+ groupLink='antarctic_frazil_flux',
+ galleryName='Frazil Accretion Rate')self.add_subtask(subtask)
@@ -317,11 +397,35 @@
Source code for mpas_analysis.ocean.climatology_map_antarctic_melt
landIceMask : xarray.DataArray
A mask indicating where there is land ice on the ocean grid (thus, where melt rates are valid)
+
+ renameDict : dict
+ A dictionary use to rename variables in the climatology """# Authors# -------# Xylar Asay-Davis
+ defsetup_and_check(self):
+"""
+ Figure out which variable(s) to remap
+ """
+ # Authors
+ # -------
+ # Xylar Asay-Davis
+
+ totalFluxVar='timeMonthly_avg_landIceFreshwaterFluxTotal'
+ landIceFluxVar='timeMonthly_avg_landIceFreshwaterFlux'
+ frazilFluxVar='timeMonthly_avg_frazilIceFreshwaterFlux'
+
+ iftotalFluxVarinself.mpasClimatologyTask.allVariables:
+ # include the total and constituent fluxes
+ self.variableList=[totalFluxVar,landIceFluxVar,frazilFluxVar]
+ else:
+ # we only have the old name without the frazil accretion rate
+ self.variableList=[landIceFluxVar]
+
+ super().setup_and_check()
+
defrun_task(self):""" Compute climatologies of melt rates from E3SM/MPAS output
@@ -365,12 +469,14 @@
Source code for mpas_analysis.ocean.climatology_map_antarctic_melt
# -------
# Xylar Asay-Davis
- fieldName=self.variableList[0]
+ forfieldNameinself.variableList:
- # scale the field to m/yr from kg/m^2/s and mask out non-land-ice areas
- climatology[fieldName]= \
- constants.sec_per_year/constants.rho_fw* \
- climatology[fieldName].where(self.landIceMask)
+ # scale the field to m/yr from kg/m^2/s and mask out non-land-ice
+ # areas
+ climatology[fieldName]= \
+ constants.sec_per_year/constants.rho_fw* \
+ climatology[fieldName].where(self.landIceMask)
+ climatology[fieldName].attrs['units']='m yr^-1'returnclimatology
@@ -527,7 +633,8 @@
Source code for mpas_analysis.ocean.climatology_map_antarctic_melt
Source code for mpas_analysis.ocean.climatology_map_antarctic_melt
# select only those regions we want to plot
dsRegionMask=dsRegionMask.isel(nRegions=regionIndices)
- cellMasks=dsRegionMask.regionCellMasks.chunk({'nRegions':10})
+ cellMasks= \
+ dsRegionMask.regionCellMasks.chunk({'nRegions':10})restartFileName= \
self.runStreams.readpath('restart')[0]
@@ -653,6 +761,56 @@
Source code for mpas_analysis.ocean.climatology_map_antarctic_melt
row[controlRunName]= \
f'{dsControl.totalMeltFlux[index].values}'writer.writerow(row)
+
+
+classPlotAntarcticMeltSubtask(PlotClimatologyMapSubtask):
+"""
+ A subtask for plotting antarctic melt fields if available
+
+ Attributes
+ ----------
+ doPlot : bool
+ Whether the required variable from the climatology is available so that
+ a plot should be generated
+ """
+ # Authors
+ # -------
+ # Xylar Asay-Davis
+
+ defsetup_and_check(self):
+"""
+ Perform steps to set up the analysis and check for errors in the setup.
+ """
+ allVariables= \
+ self.remapMpasClimatologySubtask.mpasClimatologyTask.allVariables
+
+ totalFluxVar='timeMonthly_avg_landIceFreshwaterFluxTotal'
+ landIceFluxVar='timeMonthly_avg_landIceFreshwaterFlux'
+ plotAll=(totalFluxVarinallVariables)
+
+ ifself.mpasFieldName==landIceFluxVarandplotAlland \
+ self.controlConfigisNone:
+ # need to remove obs because we only wnat to plot them vs the
+ # total flux
+ self.remapObsClimatologySubtask=None
+ self.refTitleLabel=None
+ self.refFieldName=None
+ self.diffTitleLabel=None
+
+ self.doPlot=(self.mpasFieldName==landIceFluxVarorplotAll)
+
+ ifself.doPlot:
+ super().setup_and_check()
+ else:
+ # still need to call the base class's method
+ AnalysisTask.setup_and_check(self=self)
+
+ defrun_task(self):
+"""
+ Plot the variable if available
+ """
+ ifself.doPlot:
+ super().run_task()
Source code for mpas_analysis.ocean.climatology_map_argo
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.coords['year']=('Time',np.ones(dsObs.sizes['Time'],int))dsObs=dsObs[[self.fieldName,'month']]slices=[]
diff --git a/stable/_modules/mpas_analysis/ocean/climatology_map_mld.html b/stable/_modules/mpas_analysis/ocean/climatology_map_mld.html
index b39379f6f..bebcb2044 100644
--- a/stable/_modules/mpas_analysis/ocean/climatology_map_mld.html
+++ b/stable/_modules/mpas_analysis/ocean/climatology_map_mld.html
@@ -340,7 +340,7 @@
Source code for mpas_analysis.ocean.climatology_map_mld
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.coords['year']=('Time',np.ones(dsObs.sizes['Time'],int))dsObs=dsObs[['mld','month']]returndsObs
diff --git a/stable/_modules/mpas_analysis/ocean/conservation.html b/stable/_modules/mpas_analysis/ocean/conservation.html
new file mode 100644
index 000000000..b7b018a76
--- /dev/null
+++ b/stable/_modules/mpas_analysis/ocean/conservation.html
@@ -0,0 +1,761 @@
+
+
+
+
+
+ mpas_analysis.ocean.conservation — MPAS-Analysis stable documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# This software is open source software available under the BSD-3 license.
+#
+# Copyright (c) 2022 Triad National Security, LLC. All rights reserved.
+# Copyright (c) 2022 Lawrence Livermore National Security, LLC. All rights
+# reserved.
+# Copyright (c) 2022 UT-Battelle, LLC. All rights reserved.
+#
+# Additional copyright and license information can be found in the LICENSE file
+# distributed with this code, or at
+# https://raw.githubusercontent.com/MPAS-Dev/MPAS-Analysis/main/LICENSE
+#
+# Author
+# -------
+# Carolyn Begeman
+
+fromdistutils.spawnimportfind_executable
+importnumpyasnp
+importmatplotlib.pyplotasplt
+importos
+importsubprocess
+importxarrayasxr
+
+frommpas_analysis.shared.analysis_taskimportAnalysisTask
+frommpas_analysis.shared.constantsimportconstants
+frommpas_analysis.shared.htmlimportwrite_image_xml
+frommpas_analysis.shared.ioimportopen_mpas_dataset
+frommpas_analysis.shared.io.utilityimportbuild_config_full_path, \
+ make_directories,get_files_year_month,decode_strings
+frommpas_analysis.shared.plotimporttimeseries_analysis_plot,savefig
+frommpas_analysis.shared.timekeeping.utilityimportdate_to_days, \
+ days_to_datetime
+
+
+
+[docs]
+classConservationTask(AnalysisTask):
+"""
+ This task generates time series plots from output from the conservation
+ analysis member. A number of different plot types are supported, as indicated
+ in the `plotTypes` config option in the `conservation` section.
+
+ Attributes
+ ----------
+ config : mpas_tools.config.MpasConfigParser
+ Contains configuration options
+
+ controlConfig : mpas_tools.config.MpasConfigParser
+ Contains configuration options for a control run, if provided
+
+ outputFile : str
+ The path to the output file produced by this analysis
+
+ runDirectory : str
+ The path to the restart files from the main simulation being analyzed
+
+ historyDirectory : str
+ The path to the history files from the main simulation being analyzed
+
+ startYear : int
+ The year to start the analysis
+
+ endYear : int
+ The year to end the analysis
+
+ inputFiles : list of str
+ The paths to all conservation AM files
+
+ mainRunName : str
+ The name of the main run from the config file
+
+ plotTypes : list of str
+ The plot types requested in the config file
+
+ masterVariableList : dict of key-[list of str] pairs
+ Keys are the supported plot types. Entries are lists of the variables
+ that are needed to produce that plot type.
+
+ derivedVariableList : dict of key-[list of str] pairs
+ Keys are the derived variables. Entries are lists of variables in the
+ AM output that are needed to derive that variable.
+
+ xmlFileNames : list of str
+ File names for xml output with full path
+
+ filePrefixes : list of str
+ File prefixes for xml files
+
+ variableList : dict of key-[list of str] pairs
+ Keys are the requested plot types. Entries are lists of the variables
+ in the AM output that are needed to produce that plot type.
+ """
+
+ # Authors
+ # -------
+ # Carolyn Begeman
+
+
+
+
+ defsetup_and_check(self):
+"""
+ Perform steps to set up the analysis and check for errors in the setup.
+ Also, adds attributes to the task that will be needed later.
+
+ Raises
+ ------
+ ValueError: if plot type is not supported, conservation analysis member
+ is inactive or input files are missing
+ """
+ # Authors
+ # -------
+ # Carolyn Begeman
+
+ super(ConservationTask,self).setup_and_check()
+
+ # Check that the conservation analysis member is active
+ self.check_analysis_enabled(
+ analysisOptionName='config_am_conservationcheck_enable',
+ raiseException=True)
+
+ # Specify where to put analysis task output
+ config=self.config
+ baseDirectory=build_config_full_path(
+ config,'output','conservationSubdirectory')
+ make_directories(baseDirectory)
+ self.outputFile=f'{baseDirectory}/{self.fullTaskName}.nc'
+
+ # get a list of conservationCheck output files from the streams file,
+ # reading only those that are between the start and end dates
+
+ # the run directory contains the restart files
+ self.runDirectory=build_config_full_path(self.config,'input',
+ 'runSubdirectory')
+ # if the history directory exists, use it; if not, fall back on
+ # runDirectory
+ self.historyDirectory=build_config_full_path(
+ self.config,'input',
+ f'{self.componentName}HistorySubdirectory',
+ defaultPath=self.runDirectory)
+
+ self.startYear=self.config.getint('timeSeries','startYear')
+ self.endYear=self.config.getint('timeSeries','endYear')
+ self.inputFiles=sorted(self.historyStreams.readpath(
+ 'conservationCheckOutput',
+ startDate=f'{self.startYear:04d}-01-01_00:00:00',
+ endDate=f'{self.endYear:04d}-01-01_00:00:00',
+ calendar=self.calendar))
+
+ iflen(self.inputFiles)==0:
+ raiseIOError(f'No files were found matching {self.inputFiles}')
+
+ withxr.open_dataset(self.inputFiles[0])asds:
+ self.allVariables=list(ds.data_vars.keys())
+
+ self.mainRunName=self.config.get('runs','mainRunName')
+
+ self.plotTypes=self.config.getexpression('timeSeriesConservation','plotTypes')
+
+ self.masterVariableList={'absolute_energy_error':['absoluteEnergyError'],
+ 'total_energy_flux':['netEnergyFlux'],
+ 'absolute_salt_error':['absoluteSaltError'],
+ 'ice_salt_flux':['netSaltFlux'],
+ 'total_mass_flux':['netMassFlux'],
+ 'total_mass_change':['netMassChange'],
+ 'land_ice_mass_change':['landIceMassChange'],
+ 'land_ice_ssh_change':['landIceSshChange'],
+ 'land_ice_mass_flux':['landIceMassFlux'],
+ 'land_ice_mass_flux_components':['accumulatedIcebergFlux',
+ 'accumulatedLandIceFlux',
+ 'accumulatedRemovedRiverRunoffFlux',
+ 'accumulatedRemovedIceRunoffFlux']}
+
+ # for each derived variable, which source variables are needed
+ self.derivedVariableList={'netMassChange':['massChange'],
+ 'landIceMassFlux':['accumulatedIcebergFlux',
+ 'accumulatedLandIceFlux',
+ 'accumulatedRemovedRiverRunoffFlux',
+ 'accumulatedRemovedIceRunoffFlux'],
+ 'landIceSshChange':['accumulatedIcebergFlux',
+ 'accumulatedLandIceFlux',
+ 'accumulatedRemovedRiverRunoffFlux',
+ 'accumulatedRemovedIceRunoffFlux'],
+ 'landIceMassChange':['accumulatedIcebergFlux',
+ 'accumulatedLandIceFlux',
+ 'accumulatedRemovedRiverRunoffFlux',
+ 'accumulatedRemovedIceRunoffFlux']}
+
+ # Determine the xml files for each plot and the variables each plot will use
+ self.xmlFileNames=[]
+ self.filePrefixes={}
+ self.variableList={}
+ forplot_typeinself.plotTypes:
+ ifplot_typenotinself.masterVariableList.keys():
+ raiseValueError(f'plot type {plot_type} not supported')
+ filePrefix=f'conservation_{self.mainRunName}_{plot_type}_' \
+ f'years{self.startYear:04d}-{self.endYear:04d}'
+ self.xmlFileNames.append(f'{self.plotsDirectory}/{filePrefix}.xml')
+ self.filePrefixes[plot_type]=filePrefix
+ self.variableList[plot_type]=self._add_variables(self.masterVariableList[plot_type])
+
+ defrun_task(self):
+"""
+ Create an output netCDF file that has all of the requested conservation AM variables
+ in the requested time window. Then generate all requested conservation plot types.
+ """
+ # Authors
+ # -------
+ # Carolyn Begeman
+
+ all_plots_variable_list=[]
+ forplot_typeinself.plotTypes:
+ forvarnameinself.variableList[plot_type]:
+ all_plots_variable_list.append(varname)
+ self._compute_time_series_with_ncrcat(all_plots_variable_list)
+ forplot_typeinself.plotTypes:
+ self._make_plot(plot_type)
+
+ def_add_variables(self,target_variable_list):
+"""
+ Add one or more variables to extract as a time series.
+
+ Parameters
+ ----------
+ variableList : list of str
+ A list of variable names in ``conservationCheck`` to be
+ included in the time series
+
+ Raises
+ ------
+ ValueError
+ if this function is called before this task has been set up (so
+ the list of available variables has not yet been set) or if one
+ or more of the requested variables is not available in the
+ ``conservationCheck`` output.
+ """
+ # Authors
+ # -------
+ # Xylar Asay-Davis
+
+ variable_list=[]
+ ifself.allVariablesisNone:
+ raiseValueError('add_variables() can only be called after '
+ 'setup_and_check() in ConservationTask.\n'
+ 'Presumably tasks were added in the wrong order '
+ 'or add_variables() is being called in the wrong '
+ 'place.')
+
+ forvariableintarget_variable_list:
+ ifvariablenotinself.allVariablesand \
+ variablenotinself.derivedVariableList.keys():
+ raiseValueError(
+ f'{variable} is not available in conservationCheck'
+ 'output:\n{self.allVariables}')
+
+ ifvariableinself.allVariablesandvariablenotinvariable_list:
+ variable_list.append(variable)
+ # If it's a derived variable, add all of the variables it depends on
+ ifvariableinself.derivedVariableList.keys()and \
+ variablenotinvariable_list:
+ forvarinself.derivedVariableList[variable]:
+ variable_list.append(var)
+
+ returnvariable_list
+
+ def_make_plot(self,plot_type):
+"""
+ Generate time series plots from conservation AM output.
+
+ Parameters
+ ----------
+ plot_type: str
+ The type of plot to generate from conservationCheck variables
+ """
+ config=self.config
+ filePrefix=self.filePrefixes[plot_type]
+ outFileName=f'{self.plotsDirectory}/{filePrefix}.png'
+
+ titles={}
+ titles['total_energy_flux']='Total energy flux'
+ titles['absolute_energy_error']='Energy error'
+ titles['ice_salt_flux']='Salt flux related to land ice and sea ice'
+ titles['absolute_salt_error']='Salt conservation error'
+ titles['total_mass_flux']='Total mass flux'
+ titles['total_mass_change']='Total mass anomaly'
+ titles['land_ice_mass_flux']='Mass flux due to land ice'
+ titles['land_ice_mass_change']='Mass anomaly due to land ice fluxes'
+ titles['land_ice_ssh_change']='SSH anomaly due to land ice fluxes'
+ titles['land_ice_mass_flux_components']='Mass fluxes from land ice'
+
+ y_labels={}
+ y_labels['total_energy_flux']='Energy flux (W)'
+ y_labels['absolute_energy_error']='Energy (J)'
+ y_labels['ice_salt_flux']='Salt flux (Gt/yr)'
+ y_labels['absolute_salt_error']='Salt (Gt)'
+ y_labels['total_mass_flux']='Mass flux (Gt/yr)'
+ y_labels['total_mass_change']='Mass (Gt)'
+ y_labels['land_ice_mass_flux']='Mass flux (Gt/yr)'
+ y_labels['land_ice_mass_change']='Mass (Gt)'
+ y_labels['land_ice_ssh_change']='SSH anomaly (mm)'
+ y_labels['land_ice_mass_flux_components']='Mass flux (Gt/yr)'
+
+ captions={}
+ captions['total_energy_flux']='Total energy flux'
+ captions['absolute_energy_error']='Absolute energy conservation error'
+ captions['ice_salt_flux']='Salt flux related to land ice and sea ice ' \
+ '(sea ice salinity flux, sea ice frazil flux, and land ice frazil flux)'
+ captions['absolute_salt_error']='Absolute salt conservation error'
+ captions['total_mass_flux']='Total mass flux'
+ captions['total_mass_change']='Total mass anomaly'
+ captions['land_ice_mass_flux']='Mass flux due to land ice'
+ captions['land_ice_mass_change']='Mass anomaly due to land ice fluxes'
+ captions['land_ice_ssh_change']='SSH anomaly due to land ice fluxes. Assumes a constant ocean area.'
+ captions['land_ice_mass_flux_components']='Mass flux components from land ice'
+
+ self.logger.info(f' Open conservation file {self.outputFile}...')
+ ds=open_mpas_dataset(fileName=self.outputFile,
+ calendar=self.calendar,
+ variableList=self.variableList[plot_type],
+ timeVariableNames='xtime',
+ startDate=f'{self.startYear:04d}-01-01_00:00:00',
+ endDate=f'{self.endYear:04d}-01-01_00:00:00')
+
+ ifself.controlConfigisnotNone:
+ baseDirectory=build_config_full_path(
+ self.controlConfig,'output','timeSeriesSubdirectory')
+
+ controlFileName=f'{baseDirectory}/{self.fullTaskName}.nc'
+ self.logger.info(' Load in conservation for a control run '
+ f'{controlFileName}...')
+ ds_ref=open_mpas_dataset(fileName=controlFileName,
+ calendar=self.calendar,
+ variableList=self.variableList[plot_type],
+ timeVariableNames='xtime')
+ controlEndYear=self.controlConfig.getint('timeSeries','endYear')
+ ifself.startYear<=controlEndYear:
+ timeStart=date_to_days(year=self.startYear,month=1,day=1,
+ calendar=self.calendar)
+ timeEnd=date_to_days(year=self.endYear,month=12,day=31,
+ calendar=self.calendar)
+ ds_ref_slice= \
+ ds_ref.sel(Time=slice(timeStart,timeEnd))
+ else:
+ self.logger.warning('Control time series ends before the '
+ 'timeSeries startYear and will not be '
+ 'plotted.')
+ self.controlConfig=None
+
+ # make the plot
+ self.logger.info(' Make conservation plots...')
+ xLabel='Time (years)'
+ title=titles[plot_type]
+ yLabel=y_labels[plot_type]
+ lineStylesBase=['-','--','-.',':']
+
+ # gather all the variables for this plot type
+ fields=[]
+ legendText=[]
+ lineColors=[]
+ lineStyles=[]
+ forindex,varnameinenumerate(self.masterVariableList[plot_type]):
+ variable=self._get_variable(ds,varname)
+ fields.append(variable)
+ legend_text=''
+ ifself.controlConfigisnotNone:
+ legend_text=self.mainRunName
+ iflen(self.masterVariableList[plot_type])>1:
+ iflen(legend_text)>0:
+ legend_text=f'{legend_text}, '
+ legend_text=f"{legend_text}{varname.replace('accumulated','').replace('Flux','')}"
+ legendText.append(legend_text)
+ lineColors.append(config.get('timeSeries','mainColor'))
+ lineStyles.append(lineStylesBase[index])
+ ifself.controlConfigisnotNone:
+ variable=self._get_variable(ds_ref,varname)
+ fields.append(variable)
+ legend_text=self.controlConfig.get('runs','mainRunName')
+ iflen(self.masterVariableList[plot_type])>1:
+ legend_text=f"{legend_text}, {varname.replace('accumulated','').replace('Flux','')}"
+ legendText.append(legend_text)
+ lineColors.append(config.get('timeSeries','controlColor'))
+ lineStyles.append(lineStylesBase[index])
+
+ lineWidths=[3foriinfields]
+ ifconfig.has_option('timeSeries','movingAveragePoints'):
+ movingAveragePoints=config.getint('timeSeries',
+ 'movingAveragePoints')
+ else:
+ movingAveragePoints=None
+
+ ifconfig.has_option('timeSeries','firstYearXTicks'):
+ firstYearXTicks=config.getint('timeSeries',
+ 'firstYearXTicks')
+ else:
+ firstYearXTicks=None
+
+ ifconfig.has_option('timeSeries','yearStrideXTicks'):
+ yearStrideXTicks=config.getint('timeSeries',
+ 'yearStrideXTicks')
+ else:
+ yearStrideXTicks=None
+
+ timeseries_analysis_plot(config,fields,calendar=self.calendar,
+ title=title,xlabel=xLabel,ylabel=yLabel,
+ movingAveragePoints=movingAveragePoints,
+ lineColors=lineColors,
+ lineStyles=lineStyles[:len(fields)],
+ lineWidths=lineWidths,
+ legendText=legendText,
+ firstYearXTicks=firstYearXTicks,
+ yearStrideXTicks=yearStrideXTicks)
+
+ # save the plot to the output file
+ plt.savefig(outFileName)
+
+ caption=captions[plot_type]
+ write_image_xml(
+ config=self.config,
+ filePrefix=filePrefix,
+ componentName='Ocean',
+ componentSubdirectory='ocean',
+ galleryGroup='Time Series',
+ groupLink='timeseries',
+ gallery='Conservation',
+ thumbnailDescription=title,
+ imageDescription=caption,
+ imageCaption=caption)
+
+ def_get_variable(self,ds,varname,mks=False):
+ ifvarnamenotinself.derivedVariableList:
+ variable=ds[varname]
+ else:
+ # Here we keep the units mks
+ ifvarname=='netMassChange':
+ variable=self._get_variable(ds,'massChange',mks=True)
+ # mass_flux = self._get_variable(ds, 'netMassFlux')
+ # # Assume that the frequency of output is monthly
+ # dt = constants.sec_per_month
+ # # Convert from kg/s to kg
+ # derived_variable = mass_flux.cumsum(axis=0) * dt
+ elifvarname=='landIceMassChange':
+ land_ice_mass_flux=self._get_variable(ds,'landIceMassFlux',mks=True)
+ # Assume that the frequency of output is monthly
+ dt=constants.sec_per_month
+ # Convert from kg/s to kg/month
+ land_ice_mass_flux=land_ice_mass_flux*dt
+ # Convert from kg/month to kg
+ variable=np.cumsum(land_ice_mass_flux)
+
+ elifvarname=='landIceMassFlux':
+ variable=self._get_variable(ds,'accumulatedIcebergFlux',mks=True)+ \
+ self._get_variable(ds,'accumulatedLandIceFlux',mks=True)+ \
+ self._get_variable(ds,'accumulatedRemovedRiverRunoffFlux',mks=True)+ \
+ self._get_variable(ds,'accumulatedRemovedIceRunoffFlux',mks=True)
+
+ elifvarname=='landIceSshChange':
+ ts_files=sorted(self.historyStreams.readpath(
+ 'timeSeriesStatsMonthlyOutput',
+ startDate=f'{self.startYear:04d}-01-01_00:00:00',
+ endDate=f'{self.endYear:04d}-01-01_00:00:00',
+ calendar=self.calendar))
+ # Note that here we assume that the area of the ocean is constant in time
+ # to save computational expense because most configurations do not allow
+ # the area of the ocean to change
+ ts_file=ts_files[0]
+ ifnotos.path.exists(ts_file):
+ raiseValueError(f'Could not find timeMonthlyStats file {ts_file}')
+ var='timeMonthly_avg_areaCellGlobal'
+ ds_ts=open_mpas_dataset(fileName=ts_file,
+ calendar=self.calendar,
+ variableList=[var])
+ A=ds_ts[var].mean()
+ land_ice_mass_change=self._get_variable(ds,'landIceMassChange',mks=True)
+ rho=self.namelist.getfloat('config_density0')
+ # Convert from to kg to m
+ variable=land_ice_mass_change/(rho*A)
+
+ else:
+ raiseValueError(f'Attempted to derive non-supported variable {varname}')
+
+ removed_vars=['accumulatedRemovedRiverRunoffFlux',
+ 'accumulatedRemovedIceRunoffFlux']
+ ifvarnameinremoved_vars:
+ variable=-variable
+
+ ifnotmks:
+ # Here we do all the unit conversion from mks into whatever we want
+ mass_vars=['initialMass','finalMass','absoluteMassError',
+ 'relativeMassError','massChange','landIceMassChange']
+ salt_vars=['initialSalt','finalSalt','absoluteSaltError',
+ 'relativeSaltError']
+ mass_flux_vars=['netMassFlux','landIceMassFlux']
+ salt_flux_vars=['netSaltFlux']
+ ssh_vars=['landIceSshChange','sshChange']
+ if(varnameinmass_vars)or(varnameinsalt_vars):
+ # Convert from kg to Gt
+ variable=variable*1e-12
+ if(varnameinmass_flux_vars)or(varnameinsalt_flux_vars):
+ # Convert from kg/s to Gt/yr
+ variable=variable*1e-12*constants.sec_per_year
+ ifvarnameinssh_vars:
+ # Convert from m to mm
+ variable=variable*1e3
+
+ returnvariable
+
+ def_compute_time_series_with_ncrcat(self,variable_list):
+
+"""
+ Uses ncrcat to extact time series from conservationCheckOutput files
+
+ Raises
+ ------
+ OSError
+ If ``ncrcat`` is not in the system path.
+ """
+
+ iffind_executable('ncrcat')isNone:
+ raiseOSError('ncrcat not found. Make sure the latest nco '
+ 'package is installed: \n'
+ 'conda install nco\n'
+ 'Note: this presumes use of the conda-forge '
+ 'channel.')
+
+ inputFiles=self.inputFiles
+ append=False
+ ifos.path.exists(self.outputFile):
+ # make sure all the necessary variables are also present
+ withxr.open_dataset(self.outputFile)asds:
+ ifds.sizes['Time']==0:
+ updateSubset=False
+ else:
+ updateSubset=True
+ forvariableNameinvariable_list:
+ ifvariableNamenotinds.variables:
+ updateSubset=False
+ break
+
+ ifupdateSubset:
+ # add only input files with times that aren't already in
+ # the output file
+
+ append=True
+
+ fileNames=sorted(self.inputFiles)
+ inYears,inMonths=get_files_year_month(
+ fileNames,self.historyStreams,
+ 'conservationCheckOutput')
+
+ inYears=np.array(inYears)
+ inMonths=np.array(inMonths)
+ totalMonths=12*inYears+inMonths
+
+ dates=decode_strings(ds.xtime)
+
+ lastDate=dates[-1]
+
+ lastYear=int(lastDate[0:4])
+ lastMonth=int(lastDate[5:7])
+ lastTotalMonths=12*lastYear+lastMonth
+
+ inputFiles=[]
+ forindex,inputFileinenumerate(fileNames):
+ iftotalMonths[index]>lastTotalMonths:
+ inputFiles.append(inputFile)
+
+ iflen(inputFiles)==0:
+ # nothing to do
+ return
+ else:
+ # there is an output file but it has the wrong variables
+ # so we need ot delete it.
+ self.logger.warning('Warning: deleting file {self.outputFile}'
+ ' because it is empty or some variables'
+ ' were missing')
+ os.remove(self.outputFile)
+
+ variableList=variable_list+['xtime']
+
+ args=['ncrcat','-4','--no_tmp_fl',
+ '-v',','.join(variableList)]
+
+ ifappend:
+ args.append('--record_append')
+
+ printCommand='{}{} ... {}{}'.format(' '.join(args),inputFiles[0],
+ inputFiles[-1],
+ self.outputFile)
+ args.extend(inputFiles)
+ args.append(self.outputFile)
+
+ self.logger.info(f'running: {printCommand}')
+ forhandlerinself.logger.handlers:
+ handler.flush()
+
+ process=subprocess.Popen(args,stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ stdout,stderr=process.communicate()
+
+ ifstdout:
+ stdout=stdout.decode('utf-8')
+ forlineinstdout.split('\n'):
+ self.logger.info(line)
+ ifstderr:
+ stderr=stderr.decode('utf-8')
+ forlineinstderr.split('\n'):
+ self.logger.error(line)
+
+ ifprocess.returncode!=0:
+ raisesubprocess.CalledProcessError(process.returncode,
+ ' '.join(args))
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/stable/_modules/mpas_analysis/ocean/histogram.html b/stable/_modules/mpas_analysis/ocean/histogram.html
new file mode 100644
index 000000000..e29742cb5
--- /dev/null
+++ b/stable/_modules/mpas_analysis/ocean/histogram.html
@@ -0,0 +1,733 @@
+
+
+
+
+
+ mpas_analysis.ocean.histogram — MPAS-Analysis stable documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# -*- coding: utf-8 -*-
+# This software is open source software available under the BSD-3 license.
+#
+# Copyright (c) 2022 Triad National Security, LLC. All rights reserved.
+# Copyright (c) 2022 Lawrence Livermore National Security, LLC. All rights
+# reserved.
+# Copyright (c) 2022 UT-Battelle, LLC. All rights reserved.
+#
+# Additional copyright and license information can be found in the LICENSE file
+# distributed with this code, or at
+# https://raw.githubusercontent.com/MPAS-Dev/MPAS-Analysis/main/LICENSE
+#
+importos
+importxarray
+importnumpy
+importmatplotlib.pyplotasplt
+
+frommpas_analysis.sharedimportAnalysisTask
+
+frommpas_analysis.shared.ioimportopen_mpas_dataset,write_netcdf_with_fill
+frommpas_analysis.shared.io.utilityimportbuild_config_full_path, \
+ build_obs_path,make_directories,decode_strings
+frommpas_analysis.shared.climatologyimportcompute_climatology, \
+ get_unmasked_mpas_climatology_file_name
+
+frommpas_analysis.shared.constantsimportconstants
+frommpas_analysis.shared.plotimporthistogram_analysis_plot,savefig
+frommpas_analysis.shared.htmlimportwrite_image_xml
+
+
+
+[docs]
+classOceanHistogram(AnalysisTask):
+"""
+ Plots a histogram of a 2-d ocean variable.
+
+ """
+
+
+[docs]
+ def__init__(self,config,mpasClimatologyTask,regionMasksTask,
+ controlConfig=None):
+
+"""
+ Construct the analysis task.
+
+ Parameters
+ ----------
+ config : mpas_tools.config.MpasConfigParser
+ Configuration options
+
+ mpasClimatologyTask : ``MpasClimatologyTask``
+ The task that produced the climatology to be remapped and plotted
+
+ regionMasksTask : ``ComputeRegionMasks``
+ A task for computing region masks
+
+ controlConfig : mpas_tools.config.MpasConfigParser
+ Configuration options for a control run (if any)
+ """
+
+ # first, call the constructor from the base class (AnalysisTask)
+ super().__init__(
+ config=config,
+ taskName='oceanHistogram',
+ componentName='ocean',
+ tags=['climatology','regions','histogram','publicObs'])
+
+ self.run_after(mpasClimatologyTask)
+ self.mpasClimatologyTask=mpasClimatologyTask
+
+ self.controlConfig=controlConfig
+ mainRunName=config.get('runs','mainRunName')
+
+ self.regionGroups=config.getexpression(self.taskName,'regionGroups')
+ self.regionNames=config.getexpression(self.taskName,'regionNames')
+ self.seasons=config.getexpression(self.taskName,'seasons')
+ self.variableList=config.getexpression(self.taskName,'variableList')
+ ifconfig.has_option(self.taskName,'weightList'):
+ self.weightList=config.getexpression(self.taskName,'weightList')
+ ifnotself.weightList:
+ self.weightList=None
+ eliflen(self.weightList)!=len(self.variableList):
+ raiseValueError('Histogram weightList is not the same '
+ 'length as variableList')
+ else:
+ self.weightList=None
+
+ baseDirectory=build_config_full_path(
+ config,'output','histogramSubdirectory')
+ ifnotos.path.exists(baseDirectory):
+ make_directories(baseDirectory)
+
+ self.obsList=config.getexpression(self.taskName,'obsList')
+ obsDicts={
+ 'AVISO':{
+ 'suffix':'AVISO',
+ 'gridName':'Global_1.0x1.0degree',
+ 'gridFileName':'SSH/zos_AVISO_L4_199210-201012_20180710.nc',
+ 'lonVar':'lon',
+ 'latVar':'lat',
+ 'sshVar':'zos',
+ 'pressureAdjustedSSHVar':'zos'}}
+
+ forregionGroupinself.regionGroups:
+ groupObsDicts={}
+ mpasMasksSubtask=regionMasksTask.add_mask_subtask(
+ regionGroup=regionGroup)
+ regionNames=mpasMasksSubtask.expand_region_names(
+ self.regionNames)
+
+ regionGroupSuffix=regionGroup.replace(' ','_')
+ filePrefix=f'histogram_{regionGroupSuffix}'
+
+ # Add mask subtasks for observations and prep groupObsDicts
+ # groupObsDicts is a subsetted version of localObsDicts with an
+ # additional attribute for the maskTask
+ forobsNameinself.obsList:
+ localObsDict=dict(obsDicts[obsName])
+ obsFileName=build_obs_path(
+ config,component=self.componentName,
+ relativePath=localObsDict['gridFileName'])
+ obsMasksSubtask=regionMasksTask.add_mask_subtask(
+ regionGroup,obsFileName=obsFileName,
+ lonVar=localObsDict['lonVar'],
+ latVar=localObsDict['latVar'],
+ meshName=localObsDict['gridName'])
+ localObsDict['maskTask']=obsMasksSubtask
+ groupObsDicts[obsName]=localObsDict
+
+ forregionNameinregionNames:
+ sectionName=None
+
+ # Compute weights for histogram
+ ifself.weightListisnotNone:
+ computeWeightsSubtask=ComputeHistogramWeightsSubtask(
+ self,regionName,mpasMasksSubtask,filePrefix,
+ self.variableList,self.weightList)
+ self.add_subtask(computeWeightsSubtask)
+
+ forseasoninself.seasons:
+
+ # Generate histogram plots
+ plotRegionSubtask=PlotRegionHistogramSubtask(
+ self,regionGroup,regionName,controlConfig,
+ sectionName,filePrefix,mpasClimatologyTask,
+ mpasMasksSubtask,obsMasksSubtask,groupObsDicts,
+ self.variableList,self.weightList,season)
+ plotRegionSubtask.run_after(mpasMasksSubtask)
+ plotRegionSubtask.run_after(obsMasksSubtask)
+ ifself.weightListisnotNone:
+ plotRegionSubtask.run_after(computeWeightsSubtask)
+ self.add_subtask(plotRegionSubtask)
+
+
+ defsetup_and_check(self):
+"""
+ Perform steps to set up the analysis and check for errors in the setup.
+
+ Raises
+ ------
+ OSError
+ If files are not present
+ """
+ # first, call setup_and_check from the base class (AnalysisTask),
+ # which will perform some common setup, including storing:
+ # self.inDirectory, self.plotsDirectory, self.namelist, self.streams
+ # self.calendar
+ super().setup_and_check()
+
+ # Add variables and seasons to climatology task
+ variableList=[]
+ forvarinself.variableList:
+ variableList.append(f'timeMonthly_avg_{var}')
+
+ self.mpasClimatologyTask.add_variables(variableList=variableList,
+ seasons=self.seasons)
+
+ iflen(self.obsList)>1:
+ raiseValueError('Histogram analysis does not currently support'
+ 'more than one observational product')
+
+
+
+classComputeHistogramWeightsSubtask(AnalysisTask):
+"""
+ Fetches weight variables from MPAS output files for each variable in
+ variableList.
+
+ """
+ def__init__(self,parentTask,regionName,mpasMasksSubtask,fullSuffix,
+ variableList,weightList):
+"""
+ Initialize weights task
+
+ Parameters
+ ----------
+ parentTask : ``AnalysisTask``
+ The parent task, used to get the ``taskName``, ``config`` and
+ ``componentName``
+
+ regionName : str
+ Name of the region to plot
+
+ mpasMasksSubtask : ``ComputeRegionMasksSubtask``
+ A task for creating mask MPAS files for each region to plot, used
+ to get the mask file name
+
+ obsDicts : dict of dicts
+ Information on the observations to compare agains
+
+ fullSuffix : str
+ The regionGroup and regionName combined and modified to be
+ appropriate as a task or file suffix
+
+ variableList: list of str
+ List of variables which will be weighted
+
+ weightList: list of str
+ List of variables by which to weight the variables in
+ variableList, of the same length as variableList
+
+ """
+
+ super(ComputeHistogramWeightsSubtask,self).__init__(
+ config=parentTask.config,
+ taskName=parentTask.taskName,
+ componentName=parentTask.componentName,
+ tags=parentTask.tags,
+ subtaskName=f'weights_{fullSuffix}_{regionName}')
+
+ self.mpasMasksSubtask=mpasMasksSubtask
+ self.regionName=regionName
+ self.filePrefix=fullSuffix
+ self.variableList=variableList
+ self.weightList=weightList
+
+ defrun_task(self):
+"""
+ Apply the region mask to each weight variable and save in a file common
+ to that region.
+ """
+
+ config=self.config
+ base_directory=build_config_full_path(
+ config,'output','histogramSubdirectory')
+
+ # Get cell mask for the region
+ region_mask_filename=self.mpasMasksSubtask.maskFileName
+ ds_region_mask=xarray.open_dataset(region_mask_filename)
+ mask_region_names=decode_strings(ds_region_mask.regionNames)
+ region_index=mask_region_names.index(self.regionName)
+ ds_mask=ds_region_mask.isel(nRegions=region_index)
+ cell_mask=ds_mask.regionCellMasks==1
+
+ # Open the restart file, which contains unmasked weight variables
+ restart_filename=self.runStreams.readpath('restart')[0]
+ ds_restart=xarray.open_dataset(restart_filename)
+ ds_restart=ds_restart.isel(Time=0)
+
+ # Save the cell mask only for the region in its own file, which may be
+ # referenced by future analysis (i.e., as a control run)
+ new_region_mask_filename= \
+ f'{base_directory}/{self.filePrefix}_{self.regionName}_mask.nc'
+ write_netcdf_with_fill(ds_mask,new_region_mask_filename)
+
+ ifself.weightListisnotNone:
+ ds_weights=xarray.Dataset()
+ # Fetch the weight variables and mask them for each region
+ forindex,varinenumerate(self.variableList):
+ weight_var_name=self.weightList[index]
+ ifweight_var_nameinds_restart.keys():
+ var_name=f'timeMonthly_avg_{var}'
+ ds_weights[f'{var_name}_weight']= \
+ ds_restart[weight_var_name].where(cell_mask,drop=True)
+ else:
+ self.logger.warn(f'Weight variable {weight_var_name} is '
+ f'not in the restart file, skipping')
+
+ weights_filename= \
+ f'{base_directory}/{self.filePrefix}_{self.regionName}_weights.nc'
+ write_netcdf_with_fill(ds_weights,weights_filename)
+
+
+classPlotRegionHistogramSubtask(AnalysisTask):
+"""
+ Plots a histogram diagram for a given ocean region
+
+ Attributes
+ ----------
+ regionGroup : str
+ Name of the collection of region to plot
+
+ regionName : str
+ Name of the region to plot
+
+ sectionName : str
+ The section of the config file to get options from
+
+ controlConfig : mpas_tools.config.MpasConfigParser
+ The configuration options for the control run (if any)
+
+ mpasClimatologyTask : ``MpasClimatologyTask``
+ The task that produced the climatology to be remapped and plotted
+
+ mpasMasksSubtask : ``ComputeRegionMasksSubtask``
+ A task for creating mask MPAS files for each region to plot, used
+ to get the mask file name
+
+ obsDicts : dict of dicts
+ Information on the observations to compare against
+
+ variableList: list of str
+ list of variables to plot
+
+ season : str
+ The season to compute the climatology for
+ """
+
+ def__init__(self,parentTask,regionGroup,regionName,controlConfig,
+ sectionName,fullSuffix,mpasClimatologyTask,
+ mpasMasksSubtask,obsMasksSubtask,obsDicts,variableList,
+ weightList,season):
+
+"""
+ Construct the analysis task.
+
+ Parameters
+ ----------
+ parentTask : ``AnalysisTask``
+ The parent task, used to get the ``taskName``, ``config`` and
+ ``componentName``
+
+ regionGroup : str
+ Name of the collection of region to plot
+
+ regionName : str
+ Name of the region to plot
+
+ controlconfig : mpas_tools.config.MpasConfigParser, optional
+ Configuration options for a control run (if any)
+
+ sectionName : str
+ The config section with options for this regionGroup
+
+ fullSuffix : str
+ The regionGroup and regionName combined and modified to be
+ appropriate as a task or file suffix
+
+ mpasClimatologyTask : ``MpasClimatologyTask``
+ The task that produced the climatology to be remapped and plotted
+
+ mpasMasksSubtask : ``ComputeRegionMasksSubtask``
+ A task for creating mask MPAS files for each region to plot, used
+ to get the mask file name
+
+ obsDicts : dict of dicts
+ Information on the observations to compare agains
+
+ season : str
+ The season to comput the climatogy for
+ """
+
+ # first, call the constructor from the base class (AnalysisTask)
+ super(PlotRegionHistogramSubtask,self).__init__(
+ config=parentTask.config,
+ taskName=parentTask.taskName,
+ componentName=parentTask.componentName,
+ tags=parentTask.tags,
+ subtaskName=f'plot_{fullSuffix}_{regionName}_{season}')
+
+ self.run_after(mpasClimatologyTask)
+ self.regionGroup=regionGroup
+ self.regionName=regionName
+ self.sectionName=sectionName
+ self.controlConfig=controlConfig
+ self.mpasClimatologyTask=mpasClimatologyTask
+ self.mpasMasksSubtask=mpasMasksSubtask
+ self.obsMasksSubtask=obsMasksSubtask
+ self.obsDicts=obsDicts
+ self.variableList=variableList
+ self.weightList=weightList
+ self.season=season
+ self.filePrefix=fullSuffix
+
+ defsetup_and_check(self):
+"""
+ Perform steps to set up the analysis and check for errors in the setup.
+
+ Raises
+ ------
+ IOError
+ If files are not present
+ """
+
+ # first, call setup_and_check from the base class (AnalysisTask),
+ # which will perform some common setup, including storing:
+ # self.inDirectory, self.plotsDirectory, self.namelist, self.streams
+ # self.calendar
+ super(PlotRegionHistogramSubtask,self).setup_and_check()
+
+ self.xmlFileNames=[]
+ forvarinself.variableList:
+ self.xmlFileNames.append(
+ f'{self.plotsDirectory}/{self.filePrefix}_{var}_'
+ f'{self.regionName}_{self.season}.xml')
+
+ defrun_task(self):
+"""
+ Plots histograms of properties in an ocean region.
+ """
+
+ self.logger.info(f"\nPlotting {self.season} histograms for "
+ f"{self.regionName}")
+
+ config=self.config
+ sectionName=self.sectionName
+
+ calendar=self.calendar
+
+ main_run_name=config.get('runs','mainRunName')
+
+ region_mask_filename=self.mpasMasksSubtask.maskFileName
+
+ ds_region_mask=xarray.open_dataset(region_mask_filename)
+
+ mask_region_names=decode_strings(ds_region_mask.regionNames)
+ region_index=mask_region_names.index(self.regionName)
+
+ ds_mask=ds_region_mask.isel(nRegions=region_index)
+ cell_mask=ds_mask.regionCellMasks==1
+
+ iflen(self.obsDicts)>0:
+ obs_region_mask_filename=self.obsMasksSubtask.maskFileName
+ ds_obs_region_mask=xarray.open_dataset(obs_region_mask_filename)
+ mask_region_names=decode_strings(ds_region_mask.regionNames)
+ region_index=mask_region_names.index(self.regionName)
+
+ ds_obs_mask=ds_obs_region_mask.isel(nRegions=region_index)
+ obs_cell_mask=ds_obs_mask.regionMasks==1
+
+ in_filename=get_unmasked_mpas_climatology_file_name(
+ config,self.season,self.componentName,op='avg')
+ ds=xarray.open_dataset(in_filename)
+
+ base_directory=build_config_full_path(
+ config,'output','histogramSubdirectory')
+
+ ifself.weightListisnotNone:
+ weights_filename= \
+ f'{base_directory}/{self.filePrefix}_{self.regionName}_' \
+ 'weights.nc'
+ ds_weights=xarray.open_dataset(weights_filename)
+
+ ifself.controlConfigisnotNone:
+ control_run_name=self.controlConfig.get('runs','mainRunName')
+ control_filename=get_unmasked_mpas_climatology_file_name(
+ self.controlConfig,self.season,self.componentName,op='avg')
+ ds_control=xarray.open_dataset(control_filename)
+ base_directory=build_config_full_path(
+ self.controlConfig,'output','histogramSubdirectory')
+ control_region_mask_filename= \
+ f'{base_directory}/{self.filePrefix}_{self.regionName}_mask.nc'
+ ds_control_region_masks=xarray.open_dataset(
+ control_region_mask_filename)
+ control_cell_mask=ds_control_region_masks.regionCellMasks==1
+ ifself.weightListisnotNone:
+ control_weights_filename=f'{base_directory}/' \
+ f'{self.filePrefix}_{self.regionName}_weights.nc'
+ ds_control_weights=xarray.open_dataset(
+ control_weights_filename)
+
+ ifconfig.has_option(self.taskName,'mainColor'):
+ mainColor=config.get(self.taskName,'mainColor')
+ else:
+ mainColor='C0'
+ ifconfig.has_option(self.taskName,'obsColor'):
+ obsColor=config.get(self.taskName,'obsColor')
+ else:
+ obsColor='C1'
+ ifconfig.has_option(self.taskName,'controlColor'):
+ controlColor=config.get(self.taskName,'controlColor')
+ else:
+ controlColor='C2'
+
+ ifconfig.has_option(self.taskName,'lineWidth'):
+ lineWidth=config.getfloat(self.taskName,'lineWidth')
+ else:
+ lineWidth=None
+
+ ifconfig.has_option(self.taskName,'titleFontSize'):
+ titleFontSize=config.getint(self.taskName,
+ 'titleFontSize')
+ else:
+ titleFontSize=None
+ ifconfig.has_option(self.taskName,'axisFontSize'):
+ axisFontSize=config.getint(self.taskName,
+ 'axisFontSize')
+ else:
+ axisFontSize=None
+
+ ifconfig.has_option(self.taskName,'defaultFontSize'):
+ defaultFontSize=config.getint(self.taskName,
+ 'defaultFontSize')
+ else:
+ defaultFontSize=None
+ ifconfig.has_option(self.taskName,'bins'):
+ bins=config.getint(self.taskName,'bins')
+ else:
+ bins=None
+
+ yLabel='normalized Probability Density Function'
+
+ forindex,varinenumerate(self.variableList):
+
+ fields=[]
+ weights=[]
+ legendText=[]
+ lineColors=[]
+
+ var_name=f'timeMonthly_avg_{var}'
+
+ title=f'{self.regionName.replace("_"," ")}, {self.season}'
+
+ caption=f'Normalized probability density function for ' \
+ f'{self.season}{var} climatologies in ' \
+ f'{self.regionName.replace("_"," ")}'
+
+ # Note: consider modifying this for more professional headings
+ varTitle=var
+
+ fields.append(ds[var_name].where(cell_mask,drop=True))
+ ifself.weightListisnotNone:
+ iff'{var_name}_weight'inds_weights.keys():
+ weights.append(ds_weights[f'{var_name}_weight'].values)
+ caption=f'{caption} weighted by {self.weightList[index]}'
+ else:
+ weights.append(None)
+ else:
+ weights.append(None)
+
+ legendText.append(main_run_name)
+ lineColors.append(mainColor)
+
+ xLabel=f"{ds[var_name].attrs['long_name']} " \
+ f"({ds[var_name].attrs['units']})"
+
+ forobs_nameinself.obsDicts:
+ localObsDict=dict(self.obsDicts[obs_name])
+ obs_filename=build_obs_path(
+ config,component=self.componentName,
+ relativePath=localObsDict['gridFileName'])
+ iff'{var}Var'notinlocalObsDict.keys():
+ self.logger.warn(
+ f'{var}Var is not present in {obs_name}, skipping '
+ f'{obs_name}')
+ continue
+ obs_var_name=localObsDict[f'{var}Var']
+ ds_obs=xarray.open_dataset(obs_filename)
+ ds_obs=ds_obs.where(obs_cell_mask,drop=True)
+ fields.append(ds_obs[obs_var_name])
+ legendText.append(obs_name)
+ lineColors.append(obsColor)
+ weights.append(None)
+ ifself.controlConfigisnotNone:
+ fields.append(ds_control[var_name].where(control_cell_mask,
+ drop=True))
+ control_run_name=self.controlConfig.get('runs',
+ 'mainRunName')
+ legendText.append(control_run_name)
+ lineColors.append(controlColor)
+ weights.append(ds_control_weights[f'{var_name}_weight'].values)
+
+ iflineWidthisnotNone:
+ lineWidths=[lineWidthforiinfields]
+ else:
+ lineWidths=None
+
+ histogram_analysis_plot(config,fields,calendar=calendar,
+ title=title,xLabel=xLabel,yLabel=yLabel,
+ bins=bins,weights=weights,
+ lineColors=lineColors,
+ lineWidths=lineWidths,
+ legendText=legendText,
+ titleFontSize=titleFontSize,
+ defaultFontSize=defaultFontSize)
+
+ out_filename=f'{self.plotsDirectory}/{self.filePrefix}_{var}_' \
+ f'{self.regionName}_{self.season}.png'
+ savefig(out_filename,config)
+
+ write_image_xml(
+ config=config,
+ filePrefix=f'{self.filePrefix}_{var}_{self.regionName}_'
+ f'{self.season}',
+ componentName='Ocean',
+ componentSubdirectory='ocean',
+ galleryGroup=f'{self.regionGroup} Histograms',
+ groupLink=f'histogram{var}',
+ gallery=varTitle,
+ thumbnailDescription=f'{self.regionName.replace("_"," ")} '
+ f'{self.season}',
+ imageDescription=caption,
+ imageCaption=caption)
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/stable/_modules/mpas_analysis/ocean/streamfunction_moc.html b/stable/_modules/mpas_analysis/ocean/streamfunction_moc.html
index ac5df1727..40e708709 100644
--- a/stable/_modules/mpas_analysis/ocean/streamfunction_moc.html
+++ b/stable/_modules/mpas_analysis/ocean/streamfunction_moc.html
@@ -191,13 +191,7 @@
Source code for mpas_analysis.ocean.streamfunction_moc
plotClimSubtask.run_after(computeClimSubtask)startYear=config.getint('timeSeries','startYear')
- endYear=config.get('timeSeries','endYear')
- ifendYear=='end':
- # a valid end year wasn't found, so likely the run was not found,
- # perhaps because we're just listing analysis tasks
- endYear=startYear
- else:
- endYear=int(endYear)
+ endYear=config.getint('timeSeries','endYear')years=range(startYear,endYear+1)
@@ -1148,7 +1142,7 @@
Source code for mpas_analysis.ocean.streamfunction_moc
refTopDepth =dsMOCIn.depth.values# first, copy all computed data
- forinIndexinrange(dsMOCIn.dims['Time']):
+ forinIndexinrange(dsMOCIn.sizes['Time']):mask=np.logical_and(dsMOCIn.year[inIndex].values==years,
@@ -1324,7 +1318,7 @@
Source code for mpas_analysis.ocean.streamfunction_moc
dsMOCIn.load()# first, copy all computed data
- forinIndexinrange(dsMOCIn.dims['Time']):
+ forinIndexinrange(dsMOCIn.sizes['Time']):mask=np.logical_and(dsMOCIn.year[inIndex].values==years,
diff --git a/stable/_modules/mpas_analysis/ocean/time_series_antarctic_melt.html b/stable/_modules/mpas_analysis/ocean/time_series_antarctic_melt.html
index 13b9a6234..46f08a632 100644
--- a/stable/_modules/mpas_analysis/ocean/time_series_antarctic_melt.html
+++ b/stable/_modules/mpas_analysis/ocean/time_series_antarctic_melt.html
@@ -115,6 +115,7 @@
Source code for mpas_analysis.ocean.time_series_antarctic_melt
from geometric_featuresimportFeatureCollection,read_feature_collectionfromgeometric_features.aggregationimportget_aggregator_by_name
+frommpas_tools.cime.constantsimportconstantsascime_constantsfrommpas_analysis.shared.analysis_taskimportAnalysisTask
@@ -182,19 +183,14 @@
Source code for mpas_analysis.ocean.time_series_antarctic_melt
# nothing else to do
return
- masksSubtask=regionMasksTask.add_mask_subtask(regionGroup=regionGroup)
+ masksSubtask= \
+ regionMasksTask.add_mask_subtask(regionGroup=regionGroup)self.iceShelfMasksFile=masksSubtask.geojsonFileNameiceShelvesToPlot=masksSubtask.expand_region_names(iceShelvesToPlot)startYear=config.getint('timeSeries','startYear')
- endYear=config.get('timeSeries','endYear')
- ifendYear=='end':
- # a valid end year wasn't found, so likely the run was not found,
- # perhaps because we're just listing analysis tasks
- endYear=startYear
- else:
- endYear=int(endYear)
+ endYear=config.getint('timeSeries','endYear')years=list(range(startYear,endYear+1))
@@ -289,8 +285,7 @@
Source code for mpas_analysis.ocean.time_series_antarctic_melt
Source code for mpas_analysis.ocean.time_series_antarctic_melt
savefig(outFileName,config)
- caption=f'Running Mean of Total Melt Flux under Ice Shelves in ' \
+ caption=f'Running Mean of Integrated Melt Flux under Ice Shelves in ' \
f'the {title} Region'write_image_xml(config=config,
@@ -817,13 +850,13 @@
Source code for mpas_analysis.ocean.time_series_antarctic_melt
Source code for mpas_analysis.ocean.time_series_antarctic_melt
@staticmethod
def_load_ice_shelf_fluxes(config):"""
- Reads melt flux time series and computes regional total melt flux and
- mean melt rate.
+ Reads melt flux time series and computes regional integrated melt flux
+ and mean melt rate. """# Authors# -------
@@ -909,7 +942,7 @@
Source code for mpas_analysis.ocean.time_series_antarctic_melt
Source code for mpas_analysis.ocean.time_series_ocean_regions
tags=['timeSeries','regions','antarctic'])startYear=config.getint('timeSeries','startYear')
- endYear=config.get('timeSeries','endYear')
- ifendYear=='end':
- # a valid end year wasn't found, so likely the run was not found,
- # perhaps because we're just listing analysis tasks
- endYear=startYear
- else:
- endYear=int(endYear)
+ endYear=config.getint('timeSeries','endYear')regionGroups=config.getexpression(self.taskName,'regionGroups')
@@ -645,7 +639,7 @@
Source code for mpas_analysis.ocean.time_series_ocean_regions
Source code for mpas_analysis.ocean.time_series_transport
tags=['timeSeries','transport'])startYear=config.getint('timeSeries','startYear')
- endYear=config.get('timeSeries','endYear')
- ifendYear=='end':
- # a valid end year wasn't found, so likely the run was not found,
- # perhaps because we're just listing analysis tasks
- endYear=startYear
- else:
- endYear=int(endYear)
+ endYear=config.getint('timeSeries','endYear')years=[yearforyearinrange(startYear,endYear+1)]
@@ -375,7 +369,7 @@
Source code for mpas_analysis.ocean.time_series_transport
Source code for mpas_analysis.shared.generalized_reader.generalized_reader
# select only the data in the specified range of datesds=ds.sel(Time=slice(startDate,endDate))
- ifds.dims['Time']==0:
+ ifds.sizes['Time']==0:raiseValueError('The data set contains no Time entries between ''dates {} and {}.'.format(days_to_datetime(startDate,calendar=calendar),
diff --git a/stable/_modules/mpas_analysis/shared/io/mpas_reader.html b/stable/_modules/mpas_analysis/shared/io/mpas_reader.html
index cc68772fc..73c1b5bfe 100644
--- a/stable/_modules/mpas_analysis/shared/io/mpas_reader.html
+++ b/stable/_modules/mpas_analysis/shared/io/mpas_reader.html
@@ -185,7 +185,7 @@
Source code for mpas_analysis.shared.io.mpas_reader
# select only the data in the specified range of dates
ds=ds.sel(Time=slice(startDate,endDate))
- ifds.dims['Time']==0:
+ ifds.sizes['Time']==0:raiseValueError('The data set contains no Time entries between ''dates {} and {}.'.format(days_to_datetime(startDate,calendar=calendar),
diff --git a/stable/_modules/mpas_analysis/shared/plot/climatology_map.html b/stable/_modules/mpas_analysis/shared/plot/climatology_map.html
index 0e2967c31..58339f7c8 100644
--- a/stable/_modules/mpas_analysis/shared/plot/climatology_map.html
+++ b/stable/_modules/mpas_analysis/shared/plot/climatology_map.html
@@ -647,16 +647,20 @@
Source code for mpas_analysis.shared.plot.climatology_map
# Authors
# -------# Xylar Asay-Davis
- defadd_arrow_to_line_2d(ax,path,arrow_spacing=8e5,arrow_width=1.5e4):
+
+ defadd_arrow_to_line_2d(ax,poly,arrow_spacing=8e5,arrow_width=1.5e4):""" https://stackoverflow.com/a/27637925/7728169 Add arrows to a matplotlib.lines.Line2D at selected locations.
+
+ Polygons instead of paths, following
+ https://gis.stackexchange.com/a/246861/143986 """
- v=path.vertices
- x=v[:,0]
- y=v[:,1]
+ x=poly[:,0]
+ y=poly[:,1]arrows=[]
- s=np.cumsum(np.sqrt(np.diff(x)**2+np.diff(y)**2))
+ delta=np.sqrt(np.diff(x)**2+np.diff(y)**2)
+ s=np.cumsum(delta)indices=np.searchsorted(s,arrow_spacing*np.arange(1,int(s[-1]/arrow_spacing)))forninindices:
@@ -715,7 +719,8 @@
Source code for mpas_analysis.shared.plot.climatology_map
if arrowsisnotNone:forcollectionincs.collections:forpathincollection.get_paths():
- add_arrow_to_line_2d(ax,path)
+ forpolyinpath.to_polygons():
+ add_arrow_to_line_2d(ax,poly)# 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)
diff --git a/stable/_sources/developers_guide/api.rst.txt b/stable/_sources/developers_guide/api.rst.txt
index f330adf65..b619d9016 100644
--- a/stable/_sources/developers_guide/api.rst.txt
+++ b/stable/_sources/developers_guide/api.rst.txt
@@ -61,6 +61,7 @@ Ocean tasks
.. autosummary::
:toctree: generated/
+ ConservationTask
ClimatologyMapSST
ClimatologyMapSSS
ClimatologyMapMLD
@@ -75,6 +76,7 @@ Ocean tasks
ClimatologyMapWaves
IndexNino34
MeridionalHeatTransport
+ OceanHistogram
StreamfunctionMOC
TimeSeriesOHCAnomaly
TimeSeriesTemperatureAnomaly
diff --git a/stable/_sources/developers_guide/generated/mpas_analysis.ocean.ConservationTask.rst.txt b/stable/_sources/developers_guide/generated/mpas_analysis.ocean.ConservationTask.rst.txt
new file mode 100644
index 000000000..f77b4cfbb
--- /dev/null
+++ b/stable/_sources/developers_guide/generated/mpas_analysis.ocean.ConservationTask.rst.txt
@@ -0,0 +1,54 @@
+mpas\_analysis.ocean.ConservationTask
+=====================================
+
+.. currentmodule:: mpas_analysis.ocean
+
+.. autoclass:: ConservationTask
+
+
+ .. automethod:: __init__
+
+
+ .. rubric:: Methods
+
+ .. autosummary::
+
+ ~ConservationTask.__init__
+ ~ConservationTask.add_subtask
+ ~ConservationTask.check_analysis_enabled
+ ~ConservationTask.check_generate
+ ~ConservationTask.close
+ ~ConservationTask.is_alive
+ ~ConservationTask.join
+ ~ConservationTask.kill
+ ~ConservationTask.run
+ ~ConservationTask.run_after
+ ~ConservationTask.run_task
+ ~ConservationTask.set_start_end_date
+ ~ConservationTask.setup_and_check
+ ~ConservationTask.start
+ ~ConservationTask.terminate
+
+
+
+
+
+ .. rubric:: Attributes
+
+ .. autosummary::
+
+ ~ConservationTask.BLOCKED
+ ~ConservationTask.FAIL
+ ~ConservationTask.READY
+ ~ConservationTask.RUNNING
+ ~ConservationTask.SUCCESS
+ ~ConservationTask.UNSET
+ ~ConservationTask.authkey
+ ~ConservationTask.daemon
+ ~ConservationTask.exitcode
+ ~ConservationTask.ident
+ ~ConservationTask.name
+ ~ConservationTask.pid
+ ~ConservationTask.sentinel
+
+
\ No newline at end of file
diff --git a/stable/_sources/developers_guide/generated/mpas_analysis.ocean.OceanHistogram.rst.txt b/stable/_sources/developers_guide/generated/mpas_analysis.ocean.OceanHistogram.rst.txt
new file mode 100644
index 000000000..b14e66bfe
--- /dev/null
+++ b/stable/_sources/developers_guide/generated/mpas_analysis.ocean.OceanHistogram.rst.txt
@@ -0,0 +1,54 @@
+mpas\_analysis.ocean.OceanHistogram
+===================================
+
+.. currentmodule:: mpas_analysis.ocean
+
+.. autoclass:: OceanHistogram
+
+
+ .. automethod:: __init__
+
+
+ .. rubric:: Methods
+
+ .. autosummary::
+
+ ~OceanHistogram.__init__
+ ~OceanHistogram.add_subtask
+ ~OceanHistogram.check_analysis_enabled
+ ~OceanHistogram.check_generate
+ ~OceanHistogram.close
+ ~OceanHistogram.is_alive
+ ~OceanHistogram.join
+ ~OceanHistogram.kill
+ ~OceanHistogram.run
+ ~OceanHistogram.run_after
+ ~OceanHistogram.run_task
+ ~OceanHistogram.set_start_end_date
+ ~OceanHistogram.setup_and_check
+ ~OceanHistogram.start
+ ~OceanHistogram.terminate
+
+
+
+
+
+ .. rubric:: Attributes
+
+ .. autosummary::
+
+ ~OceanHistogram.BLOCKED
+ ~OceanHistogram.FAIL
+ ~OceanHistogram.READY
+ ~OceanHistogram.RUNNING
+ ~OceanHistogram.SUCCESS
+ ~OceanHistogram.UNSET
+ ~OceanHistogram.authkey
+ ~OceanHistogram.daemon
+ ~OceanHistogram.exitcode
+ ~OceanHistogram.ident
+ ~OceanHistogram.name
+ ~OceanHistogram.pid
+ ~OceanHistogram.sentinel
+
+
\ No newline at end of file
diff --git a/stable/_sources/tutorials/dev_getting_started.rst.txt b/stable/_sources/tutorials/dev_getting_started.rst.txt
index c263396fc..abee8f44c 100644
--- a/stable/_sources/tutorials/dev_getting_started.rst.txt
+++ b/stable/_sources/tutorials/dev_getting_started.rst.txt
@@ -642,24 +642,16 @@ climate index.
## options related to producing time series plots, often to compare against
## observations and previous runs
- # start and end years for timeseries analysis. Use endYear = end to indicate
- # that the full range of the data should be used. If errorOnMissing = False,
- # the start and end year will be clipped to the valid range. Otherwise, out
- # of bounds values will lead to an error. In a "control" config file used in
- # a "main vs. control" analysis run, the range of years must be valid and
- # cannot include "end" because the original data may not be available.
+ # start and end years for timeseries analysis. Out-of-bounds values will lead
+ # to an error.
startYear = 1
endYear = 5
[index]
## options related to producing nino index.
- # start and end years for El Nino 3.4 analysis. Use endYear = end to indicate
- # that the full range of the data should be used. If errorOnMissing = False,
- # the start and end year will be clipped to the valid range. Otherwise, out
- # of bounds values will lead to an error. In a "control" config file used in
- # a "main vs. control" analysis run, the range of years must be valid and
- # cannot include "end" because the original data may not be available.
+ # start and end years for El Nino 3.4 analysis. Out-of-bounds values will lead
+ # to an error.
startYear = 1
endYear = 5
diff --git a/stable/_sources/tutorials/getting_started.rst.txt b/stable/_sources/tutorials/getting_started.rst.txt
index aa7058158..1a0036a11 100644
--- a/stable/_sources/tutorials/getting_started.rst.txt
+++ b/stable/_sources/tutorials/getting_started.rst.txt
@@ -443,24 +443,16 @@ climate index.
## options related to producing time series plots, often to compare against
## observations and previous runs
- # start and end years for timeseries analysis. Use endYear = end to indicate
- # that the full range of the data should be used. If errorOnMissing = False,
- # the start and end year will be clipped to the valid range. Otherwise, out
- # of bounds values will lead to an error. In a "control" config file used in
- # a "main vs. control" analysis run, the range of years must be valid and
- # cannot include "end" because the original data may not be available.
+ # start and end years for timeseries analysis. Out-of-bounds values will lead
+ # to an error.
startYear = 1
endYear = 5
[index]
## options related to producing nino index.
- # start and end years for El Nino 3.4 analysis. Use endYear = end to indicate
- # that the full range of the data should be used. If errorOnMissing = False,
- # the start and end year will be clipped to the valid range. Otherwise, out
- # of bounds values will lead to an error. In a "control" config file used in
- # a "main vs. control" analysis run, the range of years must be valid and
- # cannot include "end" because the original data may not be available.
+ # start and end years for timeseries analysis. Out-of-bounds values will lead
+ # to an error.
startYear = 1
endYear = 5
diff --git a/stable/_sources/users_guide/all_obs.rst.txt b/stable/_sources/users_guide/all_obs.rst.txt
index c71178f2c..368382f1a 100644
--- a/stable/_sources/users_guide/all_obs.rst.txt
+++ b/stable/_sources/users_guide/all_obs.rst.txt
@@ -11,6 +11,7 @@
obs/sose.rst
obs/rignot_melt.rst
obs/adusumilli_melt.rst
+ obs/paolo_melt.rst
obs/hadisst_nino.rst
obs/ers_sst_nino.rst
obs/schmidtko.rst
diff --git a/stable/_sources/users_guide/analysis_tasks.rst.txt b/stable/_sources/users_guide/analysis_tasks.rst.txt
index 63ccda036..313adda83 100644
--- a/stable/_sources/users_guide/analysis_tasks.rst.txt
+++ b/stable/_sources/users_guide/analysis_tasks.rst.txt
@@ -37,6 +37,7 @@ Analysis Tasks
tasks/oceanRegionalProfiles
tasks/regionalTSDiagrams
tasks/oceanHistogram
+ tasks/conservation
tasks/climatologyMapSeaIceConcNH
tasks/climatologyMapSeaIceThickNH
diff --git a/stable/_sources/users_guide/config/index.rst.txt b/stable/_sources/users_guide/config/index.rst.txt
index be798d607..48a3a95f6 100644
--- a/stable/_sources/users_guide/config/index.rst.txt
+++ b/stable/_sources/users_guide/config/index.rst.txt
@@ -13,22 +13,15 @@ determine the start and end years of climate indices (such as El Ni |n~| o
[index]
## options related to producing nino index.
- # start and end years for El Nino 3.4 analysis. Use endYear = end to indicate
- # that the full range of the data should be used. If errorOnMissing = False,
- # the start and end year will be clipped to the valid range. Otherwise, out
- # of bounds values will lead to an error. In a "control" config file used in
- # a "main vs. control" analysis run, the range of years must be valid and
- # cannot include "end" because the original data may not be available.
+ # start and end years for El Nino 3.4 analysis. Out-of-bounds values will lead
+ # to an error.
startYear = 1
- endYear = end
+ endYear = 20
Start and End Year
------------------
A custom config file should specify a start and end year for time axis.
-If ``errorOnMissing = False`` in the ``input`` section and the start or end
-year is beyond the range of the simulation, the range will be reduced to those
-dates with available data and a warning message will be displayed. If
-``errorOnMissing = True``, out of range year will produce an error.
+Out of range year will produce an error.
diff --git a/stable/_sources/users_guide/config/timeSeries.rst.txt b/stable/_sources/users_guide/config/timeSeries.rst.txt
index 15dfeccc4..425ee9abb 100644
--- a/stable/_sources/users_guide/config/timeSeries.rst.txt
+++ b/stable/_sources/users_guide/config/timeSeries.rst.txt
@@ -16,23 +16,16 @@ for anomalies::
# only the anomaly over a later span of years is of interest.
# anomalyRefYear = 249
- # start and end years for timeseries analysis. Use endYear = end to indicate
- # that the full range of the data should be used. If errorOnMissing = False,
- # the start and end year will be clipped to the valid range. Otherwise, out
- # of bounds values will lead to an error. In a "control" config file used in
- # a "main vs. control" analysis run, the range of years must be valid and
- # cannot include "end" because the original data may not be available.
+ # start and end years for timeseries analysis. Out-of-bounds values will lead
+ # to an error.
startYear = 1
- endYear = end
+ endYear = 20
Start and End Year
------------------
A custom config file should specify a start and end year for time series.
-If ``errorOnMissing = False`` in the ``input`` section and the start or end
-year is beyond the range of the simulation, the range will be reduced to those
-dates with available data and a warning message will be displayed. If
-``errorOnMissing = True``, out of range year will produce an error.
+Out of range year will produce an error.
Anomaly Reference Year
diff --git a/stable/_sources/users_guide/obs/paolo_melt.rst.txt b/stable/_sources/users_guide/obs/paolo_melt.rst.txt
new file mode 100644
index 000000000..ebbbd94be
--- /dev/null
+++ b/stable/_sources/users_guide/obs/paolo_melt.rst.txt
@@ -0,0 +1,32 @@
+.. _paolo_melt:
+
+Antarctic melt rates and fluxes
+===============================
+
+Description
+-----------
+Melt rates and melt fluxes from Paolo et al. (2023)
+
+Source
+------
+`Data from: ANT_G1920V01_IceShelfMelt.nc`_
+
+Release Policy
+--------------
+Not stated.
+
+References
+----------
+`Paolo et al. (2023)`_
+
+`bibtex file`_
+
+MPAS-Analysis Tasks
+-------------------
+- :ref:`task_climatologyMapAntarcticMelt`
+- :ref:`task_timeSeriesAntarcticMelt`
+
+.. _`Data from: ANT_G1920V01_IceShelfMelt.nc`: https://doi.org/10.5067/SE3XH9RXQWAM
+.. _`Paolo et al. (2023)`: https://doi.org/10.5194/tc-17-3409-2023
+.. _`bibtex file`: https://web.lcrc.anl.gov/public/e3sm/diagnostics/observations/Ocean/Melt/Paolo/obs.bib
+
diff --git a/stable/_sources/users_guide/ocean_obs_table.rst.txt b/stable/_sources/users_guide/ocean_obs_table.rst.txt
index 0a0c7def0..6f5b289ce 100644
--- a/stable/_sources/users_guide/ocean_obs_table.rst.txt
+++ b/stable/_sources/users_guide/ocean_obs_table.rst.txt
@@ -14,6 +14,7 @@ Observational Dataset Source
:ref:`sose` `SOSE Website at UCSD`_ `Mazloff et al. (2010)`_
:ref:`rignot_melt` `Ice-Shelf Melting Around Antarctica`_ `Rignot et al. (2013)`_
:ref:`adusumilli_melt` `Data from: Interannual variations in meltwater input to the Southern Ocean from Antarctic ice shelves`_ `Adusumilli et al. (2020)`_
+:ref:`paolo_melt` `Data from: ANT_G1920V01_IceShelfMelt.nc`_ `Paolo et al. (2023)`_
:ref:`hadisst_nino` `NCAR Hadley-NOAA/OI SST website`_ `Hurrell et al. (2008)`_
:ref:`ers_sst_nino` `NOAA ERSST v4 website`_ - `Huang et al. (2014)`_
- `Liu et al. (2014)`_
@@ -58,6 +59,8 @@ Observational Dataset Source
.. _`Rignot et al. (2013)`: http://science.sciencemag.org/content/341/6143/266
.. _`Data from: Interannual variations in meltwater input to the Southern Ocean from Antarctic ice shelves`: https://doi.org/10.6075/J04Q7SHT
.. _`Adusumilli et al. (2020)`: https://doi.org/10.1038/s41561-020-0616-z
+.. _`Data from: ANT_G1920V01_IceShelfMelt.nc`: https://doi.org/10.5067/SE3XH9RXQWAM
+.. _`Paolo et al. (2023)`: https://doi.org/10.5194/tc-17-3409-2023
.. _`NCAR Hadley-NOAA/OI SST website`: https://climatedataguide.ucar.edu/climate-data/merged-hadley-noaaoi-sea-surface-temperature-sea-ice-concentration-hurrell-et-al-2008
.. _`Hurrell et al. (2008)`: https://doi.org/10.1175/2008JCLI2292.1
.. _`NOAA ERSST v4 website`: https://www.ncdc.noaa.gov/data-access/marineocean-data/extended-reconstructed-sea-surface-temperature-ersst-v4
diff --git a/stable/_sources/users_guide/tasks/climatologyMapAntarcticMelt.rst.txt b/stable/_sources/users_guide/tasks/climatologyMapAntarcticMelt.rst.txt
index 72d0f930c..6c769b5a9 100644
--- a/stable/_sources/users_guide/tasks/climatologyMapAntarcticMelt.rst.txt
+++ b/stable/_sources/users_guide/tasks/climatologyMapAntarcticMelt.rst.txt
@@ -4,7 +4,7 @@ climatologyMapAntarcticMelt
===========================
An analysis task for comparison of Antarctic maps of melt rates against
-observations from `Adusumilli et al. (2020) `_.
+observations from `Paolo et al. (2023) `_.
Component and Tags::
@@ -76,7 +76,7 @@ For more details, see:
Observations
------------
-:ref:`adusumilli_melt`
+:ref:`paolo_melt`
Example Result
--------------
diff --git a/stable/_sources/users_guide/tasks/conservation.rst.txt b/stable/_sources/users_guide/tasks/conservation.rst.txt
new file mode 100644
index 000000000..85bc54b65
--- /dev/null
+++ b/stable/_sources/users_guide/tasks/conservation.rst.txt
@@ -0,0 +1,63 @@
+.. _task_conservation:
+
+conservation
+============
+
+An analysis task for plotting histograms of 2-d variables of climatologies
+in ocean regions.
+
+Component and Tags::
+
+ component: ocean
+ tags: timeseries, conservation
+
+Configuration Options
+---------------------
+
+The following configuration options are available for this task:
+
+.. code-block:: cfg
+
+ [conservation]
+ ## options related to producing time series plots, often to compare against
+ ## observations and previous runs
+
+ # the year from which to compute anomalies if not the start year of the
+ # simulation. This might be useful if a long spin-up cycle is performed and
+ # only the anomaly over a later span of years is of interest.
+ # anomalyRefYear = 249
+
+ # start and end years for timeseries analysis. Use endYear = end to indicate
+ # that the full range of the data should be used. If errorOnMissing = False,
+ # the start and end year will be clipped to the valid range. Otherwise, out
+ # of bounds values will lead to an error. In a "control" config file used in
+ # a "main vs. control" analysis run, the range of years must be valid and
+ # cannot include "end" because the original data may not be available.
+ startYear = 1
+ endYear = end
+
+ # Plot types to generate. The following plotTypes are supported:
+ # total_energy_flux : Total energy flux
+ # absolute_energy_error : Energy error
+ # ice_salt_flux : Salt flux related to land ice and sea ice
+ # absolute_salt_error : Salt conservation error
+ # total_mass_flux : Total mass flux
+ # total_mass_change : Total mass anomaly
+ # land_ice_mass_change : Mass anomaly due to land ice fluxes
+ # land_ice_ssh_change : SSH anomaly due to land ice fluxes
+ # land_ice_mass_flux_components : Mass fluxes from land ice
+ plotTypes = 'land_ice_mass_flux_components'
+
+ # line colors for the main, control and obs curves
+ # see https://matplotlib.org/stable/gallery/color/named_colors.html
+ # and https://matplotlib.org/stable/tutorials/colors/colors.html
+ mainColor = black
+ controlColor = tab:red
+
+
+Example Result
+--------------
+
+.. image:: examples/total_mass_flux.png
+ :width: 500 px
+ :align: center
diff --git a/stable/_sources/users_guide/tasks/timeSeriesAntarcticMelt.rst.txt b/stable/_sources/users_guide/tasks/timeSeriesAntarcticMelt.rst.txt
index 6dcef1795..50e84eeb0 100644
--- a/stable/_sources/users_guide/tasks/timeSeriesAntarcticMelt.rst.txt
+++ b/stable/_sources/users_guide/tasks/timeSeriesAntarcticMelt.rst.txt
@@ -4,8 +4,9 @@ timeSeriesAntarcticMelt
=======================
An analysis task for plotting time series of mean melt rates per ice shelf or
-Antarctic region along with observations from `Rignot et al. (2013)`_
-and `Adusumilli et al. (2020) `_.
+Antarctic region along with observations from `Rignot et al. (2013)`_,
+`Adusumilli et al. (2020) `_,
+and `Paolo et al. (2023) `_.
Component and Tags::
diff --git a/stable/_sources/versions.rst.txt b/stable/_sources/versions.rst.txt
index c507a6086..e558822d6 100644
--- a/stable/_sources/versions.rst.txt
+++ b/stable/_sources/versions.rst.txt
@@ -20,6 +20,7 @@ Documentation On GitHub
`v1.7.2`_ `1.7.2`_
`v1.8.0`_ `1.8.0`_
`v1.9.0`_ `1.9.0`_
+`v1.10.0`_ `1.10.0`_
================ ===============
.. _`stable`: ../stable/index.html
@@ -38,6 +39,7 @@ Documentation On GitHub
.. _`v1.7.2`: ../1.7.2/index.html
.. _`v1.8.0`: ../1.8.0/index.html
.. _`v1.9.0`: ../1.9.0/index.html
+.. _`v1.10.0`: ../1.10.0/index.html
.. _`main`: https://github.com/MPAS-Dev/MPAS-Analysis/tree/main
.. _`develop`: https://github.com/MPAS-Dev/MPAS-Analysis/tree/develop
.. _`1.2.6`: https://github.com/MPAS-Dev/MPAS-Analysis/tree/1.2.6
@@ -54,3 +56,4 @@ Documentation On GitHub
.. _`1.7.2`: https://github.com/MPAS-Dev/MPAS-Analysis/tree/1.7.2
.. _`1.8.0`: https://github.com/MPAS-Dev/MPAS-Analysis/tree/1.8.0
.. _`1.9.0`: https://github.com/MPAS-Dev/MPAS-Analysis/tree/1.9.0
+.. _`1.10.0`: https://github.com/MPAS-Dev/MPAS-Analysis/tree/1.10.0
diff --git a/stable/developers_guide/api.html b/stable/developers_guide/api.html
index 2b96d7849..2d4742774 100644
--- a/stable/developers_guide/api.html
+++ b/stable/developers_guide/api.html
@@ -84,6 +84,7 @@
This task generates time series plots from output from the conservation
+analysis member. A number of different plot types are supported, as indicated
+in the plotTypes config option in the conservation section.
inputFiles (list of str) – The paths to all conservation AM files
+
mainRunName (str) – The name of the main run from the config file
+
plotTypes (list of str) – The plot types requested in the config file
+
masterVariableList (dict of key-[list of str] pairs) – Keys are the supported plot types. Entries are lists of the variables
+that are needed to produce that plot type.
+
derivedVariableList (dict of key-[list of str] pairs) – Keys are the derived variables. Entries are lists of variables in the
+AM output that are needed to derive that variable.
+
xmlFileNames (list of str) – File names for xml output with full path
+
filePrefixes (list of str) – File prefixes for xml files
+
variableList (dict of key-[list of str] pairs) – Keys are the requested plot types. Entries are lists of the variables
+in the AM output that are needed to produce that plot type.