Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
d56453c
init wip pet spei diags
lukruh Jan 31, 2025
15ad545
move cdd to droughts folder
lukruh Jan 31, 2025
51fca60
force nc4, spi specialcase of spei, save any coefficients
lukruh Feb 4, 2025
dd0491d
example recipe
lukruh Feb 4, 2025
891066e
remove prints and comments
lukruh Feb 6, 2025
c0140bc
copy martin18 diags
lukruh Feb 6, 2025
b945c36
plot obs maps
lukruh Feb 10, 2025
7bbbc37
add compare time periods (as in collect_drought_models)
lukruh Feb 11, 2025
a9f7136
add provenance for all files
lukruh Feb 11, 2025
43fb9b3
adjust recipe to use spei.R (plots look the same)
lukruh Feb 12, 2025
fd0cd62
droughts diagnostic documentation
lukruh Feb 12, 2025
bf6685c
recipe_spei documentation
lukruh Feb 12, 2025
08b85be
update docs martin18grl
lukruh Feb 12, 2025
0bc32e3
update cdd recipe docs
lukruh Feb 12, 2025
85179ab
fix caption indentation in docs
lukruh Feb 13, 2025
52f13b1
fix time slicing based on comparison_period
lukruh Feb 14, 2025
5a207e9
make individual model plots optional
lukruh Feb 18, 2025
3ba2520
add PET documentation
lukruh Feb 18, 2025
a51f13e
drought folder for recipe docs
lukruh Feb 21, 2025
121aa1e
ensure mm/day as default unit and convert without leap days
lukruh Feb 21, 2025
b4bf351
rename collect_drought_func to utils.py
lukruh Feb 21, 2025
85ae1b4
use acronym as shortname and added long_name
lukruh Feb 24, 2025
98e20af
fix units
lukruh Feb 24, 2025
22f6146
added first plot diagnostic and utils
lukruh Feb 24, 2025
31ae8a9
fix unit string in martin recipe
lukruh Feb 24, 2025
b0ee72b
fixed some ruff complains
lukruh Feb 24, 2025
7b047a1
add example plots for spei recipe
lukruh Feb 24, 2025
de83aa1
fix function call and auto format utils.py
lukruh Feb 24, 2025
9dc6529
ruff up utils.py
lukruh Feb 25, 2025
9db51c0
some manual codacy fixes
lukruh Feb 25, 2025
e45589e
more codacy issues
lukruh Feb 26, 2025
9032664
flake8 issues solved
lukruh Feb 26, 2025
0512091
fix codacy issues
lukruh Feb 27, 2025
cecdb06
move martin specific functions from utils to collect
lukruh Feb 27, 2025
95987d1
some codacy complains fixed
lukruh Feb 28, 2025
7d75528
do extra calculation to reduce code complexity
lukruh Feb 28, 2025
a9f7f59
Merge branch 'main' into droughts
lukruh Feb 28, 2025
a5ff680
add filter argument to select input data from recipe
lukruh Feb 28, 2025
9093ffc
Merge branch 'droughts' of github.com:ESMValGroup/ESMValTool into dro…
lukruh Feb 28, 2025
9d0ae92
add whitespace
lukruh Mar 3, 2025
1de7790
make utils.py less than 100 lines
lukruh Mar 3, 2025
f0206a5
fix imports
lukruh Mar 3, 2025
74c8197
fix imports
lukruh Mar 3, 2025
b605d59
add diffmap diagnostic to docs
lukruh Mar 3, 2025
438acc8
fix doc titles, sort utils, add init
lukruh Mar 3, 2025
3755a1a
remove old recipes and diagnostics
lukruh Mar 3, 2025
0eb835a
fix codacy one more time
lukruh Mar 3, 2025
37b8506
remove whitespace
lukruh Mar 3, 2025
63282ab
correct diffmap doc page
lukruh Mar 4, 2025
759cd44
Merge branch 'main' into droughts
lukruh Mar 4, 2025
2c03e24
add titles, sort config options
lukruh Mar 6, 2025
32a65d6
change martin title in docs
lukruh Mar 6, 2025
5ca4179
init branch for lindenlaub25
lukruh Mar 6, 2025
4ffc13f
remove prints, fix params defaults
lukruh Mar 7, 2025
0941660
add diagnostics from private
lukruh Mar 7, 2025
3b9ad9f
modify recipe
lukruh Mar 7, 2025
041a57a
remove prints, set defaults crop, method
lukruh Mar 7, 2025
874b0eb
Merge commit '25436a023b0d8d5459e8fab1aa37a5ace5168711' into lindenla…
lukruh Mar 10, 2025
e944a2a
Merge branch 'main' of github.com:ESMValGroup/ESMValTool into lindenl…
lukruh Mar 10, 2025
cb2e532
Merge branch 'main' of github.com:ESMValGroup/ESMValTool into droughts
lukruh Mar 10, 2025
c91f40e
formatted by new pre-commit
lukruh Mar 10, 2025
e85e817
Merge branch 'droughts' into lindenlaub25
lukruh Mar 10, 2025
6bef479
safe fixes
lukruh Mar 10, 2025
268a4b2
unsafe ruff fixes
lukruh Mar 10, 2025
86887d8
pattcorr plot
lukruh Mar 12, 2025
4bd1096
single model provenance
lukruh Mar 12, 2025
52a7c6b
add plots, provenance for diffmap
lukruh Mar 13, 2025
6166978
fix timeseries: ylabels, legends, units
lukruh Mar 19, 2025
fd57443
wip paper
lukruh Apr 1, 2025
7c03c7e
init paper doc
lukruh Apr 1, 2025
5681644
init paper doc
lukruh Apr 1, 2025
ba3a0eb
Merge branch 'main' into lindenlaub25
lukruh May 28, 2025
1fadb52
folder rename, plot adjustments
lukruh Sep 29, 2025
502dd4c
seasonal cycles
lukruh Sep 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion doc/sphinx/source/recipes/droughts/recipe_consecdrydays.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,5 @@ Example plots
:align: center
:width: 14cm

