Skip to content

Commit

Permalink
Merge remote-tracking branch 'public/main' into causal_clouds
Browse files Browse the repository at this point in the history
  • Loading branch information
LisaBock committed Dec 6, 2023
2 parents a98778b + 06d8812 commit f93c2cf
Show file tree
Hide file tree
Showing 23 changed files with 972 additions and 112 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ ESMValTool can run with the following types of [data as input](https://docs.esmv

# Getting started

Please see [getting started](https://docs.esmvaltool.org/en/latest/quickstart/index.html) on readthedocs as well as [ESMValTool tutorial](https://tutorial.esmvaltool.org/index.html). The tutorial is a set of lessons that together teach skills needed to work with ESMValTool in climate-related domains.
Please see [getting started](https://docs.esmvaltool.org/en/latest/quickstart/index.html) on our instance of Read the Docs as well as [ESMValTool tutorial](https://tutorial.esmvaltool.org/index.html). The tutorial is a set of lessons that together teach skills needed to work with ESMValTool in climate-related domains.

## Getting help

The easiest way to get help if you cannot find the answer in the documentation on [readthedocs](https://docs.esmvaltool.org), is to open an [issue on GitHub](https://github.com/ESMValGroup/ESMValTool/issues).
The easiest way to get help, if you cannot find the answer in the documentation in our [docs](https://docs.esmvaltool.org), is to open an [issue on GitHub](https://github.com/ESMValGroup/ESMValTool/issues).

## Contributing

Expand Down
96 changes: 47 additions & 49 deletions conda-linux-64.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions doc/sphinx/source/api/esmvaltool.diag_scripts.monitor.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Examples
--------

* :ref:`recipe_monitor`
* :ref:`recipe_model_evaluation`


Diagnostic scripts
Expand Down
5 changes: 4 additions & 1 deletion doc/sphinx/source/community/code_documentation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,10 @@ name to the list of authors in ``CITATION.cff`` and generate the entry for the
::

pip install cffconvert
cffconvert --format zenodo --outfile .zenodo.json
cffconvert --infile CITATION.cff --format zenodo --outfile .zenodo.json

Presently, this method unfortunately discards entries `communities`
and `grants` from that file; please restore them manually.

Note that authors of recipes and/or diagnostics also need to be added to the file
`esmvaltool/config-references.yml <https://github.com/ESMValGroup/ESMValTool/blob/main/esmvaltool/config-references.yml>`__,
Expand Down
12 changes: 9 additions & 3 deletions doc/sphinx/source/faq.rst
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,15 @@ a symbolic link to it so it gets picked up at every re-run iteration:
Can ESMValTool plot arbitrary model output?
===========================================

Recipe :ref:`recipe_monitor` allows for the plotting of any preprocessed model.
The plotting parameters are set through a yaml configuration file, and the
type of plots to be generated are determined in the recipe.
:ref:`recipe_model_evaluation` provides a set of recipes that can be used for a
basic climate model evaluation with observational data.
This is especially useful to get an overview of the general performance of a
simulation.

Furthermore, recipe :ref:`recipe_monitor` allows for the plotting of any
preprocessed model.
The plotting parameters are set through a yaml configuration file, and the type
of plots to be generated are determined in the recipe.

Moreover, recipe :ref:`recipes_psyplot_diag` and the corresponding diagnostic
:ref:`psyplot_diag.py <api.esmvaltool.diag_scripts.psyplot_diag>` provide a
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions doc/sphinx/source/recipes/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ large variety of input data.
.. toctree::
:maxdepth: 1

recipe_model_evaluation
recipe_monitor
recipe_psyplot
recipe_seaborn
Expand Down
98 changes: 98 additions & 0 deletions doc/sphinx/source/recipes/recipe_model_evaluation.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
.. _recipe_model_evaluation:

General model evaluation
========================

Overview
--------

These recipes and diagnostics provide a basic climate model evaluation with
observational data.
This is especially useful to get an overview of the performance of a
simulation.
The diagnostics used here allow plotting arbitrary preprocessor output, i.e.,
arbitrary variables from arbitrary datasets.


Available recipes and diagnostics
---------------------------------

Recipes are stored in `recipes/model_evaluation`

* recipe_model_evaluation_basics.yml
* recipe_model_evaluation_clouds_clim.yml
* recipe_model_evaluation_clouds_cycles.yml
* recipe_model_evaluation_precip_zonal.yml

Diagnostics are stored in `diag_scripts/monitor/`

* :ref:`multi_datasets.py
<api.esmvaltool.diag_scripts.monitor.multi_datasets>`:
Monitoring diagnostic to show multiple datasets in one plot (incl. biases).


User settings
-------------

It is recommended to use a vector graphic file type (e.g., SVG) for the output
format when running this recipe, i.e., run the recipe with the command line
option ``--output_file_type=svg`` or use ``output_file_type: svg`` in your
:ref:`esmvalcore:user configuration file`.
Note that map and profile plots are rasterized by default.
Use ``rasterize: false`` in the recipe to disable
this.


Recipe settings
~~~~~~~~~~~~~~~

A list of all possible configuration options that can be specified in the
recipe is given for each diagnostic individually (see links given for the
available diagnostics in the previous section).


Variables
---------

Any, but the variables' number of dimensions should match the ones expected by
each diagnostic (see links given for the available diagnostics in the previous
section).


Example plots
-------------

.. _fig_1:
.. figure:: /recipes/figures/model_evaluation/map_tas_MPI-ESM1-2-HR_Amon.jpg
:align: center
:width: 14cm

Global climatology of 2m near-surface air temperature.

.. _fig_2:
.. figure:: /recipes/figures/model_evaluation/map_swcre_MPI-ESM1-2-HR_Amon.jpg
:align: center
:width: 14cm

Global climatology of the shortwave cloud radiative effect (SWCRE).

.. _fig_3:
.. figure:: /recipes/figures/model_evaluation/timeseries_rtnt_ambiguous_dataset_Amon.jpg
:align: center
:width: 14cm

Time series of the global mean top-of-the-atmosphere net radiative flux.

.. _fig_4:
.. figure:: /recipes/figures/model_evaluation/variable_vs_lat_pr_Amon.jpg
:align: center
:width: 14cm

Zonal mean precipitation.

.. _fig_5:
.. figure:: /recipes/figures/model_evaluation/annual_cycle_clt_southerocean_Amon.jpg
:align: center
:width: 14cm

Annual cycle of Southern Ocean total cloud cover.
19 changes: 9 additions & 10 deletions doc/sphinx/source/recipes/recipe_monitor.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,18 @@ Available recipes and diagnostics

Recipes are stored in `recipes/monitor`

* recipe_monitor.yml
* recipe_monitor_with_refs.yml
* recipe_monitor.yml
* recipe_monitor_with_refs.yml

Diagnostics are stored in `diag_scripts/monitor/`

* :ref:`monitor.py <api.esmvaltool.diag_scripts.monitor.monitor>`:
Monitoring diagnostic to plot arbitrary preprocessor output.
* :ref:`compute_eofs.py <api.esmvaltool.diag_scripts.monitor.compute_eofs>`:
Monitoring diagnostic to plot EOF maps and associated PC timeseries.
* :ref:`multi_datasets.py
<api.esmvaltool.diag_scripts.monitor.multi_datasets>`:
Monitoring diagnostic to show multiple datasets in one plot (incl.
biases).
* :ref:`monitor.py <api.esmvaltool.diag_scripts.monitor.monitor>`:
Monitoring diagnostic to plot arbitrary preprocessor output.
* :ref:`compute_eofs.py <api.esmvaltool.diag_scripts.monitor.compute_eofs>`:
Monitoring diagnostic to plot EOF maps and associated PC timeseries.
* :ref:`multi_datasets.py
<api.esmvaltool.diag_scripts.monitor.multi_datasets>`:
Monitoring diagnostic to show multiple datasets in one plot (incl. biases).


User settings
Expand Down
10 changes: 4 additions & 6 deletions esmvaltool/diag_scripts/monitor/monitor_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ def __init__(self, config):
)
plot_folder = plot_folder.replace('{plot_dir}',
self.cfg[names.PLOT_DIR])
self.plot_folder = os.path.abspath(plot_folder)
self.plot_folder = os.path.abspath(
os.path.expandvars(os.path.expanduser(plot_folder))
)
self.plot_filename = config.get(
'plot_filename',
'{plot_type}_{real_name}_{dataset}_{mip}_{exp}_{ensemble}')
Expand Down Expand Up @@ -293,11 +295,7 @@ def get_plot_folder(self, var_info):
'real_name': self._real_name(var_info['variable_group']),
**var_info
}
folder = os.path.expandvars(
os.path.expanduser(
list(_replace_tags(self.plot_folder, info))[0]
)
)
folder = list(_replace_tags(self.plot_folder, info))[0]
if self.plot_folder.startswith('/'):
folder = '/' + folder
if not os.path.isdir(folder):
Expand Down
68 changes: 36 additions & 32 deletions esmvaltool/diag_scripts/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,11 @@

def _get_provenance_record(cfg, plot_file, caption, loc):
"""Create a provenance record describing the diagnostic data and plot."""
all_input_files = [
k for k in cfg["input_data"].keys() if k.endswith(".nc")
]
if "_vs_" in plot_file:
model_1 = plot_file.split("_vs_")[0].split("_")[-1]
if plot_file.endswith(".png"):
model_2 = plot_file.split("_vs_")[1].strip(".png")
elif plot_file.endswith(".nc"):
model_2 = plot_file.split("_vs_")[1].strip(".nc")
ancestor_1 = [
k for k in all_input_files if model_1 in os.path.basename(k)
][0]
ancestor_2 = [
k for k in all_input_files if model_2 in os.path.basename(k)
][0]
ancestor_files = [ancestor_1, ancestor_2]
else:
model = os.path.basename(plot_file).split("_")[0]
ancestor_files = [
k for k in all_input_files if model in os.path.basename(k)
]
ancestor_files = []
for dataset in cfg['input_data'].values():
if (dataset['alias'] in plot_file and
dataset['short_name'] in plot_file):
ancestor_files.append(dataset['filename'])
record = {
'caption': caption,
'statistics': ['mean'],
Expand All @@ -72,9 +56,9 @@ def _get_provenance_record(cfg, plot_file, caption, loc):
def plot_contour(cube, cfg, plt_title, file_name):
"""Plot a contour with iris.quickplot (qplot)."""
if len(cube.shape) == 2:
qplt.contourf(cube, cmap='RdYlBu_r', bbox_inches='tight')
qplt.contourf(cube, cmap='RdYlBu_r')
else:
qplt.contourf(cube[0], cmap='RdYlBu_r', bbox_inches='tight')
qplt.contourf(cube[0], cmap='RdYlBu_r')
plt.title(plt_title)
plt.gca().coastlines()
plt.tight_layout()
Expand Down Expand Up @@ -138,7 +122,10 @@ def plot_latlon_cubes(cube_1,
# plot each cube
var = data_names.split('_')[0]
if not obs_name:
cube_names = [data_names.split('_')[1], data_names.split('_')[3]]
cube_names = [
data_names.replace(f'{var}_', '').split('_vs_')[i] for i in
range(2)
]
for cube, cube_name in zip(cubes, cube_names):
if not season:
plot_file_path = os.path.join(
Expand Down Expand Up @@ -179,23 +166,40 @@ def plot_zonal_cubes(cube_1, cube_2, cfg, plot_data):
# xcoordinate: latotude or longitude (str)
data_names, xcoordinate, period = plot_data
var = data_names.split('_')[0]
cube_names = [data_names.split('_')[1], data_names.split('_')[3]]
cube_names = data_names.replace(var + '_', '').split('_vs_')
lat_points = cube_1.coord(xcoordinate).points
plt.plot(lat_points, cube_1.data, label=cube_names[0])
plt.plot(lat_points, cube_2.data, label=cube_names[1])
plt.title(f'Annual Climatology of {var}' if period == 'alltime'
else f'{period} of {var}')
if xcoordinate == 'latitude':
plt.title(period + ' Zonal Mean for ' + var + ' ' + data_names)
axis = plt.gca()
axis.set_xticks([-60, -30, 0, 30, 60],
labels=['60\N{DEGREE SIGN} S',
'30\N{DEGREE SIGN} S',
'0\N{DEGREE SIGN}',
'30\N{DEGREE SIGN} N',
'60\N{DEGREE SIGN} N'])
elif xcoordinate == 'longitude':
plt.title(period + ' Meridional Mean for ' + var + ' ' + data_names)
axis = plt.gca()
axis.set_xticks([0, 60, 120, 180, 240, 300, 360],
labels=['0\N{DEGREE SIGN} E',
'60\N{DEGREE SIGN} E',
'120\N{DEGREE SIGN} E',
'180\N{DEGREE SIGN} E',
'240\N{DEGREE SIGN} E',
'300\N{DEGREE SIGN} E',
'0\N{DEGREE SIGN} E'])
plt.xlabel(xcoordinate + ' (deg)')
plt.ylabel(var)
plt.ylabel(f'{var} [{str(cube_1.units)}]')
plt.tight_layout()
plt.grid()
plt.legend()
png_name = f'{xcoordinate}_{period}_{data_names}.png'
if xcoordinate == 'latitude':
png_name = 'Zonal_Mean_' + xcoordinate + '_' + data_names + '.png'
png_name = 'Zonal_Mean_' + png_name
elif xcoordinate == 'longitude':
png_name = 'Merid_Mean_' + xcoordinate + '_' + data_names + '.png'
png_name = 'Merid_Mean_' + png_name
plot_file_path = os.path.join(cfg['plot_dir'], period, png_name)
plt.savefig(plot_file_path)
save_plotted_cubes(
Expand Down Expand Up @@ -252,13 +256,13 @@ def coordinate_collapse(data_set, cfg):
if 'mask_threshold' in cfg:
thr = cfg['mask_threshold']
data_set.data = np.ma.masked_array(data_set.data,
mask=(mask_cube.data > thr))
mask=mask_cube.data > thr)
else:
logger.warning('Could not find masking threshold')
logger.warning('Please specify it if needed')
logger.warning('Masking on 0-values = True (masked value)')
data_set.data = np.ma.masked_array(data_set.data,
mask=(mask_cube.data == 0))
mask=mask_cube.data == 0)

# if zonal mean on LON
if analysis_type == 'zonal_mean':
Expand Down
Loading

0 comments on commit f93c2cf

Please sign in to comment.