roms-tools 2.0.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.
Files changed (54) hide show
  1. roms_tools/__init__.py +2 -1
  2. roms_tools/setup/boundary_forcing.py +21 -30
  3. roms_tools/setup/datasets.py +13 -21
  4. roms_tools/setup/grid.py +253 -139
  5. roms_tools/setup/initial_conditions.py +21 -3
  6. roms_tools/setup/mask.py +50 -4
  7. roms_tools/setup/nesting.py +575 -0
  8. roms_tools/setup/plot.py +214 -55
  9. roms_tools/setup/river_forcing.py +125 -29
  10. roms_tools/setup/surface_forcing.py +21 -8
  11. roms_tools/setup/tides.py +21 -3
  12. roms_tools/setup/topography.py +168 -35
  13. roms_tools/setup/utils.py +127 -21
  14. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/.zmetadata +2 -3
  15. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_tracer/.zattrs +1 -2
  16. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/tracer_name/.zarray +1 -1
  17. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/tracer_name/0 +0 -0
  18. roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/.zmetadata +5 -6
  19. roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_tracer/.zarray +2 -2
  20. roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_tracer/.zattrs +1 -2
  21. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_tracer/0.0.0 +0 -0
  22. roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/tracer_name/.zarray +2 -2
  23. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/tracer_name/0 +0 -0
  24. roms_tools/tests/test_setup/test_datasets.py +2 -2
  25. roms_tools/tests/test_setup/test_nesting.py +489 -0
  26. roms_tools/tests/test_setup/test_river_forcing.py +50 -13
  27. roms_tools/tests/test_setup/test_surface_forcing.py +1 -0
  28. roms_tools/tests/test_setup/test_validation.py +2 -2
  29. {roms_tools-2.0.0.dist-info → roms_tools-2.1.0.dist-info}/METADATA +8 -4
  30. {roms_tools-2.0.0.dist-info → roms_tools-2.1.0.dist-info}/RECORD +51 -50
  31. {roms_tools-2.0.0.dist-info → roms_tools-2.1.0.dist-info}/WHEEL +1 -1
  32. roms_tools/_version.py +0 -2
  33. roms_tools/tests/test_setup/test_data/river_forcing.zarr/river_tracer/0.0.0 +0 -0
  34. roms_tools/tests/test_setup/test_data/river_forcing.zarr/tracer_name/0 +0 -0
  35. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/.zattrs +0 -0
  36. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/.zgroup +0 -0
  37. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/abs_time/.zarray +0 -0
  38. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/abs_time/.zattrs +0 -0
  39. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/abs_time/0 +0 -0
  40. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/month/.zarray +0 -0
  41. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/month/.zattrs +0 -0
  42. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/month/0 +0 -0
  43. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_name/.zarray +0 -0
  44. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_name/.zattrs +0 -0
  45. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_name/0 +0 -0
  46. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_time/.zarray +0 -0
  47. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_time/.zattrs +0 -0
  48. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_time/0 +0 -0
  49. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_volume/.zarray +0 -0
  50. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_volume/.zattrs +0 -0
  51. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_volume/0.0 +0 -0
  52. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/tracer_name/.zattrs +0 -0
  53. {roms_tools-2.0.0.dist-info → roms_tools-2.1.0.dist-info}/LICENSE +0 -0
  54. {roms_tools-2.0.0.dist-info → roms_tools-2.1.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__ = "999"
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._validate(ds)
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
- """Updates boundary coordinates for rho, u, and v variables on the grid.
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
 
@@ -980,7 +962,10 @@ class BoundaryForcing:
980
962
 
981
963
  @classmethod
982
964
  def from_yaml(
983
- cls, filepath: Union[str, Path], use_dask: bool = False
965
+ cls,
966
+ filepath: Union[str, Path],
967
+ use_dask: bool = False,
968
+ bypass_validation: bool = False,
984
969
  ) -> "BoundaryForcing":
985
970
  """Create an instance of the BoundaryForcing class from a YAML file.
986
971
 
@@ -990,6 +975,10 @@ class BoundaryForcing:
990
975
  The path to the YAML file from which the parameters will be read.
991
976
  use_dask: bool, optional
992
977
  Indicates whether to use dask for processing. If True, data is processed with dask; if False, data is processed eagerly. Defaults to False.
978
+ bypass_validation: bool, optional
979
+ Indicates whether to skip validation checks in the processed data. When set to True,
980
+ the validation process that ensures no NaN values exist at wet points
981
+ in the processed dataset is bypassed. Defaults to False.
993
982
 
994
983
  Returns
995
984
  -------
@@ -1002,7 +991,9 @@ class BoundaryForcing:
1002
991
  params = _from_yaml(cls, filepath)
1003
992
 
1004
993
  # Create and return an instance of InitialConditions
1005
- return cls(grid=grid, **params, use_dask=use_dask)
994
+ return cls(
995
+ grid=grid, **params, use_dask=use_dask, bypass_validation=bypass_validation
996
+ )
1006
997
 
1007
998
 
1008
999
  def apply_1d_horizontal_fill(processed_fields: dict) -> dict:
@@ -1369,7 +1369,7 @@ class ERA5Correction(Dataset):
1369
1369
 
1370
1370
  super().__post_init__()
1371
1371
 
1372
- def choose_subdomain(self, coords, straddle: bool):
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
- coords : dict
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
- lon = self.ds[self.dim_names["longitude"]]
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
- if lon.max().values > 180 and straddle:
1408
- # Convert from [0, 360] to [-180, 180]
1409
- self.ds[self.dim_names["longitude"]] = xr.where(
1410
- lon > 180, lon - 360, lon
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 based on the specified latitude and longitude ranges
1414
- subdomain = self.ds.sel(**coords)
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
  )