Example of the number of occurrences with consecutive dry days of more than five days in the period 2001 to 2002 for the CMIP5 model bcc-csm1-1-m.
Example of the number of occurrences with consecutive dry days of more than
five days in the period 2001 to 2002 for the CMIP5 model bcc-csm1-1-m.
81 changes: 81 additions & 0 deletions doc/sphinx/source/recipes/droughts/recipe_lindenlaub25.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@

.. _recipes_martin18grl:

Agricultural Droughts in CMIP6 Future Projections
=================================================

Overview
--------

The two recipes presented here evaulate historical simulations of 18 CMIP6
models and analyse their projections for three different future pathways.
The results are published in Lindenlaub (2025).


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

Recipes are stored in ``recipes/droughts/``

* recipe_lindenlaub25_historical.yml
* recipe_lindenlaub25_scenarios.yml

Diagnostics used by this recipes:

* droughts/pet.R
* droughts/spei.R
* :ref:`droughts/diffmap.py <api.esmvaltool.diag_scripts.droughts.diffmap>`
* :ref:`droughts/distribution.py <api.esmvaltool.diag_scripts.droughts.distribution>`
* :ref:`droughts/event_area_timeseries.py <api.esmvaltool.diag_scripts.droughts.event_area_timeseries>`
* :ref:`droughts/pattern_correlation.py <api.esmvaltool.diag_scripts.droughts.pattern_correlation>`
* :ref:`droughts/timeseries_scenarios.py <api.esmvaltool.diag_scripts.droughts.timeseries_scenarios>`
* :ref:`droughts/regional_hexagons.py <api.esmvaltool.diag_scripts.droughts.regional_hexagons>`

Data
----

Soil moisture is evaluated and discussed, but not required for PET and SPEI
calculation.
``tasmin``, ``tasmax``, ``sfcWind``, ``ps``, ``rsds`` are used to approximate
``evspsblpot`` for ERA5 and 18 CMIP6 datasets.
``SPEI`` is calculated from ``evspsblpot`` and ``pr``.


TODO: ERA5 native on the fly or cmorizer?

Reference Data (ERA5, CDS-SM, CRU) can be downloaded and cmorized by the
esmvaltool executing the `data` command:

```
esmvaltool data download ERA5 pr
esmvaltool data format ERA5 pr
```

``tasmin`` and ``tasmax`` are not directly available in the monthly averaged data
product. The download script `diag_scripts/droughts/download_era5_tasminmax.py`
can be used to download and preprocess the data based on the
``minimum_2m_temperature_since_previous_post_processing`` and
``maximum_2m_temperature_since_previous_post_processing`` variables from ERA5
daily data on single levels. The output files are compatible with the esmvaltool
and can be copied into the ``native6/Tier3/ERA5/v1/mon/``
directory. Using the esmvaltool environment ensures that all required libraries
are available. The script can be run with the following command:

```
python diag_scripts/droughts/download_era5_tasminmax.py
```

For more options use the ``--help`` flag.

The CMIP6 data can be downloaded automatically by the ESMValTool. Just ensure
that ``esgf_download`` is set to ``True`` or ``when_missing`` in the
user configuration.

Figures
-------

References
----------

