Repository: ESMValGroup/ESMValCore

Issue:
var has not key 'diagnostic' error when checking var from the recipe
**Describe the bug**
When a necessary entry is missing in a variable, instead of raising the correct error defined in
https://github.com/ESMValGroup/ESMValCore/blob/989839f596a6b4433731c0efcb6b5cb4f3ca7045/esmvalcore/_recipe/check.py#L103-L112

another error is raised as `var` has no key `'diagnostic`. Also a format `f` is missing in line 111.

Solving it with a PR.

**Please attach**
An example with missing `mip` won't produce the expected error message.
```yml
datasets:
- project: OBS
  institute: ukmo
  type: obs
  dataset: en4-v4.2.2
  freq_base: ''
  freq_folder: monthly_mean
  timerange: 190001/190001
diagnostics:
  temperature:
    variables:
      thetao: {}
    scripts: null
```

```
2024-01-30 16:30:49,330 UTC [3133476] ERROR   Program terminated abnormally, see stack trace below for more information:
Traceback (most recent call last):
  File "/gpfs/projects/bsc32/software/suselinux/11/software/ESMValTool/2.10.0/lib/python3.11/site-packages/esmvalcore/_main.py", line 518, in run
    fire.Fire(ESMValTool())
  File "/gpfs/projects/bsc32/software/suselinux/11/software/ESMValTool/2.10.0/lib/python3.11/site-packages/fire/core.py", line 141, in Fire
    component_trace = _Fire(component, args, parsed_flag_args, context, name)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/gpfs/projects/bsc32/software/suselinux/11/software/ESMValTool/2.10.0/lib/python3.11/site-packages/fire/core.py", line 475, in _Fire
    component, remaining_args = _CallAndUpdateTrace(
                                ^^^^^^^^^^^^^^^^^^^^
  File "/gpfs/projects/bsc32/software/suselinux/11/software/ESMValTool/2.10.0/lib/python3.11/site-packages/fire/core.py", line 691, in _CallAndUpdateTrace
    component = fn(*varargs, **kwargs)
                ^^^^^^^^^^^^^^^^^^^^^^
  File "/gpfs/projects/bsc32/software/suselinux/11/software/ESMValTool/2.10.0/lib/python3.11/site-packages/esmvalcore/_main.py", line 405, in run
    self._run(recipe, session)
  File "/gpfs/projects/bsc32/software/suselinux/11/software/ESMValTool/2.10.0/lib/python3.11/site-packages/esmvalcore/_main.py", line 447, in _run
    process_recipe(recipe_file=recipe, session=session)
  File "/gpfs/projects/bsc32/software/suselinux/11/software/ESMValTool/2.10.0/lib/python3.11/site-packages/esmvalcore/_main.py", line 127, in process_recipe
    recipe = read_recipe_file(recipe_file, session)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/gpfs/projects/bsc32/software/suselinux/11/software/ESMValTool/2.10.0/lib/python3.11/site-packages/esmvalcore/_recipe/recipe.py", line 73, in read_recipe_file
    return Recipe(raw_recipe, session, recipe_file=filename)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/gpfs/projects/bsc32/software/suselinux/11/software/ESMValTool/2.10.0/lib/python3.11/site-packages/esmvalcore/_recipe/recipe.py", line 714, in __init__
    self.datasets = Dataset.from_recipe(recipe_file, session)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/gpfs/projects/bsc32/software/suselinux/11/software/ESMValTool/2.10.0/lib/python3.11/site-packages/esmvalcore/dataset.py", line 152, in from_recipe
    return datasets_from_recipe(recipe, session)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/gpfs/projects/bsc32/software/suselinux/11/software/ESMValTool/2.10.0/lib/python3.11/site-packages/esmvalcore/_recipe/to_datasets.py", line 386, in datasets_from_recipe
    variable_datasets = _get_datasets_for_variable(
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/gpfs/projects/bsc32/software/suselinux/11/software/ESMValTool/2.10.0/lib/python3.11/site-packages/esmvalcore/_recipe/to_datasets.py", line 350, in _get_datasets_for_variable
    for facets, supplementaries in _get_facets_from_recipe(
  File "/gpfs/projects/bsc32/software/suselinux/11/software/ESMValTool/2.10.0/lib/python3.11/site-packages/esmvalcore/_recipe/to_datasets.py", line 327, in _get_facets_from_recipe
    yield _get_dataset_facets_from_recipe(
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/gpfs/projects/bsc32/software/suselinux/11/software/ESMValTool/2.10.0/lib/python3.11/site-packages/esmvalcore/_recipe/to_datasets.py", line 281, in _get_dataset_facets_from_recipe
    check.variable(
  File "/gpfs/projects/bsc32/software/suselinux/11/software/ESMValTool/2.10.0/lib/python3.11/site-packages/esmvalcore/_recipe/check.py", line 112, in variable
    f"{var['diagnostic']}")
       ~~~^^^^^^^^^^^^^^
KeyError: 'diagnostic'
2024-01-30 16:30:49,405 UTC [3133476] INFO    
If you have a question or need help, please start a new discussion on https://github.com/ESMValGroup/ESMValTool/discussions
If you suspect this is a bug, please open an issue on https://github.com/ESMValGroup/ESMValTool/issues
To make it easier to find out what the problem is, please consider attaching the files run/recipe_*.yml and run/main_log_debug.txt from the output directory.
```


Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/ESMValGroup/ESMValCore/pull/2313?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ESMValGroup) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Comparison is base [(`989839f`)](https://app.codecov.io/gh/ESMValGroup/ESMValCore/commit/989839f596a6b4433731c0efcb6b5cb4f3ca7045?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ESMValGroup) 93.74% compared to head [(`e4e6da4`)](https://app.codecov.io/gh/ESMValGroup/ESMValCore/pull/2313?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ESMValGroup) 93.74%.


<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main    #2313   +/-   ##
=======================================
  Coverage   93.74%   93.74%           
=======================================
  Files         238      238           
  Lines       13153    13153           
=======================================
  Hits        12330    12330           
  Misses        823      823           
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/ESMValGroup/ESMValCore/pull/2313?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ESMValGroup).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ESMValGroup).

enekomartinmartinez: I just updated the commit so as not to have the same error with the `variable_group`. It would be nice to include unity tests for these error messages.
bouweandela: If you'd like to add a unit tests for this, see here for an example of how to write such a test: https://github.com/ESMValGroup/ESMValCore/blob/b0d30f65ccc34b9a85b3edcc722f77560fba2adf/tests/integration/recipe/test_check.py#L337
enekomartinmartinez: > Thanks for taking the time to contribute! Looks good, just a minor suggestion to make the error message even more readable.

Thanks for the suggestions, I will also update the error message in similar functions to have something more "uniform".
bouweandela: Would you still like to add a test, it shall we merge this as-is?
enekomartinmartinez: Hi @bouweandela I have tried to uniformize the RecipeErrors and tests them for:

