roms-tools 1.7.0__py3-none-any.whl → 2.1.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.
- roms_tools/__init__.py +2 -1
- roms_tools/setup/boundary_forcing.py +246 -146
- roms_tools/setup/datasets.py +229 -69
- roms_tools/setup/download.py +13 -17
- roms_tools/setup/grid.py +777 -614
- roms_tools/setup/initial_conditions.py +168 -32
- roms_tools/setup/mask.py +115 -0
- roms_tools/setup/nesting.py +575 -0
- roms_tools/setup/plot.py +218 -63
- roms_tools/setup/regrid.py +4 -2
- roms_tools/setup/river_forcing.py +125 -29
- roms_tools/setup/surface_forcing.py +31 -25
- roms_tools/setup/tides.py +29 -14
- roms_tools/setup/topography.py +250 -153
- roms_tools/setup/utils.py +174 -44
- roms_tools/setup/vertical_coordinate.py +5 -16
- roms_tools/tests/test_setup/test_boundary_forcing.py +10 -5
- roms_tools/tests/test_setup/test_data/grid.zarr/.zattrs +0 -1
- roms_tools/tests/test_setup/test_data/grid.zarr/.zmetadata +56 -201
- roms_tools/tests/test_setup/test_data/grid.zarr/Cs_r/.zattrs +1 -1
- roms_tools/tests/test_setup/test_data/grid.zarr/Cs_w/.zattrs +1 -1
- roms_tools/tests/test_setup/test_data/grid.zarr/{layer_depth_rho → sigma_r}/.zarray +2 -6
- roms_tools/tests/test_setup/test_data/grid.zarr/sigma_r/.zattrs +7 -0
- roms_tools/tests/test_setup/test_data/grid.zarr/sigma_r/0 +0 -0
- roms_tools/tests/test_setup/test_data/grid.zarr/sigma_w/.zarray +20 -0
- roms_tools/tests/test_setup/test_data/grid.zarr/sigma_w/.zattrs +7 -0
- roms_tools/tests/test_setup/test_data/grid.zarr/sigma_w/0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zattrs +1 -2
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zmetadata +58 -203
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/Cs_r/.zattrs +1 -1
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/Cs_w/.zattrs +1 -1
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/h/.zattrs +1 -1
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/h/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_coarse/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_rho/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_u/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_v/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/sigma_r/.zarray +20 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/sigma_r/.zattrs +7 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/sigma_r/0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/sigma_w/.zarray +20 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/sigma_w/.zattrs +7 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/sigma_w/0 +0 -0
- roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/.zmetadata +2 -3
- roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_tracer/.zattrs +1 -2
- roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/tracer_name/.zarray +1 -1
- roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/tracer_name/0 +0 -0
- roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/.zmetadata +5 -6
- roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_tracer/.zarray +2 -2
- roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_tracer/.zattrs +1 -2
- roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_tracer/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/tracer_name/.zarray +2 -2
- roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/tracer_name/0 +0 -0
- roms_tools/tests/test_setup/test_datasets.py +2 -2
- roms_tools/tests/test_setup/test_grid.py +110 -12
- roms_tools/tests/test_setup/test_initial_conditions.py +2 -1
- roms_tools/tests/test_setup/test_nesting.py +489 -0
- roms_tools/tests/test_setup/test_river_forcing.py +53 -15
- roms_tools/tests/test_setup/test_surface_forcing.py +3 -22
- roms_tools/tests/test_setup/test_tides.py +2 -1
- roms_tools/tests/test_setup/test_topography.py +106 -1
- roms_tools/tests/test_setup/test_validation.py +2 -2
- {roms_tools-1.7.0.dist-info → roms_tools-2.1.0.dist-info}/LICENSE +1 -1
- {roms_tools-1.7.0.dist-info → roms_tools-2.1.0.dist-info}/METADATA +9 -4
- {roms_tools-1.7.0.dist-info → roms_tools-2.1.0.dist-info}/RECORD +85 -108
- {roms_tools-1.7.0.dist-info → roms_tools-2.1.0.dist-info}/WHEEL +1 -1
- roms_tools/_version.py +0 -2
- roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_rho/.zarray +0 -24
- roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_rho/.zattrs +0 -9
- roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_rho/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_u/.zarray +0 -24
- roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_u/.zattrs +0 -9
- roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_u/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_v/.zarray +0 -24
- roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_v/.zattrs +0 -9
- roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_v/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_rho/.zattrs +0 -9
- roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_rho/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_u/.zarray +0 -24
- roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_u/.zattrs +0 -9
- roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_u/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_v/.zarray +0 -24
- roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_v/.zattrs +0 -9
- roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_v/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_rho/.zarray +0 -24
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_rho/.zattrs +0 -9
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_rho/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_u/.zarray +0 -24
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_u/.zattrs +0 -9
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_u/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_v/.zarray +0 -24
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_v/.zattrs +0 -9
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_v/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_rho/.zarray +0 -24
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_rho/.zattrs +0 -9
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_rho/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_u/.zarray +0 -24
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_u/.zattrs +0 -9
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_u/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_v/.zarray +0 -24
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_v/.zattrs +0 -9
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_v/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/river_forcing.zarr/river_tracer/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/river_forcing.zarr/tracer_name/0 +0 -0
- roms_tools/tests/test_setup/test_vertical_coordinate.py +0 -91
- /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/.zattrs +0 -0
- /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/.zgroup +0 -0
- /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/abs_time/.zarray +0 -0
- /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/abs_time/.zattrs +0 -0
- /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/abs_time/0 +0 -0
- /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/month/.zarray +0 -0
- /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/month/.zattrs +0 -0
- /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/month/0 +0 -0
- /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_name/.zarray +0 -0
- /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_name/.zattrs +0 -0
- /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_name/0 +0 -0
- /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_time/.zarray +0 -0
- /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_time/.zattrs +0 -0
- /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_time/0 +0 -0
- /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_volume/.zarray +0 -0
- /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_volume/.zattrs +0 -0
- /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_volume/0.0 +0 -0
- /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/tracer_name/.zattrs +0 -0
- {roms_tools-1.7.0.dist-info → roms_tools-2.1.0.dist-info}/top_level.txt +0 -0
roms_tools/setup/datasets.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import time
|
|
1
2
|
import re
|
|
2
3
|
import xarray as xr
|
|
3
4
|
from dataclasses import dataclass, field
|
|
@@ -15,7 +16,11 @@ from roms_tools.setup.utils import (
|
|
|
15
16
|
one_dim_fill,
|
|
16
17
|
gc_dist,
|
|
17
18
|
)
|
|
18
|
-
from roms_tools.setup.download import
|
|
19
|
+
from roms_tools.setup.download import (
|
|
20
|
+
download_correction_data,
|
|
21
|
+
download_topo,
|
|
22
|
+
download_river_data,
|
|
23
|
+
)
|
|
19
24
|
from roms_tools.setup.fill import LateralFill
|
|
20
25
|
|
|
21
26
|
# lat-lon datasets
|
|
@@ -74,7 +79,7 @@ class Dataset:
|
|
|
74
79
|
)
|
|
75
80
|
var_names: Dict[str, str]
|
|
76
81
|
climatology: Optional[bool] = False
|
|
77
|
-
use_dask: Optional[bool] =
|
|
82
|
+
use_dask: Optional[bool] = False
|
|
78
83
|
apply_post_processing: Optional[bool] = True
|
|
79
84
|
|
|
80
85
|
is_global: bool = field(init=False, repr=False)
|
|
@@ -117,6 +122,8 @@ class Dataset:
|
|
|
117
122
|
|
|
118
123
|
# Make sure that latitude is ascending
|
|
119
124
|
ds = self.ensure_dimension_is_ascending(ds, dim="latitude")
|
|
125
|
+
# Make sure there are no 360 degree jumps in longitude
|
|
126
|
+
ds = self.ensure_dimension_is_ascending(ds, dim="longitude")
|
|
120
127
|
|
|
121
128
|
if "depth" in self.dim_names:
|
|
122
129
|
# Make sure that depth is ascending
|
|
@@ -126,11 +133,6 @@ class Dataset:
|
|
|
126
133
|
|
|
127
134
|
# Check whether the data covers the entire globe
|
|
128
135
|
object.__setattr__(self, "is_global", self.check_if_global(ds))
|
|
129
|
-
|
|
130
|
-
# If dataset is global concatenate three copies of field along longitude dimension
|
|
131
|
-
if self.is_global:
|
|
132
|
-
ds = self.concatenate_longitudes(ds)
|
|
133
|
-
|
|
134
136
|
object.__setattr__(self, "ds", ds)
|
|
135
137
|
|
|
136
138
|
if self.apply_post_processing:
|
|
@@ -289,7 +291,11 @@ class Dataset:
|
|
|
289
291
|
) -> xr.Dataset:
|
|
290
292
|
"""Ensure that the specified dimension in the dataset is in ascending order.
|
|
291
293
|
|
|
292
|
-
|
|
294
|
+
This function checks the order of values along the specified dimension. If they
|
|
295
|
+
are in descending order, it reverses the dimension to make it ascending. For
|
|
296
|
+
the "longitude" dimension, if it has a discontinuity (e.g., [0, 180][-180, 0]),
|
|
297
|
+
the function adjusts values to eliminate the 360-degree jump, transforming
|
|
298
|
+
the range into a continuous [0, 360) span.
|
|
293
299
|
|
|
294
300
|
Parameters
|
|
295
301
|
----------
|
|
@@ -303,14 +309,23 @@ class Dataset:
|
|
|
303
309
|
-------
|
|
304
310
|
xr.Dataset
|
|
305
311
|
A new `xarray.Dataset` with the specified dimension in ascending order.
|
|
306
|
-
If the dimension was already in ascending order, the original dataset is returned unchanged.
|
|
307
|
-
If the dimension was in descending order, the dataset is returned with the dimension reversed.
|
|
312
|
+
- If the dimension was already in ascending order, the original dataset is returned unchanged.
|
|
313
|
+
- If the dimension was in descending order, the dataset is returned with the dimension reversed.
|
|
314
|
+
- If the dimension is "longitude" with a discontinuity (e.g., [0, 180][-180, 0]), the values are adjusted to eliminate the 360-degree jump.
|
|
308
315
|
"""
|
|
309
|
-
#
|
|
316
|
+
# Check if the dimension is in descending order and reverse if needed
|
|
310
317
|
diff = np.diff(ds[self.dim_names[dim]])
|
|
311
318
|
if np.all(diff < 0):
|
|
312
319
|
ds = ds.isel(**{self.dim_names[dim]: slice(None, None, -1)})
|
|
313
320
|
|
|
321
|
+
# Check for a discontinuity in longitude and adjust values if present
|
|
322
|
+
elif np.any(diff < 0) and dim == "longitude":
|
|
323
|
+
ds[self.dim_names[dim]] = xr.where(
|
|
324
|
+
ds[self.dim_names[dim]] < 0,
|
|
325
|
+
ds[self.dim_names[dim]] + 360,
|
|
326
|
+
ds[self.dim_names[dim]],
|
|
327
|
+
)
|
|
328
|
+
|
|
314
329
|
return ds
|
|
315
330
|
|
|
316
331
|
def infer_horizontal_resolution(self, ds: xr.Dataset):
|
|
@@ -364,43 +379,68 @@ class Dataset:
|
|
|
364
379
|
|
|
365
380
|
return is_global
|
|
366
381
|
|
|
367
|
-
def concatenate_longitudes(self, ds):
|
|
368
|
-
"""
|
|
369
|
-
Concatenates the field three times: with longitudes shifted by -360, original longitudes, and shifted by +360.
|
|
382
|
+
def concatenate_longitudes(self, ds, end="upper", verbose=False):
|
|
383
|
+
"""Concatenates fields in dataset twice along the longitude dimension.
|
|
370
384
|
|
|
371
385
|
Parameters
|
|
372
386
|
----------
|
|
373
|
-
|
|
374
|
-
The
|
|
387
|
+
ds: xr.Dataset
|
|
388
|
+
The dataset to be concatenated. The longitude dimension must be present in this dataset.
|
|
389
|
+
end : str, optional
|
|
390
|
+
Specifies which end to shift the longitudes.
|
|
391
|
+
Options are:
|
|
392
|
+
- "lower": shifts longitudes by -360 degrees and concatenates to the lower end.
|
|
393
|
+
- "upper": shifts longitudes by +360 degrees and concatenates to the upper end.
|
|
394
|
+
- "both": shifts longitudes by -360 degrees and 360 degrees and concatenates to both ends.
|
|
395
|
+
Default is "upper".
|
|
396
|
+
verbose : bool, optional
|
|
397
|
+
If True, print message if dataset is concatenated along longitude dimension.
|
|
398
|
+
Defaults to False.
|
|
375
399
|
|
|
376
400
|
Returns
|
|
377
401
|
-------
|
|
378
|
-
xr.
|
|
379
|
-
The concatenated
|
|
402
|
+
ds_concatenated : xr.Dataset
|
|
403
|
+
The concatenated dataset.
|
|
404
|
+
"""
|
|
380
405
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
Concatenating three times may be overkill in most situations, but it is safe. Alternatively, we could refactor
|
|
384
|
-
to figure out whether concatenating on the lower end, upper end, or at all is needed.
|
|
406
|
+
if verbose:
|
|
407
|
+
start_time = time.time()
|
|
385
408
|
|
|
386
|
-
"""
|
|
387
409
|
ds_concatenated = xr.Dataset()
|
|
388
410
|
|
|
389
411
|
lon = ds[self.dim_names["longitude"]]
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
412
|
+
if end == "lower":
|
|
413
|
+
lon_minus360 = lon - 360
|
|
414
|
+
lon_concatenated = xr.concat(
|
|
415
|
+
[lon_minus360, lon], dim=self.dim_names["longitude"]
|
|
416
|
+
)
|
|
395
417
|
|
|
396
|
-
|
|
418
|
+
elif end == "upper":
|
|
419
|
+
lon_plus360 = lon + 360
|
|
420
|
+
lon_concatenated = xr.concat(
|
|
421
|
+
[lon, lon_plus360], dim=self.dim_names["longitude"]
|
|
422
|
+
)
|
|
397
423
|
|
|
398
|
-
|
|
424
|
+
elif end == "both":
|
|
425
|
+
lon_minus360 = lon - 360
|
|
426
|
+
lon_plus360 = lon + 360
|
|
427
|
+
lon_concatenated = xr.concat(
|
|
428
|
+
[lon_minus360, lon, lon_plus360], dim=self.dim_names["longitude"]
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
for var in ds.data_vars:
|
|
399
432
|
if self.dim_names["longitude"] in ds[var].dims:
|
|
400
433
|
field = ds[var]
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
434
|
+
|
|
435
|
+
if end == "both":
|
|
436
|
+
field_concatenated = xr.concat(
|
|
437
|
+
[field, field, field], dim=self.dim_names["longitude"]
|
|
438
|
+
)
|
|
439
|
+
else:
|
|
440
|
+
field_concatenated = xr.concat(
|
|
441
|
+
[field, field], dim=self.dim_names["longitude"]
|
|
442
|
+
)
|
|
443
|
+
|
|
404
444
|
if self.use_dask:
|
|
405
445
|
field_concatenated = field_concatenated.chunk(
|
|
406
446
|
{self.dim_names["longitude"]: -1}
|
|
@@ -410,6 +450,13 @@ class Dataset:
|
|
|
410
450
|
else:
|
|
411
451
|
ds_concatenated[var] = ds[var]
|
|
412
452
|
|
|
453
|
+
ds_concatenated[self.dim_names["longitude"]] = lon_concatenated
|
|
454
|
+
|
|
455
|
+
if verbose:
|
|
456
|
+
logging.info(
|
|
457
|
+
f"Concatenating the data along the longitude dimension: {time.time() - start_time:.3f} seconds"
|
|
458
|
+
)
|
|
459
|
+
|
|
413
460
|
return ds_concatenated
|
|
414
461
|
|
|
415
462
|
def post_process(self):
|
|
@@ -423,7 +470,9 @@ class Dataset:
|
|
|
423
470
|
"""
|
|
424
471
|
pass
|
|
425
472
|
|
|
426
|
-
def choose_subdomain(
|
|
473
|
+
def choose_subdomain(
|
|
474
|
+
self, target_coords, buffer_points=20, return_copy=False, verbose=False
|
|
475
|
+
):
|
|
427
476
|
"""Selects a subdomain from the xarray Dataset based on specified target
|
|
428
477
|
coordinates, extending the selection by a defined buffer. Adjusts longitude
|
|
429
478
|
ranges as necessary to accommodate the dataset's expected range and handles
|
|
@@ -440,6 +489,9 @@ class Dataset:
|
|
|
440
489
|
return_subdomain : bool, optional
|
|
441
490
|
If True, returns the subset of the original dataset representing the chosen
|
|
442
491
|
subdomain. If False, assigns the subset to `self.ds`. Defaults to False.
|
|
492
|
+
verbose : bool, optional
|
|
493
|
+
If True, print message if dataset is concatenated along longitude dimension.
|
|
494
|
+
Defaults to False.
|
|
443
495
|
|
|
444
496
|
Returns
|
|
445
497
|
-------
|
|
@@ -462,9 +514,43 @@ class Dataset:
|
|
|
462
514
|
|
|
463
515
|
margin = self.resolution * buffer_points
|
|
464
516
|
|
|
465
|
-
if
|
|
517
|
+
# Select the subdomain in latitude direction (so that we have to concatenate fewer latitudes below if concatenation is necessary)
|
|
518
|
+
subdomain = self.ds.sel(
|
|
519
|
+
**{
|
|
520
|
+
self.dim_names["latitude"]: slice(lat_min - margin, lat_max + margin),
|
|
521
|
+
}
|
|
522
|
+
)
|
|
523
|
+
lon = subdomain[self.dim_names["longitude"]]
|
|
524
|
+
|
|
525
|
+
if self.is_global:
|
|
526
|
+
# Concatenate only if necessary
|
|
527
|
+
if lon_max + margin > lon.max():
|
|
528
|
+
# See if shifting by +360 degrees helps
|
|
529
|
+
if (lon_min - margin > (lon + 360).min()) and (
|
|
530
|
+
lon_max + margin < (lon + 360).max()
|
|
531
|
+
):
|
|
532
|
+
subdomain[self.dim_names["longitude"]] = lon + 360
|
|
533
|
+
lon = subdomain[self.dim_names["longitude"]]
|
|
534
|
+
else:
|
|
535
|
+
subdomain = self.concatenate_longitudes(
|
|
536
|
+
subdomain, end="upper", verbose=verbose
|
|
537
|
+
)
|
|
538
|
+
lon = subdomain[self.dim_names["longitude"]]
|
|
539
|
+
if lon_min - margin < lon.min():
|
|
540
|
+
# See if shifting by -360 degrees helps
|
|
541
|
+
if (lon_min - margin > (lon - 360).min()) and (
|
|
542
|
+
lon_max + margin < (lon - 360).max()
|
|
543
|
+
):
|
|
544
|
+
subdomain[self.dim_names["longitude"]] = lon - 360
|
|
545
|
+
lon = subdomain[self.dim_names["longitude"]]
|
|
546
|
+
else:
|
|
547
|
+
subdomain = self.concatenate_longitudes(
|
|
548
|
+
subdomain, end="lower", verbose=verbose
|
|
549
|
+
)
|
|
550
|
+
lon = subdomain[self.dim_names["longitude"]]
|
|
551
|
+
|
|
552
|
+
else:
|
|
466
553
|
# Adjust longitude range if needed to match the expected range
|
|
467
|
-
lon = self.ds[self.dim_names["longitude"]]
|
|
468
554
|
if not target_coords["straddle"]:
|
|
469
555
|
if lon.min() < -180:
|
|
470
556
|
if lon_max + margin > 0:
|
|
@@ -484,12 +570,9 @@ class Dataset:
|
|
|
484
570
|
if lon_min - margin < 0:
|
|
485
571
|
lon_min += 360
|
|
486
572
|
lon_max += 360
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
subdomain = self.ds.sel(
|
|
573
|
+
# Select the subdomain in longitude direction
|
|
574
|
+
subdomain = subdomain.sel(
|
|
491
575
|
**{
|
|
492
|
-
self.dim_names["latitude"]: slice(lat_min - margin, lat_max + margin),
|
|
493
576
|
self.dim_names["longitude"]: slice(lon_min - margin, lon_max + margin),
|
|
494
577
|
}
|
|
495
578
|
)
|
|
@@ -1286,7 +1369,7 @@ class ERA5Correction(Dataset):
|
|
|
1286
1369
|
|
|
1287
1370
|
super().__post_init__()
|
|
1288
1371
|
|
|
1289
|
-
def choose_subdomain(self,
|
|
1372
|
+
def choose_subdomain(self, target_coords, straddle: bool):
|
|
1290
1373
|
"""Converts longitude values in the dataset if necessary and selects a subdomain
|
|
1291
1374
|
based on the specified coordinates.
|
|
1292
1375
|
|
|
@@ -1295,7 +1378,7 @@ class ERA5Correction(Dataset):
|
|
|
1295
1378
|
|
|
1296
1379
|
Parameters
|
|
1297
1380
|
----------
|
|
1298
|
-
|
|
1381
|
+
target_coords : dict
|
|
1299
1382
|
A dictionary specifying the target coordinates for selecting the subdomain. Keys should correspond to the
|
|
1300
1383
|
dimension names of the dataset (e.g., latitude and longitude), and values should be the desired ranges or
|
|
1301
1384
|
specific coordinate values.
|
|
@@ -1314,38 +1397,122 @@ class ERA5Correction(Dataset):
|
|
|
1314
1397
|
- The dataset (`self.ds`) is updated in place to reflect the chosen subdomain.
|
|
1315
1398
|
"""
|
|
1316
1399
|
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
if not self.is_global:
|
|
1320
|
-
if lon.min().values < 0 and not straddle:
|
|
1321
|
-
# Convert from [-180, 180] to [0, 360]
|
|
1322
|
-
self.ds[self.dim_names["longitude"]] = xr.where(lon < 0, lon + 360, lon)
|
|
1400
|
+
# Select the subdomain in latitude direction (so that we have to concatenate fewer latitudes below if concatenation is performed)
|
|
1401
|
+
subdomain = self.ds.sel({self.dim_names["latitude"]: target_coords["lat"]})
|
|
1323
1402
|
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1403
|
+
if self.is_global:
|
|
1404
|
+
# Always concatenate because computational overhead should be managable for 1/4 degree ERA5 resolution
|
|
1405
|
+
subdomain = self.concatenate_longitudes(
|
|
1406
|
+
subdomain, end="both", verbose=False
|
|
1407
|
+
)
|
|
1329
1408
|
|
|
1330
|
-
# Select the subdomain
|
|
1331
|
-
subdomain =
|
|
1409
|
+
# Select the subdomain in longitude direction
|
|
1410
|
+
subdomain = subdomain.sel({self.dim_names["longitude"]: target_coords["lon"]})
|
|
1332
1411
|
|
|
1333
1412
|
# Check if the selected subdomain contains the specified latitude and longitude values
|
|
1334
|
-
if not subdomain[self.dim_names["latitude"]].equals(
|
|
1335
|
-
coords[self.dim_names["latitude"]]
|
|
1336
|
-
):
|
|
1413
|
+
if not subdomain[self.dim_names["latitude"]].equals(target_coords["lat"]):
|
|
1337
1414
|
raise ValueError(
|
|
1338
1415
|
"The correction dataset does not contain all specified latitude values."
|
|
1339
1416
|
)
|
|
1340
|
-
if not subdomain[self.dim_names["longitude"]].equals(
|
|
1341
|
-
coords[self.dim_names["longitude"]]
|
|
1342
|
-
):
|
|
1417
|
+
if not subdomain[self.dim_names["longitude"]].equals(target_coords["lon"]):
|
|
1343
1418
|
raise ValueError(
|
|
1344
1419
|
"The correction dataset does not contain all specified longitude values."
|
|
1345
1420
|
)
|
|
1346
1421
|
object.__setattr__(self, "ds", subdomain)
|
|
1347
1422
|
|
|
1348
1423
|
|
|
1424
|
+
@dataclass(frozen=True, kw_only=True)
|
|
1425
|
+
class ETOPO5Dataset(Dataset):
|
|
1426
|
+
"""Represents topography data on the original grid from the ETOPO5 dataset.
|
|
1427
|
+
|
|
1428
|
+
Parameters
|
|
1429
|
+
----------
|
|
1430
|
+
filename : str, optional
|
|
1431
|
+
The path to the ETOPO5 dataset file. If not provided, the dataset will be downloaded
|
|
1432
|
+
automatically via the `pooch` library.
|
|
1433
|
+
var_names : Dict[str, str], optional
|
|
1434
|
+
Dictionary of variable names required in the dataset. Defaults to:
|
|
1435
|
+
{
|
|
1436
|
+
"topo": "topo",
|
|
1437
|
+
}
|
|
1438
|
+
dim_names : Dict[str, str], optional
|
|
1439
|
+
Dictionary specifying the names of dimensions in the dataset. Defaults to:
|
|
1440
|
+
{"longitude": "lon", "latitude": "lat"}.
|
|
1441
|
+
|
|
1442
|
+
Attributes
|
|
1443
|
+
----------
|
|
1444
|
+
ds : xr.Dataset
|
|
1445
|
+
The xarray Dataset containing the ETOPO5 data, loaded from the specified file.
|
|
1446
|
+
"""
|
|
1447
|
+
|
|
1448
|
+
filename: str = field(default_factory=lambda: download_topo("etopo5.nc"))
|
|
1449
|
+
var_names: Dict[str, str] = field(
|
|
1450
|
+
default_factory=lambda: {
|
|
1451
|
+
"topo": "topo",
|
|
1452
|
+
}
|
|
1453
|
+
)
|
|
1454
|
+
dim_names: Dict[str, str] = field(
|
|
1455
|
+
default_factory=lambda: {"longitude": "lon", "latitude": "lat"}
|
|
1456
|
+
)
|
|
1457
|
+
ds: xr.Dataset = field(init=False, repr=False)
|
|
1458
|
+
|
|
1459
|
+
def clean_up(self, ds: xr.Dataset) -> xr.Dataset:
|
|
1460
|
+
"""Assign lat and lon as coordinates.
|
|
1461
|
+
|
|
1462
|
+
Parameters
|
|
1463
|
+
----------
|
|
1464
|
+
ds : xr.Dataset
|
|
1465
|
+
The input dataset.
|
|
1466
|
+
|
|
1467
|
+
Returns
|
|
1468
|
+
-------
|
|
1469
|
+
ds : xr.Dataset
|
|
1470
|
+
A cleaned `xarray.Dataset` with updated coordinates.
|
|
1471
|
+
"""
|
|
1472
|
+
ds = ds.assign_coords(
|
|
1473
|
+
{
|
|
1474
|
+
"lon": ds["topo_lon"],
|
|
1475
|
+
"lat": ds["topo_lat"],
|
|
1476
|
+
}
|
|
1477
|
+
)
|
|
1478
|
+
return ds
|
|
1479
|
+
|
|
1480
|
+
|
|
1481
|
+
@dataclass(frozen=True, kw_only=True)
|
|
1482
|
+
class SRTM15Dataset(Dataset):
|
|
1483
|
+
"""Represents topography data on the original grid from the SRTM15 dataset.
|
|
1484
|
+
|
|
1485
|
+
Parameters
|
|
1486
|
+
----------
|
|
1487
|
+
filename : str
|
|
1488
|
+
The path to the SRTM15 dataset file.
|
|
1489
|
+
var_names : Dict[str, str], optional
|
|
1490
|
+
Dictionary of variable names required in the dataset. Defaults to:
|
|
1491
|
+
{
|
|
1492
|
+
"topo": "z",
|
|
1493
|
+
}
|
|
1494
|
+
dim_names : Dict[str, str], optional
|
|
1495
|
+
Dictionary specifying the names of dimensions in the dataset. Defaults to:
|
|
1496
|
+
{"longitude": "lon", "latitude": "lat"}.
|
|
1497
|
+
|
|
1498
|
+
Attributes
|
|
1499
|
+
----------
|
|
1500
|
+
ds : xr.Dataset
|
|
1501
|
+
The xarray Dataset containing the SRTM15 data, loaded from the specified file.
|
|
1502
|
+
"""
|
|
1503
|
+
|
|
1504
|
+
filename: str
|
|
1505
|
+
var_names: Dict[str, str] = field(
|
|
1506
|
+
default_factory=lambda: {
|
|
1507
|
+
"topo": "z",
|
|
1508
|
+
}
|
|
1509
|
+
)
|
|
1510
|
+
dim_names: Dict[str, str] = field(
|
|
1511
|
+
default_factory=lambda: {"longitude": "lon", "latitude": "lat"}
|
|
1512
|
+
)
|
|
1513
|
+
ds: xr.Dataset = field(init=False, repr=False)
|
|
1514
|
+
|
|
1515
|
+
|
|
1349
1516
|
# river datasets
|
|
1350
1517
|
@dataclass(frozen=True, kw_only=True)
|
|
1351
1518
|
class RiverDataset:
|
|
@@ -1414,13 +1581,6 @@ class RiverDataset:
|
|
|
1414
1581
|
-------
|
|
1415
1582
|
ds : xr.Dataset
|
|
1416
1583
|
The loaded xarray Dataset containing the forcing data.
|
|
1417
|
-
|
|
1418
|
-
Raises
|
|
1419
|
-
------
|
|
1420
|
-
FileNotFoundError
|
|
1421
|
-
If the specified file does not exist.
|
|
1422
|
-
ValueError
|
|
1423
|
-
If a list of files is provided but self.dim_names["time"] is not available or use_dask=False.
|
|
1424
1584
|
"""
|
|
1425
1585
|
ds = _load_data(
|
|
1426
1586
|
self.filename, self.dim_names, use_dask=False, decode_times=False
|
roms_tools/setup/download.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import pooch
|
|
2
|
-
import xarray as xr
|
|
3
2
|
|
|
4
3
|
# Create a Pooch object to manage the global topography data
|
|
5
4
|
topo_data = pooch.create(
|
|
@@ -19,7 +18,6 @@ correction_data = pooch.create(
|
|
|
19
18
|
base_url="https://github.com/CWorthy-ocean/roms-tools-data/raw/main/",
|
|
20
19
|
# The registry specifies the files that can be fetched
|
|
21
20
|
registry={
|
|
22
|
-
"etopo5.nc": "sha256:23600e422d59bbf7c3666090166a0d468c8ee16092f4f14e32c4e928fbcd627b",
|
|
23
21
|
"SSR_correction.nc": "sha256:a170c1698e6cc2765b3f0bb51a18c6a979bc796ac3a4c014585aeede1f1f8ea0",
|
|
24
22
|
},
|
|
25
23
|
)
|
|
@@ -50,6 +48,7 @@ pup_test_data = pooch.create(
|
|
|
50
48
|
"ERA5_global_test_data.nc": "8ed177ab64c02caf509b9fb121cf6713f286cc603b1f302f15f3f4eb0c21dc4f",
|
|
51
49
|
"TPXO_global_test_data.nc": "457bfe87a7b247ec6e04e3c7d3e741ccf223020c41593f8ae33a14f2b5255e60",
|
|
52
50
|
"TPXO_regional_test_data.nc": "11739245e2286d9c9d342dce5221e6435d2072b50028bef2e86a30287b3b4032",
|
|
51
|
+
"CESM_BGC_coarse_global_clim.nc": "20806e4e99285d6de168d3236e2d9245f4e9106474b1464beaa266a73e6ef79f",
|
|
53
52
|
"CESM_BGC_2012.nc": "e374d5df3c1be742d564fd26fd861c2d40af73be50a432c51d258171d5638eb6",
|
|
54
53
|
"CESM_regional_test_data_one_time_slice.nc": "43b578ecc067c85f95d6b97ed7b9dc8da7846f07c95331c6ba7f4a3161036a17",
|
|
55
54
|
"CESM_regional_test_data_climatology.nc": "986a200029d9478fd43e6e4a8bc43e8a8f4407554893c59b5fcc2e86fd203272",
|
|
@@ -58,36 +57,33 @@ pup_test_data = pooch.create(
|
|
|
58
57
|
"CESM_surface_global_test_data_climatology.nc": "a072757110c6f7b716a98f867688ef4195a5966741d2f368201ac24617254e35",
|
|
59
58
|
"CESM_surface_global_test_data.nc": "874106ffbc8b1b220db09df1551bbb89d22439d795b4d1e5a24ee775e9a7bf6e",
|
|
60
59
|
"grid_created_with_matlab.nc": "fd537ef8159fabb18e38495ec8d44e2fa1b7fb615fcb1417dd4c0e1bb5f4e41d",
|
|
60
|
+
"etopo5_coarsened_and_shifted.nc": "9a5cb4b38c779d22ddb0ad069b298b9722db34ca85a89273eccca691e89e6f96",
|
|
61
|
+
"srtm15_coarsened.nc": "48bc8f4beecfdca9c192b13f4cbeef1455f49d8261a82563aaec5757e100dff9",
|
|
61
62
|
},
|
|
62
63
|
)
|
|
63
64
|
|
|
64
65
|
|
|
65
|
-
def
|
|
66
|
-
"""
|
|
66
|
+
def download_topo(filename: str) -> str:
|
|
67
|
+
"""Download simple topography file.
|
|
67
68
|
|
|
68
69
|
Parameters
|
|
69
70
|
----------
|
|
70
|
-
|
|
71
|
-
The
|
|
72
|
-
- "
|
|
71
|
+
filename : str
|
|
72
|
+
The name of the test data file to be downloaded. Available options:
|
|
73
|
+
- "etopo5.nc"
|
|
73
74
|
|
|
74
75
|
Returns
|
|
75
76
|
-------
|
|
76
|
-
|
|
77
|
-
The
|
|
77
|
+
str
|
|
78
|
+
The path to the downloaded test data file.
|
|
78
79
|
"""
|
|
79
|
-
# Mapping from user-specified topography options to corresponding filenames in the registry
|
|
80
|
-
topo_dict = {"ETOPO5": "etopo5.nc"}
|
|
81
|
-
|
|
82
80
|
# Fetch the file using Pooch, downloading if necessary
|
|
83
|
-
fname = topo_data.fetch(
|
|
81
|
+
fname = topo_data.fetch(filename)
|
|
84
82
|
|
|
85
|
-
|
|
86
|
-
ds = xr.open_dataset(fname)
|
|
87
|
-
return ds
|
|
83
|
+
return fname
|
|
88
84
|
|
|
89
85
|
|
|
90
|
-
def download_river_data(filename: str) ->
|
|
86
|
+
def download_river_data(filename: str) -> str:
|
|
91
87
|
"""Download river data file.
|
|
92
88
|
|
|
93
89
|
Parameters
|