Skip to content

Commit cd68778

Browse files
rbeucherflicj191
andauthored
CMORiser for the IAPv4.2 global ocean temperature dataset (#3887)
Co-authored-by: Felicity Chun <[email protected]>
1 parent c6e6e42 commit cd68778

File tree

7 files changed

+321
-0
lines changed

7 files changed

+321
-0
lines changed

doc/sphinx/source/input.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,8 @@ A list of the datasets for which a CMORizers is available is provided in the fol
381381
+------------------------------+------------------------------------------------------------------------------------------------------+------+-----------------+
382382
| HWSD | cSoil (Lmon), areacella (fx), sftlf (fx) | 3 | Python |
383383
+------------------------------+------------------------------------------------------------------------------------------------------+------+-----------------+
384+
| IAP | thetao, tos (Omon) | 2 | Python |
385+
+------------------------------+------------------------------------------------------------------------------------------------------+------+-----------------+
384386
| ISCCP-FH | alb, prw, ps, rlds, rlus, rlut, rlutcs, rsds, rsdt, rsus, rsut, rsutcs, tas, ts (Amon) | 2 | NCL |
385387
+------------------------------+------------------------------------------------------------------------------------------------------+------+-----------------+
386388
| JMA-TRANSCOM | nbp (Lmon), fgco2 (Omon) | 3 | Python |
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
# Common global attributes for Cmorizer output
3+
attributes:
4+
dataset_id: IAP
5+
version: 'v4.2'
6+
tier: 2
7+
modeling_realm: reanaly
8+
project_id: OBS6
9+
source: 'http://www.ocean.iap.ac.cn/'
10+
reference: 'iap'
11+
comment: ''
12+
13+
# Variables to cmorize (here use only filename prefix)
14+
variables:
15+
thetao:
16+
mip: Omon
17+
name: temperature
18+
raw_var: temp
19+
srf_var: tos
20+
21+
custom:
22+
create_areacello: false
23+
reference_year: 2000

esmvaltool/cmorizers/data/datasets.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,22 @@ datasets:
820820
HWSD_SOIL_CLM_RES.nc4
821821
A registration is required
822822
823+
IAP:
824+
tier: 2
825+
source: http://www.ocean.iap.ac.cn/pages/dataService/dataService.html
826+
last_access: 2025-01-20
827+
info: |
828+
Download the following files:
829+
Temperature_IAPv4.2_gridded_data_1940_1949.zip
830+
Temperature_IAPv4.2_gridded_data_1950_1959.zip
831+
Temperature_IAPv4.2_gridded_data_1960_1969.zip
832+
Temperature_IAPv4.2_gridded_data_1970_1979.zip
833+
Temperature_IAPv4.2_gridded_data_1980_1989.zip
834+
Temperature_IAPv4.2_gridded_data_1990_1999.zip
835+
Temperature_IAPv4.2_gridded_data_2000_2009.zip
836+
Temperature_IAPv4.2_gridded_data_2010_2019.zip
837+
Temperature_IAPv4.2_gridded_data_2020_2023.zip
838+
823839
ISCCP-FH:
824840
tier: 2
825841
source: https://isccp.giss.nasa.gov/pub/flux-fh/tar-nc4_MPF/
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# pylint: disable=too-many-arguments
2+
# pylint: disable=R0917
3+
# pylint: disable=too-many-locals
4+
"""Script to download IAP datasets."""
5+
6+
import logging
7+
from datetime import datetime
8+
9+
from dateutil import relativedelta
10+
11+
from esmvaltool.cmorizers.data.downloaders.wget import WGetDownloader
12+
13+
logger = logging.getLogger(__name__)
14+
15+
16+
def download_dataset(
17+
config, dataset, dataset_info, start_date, end_date, overwrite
18+
):
19+
"""Download dataset.
20+
21+
Parameters
22+
----------
23+
config : dict
24+
ESMValTool's user configuration
25+
dataset : str
26+
Name of the dataset
27+
dataset_info : dict
28+
Dataset information from the datasets.yml file
29+
start_date : datetime
30+
Start of the interval to download
31+
end_date : datetime
32+
End of the interval to download
33+
overwrite : bool
34+
Overwrite already downloaded files
35+
"""
36+
if start_date is None:
37+
start_date = datetime(year=1940, month=1, day=1)
38+
if end_date is None:
39+
end_date = datetime(year=2024, month=12, day=31)
40+
41+
loop_date = start_date
42+
43+
downloader = WGetDownloader(
44+
config=config,
45+
dataset=dataset,
46+
dataset_info=dataset_info,
47+
overwrite=overwrite,
48+
)
49+
50+
while loop_date <= end_date:
51+
print(loop_date)
52+
downloader.download_file(
53+
"http://www.ocean.iap.ac.cn/ftp/cheng/"
54+
"IAPv4.2_IAP_Temperature_gridded_1month_netcdf/Monthly/"
55+
f"IAPv4_Temp_monthly_1_6000m_year_{loop_date.year}"
56+
f"_month_{loop_date.month:02d}.nc",
57+
wget_options=[],
58+
)
59+
loop_date += relativedelta.relativedelta(months=1)
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
# pylint: disable=unused-argument
2+
# pylint: disable=too-many-arguments
3+
# pylint: disable=too-many-function-args
4+
# pylint: disable=R0917
5+
# pylint: disable=E1121
6+
"""ESMValTool CMORizer for IAP data.
7+
8+
Tier
9+
Tier 2: other freely-available dataset.
10+
11+
Source
12+
IAPv4.2: "http://www.ocean.iap.ac.cn/ftp/cheng/"
13+
"IAPv4.2_IAP_Temperature_gridded_1month_netcdf/Monthly/"
14+
15+
Last access: 20250220
16+
17+
Download and processing instructions
18+
All handled by the script (download only if local data are missing)
19+
20+
Alternatively, download and unzip the following files:
21+
Temperature_IAPv4.2_gridded_data_1940_1949.zip
22+
Temperature_IAPv4.2_gridded_data_1950_1959.zip
23+
Temperature_IAPv4.2_gridded_data_1960_1969.zip
24+
Temperature_IAPv4.2_gridded_data_1970_1979.zip
25+
Temperature_IAPv4.2_gridded_data_1980_1989.zip
26+
Temperature_IAPv4.2_gridded_data_1990_1999.zip
27+
Temperature_IAPv4.2_gridded_data_2000_2009.zip
28+
Temperature_IAPv4.2_gridded_data_2010_2019.zip
29+
Temperature_IAPv4.2_gridded_data_2020_2023.zip
30+
"""
31+
32+
import logging
33+
import os
34+
import warnings
35+
from datetime import datetime
36+
from warnings import catch_warnings
37+
38+
import iris
39+
import numpy as np
40+
from dateutil import relativedelta
41+
42+
from esmvaltool.cmorizers.data.utilities import (
43+
fix_coords,
44+
fix_var_metadata,
45+
save_variable,
46+
set_global_atts,
47+
)
48+
49+
logger = logging.getLogger(__name__)
50+
51+
try:
52+
iris.FUTURE.date_microseconds = True
53+
iris.FUTURE.save_split_attrs = True
54+
except AttributeError as e:
55+
# Handle cases where FUTURE or the attributes don't exist
56+
logger.warning("AttributeError: %s", e)
57+
except (TypeError, ValueError) as e:
58+
# Handle specific errors if these might occur
59+
logger.warning("TypeError or ValueError: %s", e)
60+
61+
62+
def collect_files(in_dir, cfg, start_date, end_date):
63+
"""Create list of files path to be processed."""
64+
file_list = []
65+
66+
if start_date is None:
67+
start_date = datetime(year=1940, month=1, day=1)
68+
if end_date is None:
69+
end_date = datetime(year=2024, month=12, day=31)
70+
71+
loop_date = start_date
72+
73+
while loop_date <= end_date:
74+
fname = (
75+
f"IAPv4_Temp_monthly_1_6000m_year_{loop_date.year}"
76+
f"_month_{loop_date.month:02d}.nc"
77+
)
78+
in_file = os.path.join(in_dir, fname)
79+
file_list.append(in_file)
80+
loop_date += relativedelta.relativedelta(months=1)
81+
82+
return file_list
83+
84+
85+
def process_data(cube):
86+
"""Process raw data: concatenate the cubes and return the new cube."""
87+
# Add time dimension
88+
temperature_data = np.expand_dims(cube.data, axis=0)
89+
temperature_data = np.moveaxis(
90+
temperature_data,
91+
(0, 1, 2, 3),
92+
(0, 2, 3, 1),
93+
) # Reorder axes
94+
95+
# Create time coordinate
96+
start_date = datetime(
97+
int(cube.attributes["StartYear"]),
98+
int(cube.attributes["StartMonth"]),
99+
int(cube.attributes["StartDay"]),
100+
)
101+
reference_date = datetime(2000, 1, 1)
102+
time_points = [(start_date - reference_date).days]
103+
104+
time_coord = iris.coords.DimCoord(
105+
time_points,
106+
standard_name="time",
107+
units=(
108+
f"days since {reference_date.year}-"
109+
f"{reference_date.month}-{reference_date.day}"
110+
),
111+
)
112+
113+
# Remove old date attributes
114+
for key in [
115+
"StartDay",
116+
"StartMonth",
117+
"StartYear",
118+
"EndDay",
119+
"EndMonth",
120+
"EndYear",
121+
]:
122+
del cube.attributes[key]
123+
124+
# Get existing coordinates and rename 'standard depth' to 'depth'
125+
latitude_coord = cube.coord("latitude")
126+
longitude_coord = cube.coord("longitude")
127+
depth_coord = cube.coord("standard depth")
128+
depth_coord.rename("depth")
129+
depth_coord.var_name = "lev"
130+
depth_coord.attributes["positive"] = "down"
131+
132+
# Create and return the new cube
133+
return iris.cube.Cube(
134+
temperature_data,
135+
var_name="Temperature",
136+
dim_coords_and_dims=[
137+
(time_coord, 0),
138+
(depth_coord, 1),
139+
(latitude_coord, 2),
140+
(longitude_coord, 3),
141+
],
142+
attributes=cube.attributes,
143+
)
144+
145+
146+
def extract_variable(in_files, out_dir, attrs, raw_info, cmor_table):
147+
"""Extract variables and create OBS dataset."""
148+
var = raw_info["var"]
149+
var_info = cmor_table.get_variable(raw_info["mip"], var)
150+
rawvar = raw_info["raw_var"]
151+
with catch_warnings():
152+
warnings.simplefilter("ignore") # Ignore all warnings
153+
cubes = iris.load(in_files, rawvar)
154+
cubes = iris.cube.CubeList(
155+
[process_data(cube) for cube in cubes],
156+
)
157+
158+
iris.util.equalise_attributes(cubes)
159+
cube = cubes.concatenate_cube()
160+
fix_var_metadata(cube, var_info)
161+
fix_coords(cube)
162+
set_global_atts(cube, attrs)
163+
save_variable(cube, var, out_dir, attrs, unlimited_dimensions=["time"])
164+
165+
# derive ocean surface
166+
if "srf_var" in raw_info:
167+
var_info = cmor_table.get_variable(
168+
raw_info["mip"], raw_info["srf_var"]
169+
)
170+
logger.info("Extract surface OBS for %s", raw_info["srf_var"])
171+
level_constraint = iris.Constraint(cube.var_name, depth=1)
172+
cube_os = cube.extract(level_constraint)
173+
fix_var_metadata(cube_os, var_info)
174+
save_variable(
175+
cube_os,
176+
raw_info["srf_var"],
177+
out_dir,
178+
attrs,
179+
unlimited_dimensions=["time"],
180+
)
181+
182+
183+
def cmorization(in_dir, out_dir, cfg, cfg_user, start_date, end_date):
184+
"""Cmorization func call."""
185+
cmor_table = cfg["cmor_table"]
186+
glob_attrs = cfg["attributes"]
187+
188+
# run the cmorization
189+
for var, vals in cfg["variables"].items():
190+
in_files = collect_files(in_dir, cfg, start_date, end_date)
191+
logger.info("CMORizing var %s from input set %s", var, vals["name"])
192+
raw_info = cfg["variables"][var]
193+
raw_info.update(
194+
{
195+
"var": var,
196+
"reference_year": cfg["custom"]["reference_year"],
197+
},
198+
)
199+
glob_attrs["mip"] = vals["mip"]
200+
extract_variable(in_files, out_dir, glob_attrs, raw_info, cmor_table)

esmvaltool/recipes/examples/recipe_check_obs.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,16 @@ diagnostics:
579579
type: sat, version: 1, start_year: 1991, end_year: 2002}
580580
scripts: null
581581

