diff --git a/stable/_images/total_mass_flux.png b/stable/_images/total_mass_flux.png new file mode 100644 index 000000000..efd40eabf Binary files /dev/null and b/stable/_images/total_mass_flux.png differ diff --git a/stable/_modules/index.html b/stable/_modules/index.html index 59e3cc847..b0ffcec02 100644 --- a/stable/_modules/index.html +++ b/stable/_modules/index.html @@ -110,6 +110,8 @@

All modules for which code is available

  • mpas_analysis.ocean.climatology_map_sst
  • mpas_analysis.ocean.climatology_map_waves
  • mpas_analysis.ocean.compute_anomaly_subtask
  • +
  • mpas_analysis.ocean.conservation
  • +
  • mpas_analysis.ocean.histogram
  • mpas_analysis.ocean.index_nino34
  • mpas_analysis.ocean.meridional_heat_transport
  • mpas_analysis.ocean.plot_depth_integrated_time_series_subtask
  • diff --git a/stable/_modules/mpas_analysis/__main__.html b/stable/_modules/mpas_analysis/__main__.html index 0d97d1b44..27dbe5514 100644 --- a/stable/_modules/mpas_analysis/__main__.html +++ b/stable/_modules/mpas_analysis/__main__.html @@ -273,6 +273,9 @@

    Source code for mpas_analysis.__main__

             config, oceanClimatologyTasks['avg'], oceanRegionMasksTask,
             controlConfig))
     
    +    analyses.append(ocean.ConservationTask(
    +        config, controlConfig))
    +
         analyses.append(ocean.RegionalTSDiagrams(
             config, oceanClimatologyTasks['avg'], oceanRegionMasksTask,
             controlConfig))
    diff --git a/stable/_modules/mpas_analysis/ocean/climatology_map_antarctic_melt.html b/stable/_modules/mpas_analysis/ocean/climatology_map_antarctic_melt.html
    index 6f0085b21..3f532826a 100644
    --- a/stable/_modules/mpas_analysis/ocean/climatology_map_antarctic_melt.html
    +++ b/stable/_modules/mpas_analysis/ocean/climatology_map_antarctic_melt.html
    @@ -179,7 +179,6 @@ 

    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

    mpasClimatologyTask=mpasClimatologyTask, parentTask=self, climatologyName=fieldName, - variableList=[mpasFieldName], + variableList=None, comparisonGridNames=comparisonGridNames, seasons=seasons, iselValues=iselValues) @@ -220,7 +219,7 @@

    Source code for mpas_analysis.ocean.climatology_map_antarctic_melt

    if controlConfig is None: refTitleLabel = \ - 'Observations (Adusumilli et al, 2020)' + 'Observations (Paolo et al. 2023)' observationsDirectory = build_obs_path( config, 'ocean', 'meltSubdirectory') @@ -238,16 +237,13 @@

    Source code for mpas_analysis.ocean.climatology_map_antarctic_melt

    res = np.amax(avail_res[valid]) obsFileName = \ - f'{observationsDirectory}/Adusumilli/Adusumilli_2020_' \ - f'iceshelf_melt_rates_2010-2018_v0_6000x6000km_{res:g}km_' \ - f'Antarctic_stereo.20230504.nc' - refFieldName = 'meltRate' - outFileLabel = 'meltAdusumilli' - galleryName = 'Observations: Adusumilli et al. (2020)' + f'{observationsDirectory}/Paolo/Paolo_2023_' \ + f'iceshelf_melt_rates_1992-2017_v1.0_6000x6000km_{res:g}km_' \ + f'Antarctic_stereo.20240220.nc' remapObservationsSubtask = RemapObservedAntarcticMeltClimatology( parentTask=self, seasons=seasons, fileName=obsFileName, - outFilePrefix=refFieldName, + outFilePrefix='meltRate', comparisonGridNames=comparisonGridNames) self.add_subtask(remapObservationsSubtask) diffTitleLabel = 'Model - Observations' @@ -255,33 +251,117 @@

    Source code for mpas_analysis.ocean.climatology_map_antarctic_melt

    else: remapObservationsSubtask = None controlRunName = controlConfig.get('runs', 'mainRunName') - galleryName = None refTitleLabel = f'Control: {controlRunName}' + diffTitleLabel = 'Main - Control' + + totalFluxVar = 'timeMonthly_avg_landIceFreshwaterFluxTotal' + landIceFluxVar = 'timeMonthly_avg_landIceFreshwaterFlux' + frazilFluxVar = 'timeMonthly_avg_frazilIceFreshwaterFlux' + mpasFieldName = totalFluxVar + + if controlConfig is None: + refFieldName = 'meltRate' + else: refFieldName = mpasFieldName - outFileLabel = 'melt' - diffTitleLabel = 'Main - Control' for comparisonGridName in comparisonGridNames: for season in seasons: # 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 + + if controlConfig is None: + refFieldName = 'meltRate' + else: + refFieldName = mpasFieldName + + for comparisonGridName in comparisonGridNames: + for season in seasons: + # 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 + + if controlConfig is None: + refTitleLabel = None + refFieldName = None + diffTitleLabel = None + + else: + controlRunName = controlConfig.get('runs', 'mainRunName') + refTitleLabel = f'Control: {controlRunName}' + refFieldName = mpasFieldName + diffTitleLabel = 'Main - Control' + + for comparisonGridName in comparisonGridNames: + for season in seasons: + # 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 + def setup_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' + + if totalFluxVar in self.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() + def run_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] + for fieldName in self.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' return climatology @@ -527,7 +633,8 @@

    Source code for mpas_analysis.ocean.climatology_map_antarctic_melt

    pool=ThreadPool(1)): # Load data: - inFileName = self.mpasClimatologyTask.get_file_name(self.season) + inFileName = \ + self.mpasClimatologyTask.get_file_name(self.season) mpasFieldName = 'timeMonthly_avg_landIceFreshwaterFlux' dsIn = xr.open_dataset(inFileName) freshwaterFlux = dsIn[mpasFieldName] @@ -550,7 +657,8 @@

    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) + + +class PlotAntarcticMeltSubtask(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 + + def setup_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 = (totalFluxVar in allVariables) + + if self.mpasFieldName == landIceFluxVar and plotAll and \ + self.controlConfig is None: + # 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 == landIceFluxVar or plotAll) + + if self.doPlot: + super().setup_and_check() + else: + # still need to call the base class's method + AnalysisTask.setup_and_check(self=self) + + def run_task(self): + """ + Plot the variable if available + """ + if self.doPlot: + super().run_task()
    diff --git a/stable/_modules/mpas_analysis/ocean/climatology_map_argo.html b/stable/_modules/mpas_analysis/ocean/climatology_map_argo.html index 9b7d2c3e1..6b3895287 100644 --- a/stable/_modules/mpas_analysis/ocean/climatology_map_argo.html +++ b/stable/_modules/mpas_analysis/ocean/climatology_map_argo.html @@ -541,7 +541,7 @@

    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']] return dsObs 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 + + + + + + + + + + + + + + + +
    + + +
    + +
    +
    +
    +
      +
    • + + +
    • +
    • +
    +
    +
    +
    +
    + +

    Source code for mpas_analysis.ocean.conservation

    +# 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
    +
    +from distutils.spawn import find_executable
    +import numpy as np
    +import matplotlib.pyplot as plt
    +import os
    +import subprocess
    +import xarray as xr
    +
    +from mpas_analysis.shared.analysis_task import AnalysisTask
    +from mpas_analysis.shared.constants import constants
    +from mpas_analysis.shared.html import write_image_xml
    +from mpas_analysis.shared.io import open_mpas_dataset
    +from mpas_analysis.shared.io.utility import build_config_full_path, \
    +    make_directories, get_files_year_month, decode_strings
    +from mpas_analysis.shared.plot import timeseries_analysis_plot, savefig
    +from mpas_analysis.shared.timekeeping.utility import date_to_days, \
    +    days_to_datetime
    +
    +
    +
    +[docs] +class ConservationTask(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 + +
    +[docs] + def __init__(self, config, controlConfig): + """ + Construct the analysis task. + + Parameters + ---------- + config : mpas_tools.config.MpasConfigParser + Contains configuration options + """ + # Authors + # ------- + # Carolyn Begeman + + super(ConservationTask, self).__init__( + config=config, + taskName='oceanConservation', + componentName='ocean', + tags=['timeSeries', 'conservation']) + + self.controlConfig = controlConfig
    + + + def setup_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)) + + if len(self.inputFiles) == 0: + raise IOError(f'No files were found matching {self.inputFiles}') + + with xr.open_dataset(self.inputFiles[0]) as ds: + 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 = {} + for plot_type in self.plotTypes: + if plot_type not in self.masterVariableList.keys(): + raise ValueError(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]) + + def run_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 = [] + for plot_type in self.plotTypes: + for varname in self.variableList[plot_type]: + all_plots_variable_list.append(varname) + self._compute_time_series_with_ncrcat(all_plots_variable_list) + for plot_type in self.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 = [] + if self.allVariables is None: + raise ValueError('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.') + + for variable in target_variable_list: + if variable not in self.allVariables and \ + variable not in self.derivedVariableList.keys(): + raise ValueError( + f'{variable} is not available in conservationCheck' + 'output:\n{self.allVariables}') + + if variable in self.allVariables and variable not in variable_list: + variable_list.append(variable) + # If it's a derived variable, add all of the variables it depends on + if variable in self.derivedVariableList.keys() and \ + variable not in variable_list: + for var in self.derivedVariableList[variable]: + variable_list.append(var) + + return variable_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') + + if self.controlConfig is not None: + 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') + if self.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 = [] + for index, varname in enumerate(self.masterVariableList[plot_type]): + variable = self._get_variable(ds, varname) + fields.append(variable) + legend_text = '' + if self.controlConfig is not None: + legend_text = self.mainRunName + if len(self.masterVariableList[plot_type]) > 1: + if len(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]) + if self.controlConfig is not None: + variable = self._get_variable(ds_ref, varname) + fields.append(variable) + legend_text = self.controlConfig.get('runs', 'mainRunName') + if len(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 = [3 for i in fields] + if config.has_option('timeSeries', 'movingAveragePoints'): + movingAveragePoints = config.getint('timeSeries', + 'movingAveragePoints') + else: + movingAveragePoints = None + + if config.has_option('timeSeries', 'firstYearXTicks'): + firstYearXTicks = config.getint('timeSeries', + 'firstYearXTicks') + else: + firstYearXTicks = None + + if config.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): + if varname not in self.derivedVariableList: + variable = ds[varname] + else: + # Here we keep the units mks + if varname == '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 + elif varname == '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) + + elif varname == '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) + + elif varname == '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] + if not os.path.exists(ts_file): + raise ValueError(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: + raise ValueError(f'Attempted to derive non-supported variable {varname}') + + removed_vars = ['accumulatedRemovedRiverRunoffFlux', + 'accumulatedRemovedIceRunoffFlux'] + if varname in removed_vars: + variable = -variable + + if not mks: + # 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 (varname in mass_vars) or (varname in salt_vars): + # Convert from kg to Gt + variable = variable * 1e-12 + if (varname in mass_flux_vars) or (varname in salt_flux_vars): + # Convert from kg/s to Gt/yr + variable = variable * 1e-12 * constants.sec_per_year + if varname in ssh_vars: + # Convert from m to mm + variable = variable * 1e3 + + return variable + + 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. + """ + + if find_executable('ncrcat') is None: + raise OSError('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 + if os.path.exists(self.outputFile): + # make sure all the necessary variables are also present + with xr.open_dataset(self.outputFile) as ds: + if ds.sizes['Time'] == 0: + updateSubset = False + else: + updateSubset = True + for variableName in variable_list: + if variableName not in ds.variables: + updateSubset = False + break + + if updateSubset: + # 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 = [] + for index, inputFile in enumerate(fileNames): + if totalMonths[index] > lastTotalMonths: + inputFiles.append(inputFile) + + if len(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)] + + if append: + 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}') + for handler in self.logger.handlers: + handler.flush() + + process = subprocess.Popen(args, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + stdout, stderr = process.communicate() + + if stdout: + stdout = stdout.decode('utf-8') + for line in stdout.split('\n'): + self.logger.info(line) + if stderr: + stderr = stderr.decode('utf-8') + for line in stderr.split('\n'): + self.logger.error(line) + + if process.returncode != 0: + raise subprocess.CalledProcessError(process.returncode, + ' '.join(args))
    + +
    + +
    +
    +
    + +
    + +
    +

    © Copyright This software is open source software available under the BSD-3license. Copyright (c) 2022 Triad National Security, LLC. All rights reserved. Copyright (c) 2018 Lawrence Livermore National Security, LLC. All rights reserved. Copyright (c) 2018 UT-Battelle, LLC. All rights reserved..

    +
    + + Built with Sphinx using a + theme + provided by Read the Docs. + + +
    +
    +
    +
    +
    + + + + \ 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 + + + + + + + + + + + + + + + +
    + + +
    + +
    +
    +
    +
      +
    • + + +
    • +
    • +
    +
    +
    +
    +
    + +

    Source code for mpas_analysis.ocean.histogram

    +# -*- 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
    +#
    +import os
    +import xarray
    +import numpy
    +import matplotlib.pyplot as plt
    +
    +from mpas_analysis.shared import AnalysisTask
    +
    +from mpas_analysis.shared.io import open_mpas_dataset, write_netcdf_with_fill
    +from mpas_analysis.shared.io.utility import build_config_full_path, \
    +    build_obs_path, make_directories, decode_strings
    +from mpas_analysis.shared.climatology import compute_climatology, \
    +    get_unmasked_mpas_climatology_file_name
    +
    +from mpas_analysis.shared.constants import constants
    +from mpas_analysis.shared.plot import histogram_analysis_plot, savefig
    +from mpas_analysis.shared.html import write_image_xml
    +
    +
    +
    +[docs] +class OceanHistogram(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') + if config.has_option(self.taskName, 'weightList'): + self.weightList = config.getexpression(self.taskName, 'weightList') + if not self.weightList: + self.weightList = None + elif len(self.weightList) != len(self.variableList): + raise ValueError('Histogram weightList is not the same ' + 'length as variableList') + else: + self.weightList = None + + baseDirectory = build_config_full_path( + config, 'output', 'histogramSubdirectory') + if not os.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'}} + + for regionGroup in self.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 + for obsName in self.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 + + for regionName in regionNames: + sectionName = None + + # Compute weights for histogram + if self.weightList is not None: + computeWeightsSubtask = ComputeHistogramWeightsSubtask( + self, regionName, mpasMasksSubtask, filePrefix, + self.variableList, self.weightList) + self.add_subtask(computeWeightsSubtask) + + for season in self.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) + if self.weightList is not None: + plotRegionSubtask.run_after(computeWeightsSubtask) + self.add_subtask(plotRegionSubtask)
    + + + def setup_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 = [] + for var in self.variableList: + variableList.append(f'timeMonthly_avg_{var}') + + self.mpasClimatologyTask.add_variables(variableList=variableList, + seasons=self.seasons) + + if len(self.obsList) > 1: + raise ValueError('Histogram analysis does not currently support' + 'more than one observational product')
    + + + +class ComputeHistogramWeightsSubtask(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 + + def run_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) + + if self.weightList is not None: + ds_weights = xarray.Dataset() + # Fetch the weight variables and mask them for each region + for index, var in enumerate(self.variableList): + weight_var_name = self.weightList[index] + if weight_var_name in ds_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) + + +class PlotRegionHistogramSubtask(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 + + def setup_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 = [] + for var in self.variableList: + self.xmlFileNames.append( + f'{self.plotsDirectory}/{self.filePrefix}_{var}_' + f'{self.regionName}_{self.season}.xml') + + def run_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 + + if len(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') + + if self.weightList is not None: + weights_filename = \ + f'{base_directory}/{self.filePrefix}_{self.regionName}_' \ + 'weights.nc' + ds_weights = xarray.open_dataset(weights_filename) + + if self.controlConfig is not None: + 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 + if self.weightList is not None: + control_weights_filename = f'{base_directory}/' \ + f'{self.filePrefix}_{self.regionName}_weights.nc' + ds_control_weights = xarray.open_dataset( + control_weights_filename) + + if config.has_option(self.taskName, 'mainColor'): + mainColor = config.get(self.taskName, 'mainColor') + else: + mainColor = 'C0' + if config.has_option(self.taskName, 'obsColor'): + obsColor = config.get(self.taskName, 'obsColor') + else: + obsColor = 'C1' + if config.has_option(self.taskName, 'controlColor'): + controlColor = config.get(self.taskName, 'controlColor') + else: + controlColor = 'C2' + + if config.has_option(self.taskName, 'lineWidth'): + lineWidth = config.getfloat(self.taskName, 'lineWidth') + else: + lineWidth = None + + if config.has_option(self.taskName, 'titleFontSize'): + titleFontSize = config.getint(self.taskName, + 'titleFontSize') + else: + titleFontSize = None + if config.has_option(self.taskName, 'axisFontSize'): + axisFontSize = config.getint(self.taskName, + 'axisFontSize') + else: + axisFontSize = None + + if config.has_option(self.taskName, 'defaultFontSize'): + defaultFontSize = config.getint(self.taskName, + 'defaultFontSize') + else: + defaultFontSize = None + if config.has_option(self.taskName, 'bins'): + bins = config.getint(self.taskName, 'bins') + else: + bins = None + + yLabel = 'normalized Probability Density Function' + + for index, var in enumerate(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)) + if self.weightList is not None: + if f'{var_name}_weight' in ds_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']})" + + for obs_name in self.obsDicts: + localObsDict = dict(self.obsDicts[obs_name]) + obs_filename = build_obs_path( + config, component=self.componentName, + relativePath=localObsDict['gridFileName']) + if f'{var}Var' not in localObsDict.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) + if self.controlConfig is not None: + 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) + + if lineWidth is not None: + lineWidths = [lineWidth for i in fields] + 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) +
    + +
    +
    +
    + +
    + +
    +

    © Copyright This software is open source software available under the BSD-3license. Copyright (c) 2022 Triad National Security, LLC. All rights reserved. Copyright (c) 2018 Lawrence Livermore National Security, LLC. All rights reserved. Copyright (c) 2018 UT-Battelle, LLC. All rights reserved..

    +
    + + Built with Sphinx using a + theme + provided by Read the Docs. + + +
    +
    +
    +
    +
    + + + + \ 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') - if endYear == '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 - for inIndex in range(dsMOCIn.dims['Time']): + for inIndex in range(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 - for inIndex in range(dsMOCIn.dims['Time']): + for inIndex in range(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_features import FeatureCollection, read_feature_collection from geometric_features.aggregation import get_aggregator_by_name +from mpas_tools.cime.constants import constants as cime_constants from mpas_analysis.shared.analysis_task import AnalysisTask @@ -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.geojsonFileName iceShelvesToPlot = masksSubtask.expand_region_names(iceShelvesToPlot) startYear = config.getint('timeSeries', 'startYear') - endYear = config.get('timeSeries', 'endYear') - if endYear == '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

    self.endYear = endYear self.startDate = f'{self.startYear:04d}-01-01_00:00:00' self.endDate = f'{self.endYear:04d}-12-31_23:59:59' - self.variableList = \ - ['timeMonthly_avg_landIceFreshwaterFlux'] + self.variableList = None def setup_and_check(self): """ @@ -334,6 +329,13 @@

    Source code for mpas_analysis.ocean.time_series_antarctic_melt

    raise IOError('No MPAS-O restart file found: need at least one ' 'restart file for Antarctic melt calculations') + totalFluxVar = 'timeMonthly_avg_landIceFreshwaterFluxTotal' + landIceFluxVar = 'timeMonthly_avg_landIceFreshwaterFlux' + if totalFluxVar in self.mpasTimeSeriesTask.allVariables: + self.variableList = [totalFluxVar] + else: + self.variableList = [landIceFluxVar] + self.mpasTimeSeriesTask.add_variables(variableList=self.variableList) return @@ -413,17 +415,18 @@

    Source code for mpas_analysis.ocean.time_series_antarctic_melt

    regionNames = decode_strings(dsRegionMask.regionNames) + fluxVar = self.variableList[0] + datasets = [] nTime = dsIn.sizes['Time'] for tIndex in range(nTime): self.logger.info(f' {tIndex + 1}/{nTime}') - freshwaterFlux = \ - dsIn.timeMonthly_avg_landIceFreshwaterFlux.isel(Time=tIndex) + freshwaterFlux = dsIn[fluxVar].isel(Time=tIndex) nRegions = dsRegionMask.sizes['nRegions'] meltRates = numpy.zeros((nRegions,)) - totalMeltFluxes = numpy.zeros((nRegions,)) + integratedMeltFluxes = numpy.zeros((nRegions,)) for regionIndex in range(nRegions): self.logger.info(f' {regionNames[regionIndex]}') @@ -431,7 +434,7 @@

    Source code for mpas_analysis.ocean.time_series_antarctic_melt

    dsRegionMask.regionCellMasks.isel(nRegions=regionIndex) # convert from kg/s to kg/yr - totalMeltFlux = constants.sec_per_year * \ + integratedMeltFlux = constants.sec_per_year * \ (cellMask * areaCell * freshwaterFlux).sum(dim='nCells') totalArea = \ @@ -439,23 +442,23 @@

    Source code for mpas_analysis.ocean.time_series_antarctic_melt

    # from kg/m^2/yr to m/yr meltRates[regionIndex] = ((1. / constants.rho_fw) * - (totalMeltFlux / totalArea)) + (integratedMeltFlux / totalArea)) # convert from kg/yr to GT/yr - totalMeltFlux /= constants.kg_per_GT - totalMeltFluxes[regionIndex] = totalMeltFlux + integratedMeltFlux /= constants.kg_per_GT + integratedMeltFluxes[regionIndex] = integratedMeltFlux dsOut = xarray.Dataset() dsOut.coords['Time'] = dsIn.Time.isel(Time=tIndex) - dsOut['totalMeltFlux'] = (('nRegions',), totalMeltFluxes) + dsOut['integratedMeltFlux'] = (('nRegions',), integratedMeltFluxes) dsOut['meltRates'] = (('nRegions',), meltRates) datasets.append(dsOut) dsOut = xarray.concat(objs=datasets, dim='Time') dsOut['regionNames'] = dsRegionMask.regionNames - dsOut.totalMeltFlux.attrs['units'] = 'GT a$^{-1}$' - dsOut.totalMeltFlux.attrs['description'] = \ - 'Total melt flux summed over each ice shelf or region' + dsOut.integratedMeltFlux.attrs['units'] = 'GT a$^{-1}$' + dsOut.integratedMeltFlux.attrs['description'] = \ + 'Integrated melt flux summed over each ice shelf or region' dsOut.meltRates.attrs['units'] = 'm a$^{-1}$' dsOut.meltRates.attrs['description'] = \ 'Melt rate averaged over each ice shelf or region' @@ -640,17 +643,17 @@

    Source code for mpas_analysis.ocean.time_series_antarctic_melt

    fc.add_feature(feature) break - totalMeltFlux, meltRates = self._load_ice_shelf_fluxes(config) + integratedMeltFlux, meltRates = self._load_ice_shelf_fluxes(config) plotControl = self.controlConfig is not None if plotControl: controlRunName = self.controlConfig.get('runs', 'mainRunName') - refTotalMeltFlux, refMeltRates = \ + refintegratedMeltFlux, refMeltRates = \ self._load_ice_shelf_fluxes(self.controlConfig) else: controlRunName = None - refTotalMeltFlux = None + refintegratedMeltFlux = None refMeltRates = None # Load observations from multiple files and put in dictionary based @@ -704,6 +707,36 @@

    Source code for mpas_analysis.ocean.time_series_antarctic_melt

    'meltRate': ds_shelf.meanMeltRate.values, 'meltRateUncertainty': ds_shelf.meltRateUncertainty.values} + rho_fw = cime_constants['SHR_CONST_RHOFW'] + kg_per_gt = constants.kg_per_GT + gt_per_m3 = rho_fw / kg_per_gt + + obsFileName = f'{observationsDirectory}/Paolo/' \ + f'Paolo_2023_melt_rates.20240220.csv' + obsName = 'Paolo et al. (2023)' + obsDict[obsName] = {} + obsFile = csv.reader(open(obsFileName, 'r')) + next(obsFile, None) # skip the header line + for line in obsFile: # some later useful values commented out + shelfName = line[0] + if shelfName != self.iceShelf: + continue + + # km^2 --> m^2 + area = 1e6 * float(line[1]) + meltRate = float(line[2]) + meltRateUncertainty = float(line[3]) + meltFlux = gt_per_m3 * area * meltRate + meltFluxUncertainty = gt_per_m3 * area * meltRateUncertainty + + # build dict of obs. keyed to filename description + # (which will be used for plotting) + obsDict[obsName] = { + 'meltFlux': meltFlux, + 'meltFluxUncertainty': meltFluxUncertainty, + 'meltRate': meltRate, + 'meltRateUncertainty': meltRateUncertainty} + mainRunName = config.get('runs', 'mainRunName') movingAveragePoints = config.getint('timeSeriesAntarcticMelt', 'movingAveragePoints') @@ -745,7 +778,7 @@

    Source code for mpas_analysis.ocean.time_series_antarctic_melt

    xLabel = 'Time (yr)' yLabel = 'Melt Flux (GT/yr)' - timeSeries = totalMeltFlux.isel(nRegions=self.regionIndex) + timeSeries = integratedMeltFlux.isel(nRegions=self.regionIndex) filePrefix = f'melt_flux_{suffix}' outFileName = f'{self.plotsDirectory}/{filePrefix}.png' @@ -755,7 +788,7 @@

    Source code for mpas_analysis.ocean.time_series_antarctic_melt

    lineWidths = [2.5] legendText = [mainRunName] if plotControl: - fields.append(refTotalMeltFlux.isel(nRegions=self.regionIndex)) + fields.append(refintegratedMeltFlux.isel(nRegions=self.regionIndex)) lineColors.append(config.get('timeSeries', 'controlColor')) lineWidths.append(1.2) legendText.append(controlRunName) @@ -808,7 +841,7 @@

    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

    componentSubdirectory='ocean', galleryGroup='Antarctic Melt Time Series', groupLink='antmelttime', - gallery='Total Melt Flux', + gallery='Integrated Melt Flux', thumbnailDescription=title, imageDescription=caption, imageCaption=caption) xLabel = 'Time (yr)' - yLabel = 'Melt Rate (m/yr)' + yLabel = 'Melt Rate (m/yr) freshwater equiv.' timeSeries = meltRates.isel(nRegions=self.regionIndex) @@ -890,8 +923,8 @@

    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

    f'{startYear:04d}-{endYear:04d}.nc' dsOut = xarray.open_dataset(outFileName) - return dsOut.totalMeltFlux, dsOut.meltRates + return dsOut.integratedMeltFlux, dsOut.meltRates
    diff --git a/stable/_modules/mpas_analysis/ocean/time_series_ocean_regions.html b/stable/_modules/mpas_analysis/ocean/time_series_ocean_regions.html index 61b4c036a..ee664748a 100644 --- a/stable/_modules/mpas_analysis/ocean/time_series_ocean_regions.html +++ b/stable/_modules/mpas_analysis/ocean/time_series_ocean_regions.html @@ -173,13 +173,7 @@

    Source code for mpas_analysis.ocean.time_series_ocean_regions

    tags=['timeSeries', 'regions', 'antarctic']) startYear = config.getint('timeSeries', 'startYear') - endYear = config.get('timeSeries', 'endYear') - if endYear == '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

    startDate=startDate, endDate=endDate) as dsOut: - for inIndex in range(dsOut.dims['Time']): + for inIndex in range(dsOut.sizes['Time']): mask = numpy.logical_and( dsOut.year[inIndex].values == years, diff --git a/stable/_modules/mpas_analysis/ocean/time_series_transport.html b/stable/_modules/mpas_analysis/ocean/time_series_transport.html index 146145f6a..e788ddb0c 100644 --- a/stable/_modules/mpas_analysis/ocean/time_series_transport.html +++ b/stable/_modules/mpas_analysis/ocean/time_series_transport.html @@ -170,13 +170,7 @@

    Source code for mpas_analysis.ocean.time_series_transport

    tags=['timeSeries', 'transport']) startYear = config.getint('timeSeries', 'startYear') - endYear = config.get('timeSeries', 'endYear') - if endYear == '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 = [year for year in range(startYear, endYear + 1)] @@ -375,7 +369,7 @@

    Source code for mpas_analysis.ocean.time_series_transport

    startDate=startDate, endDate=endDate) as dsOut: - for inIndex in range(dsOut.dims['Time']): + for inIndex in range(dsOut.sizes['Time']): mask = numpy.logical_and( dsOut.year[inIndex].values == years, diff --git a/stable/_modules/mpas_analysis/sea_ice/time_series.html b/stable/_modules/mpas_analysis/sea_ice/time_series.html index d42c68082..823946ee5 100644 --- a/stable/_modules/mpas_analysis/sea_ice/time_series.html +++ b/stable/_modules/mpas_analysis/sea_ice/time_series.html @@ -691,10 +691,13 @@

    Source code for mpas_analysis.sea_ice.time_series

    indices to process. """ + config = self.config + chunkYears = config.getint('timeSeriesSeaIceAreaVol', 'chunkYears') + outFileNames = {} for hemisphere in ['NH', 'SH']: baseDirectory = build_config_full_path( - self.config, 'output', 'timeSeriesSubdirectory') + config, 'output', 'timeSeriesSubdirectory') make_directories(baseDirectory) @@ -713,6 +716,11 @@

    Source code for mpas_analysis.sea_ice.time_series

    startDate=self.startDate, endDate=self.endDate) + nTime = ds.sizes['Time'] + # chunk into 10-year seguments so we don't run out of memory + if nTime > 12 * chunkYears: + ds = ds.chunk({'Time': 12 * chunkYears}) + for hemisphere in ['NH', 'SH']: if hemisphere == 'NH': diff --git a/stable/_modules/mpas_analysis/shared/analysis_task.html b/stable/_modules/mpas_analysis/shared/analysis_task.html index 0ce6ae746..404cf40eb 100644 --- a/stable/_modules/mpas_analysis/shared/analysis_task.html +++ b/stable/_modules/mpas_analysis/shared/analysis_task.html @@ -716,8 +716,6 @@

    Source code for mpas_analysis.shared.analysis_task

    '{}HistorySubdirectory'.format(componentName), defaultPath=runDirectory) - errorOnMissing = config.getboolean('input', 'errorOnMissing') - namelistFileName = build_config_full_path( config, 'input', '{}NamelistFileName'.format(componentName)) @@ -740,18 +738,10 @@

    Source code for mpas_analysis.shared.analysis_task

    calendar = namelist.get('config_calendar_type') requestedStartYear = config.getint(section, 'startYear') - requestedEndYear = config.get(section, 'endYear') - if requestedEndYear == 'end': - requestedEndYear = None - else: - # get it again as an integer - requestedEndYear = config.getint(section, 'endYear') + requestedEndYear = config.getint(section, 'endYear') startDate = '{:04d}-01-01_00:00:00'.format(requestedStartYear) - if requestedEndYear is None: - endDate = None - else: - endDate = '{:04d}-12-31_23:59:59'.format(requestedEndYear) + endDate = '{:04d}-12-31_23:59:59'.format(requestedEndYear) streamName = 'timeSeriesStatsMonthlyOutput' try: @@ -784,29 +774,13 @@

    Source code for mpas_analysis.shared.analysis_task

    lastIndex -= 1 endYear = years[lastIndex] - if requestedEndYear is None: - config.set(section, 'endYear', str(endYear)) - requestedEndYear = endYear - if startYear != requestedStartYear or endYear != requestedEndYear: - if errorOnMissing: - raise ValueError( - "{} start and/or end year different from requested\n" - "requested: {:04d}-{:04d}\n" - "actual: {:04d}-{:04d}\n".format( - section, requestedStartYear, requestedEndYear, startYear, - endYear)) - else: - print("Warning: {} start and/or end year different from " - "requested\n" - "requested: {:04d}-{:04d}\n" - "actual: {:04d}-{:04d}\n".format(section, - requestedStartYear, - requestedEndYear, - startYear, - endYear)) - config.set(section, 'startYear', str(startYear)) - config.set(section, 'endYear', str(endYear)) + raise ValueError( + "{} start and/or end year different from requested\n" + "requested: {:04d}-{:04d}\n" + "actual: {:04d}-{:04d}\n".format( + section, requestedStartYear, requestedEndYear, startYear, + endYear)) startDate = '{:04d}-01-01_00:00:00'.format(startYear) config.set(section, 'startDate', startDate) diff --git a/stable/_modules/mpas_analysis/shared/climatology/climatology.html b/stable/_modules/mpas_analysis/shared/climatology/climatology.html index 6ce96e79c..932122c8a 100644 --- a/stable/_modules/mpas_analysis/shared/climatology/climatology.html +++ b/stable/_modules/mpas_analysis/shared/climatology/climatology.html @@ -838,7 +838,7 @@

    Source code for mpas_analysis.shared.climatology.climatology

    cacheInfo = [] - cacheIndices = -1 * numpy.ones(ds.dims['Time'], int) + cacheIndices = -1 * numpy.ones(ds.sizes['Time'], int) monthsInDs = ds.month.values yearsInDs = ds.year.values @@ -910,7 +910,7 @@

    Source code for mpas_analysis.shared.climatology.climatology

    totalDays = dsYear.daysInMonth.sum(dim='Time').values - monthCount = dsYear.dims['Time'] + monthCount = dsYear.sizes['Time'] climatology = compute_climatology(dsYear, monthValues, calendar, maskVaries=False) diff --git a/stable/_modules/mpas_analysis/shared/climatology/remap_mpas_climatology_subtask.html b/stable/_modules/mpas_analysis/shared/climatology/remap_mpas_climatology_subtask.html index 1646c8c8f..6bf9a5364 100644 --- a/stable/_modules/mpas_analysis/shared/climatology/remap_mpas_climatology_subtask.html +++ b/stable/_modules/mpas_analysis/shared/climatology/remap_mpas_climatology_subtask.html @@ -113,7 +113,7 @@

    Source code for mpas_analysis.shared.climatology.remap_mpas_climatology_subt import os from mpas_tools.io import write_netcdf -from pyremap import MpasMeshDescriptor +from pyremap import MpasCellMeshDescriptor, MpasVertexMeshDescriptor from mpas_analysis.shared.analysis_task import AnalysisTask @@ -541,9 +541,12 @@

    Source code for mpas_analysis.shared.climatology.remap_mpas_climatology_subt self.comparisonDescriptors[comparisonGridName] self.comparisonGridName = comparisonDescriptor.meshName meshName = config.get('input', 'mpasMeshName') - mpasDescriptor = MpasMeshDescriptor( - self.restartFileName, meshName=meshName, - vertices=self.vertices) + if self.vertices: + mpasDescriptor = MpasVertexMeshDescriptor( + self.restartFileName, meshName=meshName) + else: + mpasDescriptor = MpasCellMeshDescriptor( + self.restartFileName, meshName=meshName) self.mpasMeshName = mpasDescriptor.meshName self.remappers[comparisonGridName] = get_remapper( diff --git a/stable/_modules/mpas_analysis/shared/generalized_reader/generalized_reader.html b/stable/_modules/mpas_analysis/shared/generalized_reader/generalized_reader.html index 0c68942c6..12b96f36f 100644 --- a/stable/_modules/mpas_analysis/shared/generalized_reader/generalized_reader.html +++ b/stable/_modules/mpas_analysis/shared/generalized_reader/generalized_reader.html @@ -258,7 +258,7 @@

    Source code for mpas_analysis.shared.generalized_reader.generalized_reader# select only the data in the specified range of dates ds = ds.sel(Time=slice(startDate, endDate)) - if ds.dims['Time'] == 0: + if ds.sizes['Time'] == 0: raise ValueError('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)) - if ds.dims['Time'] == 0: + if ds.sizes['Time'] == 0: raise ValueError('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 - def add_arrow_to_line_2d(ax, path, arrow_spacing=8e5, arrow_width=1.5e4): + + def add_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))) for n in indices: @@ -715,7 +719,8 @@

    Source code for mpas_analysis.shared.plot.climatology_map

    if arrows is not None: for collection in cs.collections: for path in collection.get_paths(): - add_arrow_to_line_2d(ax, path) + for poly in path.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 @@
  • Ocean tasks
  • +
    +
    + +
    + +
    +

    © Copyright This software is open source software available under the BSD-3license. Copyright (c) 2022 Triad National Security, LLC. All rights reserved. Copyright (c) 2018 Lawrence Livermore National Security, LLC. All rights reserved. Copyright (c) 2018 UT-Battelle, LLC. All rights reserved..

    +
    + + Built with Sphinx using a + theme + provided by Read the Docs. + + +
    +
    +
    + +
    + + + + \ No newline at end of file diff --git a/stable/developers_guide/generated/mpas_analysis.ocean.IndexNino34.html b/stable/developers_guide/generated/mpas_analysis.ocean.IndexNino34.html index 6809a892c..86aaa1bb9 100644 --- a/stable/developers_guide/generated/mpas_analysis.ocean.IndexNino34.html +++ b/stable/developers_guide/generated/mpas_analysis.ocean.IndexNino34.html @@ -61,6 +61,7 @@
  • Analysis tasks

  • diff --git a/stable/developers_guide/generated/mpas_analysis.ocean.OceanHistogram.html b/stable/developers_guide/generated/mpas_analysis.ocean.OceanHistogram.html new file mode 100644 index 000000000..8f7f246b2 --- /dev/null +++ b/stable/developers_guide/generated/mpas_analysis.ocean.OceanHistogram.html @@ -0,0 +1,294 @@ + + + + + + + mpas_analysis.ocean.OceanHistogram — MPAS-Analysis stable documentation + + + + + + + + + + + + + + + + + +
    + + +
    + +
    +
    +
    + +
    +
    +
    +
    + +
    +

    mpas_analysis.ocean.OceanHistogram

    +
    +
    +class mpas_analysis.ocean.OceanHistogram(config, mpasClimatologyTask, regionMasksTask, controlConfig=None)[source]
    +

    Plots a histogram of a 2-d ocean variable.

    +
    +
    +__init__(config, mpasClimatologyTask, regionMasksTask, controlConfig=None)[source]
    +

    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)

    • +
    +
    +
    +
    + +

    Methods

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    __init__(config, mpasClimatologyTask, ...[, ...])

    Construct the analysis task.

    add_subtask(subtask)

    Add a subtask to this tasks.

    check_analysis_enabled(analysisOptionName[, ...])

    Check to make sure a given analysis is turned on, issuing a warning or raising an exception if not.

    check_generate()

    Determines if this analysis should be generated, based on the generate config option and taskName, componentName and tags.

    close()

    Close the Process object.

    is_alive()

    Return whether process is alive

    join([timeout])

    Wait until child process terminates

    kill()

    Terminate process; sends SIGKILL signal or uses TerminateProcess()

    run([writeLogFile])

    Sets up logging and then runs the analysis task.

    run_after(task)

    Only run this task after the given task has completed.

    run_task()

    Run the analysis.

    set_start_end_date(section)

    Set the start and end dates in the config correspond to the start and end years in a given category of analysis

    setup_and_check()

    Perform steps to set up the analysis and check for errors in the setup.

    start()

    Start child process

    terminate()

    Terminate process; sends SIGTERM signal or uses TerminateProcess()

    +

    Attributes

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    BLOCKED

    FAIL

    READY

    RUNNING

    SUCCESS

    UNSET

    authkey

    daemon

    Return whether process is a daemon

    exitcode

    Return exit code of process or None if it has yet to stop

    ident

    Return identifier (PID) of process or None if it has yet to start

    name

    pid

    Return identifier (PID) of process or None if it has yet to start

    sentinel

    Return a file descriptor (Unix) or handle (Windows) suitable for waiting for process termination.

    +
    + +
    + + +
    +
    +
    + +
    + +
    +

    © Copyright This software is open source software available under the BSD-3license. Copyright (c) 2022 Triad National Security, LLC. All rights reserved. Copyright (c) 2018 Lawrence Livermore National Security, LLC. All rights reserved. Copyright (c) 2018 UT-Battelle, LLC. All rights reserved..

    +
    + + Built with Sphinx using a + theme + provided by Read the Docs. + + +
    +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/stable/developers_guide/generated/mpas_analysis.ocean.StreamfunctionMOC.html b/stable/developers_guide/generated/mpas_analysis.ocean.StreamfunctionMOC.html index 75872b33d..d638be009 100644 --- a/stable/developers_guide/generated/mpas_analysis.ocean.StreamfunctionMOC.html +++ b/stable/developers_guide/generated/mpas_analysis.ocean.StreamfunctionMOC.html @@ -20,7 +20,7 @@ - + @@ -61,6 +61,7 @@
  • Analysis tasks
  • diff --git a/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesAntarcticMelt.html b/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesAntarcticMelt.html index 165c3dfd2..88d438fdf 100644 --- a/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesAntarcticMelt.html +++ b/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesAntarcticMelt.html @@ -61,6 +61,7 @@
  • Analysis tasks
    • Base Class
    • Ocean tasks
        +
      • mpas_analysis.ocean.ConservationTask
      • mpas_analysis.ocean.ClimatologyMapSST
      • mpas_analysis.ocean.ClimatologyMapSSS
      • mpas_analysis.ocean.ClimatologyMapMLD
      • @@ -75,6 +76,7 @@
      • mpas_analysis.ocean.ClimatologyMapWaves
      • mpas_analysis.ocean.IndexNino34
      • mpas_analysis.ocean.MeridionalHeatTransport
      • +
      • mpas_analysis.ocean.OceanHistogram
      • mpas_analysis.ocean.StreamfunctionMOC
      • mpas_analysis.ocean.TimeSeriesOHCAnomaly
      • mpas_analysis.ocean.TimeSeriesTemperatureAnomaly
      • diff --git a/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesOHCAnomaly.html b/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesOHCAnomaly.html index 1dbaeb5a1..fe6d057a8 100644 --- a/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesOHCAnomaly.html +++ b/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesOHCAnomaly.html @@ -61,6 +61,7 @@
      • Analysis tasks
        • Base Class
        • Ocean tasks
            +
          • mpas_analysis.ocean.ConservationTask
          • mpas_analysis.ocean.ClimatologyMapSST
          • mpas_analysis.ocean.ClimatologyMapSSS
          • mpas_analysis.ocean.ClimatologyMapMLD
          • @@ -75,6 +76,7 @@
          • mpas_analysis.ocean.ClimatologyMapWaves
          • mpas_analysis.ocean.IndexNino34
          • mpas_analysis.ocean.MeridionalHeatTransport
          • +
          • mpas_analysis.ocean.OceanHistogram
          • mpas_analysis.ocean.StreamfunctionMOC
          • mpas_analysis.ocean.TimeSeriesOHCAnomaly
          • mpas_analysis.ocean.TimeSeriesTemperatureAnomaly
          • diff --git a/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesOceanRegions.html b/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesOceanRegions.html index 88a2921fe..f064bb8cf 100644 --- a/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesOceanRegions.html +++ b/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesOceanRegions.html @@ -61,6 +61,7 @@
          • Analysis tasks
            • Base Class
            • Ocean tasks
                +
              • mpas_analysis.ocean.ConservationTask
              • mpas_analysis.ocean.ClimatologyMapSST
              • mpas_analysis.ocean.ClimatologyMapSSS
              • mpas_analysis.ocean.ClimatologyMapMLD
              • @@ -75,6 +76,7 @@
              • mpas_analysis.ocean.ClimatologyMapWaves
              • mpas_analysis.ocean.IndexNino34
              • mpas_analysis.ocean.MeridionalHeatTransport
              • +
              • mpas_analysis.ocean.OceanHistogram
              • mpas_analysis.ocean.StreamfunctionMOC
              • mpas_analysis.ocean.TimeSeriesOHCAnomaly
              • mpas_analysis.ocean.TimeSeriesTemperatureAnomaly
              • diff --git a/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesSST.html b/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesSST.html index c654df5e4..b662ed651 100644 --- a/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesSST.html +++ b/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesSST.html @@ -61,6 +61,7 @@
              • Analysis tasks
                • Base Class
                • Ocean tasks
                    +
                  • mpas_analysis.ocean.ConservationTask
                  • mpas_analysis.ocean.ClimatologyMapSST
                  • mpas_analysis.ocean.ClimatologyMapSSS
                  • mpas_analysis.ocean.ClimatologyMapMLD
                  • @@ -75,6 +76,7 @@
                  • mpas_analysis.ocean.ClimatologyMapWaves
                  • mpas_analysis.ocean.IndexNino34
                  • mpas_analysis.ocean.MeridionalHeatTransport
                  • +
                  • mpas_analysis.ocean.OceanHistogram
                  • mpas_analysis.ocean.StreamfunctionMOC
                  • mpas_analysis.ocean.TimeSeriesOHCAnomaly
                  • mpas_analysis.ocean.TimeSeriesTemperatureAnomaly
                  • diff --git a/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesSalinityAnomaly.html b/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesSalinityAnomaly.html index 7ab781da2..dbe5550b5 100644 --- a/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesSalinityAnomaly.html +++ b/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesSalinityAnomaly.html @@ -61,6 +61,7 @@
                  • Analysis tasks
                    • Base Class
                    • Ocean tasks
                        +
                      • mpas_analysis.ocean.ConservationTask
                      • mpas_analysis.ocean.ClimatologyMapSST
                      • mpas_analysis.ocean.ClimatologyMapSSS
                      • mpas_analysis.ocean.ClimatologyMapMLD
                      • @@ -75,6 +76,7 @@
                      • mpas_analysis.ocean.ClimatologyMapWaves
                      • mpas_analysis.ocean.IndexNino34
                      • mpas_analysis.ocean.MeridionalHeatTransport
                      • +
                      • mpas_analysis.ocean.OceanHistogram
                      • mpas_analysis.ocean.StreamfunctionMOC
                      • mpas_analysis.ocean.TimeSeriesOHCAnomaly
                      • mpas_analysis.ocean.TimeSeriesTemperatureAnomaly
                      • diff --git a/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesTemperatureAnomaly.html b/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesTemperatureAnomaly.html index e34819a35..39c6a41f0 100644 --- a/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesTemperatureAnomaly.html +++ b/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesTemperatureAnomaly.html @@ -61,6 +61,7 @@
                      • Analysis tasks
                        • Base Class
                        • Ocean tasks
                            +
                          • mpas_analysis.ocean.ConservationTask
                          • mpas_analysis.ocean.ClimatologyMapSST
                          • mpas_analysis.ocean.ClimatologyMapSSS
                          • mpas_analysis.ocean.ClimatologyMapMLD
                          • @@ -75,6 +76,7 @@
                          • mpas_analysis.ocean.ClimatologyMapWaves
                          • mpas_analysis.ocean.IndexNino34
                          • mpas_analysis.ocean.MeridionalHeatTransport
                          • +
                          • mpas_analysis.ocean.OceanHistogram
                          • mpas_analysis.ocean.StreamfunctionMOC
                          • mpas_analysis.ocean.TimeSeriesOHCAnomaly
                          • mpas_analysis.ocean.TimeSeriesTemperatureAnomaly
                          • diff --git a/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesTransport.html b/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesTransport.html index eb1bab206..ac61fb9ca 100644 --- a/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesTransport.html +++ b/stable/developers_guide/generated/mpas_analysis.ocean.TimeSeriesTransport.html @@ -61,6 +61,7 @@
                          • Analysis tasks
                            • Base Class
                            • Ocean tasks
                                +
                              • mpas_analysis.ocean.ConservationTask
                              • mpas_analysis.ocean.ClimatologyMapSST
                              • mpas_analysis.ocean.ClimatologyMapSSS
                              • mpas_analysis.ocean.ClimatologyMapMLD
                              • @@ -75,6 +76,7 @@
                              • mpas_analysis.ocean.ClimatologyMapWaves
                              • mpas_analysis.ocean.IndexNino34
                              • mpas_analysis.ocean.MeridionalHeatTransport
                              • +
                              • mpas_analysis.ocean.OceanHistogram
                              • mpas_analysis.ocean.StreamfunctionMOC
                              • mpas_analysis.ocean.TimeSeriesOHCAnomaly
                              • mpas_analysis.ocean.TimeSeriesTemperatureAnomaly
                              • diff --git a/stable/developers_guide/generated/mpas_analysis.ocean.compute_anomaly_subtask.ComputeAnomalySubtask.html b/stable/developers_guide/generated/mpas_analysis.ocean.compute_anomaly_subtask.ComputeAnomalySubtask.html index 592c783d6..0ae6e7b0c 100644 --- a/stable/developers_guide/generated/mpas_analysis.ocean.compute_anomaly_subtask.ComputeAnomalySubtask.html +++ b/stable/developers_guide/generated/mpas_analysis.ocean.compute_anomaly_subtask.ComputeAnomalySubtask.html @@ -61,6 +61,7 @@
                              • Analysis tasks
                                • Base Class
                                • Ocean tasks
                                    +
                                  • mpas_analysis.ocean.ConservationTask
                                  • mpas_analysis.ocean.ClimatologyMapSST
                                  • mpas_analysis.ocean.ClimatologyMapSSS
                                  • mpas_analysis.ocean.ClimatologyMapMLD
                                  • @@ -75,6 +76,7 @@
                                  • mpas_analysis.ocean.ClimatologyMapWaves
                                  • mpas_analysis.ocean.IndexNino34
                                  • mpas_analysis.ocean.MeridionalHeatTransport
                                  • +
                                  • mpas_analysis.ocean.OceanHistogram
                                  • mpas_analysis.ocean.StreamfunctionMOC
                                  • mpas_analysis.ocean.TimeSeriesOHCAnomaly
                                  • mpas_analysis.ocean.TimeSeriesTemperatureAnomaly
                                  • diff --git a/stable/developers_guide/generated/mpas_analysis.ocean.plot_depth_integrated_time_series_subtask.PlotDepthIntegratedTimeSeriesSubtask.html b/stable/developers_guide/generated/mpas_analysis.ocean.plot_depth_integrated_time_series_subtask.PlotDepthIntegratedTimeSeriesSubtask.html index 67aa79442..64555562f 100644 --- a/stable/developers_guide/generated/mpas_analysis.ocean.plot_depth_integrated_time_series_subtask.PlotDepthIntegratedTimeSeriesSubtask.html +++ b/stable/developers_guide/generated/mpas_analysis.ocean.plot_depth_integrated_time_series_subtask.PlotDepthIntegratedTimeSeriesSubtask.html @@ -61,6 +61,7 @@
                                  • Analysis tasks
                                    • Base Class
                                    • Ocean tasks
                                        +
                                      • mpas_analysis.ocean.ConservationTask
                                      • mpas_analysis.ocean.ClimatologyMapSST
                                      • mpas_analysis.ocean.ClimatologyMapSSS
                                      • mpas_analysis.ocean.ClimatologyMapMLD
                                      • @@ -75,6 +76,7 @@
                                      • mpas_analysis.ocean.ClimatologyMapWaves
                                      • mpas_analysis.ocean.IndexNino34
                                      • mpas_analysis.ocean.MeridionalHeatTransport
                                      • +
                                      • mpas_analysis.ocean.OceanHistogram
                                      • mpas_analysis.ocean.StreamfunctionMOC
                                      • mpas_analysis.ocean.TimeSeriesOHCAnomaly
                                      • mpas_analysis.ocean.TimeSeriesTemperatureAnomaly
                                      • diff --git a/stable/developers_guide/generated/mpas_analysis.ocean.plot_hovmoller_subtask.PlotHovmollerSubtask.html b/stable/developers_guide/generated/mpas_analysis.ocean.plot_hovmoller_subtask.PlotHovmollerSubtask.html index f359944e7..7dd32f223 100644 --- a/stable/developers_guide/generated/mpas_analysis.ocean.plot_hovmoller_subtask.PlotHovmollerSubtask.html +++ b/stable/developers_guide/generated/mpas_analysis.ocean.plot_hovmoller_subtask.PlotHovmollerSubtask.html @@ -61,6 +61,7 @@
                                      • Analysis tasks
                                        • Base Class
                                        • Ocean tasks
                                            +
                                          • mpas_analysis.ocean.ConservationTask
                                          • mpas_analysis.ocean.ClimatologyMapSST
                                          • mpas_analysis.ocean.ClimatologyMapSSS
                                          • mpas_analysis.ocean.ClimatologyMapMLD
                                          • @@ -75,6 +76,7 @@
                                          • mpas_analysis.ocean.ClimatologyMapWaves
                                          • mpas_analysis.ocean.IndexNino34
                                          • mpas_analysis.ocean.MeridionalHeatTransport
                                          • +
                                          • mpas_analysis.ocean.OceanHistogram
                                          • mpas_analysis.ocean.StreamfunctionMOC
                                          • mpas_analysis.ocean.TimeSeriesOHCAnomaly
                                          • mpas_analysis.ocean.TimeSeriesTemperatureAnomaly
                                          • diff --git a/stable/developers_guide/generated/mpas_analysis.shared.AnalysisTask.set_start_end_date.html b/stable/developers_guide/generated/mpas_analysis.shared.AnalysisTask.set_start_end_date.html index 187065bef..90c038638 100644 --- a/stable/developers_guide/generated/mpas_analysis.shared.AnalysisTask.set_start_end_date.html +++ b/stable/developers_guide/generated/mpas_analysis.shared.AnalysisTask.set_start_end_date.html @@ -19,7 +19,7 @@ - + @@ -145,7 +145,7 @@

                                            mpas_analysis.shared.AnalysisTask.set_start_end_date