diff --git a/.zenodo.json b/.zenodo.json index 6cadfcf3f0..38218f7ed5 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -452,6 +452,10 @@ "affiliation": "ACCESS-NRI, Australia", "name": "Chun, Felicity", "orcid": "0009-0007-0845-0953" + }, + { + "affiliation": "Met Office, UK", + "name": "Ellis, Hannah" } ], "description": "ESMValTool: A community diagnostic and performance metrics tool for routine evaluation of Earth system models in CMIP.", diff --git a/CITATION.cff b/CITATION.cff index 2640cce4b6..9fa2857ec4 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -448,6 +448,11 @@ authors: family-names: Chun given-names: Felicity orcid: "https://orcid.org/0009-0007-0845-0953" + - + affiliation: "Met Office, UK" + family-names: Ellis + given-names: Hannah + cff-version: 1.2.0 date-released: 2025-10-20 diff --git a/doc/sphinx/source/input.rst b/doc/sphinx/source/input.rst index a2b20db04b..11c59f0228 100644 --- a/doc/sphinx/source/input.rst +++ b/doc/sphinx/source/input.rst @@ -296,6 +296,8 @@ A list of the datasets for which a CMORizers is available is provided in the fol +----------------------------------------+------------------------------------------------------------------------------------------------------+------+-----------------+ | Duveiller2018 | albDiffiTr13 | 2 | Python | +----------------------------------------+------------------------------------------------------------------------------------------------------+------+-----------------+ +| EN4 | thetao, so (Omon) | 2 | Python | ++----------------------------------------+------------------------------------------------------------------------------------------------------+------+-----------------+ | E-OBS | tas, tasmin, tasmax, pr, psl (day, Amon) | 2 | Python | +----------------------------------------+------------------------------------------------------------------------------------------------------+------+-----------------+ | Eppley-VGPM-MODIS | intpp (Omon) | 2 | Python | diff --git a/esmvaltool/cmorizers/data/cmor_config/EN4.yml b/esmvaltool/cmorizers/data/cmor_config/EN4.yml new file mode 100644 index 0000000000..7ddd023356 --- /dev/null +++ b/esmvaltool/cmorizers/data/cmor_config/EN4.yml @@ -0,0 +1,26 @@ +--- + +# Filename +filename: 'EN.4.2.2.f.analysis.g10.*.nc' + +# Common global attributes for Cmorizer output +attributes: + dataset_id: EN4 + version: '4.2.2' + tier: 2 + modeling_realm: reanaly + project_id: OBS6 + source: 'https://www.metoffice.gov.uk/hadobs/en4/download-en4-2-2.html' + reference: 'good2013jgr' + comment: 'Uses analyses with Gouretski and Reseghetti (2010) XBT corrections and Gouretski and Cheng (2020) MBT corrections applied.' + +# Variables to cmorize +variables: + thetao: + mip: Omon + raw_var: sea_water_potential_temperature + srf_var: tos + so: + mip: Omon + raw_var: sea_water_salinity + srf_var: sos diff --git a/esmvaltool/cmorizers/data/datasets.yml b/esmvaltool/cmorizers/data/datasets.yml index 16d5960693..bdabff781d 100644 --- a/esmvaltool/cmorizers/data/datasets.yml +++ b/esmvaltool/cmorizers/data/datasets.yml @@ -332,6 +332,20 @@ datasets: - Complete the CMOR-config specifications (see instructions in the file itself) + EN4: + tier: 2 + source: "https://www.metoffice.gov.uk/hadobs/en4/download-en4-2-2.html" + last_access: 2025-06-13 + info: | + EN4: quality controlled subsurface ocean temperature and salinity objective analyses. + Script tested using analyses with Gouretski and Reseghetti (2010) XBT corrections + and Gouretski and Cheng (2020) MBT corrections applied. + To download data: + - Edit the text file for your chosen years, https://www.metoffice.gov.uk/hadobs/en4/EN.4.2.2.analyses.g10.download-list.txt + - Save .txt file in directory for data to be downloaded to. + - Run 'wget -i EN.4.2.2.profiles.g10.download-list.txt' in same directory. + - Unzip files prior to running the cmorizer script. + E-OBS: tier: 2 source: http://surfobs.climate.copernicus.eu/dataaccess/access_eobs.php#datafiles diff --git a/esmvaltool/cmorizers/data/formatters/datasets/en4.py b/esmvaltool/cmorizers/data/formatters/datasets/en4.py new file mode 100644 index 0000000000..234848d451 --- /dev/null +++ b/esmvaltool/cmorizers/data/formatters/datasets/en4.py @@ -0,0 +1,128 @@ +""" +CMORizer for EN4 dataset. + +This script processes EN4 ocean temperature and salinity data to CMOR-compliant format for use in ESMValTool. + +Tier + Tier 2: other freely-available dataset. + +Source + https://www.metoffice.gov.uk/hadobs/en4/download-en4-2-2.html + +Last access + 2025-06-13 + +Info + EN4: quality controlled subsurface ocean temperature and salinity objective analyses. + Script tested using analyses with Gouretski and Reseghetti (2010) XBT corrections + and Gouretski and Cheng (2020) MBT corrections applied. + +Download instructions + - Edit the text file for your chosen years, https://www.metoffice.gov.uk/hadobs/en4/EN.4.2.2.analyses.g10.download-list.txt + - Save .txt file in directory for data to be downloaded to. + - Run 'wget -i EN.4.2.2.profiles.g10.download-list.txt' in same directory. + - Unzip files prior to running the cmorizer script. + +""" + +import logging +from pathlib import Path + +import iris + +from esmvaltool.cmorizers.data import utilities as utils + +logger = logging.getLogger(__name__) + + +def load_and_prepare_cube(fullpath, var, var_info, glob_attrs, cmor_table): + """ + Load and prepare a data cube for CMORization. Fix attributes, coordinates, and units. + + Parameters + ---------- + fullpath : str + Path to the input file. + var : str + Name of the variable to save in output cube. + var_info : dict + Variable information dictionary. + glob_attrs : dict + Global attributes to set on the cube. + cmor_table : object + CMOR table. + + Returns + ------- + iris.cube.Cube + The prepared data cube. + """ + + glob_attrs["mip"] = var_info["mip"] + raw_var = var_info["raw_var"] + cmor_info = cmor_table.get_variable(var_info["mip"], var) + + cubes = iris.load(fullpath, raw_var) + iris.util.equalise_attributes(cubes) + cube = cubes.concatenate_cube() + + if cube.units == "K": + cube.convert_units("degC") + + cube.coord("depth").units = "m" + cube = utils.fix_coords(cube) + utils.fix_var_metadata(cube, cmor_info) + utils.set_global_atts(cube, glob_attrs) + + return cube + + +def extract_surface_var(cube, cmor_info): + """ + Extract the surface level variable from a data cube. + + Parameters + ---------- + cube : iris.cube.Cube + Input data cube. + cmor_info : object + CMOR table object for the surface variable. + + Returns + ------- + iris.cube.Cube + The extracted surface level cube. + """ + logger.info("Extracting surface level") + + depth0 = iris.Constraint(depth=cube.coord("depth").points[0]) + surface_cube = cube.extract(depth0) + + utils.fix_var_metadata(surface_cube, cmor_info) + + return surface_cube + + +def cmorization(in_dir, out_dir, cfg, cfg_user, start_date, end_date): + """ + CMORization main function call. + """ + cmor_table = cfg["cmor_table"] + glob_attrs = cfg["attributes"] + fullpath = str(Path(in_dir) / cfg["filename"]) + + for var, var_info in cfg["variables"].items(): + logger.info("Loading %s", fullpath) + + srf_var = var_info["srf_var"] + cmor_info_srf = cmor_table.get_variable(var_info["mip"], srf_var) + + cube = load_and_prepare_cube( + fullpath, var, var_info, glob_attrs, cmor_table + ) + surface_cube = extract_surface_var(cube, cmor_info_srf) + logger.info("Saving for %s", var) + utils.save_variable(cube, var, out_dir, glob_attrs) + + logger.info("Saving for %s", srf_var) + utils.save_variable(surface_cube, srf_var, out_dir, glob_attrs) diff --git a/esmvaltool/recipes/examples/recipe_check_obs.yml b/esmvaltool/recipes/examples/recipe_check_obs.yml index 403f43fb92..e63f96b901 100644 --- a/esmvaltool/recipes/examples/recipe_check_obs.yml +++ b/esmvaltool/recipes/examples/recipe_check_obs.yml @@ -3,7 +3,7 @@ --- documentation: description: | - Test recipe for OBS, no proprocessor or diagnostics are applied, + Test recipe for OBS, no preprocessor or diagnostics are applied, just to check correct reading of the CMORized data. title: Recipe to test run all obs cmorizers. @@ -154,6 +154,17 @@ diagnostics: type: clim, version: v2018, start_year: 2010, end_year: 2010} scripts: null + EN4: + description: EN4 check + variables: + thetao: + tos: + so: + sos: + additional_datasets: + - {dataset: EN4, project: OBS6, mip: Omon, type: reanaly, + version: 4.2.2, tier: 2, start_year: 1900, end_year: 2024} + scripts: null E-OBS: description: E-OBS check diff --git a/esmvaltool/references/good2013jgr.bibtex b/esmvaltool/references/good2013jgr.bibtex new file mode 100644 index 0000000000..e7e9a1de98 --- /dev/null +++ b/esmvaltool/references/good2013jgr.bibtex @@ -0,0 +1,12 @@ +@article{Good2013EN4:Estimates, + title = {{EN4: Quality controlled ocean temperature and salinity profiles and monthly objective analyses with uncertainty estimates}}, + year = {2013}, + journal = {Journal of Geophysical Research: Oceans}, + author = {Good, Simon A. and Martin, Matthew J. and Rayner, Nick A.}, + number = {12}, + pages = {6704--6716}, + volume = {118}, + publisher = {Blackwell Publishing Ltd}, + doi = {10.1002/2013JC009067}, + issn = {21699291}, + keywords = {objective analysis, ocean observations, quality control, uncertainty estimation}