* Lindenlaub, L. (2025). Agricultural Droughts in CMIP6 Future Projections.
Journal of Climate, 38(1), 1-15. https://doi.org/10.1029/2025JC012345
Binary file not shown.
Binary file not shown.
32 changes: 15 additions & 17 deletions esmvaltool/diag_scripts/droughts/diffmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@
from __future__ import annotations

import logging
import os
from collections import defaultdict
from pathlib import Path

Expand Down Expand Up @@ -210,7 +209,10 @@ def plot(
basename: str,
kwargs: dict | None = None,
) -> str:
"""Plot map using diag_scripts.shared module."""
"""Plot map using diag_scripts.shared module.

Returns the plot filename.
"""
plotfile = get_plot_filename(basename, cfg)
plot_kwargs = cfg.get("plot_kwargs", {}).copy()
if kwargs is not None:
Expand All @@ -231,7 +233,7 @@ def plot(
add_cyclic_point(cube.data, cube.coord("longitude").points)
mapplot = global_contourf(cube, **plot_kwargs)
if cfg.get("clip_land", False):
plt.gca().set_extent((220, 170, -55, 90))
plt.gca().set_extent((220, 170, -55, 90)) # type: ignore[attr-defined]
plt.title(meta.get("title", basename))
if cfg.get("strip_plots", False):
plt.gca().set_title(None)
Expand Down Expand Up @@ -285,15 +287,15 @@ def calculate_diff(cfg, meta, mm_data, output_meta, group) -> None:
cube = pp.extract_time(
cube, cfg["start_year"], 1, 1, cfg["end_year"], 12, 31
)
dtime = cfg.get("comparison_period", 10) * 12
cubes = {}
dtime = cfg["comparison_period"] * 12
cubes: dict[Cube] = {}
cubes["total"] = cube.collapsed("time", MEAN)
do_metrics = cfg.get("metrics", METRICS)
norm = (
int(meta["end_year"])
- int(meta["start_year"])
+ 1 # count full end year
- cfg.get("comparison_period", 10) # decades center to center
- cfg["comparison_period"] # decades center to center
) / 10
cubes["first"] = cube[0:dtime].collapsed("time", MEAN)
cubes["last"] = cube[-dtime:].collapsed("time", MEAN)
Expand Down Expand Up @@ -335,26 +337,22 @@ def calculate_diff(cfg, meta, mm_data, output_meta, group) -> None:
def calculate_mmm(cfg, meta, mm_data, output_meta, group) -> None:
"""Calculate multi-model mean for a given metric."""
for metric in cfg.get("metrics", METRICS):
drop = cfg.get("dropcoords", ["time", "height"])
meta = meta.copy() # don't modify meta in place:
meta["dataset"] = "MMM"
meta["diffmap_metric"] = metric
basename = cfg["basename"].format(**meta)
mmm, _ = utils.mmm(
mm_data[metric],
dropcoords=drop,
dropmethods=metric != "diff",
mdtol=cfg.get("mdtol", 0.3),
results = pp.multi_model_statistics(
mm_data[metric], "overlap", ["mean"], ignore_scalar_coords=True
)
mmm = results["mean"]
meta["title"] = f"Multi-model Mean ({cfg['titles'][metric]})"
if cfg.get("plot_mmm", True):
plot_kwargs = cfg.get("plot_kwargs", {}).copy()
overwrites = cfg.get("plot_kwargs_overwrite", [])
apply_plot_kwargs_overwrite(plot_kwargs, overwrites, metric, group)
plotfile = plot(cfg, meta, mmm, basename, kwargs=plot_kwargs)
plot_file = plot(cfg, meta, mmm, basename, kwargs=plot_kwargs)
prov = _get_provenance(cfg, meta)
prov["ancestors"] = meta["ancestors"]
utils.log_provenance(cfg, plotfile, prov)
utils.log_provenance(cfg, plot_file, prov)
if cfg.get("save_mmm", True):
work_file = str(Path(cfg["work_dir"]) / f"{basename}.nc")
meta["filename"] = work_file
Expand All @@ -365,8 +363,8 @@ def calculate_mmm(cfg, meta, mm_data, output_meta, group) -> None:

def set_defaults(cfg: dict) -> None:
"""Update cfg with default values from diffmap.yml in place."""
config_fpath = os.path.realpath(__file__)[:-3] + ".yml"
with open(config_fpath, encoding="utf-8") as config_file:
config_fpath = Path(__file__).with_suffix(".yml")
with config_fpath.open() as config_file:
defaults = yaml.safe_load(config_file)
for key, val in defaults.items():
cfg.setdefault(key, val)
Expand Down
Loading