OceanDataStore 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- OceanDataStore/__init__.py +21 -0
- OceanDataStore/catalog/__init__.py +12 -0
- OceanDataStore/catalog/oceandatacatalog.py +1242 -0
- OceanDataStore/catalog/stac/README.md +34 -0
- OceanDataStore/catalog/stac/__init__.py +30 -0
- OceanDataStore/catalog/stac/create_noc_stac.py +109 -0
- OceanDataStore/catalog/stac/npd_era5_collection.py +364 -0
- OceanDataStore/catalog/stac/npd_jra55_collection.py +196 -0
- OceanDataStore/catalog/stac/ods_obs_collection.py +534 -0
- OceanDataStore/catalog/stac/rapid_evo_collection.py +309 -0
- OceanDataStore/catalog/stac/template_collection.py +85 -0
- OceanDataStore/catalog/stac/utils.py +476 -0
- OceanDataStore/cli/__init__.py +34 -0
- OceanDataStore/cli/arg_parser.py +182 -0
- OceanDataStore/cli/cli.py +203 -0
- OceanDataStore/cli/exceptions.py +83 -0
- OceanDataStore/cli/icechunk.py +888 -0
- OceanDataStore/cli/logging.py +52 -0
- OceanDataStore/cli/object_store.py +293 -0
- OceanDataStore/cli/utils.py +275 -0
- OceanDataStore/cli/zarr.py +870 -0
- OceanDataStore/data/ARMOR3D/create_ARMOR3D_P1M-m_monthly_climatology.py +135 -0
- OceanDataStore/data/ARMOR3D/download_ARMOR3D_0.125def_P1M-m_1993_2024.py +33 -0
- OceanDataStore/data/ARMOR3D/run_create_ARMOR3D_P1M-m_monthly_climatology.slurm +32 -0
- OceanDataStore/data/ARMOR3D/run_send_ARMOR3D_P1M-m_climatology_to_os.slurm +32 -0
- OceanDataStore/data/ARMOR3D/run_send_ARMOR3D_P1M-m_monthly_to_os.slurm +32 -0
- OceanDataStore/data/ARMOR3D/run_update_ARMOR3D_P1m-m_monthly_to_os.slurm +32 -0
- OceanDataStore/data/ARMOR3D/send_ARMOR3D_P1m-m_monthly_climatology_to_os.py +99 -0
- OceanDataStore/data/ARMOR3D/send_ARMOR3D_P1m-m_monthly_to_os.py +147 -0
- OceanDataStore/data/ARMOR3D/update_ARMOR3D_P1m-m_monthly_to_os.py +143 -0
- OceanDataStore/data/EN.4.2.2/create_EN4.2.2_analysis_g10_climatology.py +162 -0
- OceanDataStore/data/EN.4.2.2/download_EN4.2.2_analysis_g10_data.sh +51 -0
- OceanDataStore/data/EN.4.2.2/run_send_EN4.2.2_analysis_g10_climatology_to_os.slurm +32 -0
- OceanDataStore/data/EN.4.2.2/run_send_EN4.2.2_analysis_g10_monthly_to_os.slurm +32 -0
- OceanDataStore/data/EN.4.2.2/run_update_EN4.2.2_analysis_g10_monthly_to_os.slurm +32 -0
- OceanDataStore/data/EN.4.2.2/send_EN4.2.2_analysis_g10_monthly_climatology_to_os.py +76 -0
- OceanDataStore/data/EN.4.2.2/send_EN4.2.2_analysis_g10_monthly_to_os.py +165 -0
- OceanDataStore/data/EN.4.2.2/update_EN4.2.2_analysis_g10_monthly_to_os.py +161 -0
- OceanDataStore/data/ERA5/create_ERA5_daily_climatology.py +110 -0
- OceanDataStore/data/ERA5/create_ERA5_daily_mean.py +69 -0
- OceanDataStore/data/ERA5/create_ERA5_monthly_mean.py +74 -0
- OceanDataStore/data/ERA5/run_create_ERA5_daily_climatology.slurm +54 -0
- OceanDataStore/data/ERA5/run_send_ERA5_daily_climatology_to_os.slurm +32 -0
- OceanDataStore/data/ERA5/run_send_ERA5_daily_to_os.slurm +32 -0
- OceanDataStore/data/ERA5/run_send_ERA5_monthly_to_os.slurm +32 -0
- OceanDataStore/data/ERA5/run_update_ERA5_daily_to_os.slurm +32 -0
- OceanDataStore/data/ERA5/run_update_ERA5_monthly_to_os.slurm +32 -0
- OceanDataStore/data/ERA5/send_ERA5_daily_climatology_to_os.py +159 -0
- OceanDataStore/data/ERA5/send_ERA5_daily_to_os.py +141 -0
- OceanDataStore/data/ERA5/send_ERA5_monthly_to_os.py +173 -0
- OceanDataStore/data/ERA5/update_ERA5_daily_to_os.py +141 -0
- OceanDataStore/data/ERA5/update_ERA5_monthly_to_os.py +169 -0
- OceanDataStore/data/HadISST/download_HadISST1_data.sh +43 -0
- OceanDataStore/data/HadISST/run_send_HadISST1_monthly_to_os.slurm +32 -0
- OceanDataStore/data/HadISST/send_HadISST1_monthly_to_os.py +133 -0
- OceanDataStore/data/NSIDC/download_NSIDC_monthly_1979_2025_data.sh +54 -0
- OceanDataStore/data/NSIDC/process_NSIDC_SSI_Antarctic_data.py +130 -0
- OceanDataStore/data/NSIDC/process_NSIDC_SSI_Arctic_data.py +129 -0
- OceanDataStore/data/NSIDC/run_send_NSIDC_v4.0_to_OS.slurm +32 -0
- OceanDataStore/data/NSIDC/send_NSIDC_SII_v4.0_to_os.py +140 -0
- OceanDataStore/data/OISST/create_OISSTv2_daily_climatology.py +83 -0
- OceanDataStore/data/OISST/download_oisstv2_data.sh +43 -0
- OceanDataStore/data/OISST/run_create_OISSTv2_daily_climatology.slurm +44 -0
- OceanDataStore/data/OISST/run_send_OISSTv2_daily_climatology_to_os.slurm +32 -0
- OceanDataStore/data/OISST/run_send_OISSTv2_daily_to_os.slurm +32 -0
- OceanDataStore/data/OISST/run_send_OISSTv2_monthly_climatology_to_os.slurm +32 -0
- OceanDataStore/data/OISST/run_send_OISSTv2_monthly_to_os.slurm +32 -0
- OceanDataStore/data/OISST/run_update_OISSTv2_daily_to_os.slurm +32 -0
- OceanDataStore/data/OISST/send_OISSTv2_daily_climatology_to_os.py +154 -0
- OceanDataStore/data/OISST/send_OISSTv2_daily_ltm_climatology_to_os.py +151 -0
- OceanDataStore/data/OISST/send_OISSTv2_daily_to_os.py +142 -0
- OceanDataStore/data/OISST/send_OISSTv2_monthly_climatology_to_os.py +150 -0
- OceanDataStore/data/OISST/send_OISSTv2_monthly_to_os.py +145 -0
- OceanDataStore/data/OISST/update_OISSTv2_daily_to_os.py +142 -0
- OceanDataStore/data/OSTIA/create_OSTIA_daily_climatology.py +120 -0
- OceanDataStore/data/OSTIA/download_OSTIA_NRT.py +42 -0
- OceanDataStore/data/OSTIA/download_OSTIA_REP_1981_2025.py +42 -0
- OceanDataStore/data/OSTIA/run_create_OSTIA_daily_climatology.slurm +54 -0
- OceanDataStore/data/OSTIA/run_send_OSTIA_daily_climatology_to_os.slurm +32 -0
- OceanDataStore/data/OSTIA/run_send_OSTIA_nrt_daily_to_os.slurm +32 -0
- OceanDataStore/data/OSTIA/run_send_OSTIA_rep_daily_to_os.slurm +32 -0
- OceanDataStore/data/OSTIA/run_update_OSTIA_daily_to_os.slurm +33 -0
- OceanDataStore/data/OSTIA/send_OSTIA_daily_climatology_to_os.py +194 -0
- OceanDataStore/data/OSTIA/send_OSTIA_nrt_daily_to_os.py +141 -0
- OceanDataStore/data/OSTIA/send_OSTIA_rep_daily_to_os.py +145 -0
- OceanDataStore/data/OSTIA/update_OSTIA_copernicus_nrt_daily_to_os.py +144 -0
- OceanDataStore/data/OSTIA/update_OSTIA_nrt_daily_to_os.py +137 -0
- OceanDataStore/data/WOA23/download_WOA23_climatology.sh +41 -0
- OceanDataStore/data/WOA23/run_send_WOA23_annual_climatology_to_os.slurm +32 -0
- OceanDataStore/data/WOA23/run_send_WOA23_monthly_climatology_to_os.slurm +32 -0
- OceanDataStore/data/WOA23/send_WOA23_annual_climatology_to_os.py +263 -0
- OceanDataStore/data/WOA23/send_WOA23_monthly_climatology_to_os.py +292 -0
- OceanDataStore/data/update_icechunk_repo_attrs.py +76 -0
- OceanDataStore/data/update_noc_npd_era5v1_attrs.py +172 -0
- OceanDataStore/data/utils.py +506 -0
- OceanDataStore/zarr.py +993 -0
- oceandatastore-0.3.0.dist-info/METADATA +184 -0
- oceandatastore-0.3.0.dist-info/RECORD +104 -0
- oceandatastore-0.3.0.dist-info/WHEEL +5 -0
- oceandatastore-0.3.0.dist-info/entry_points.txt +2 -0
- oceandatastore-0.3.0.dist-info/licenses/LICENSE +201 -0
- oceandatastore-0.3.0.dist-info/scm_file_list.json +154 -0
- oceandatastore-0.3.0.dist-info/scm_version.json +8 -0
- oceandatastore-0.3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# =========================================================
|
|
2
|
+
# send_OISSTv2_monthly_climatology_to_os.py
|
|
3
|
+
#
|
|
4
|
+
# Script to write OISST v2.1 long-term monthly climatologies
|
|
5
|
+
# to Icechunk repositories in JASMIN cloud object storage.
|
|
6
|
+
#
|
|
7
|
+
# Created By: Ollie Tooth (oliver.tooth@noc.ac.uk)
|
|
8
|
+
# =========================================================
|
|
9
|
+
import logging
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
import xarray as xr
|
|
13
|
+
import zarr
|
|
14
|
+
|
|
15
|
+
from OceanDataStore.cli import initialise_logging, send_to_icechunk
|
|
16
|
+
from OceanDataStore.data.utils import (
|
|
17
|
+
compute_cell_area,
|
|
18
|
+
compute_dx,
|
|
19
|
+
compute_dy,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def main():
|
|
26
|
+
# ========== Initialise OceanDataStore Logging ========== #
|
|
27
|
+
initialise_logging()
|
|
28
|
+
|
|
29
|
+
# ========== Send to Icechunk Repository ========== #
|
|
30
|
+
bucket = "oisst"
|
|
31
|
+
exists = False
|
|
32
|
+
store_credentials_json = ".../credentials/jasmin_os_credentials.json"
|
|
33
|
+
branch = "main"
|
|
34
|
+
variable_commits = True
|
|
35
|
+
|
|
36
|
+
# Define climatology period:
|
|
37
|
+
start_yr = 1991
|
|
38
|
+
end_yr = 2020
|
|
39
|
+
|
|
40
|
+
logging.info(f"In Progress: Sending OISSTv2.1 monthly climatology for {start_yr}-{end_yr} to Icechunk...")
|
|
41
|
+
# Open OISSTv2 dataset:
|
|
42
|
+
filepaths = [f"/dssgfs01/scratch/otooth/npd_data/observations/OISST/icec.mon.ltm.{start_yr}-{end_yr}.nc",
|
|
43
|
+
f"/dssgfs01/scratch/otooth/npd_data/observations/OISST/sst.mon.ltm.{start_yr}-{end_yr}.nc"
|
|
44
|
+
]
|
|
45
|
+
ds = xr.merge([xr.open_dataset(filepath, decode_times=False).drop_vars("valid_yr_count") for filepath in filepaths], compat="no_conflicts")
|
|
46
|
+
# Open OISSTv2 land-sea mask dataset:
|
|
47
|
+
ds_mask = xr.open_dataset("http://psl.noaa.gov/thredds/dodsC/Datasets/noaa.oisst.v2.highres/lsmask.oisst.nc", decode_times=False)
|
|
48
|
+
ds_mask = ds_mask.squeeze(drop=True).rename({"lon": "longitude", "lat": "latitude", "lsmask": "mask"})
|
|
49
|
+
ds_mask = ds_mask.assign_coords(
|
|
50
|
+
longitude=((ds_mask["longitude"] + 180) % 360) - 180
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Standardise coordinate dimension names:
|
|
54
|
+
ds = ds.rename({"lon": "longitude", "lat": "latitude", "time": "month"})
|
|
55
|
+
|
|
56
|
+
# Update longitude coordinates to be in the range [-180, 180]:
|
|
57
|
+
ds = ds.assign_coords(
|
|
58
|
+
longitude=((ds["longitude"] + 180) % 360) - 180
|
|
59
|
+
)
|
|
60
|
+
ds = ds.sortby("longitude")
|
|
61
|
+
|
|
62
|
+
# Add month of year coordinate (1-12):
|
|
63
|
+
ds = ds.assign_coords(
|
|
64
|
+
month=np.arange(1, 13)
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Rename variables to standard names:
|
|
68
|
+
ds = ds.rename({"sst": "tos",
|
|
69
|
+
"icec": "siconc",
|
|
70
|
+
"climatology_bounds": "time_bnds",
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
# Add standard names and units:
|
|
74
|
+
ds["tos"].attrs["standard_name"] = "sea_surface_temperature"
|
|
75
|
+
ds["siconc"].attrs["standard_name"] = "sea_ice_area_fraction"
|
|
76
|
+
ds["siconc"].attrs["units"] = "1"
|
|
77
|
+
|
|
78
|
+
# Add OISSTv2 land mask:
|
|
79
|
+
ds["mask"] = ds_mask["mask"]
|
|
80
|
+
ds["mask"].attrs.clear()
|
|
81
|
+
ds["mask"] = ds["mask"].assign_attrs({'long_name': "Land-Sea Binary Mask",
|
|
82
|
+
"standard_name": "sea_binary_mask",
|
|
83
|
+
"comment": "1 = sea, 0 = land"
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
# Add horizontal grid cell area:
|
|
87
|
+
ds['dx'] = compute_dx(ds)
|
|
88
|
+
ds['dy'] = compute_dy(ds)
|
|
89
|
+
ds['cell_area'] = compute_cell_area(ds)
|
|
90
|
+
|
|
91
|
+
# Update time bounds to reflect climatological period:
|
|
92
|
+
ds['time_bnds'] = ds['time_bnds'].astype('datetime64[ns]')
|
|
93
|
+
ds['time_bnds'].data[:, 0] = (np.datetime64(f'{start_yr}-01', 'M') + (np.timedelta64(1, 'M') * np.arange(ds['month'].size))).astype('datetime64[ns]')
|
|
94
|
+
ds['time_bnds'].data[:, 1] = (np.datetime64(f'{end_yr}-01', 'M') + (np.timedelta64(1, 'M') * np.arange(ds['month'].size))).astype('datetime64[ns]')
|
|
95
|
+
ds.time_bnds.attrs.clear()
|
|
96
|
+
|
|
97
|
+
# Update global attributes:
|
|
98
|
+
ds.attrs.clear()
|
|
99
|
+
ds = ds.assign_attrs({
|
|
100
|
+
"Conventions": "CF-1.5",
|
|
101
|
+
"title": f"NOAA OISSTv2.1 Monthly Climatology ({start_yr}-{end_yr})",
|
|
102
|
+
"description": f"NOAA 1/4° Monthly Optimum Interpolation Sea Surface Temperature (OISST) version 2.1 monthly sea surface temperature and sea ice fraction climatology ({start_yr}-{end_yr}).",
|
|
103
|
+
"source": "Numerical models: Optimal Interpolation. In-situ observations: ICOADS-D R3.0.2, Argo GDAC. Satellite observations: Advanced Very High Resolution Radiometer (AVHRR).",
|
|
104
|
+
"dataset_type": "observation",
|
|
105
|
+
"product_type": "climatology",
|
|
106
|
+
"product_version": "2.1",
|
|
107
|
+
"institution": "NOAA National Centers for Environmental Information (NCEI)",
|
|
108
|
+
"citation": "Huang, B., C. Liu, V. Banzon, E. Freeman, G. Graham, B. Hankins, T. Smith, and H.-M. Zhang, 2021: Improvements of the Daily Optimum Interpolation Sea Surface Temperature (DOISST) Version 2.1, Journal of Climate, 34, 2923-2939. doi: 10.1175/JCLI-D-20-0166.1",
|
|
109
|
+
"references": "Huang, B., C. Liu, V. Banzon, E. Freeman, G. Graham, B. Hankins, T. Smith, and H.-M. Zhang, 2020: Improvements of the Daily Optimum Interpolation Sea Surface Temperature (DOISST) Version 2.1, Journal of Climate, 34, 2923-2939. doi: 10.1175/JCLI-D-20-0166.1. Banzon, V., Smith, T. M., Chin, T. M., Liu, C., and Hankins, W., 2016: A long-term record of blended satellite and in situ sea-surface temperature for climate monitoring, modeling and environmental studies. Earth Syst. Sci. Data, 8, 165-176, doi:10.5194/essd-8-165-2016. Reynolds, R. W., T. M. Smith, C. Liu, D. B. Chelton, K. S. Casey, and M. G. Schlax, 2007: Daily high-resolution-blended analyses for sea surface temperature. Journal of Climate, 20, 5473-5496, doi:10.1175/JCLI-D-14-00293.1",
|
|
110
|
+
"acknowledgement": "NOAA OI SST V2 High Resolution Dataset data provided by the NOAA PSL, Boulder, Colorado, USA, from their website at https://psl.noaa.gov.",
|
|
111
|
+
"license": "OISST v2.1 data were obtained from https://psl.noaa.gov/data/gridded/data.noaa.oisst.v2.highres.html and are provided under a Creative Commons CC0 1.0 Universal License https://creativecommons.org/publicdomain/zero/1.0/",
|
|
112
|
+
"doi": "10.1175/JCLI-D-20-0166.1",
|
|
113
|
+
"platform": "gr",
|
|
114
|
+
"horizontal_grid_type": "regular rectilinear",
|
|
115
|
+
"horizontal_grid_resolution": "0.25 degree",
|
|
116
|
+
"aggregation": "mean",
|
|
117
|
+
"aggregation_frequency": "monthly",
|
|
118
|
+
"status": "completed",
|
|
119
|
+
"update_frequency": "None",
|
|
120
|
+
"bbox": "[-180.0, 180.0, -90.0, 90.0]",
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
# Optimise chunk sizes for spatial analysis:
|
|
124
|
+
ds = ds.chunk({'month': 1, 'latitude': 720, 'longitude': 1440})
|
|
125
|
+
|
|
126
|
+
# Update variable encodings:
|
|
127
|
+
blosccodec = zarr.codecs.BloscCodec(cname="zstd", clevel=3, shuffle=zarr.codecs.BloscShuffle.shuffle)
|
|
128
|
+
for var in list(ds.data_vars) + list(ds.coords):
|
|
129
|
+
ds[var].encoding['compressors'] = [blosccodec]
|
|
130
|
+
|
|
131
|
+
# Define prefix and commit message based on climatology period:
|
|
132
|
+
prefix = f"oisst_v2.1_{start_yr}_{end_yr}_monthly_climatology"
|
|
133
|
+
commit_message = f"Added OISSTv2.1 Sea Surface Temperature Climatology ({start_yr}-{end_yr})."
|
|
134
|
+
|
|
135
|
+
send_to_icechunk(
|
|
136
|
+
file=ds,
|
|
137
|
+
bucket=bucket,
|
|
138
|
+
object_prefix=prefix,
|
|
139
|
+
store_credentials_json=store_credentials_json,
|
|
140
|
+
exists=exists,
|
|
141
|
+
append_dim='month',
|
|
142
|
+
branch=branch,
|
|
143
|
+
commit_message=commit_message,
|
|
144
|
+
variable_commits=variable_commits,
|
|
145
|
+
dask_config_kwargs=None,
|
|
146
|
+
dask_cluster_kwargs=None,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
if __name__ == "__main__":
|
|
150
|
+
main()
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# =========================================================
|
|
2
|
+
# send_OISSTv2_monthly_climatology_to_os.py
|
|
3
|
+
#
|
|
4
|
+
# Script to write OISST v2.1 long-term monthly climatologies
|
|
5
|
+
# to Icechunk repositories in JASMIN cloud object storage.
|
|
6
|
+
#
|
|
7
|
+
# Created By: Ollie Tooth (oliver.tooth@noc.ac.uk)
|
|
8
|
+
# =========================================================
|
|
9
|
+
import logging
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
import xarray as xr
|
|
13
|
+
import zarr
|
|
14
|
+
|
|
15
|
+
from OceanDataStore.cli import initialise_logging, send_to_icechunk
|
|
16
|
+
from OceanDataStore.data.utils import (
|
|
17
|
+
compute_cell_area,
|
|
18
|
+
compute_dx,
|
|
19
|
+
compute_dy,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def main():
|
|
26
|
+
# ========== Initialise OceanDataStore Logging ========== #
|
|
27
|
+
initialise_logging()
|
|
28
|
+
|
|
29
|
+
# ========== Send to Icechunk Repository ========== #
|
|
30
|
+
bucket = "oisst"
|
|
31
|
+
exists = False
|
|
32
|
+
store_credentials_json = ".../credentials/jasmin_os_credentials.json"
|
|
33
|
+
branch = "main"
|
|
34
|
+
variable_commits = True
|
|
35
|
+
|
|
36
|
+
logging.info("In Progress: Sending OISSTv2.1 monthly mean time series to Icechunk...")
|
|
37
|
+
# Open OISSTv2 dataset:
|
|
38
|
+
filepaths = ["/dssgfs01/scratch/otooth/npd_data/observations/OISST/icec.mon.mean.nc",
|
|
39
|
+
"/dssgfs01/scratch/otooth/npd_data/observations/OISST/sst.mon.mean.nc"
|
|
40
|
+
]
|
|
41
|
+
ds = xr.merge([xr.open_dataset(filepath, decode_times=False) for filepath in filepaths], compat="no_conflicts")
|
|
42
|
+
# Open OISSTv2 land-sea mask dataset:
|
|
43
|
+
ds_mask = xr.open_dataset("http://psl.noaa.gov/thredds/dodsC/Datasets/noaa.oisst.v2.highres/lsmask.oisst.nc", decode_times=False)
|
|
44
|
+
ds_mask = ds_mask.squeeze(drop=True).rename({"lon": "longitude", "lat": "latitude", "lsmask": "mask"})
|
|
45
|
+
ds_mask = ds_mask.assign_coords(
|
|
46
|
+
longitude=((ds_mask["longitude"] + 180) % 360) - 180
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Standardise coordinate dimension names:
|
|
50
|
+
ds = ds.rename({"lon": "longitude", "lat": "latitude"})
|
|
51
|
+
|
|
52
|
+
# Update longitude coordinates to be in the range [-180, 180]:
|
|
53
|
+
ds = ds.assign_coords(
|
|
54
|
+
longitude=((ds["longitude"] + 180) % 360) - 180
|
|
55
|
+
)
|
|
56
|
+
ds = ds.sortby("longitude")
|
|
57
|
+
|
|
58
|
+
# Update time coordinate to be datetime objects:
|
|
59
|
+
ds = ds.assign_coords(time=(np.datetime64('1800-01-01', 'D') + (np.timedelta64(1, 'D') * ds['time'])).astype('datetime64[ns]'))
|
|
60
|
+
ds['time'].attrs.pop('units', None)
|
|
61
|
+
|
|
62
|
+
# Rename variables to standard names:
|
|
63
|
+
ds = ds.rename({"sst": "tos",
|
|
64
|
+
"icec": "siconc",
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
# Add standard names and units:
|
|
68
|
+
ds["tos"].attrs["standard_name"] = "sea_surface_temperature"
|
|
69
|
+
ds["siconc"].attrs["standard_name"] = "sea_ice_area_fraction"
|
|
70
|
+
ds["siconc"].attrs["units"] = "1"
|
|
71
|
+
|
|
72
|
+
# Add OISSTv2 land mask:
|
|
73
|
+
ds["mask"] = ds_mask["mask"]
|
|
74
|
+
ds["mask"].attrs.clear()
|
|
75
|
+
ds["mask"] = ds["mask"].assign_attrs({'long_name': "Land-Sea Binary Mask",
|
|
76
|
+
"standard_name": "sea_binary_mask",
|
|
77
|
+
"comment": "1 = sea, 0 = land"
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
# Add horizontal grid cell area:
|
|
81
|
+
ds["dx"] = compute_dx(ds)
|
|
82
|
+
ds["dy"] = compute_dy(ds)
|
|
83
|
+
ds['cell_area'] = compute_cell_area(ds)
|
|
84
|
+
|
|
85
|
+
# Add Northern and Southern Hemisphere sea ice area timeseries:
|
|
86
|
+
ds['siarea_NH'] = (ds['siconc'].where(ds['latitude'] > 0) * ds['cell_area']).sum(dim=['latitude', 'longitude'])
|
|
87
|
+
ds['siarea_NH'].attrs = {'long_name': 'Total Northern Hemisphere Sea Ice Area', 'standard_name': 'sea_ice_area', 'units': 'm2'}
|
|
88
|
+
|
|
89
|
+
ds['siarea_SH'] = (ds['siconc'].where(ds['latitude'] < 0) * ds['cell_area']).sum(dim=['latitude', 'longitude'])
|
|
90
|
+
ds['siarea_SH'].attrs = {'long_name': 'Total Southern Hemisphere Sea Ice Area', 'standard_name': 'sea_ice_area', 'units': 'm2'}
|
|
91
|
+
|
|
92
|
+
# Update global attributes:
|
|
93
|
+
ds.attrs.clear()
|
|
94
|
+
ds = ds.assign_attrs({
|
|
95
|
+
"Conventions": "CF-1.5",
|
|
96
|
+
"title": "NOAA OISSTv2.1 Monthly Timeseries",
|
|
97
|
+
"description": "NOAA 1/4° Monthly Optimum Interpolation Sea Surface Temperature (OISST) version 2.1 monthly sea surface temperature and sea ice fraction timeseries.",
|
|
98
|
+
"source": "Numerical models: Optimal Interpolation. In-situ observations: ICOADS-D R3.0.2, Argo GDAC. Satellite observations: Advanced Very High Resolution Radiometer (AVHRR).",
|
|
99
|
+
"dataset_type": "observation",
|
|
100
|
+
"product_type": "timeseries",
|
|
101
|
+
"product_version": "2.1",
|
|
102
|
+
"institution": "NOAA National Centers for Environmental Information (NCEI)",
|
|
103
|
+
"citation": "Huang, B., C. Liu, V. Banzon, E. Freeman, G. Graham, B. Hankins, T. Smith, and H.-M. Zhang, 2021: Improvements of the Daily Optimum Interpolation Sea Surface Temperature (DOISST) Version 2.1, Journal of Climate, 34, 2923-2939. doi: 10.1175/JCLI-D-20-0166.1",
|
|
104
|
+
"references": "Huang, B., C. Liu, V. Banzon, E. Freeman, G. Graham, B. Hankins, T. Smith, and H.-M. Zhang, 2020: Improvements of the Daily Optimum Interpolation Sea Surface Temperature (DOISST) Version 2.1, Journal of Climate, 34, 2923-2939. doi: 10.1175/JCLI-D-20-0166.1. Banzon, V., Smith, T. M., Chin, T. M., Liu, C., and Hankins, W., 2016: A long-term record of blended satellite and in situ sea-surface temperature for climate monitoring, modeling and environmental studies. Earth Syst. Sci. Data, 8, 165-176, doi:10.5194/essd-8-165-2016. Reynolds, R. W., T. M. Smith, C. Liu, D. B. Chelton, K. S. Casey, and M. G. Schlax, 2007: Daily high-resolution-blended analyses for sea surface temperature. Journal of Climate, 20, 5473-5496, doi:10.1175/JCLI-D-14-00293.1",
|
|
105
|
+
"acknowledgement": "NOAA OI SST V2 High Resolution Dataset data provided by the NOAA PSL, Boulder, Colorado, USA, from their website at https://psl.noaa.gov.",
|
|
106
|
+
"license": "OISST v2.1 data were obtained from https://psl.noaa.gov/data/gridded/data.noaa.oisst.v2.highres.html and are provided under a Creative Commons CC0 1.0 Universal License https://creativecommons.org/publicdomain/zero/1.0/",
|
|
107
|
+
"doi": "10.1175/JCLI-D-20-0166.1",
|
|
108
|
+
"platform": "gr",
|
|
109
|
+
"horizontal_grid_type": "regular rectilinear",
|
|
110
|
+
"horizontal_grid_resolution": "0.25 degree",
|
|
111
|
+
"aggregation": "mean",
|
|
112
|
+
"aggregation_frequency": "monthly",
|
|
113
|
+
"status": "ongoing",
|
|
114
|
+
"update_frequency": "quarterly",
|
|
115
|
+
"bbox": "[-180.0, 180.0, -90.0, 90.0]",
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
# Optimise chunk sizes for spatial analysis:
|
|
119
|
+
ds = ds.chunk({'time': 1, 'latitude': 720, 'longitude': 1440})
|
|
120
|
+
|
|
121
|
+
# Update variable encodings:
|
|
122
|
+
blosccodec = zarr.codecs.BloscCodec(cname="zstd", clevel=3, shuffle=zarr.codecs.BloscShuffle.shuffle)
|
|
123
|
+
for var in list(ds.data_vars) + list(ds.coords):
|
|
124
|
+
ds[var].encoding['compressors'] = [blosccodec]
|
|
125
|
+
|
|
126
|
+
# Define prefix and commit message based on climatology period:
|
|
127
|
+
prefix = "oisst_v2.1_monthly"
|
|
128
|
+
commit_message = "Added OISSTv2.1 Sea Surface Temperature & Sea Ice Fraction Monthly Timeseries (1981-2026)."
|
|
129
|
+
|
|
130
|
+
send_to_icechunk(
|
|
131
|
+
file=ds,
|
|
132
|
+
bucket=bucket,
|
|
133
|
+
object_prefix=prefix,
|
|
134
|
+
store_credentials_json=store_credentials_json,
|
|
135
|
+
exists=exists,
|
|
136
|
+
append_dim='time',
|
|
137
|
+
branch=branch,
|
|
138
|
+
commit_message=commit_message,
|
|
139
|
+
variable_commits=variable_commits,
|
|
140
|
+
dask_config_kwargs=None,
|
|
141
|
+
dask_cluster_kwargs=None,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
if __name__ == "__main__":
|
|
145
|
+
main()
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# =========================================================
|
|
2
|
+
# update_OISSTv2_daily_to_os.py
|
|
3
|
+
#
|
|
4
|
+
# Script to write OISST v2.1 daily mean timeseries
|
|
5
|
+
# to Icechunk repositories in JASMIN cloud object storage.
|
|
6
|
+
#
|
|
7
|
+
# Created By: Ollie Tooth (oliver.tooth@noc.ac.uk)
|
|
8
|
+
# =========================================================
|
|
9
|
+
import logging
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
import xarray as xr
|
|
13
|
+
import zarr
|
|
14
|
+
|
|
15
|
+
from OceanDataStore.cli import initialise_logging, update_icechunk
|
|
16
|
+
from OceanDataStore.data.utils import (
|
|
17
|
+
compute_cell_area,
|
|
18
|
+
compute_dx,
|
|
19
|
+
compute_dy,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def main():
|
|
26
|
+
# ========== Initialise OceanDataStore Logging ========== #
|
|
27
|
+
initialise_logging()
|
|
28
|
+
|
|
29
|
+
# ========== Update Icechunk Repository ========== #
|
|
30
|
+
bucket = "oisst"
|
|
31
|
+
store_credentials_json = ".../credentials/jasmin_os_credentials.json"
|
|
32
|
+
branch = "main"
|
|
33
|
+
config_kwargs = {
|
|
34
|
+
"temporary_directory":"/dssgfs01/working/otooth/Software/OceanDataStore/OceanDataStore/data/OISST/",
|
|
35
|
+
"local_directory":"/dssgfs01/working/otooth/Software/OceanDataStore/OceanDataStore/data/OISST/"
|
|
36
|
+
}
|
|
37
|
+
cluster_kwargs = {
|
|
38
|
+
"n_workers" : 15,
|
|
39
|
+
"threads_per_worker" : 1,
|
|
40
|
+
"memory_limit":"6GB"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
logging.info("In Progress: Updating OISSTv2.1 daily mean time series in Icechunk...")
|
|
44
|
+
# Open OISSTv2 dataset:
|
|
45
|
+
filepaths = []
|
|
46
|
+
base = Path("/dssgfs01/scratch/otooth/npd_data/observations/OISST/daily/")
|
|
47
|
+
for year in range(2026, 2027):
|
|
48
|
+
filepaths.extend(sorted(base.glob(f"sst.day.mean.{year}.nc")))
|
|
49
|
+
ds = xr.open_mfdataset(filepaths,
|
|
50
|
+
combine="by_coords",
|
|
51
|
+
data_vars="all",
|
|
52
|
+
engine="h5netcdf",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# Open OISSTv2 land-sea mask dataset:
|
|
56
|
+
ds_mask = xr.open_dataset("http://psl.noaa.gov/thredds/dodsC/Datasets/noaa.oisst.v2.highres/lsmask.oisst.nc", decode_times=False)
|
|
57
|
+
ds_mask = ds_mask.squeeze(drop=True).rename({"lon": "longitude", "lat": "latitude", "lsmask": "mask"})
|
|
58
|
+
ds_mask = ds_mask.assign_coords(
|
|
59
|
+
longitude=((ds_mask["longitude"] + 180) % 360) - 180
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Standardise coordinate dimension names:
|
|
63
|
+
ds = ds.rename({"lon": "longitude", "lat": "latitude"})
|
|
64
|
+
|
|
65
|
+
# Update longitude coordinates to be in the range [-180, 180]:
|
|
66
|
+
ds = ds.assign_coords(
|
|
67
|
+
longitude=((ds["longitude"] + 180) % 360) - 180
|
|
68
|
+
)
|
|
69
|
+
ds = ds.sortby("longitude")
|
|
70
|
+
|
|
71
|
+
# Rename variables to standard names:
|
|
72
|
+
ds = ds.rename({"sst": "tos"})
|
|
73
|
+
|
|
74
|
+
# Add standard names and units:
|
|
75
|
+
ds["tos"].attrs["standard_name"] = "sea_surface_temperature"
|
|
76
|
+
|
|
77
|
+
# Add OISSTv2 land mask:
|
|
78
|
+
ds["mask"] = ds_mask["mask"]
|
|
79
|
+
ds["mask"].attrs.clear()
|
|
80
|
+
ds["mask"] = ds["mask"].assign_attrs({"long_name": "Land-Sea Binary Mask",
|
|
81
|
+
"standard_name": "sea_binary_mask",
|
|
82
|
+
"comment": "1 = sea, 0 = land"
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
# Add horizontal grid cell area:
|
|
86
|
+
ds["dx"] = compute_dx(ds)
|
|
87
|
+
ds["dy"] = compute_dy(ds)
|
|
88
|
+
ds['cell_area'] = compute_cell_area(ds)
|
|
89
|
+
|
|
90
|
+
# Update global attributes:
|
|
91
|
+
ds.attrs.clear()
|
|
92
|
+
ds = ds.assign_attrs({
|
|
93
|
+
"Conventions": "CF-1.5",
|
|
94
|
+
"title": "NOAA OISSTv2.1 Daily Timeseries",
|
|
95
|
+
"description": "NOAA 1/4° Daily Optimum Interpolation Sea Surface Temperature (OISST) version 2.1 daily sea surface temperature timeseries.",
|
|
96
|
+
"source": "Numerical models: Optimal Interpolation. In-situ observations: ICOADS-D R3.0.2, Argo GDAC. Satellite observations: Advanced Very High Resolution Radiometer (AVHRR).",
|
|
97
|
+
"dataset_type": "observation",
|
|
98
|
+
"product_type": "timeseries",
|
|
99
|
+
"product_version": "2.1",
|
|
100
|
+
"institution": "NOAA National Centers for Environmental Information (NCEI)",
|
|
101
|
+
"citation": "Huang, B., C. Liu, V. Banzon, E. Freeman, G. Graham, B. Hankins, T. Smith, and H.-M. Zhang, 2021: Improvements of the Daily Optimum Interpolation Sea Surface Temperature (DOISST) Version 2.1, Journal of Climate, 34, 2923-2939. doi: 10.1175/JCLI-D-20-0166.1",
|
|
102
|
+
"references": "Huang, B., C. Liu, V. Banzon, E. Freeman, G. Graham, B. Hankins, T. Smith, and H.-M. Zhang, 2020: Improvements of the Daily Optimum Interpolation Sea Surface Temperature (DOISST) Version 2.1, Journal of Climate, 34, 2923-2939. doi: 10.1175/JCLI-D-20-0166.1. Banzon, V., Smith, T. M., Chin, T. M., Liu, C., and Hankins, W., 2016: A long-term record of blended satellite and in situ sea-surface temperature for climate monitoring, modeling and environmental studies. Earth Syst. Sci. Data, 8, 165-176, doi:10.5194/essd-8-165-2016. Reynolds, R. W., T. M. Smith, C. Liu, D. B. Chelton, K. S. Casey, and M. G. Schlax, 2007: Daily high-resolution-blended analyses for sea surface temperature. Journal of Climate, 20, 5473-5496, doi:10.1175/JCLI-D-14-00293.1",
|
|
103
|
+
"acknowledgement": "NOAA OI SST V2 High Resolution Dataset data provided by the NOAA PSL, Boulder, Colorado, USA, from their website at https://psl.noaa.gov.",
|
|
104
|
+
"license": "OISST v2.1 data were obtained from https://psl.noaa.gov/data/gridded/data.noaa.oisst.v2.highres.html and are provided under a Creative Commons CC0 1.0 Universal License https://creativecommons.org/publicdomain/zero/1.0/",
|
|
105
|
+
"doi": "10.1175/JCLI-D-20-0166.1",
|
|
106
|
+
"platform": "gr",
|
|
107
|
+
"horizontal_grid_type": "regular rectilinear",
|
|
108
|
+
"horizontal_grid_resolution": "0.25 degree",
|
|
109
|
+
"aggregation": "mean",
|
|
110
|
+
"aggregation_frequency": "daily",
|
|
111
|
+
"status": "ongoing",
|
|
112
|
+
"update_frequency": "quarterly",
|
|
113
|
+
"bbox": "[-180.0, 180.0, -90.0, 90.0]",
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
# Optimise chunk sizes for time-series analysis:
|
|
117
|
+
ds = ds.chunk({'time': ds['time'].size, 'latitude': 50, 'longitude': 50})
|
|
118
|
+
|
|
119
|
+
# Update variable encodings:
|
|
120
|
+
blosccodec = zarr.codecs.BloscCodec(cname="zstd", clevel=3, shuffle=zarr.codecs.BloscShuffle.shuffle)
|
|
121
|
+
for var in list(ds.data_vars) + list(ds.coords):
|
|
122
|
+
ds[var].encoding.clear()
|
|
123
|
+
ds[var].encoding['compressors'] = [blosccodec]
|
|
124
|
+
|
|
125
|
+
# Define prefix and commit message based on climatology period:
|
|
126
|
+
prefix = "oisst_v2.1_daily"
|
|
127
|
+
commit_message = "Added OISSTv2.1 Sea Surface Temperature Daily Timeseries (2026-01-2026-06)."
|
|
128
|
+
|
|
129
|
+
update_icechunk(
|
|
130
|
+
file=ds,
|
|
131
|
+
bucket=bucket,
|
|
132
|
+
object_prefix=prefix,
|
|
133
|
+
store_credentials_json=store_credentials_json,
|
|
134
|
+
append_dim='time',
|
|
135
|
+
branch=branch,
|
|
136
|
+
commit_message=commit_message,
|
|
137
|
+
dask_config_kwargs=config_kwargs,
|
|
138
|
+
dask_cluster_kwargs=cluster_kwargs,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
if __name__ == "__main__":
|
|
142
|
+
main()
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# =========================================================
|
|
2
|
+
# create_OSTIA_daily_climatology.py
|
|
3
|
+
#
|
|
4
|
+
# Script to calculate daily mean and quantile climatology
|
|
5
|
+
# for OSTIA sea surface temperature data.
|
|
6
|
+
#
|
|
7
|
+
# Created By: Oliver Tooth (oliver.tooth@noc.ac.uk)
|
|
8
|
+
# =========================================================
|
|
9
|
+
import argparse
|
|
10
|
+
import logging
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
import xarray as xr
|
|
14
|
+
import zarr
|
|
15
|
+
from dask.distributed import Client
|
|
16
|
+
|
|
17
|
+
from OceanDataStore.cli import initialise_logging
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
def pooled_days(day: int, half_width: int=5, ndays: int=366):
|
|
22
|
+
"""
|
|
23
|
+
Return cyclic day window centred on `day`.
|
|
24
|
+
"""
|
|
25
|
+
offsets = np.arange(-half_width, half_width + 1)
|
|
26
|
+
return ((day - 1 + offsets) % ndays) + 1
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def main(start_year, end_year, data_path="/dssgfs01/working/otooth/data/observations/OSTIA/", output="sst_climatology.zarr"):
|
|
30
|
+
|
|
31
|
+
# ========== Initialise OceanDataStore Logging ========== #
|
|
32
|
+
initialise_logging()
|
|
33
|
+
|
|
34
|
+
# ========== Initialise Dask Client ========== #
|
|
35
|
+
client = Client(n_workers=20, threads_per_worker=1)
|
|
36
|
+
logging.info(f"Dask client initialized: {client}")
|
|
37
|
+
|
|
38
|
+
# ========== Compute Daily Climatology from OSTIA Dataset ========== #
|
|
39
|
+
logging.info(f"Computing OSTIA Daily Climatology from {start_year} to {end_year}")
|
|
40
|
+
|
|
41
|
+
# Open multiple files
|
|
42
|
+
ds_ostia_rep = xr.open_mfdataset([f"{data_path}ostia_global_sst_daily_NA_1990_1999.zarr",
|
|
43
|
+
f"{data_path}ostia_global_sst_daily_NA_2000_2025.zarr"
|
|
44
|
+
], engine="zarr", parallel=True)
|
|
45
|
+
|
|
46
|
+
ds_ostia_nrt = xr.open_dataset(f"{data_path}ostia_global_sst_daily_NA_nrt_2025_2026.zarr",
|
|
47
|
+
engine="zarr",
|
|
48
|
+
)
|
|
49
|
+
ds_ostia_nrt = ds_ostia_nrt.sel(time=slice("2025-12-19", None))
|
|
50
|
+
|
|
51
|
+
ds = xr.concat([ds_ostia_rep, ds_ostia_nrt], dim="time")
|
|
52
|
+
ds = ds.sel(time=slice(f"{start_year}-01-01", f"{end_year}-12-31"))
|
|
53
|
+
logging.info(f"Completed: Opened OSTIA dataset for years: {start_year} to {end_year}")
|
|
54
|
+
|
|
55
|
+
ds = ds.chunk({
|
|
56
|
+
"time": -1,
|
|
57
|
+
"latitude": 100,
|
|
58
|
+
"longitude": 100
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
# Add Climatological Day of Year (clim_day) coordinate to the dataset
|
|
62
|
+
doy = ds['time'].dt.dayofyear
|
|
63
|
+
is_leap = ds['time'].dt.is_leap_year
|
|
64
|
+
after_feb28 = (~is_leap) & (doy >= 60)
|
|
65
|
+
clim_day = xr.where(after_feb28, doy + 1, doy)
|
|
66
|
+
ds = ds.assign_coords(clim_day=("time", clim_day.data))
|
|
67
|
+
|
|
68
|
+
# Rename variables and transform temperature from Kelvin to Celsius
|
|
69
|
+
ds = ds.rename({"analysed_sst": "tos",
|
|
70
|
+
"analysis_error": "tos_error",
|
|
71
|
+
"sea_ice_fraction": "siconc",
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
ds['tos'] = ds['tos'] - 273.15 # Convert from Kelvin to Celsius
|
|
75
|
+
ds['tos_error'] = ds['tos_error'] - 273.15 # Convert from Kelvin to Celsius
|
|
76
|
+
|
|
77
|
+
# Compute daily climatology (day of year):
|
|
78
|
+
for day in range(1, 367):
|
|
79
|
+
logging.info(f"Calculating Mean, 10th and 90th percentiles for day {day}...")
|
|
80
|
+
pooled = ds['tos'][ds['clim_day'].isin(pooled_days(day=day))]
|
|
81
|
+
|
|
82
|
+
# Build output dataset
|
|
83
|
+
clim = xr.Dataset()
|
|
84
|
+
clim["tos_mean"] = pooled.mean(dim="time", skipna=True)
|
|
85
|
+
clim["tos_p10"] = pooled.quantile(q=0.1, dim="time", skipna=True).astype(np.float32)
|
|
86
|
+
clim["tos_p90"] = pooled.quantile(q=0.9, dim="time", skipna=True).astype(np.float32)
|
|
87
|
+
clim = clim.expand_dims(clim_day=[day])
|
|
88
|
+
logging.info("Completed: Created OSTIA Daily Climatology SST dataset.")
|
|
89
|
+
|
|
90
|
+
clim = clim.chunk({
|
|
91
|
+
"clim_day": 1,
|
|
92
|
+
"latitude": 721,
|
|
93
|
+
"longitude": 1440
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
# Update variable encodings:
|
|
97
|
+
blosccodec = zarr.codecs.BloscCodec(cname="zstd", clevel=3, shuffle=zarr.codecs.BloscShuffle.shuffle)
|
|
98
|
+
for var in list(clim.data_vars) + list(clim.coords):
|
|
99
|
+
clim[var].encoding.clear()
|
|
100
|
+
clim[var].encoding['compressors'] = [blosccodec]
|
|
101
|
+
|
|
102
|
+
# Save output
|
|
103
|
+
logging.info(f"In Progress: Saving climatology to {output}...")
|
|
104
|
+
if day == 1:
|
|
105
|
+
clim.to_zarr(store=output, mode="w", zarr_format=3)
|
|
106
|
+
else:
|
|
107
|
+
clim.to_zarr(store=output, append_dim='clim_day', zarr_format=3)
|
|
108
|
+
logging.info(f"Completed: Saved climatology to {output}")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
if __name__ == "__main__":
|
|
112
|
+
parser = argparse.ArgumentParser(description="Compute SST Daily Climatology")
|
|
113
|
+
parser.add_argument("start_year", type=int, help="Start year (e.g. 2000)")
|
|
114
|
+
parser.add_argument("end_year", type=int, help="End year (e.g. 2010)")
|
|
115
|
+
parser.add_argument("--data_path", default=".", help="Directory containing SST files")
|
|
116
|
+
parser.add_argument("--output", default="sst_climatology.zarr", help="Output file")
|
|
117
|
+
|
|
118
|
+
args = parser.parse_args()
|
|
119
|
+
|
|
120
|
+
main(args.start_year, args.end_year, args.data_path, args.output)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Download North Atlantic subdomain of OSTIA Global SST dataset from Copernicus Marine.
|
|
3
|
+
This script downloads near-real time data from 2025 onwards.
|
|
4
|
+
|
|
5
|
+
Subdomain is defined as: (-45 degE, 14 degE, 31 degN, 85 degN)
|
|
6
|
+
|
|
7
|
+
Created By: Ollie Tooth
|
|
8
|
+
Created On: 2026-06-27
|
|
9
|
+
Contact: oliver.tooth@noc.ac.uk
|
|
10
|
+
|
|
11
|
+
Virtual Environment: env_ods.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
# Import the Copernicus Marine API toolbox:
|
|
15
|
+
import copernicusmarine
|
|
16
|
+
|
|
17
|
+
# Define filepath to credentials:
|
|
18
|
+
credentials_fpath = "~/.copernicusmarine/.copernicusmarine-credentials"
|
|
19
|
+
# Define output directory:
|
|
20
|
+
out_fdir = "/dssgfs01/working/otooth/data/observations/OSTIA/"
|
|
21
|
+
|
|
22
|
+
# Define start and end years:
|
|
23
|
+
year_start = 2025
|
|
24
|
+
year_end = 2026
|
|
25
|
+
|
|
26
|
+
# Download the OSTIA Global SST dataset for North Atlantic subdomain:
|
|
27
|
+
print(f"In Progress: Downloading OSTIA Global SST dataset for: {year_start} to {year_end}...")
|
|
28
|
+
copernicusmarine.subset(
|
|
29
|
+
dataset_id="METOFFICE-GLO-SST-L4-NRT-OBS-SST-V2",
|
|
30
|
+
variables=["analysed_sst", "analysis_error", "mask", "sea_ice_fraction"],
|
|
31
|
+
start_datetime=f"{year_start}-01-01T00:00:00",
|
|
32
|
+
end_datetime=f"{year_end}-12-31T23:59:59",
|
|
33
|
+
minimum_longitude=-45,
|
|
34
|
+
maximum_longitude=14,
|
|
35
|
+
minimum_latitude=31,
|
|
36
|
+
maximum_latitude=85,
|
|
37
|
+
credentials_file=credentials_fpath,
|
|
38
|
+
output_directory=out_fdir,
|
|
39
|
+
file_format="zarr",
|
|
40
|
+
output_filename=f"ostia_global_sst_daily_NA_nrt_{year_start}_{year_end}.zarr",
|
|
41
|
+
)
|
|
42
|
+
print(f"Completed: downloading OSTIA Global SST dataset for: {year_start} to {year_end}.")
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Download North Atlantic subdomain of OSTIA Global SST dataset from Copernicus Marine.
|
|
3
|
+
This script downloads the monthly reprocessed files from 1981 to 2025.
|
|
4
|
+
|
|
5
|
+
Subdomain is defined as: (-45 degE, 14 degE, 31 degN, 85 degN)
|
|
6
|
+
|
|
7
|
+
Created By: Ollie Tooth
|
|
8
|
+
Created On: 2026-06-27
|
|
9
|
+
Contact: oliver.tooth@noc.ac.uk
|
|
10
|
+
|
|
11
|
+
Virtual Environment: env_ods.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
# Import the Copernicus Marine API toolbox:
|
|
15
|
+
import copernicusmarine
|
|
16
|
+
|
|
17
|
+
# Define filepath to credentials:
|
|
18
|
+
credentials_fpath = "~/.copernicusmarine/.copernicusmarine-credentials"
|
|
19
|
+
# Define output directory:
|
|
20
|
+
out_fdir = "/dssgfs01/working/otooth/data/observations/OSTIA/"
|
|
21
|
+
|
|
22
|
+
# Define start and end years:
|
|
23
|
+
year_start = 2000
|
|
24
|
+
year_end = 2025
|
|
25
|
+
|
|
26
|
+
# Download the OSTIA Global SST dataset for North Atlantic subdomain:
|
|
27
|
+
print(f"In Progress: Downloading OSTIA Global SST dataset for: {year_start} to {year_end}...")
|
|
28
|
+
copernicusmarine.subset(
|
|
29
|
+
dataset_id="METOFFICE-GLO-SST-L4-REP-OBS-SST",
|
|
30
|
+
variables=["analysed_sst", "analysis_error", "mask", "sea_ice_fraction"],
|
|
31
|
+
start_datetime=f"{year_start}-01-01T00:00:00",
|
|
32
|
+
end_datetime=f"{year_end}-12-31T23:59:59",
|
|
33
|
+
minimum_longitude=-45,
|
|
34
|
+
maximum_longitude=14,
|
|
35
|
+
minimum_latitude=31,
|
|
36
|
+
maximum_latitude=85,
|
|
37
|
+
credentials_file=credentials_fpath,
|
|
38
|
+
output_directory=out_fdir,
|
|
39
|
+
file_format="zarr",
|
|
40
|
+
output_filename=f"ostia_global_sst_daily_NA_rep_{year_start}_{year_end}.zarr",
|
|
41
|
+
)
|
|
42
|
+
print(f"Completed: downloading OSTIA Global SST dataset for: {year_start} to {year_end}.")
|