Source code for mpas_analysis.ocean.time_series_ohc_anomaly

# -*- 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 xarray as xr
import numpy
import matplotlib.pyplot as plt

from mpas_tools.cime.constants import constants as cime_constants

from mpas_analysis.shared import AnalysisTask

from mpas_analysis.ocean.compute_anomaly_subtask import ComputeAnomalySubtask
from mpas_analysis.ocean.plot_hovmoller_subtask import PlotHovmollerSubtask
from mpas_analysis.ocean.plot_depth_integrated_time_series_subtask import \
    PlotDepthIntegratedTimeSeriesSubtask

from mpas_analysis.shared.constants import constants as mpas_constants


[docs] class TimeSeriesOHCAnomaly(AnalysisTask): """ Performs analysis of ocean heat content (OHC) from time-series output. """ # Authors # ------- # Xylar Asay-Davis, Milena Veneziani, Greg Streletz
[docs] def __init__(self, config, mpasTimeSeriesTask, controlConfig=None): """ Construct the analysis task. Parameters ---------- config : mpas_tools.config.MpasConfigParser Configuration options mpasTimeSeriesTask : ``MpasTimeSeriesTask`` The task that extracts the time series from MPAS monthly output controlconfig : mpas_tools.config.MpasConfigParser, optional Configuration options for a control run (if any) """ # Authors # ------- # Xylar Asay-Davis # first, call the constructor from the base class (AnalysisTask) super(TimeSeriesOHCAnomaly, self).__init__( config=config, taskName='timeSeriesOHCAnomaly', componentName='ocean', tags=['timeSeries', 'ohc', 'publicObs', 'anomaly']) sectionName = 'timeSeriesOHCAnomaly' regionNames = config.getexpression(sectionName, 'regions') movingAveragePoints = config.getint(sectionName, 'movingAveragePoints') self.variableDict = {} for suffix in ['avgLayerTemperature', 'sumLayerMaskValue', 'avgLayerArea', 'avgLayerThickness']: key = 'timeMonthly_avg_avgValueWithinOceanLayerRegion_' + suffix self.variableDict[key] = suffix mpasFieldName = 'ohc' timeSeriesFileName = 'regionAveragedOHCAnomaly.nc' anomalyTask = ComputeAnomalySubtask( parentTask=self, mpasTimeSeriesTask=mpasTimeSeriesTask, outFileName=timeSeriesFileName, variableList=list(self.variableDict.keys()), movingAveragePoints=movingAveragePoints, alter_dataset=self._compute_ohc) self.add_subtask(anomalyTask) for regionName in regionNames: caption = 'Trend of {} OHC Anomaly vs depth'.format( regionName) plotTask = PlotHovmollerSubtask( parentTask=self, regionName=regionName, inFileName=timeSeriesFileName, outFileLabel='ohcAnomalyZ', fieldNameInTitle='OHC Anomaly', mpasFieldName=mpasFieldName, unitsLabel=r'[$\times 10^{22}$ J]', sectionName='hovmollerOHCAnomaly', thumbnailSuffix=u'ΔOHC', imageCaption=caption, galleryGroup='Trends vs Depth', groupSubtitle=None, groupLink='trendsvsdepth', galleryName=None) plotTask.run_after(anomalyTask) self.add_subtask(plotTask) caption = 'Running Mean of the Anomaly in {} Ocean Heat ' \ 'Content'.format(regionName) plotTask = PlotOHCAnomaly( parentTask=self, regionName=regionName, inFileName=timeSeriesFileName, outFileLabel='ohcAnomaly', fieldNameInTitle='OHC Anomaly', mpasFieldName=mpasFieldName, yAxisLabel=r'$\Delta$OHC [$\times 10^{22}$ J]', sectionName='timeSeriesOHCAnomaly', thumbnailSuffix=u'ΔOHC', imageCaption=caption, galleryGroup='Time Series', groupSubtitle=None, groupLink='timeseries', galleryName=None, controlConfig=controlConfig) plotTask.run_after(anomalyTask) self.add_subtask(plotTask)
def _compute_ohc(self, ds): """ Compute the OHC time series. """ # regionNames = self.config.getexpression('regions', 'regions') # ds['regionNames'] = ('nOceanRegionsTmp', regionNames) # for convenience, rename the variables to simpler, shorter names ds = ds.rename(self.variableDict) # specific heat [J/(kg*degC)] cp = self.namelist.getfloat('config_specific_heat_sea_water') # [kg/m3] rho = self.namelist.getfloat('config_density0') unitsScalefactor = 1e-22 ds['ohc'] = unitsScalefactor * rho * cp * ds['sumLayerMaskValue'] * \ ds['avgLayerArea'] * ds['avgLayerThickness'] * \ ds['avgLayerTemperature'] ds.ohc.attrs['units'] = '$10^{22}$ J' ds.ohc.attrs['description'] = 'Ocean heat content in each region' # Note: restart file, not a mesh file because we need refBottomDepth, # not in a mesh file try: restartFile = self.runStreams.readpath('restart')[0] except ValueError: raise IOError('No MPAS-O restart file found: need at least one ' 'restart file for OHC calculation') # Define/read in general variables with xr.open_dataset(restartFile) as dsRestart: # reference depth [m] # add depths as a coordinate to the data set ds.coords['depth'] = (('nVertLevels',), dsRestart.refBottomDepth.values) return ds
class PlotOHCAnomaly(PlotDepthIntegratedTimeSeriesSubtask): def customize_fig(self, fig): """ A function to override to customize the figure. fig : matplotlib.pyplot.Figure The figure """ def joules_to_watts_m2(joules): watts_m2 = joules/factor return watts_m2 def watts_m2_to_joules(watts_m2): joules = factor*watts_m2 return joules # add an axis on the right-hand side color = 'tab:blue' ax = plt.gca() xlim = ax.get_xlim() earth_surface_area = (4. * numpy.pi * cime_constants['SHR_CONST_REARTH']**2) max_time = xlim[-1]*mpas_constants.sec_per_day factor = earth_surface_area*max_time/10**22 secaxy = ax.secondary_yaxis( 'right', functions=(joules_to_watts_m2, watts_m2_to_joules)) secaxy.set_ylabel(r'W/m$^2$', color=color) secaxy.tick_params(axis='y', colors=color) ax.spines['right'].set_color(color) plt.draw() yticks = secaxy.get_yticks() for ytick in yticks: plt.plot(xlim, [0, watts_m2_to_joules(ytick)], color=color, linewidth=0.5)