roms-tools 2.0.0__py3-none-any.whl → 2.2.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 +22 -32
- roms_tools/setup/datasets.py +19 -21
- roms_tools/setup/grid.py +253 -139
- roms_tools/setup/initial_conditions.py +29 -6
- roms_tools/setup/mask.py +50 -4
- roms_tools/setup/nesting.py +575 -0
- roms_tools/setup/plot.py +214 -55
- roms_tools/setup/river_forcing.py +125 -29
- roms_tools/setup/surface_forcing.py +33 -12
- roms_tools/setup/tides.py +31 -6
- roms_tools/setup/topography.py +168 -35
- roms_tools/setup/utils.py +137 -21
- roms_tools/tests/test_setup/test_boundary_forcing.py +7 -5
- 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_initial_conditions.py +6 -6
- roms_tools/tests/test_setup/test_nesting.py +489 -0
- roms_tools/tests/test_setup/test_river_forcing.py +50 -13
- roms_tools/tests/test_setup/test_surface_forcing.py +9 -8
- roms_tools/tests/test_setup/test_tides.py +5 -5
- roms_tools/tests/test_setup/test_validation.py +2 -2
- {roms_tools-2.0.0.dist-info → roms_tools-2.2.0.dist-info}/METADATA +9 -5
- {roms_tools-2.0.0.dist-info → roms_tools-2.2.0.dist-info}/RECORD +54 -53
- {roms_tools-2.0.0.dist-info → roms_tools-2.2.0.dist-info}/WHEEL +1 -1
- roms_tools/_version.py +0 -2
- 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_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-2.0.0.dist-info → roms_tools-2.2.0.dist-info}/LICENSE +0 -0
- {roms_tools-2.0.0.dist-info → roms_tools-2.2.0.dist-info}/top_level.txt +0 -0
roms_tools/__init__.py
CHANGED
|
@@ -5,7 +5,7 @@ try:
|
|
|
5
5
|
__version__ = _version("roms_tools")
|
|
6
6
|
except ImportError: # pragma: no cover
|
|
7
7
|
# Local copy or not installed with setuptools
|
|
8
|
-
__version__ = "
|
|
8
|
+
__version__ = "9999"
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
from roms_tools.setup.grid import Grid # noqa: F401
|
|
@@ -14,6 +14,7 @@ from roms_tools.setup.surface_forcing import SurfaceForcing # noqa: F401
|
|
|
14
14
|
from roms_tools.setup.initial_conditions import InitialConditions # noqa: F401
|
|
15
15
|
from roms_tools.setup.boundary_forcing import BoundaryForcing # noqa: F401
|
|
16
16
|
from roms_tools.setup.river_forcing import RiverForcing # noqa: F401
|
|
17
|
+
from roms_tools.setup.nesting import Nesting # noqa: F401
|
|
17
18
|
|
|
18
19
|
# Configure logging when the package is imported
|
|
19
20
|
logging.basicConfig(level=logging.INFO, format="%(levelname)s - %(message)s")
|
|
@@ -24,6 +24,7 @@ from roms_tools.setup.utils import (
|
|
|
24
24
|
interpolate_from_rho_to_u,
|
|
25
25
|
interpolate_from_rho_to_v,
|
|
26
26
|
convert_to_roms_time,
|
|
27
|
+
get_boundary_coords,
|
|
27
28
|
_to_yaml,
|
|
28
29
|
_from_yaml,
|
|
29
30
|
)
|
|
@@ -71,6 +72,10 @@ class BoundaryForcing:
|
|
|
71
72
|
Reference date for the model. Default is January 1, 2000.
|
|
72
73
|
use_dask: bool, optional
|
|
73
74
|
Indicates whether to use dask for processing. If True, data is processed with dask; if False, data is processed eagerly. Defaults to False.
|
|
75
|
+
bypass_validation: bool, optional
|
|
76
|
+
Indicates whether to skip validation checks in the processed data. When set to True,
|
|
77
|
+
the validation process that ensures no NaN values exist at wet points
|
|
78
|
+
in the processed dataset is bypassed. Defaults to False.
|
|
74
79
|
|
|
75
80
|
Examples
|
|
76
81
|
--------
|
|
@@ -100,6 +105,7 @@ class BoundaryForcing:
|
|
|
100
105
|
apply_2d_horizontal_fill: bool = False
|
|
101
106
|
model_reference_date: datetime = datetime(2000, 1, 1)
|
|
102
107
|
use_dask: bool = False
|
|
108
|
+
bypass_validation: bool = False
|
|
103
109
|
|
|
104
110
|
ds: xr.Dataset = field(init=False, repr=False)
|
|
105
111
|
|
|
@@ -279,7 +285,8 @@ class BoundaryForcing:
|
|
|
279
285
|
# Add global information
|
|
280
286
|
ds = self._add_global_metadata(data, ds)
|
|
281
287
|
|
|
282
|
-
self.
|
|
288
|
+
if not self.bypass_validation:
|
|
289
|
+
self._validate(ds)
|
|
283
290
|
|
|
284
291
|
# substitute NaNs over land by a fill value to avoid blow-up of ROMS
|
|
285
292
|
for var_name in ds.data_vars:
|
|
@@ -459,7 +466,7 @@ class BoundaryForcing:
|
|
|
459
466
|
return ds
|
|
460
467
|
|
|
461
468
|
def _set_boundary_info(self):
|
|
462
|
-
"""
|
|
469
|
+
"""Sets boundary coordinates for rho, u, and v variables on the grid.
|
|
463
470
|
|
|
464
471
|
This method determines the boundary points for the grid variables by specifying the
|
|
465
472
|
indices for the south, east, north, and west boundaries. The resulting boundary
|
|
@@ -476,32 +483,7 @@ class BoundaryForcing:
|
|
|
476
483
|
grid indices for the respective variable types.
|
|
477
484
|
"""
|
|
478
485
|
|
|
479
|
-
bdry_coords =
|
|
480
|
-
"rho": {
|
|
481
|
-
"south": {"eta_rho": 0},
|
|
482
|
-
"east": {"xi_rho": -1},
|
|
483
|
-
"north": {"eta_rho": -1},
|
|
484
|
-
"west": {"xi_rho": 0},
|
|
485
|
-
},
|
|
486
|
-
"u": {
|
|
487
|
-
"south": {"eta_rho": 0},
|
|
488
|
-
"east": {"xi_u": -1},
|
|
489
|
-
"north": {"eta_rho": -1},
|
|
490
|
-
"west": {"xi_u": 0},
|
|
491
|
-
},
|
|
492
|
-
"v": {
|
|
493
|
-
"south": {"eta_v": 0},
|
|
494
|
-
"east": {"xi_rho": -1},
|
|
495
|
-
"north": {"eta_v": -1},
|
|
496
|
-
"west": {"xi_rho": 0},
|
|
497
|
-
},
|
|
498
|
-
"vector": {
|
|
499
|
-
"south": {"eta_rho": [0, 1]},
|
|
500
|
-
"east": {"xi_rho": [-2, -1]},
|
|
501
|
-
"north": {"eta_rho": [-2, -1]},
|
|
502
|
-
"west": {"xi_rho": [0, 1]},
|
|
503
|
-
},
|
|
504
|
-
}
|
|
486
|
+
bdry_coords = get_boundary_coords()
|
|
505
487
|
|
|
506
488
|
object.__setattr__(self, "bdry_coords", bdry_coords)
|
|
507
489
|
|
|
@@ -728,8 +710,7 @@ class BoundaryForcing:
|
|
|
728
710
|
variable in the dataset.
|
|
729
711
|
"""
|
|
730
712
|
for var_name in self.variable_info:
|
|
731
|
-
|
|
732
|
-
if not self.use_dask or self.variable_info[var_name]["validate"]:
|
|
713
|
+
if self.variable_info[var_name]["validate"]:
|
|
733
714
|
location = self.variable_info[var_name]["location"]
|
|
734
715
|
|
|
735
716
|
# Select the appropriate mask based on variable location
|
|
@@ -980,7 +961,10 @@ class BoundaryForcing:
|
|
|
980
961
|
|
|
981
962
|
@classmethod
|
|
982
963
|
def from_yaml(
|
|
983
|
-
cls,
|
|
964
|
+
cls,
|
|
965
|
+
filepath: Union[str, Path],
|
|
966
|
+
use_dask: bool = False,
|
|
967
|
+
bypass_validation: bool = False,
|
|
984
968
|
) -> "BoundaryForcing":
|
|
985
969
|
"""Create an instance of the BoundaryForcing class from a YAML file.
|
|
986
970
|
|
|
@@ -990,6 +974,10 @@ class BoundaryForcing:
|
|
|
990
974
|
The path to the YAML file from which the parameters will be read.
|
|
991
975
|
use_dask: bool, optional
|
|
992
976
|
Indicates whether to use dask for processing. If True, data is processed with dask; if False, data is processed eagerly. Defaults to False.
|
|
977
|
+
bypass_validation: bool, optional
|
|
978
|
+
Indicates whether to skip validation checks in the processed data. When set to True,
|
|
979
|
+
the validation process that ensures no NaN values exist at wet points
|
|
980
|
+
in the processed dataset is bypassed. Defaults to False.
|
|
993
981
|
|
|
994
982
|
Returns
|
|
995
983
|
-------
|
|
@@ -1002,7 +990,9 @@ class BoundaryForcing:
|
|
|
1002
990
|
params = _from_yaml(cls, filepath)
|
|
1003
991
|
|
|
1004
992
|
# Create and return an instance of InitialConditions
|
|
1005
|
-
return cls(
|
|
993
|
+
return cls(
|
|
994
|
+
grid=grid, **params, use_dask=use_dask, bypass_validation=bypass_validation
|
|
995
|
+
)
|
|
1006
996
|
|
|
1007
997
|
|
|
1008
998
|
def apply_1d_horizontal_fill(processed_fields: dict) -> dict:
|
roms_tools/setup/datasets.py
CHANGED
|
@@ -1369,7 +1369,7 @@ class ERA5Correction(Dataset):
|
|
|
1369
1369
|
|
|
1370
1370
|
super().__post_init__()
|
|
1371
1371
|
|
|
1372
|
-
def choose_subdomain(self,
|
|
1372
|
+
def choose_subdomain(self, target_coords, straddle: bool):
|
|
1373
1373
|
"""Converts longitude values in the dataset if necessary and selects a subdomain
|
|
1374
1374
|
based on the specified coordinates.
|
|
1375
1375
|
|
|
@@ -1378,7 +1378,7 @@ class ERA5Correction(Dataset):
|
|
|
1378
1378
|
|
|
1379
1379
|
Parameters
|
|
1380
1380
|
----------
|
|
1381
|
-
|
|
1381
|
+
target_coords : dict
|
|
1382
1382
|
A dictionary specifying the target coordinates for selecting the subdomain. Keys should correspond to the
|
|
1383
1383
|
dimension names of the dataset (e.g., latitude and longitude), and values should be the desired ranges or
|
|
1384
1384
|
specific coordinate values.
|
|
@@ -1397,32 +1397,24 @@ class ERA5Correction(Dataset):
|
|
|
1397
1397
|
- The dataset (`self.ds`) is updated in place to reflect the chosen subdomain.
|
|
1398
1398
|
"""
|
|
1399
1399
|
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
if not self.is_global:
|
|
1403
|
-
if lon.min().values < 0 and not straddle:
|
|
1404
|
-
# Convert from [-180, 180] to [0, 360]
|
|
1405
|
-
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"]})
|
|
1406
1402
|
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
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
|
+
)
|
|
1412
1408
|
|
|
1413
|
-
# Select the subdomain
|
|
1414
|
-
subdomain =
|
|
1409
|
+
# Select the subdomain in longitude direction
|
|
1410
|
+
subdomain = subdomain.sel({self.dim_names["longitude"]: target_coords["lon"]})
|
|
1415
1411
|
|
|
1416
1412
|
# Check if the selected subdomain contains the specified latitude and longitude values
|
|
1417
|
-
if not subdomain[self.dim_names["latitude"]].equals(
|
|
1418
|
-
coords[self.dim_names["latitude"]]
|
|
1419
|
-
):
|
|
1413
|
+
if not subdomain[self.dim_names["latitude"]].equals(target_coords["lat"]):
|
|
1420
1414
|
raise ValueError(
|
|
1421
1415
|
"The correction dataset does not contain all specified latitude values."
|
|
1422
1416
|
)
|
|
1423
|
-
if not subdomain[self.dim_names["longitude"]].equals(
|
|
1424
|
-
coords[self.dim_names["longitude"]]
|
|
1425
|
-
):
|
|
1417
|
+
if not subdomain[self.dim_names["longitude"]].equals(target_coords["lon"]):
|
|
1426
1418
|
raise ValueError(
|
|
1427
1419
|
"The correction dataset does not contain all specified longitude values."
|
|
1428
1420
|
)
|
|
@@ -2060,6 +2052,12 @@ def _load_data(filename, dim_names, use_dask, decode_times=True):
|
|
|
2060
2052
|
**combine_kwargs,
|
|
2061
2053
|
**kwargs,
|
|
2062
2054
|
)
|
|
2055
|
+
|
|
2056
|
+
# Rechunk the dataset along the tidal constituent dimension ("ntides") after loading
|
|
2057
|
+
# because the original dataset does not have a chunk size of 1 along this dimension.
|
|
2058
|
+
if "ntides" in dim_names:
|
|
2059
|
+
ds = ds.chunk({dim_names["ntides"]: 1})
|
|
2060
|
+
|
|
2063
2061
|
else:
|
|
2064
2062
|
ds_list = []
|
|
2065
2063
|
for file in matching_files:
|