582+
IAP:
583+
description: IAP check
584+
variables:
585+
thetao:
586+
tos:
587+
additional_datasets:
588+
- {dataset: IAP, project: OBS6, mip: Omon, tier: 2,
589+
type: reanaly, version: "v4.2", start_year: 1990, end_year: 1991}
590+
scripts: null
591+
582592
ISCCP-FH:
583593
description: ISCCP-FH check
584594
variables:

esmvaltool/references/iap.bibtex

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
@article{cheng2024,
2+
doi = {10.5194/essd-16-3517-2024},
3+
author = {Cheng, L. and Pan, Y. and Tan, Z. and Zheng, H. and Zhu, Y. and Wei, W. and Du, J. and Yuan, H. and Li, G. and Ye, H. and Gouretski, V. and Li, Y. and Trenberth, K. E. and Abraham, J. and Jin, Y. and Reseghetti, F. and Lin, X. and Zhang, B. and Chen, G. and Mann, M. E. and Zhu, J.},
4+
title = {IAPv4 ocean temperature and ocean heat content gridded dataset},
5+
journal = {Earth System Science Data},
6+
volume = {16},
7+
year = {2024},
8+
number = {8},
9+
pages = {3517--3546},
10+
url = {https://essd.copernicus.org/articles/16/3517/2024/}
11+
}

0 commit comments

Comments
 (0)