Skip to content

Commit f20b5d9

Browse files
authored
Merge pull request #105 from MET-OM/fix-cmems-current
Fix cmems current
2 parents 4e21145 + ae7e4ca commit f20b5d9

8 files changed

Lines changed: 84 additions & 51 deletions

File tree

dnora/read/cmems_functions.py

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,20 @@
1-
from dnora.read.ds_read_functions import setup_temp_dir
2-
from dnora.type_manager.dnora_types import DnoraDataType
31
import os
42
import pandas as pd
53
import xarray as xr
64
from dnora import msg
5+
76
def ds_cmems_read(
87
start_time: pd.Timestamp,
98
end_time: pd.Timestamp,
109
url: str,
1110
## Partial variables from ProductReader
1211
lon: tuple[float],
1312
lat: tuple[float],
14-
name: str,
15-
data_type: DnoraDataType,
1613
## Partial variables in ProductConfiguration
1714
**kwargs
1815

1916
):
20-
temp_dir = setup_temp_dir(data_type, name)
21-
17+
2218
cred_file = os.path.expanduser("~/.copernicusmarine/.copernicusmarine-credentials")
2319
try:
2420
import copernicusmarine
@@ -32,8 +28,7 @@ def ds_cmems_read(
3228
)
3329

3430
copernicusmarine.login()
35-
36-
copernicusmarine.subset(
31+
ds = copernicusmarine.open_dataset(
3732
dataset_id=kwargs.get('dataset_id'),
3833
variables=kwargs.get('variables'),
3934
minimum_longitude=lon[0],
@@ -44,11 +39,7 @@ def ds_cmems_read(
4439
end_datetime=end_time.strftime("%Y-%m-%dT%H:%M:00"),
4540
minimum_depth=kwargs.get('minimum_depth'),
4641
maximum_depth=kwargs.get('maximum_depth'),
47-
output_directory=temp_dir,
48-
credentials_file=cred_file,
49-
force_download=True,
50-
output_filename=f"{name}_CMEMS_temp.nc",
42+
5143
)
5244

53-
ds = xr.open_dataset(f"{temp_dir}/{name}_CMEMS_temp.nc")
54-
return ds
45+
return ds

dnora/read/current/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
from . import metno
1+
from . import metno, cmems

dnora/read/current/cmems.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from dnora.type_manager.data_sources import DataSource
2+
from dnora.read.file_structure import FileStructure
3+
4+
from functools import partial
5+
from dnora.read.cmems_functions import ds_cmems_read
6+
from dnora.read.product_readers import ProductReader
7+
from dnora.read.product_configuration import ProductConfiguration
8+
from dnora.process.gridded import FillNaNs
9+
from dnora.read.depreciation_decorator import deprecated_class_call
10+
import geo_parameters as gp
11+
12+
@deprecated_class_call("CMEMS", "cmems", "current")
13+
class Global(ProductReader):
14+
"""This product is a L4 REP and NRT global total velocity field at 0m and 15m together wiht its individual components
15+
(geostrophy and Ekman) and related uncertainties. It consists of the zonal and meridional velocity at a 1h frequency
16+
and at 1/4 degree regular grid. The total velocity fields are obtained by combining CMEMS satellite Geostrophic surface
17+
currents and modelled Ekman currents at the surface and 15m depth (using ERA5 wind stress in REP and ERA5* in NRT).
18+
1 hourly product, daily and monthly means are available. This product has been initiated in the frame of CNES/CLS projects.
19+
Then it has been consolidated during the Globcurrent project (funded by the ESA User Element Program)..
20+
21+
DOI (product): https://doi.org/10.48670/mds-00327
22+
https://https://data.marine.copernicus.eu/product/MULTIOBS_GLO_PHY_MYNRT_015_003/description
23+
"""
24+
25+
product_configuration = ProductConfiguration(
26+
ds_creator_function=partial(
27+
ds_cmems_read,
28+
dataset_id="cmems_obs-mob_glo_phy-cur_my_0.25deg_PT1H-i",
29+
variables=["uo","vo"],
30+
minimum_depth=0,
31+
maximum_depth=0,
32+
),
33+
ds_aliases={"uo": gp.ocean.XCurrent, "vo": gp.ocean.YCurrent},
34+
default_data_source=DataSource.REMOTE,
35+
)
36+
37+
file_structure = FileStructure()
38+
39+
def post_processing(self):
40+
return FillNaNs(0)

dnora/read/ds_read_functions.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,8 @@ def read_ds_list(
278278
)
279279

280280
if ds is not None:
281-
msg.from_file(url)
281+
if url:
282+
msg.from_file(url)
282283
if not ds_list:
283284
keys = list(ds.sizes.keys())
284285
expected_shape = tuple(

dnora/read/product_configuration.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def get_constant_url(folder, filename, file_times, **kwargs) -> list[str]:
3232

3333
@dataclass
3434
class ProductConfiguration:
35-
filename: str = "model_output_%Y%m.nc"
35+
filename: str = None
3636
default_filenames: dict[DataSource, str] = field(default_factory=dict)
3737
default_folders: dict[DataSource, str] = field(default_factory=dict)
3838
tile: Optional[str] = None

dnora/read/waterlevel/ec.py

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def download_GTSM_from_cds(start_time, end_time, folder="dnora_wlv_temp") -> str
3939

4040
c = cdsapi.Client()
4141

42-
filename = f"{folder}/EC_GTSM_ERA5.tar.gz"
42+
filename = f"{folder}/EC_GTSM_ERA5.zip"
4343
# cds_command_test = {
4444
# 'product_type': 'reanalysis',
4545
# 'format': 'netcdf',
@@ -75,12 +75,12 @@ def download_GTSM_from_cds(start_time, end_time, folder="dnora_wlv_temp") -> str
7575
months = months[0]
7676

7777
cds_command = {
78-
"data_format": "tgz",
7978
"variable": ["total_water_level"],
8079
"experiment": "reanalysis",
8180
"temporal_aggregation": "hourly",
8281
"year": years, # 1979-2018
8382
"month": months,
83+
"version": ["v3"]
8484
}
8585

8686
c.retrieve("sis-water-level-change-timeseries-cmip6", cds_command, filename)
@@ -120,26 +120,23 @@ def __call__(
120120
out_file = download_GTSM_from_cds(start_time, end_time, folder=temp_folder)
121121

122122
temppath = os.path.dirname(out_file)
123-
# first unpack the tar.gz file.
124-
nc_file = (
125-
subprocess.run(["tar", "-ztf", out_file], stdout=subprocess.PIPE)
126-
.stdout.decode("utf-8")
127-
.split("\n")[0:-1]
128-
)
129-
nc_file = sorted([ff.strip("\r") for ff in nc_file])
130-
# print(nc_file)
131-
subprocess.run(
132-
["tar", "-xzvf", out_file, "--directory", temppath], stdout=subprocess.PIPE
133-
) # Extract tar file
134-
123+
124+
import zipfile
125+
with zipfile.ZipFile(out_file, "r") as zip_ref:
126+
nc_files = zip_ref.namelist()
127+
128+
# Extract all files to the specified directory
129+
zip_ref.extractall(temppath)
130+
131+
135132
lon_local = np.arange(lon[0], lon[1], 0.1)
136133
lat_local = np.arange(lat[0], lat[1], 0.1)
137134
grid_x, grid_y = np.meshgrid(lon_local, lat_local, indexing="xy")
138135

139-
print(nc_file)
136+
print(nc_files)
140137
grid_tot = []
141138
time_tot = []
142-
for ncfile in nc_file:
139+
for ncfile in nc_files:
143140
# print(os.path.join(temppath,nc_file))
144141
waterlevel = xr.open_dataset(
145142
os.path.join(temppath, ncfile), engine="netcdf4"
@@ -190,7 +187,8 @@ def __call__(
190187
# )
191188

192189
# print(waterlevel_gridded)
193-
coord_dict = {"lon": lon_local, "lat": lat_local, "time": time}
190+
191+
coord_dict = {"lon": lon_local, "lat": lat_local, "time": time_tot}
194192
data_dict = {"eta": grid_tot}
195193
meta_dict = {"description": "Waterlevel from GTSM/ERA5"}
196194

dnora/utils/io.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -55,22 +55,25 @@ def get_url(
5555
folder = time_stamp.strftime(folder)
5656
url = []
5757
for fn in filename:
58-
url_temp = Path(folder).joinpath(fn)
59-
# If we are on a Windows machine, then / will be replaced by \
60-
# If we have an url, then we still want /
61-
if "http" in str(url_temp) or "ftp" in str(url_temp):
62-
url_temp = url_temp.as_posix()
63-
else:
64-
url_temp = str(url_temp)
65-
66-
url_temp = re.sub("https:/", "https://", url_temp, 1)
67-
url_temp = re.sub("http:/", "http://", url_temp, 1)
68-
url_temp = re.sub("ftp:/", "ftp://", url_temp, 1)
69-
if time_stamp is not None:
70-
for floor_hour in range(1, 24):
71-
hfloor = int(np.floor(time_stamp.hour / floor_hour) * floor_hour)
72-
url_temp = re.sub(f"\\[{floor_hour}\\]", f"{hfloor:02.0f}", url_temp)
58+
if fn: # Otherwise '' will become '.'
59+
url_temp = Path(folder).joinpath(fn)
60+
# If we are on a Windows machine, then / will be replaced by \
61+
# If we have an url, then we still want /
62+
if "http" in str(url_temp) or "ftp" in str(url_temp):
63+
url_temp = url_temp.as_posix()
64+
else:
65+
url_temp = str(url_temp)
7366

67+
url_temp = re.sub("https:/", "https://", url_temp, 1)
68+
url_temp = re.sub("http:/", "http://", url_temp, 1)
69+
url_temp = re.sub("ftp:/", "ftp://", url_temp, 1)
70+
if time_stamp is not None:
71+
for floor_hour in range(1, 24):
72+
hfloor = int(np.floor(time_stamp.hour / floor_hour) * floor_hour)
73+
url_temp = re.sub(f"\\[{floor_hour}\\]", f"{hfloor:02.0f}", url_temp)
74+
else:
75+
url_temp = fn
76+
7477
url.append(url_temp)
7578
if len(url) == 1 and not get_list:
7679
return os.path.expanduser(url[0])

tests/test_readers/test_remote_waterlevel_readers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ def cleanup():
3030
@pytest.mark.remote
3131
def test_era5(grid, timevec):
3232
cleanup()
33-
model = dn.modelrun.ModelRun(grid, year=2023, month=4, day=1)
33+
model = dn.modelrun.ModelRun(grid, start_time='2023-04-30 00:00', end_time='2023-05-01 23:00')
3434
model.import_waterlevel(dn.read.waterlevel.ec.GTSM_ERA5())
3535

36-
assert np.all(model.waterlevel().time() == timevec)
36+
assert np.all(model.waterlevel().time() == pd.date_range('2023-04-30 00:00', '2023-05-01 23:00', freq="1h"))
3737

3838
grid_is_covered(grid, model.waterlevel())
3939
cleanup()

0 commit comments

Comments
 (0)