How to Add Diagnostic Sets to E3SM Diagnostics
Introduction
When planning to expand the current diagnostic sets, efforts have
been made to structure the e3sm_diags
code base in a more modularized
and expandable fashion. The developers will be able to add new
diagnostic sets easily and cleanly. This guide documents the essential
steps for guiding users to add custom diagnostics by providing
an example. If a user has a streamlined Python script for a
complete analysis, from reading in files, data manipulation,
computation, and visualization, it should be straightforward
to take the below steps to add the analysis to e3sm_diags
.
In this document, we will explain by example the process
of adding a diagnostics set into e3sm_diags
.
Some of the current diagnostics sets in e3sm_diags
are
the different kinds of plots seen here.
If you have any questions or issues regarding this,
please make a Github issue on the e3sm_diags repo.
First off, to develop, you must have an e3sm_diags development environment installed.
Below are the components needed to add new diags:
1. Parameters: A set of variables that a user uses to define things pertaining to a diagnostics run. These can be anything, from the path of the reference/test data to parameters related to the plots created. As a reference, look here for existing parameters and their description.
2. Driver: The main code which takes a set of parameters and does the diagnostics, including reading in data files, manipulating data sets, computing metrics, and getting the data ready to be carried over to the plotting function.
3. Adding Default Diagnostics: Though users can choose what variables they want to run diags on, we need to provide default variables. Adding in derived variables is also covered in this guide.
4. Plot: A script which takes the output from the driver and creates the plots.
5. Viewer: For a given plotset, create the htmls that host the output of the plotting.
The Example Diagnostics Set
Say we have a diagnostics set that will simply take the difference between
some reference data and some test data (ex. regular lat-lon grid data in
climatology annual mean). Let’s call it diff_diags
.
From this point on, ‘diff_diags’ is the name of this diagnostics set.
Adding In The Parameters
All of diagnostics sets in e3sm_diags
share a set of core parameters,
which can be changed during runtime. The default values for these
parameters are defined in the
e3sm_diags/parameter/core_parameter.py
folder.
On the documentation website, there is more information explaining what each one does.
First, open e3sm_diags/parameter/core_parameter.py
and edit self.sets
to include 'diff_diags'
.
self.sets = ['zonal_mean_xy', 'zonal_mean_2d', 'meridional_mean_2d', 'lat_lon', 'polar', 'area_mean_time_series', 'cosp_histogram', 'diff_diags',]
This ensures that when the user wants to run all of the plotsets, the new one you made is included.
Now to make things more interesting, we want to add parameters
that are just specific to this plotset. Say we have parameters
called projection
and central_lon
.
projection
changes the plot the that specific projection. The only values we support for now are'mercator'
,'platecarree'
, or'miller'
. The default value is'platecarree'
. For more information regarding these projections, see here.central_lon
changes the central point of the plot. It’s180
be default.
In the e3sm_diags/parameter
directory, create a file called diff_diags_parameter.py
.
Below is the code for it.
When you create your own plotset, please change the name of the class.
In this case, the name of the class in DiffDiagsParameter
.
from .core_parameter import CoreParameter class DiffDiagsParameter(CoreParameter): def __init__(self): super().__init__() self.projection = 'platecarree' self.central_lon = 180 def check_values(self): # Make sure that the core parameters are also valid. super().check_values() # Check that the user inputted values are valid. valid_projections = [ 'mercator', 'platecarree', 'miller' ] if self.projection not in valid_projections: msg = "Your projection ({}) isn't a valid value in: {}" raise RuntimeError(msg.format(self.projection, valid_projections)) if not (0 <= self.central_lon <= 360): raise RuntimeError('central_lon must be between 0 and 360 inclusive.')
Note that we have a definition for the check_values()
function.
This function makes sure that the user has inputted correct values for the parameters.
This function is optional and isn’t required.
Also note that this Parameter class inherits from the CoreParameter
,
which contains the core parameters that all plotsets use. This means that
this plotset will use all of the core parameters, in addition to the
set-specific ones define in the DiffDiagsParameter
.
Letting The Parameter Class Be Used
Open e3sm_diags/parameter/__init__.py
and edit it like so.
Please read the comments.
from .core_parameter import CoreParameter from .zonal_mean_2d_parameter import ZonalMean2dParameter # First, import the new Parameter class. from .diff_diags_parameter import DiffDiagsParameter SET_TO_PARAMETERS = { 'zonal_mean_xy': CoreParameter, 'zonal_mean_2d': ZonalMean2dParameter, 'meridional_mean_2d': CoreParameter, 'lat_lon': CoreParameter, 'polar': CoreParameter, 'cosp_histogram': CoreParameter, 'area_mean_time_series': CoreParameter, # For the diff_diags plotset, we want to use the # below Parameter class. 'diff_diags': DiffDiagsParameter, }
Adding In The Parser
Notice that in the e3sm_diags/parameter folder, we have the following:
core_parameter.py
, whereCoreParameter
is located.zonal_mean_2d_parameter.py
, whereZonalMean2dParameter
is located.This container parameters specific to the
'zonal_mean_2d'
plotset.Again, since
ZonalMean2dParameter
inherits fromCoreParameter
, it’ll use all of the core parameters, in addition to it’s set-specific ones.
Every Parameter object needs a corresponding Parser object.
The Parser is the command line parser which can take in the parameters
as command line arguments. The users have the option to run e3sm_diags
via the command line. For example, this is done for the provenance, which
is the command shown in the bottom for each webpage with a plot.
One can reproduce or fine tune a single diagnostic by using the provenance command line.
In the e3sm_diags/parser
directory, create a file called diff_diags_parser.py
.
Below is the code for it. Some points:
Like how the
DiffDiagsParameter
inherits fromCoreParameter
, our parserDiffDiagsParser
inherits fromCoreParser
.Remember to change the name of your class accordingly based on your plotset name.
Please read the comments as well and implement the changes.
from .core_parser import CoreParser # We need to import the corresponding Parameter object. from e3sm_diags.parameter.diff_diags_parameter import DiffDiagsParameter class DiffDiagsParser(CoreParser): def __init__(self, *args, **kwargs): if 'parameter_cls' in kwargs: super().__init__(*args, **kwargs) else: # We want this Parser to create objects of type DiffDiagsParameter. super().__init__(parameter_cls=DiffDiagsParameter, *args, **kwargs) def load_default_args(self, files=[]): # This has '-p' and '--parameter' reserved. super().load_default_args(files) # The parameters unique to DiffDiagsParameter are added here. # For more information about adding arguments to the parser, # please search how to add arguments to Python's argument parser. # The way is exactly the same. self.add_argument( '--projection', type=str, dest='projection', help='The type of the projection ' + 'used when plotting.', required=False) self.add_argument( '--central_lon', type=float, dest='central_lon', help='The central longitude of the plot.', required=False)
Letting The Parser Class Be Used
Open e3sm_diags/parser/__init__.py
and edit it like so.
Please read the comments.
from .core_parser import CoreParser from .zonal_mean_2d_parser import ZonalMean2dParser # First, import the new Parser class. from .diff_diags_parser import DiffDiagsParser SET_TO_PARSER = { 'zonal_mean_xy': CoreParser, 'zonal_mean_2d': ZonalMean2dParser, 'meridional_mean_2d': CoreParser, 'lat_lon': CoreParser, 'polar': CoreParser, 'cosp_histogram': CoreParser, 'area_mean_time_series': CoreParser, # For the diff_diags plotset, we want to use the # below Parser class. 'diff_diags': DiffDiagsParser, }
Adding In The Driver
The driver is the main code which takes in a single Parameter object and does the diagnostics. For each plotset, its corresponding driver is located in the e3sm_diags/driver folder. Please refer to these existing drivers, and if you need help creating your driver, create a Github issue.
This part of the code varies greatly based on the analysis. There’s no set way to do this.
However, to get a variable based on the user’s parameters (reference_data_path
,
test_data_path
, and more) and the way e3sm_diags
input data is structured
(how the obs are named, the file naming conventions of the model files, etc.)
using the Dataset
class is highly recommended.
The
diff_diags_driver.py
below uses it.It’s located in e3sm_diags/driver/utils/dataset.py.
With only two lines of code, here’s how you get the variable PRECT from the test data with ANN climatology ran on it.
test_data = utils.dataset.Dataset(parameter, test=True) prect_climo = test_data.get_climo_variable('PRECT', 'ANN')
You can also get time-series data as well:
test_data = utils.dataset.Dataset(parameter, test=True) prect_time_series = test_data.get_timeseries_variable('PRECT')
In e3sm_diags/driver
, create a file called diff_diags_driver.py
.
Each Driver must have a run_diags()
function which takes in a single Parameters object.
It also must return that Parameters object as well at the end of all of the for-loops.
from e3sm_diags.driver import utils from e3sm_diags.metrics import min_cdms, max_cdms, mean # The below will be defined in a future section. from e3sm_diags.plot.cartopy import diff_diags_plot def run_diag(parameter): variables = parameter.variables seasons = parameter.seasons test_data = utils.dataset.Dataset(parameter, test=True) ref_data = utils.dataset.Dataset(parameter, ref=True) for season in seasons: for var in variables: test_var = test_data.get_climo_variable(var, season) ref_var = ref_data.get_climo_variable(var, season) # Only needed because our viewer (the final step) # displays this data. parameter.viewer_descr[var] = getattr(test_var, 'long_name', var) # Regrid towards the lower resolution of the two # variables for calculating the difference. # The regrid_tool and regrid_method have default values. test_var_reg, ref_var_reg = utils.general.regrid_to_lower_res( test_var, ref_var, parameter.regrid_tool, parameter.regrid_method) diff = test_var_reg - ref_var_reg # We want to compute some metrics to plot as well. metrics = { 'min': float(min_cdms(diff)), 'max': float(max_cdms(diff)), 'mean': float(mean(diff)) } # This part will be defined in a forthcoming section. diff_diags_plot.plot(diff, var, season, metrics, parameter) # Don't forget this. return parameter
Adding In The Config File For The Default Diagnostics
When the user selects a certain number of plotsets to run, their
parameters are combined with default parameters for that plot set.
These are defined in
e3sm_diags/driver/default_diags/.
For the each plotset, we have two default files, one for model_vs_model
runs and one for model_vs_obs
runs.
The type of file used is determined by the run_type
parameter in CoreParameter
, which is 'model_vs_obs'
by default.
Create a file diff_diags_model_vs_obs.cfg
in the directory with the below contents.
[#] sets = ["diff_diags"] case_id = "GPCP_v2.2" variables = ["NEW_PRECT"] ref_name = "GPCP_v2.2" seasons = ["ANN", "DJF", "MAM", "JJA", "SON"] diff_levels = [-5, -4, -3, -2, -1, -0.5, 0.5, 1, 2, 3, 4, 5] [#] sets = ["diff_diags"] case_id = "SST_CL_HadISST" variables = ["SST"] ref_name = "HadISST_CL" seasons = ["ANN", "DJF", "MAM", "JJA", "SON"] diff_levels = [-5, -4, -3, -2, -1, -0.5, -0.2, 0.2, 0.5, 1, 2, 3, 4, 5] [#] sets = ["diff_diags"] case_id = "CERES-EBAF-TOA-v2.8" variables = ["SOLIN"] ref_name = "ceres_ebaf_toa_v2.8" seasons = ["ANN", "DJF", "MAM", "JJA", "SON"] diff_levels = [-5, -4, -3, -2, -1, -0.5, 0.5, 1, 2, 3, 4, 5]
You must make sure the sets
parameter in each section (a section starts with [#]
) is ["diff_diags"]
.
In the above file, we have three sections. The result of this are three DiffDiagsParameter
objects.
The user-inputted parameters will be added to each of these three objects.
Once combined with the user’s input, each of the Parameter objects has the valid parameters to run the software.
Each is passed in to the run_diags()
function in diff_diags_driver.py
automatically.
Adding In Derived Variables
This part only needs to be done if new variables are added for you new plotset.
A set of variables are defined in e3sm_diags in
e3sm_diags/derivations/acme.py.
Notice that in the above file, we had a variable NEW_PRECT
.
It’s a new and derived variable, composed of two or more variables.
In this case, NEW_PRECT
is composed of PRECT
and PRECL
.
Read more about derived variables here.
Open e3sm_diags/derivations/acme.py
and add the function below. It handles what to do when we have NEW_PRECT
as a variable.
def new_prect(precc, precl): """ Total precipitation flux = convective + large-scale. """ var = precc + precl var = convert_units(var, "mm/day") var.long_name = "Total precipitation rate (convective + large-scale)" return var
We need to make sure that this function is actually called.
In the derived_variables
dictionary in e3sm_diags/derivations/acme.py
, add the following entry.
It’s basically the same as PRECT
, but with the new_prect()
function being called.
derived_variables = { 'NEW_PRECT': OrderedDict([ # This variable is 'PRECT' in newer versions of the obs data. # So we just get the variable and don't do anything. (('PRECT',), lambda prect: prect), # This variable is 'pr' in older versions of the obs data. (('pr',), lambda pr: qflxconvert_units(rename(pr))), # In the model data, it's composed of PRECC and PRECL. (('PRECC', 'PRECL'), lambda precc, precl: new_prect(precc, precl)) ]), # Below is the old stuff. Don't insert the below. 'PRECT': OrderedDict([ (('pr',), lambda pr: qflxconvert_units(rename(pr))), (('PRECC', 'PRECL'), lambda precc, precl: prect(precc, precl)) ]),
Installing This File
Open setup.py
in the root of this directory and add the following, somewhere before data_files
is initialized.
diff_diags_files = get_all_files_in_dir('e3sm_diags/driver/default_diags', 'diff_diags*')
Now modify data_files
like below.
(os.path.join(INSTALL_PATH, 'area_mean_time_series'), area_mean_time_series ), # We added in the below. (os.path.join(INSTALL_PATH, 'diff_diags'), diff_diags_files ), # The above was added in. (INSTALL_PATH, ['e3sm_diags/driver/acme_ne30_ocean_land_mask.nc', 'misc/e3sm_logo.png' ])
Every time you make a change to diff_diags_model_vs_obs.cfg
and do a run, make sure you run
pip install .
as explained in the “Putting It All Together And Running On Cori At NERSC” section.
This is because when running the software, these files are obtained from where they’re installed.
Adding In The Plotting
Data created in the driver needs to be carried over and needs to be plotted
using the plotting script. In our case, we only want to plot the diff
data.
Other plotsets, like lat_lon
, plot more data.
In e3sm_diags/plot/cartopy/
, create a file called diff_diags_plot.py
.
The plot()
function is what the driver, diff_diags_driver.py
will call.
Again, the code for this varies greatly based on the actual plot set.
In this script, we’re using the projection
and central_lon
parameters.
import os import numpy as np import numpy.ma as ma import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import matplotlib.pyplot as plt import matplotlib.colors as colors import cartopy.crs as ccrs from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter from e3sm_diags.plot import get_colormap from e3sm_diags.driver.utils.general import get_output_dir def add_cyclic(var): lon = var.getLongitude() return var(longitude=(lon[0], lon[0] + 360.0, 'coe')) def get_ax_size(fig, ax): bbox = ax.get_window_extent().transformed(fig.dpi_scale_trans.inverted()) width, height = bbox.width, bbox.height width *= fig.dpi height *= fig.dpi return width, height def plot(diff, var, season, metrics, parameter): # Create figure, projection fig = plt.figure(figsize=parameter.figsize, dpi=parameter.dpi) if parameter.projection == 'mercator': proj_cls = ccrs.Mercator elif parameter.projection == 'platecarree': proj_cls = ccrs.PlateCarree elif parameter.projection == 'miller': proj_cls = ccrs.Miller central_lon = parameter.central_lon proj = proj_cls(central_longitude=central_lon) diff = add_cyclic(diff) lon = diff.getLongitude() lat = diff.getLatitude() diff = ma.squeeze(diff.asma()) # Contour levels clevels = parameter.diff_levels levels = None norm = None if len(clevels) > 0: levels = [-1.0e8] + clevels + [1.0e8] norm = colors.BoundaryNorm(boundaries=levels, ncolors=256) panel = (0.1691, 0.6810, 0.6465, 0.2258) # Contour plot ax = fig.add_axes(panel, projection=proj) # ax = fig.add_axes(panel[n], projection=proj) ax.set_global() cmap = get_colormap(parameter.diff_colormap, parameter) p1 = ax.contourf(lon, lat, diff, transform=proj_cls(), norm=norm, levels=levels, cmap=cmap, extend='both', ) ax.set_aspect('auto') ax.coastlines(lw=0.3) if parameter.diff_title: ax.set_title(parameter.diff_title, fontdict={'fontsize': 11.5}) ax.set_xticks([0, 60, 120, 180, 240, 300, 359.99], crs=proj_cls()) ax.set_yticks([-90, -60, -30, 0, 30, 60, 90], crs=proj_cls()) lon_formatter = LongitudeFormatter( zero_direction_label=True, number_format='.0f') lat_formatter = LatitudeFormatter() ax.xaxis.set_major_formatter(lon_formatter) ax.yaxis.set_major_formatter(lat_formatter) ax.tick_params(labelsize=8.0, direction='out', width=1) ax.xaxis.set_ticks_position('bottom') ax.yaxis.set_ticks_position('left') # Color bar cbax = fig.add_axes( (panel[0] + 0.6635, panel[1] + 0.0215, 0.0326, 0.1792)) cbar = fig.colorbar(p1, cax=cbax) w, h = get_ax_size(fig, cbax) if levels is None: cbar.ax.tick_params(labelsize=9.0, length=0) else: maxval = np.amax(np.absolute(levels[1:-1])) if maxval < 10.0: fmt = "%5.2f" pad = 25 elif maxval < 100.0: fmt = "%5.1f" pad = 25 else: fmt = "%6.1f" pad = 30 cbar.set_ticks(levels[1:-1]) labels = [fmt % l for l in levels[1:-1]] cbar.ax.set_yticklabels(labels, ha='right') cbar.ax.tick_params(labelsize=9.0, pad=pad, length=0) # Min, Mean, Max plotSideTitle = {'fontsize': 9.5} fig.text(panel[0] + 0.6635, panel[1] + 0.2107, "Max\nMean\nMin", ha='left', fontdict=plotSideTitle) stats = metrics['min'], metrics['max'], metrics['mean'] fig.text(panel[0] + 0.7635, panel[1] + 0.2107, "%.2f\n%.2f\n%.2f" % stats[0:3], ha='right', fontdict=plotSideTitle) # Figure title if not parameter.main_title: fig.suptitle('{} {}'.format(var, season), x=0.5, y=0.96, fontsize=18) else: fig.suptitle(parameter.main_title, x=0.5, y=0.96, fontsize=18) # Save figure # Get the filename that the user has passed in and display that. file_name = '{}_{}.png'.format(var, season) path = os.path.join(get_output_dir('diff_diags', parameter), file_name) plt.savefig(path) print('Plot saved in: ' + path) plt.close()
Adding The Viewer
Each plotset needs to have webpages generated for it that allow users to look at the resultant figures.
In e3sm_diags
, each of the plotset is mapped to a function that takes in all of the Parameter objects
for that plotset, then creates the webpages and returns a (display_name, url)
tuple of strings.
First in e3sm_diags/viewer/
create a file diff_diags_viewer.py
paste in the below code.
import os from .utils import add_header, h1_to_h3 from .default_viewer import create_metadata from cdp.cdp_viewer import OutputViewer def create_viewer(root_dir, parameters): """ Given a set of parameters for a the diff_diags set, create a single webpage. Return the title and url for this page. """ viewer = OutputViewer(path=root_dir) # The name that's displayed on the viewer. display_name = 'Diff Diagnostics' set_name = 'diff_diags' # The title of the colums on the webpage. cols = ['Description', 'Plot'] viewer.add_page(display_name, short_name=set_name, columns=cols) viewer.add_group('Variable') for param in parameters: for var in param.variables: for season in param.seasons: viewer.add_row('{} {}'.format(var, season)) # Adding the description for this var to the current row. # This was obtained and stored in the driver for this plotset. viewer.add_col(param.viewer_descr[var]) file_name = '{}_{}.png'.format(var, season) # We need to make sure we have relative paths, and not absolute ones. # This is why we don't use get_output_dir() as in the plotting script # to get the file name. file_name = os.path.join('..', set_name, param.case_id, file_name) viewer.add_col(file_name, is_file=True, title='Plot', meta=create_metadata(param)) url = viewer.generate_page() add_header(root_dir, os.path.join(root_dir, url), parameters) h1_to_h3(os.path.join(root_dir, url)) return display_name, url
Now make sure that this create_viewer()
function is actually called.
Open e3sm_diags/viewer/main.py
and edit SET_TO_VIEWER
.
# Import the newly created module. from . import diff_diags_viewer SET_TO_VIEWER = { 'lat_lon': default_viewer.create_viewer, 'polar': default_viewer.create_viewer, 'zonal_mean_xy': default_viewer.create_viewer, 'zonal_mean_2d': zonal_mean_2d_viewer.create_viewer, 'meridional_mean_2d': default_viewer.create_viewer, 'cosp_histogram': default_viewer.create_viewer, 'area_mean_time_series': area_mean_time_series_viewer.create_viewer, # Add the below: 'diff_diags': diff_diags_viewer.create_viewer, }
We use the CDP Viewer to create the webpages.
This is not needed! Use whatever you want to create the webpages.
Just make sure that your function that’s mapped to the plotset in SET_TO_VIEWER
:
Takes the
root_dir
(where the user wants the results outputted, so theresults_dir
parameter) andparameters
(a list of Parameter objects for your plotset) as arguments.Returns a tuple
(display_name, url)
, wheredisplay_name
is the name displayed in the index andurl
is the URL of the webpage.
Putting It All Together And Running On Cori At NERSC
Go to the root of the repo where setup.py
is located and run:
pip install .
Some Examples To Run
We are running e3sm_diags
via the API. If you’re not familar with running
the software that way, please see this document.
Also, the paths to the reference and test data are for Cori at NERSC. If on another machine, please change your paths accordingly.
Also, make sure to make sure your results_dir
parameter is valid.
Running Without Any Set-Specific Parameters
Call this script run_diff_diags_demo.py
.
import os from e3sm_diags.parameter.core_parameter import CoreParameter from e3sm_diags.run import runner param = CoreParameter() param.reference_data_path = '/global/cfs/cdirs/e3sm/e3sm_diags/obs_for_e3sm_diags/climatology/' param.test_data_path = '/global/cfs/cdirs/e3sm/e3sm_diags/test_model_data_for_acme_diags/climatology/' param.test_name = '20161118.beta0.FC5COSP.ne30_ne30.edison' param.seasons = ["ANN"] prefix = '/global/cfs/cdirs/e3sm/www/shaheen2/runs_with_api' param.results_dir = os.path.join(prefix, 'diff_diags_demo') runner.sets_to_run = ['diff_diags'] runner.run_diags([param])
Run it like so:
python run_diff_diags_demo.py
Running With Set-Specific Parameters
Call this script run_diff_diags_demo_specific.py
.
import os from e3sm_diags.parameter.core_parameter import CoreParameter from e3sm_diags.parameter.diff_diags_parameter import DiffDiagsParameter from e3sm_diags.run import runner param = CoreParameter() param.reference_data_path = '/global/cfs/cdirs/e3sm/e3sm_diags/obs_for_e3sm_diags/climatology/' param.test_data_path = '/global/cfs/cdirs/e3sm/e3sm_diags/test_model_data_for_acme_diags/climatology/' param.test_name = '20161118.beta0.FC5COSP.ne30_ne30.edison' param.seasons = ["ANN"] prefix = '/global/cfs/cdirs/e3sm/www/shaheen2/runs_with_api' param.results_dir = os.path.join(prefix, 'diff_diags_demo_specific') # Set specific parameters. diff_diags_param = DiffDiagsParameter() diff_diags_param.projection = 'miller' diff_diags_param.central_lon = 30 runner.sets_to_run = ['diff_diags'] # We're passing in this new object as well, in # addtion to the CoreParameter object. runner.run_diags([param, diff_diags_param])
Run it like so:
python run_diff_diags_demo_specific.py
The ticks on the plot won’t match the newly changed central_lon
but whatever.
This is just an example.
Running With Your Own Diags
Create a file diags.cfg
.
[#] sets = ["diff_diags"] case_id = "CERES-EBAF-TOA-v2.8" variables = ["ALBEDOC"] ref_name = "ceres_ebaf_toa_v2.8" seasons = ["ANN", "DJF", "MAM", "JJA", "SON"] diff_levels = [-0.25, -0.2, -0.15, -0.1, -0.07, -0.05, -0.02, 0.02, 0.05, 0.07, 0.1, 0.15, 0.2, 0.25] [#] sets = ["diff_diags"] case_id = "CERES-EBAF-TOA-v2.8" variables = ["RESTOM"] ref_name = "ceres_ebaf_toa_v2.8" seasons = ["ANN"] diff_levels = [-50, -40, -30, -20, -10, -5, 5, 10, 20, 30, 40, 50] [#] sets = ["diff_diags"] case_id = "CERES-EBAF-TOA-v2.8" variables = ["FLUT"] ref_name = "ceres_ebaf_toa_v2.8" seasons = ["ANN"] diff_levels = [-50, -40, -30, -20, -10, -5, 5, 10, 20, 30, 40, 50]
Run it again with the same run_diff_diags_demo_specific.py
previously defined.
python run_diff_diags_demo_specific.py -d diags.cfg