- [diagnostics](https://github.com/ESMValGroup/ESMValCore/blob/6f30fa6495fbe76d5694e7ba9260e09db70ea631/esmvalcore/_recipe/check.py#L66)
- [duplicate_datasets](https://github.com/ESMValGroup/ESMValCore/blob/6f30fa6495fbe76d5694e7ba9260e09db70ea631/esmvalcore/_recipe/check.py#L90)
- [variable](https://github.com/ESMValGroup/ESMValCore/blob/6f30fa6495fbe76d5694e7ba9260e09db70ea631/esmvalcore/_recipe/check.py#L109)

Note that there are two errors (currently added a tests with `xfail`) that are not raised:
https://github.com/ESMValGroup/ESMValCore/blob/6f30fa6495fbe76d5694e7ba9260e09db70ea631/esmvalcore/_recipe/check.py#L71-L73
https://github.com/ESMValGroup/ESMValCore/blob/6f30fa6495fbe76d5694e7ba9260e09db70ea631/esmvalcore/_recipe/check.py#L84-L87

This is because the error was raised before by the [yamale.validate check](https://github.com/ESMValGroup/ESMValCore/blob/6f30fa6495fbe76d5694e7ba9260e09db70ea631/esmvalcore/_recipe/check.py#L57).

Therefore, for me, it would make sense to do any of these:
1. Remove the RecipeErrors that are not raised and convert the test to a yamale.validate errors
or
2. Soften the condition for the yamale.validate, setting `required=False` for those entries in the [recipe_schema.yml](https://github.com/ESMValGroup/ESMValCore/blob/6f30fa6495fbe76d5694e7ba9260e09db70ea631/esmvalcore/_recipe/recipe_schema.yml) and let the RecipeError raise later (which I think is more user friendly).

Let me know which one you prefer

bouweandela: I agree that option 2 is more user-friendly. Especially less technical users can get confused by the cryptic error messages coming from Yamale (see e.g. #1080 and #681)
enekomartinmartinez: @bouweandela, this is already okay and ready to merge by my side. Please, let me know if you find anything else missing!
bouweandela: Would you like to add yourself to the [list of authors](https://docs.esmvaltool.org/projects/ESMValCore/en/latest/contributing.html#list-of-authors)?

Patch:
diff --git a/.zenodo.json b/.zenodo.json
index 31727467b..df7a49977 100644
--- a/.zenodo.json
+++ b/.zenodo.json
@@ -178,6 +178,11 @@
             "name": "Kazeroni, Rémi",
             "orcid": "0000-0001-7205-9528"
         },
+        {
+            "affiliation": "GEOMAR, Germany",
+            "name": "Hohn, David",
+            "orcid": "0000-0002-5317-1247"
+        },
         {
             "affiliation": "DLR, Germany",
             "name": "Bauer, Julian"
@@ -190,6 +195,11 @@
         {
             "affiliation": "Forschungszentrum Juelich, Germany",
             "name": "Benke, Joerg"
+        },
+        {
+            "affiliation": "BSC, Spain",
+            "name": "Martin-Martinez, Eneko",
+            "orcid": "0000-0002-9213-7818"
         }
     ],
     "description": "ESMValCore: A community tool for pre-processing data from Earth system models in CMIP and running analysis scripts.",
diff --git a/CITATION.cff b/CITATION.cff
index f0b66da54..d188af5d3 100644
--- a/CITATION.cff
+++ b/CITATION.cff
@@ -199,6 +199,11 @@ authors:
     affiliation: "Forschungszentrum Juelich (FZJ), Germany"
     family-names: Benke
     given-names: Joerg
+  -
+    affiliation: "BSC, Spain"
+    family-names: Martin-Martinez
+    given-names: Eneko
+    orcid: "https://orcid.org/0000-0002-9213-7818"
 
 cff-version: 1.2.0
 date-released: 2023-12-19
diff --git a/conda-linux-64.lock b/conda-linux-64.lock
index 79a33afd0..c2d8d02ef 100644
--- a/conda-linux-64.lock
+++ b/conda-linux-64.lock
@@ -3,7 +3,7 @@
 # input_hash: 69ce949889818c504d81d521be29fb77d725352f185b4be94a01411d8bff16a5
 @EXPLICIT
 https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81
-https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2023.11.17-hbcca054_0.conda#01ffc8d36f9eba0ce0b3c1955fa780ee
+https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2024.2.2-hbcca054_0.conda#2f4327a1cbe7f022401b236e915a5fef
 https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45
 https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6
 https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb
@@ -11,21 +11,22 @@ https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_1.co
 https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-2.6.32-he073ed8_16.conda#7ca122655873935e02c91279c5b03c8c
 https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.40-h41732ed_0.conda#7aca3059a1729aa76c597603f10b0dd3
 https://conda.anaconda.org/conda-forge/linux-64/libboost-headers-1.84.0-ha770c72_0.conda#9c595e87653a36aa4d8c71b4e2f7e586
-https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-12.3.0-h8bca6fd_104.conda#06bb2cb81d0e720d6070af4b76c26602
-https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-12.3.0-h8bca6fd_104.conda#45d77c630e501dc1ae76d743b9e31c52
-https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-13.2.0-h7e041cc_4.conda#f6a3a9c67eb8030555d04066bcd65320
+https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-12.3.0-h8bca6fd_105.conda#e12ce6b051085b8f27e239f5e5f5bce5
+https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-12.3.0-h8bca6fd_105.conda#b3c6062c84a8e172555ee104ea6a01ab
+https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-13.2.0-h7e041cc_5.conda#f6f6600d18a4047b54f803cf708b868a
+https://conda.anaconda.org/conda-forge/linux-64/pandoc-3.1.11.1-ha770c72_0.conda#0e2f14aff42adf4675bcd5335d644a5f
 https://conda.anaconda.org/conda-forge/noarch/poppler-data-0.4.12-hd8ed1ab_0.conda#d8d7293c5b37f39b2ac32940621c6592
 https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.11-4_cp311.conda#d786502c97404c94d7d58d258a445a65
-https://conda.anaconda.org/conda-forge/noarch/tzdata-2023d-h0c530f3_0.conda#8dee24b8be2d9ff81e7bd4d7d97ff1b0
+https://conda.anaconda.org/conda-forge/noarch/tzdata-2024a-h0c530f3_0.conda#161081fc7cec0bfda0d86d7cb595f8d8
 https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2#f766549260d6815b0c52253f1fb1bb29
-https://conda.anaconda.org/conda-forge/linux-64/libgomp-13.2.0-h807b86a_4.conda#cd6ae9659d3cd53207efa8e0ed3ab15d
+https://conda.anaconda.org/conda-forge/linux-64/libgomp-13.2.0-h807b86a_5.conda#d211c42b9ce49aee3734fdc828731689
 https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.12-he073ed8_16.conda#071ea8dceff4d30ac511f4a2f8437cd1
 https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d
 https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.40-hf600244_0.conda#33084421a8c0af6aef1b439707f7662a
 https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab
 https://conda.anaconda.org/conda-forge/linux-64/binutils-2.40-hdd6e379_0.conda#ccc940fddbc3fcd3d79cd4c654c4b5c4
 https://conda.anaconda.org/conda-forge/linux-64/binutils_linux-64-2.40-hbdbef99_2.conda#adfebae9fdc63a598495dfe3b006973a
-https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-13.2.0-h807b86a_4.conda#e0dee4121cc9d961b3740e3759b02d13
+https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-13.2.0-h807b86a_5.conda#d4ff227c46917d3b4565302a2bbb276b
 https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.9.12-hd590300_0.conda#7dbb94ffb9df66406f3101625807cac1
 https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hd590300_5.conda#69b8b6202a07720f448be700e300ccf4
 https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.26.0-hd590300_0.conda#a86d90025198fd411845fc245ebc06c8
@@ -34,7 +35,6 @@ https://conda.anaconda.org/conda-forge/linux-64/geos-3.12.1-h59595ed_0.conda#8c0
 https://conda.anaconda.org/conda-forge/linux-64/gettext-0.21.1-h27087fc_0.tar.bz2#14947d8770185e5153fdd04d4673ed37
 https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-he1b5a44_1004.tar.bz2#cddaf2c63ea4a5901cf09524c490ecdc
 https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.1-h0b41bf4_3.conda#96f3b11872ef6fad973eac856cd2624f
-https://conda.anaconda.org/conda-forge/linux-64/gmp-6.3.0-h59595ed_0.conda#0e33ef437202db431aa5a928248cf2e8
 https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.13-h58526e2_1001.tar.bz2#8c54672728e8ec6aa6db90cf2806d220
 https://conda.anaconda.org/conda-forge/linux-64/icu-73.2-h59595ed_0.conda#cc47e1facc155f91abd89b11e48e72ff
 https://conda.anaconda.org/conda-forge/linux-64/json-c-0.17-h7ab15ed_0.conda#9961b1f100c3b6852bd97c9233d06979
@@ -48,13 +48,13 @@ https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.19-hd590300_0.conda
 https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda#172bf1cd1ff8629f2b1179945ed45055
 https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.5.0-hcb278e6_1.conda#6305a3dd2752c76335295da4e581f2fd
 https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2#d645c6d2ac96843a2bfaccd2d62b3ac3
-https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-13.2.0-ha4646dd_4.conda#e661d5238ba69d8fe2e488bbde6f9cc4
+https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-13.2.0-ha4646dd_5.conda#7a6bd7a12a4bd359e2afe6c0fa1acace
 https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-hd590300_2.conda#d66573916ffcf376178462f1b61c941e
 https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.0.0-hd590300_1.conda#ea25936bb4080d843790b586850f82b8
 https://conda.anaconda.org/conda-forge/linux-64/libnl-3.9.0-hd590300_0.conda#d27c451db4f1d3c983c78167d2fdabc2
 https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda#30fd6e37fe21f86f4bd26d6ee73eeec7
 https://conda.anaconda.org/conda-forge/linux-64/libnuma-2.0.16-h0b41bf4_1.conda#28bfe2cb11357ccc5be21101a6b7ce86
-https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-12.3.0-h0f45ef3_4.conda#6d9a8d4df38293eda7b836231dd30ee1
+https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-12.3.0-h0f45ef3_5.conda#11d1ceacff40054d5a74b12975d76f20
 https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.18-h36c2ea0_1.tar.bz2#c3788462a6fbddafdb413a9f9053e58d
 https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.8.0-h166bdaf_0.tar.bz2#ede4266dc02e875fe1ea77b25dd43747
 https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b
@@ -69,7 +69,7 @@ https://conda.anaconda.org/conda-forge/linux-64/openssl-3.2.1-hd590300_0.conda#5
 https://conda.anaconda.org/conda-forge/linux-64/pixman-0.43.2-h59595ed_0.conda#71004cbf7924e19c02746ccde9fd7123
 https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h36c2ea0_1001.tar.bz2#22dad4df6e8630e8dff2428f6f6a7036
 https://conda.anaconda.org/conda-forge/linux-64/snappy-1.1.10-h9fff704_0.conda#e6d228cd0bb74a51dd18f5bfce0b4115
-https://conda.anaconda.org/conda-forge/linux-64/tzcode-2023d-h3f72095_0.conda#1c63518899838477ebd497e3e3327f81
+https://conda.anaconda.org/conda-forge/linux-64/tzcode-2024a-h3f72095_0.conda#32146e34aaec3745a08b6f49af3f41b0
 https://conda.anaconda.org/conda-forge/linux-64/uriparser-0.9.7-hcb278e6_1.conda#2c46deb08ba9b10e90d0a6401ad65deb
 https://conda.anaconda.org/conda-forge/linux-64/xorg-kbproto-1.0.7-h7f98852_1002.tar.bz2#4b230e8381279d76131116660f5a241a
 https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.1-hd590300_0.conda#b462a33c0be1421532f28bfe8f4a7514
@@ -86,43 +86,43 @@ https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.2.17-h572eab
 https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.1.14-h572eabf_0.conda#42db61eee93a2c0f918d18bd4422d331
 https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.1.17-h572eabf_7.conda#f7323eedc2685a24661cd6b57d7ed321
 https://conda.anaconda.org/conda-forge/linux-64/expat-2.5.0-hcb278e6_1.conda#8b9b5aca60558d02ddaa09d599e55920
-https://conda.anaconda.org/conda-forge/linux-64/gcc_impl_linux-64-12.3.0-he2b93b0_4.conda#bffcb40b19d5cd3ba9bc2d0d23b113b8
+https://conda.anaconda.org/conda-forge/linux-64/gcc_impl_linux-64-12.3.0-he2b93b0_5.conda#e89827619e73df59496c708b94f6f3d5
 https://conda.anaconda.org/conda-forge/linux-64/glog-0.6.0-h6f12383_0.tar.bz2#b31f3565cb84435407594e548a2fb7b2
 https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h2a13503_7.conda#bd77f8da987968ec3927990495dc22e4
 https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hd590300_1.conda#f07002e225d7a60a694d42a7bf5ff53f
 https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hd590300_1.conda#5fc11c6020d421960607d821310fcd4d
 https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#4d331e44109e3f0e19b4cb8f9b82f3e1
 https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda#a1cfcc585f0c42bf8d5546bb1dfb668d
-https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-13.2.0-h69a702a_4.conda#4956aae564e90a1fc96f3bc58152c2b2
+https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-13.2.0-h69a702a_5.conda#e73e9cfd1191783392131e6238bdb3e9
 https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-h01aab08_1018.conda#3eb5f16bcc8a02892199aa63555c731f
 https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.58.0-h47da74e_1.conda#700ac6ea6d53d5510591c4344d5c989a
-https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.39-h753d276_0.conda#e1c890aebdebbfbf87e2c917187b4416
-https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-4.25.1-hf27288f_0.conda#fa5eb01e989d7484fed5ca88997e892b
+https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.42-h2797004_0.conda#d67729828dc6ff7ba44a61062ad79880
+https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-4.25.1-hf27288f_1.conda#78ad06185133494138cd5e922ed73ac7
 https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2023.06.02-h7a70373_0.conda#c0e7eacd9694db3ef5ef2979a7deea70
 https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-h8917695_15.conda#20c3c14bc491f30daecaa6f73e2223ae
-https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.44.2-h2797004_0.conda#3b6a9f225c3dbe0d24f4fedd4625c5bf
+https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.45.1-h2797004_0.conda#fc4ccadfbf6d4784de88c41704792562
 https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.0-h0841786_0.conda#1f5a58e686b13bcfde88b93f547d23fe
 https://conda.anaconda.org/conda-forge/linux-64/libudunits2-2.2.28-h40f5838_3.conda#4bdace082e911a3e1f1f0b721bed5b56
 https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.15-h0b41bf4_0.conda#33277193f5b92bad9fdd230eb700929c
-https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.12.4-h232c23b_1.conda#53e951fab78d7e3bab40745f7b3d1620
+https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.12.5-h232c23b_0.conda#c442ebfda7a475f5e78f1c8e45f1e919
 https://conda.anaconda.org/conda-forge/linux-64/libzip-1.10.1-h2629f0a_3.conda#ac79812548e7e8cf61f7b0abdef01d3b
 https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.42-hcad00b1_0.conda#679c8961826aa4b50653bce17ee52abe
 https://conda.anaconda.org/conda-forge/linux-64/rdma-core-50.0-hd3aeb46_0.conda#4594b391274e38f07c668acb45285a1f
 https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda#47d31b792659ce70f470b5c82fdfb7a4
-https://conda.anaconda.org/conda-forge/linux-64/s2n-1.4.2-h06160fa_0.conda#cb0ddb996c2500eff369e80f48eac414
+https://conda.anaconda.org/conda-forge/linux-64/s2n-1.4.3-h06160fa_0.conda#860332295eef2ef9fef370f365ea78b4
 https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda#d453b98d9c83e71da0741bb0ff4d76bc
 https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.4-h7391055_0.conda#93ee23f12bc2e684548181256edd2cf6
 https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h59595ed_0.conda#8851084c192dbc56215ac4e3c9aa30fa
 https://conda.anaconda.org/conda-forge/linux-64/zlib-1.2.13-hd590300_5.conda#68c34ec6149623be41a1933ab996a209
 https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.5-hfc55251_0.conda#04b88013080254850d6c01ed54810589
-https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.14.3-hbde70e5_0.conda#5f2096c8888ab7278ab54ab55ede51bd
+https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.14.3-h3c8c088_1.conda#12af79204e13550614bc51bb380c32e5
 https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.5-h0f2a231_0.conda#009521b7ed97cca25f8f997f9e745976
 https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hd590300_1.conda#39f910d205726805a958da408ca194ba
 https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-h267a509_2.conda#9ae35c3d96db2c94ce0cef86efdfa2cb
 https://conda.anaconda.org/conda-forge/linux-64/gcc-12.3.0-h8d2909c_2.conda#e2f2f81f367e14ca1f77a870bda2fe59
 https://conda.anaconda.org/conda-forge/linux-64/gcc_linux-64-12.3.0-h76fc315_2.conda#11517e7b5c910c5b5d6985c0c7eb7f50
-https://conda.anaconda.org/conda-forge/linux-64/gfortran_impl_linux-64-12.3.0-hfcedea8_4.conda#467ff732cd0ca719838140e89d0cd4a0
-https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-12.3.0-he2b93b0_4.conda#4b010038c07cfb011ccc85101523ddea
+https://conda.anaconda.org/conda-forge/linux-64/gfortran_impl_linux-64-12.3.0-hfcedea8_5.conda#4d72ee7c82f8a9b2ecef4fcefa9acd19
+https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-12.3.0-he2b93b0_5.conda#cddba8fd94e52012abea1caad722b9c2
 https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.2-h659d440_0.conda#cd95826dbd331ed1be26bdf401432844
 https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.7.2-h2aa1ff5_1.conda#3bf887827d1968275978361a6e405e4f
 https://conda.anaconda.org/conda-forge/linux-64/libglib-2.78.3-h783c2da_0.conda#9bd06b12bbfa6fd1740fd23af4b0f0c7
@@ -134,10 +134,9 @@ https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.39-h76b75d6_0.conda#
 https://conda.anaconda.org/conda-forge/linux-64/minizip-4.0.4-h0ab5242_0.conda#813bc75d9c33ddd9c9d5b8d9c560e152
 https://conda.anaconda.org/conda-forge/linux-64/nss-3.97-h1d7d5a4_0.conda#b916d71a3032416e3f9136090d814472
 https://conda.anaconda.org/conda-forge/linux-64/orc-1.9.2-h7829240_1.conda#306ffb76ce3cdfc539d29fa5b8dd716c
-https://conda.anaconda.org/conda-forge/linux-64/pandoc-3.1.3-h32600fe_0.conda#8287aeb8462e2d4b235eff788e75919d
 https://conda.anaconda.org/conda-forge/linux-64/python-3.11.7-hab00c5b_1_cpython.conda#27cf681282c11dba7b0b1fd266e8f289
 https://conda.anaconda.org/conda-forge/linux-64/re2-2023.06.02-h2873b5e_0.conda#bb2d5e593ef13fe4aff0bc9440f945ae
-https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.44.2-h2c6b66d_0.conda#4f2892c672829693fd978d065db4e8be
+https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.45.1-h2c6b66d_0.conda#93acf31b379acebada263b9bce3dc6ed
 https://conda.anaconda.org/conda-forge/linux-64/ucx-1.15.0-h75e419f_3.conda#5baf4efbca923cdf73490c62cc7de1e2
 https://conda.anaconda.org/conda-forge/linux-64/udunits2-2.2.28-h40f5838_3.conda#6bb8deb138f87c9d48320ac21b87e7a1
 https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.7-h8ee46fc_0.conda#49e482d882669206653b095f5206c05b
@@ -151,7 +150,7 @@ https://conda.anaconda.org/conda-forge/linux-64/backports.zoneinfo-0.2.1-py311h3
 https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hd590300_1.conda#f27a24d46e3ea7b70a1f98e50c62508f
 https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py311hb755f60_1.conda#cce9e7c3f1c307f2a5fb08a2922d6164
 https://conda.anaconda.org/conda-forge/linux-64/c-compiler-1.7.0-hd590300_0.conda#fad1d0a651bf929c6c16fbf1f6ccfa7c
-https://conda.anaconda.org/conda-forge/noarch/certifi-2023.11.17-pyhd8ed1ab_0.conda#2011bcf45376341dd1d690263fdbc789
+https://conda.anaconda.org/conda-forge/noarch/certifi-2024.2.2-pyhd8ed1ab_0.conda#0876280e409658fc6f9e75d035960333
 https://conda.anaconda.org/conda-forge/noarch/cfgv-3.3.1-pyhd8ed1ab_0.tar.bz2#ebb5f5f7dc4f1a3780ef7ea7738db08c
 https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.3.2-pyhd8ed1ab_0.conda#7f4a9e3fcff3f6356ae99244a014da6a
 https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_0.conda#f3ad426304898027fc619827ff428eca
@@ -173,7 +172,7 @@ https://conda.anaconda.org/conda-forge/noarch/executing-2.0.1-pyhd8ed1ab_0.conda
 https://conda.anaconda.org/conda-forge/noarch/filelock-3.13.1-pyhd8ed1ab_0.conda#0c1729b74a8152fde6a38ba0a2ab9f45
 https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.14.2-h14ed4e7_0.conda#0f69b688f52ff6da70bccb7ff7001d1d
 https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h743c826_0.conda#12e6988845706b2cfbc3bc35c9a61a95
-https://conda.anaconda.org/conda-forge/noarch/fsspec-2023.12.2-pyhca7485f_0.conda#bf40f2a8835b78b1f91083d306b493d2
+https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.2.0-pyhca7485f_0.conda#fad86b90138cf5d82c6f5a2ed6e683d9
 https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.42.10-h829c605_4.conda#252a696860674caf7a855e16f680d63a
 https://conda.anaconda.org/conda-forge/noarch/geographiclib-1.52-pyhd8ed1ab_0.tar.bz2#6880e7100ebae550a33ce26663316d85
 https://conda.anaconda.org/conda-forge/linux-64/gfortran-12.3.0-h499e0f7_2.conda#0558a8c44eb7a18e6682bd3a8ae6dcab
@@ -192,12 +191,12 @@ https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.16-hb7c19ff_0.conda#51bb
 https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-21_linux64_openblas.conda#0ac9f44fc096772b0aa092119b00c3ca
 https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.5.0-hca28451_0.conda#7144d5a828e2cae218e0e3c98d8a0aeb
 https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.60.0-h74775cd_1.conda#e5dac7b919ed16dbcf9dc0f512cb68c0
-https://conda.anaconda.org/conda-forge/linux-64/libpq-16.1-h33b98f1_7.conda#675317e46167caea24542d85c72f19a3
+https://conda.anaconda.org/conda-forge/linux-64/libpq-16.2-h33b98f1_0.conda#fe0e297faf462ee579c95071a5211665
 https://conda.anaconda.org/conda-forge/linux-64/libwebp-1.3.2-h658648e_1.conda#0ebb65e8d86843865796c7c95a941f34
 https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2#91e27ef3d05cc772ce627e51cff111c4
 https://conda.anaconda.org/conda-forge/linux-64/lxml-5.1.0-py311h9691dec_0.conda#cee803b62c62e5f3326be31e57161ff5
 https://conda.anaconda.org/conda-forge/linux-64/lz4-4.3.3-py311h38e4bf4_0.conda#3910c815fc788621f88b2bdc0fa9f0a6
-https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.4-py311h459d7ec_0.conda#ee1b39a6ad0c74155593176ff3664f15
+https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.5-py311h459d7ec_0.conda#a322b4185121935c871d201ae00ac143
 https://conda.anaconda.org/conda-forge/noarch/mccabe-0.7.0-pyhd8ed1ab_0.tar.bz2#34fc335fc50eef0b5ea708f2b5f54e0c
 https://conda.anaconda.org/conda-forge/noarch/mistune-3.0.2-pyhd8ed1ab_0.conda#5cbee699846772cc939bef23a0d524ed
 https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.0.7-py311h9547e67_0.conda#3ac85c6c226e2a2e4b17864fc2ca88ff
@@ -226,7 +225,7 @@ https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2
 https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.19.1-pyhd8ed1ab_0.conda#4d3ceee3af4b0f9a1f48f57176bf8625
 https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2023.4-pyhd8ed1ab_0.conda#c79cacf8a06a51552fc651652f170208
 https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.4.1-py311h459d7ec_0.conda#60b5332b3989fda37884b92c7afd6a91
-https://conda.anaconda.org/conda-forge/noarch/pytz-2023.4-pyhd8ed1ab_0.conda#89445e229eb2d6605be88e0908afc912
+https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda#3eeeeb9e4827ace8c0c1419c85d590ad
 https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.1-py311h459d7ec_1.conda#52719a74ad130de8fb5d047dc91f247a
 https://conda.anaconda.org/conda-forge/linux-64/pyzmq-25.1.2-py311h34ded2d_0.conda#819aa640a0493d4b52faf938e94d129e
 https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.17.1-py311h46250e7_0.conda#a206e8c500a27fa82adae7c2f1929675
@@ -266,9 +265,9 @@ https://conda.anaconda.org/conda-forge/noarch/accessible-pygments-0.0.4-pyhd8ed1
 https://conda.anaconda.org/conda-forge/noarch/asgiref-3.7.2-pyhd8ed1ab_0.conda#596932155bf88bb6837141550cb721b0
 https://conda.anaconda.org/conda-forge/linux-64/astroid-2.15.8-py311h38be061_0.conda#46d70fcb74472aab178991f0231ee3c6
 https://conda.anaconda.org/conda-forge/noarch/asttokens-2.4.1-pyhd8ed1ab_0.conda#5f25798dcefd8252ce5f9dc494d5f571
-https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.7.14-h70caa3e_3.conda#bcf273598ba3626de3a2a33dfcc1498d
+https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.7.15-h70caa3e_0.conda#ac982c47c6439386e10afb4e0fe0d3e0
 https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.10.1-h0ef3971_3.conda#5f80f11865fad4cc684f1007170df6ec
-https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.11.0-h91d86a7_1.conda#0babb92c299af923d2cc98f88bbc6539
+https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.10.3-h91d86a7_1.conda#c05a913b8203d14b4a91c54d57b52282
 https://conda.anaconda.org/conda-forge/noarch/babel-2.14.0-pyhd8ed1ab_0.conda#9669586875baeced8fc30c0826c3270e
 https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.12.3-pyha770c72_0.conda#332493000404d8411859539a5a630865
 https://conda.anaconda.org/conda-forge/noarch/bleach-6.1.0-pyhd8ed1ab_0.conda#0ed9d7c0e9afa7c025807a9a8136ea3e
@@ -279,10 +278,10 @@ https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1-py_0.tar.bz2#4
 https://conda.anaconda.org/conda-forge/noarch/cligj-0.7.2-pyhd8ed1ab_1.tar.bz2#a29b7c141d6b2de4bb67788a5f107734
 https://conda.anaconda.org/conda-forge/linux-64/coverage-7.4.1-py311h459d7ec_0.conda#9caf3270065a2d40fd9a443ba1568e96
 https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-1.7.0-h00ab1b0_0.conda#b4537c98cb59f8725b0e1e65816b4a28
-https://conda.anaconda.org/conda-forge/linux-64/cytoolz-0.12.2-py311h459d7ec_1.conda#afe341dbe834ae76d2c23157ff00e633
+https://conda.anaconda.org/conda-forge/linux-64/cytoolz-0.12.3-py311h459d7ec_0.conda#13d385f635d7fbe9acc93600f67a6cb4
 https://conda.anaconda.org/conda-forge/noarch/docformatter-1.7.5-pyhd8ed1ab_0.conda#3a941b6083e945aa87e739a9b85c82e9
 https://conda.anaconda.org/conda-forge/noarch/fire-0.5.0-pyhd8ed1ab_0.conda#9fd22aae8d2f319e80f68b295ab91d64
-https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.47.2-py311h459d7ec_0.conda#166735a569ab42067b858ca0090a0d22
+https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.48.1-py311h459d7ec_0.conda#36363685b6e56682b1b256eb0ad503f6
 https://conda.anaconda.org/conda-forge/linux-64/fortran-compiler-1.7.0-heb67821_0.conda#7ef7c0f111dad1c8006504a0f1ccd820
 https://conda.anaconda.org/conda-forge/noarch/geopy-2.4.1-pyhd8ed1ab_0.conda#c75621ce68f6570fff9a6734cf21c9a7
 https://conda.anaconda.org/conda-forge/noarch/gitdb-4.0.11-pyhd8ed1ab_0.conda#623b19f616f2ca0c261441067e18ae40
@@ -294,7 +293,7 @@ https://conda.anaconda.org/conda-forge/noarch/isort-5.13.2-pyhd8ed1ab_0.conda#1d
 https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.1-pyhd8ed1ab_0.conda#81a3be0b2023e1ea8555781f0ad904a2
 https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.3-pyhd8ed1ab_0.conda#e7d8df6509ba635247ff9aea31134262
 https://conda.anaconda.org/conda-forge/linux-64/jupyter_core-5.7.1-py311h38be061_0.conda#175a430872841f7c351879f4c4c85b9e
-https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_0.conda#3f0915b1fb2252ab73686a533c5f9d3f
+https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_1.conda#afcd1b53bcac8844540358e33f33d28f
 https://conda.anaconda.org/conda-forge/noarch/latexcodec-2.0.1-pyh9f0ad1d_0.tar.bz2#8d67904973263afd2985ba56aa2d6bb4
 https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-21_linux64_openblas.conda#4a3816d06451c4946e2db26b86472cb6
 https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h119a65a_9.conda#cfebc557e54905dadc355c0e9f003004
@@ -308,8 +307,8 @@ https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.8.0-pyhd8ed1ab_0.conda#2
 https://conda.anaconda.org/conda-forge/noarch/partd-1.4.1-pyhd8ed1ab_0.conda#acf4b7c0bcd5fa3b0e05801c4d2accd6
 https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_0.conda#629f3203c99b32e0988910c93e77f3b6
 https://conda.anaconda.org/conda-forge/linux-64/pillow-10.2.0-py311ha6c5da5_0.conda#a5ccd7f2271f28b7d2de0b02b64e3796
-https://conda.anaconda.org/conda-forge/noarch/pip-23.3.2-pyhd8ed1ab_0.conda#8591c748f98dcc02253003533bc2e4b1
-https://conda.anaconda.org/conda-forge/linux-64/postgresql-16.1-h7387d8b_7.conda#563017467245a8a02671a5257ad9331e
+https://conda.anaconda.org/conda-forge/noarch/pip-24.0-pyhd8ed1ab_0.conda#f586ac1e56c8638b64f9c8122a7b8a67
+https://conda.anaconda.org/conda-forge/linux-64/postgresql-16.2-h7387d8b_0.conda#4e86738066b4966f0357f661b3691cae
 https://conda.anaconda.org/conda-forge/linux-64/proj-9.3.1-h1d62c97_0.conda#44ec51d0857d9be26158bb85caa74fdb
 https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.42-pyha770c72_0.conda#0bf64bf10eee21f46ac83c161917fa86
 https://conda.anaconda.org/conda-forge/noarch/pydocstyle-6.3.0-pyhd8ed1ab_0.conda#7e23a61a7fbaedfef6eb0e1ac775c8e5
@@ -323,13 +322,13 @@ https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.0-pyhd8ed1ab_0.conda#6
 https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.25.0-pyhd8ed1ab_0.conda#c119653cba436d8183c27bf6d190e587
 https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.2.5-hac6953d_0.conda#63b80ca78d29380fe69e69412dcbe4ac
 https://conda.anaconda.org/conda-forge/noarch/yamale-4.0.4-pyh6c4a22f_0.tar.bz2#cc9f59f147740d88679bf1bd94dbe588
-https://conda.anaconda.org/conda-forge/noarch/yamllint-1.33.0-pyhd8ed1ab_0.conda#57d32eb2c4b76ef288f9dd789f8fe5af
-https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.5.0-hb337f33_1.conda#5a79a179185e87891d43530a49fea2c6
-https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.5.0-h94269e2_3.conda#2862966bef50f0f91f528f23b1270e52
+https://conda.anaconda.org/conda-forge/noarch/yamllint-1.34.0-pyhd8ed1ab_0.conda#262273faaed1e4bdf0c61209c38abd15
+https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.5.0-h1b46bed_2.conda#cbbdaaec72d302636a64a3fcaa3a72c7
+https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.5.0-hb858b4b_2.conda#19f23b45d1925a9a8f701a3f6f9cce4f
 https://conda.anaconda.org/conda-forge/noarch/cattrs-23.2.3-pyhd8ed1ab_0.conda#91fc4700dcce4a46d439900a132fe4e5
 https://conda.anaconda.org/conda-forge/linux-64/compilers-1.7.0-ha770c72_0.conda#81458b3aed8ab8711951ec3c0c04e097
 https://conda.anaconda.org/conda-forge/linux-64/cryptography-42.0.2-py311hcb13ee4_0.conda#c61fd9e9fcfa599ea5a8b1de42b147a8
-https://conda.anaconda.org/conda-forge/noarch/django-5.0.1-pyhd8ed1ab_0.conda#1e9684296d2b6082dc5c436af3713ad1
+https://conda.anaconda.org/conda-forge/noarch/django-5.0.2-pyhd8ed1ab_0.conda#596031b6473e1fead388589348472748
 https://conda.anaconda.org/conda-forge/noarch/flake8-5.0.4-pyhd8ed1ab_0.tar.bz2#8079ea7dec0a917dd0cb6c257f7ea9ea
 https://conda.anaconda.org/conda-forge/linux-64/geotiff-1.7.1-h6b2125f_15.conda#218a726155bd9ae1787b26054eed8566
 https://conda.anaconda.org/conda-forge/noarch/gitpython-3.1.41-pyhd8ed1ab_0.conda#84874a90c312088f7b5e63402fc44a58
@@ -339,14 +338,14 @@ https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2023.12.
 https://conda.anaconda.org/conda-forge/linux-64/kealib-1.5.3-h2f55d51_0.conda#f7e7077802927590efc8bf7328208f12
 https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.2-nompi_h9612171_113.conda#b2414908e43c442ddc68e6148774a304
 https://conda.anaconda.org/conda-forge/linux-64/libspatialite-5.1.0-h7bd4643_4.conda#127d36f9ee392fa81b45e81867ce30ab
-https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.3-py311h64a7726_0.conda#231eef4f33640338f64ef9ab690ba08d
-https://conda.anaconda.org/conda-forge/linux-64/poppler-23.12.0-h590f24d_0.conda#480189ac126a8c6c61e14476c8ba7c9a
+https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py311h64a7726_0.conda#a502d7aad449a1206efb366d6a12c52d
+https://conda.anaconda.org/conda-forge/linux-64/poppler-24.02.0-h590f24d_0.conda#7e715c1572de09d6106c5a31fa70ffca
 https://conda.anaconda.org/conda-forge/noarch/pybtex-0.24.0-pyhd8ed1ab_2.tar.bz2#2099b86a7399c44c0c61cdb6de6915ba
 https://conda.anaconda.org/conda-forge/noarch/pylint-2.17.7-pyhd8ed1ab_0.conda#3cab6aee60038b3f621bce3e50f52bed
 https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.6.1-py311hca0b8b9_5.conda#cac429fcb9126d5e6f02c8ba61c2a811
 https://conda.anaconda.org/conda-forge/noarch/pytest-cov-4.1.0-pyhd8ed1ab_0.conda#06eb685a3a0b146347a58dda979485da
 https://conda.anaconda.org/conda-forge/noarch/pytest-env-1.1.3-pyhd8ed1ab_0.conda#1dbdf019d740419852c4a7803fff49d9
-https://conda.anaconda.org/conda-forge/noarch/pytest-metadata-3.0.0-pyhd8ed1ab_2.conda#54055dbe631506cf58fb32c41b0b2cbf
+https://conda.anaconda.org/conda-forge/noarch/pytest-metadata-3.1.0-pyhd8ed1ab_0.conda#d87474b01a3e2b8e919a24b922463056
 https://conda.anaconda.org/conda-forge/noarch/pytest-mock-3.12.0-pyhd8ed1ab_0.conda#ac9fedc9a0c397f2318e82525491dd83
 https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.5.0-pyhd8ed1ab_0.conda#d5f595da2daead898ca958ac62f0307b
 https://conda.anaconda.org/conda-forge/noarch/rdflib-7.0.0-pyhd8ed1ab_0.conda#44d14ef95495b3d4438f28998e0296a9
@@ -356,14 +355,14 @@ https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.2-pyhd8ed1ab_0.cond
 https://conda.anaconda.org/conda-forge/noarch/types-requests-2.31.0.20240125-pyhd8ed1ab_0.conda#0200f9f9bb98c12a172ae49ef7d83d60
 https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py311h9547e67_4.conda#586da7df03b68640de14dc3e8bcbf76f
 https://conda.anaconda.org/conda-forge/noarch/yapf-0.40.1-pyhd8ed1ab_0.conda#f269942e802d5e148632143d4c37acc9
-https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.26.1-h0637f07_8.conda#dcf0d0d5d3522dd8ed1081d6fca9cca8
+https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.26.1-h33f84b2_9.conda#feff02dd51629ad703677c28eaf03a1e
 https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.10.0-h00ab1b0_0.conda#64eec459779f01803594f5272cdde23c
 https://conda.anaconda.org/conda-forge/linux-64/cftime-1.6.3-py311h1f0f07a_0.conda#b7e6d52b39e199238c3400cafaabafb3
 https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.2.0-py311h9547e67_0.conda#40828c5b36ef52433e21f89943e09f33
-https://conda.anaconda.org/conda-forge/noarch/dask-core-2024.1.1-pyhd8ed1ab_0.conda#1a92a5bd77b2430796696e25c3d8dbcb
+https://conda.anaconda.org/conda-forge/noarch/dask-core-2024.2.0-pyhd8ed1ab_0.conda#5973bc565e2aea620c3a431cafdde032
 https://conda.anaconda.org/conda-forge/noarch/flake8-polyfill-1.0.2-py_0.tar.bz2#a53db35e3d07f0af2eccd59c2a00bffe
-https://conda.anaconda.org/conda-forge/noarch/identify-2.5.33-pyhd8ed1ab_0.conda#93c8f8ceb83827d88deeba796f07fba7
-https://conda.anaconda.org/conda-forge/noarch/ipython-8.20.0-pyh707e725_0.conda#c2d2d7453560c80a2138c354610c08ba
+https://conda.anaconda.org/conda-forge/noarch/identify-2.5.34-pyhd8ed1ab_0.conda#048ba98aa7b16ef0d8866f8c87d7c5b8
+https://conda.anaconda.org/conda-forge/noarch/ipython-8.21.0-pyh707e725_0.conda#371344fdbdf9c70cfe9adb512a8cbca6
 https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.21.1-pyhd8ed1ab_0.conda#8a3a3d01629da20befa340919e3dd2c4
 https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.0-pyhd8ed1ab_0.conda#6bd3f1069cdebb44c7ae9efb900e312d
 https://conda.anaconda.org/conda-forge/linux-64/netcdf-fortran-4.6.1-nompi_hacb5139_103.conda#50f05f98d084805642d24dff910e11e8
@@ -379,7 +378,7 @@ https://conda.anaconda.org/conda-forge/linux-64/shapely-2.0.2-py311h2032efe_1.co
 https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.242-h65f022c_0.conda#09b53fbd76044de441d25261840821ac
 https://conda.anaconda.org/conda-forge/noarch/bokeh-3.3.4-pyhd8ed1ab_0.conda#6cc92bba68b7bb5a3b180e96508f9480
 https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.2.0-py311h1f0f07a_4.conda#1e105c1a8ea2163507726144b401eb1b
-https://conda.anaconda.org/conda-forge/noarch/distributed-2024.1.1-pyhd8ed1ab_0.conda#81039f39690f341dcb0a68bf62e812be
+https://conda.anaconda.org/conda-forge/noarch/distributed-2024.2.0-pyhd8ed1ab_0.conda#81c14e12f44f94613fe5922403e32341
 https://conda.anaconda.org/conda-forge/linux-64/esmf-8.4.2-nompi_h9e768e6_3.conda#c330e87e698bae8e7381c0315cf25dd0
 https://conda.anaconda.org/conda-forge/linux-64/gtk2-2.24.33-h7f000aa_3.conda#0abfa7f9241a0f4fd732bc15773cfb0c
 https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.56.3-he3f83f7_1.conda#03bd1ddcc942867a19528877143b9852
@@ -388,7 +387,7 @@ https://conda.anaconda.org/conda-forge/noarch/myproxyclient-2.1.1-pyhd8ed1ab_0.c
 https://conda.anaconda.org/conda-forge/noarch/nbformat-5.9.2-pyhd8ed1ab_0.conda#61ba076de6530d9301a0053b02f093d2
 https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.6.5-nompi_py311he8ad708_100.conda#597b1ad6cb7011b7561c20ea30295cae
 https://conda.anaconda.org/conda-forge/noarch/pep8-naming-0.10.0-pyh9f0ad1d_0.tar.bz2#b3c5536e4f9f58a4b16adb6f1e11732d
-https://conda.anaconda.org/conda-forge/noarch/pre-commit-3.6.0-pyha770c72_0.conda#473a7cfca197da0a10cff3f6dded7d4b
+https://conda.anaconda.org/conda-forge/noarch/pre-commit-3.6.1-pyha770c72_0.conda#4efd2c755bf2079e5651e57e1999db6c
 https://conda.anaconda.org/conda-forge/noarch/pylint-celery-0.3-py_1.tar.bz2#e29456a611a62d3f26105a2f9c68f759
 https://conda.anaconda.org/conda-forge/noarch/pylint-django-2.5.3-pyhd8ed1ab_0.tar.bz2#00d8853fb1f87195722ea6a582cc9b56
 https://conda.anaconda.org/conda-forge/noarch/pylint-flask-0.6-py_0.tar.bz2#5a9afd3d0a61b08d59eed70fab859c1b
@@ -396,35 +395,35 @@ https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.3.0-py311h1f0f
 https://conda.anaconda.org/conda-forge/linux-64/tiledb-2.19.1-h4386cac_0.conda#8d16e7b2529607a12aa6722c7a7c7356
 https://conda.anaconda.org/conda-forge/noarch/xarray-2024.1.1-pyhd8ed1ab_0.conda#38b5de3877d1f28089b231d24622dd64
 https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.22.0-py311h320fe9a_1.conda#10d1806e20da040c58c36deddf51c70c
-https://conda.anaconda.org/conda-forge/noarch/cf_xarray-0.8.8-pyhd8ed1ab_0.conda#9ad9310f388ea88e23d6c945956de172
+https://conda.anaconda.org/conda-forge/noarch/cf_xarray-0.8.9-pyhd8ed1ab_0.conda#f9f82c2c3d1b3588e8ab34976f98af91
 https://conda.anaconda.org/conda-forge/noarch/dask-jobqueue-0.8.2-pyhd8ed1ab_0.conda#cc344a296a41369bcb05f7216661cec8
 https://conda.anaconda.org/conda-forge/noarch/esgf-pyclient-0.3.1-pyhca7485f_3.conda#1d43833138d38ad8324700ce45a7099a
 https://conda.anaconda.org/conda-forge/noarch/esmpy-8.4.2-pyhc1e730c_4.conda#ddcf387719b2e44df0cc4dd467643951
 https://conda.anaconda.org/conda-forge/linux-64/graphviz-9.0.0-h78e8752_1.conda#a3f4cd4a512ec5db35ffbf25ba11f537
 https://conda.anaconda.org/conda-forge/linux-64/libarrow-15.0.0-he2c5238_2_cpu.conda#cd7cd1c21dc42befdbb44b5afe2cd048
-https://conda.anaconda.org/conda-forge/linux-64/libgdal-3.8.3-hcd1fc54_0.conda#ef5ae0528509a7987cf29e8827f46938
+https://conda.anaconda.org/conda-forge/linux-64/libgdal-3.8.3-h80d7d79_2.conda#b0c8bd68d3119c8e3a355ff9417ceff1
 https://conda.anaconda.org/conda-forge/noarch/nbclient-0.8.0-pyhd8ed1ab_0.conda#e78da91cf428faaf05701ce8cc8f2f9b
 https://conda.anaconda.org/conda-forge/noarch/nc-time-axis-1.4.1-pyhd8ed1ab_0.tar.bz2#281b58948bf60a2582de9e548bcc5369
 https://conda.anaconda.org/conda-forge/noarch/prospector-1.10.3-pyhd8ed1ab_0.conda#f551d4d859a1d70c6abff8310a655481
-https://conda.anaconda.org/conda-forge/linux-64/gdal-3.8.3-py311h39b4e0e_0.conda#a0119e816e54d16b8403bbe4594e8904
+https://conda.anaconda.org/conda-forge/linux-64/gdal-3.8.3-py311h8be719e_2.conda#4b587b3a15d00f6ec7fdecfe2d805140
 https://conda.anaconda.org/conda-forge/noarch/iris-3.7.0-pyha770c72_0.conda#dccc1f660bf455c239adaabf56b91dc9
 https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-15.0.0-h59595ed_2_cpu.conda#85d3e05ea2b427e879e486f09fb8cf54
 https://conda.anaconda.org/conda-forge/linux-64/libarrow-flight-15.0.0-hdc44a87_2_cpu.conda#41f4c79b79d6c13ffb7abc71ab4f0c54
 https://conda.anaconda.org/conda-forge/linux-64/libarrow-gandiva-15.0.0-hacb8726_2_cpu.conda#d8415318348d02dad131144d6fc151ec
 https://conda.anaconda.org/conda-forge/linux-64/libparquet-15.0.0-h352af49_2_cpu.conda#8d99909e413b67872996d46093dda024
-https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.14.2-pyhd8ed1ab_0.conda#631800aa8cc7ccf61e70087355d95827
+https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.0-pyhd8ed1ab_0.conda#de2255e7a38fad6eaf457739c6599413
 https://conda.anaconda.org/conda-forge/noarch/py-cordex-0.6.6-pyhd8ed1ab_0.conda#255f9eac03143526c8aed41d1d091c63
 https://conda.anaconda.org/conda-forge/linux-64/pydot-2.0.0-py311h38be061_0.conda#cdfd23a54a18f3c8d5320d7717f4ed52
 https://conda.anaconda.org/conda-forge/linux-64/fiona-1.9.5-py311hf8e0aa6_3.conda#a5277325e005e9d014eca37187b3f4a2
 https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-15.0.0-h59595ed_2_cpu.conda#3cda69f7af9b2341e3ee0fb602861726
 https://conda.anaconda.org/conda-forge/linux-64/libarrow-flight-sql-15.0.0-hfbc7f12_2_cpu.conda#d779a1334ac200d6e9c04ee6bfa2a6af
-https://conda.anaconda.org/conda-forge/noarch/nbconvert-pandoc-7.14.2-pyhd8ed1ab_0.conda#27bb156a0e59f9c3b656e3e289475fbd
+https://conda.anaconda.org/conda-forge/noarch/nbconvert-pandoc-7.16.0-pyhd8ed1ab_0.conda#28dde45c295b3f110bc6bb425472137b
 https://conda.anaconda.org/conda-forge/noarch/prov-2.0.0-pyhd3deb0d_0.tar.bz2#aa9b3ad140f6c0668c646f32e20ccf82
 https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-15.0.0-hfbc7f12_2_cpu.conda#21de7bd8fd4568ccb232bc7bfbf3d112
-https://conda.anaconda.org/conda-forge/noarch/nbconvert-7.14.2-pyhd8ed1ab_0.conda#0ac429dbe14800f8cb97b892b107bc85
+https://conda.anaconda.org/conda-forge/noarch/nbconvert-7.16.0-pyhd8ed1ab_0.conda#342ba1099325da21a811e80397006461
 https://conda.anaconda.org/conda-forge/linux-64/pyarrow-15.0.0-py311h39c9aba_2_cpu.conda#6c70dd512802dfc4114b2df223a883bd
 https://conda.anaconda.org/conda-forge/noarch/pyarrow-hotfix-0.6-pyhd8ed1ab_0.conda#ccc06e6ef2064ae129fab3286299abda
-https://conda.anaconda.org/conda-forge/noarch/dask-2024.1.1-pyhd8ed1ab_0.conda#c319bdbcfa9952e246f9d35e7c13a927
+https://conda.anaconda.org/conda-forge/noarch/dask-2024.2.0-pyhd8ed1ab_0.conda#085d464298ca31a98193af99ee5e75e7
 https://conda.anaconda.org/conda-forge/noarch/iris-esmf-regrid-0.9.0-pyhd8ed1ab_0.conda#570f2c6e387fd6dac5356a5152f91b3f
 https://conda.anaconda.org/conda-forge/noarch/autodocsumm-0.2.6-pyhd8ed1ab_0.tar.bz2#4409dd7e06a62c3b2aa9e96782c49c6d
 https://conda.anaconda.org/conda-forge/noarch/nbsphinx-0.9.3-pyhd8ed1ab_0.conda#0dbaa7d08d3d79b2a1a4dd6a02cc4581
diff --git a/doc/quickstart/configure.rst b/doc/quickstart/configure.rst
index c14bcb6ea..fadd145b2 100644
--- a/doc/quickstart/configure.rst
+++ b/doc/quickstart/configure.rst
@@ -382,8 +382,8 @@ corresponding command line arguments ``--search_esgf=when_missing`` or
    tool by pressing the ``Ctrl`` and ``C`` keys on your keyboard simultaneously
    several times, edit the recipe so it contains fewer datasets and try again.
 
-For downloading some files (e.g. those produced by the CORDEX project),
-you need to log in to be able to download the data.
+For downloading some files, you may need to log in to be able to download the
+data.
 
 See the
 `ESGF user guide <https://esgf.github.io/esgf-user-support/user_guide.html>`_
diff --git a/esmvalcore/_recipe/check.py b/esmvalcore/_recipe/check.py
index 9b4b2abb3..1358b27d8 100644
--- a/esmvalcore/_recipe/check.py
+++ b/esmvalcore/_recipe/check.py
@@ -70,7 +70,7 @@ def diagnostics(diags):
     for name, diagnostic in diags.items():
         if 'scripts' not in diagnostic:
             raise RecipeError(
-                "Missing scripts section in diagnostic {}".format(name))
+                f"Missing scripts section in diagnostic '{name}'.")
         variable_names = tuple(diagnostic.get('variables', {}))
         scripts = diagnostic.get('scripts')
         if scripts is None:
@@ -78,13 +78,13 @@ def diagnostics(diags):
         for script_name, script in scripts.items():
             if script_name in variable_names:
                 raise RecipeError(
-                    "Invalid script name {} encountered in diagnostic {}: "
-                    "scripts cannot have the same name as variables.".format(
-                        script_name, name))
+                    f"Invalid script name '{script_name}' encountered "
+                    f"in diagnostic '{name}': scripts cannot have the "
+                    "same name as variables.")
             if not script.get('script'):
                 raise RecipeError(
-                    "No script defined for script {} in diagnostic {}".format(
-                        script_name, name))
+                    f"No script defined for script '{script_name}' in "
+                    f"diagnostic '{name}'.")
 
 
 def duplicate_datasets(
@@ -95,27 +95,31 @@ def duplicate_datasets(
     """Check for duplicate datasets."""
     if not datasets:
         raise RecipeError(
-            "You have not specified any dataset or additional_dataset groups "
-            f"for variable {variable_group} in diagnostic {diagnostic}.")
+            "You have not specified any dataset or additional_dataset "
+            f"groups for variable '{variable_group}' in diagnostic "
+            f"'{diagnostic}'.")
     checked_datasets_ = []
     for dataset in datasets:
         if dataset in checked_datasets_:
             raise RecipeError(
-                f"Duplicate dataset {dataset} for variable {variable_group} "
-                f"in diagnostic {diagnostic}.")
+                f"Duplicate dataset\n{pformat(dataset)}\nfor variable "
+                f"'{variable_group}' in diagnostic '{diagnostic}'.")
         checked_datasets_.append(dataset)
 
 
-def variable(var: dict[str, Any], required_keys: Iterable[str]):
+def variable(
+    var: dict[str, Any],
+    required_keys: Iterable[str],
+    diagnostic: str,
+    variable_group: str
+) -> None:
     """Check variables as derived from recipe."""
     required = set(required_keys)
     missing = required - set(var)
     if missing:
         raise RecipeError(
-            f"Missing keys {missing} in\n"
-            f"{pformat(var)}\n"
-            "for variable {var['variable_group']} in diagnostic "
-            f"{var['diagnostic']}")
+            f"Missing keys {missing} in\n{pformat(var)}\nfor variable "
+            f"'{variable_group}' in diagnostic '{diagnostic}'.")
 
 
 def _log_data_availability_errors(dataset):
diff --git a/esmvalcore/_recipe/recipe_schema.yml b/esmvalcore/_recipe/recipe_schema.yml
index ed61c4ad4..dc254a0d5 100644
--- a/esmvalcore/_recipe/recipe_schema.yml
+++ b/esmvalcore/_recipe/recipe_schema.yml
@@ -46,7 +46,7 @@ variable:
 # TODO: add preprocessor item
 
 diagnostic:
-  scripts: any(null(), map(include('script')))
+  scripts: any(null(), map(include('script')), required=False)
   additional_datasets: list(include('dataset'), required=False)
   title: str(required=False)
   description: str(required=False)
@@ -55,4 +55,4 @@ diagnostic:
   variables: map(include('variable'), null(), required=False)
 
 script:
-  script: str()
+  script: str(required=False)
diff --git a/esmvalcore/_recipe/to_datasets.py b/esmvalcore/_recipe/to_datasets.py
index 91ebed117..fd286ea81 100644
--- a/esmvalcore/_recipe/to_datasets.py
+++ b/esmvalcore/_recipe/to_datasets.py
@@ -244,6 +244,7 @@ def _get_dataset_facets_from_recipe(
     recipe_variable: dict[str, Any],
     recipe_dataset: dict[str, Any],
     profiles: dict[str, Any],
+    diagnostic_name: str,
     session: Session,
 ) -> tuple[Facets, list[Facets]]:
     """Read the facets for a single dataset definition from the recipe."""
@@ -286,6 +287,8 @@ def _get_dataset_facets_from_recipe(
             'dataset',
             'project',
         ),
+        diagnostic=diagnostic_name,
+        variable_group=variable_group
     )
 
     preprocessor = facets.get('preprocessor', 'default')
@@ -329,6 +332,7 @@ def _get_facets_from_recipe(
             recipe_variable=recipe_variable,
             recipe_dataset=recipe_dataset,
             profiles=profiles,
+            diagnostic_name=diagnostic_name,
             session=session,
         )
 
diff --git a/esmvalcore/cmor/_fixes/cmip6/ciesm.py b/esmvalcore/cmor/_fixes/cmip6/ciesm.py
index c169db6ed..c344c1b61 100644
--- a/esmvalcore/cmor/_fixes/cmip6/ciesm.py
+++ b/esmvalcore/cmor/_fixes/cmip6/ciesm.py
@@ -20,9 +20,32 @@ class Cl(ClFixHybridPressureCoord):
         -------
         iris.cube.Cube
         """
-        metadata = cube.metadata
-        cube *= 100
-        cube.metadata = metadata
+        if cube.core_data().max() <= 1.0:
+            cube.units = '1'
+            cube.convert_units('%')
+        return cube
+
+
+class Clt(Fix):
+    """Fixes for clt."""
+
+    def fix_data(self, cube):
+        """Fix data.
+
+        Fixes discrepancy between declared units and real units.
+
+        Parameters
+        ----------
+        cube: iris.cube.Cube
+            Input cube.
+
+        Returns
+        -------
+        iris.cube.Cube
+        """
+        if cube.core_data().max() <= 1.0:
+            cube.units = '1'
+            cube.convert_units('%')
         return cube
 
 
diff --git a/esmvalcore/cmor/_fixes/cmip6/fio_esm_2_0.py b/esmvalcore/cmor/_fixes/cmip6/fio_esm_2_0.py
index 748e181e0..d6462ad92 100644
--- a/esmvalcore/cmor/_fixes/cmip6/fio_esm_2_0.py
+++ b/esmvalcore/cmor/_fixes/cmip6/fio_esm_2_0.py
@@ -70,3 +70,26 @@ class Amon(Fix):
                     longitude.bounds = None
                     longitude.guess_bounds()
         return cubes
+
+
+class Clt(Fix):
+    """Fixes for clt."""
+
+    def fix_data(self, cube):
+        """Fix data.
+
+        Fixes discrepancy between declared units and real units.
+
+        Parameters
+        ----------
+        cube: iris.cube.Cube
+            Input cube.
+
+        Returns
+        -------
+        iris.cube.Cube
+        """
+        if cube.core_data().max() <= 1.0:
+            cube.units = '1'
+            cube.convert_units('%')
+        return cube
diff --git a/esmvalcore/config/_esgf_pyclient.py b/esmvalcore/config/_esgf_pyclient.py
index 1d473068e..1dfcd38ed 100644
--- a/esmvalcore/config/_esgf_pyclient.py
+++ b/esmvalcore/config/_esgf_pyclient.py
@@ -12,7 +12,6 @@ import importlib
 import logging
 import os
 import stat
-import textwrap
 from functools import lru_cache
 from pathlib import Path
 from types import ModuleType
@@ -30,59 +29,6 @@ logger = logging.getLogger(__name__)
 
 CONFIG_FILE = Path.home() / '.esmvaltool' / 'esgf-pyclient.yml'
 
-INSTRUCTIONS = textwrap.dedent("""
-ESGF credentials missing, only data that is accessible without
-logging in will be available.
-
-See https://esgf.github.io/esgf-user-support/user_guide.html
-for instructions on how to create an account if you do not have
-one yet.
-
-Next, configure your system so esmvaltool can use your
-credentials. This can be done using the keyring package, or
-you can just enter them in {cfg_file}.
-
-keyring
-=======
-First install the keyring package (requires a supported
-backend, see https://pypi.org/project/keyring/):
-$ pip install keyring
-
-Next, set your username and password by running the commands:
-$ keyring set ESGF hostname
-$ keyring set ESGF username
-$ keyring set ESGF password
-
-To check that you entered your credentials correctly, run:
-$ keyring get ESGF hostname
-$ keyring get ESGF username
-$ keyring get ESGF password
-
-configuration file
-==================
-You can store the hostname, username, and password or your OpenID
-account in a plain text in the file {cfg_file} like this:
-
-logon:
-  hostname: "your-hostname"
-  username: "your-username"
-  password: "your-password"
-
-or your can configure an interactive log in:
-
-logon:
-  interactive: true
-
-Note that storing your password in plain text in the configuration
-file is less secure. On shared systems, make sure the permissions
-of the file are set so only you can read it, i.e.
-
-$ ls -l {cfg_file}
-
-shows permissions -rw-------.
-
-""".format(cfg_file=CONFIG_FILE))
-
 
 def get_keyring_credentials():
     """Load credentials from keyring."""
@@ -172,14 +118,6 @@ def load_esgf_pyclient_config():
         cfg['search_connection']['cache'] = cache_file
         Path(cache_file).parent.mkdir(parents=True, exist_ok=True)
 
-    missing_credentials = []
-    for key in ['hostname', 'username', 'password']:
-        if key not in cfg['logon']:
-            missing_credentials.append(key)
-
-    if missing_credentials and not cfg['logon'].get('interactive'):
-        logger.warning(INSTRUCTIONS)
-
     return cfg
 
 


Test patch:
diff --git a/tests/integration/cmor/_fixes/cmip6/test_ciesm.py b/tests/integration/cmor/_fixes/cmip6/test_ciesm.py
index 1cf1aa3aa..53fc57723 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_ciesm.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_ciesm.py
@@ -1,5 +1,6 @@
 """Tests for the fixes of CIESM."""
 import iris.cube
+import numpy as np
 import pytest
 
 from esmvalcore.cmor._fixes.cmip6.ciesm import Cl
@@ -38,8 +39,17 @@ def test_cl_fix_data(cl_cube):
     assert out_cube.data == [100.0]
 
 
+def test_clt_fix():
+    """Test `Clt.fix_data`."""
+    cube = iris.cube.Cube(0.5)
+    fix = Fix.get_fixes('CMIP6', 'CIESM', 'Amon', 'clt')[0]
+    out_cube = fix.fix_data(cube)
+    np.testing.assert_allclose(out_cube.data, 50.0)
+    assert out_cube.units == '%'
+
+
 def test_pr_fix():
-    """Test `Pr.fix_metadata`."""
+    """Test `Pr.fix_data`."""
     cube = iris.cube.Cube(
         [2.82e-08],
         var_name='pr',
diff --git a/tests/integration/cmor/_fixes/cmip6/test_fio_esm_2_0.py b/tests/integration/cmor/_fixes/cmip6/test_fio_esm_2_0.py
index 4ac1d05d4..2bf811e45 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_fio_esm_2_0.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_fio_esm_2_0.py
@@ -11,6 +11,15 @@ from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor.table import get_var_info
 
 
+def test_clt_fix():
+    """Test `Clt.fix_data`."""
+    cube = iris.cube.Cube(0.5)
+    fix = Fix.get_fixes('CMIP6', 'FIO-ESM-2-0', 'Amon', 'clt')[0]
+    out_cube = fix.fix_data(cube)
+    np.testing.assert_allclose(out_cube.data, 50.0)
+    assert out_cube.units == '%'
+
+
 def test_get_tas_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'FIO-ESM-2-0', 'Amon', 'tas')
diff --git a/tests/integration/recipe/test_recipe.py b/tests/integration/recipe/test_recipe.py
index f8c6c3535..b3ec08126 100644
--- a/tests/integration/recipe/test_recipe.py
+++ b/tests/integration/recipe/test_recipe.py
@@ -165,6 +165,80 @@ def get_recipe(tempdir: Path, content: str, session: Session):
     return recipe
 
 
+def test_recipe_missing_scripts(tmp_path, session):
+    content = dedent("""
+        datasets:
+          - dataset: bcc-csm1-1
+
+        diagnostics:
+          diagnostic_name:
+            variables:
+              ta:
+                project: CMIP5
+                mip: Amon
+                exp: historical
+                ensemble: r1i1p1
+                timerange: 1999/2002
+        """)
+    exc_message = ("Missing scripts section in diagnostic 'diagnostic_name'.")
+    with pytest.raises(RecipeError) as exc:
+        get_recipe(tmp_path, content, session)
+    assert str(exc.value) == exc_message
+
+
+def test_recipe_duplicate_var_script_name(tmp_path, session):
+    content = dedent("""
+        datasets:
+          - dataset: bcc-csm1-1
+
+        diagnostics:
+          diagnostic_name:
+            variables:
+              ta:
+                project: CMIP5
+                mip: Amon
+                exp: historical
+                ensemble: r1i1p1
+                start_year: 1999
+                end_year: 2002
+            scripts:
+              ta:
+                script: tmp_path / 'diagnostic.py'
+        """)
+    exc_message = ("Invalid script name 'ta' encountered in diagnostic "
+                   "'diagnostic_name': scripts cannot have the same "
+                   "name as variables.")
+    with pytest.raises(RecipeError) as exc:
+        get_recipe(tmp_path, content, session)
+    assert str(exc.value) == exc_message
+
+
+def test_recipe_no_script(tmp_path, session):
+    content = dedent("""
+        datasets:
+          - dataset: bcc-csm1-1
+
+        diagnostics:
+          diagnostic_name:
+            variables:
+              ta:
+                project: CMIP5
+                mip: Amon
+                exp: historical
+                ensemble: r1i1p1
+                start_year: 1999
+                end_year: 2002
+            scripts:
+              script_name:
+                argument: 1
+        """)
+    exc_message = ("No script defined for script 'script_name' in "
+                   "diagnostic 'diagnostic_name'.")
+    with pytest.raises(RecipeError) as exc:
+        get_recipe(tmp_path, content, session)
+    assert str(exc.value) == exc_message
+
+
 def test_recipe_no_datasets(tmp_path, session):
     content = dedent("""
         diagnostics:
@@ -181,7 +255,56 @@ def test_recipe_no_datasets(tmp_path, session):
         """)
     exc_message = ("You have not specified any dataset "
                    "or additional_dataset groups for variable "
-                   "ta in diagnostic diagnostic_name.")
+                   "'ta' in diagnostic 'diagnostic_name'.")
+    with pytest.raises(RecipeError) as exc:
+        get_recipe(tmp_path, content, session)
+    assert str(exc.value) == exc_message
+
+
+def test_recipe_duplicated_datasets(tmp_path, session):
+    content = dedent("""
+        datasets:
+          - dataset: bcc-csm1-1
+          - dataset: bcc-csm1-1
+
+        diagnostics:
+          diagnostic_name:
+            variables:
+              ta:
+                project: CMIP5
+                mip: Amon
+                exp: historical
+                ensemble: r1i1p1
+                timerange: 1999/2002
+            scripts: null
+        """)
+    exc_message = ("Duplicate dataset\n{'dataset': 'bcc-csm1-1'}\n"
+                   "for variable 'ta' in diagnostic 'diagnostic_name'.")
+    with pytest.raises(RecipeError) as exc:
+        get_recipe(tmp_path, content, session)
+    assert str(exc.value) == exc_message
+
+
+def test_recipe_var_missing_args(tmp_path, session):
+    content = dedent("""
+        datasets:
+          - dataset: bcc-csm1-1
+
+        diagnostics:
+          diagnostic_name:
+            variables:
+              ta:
+                project: CMIP5
+                exp: historical
+                ensemble: r1i1p1
+                timerange: 1999/2002
+            scripts: null
+        """)
+    exc_message = ("Missing keys {'mip'} in\n{'dataset': 'bcc-csm1-1',"
+                   "\n 'ensemble': 'r1i1p1',\n 'exp': 'historical',\n"
+                   " 'project': 'CMIP5',\n 'short_name': 'ta',\n "
+                   "'timerange': '1999/2002'}\nfor variable 'ta' "
+                   "in diagnostic 'diagnostic_name'.")
     with pytest.raises(RecipeError) as exc:
         get_recipe(tmp_path, content, session)
     assert str(exc.value) == exc_message