roms-tools 2.2.1__py3-none-any.whl → 2.4.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 (152) hide show
  1. ci/environment.yml +1 -0
  2. roms_tools/__init__.py +2 -0
  3. roms_tools/analysis/roms_output.py +590 -0
  4. roms_tools/{setup/download.py → download.py} +3 -0
  5. roms_tools/{setup/plot.py → plot.py} +34 -28
  6. roms_tools/setup/boundary_forcing.py +199 -203
  7. roms_tools/setup/datasets.py +60 -136
  8. roms_tools/setup/grid.py +40 -67
  9. roms_tools/setup/initial_conditions.py +249 -247
  10. roms_tools/setup/nesting.py +6 -27
  11. roms_tools/setup/river_forcing.py +41 -76
  12. roms_tools/setup/surface_forcing.py +125 -75
  13. roms_tools/setup/tides.py +31 -51
  14. roms_tools/setup/topography.py +1 -1
  15. roms_tools/setup/utils.py +44 -224
  16. roms_tools/tests/test_analysis/test_roms_output.py +269 -0
  17. roms_tools/tests/{test_setup/test_regrid.py → test_regrid.py} +1 -1
  18. roms_tools/tests/test_setup/test_boundary_forcing.py +221 -58
  19. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/.zattrs +5 -3
  20. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/.zmetadata +156 -121
  21. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/abs_time/.zarray +2 -2
  22. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/abs_time/.zattrs +2 -1
  23. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/abs_time/0 +0 -0
  24. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/bry_time/.zarray +2 -2
  25. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/bry_time/.zattrs +1 -1
  26. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/bry_time/0 +0 -0
  27. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_east/.zarray +4 -4
  28. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_east/0.0.0 +0 -0
  29. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_north/.zarray +4 -4
  30. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_north/0.0.0 +0 -0
  31. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_south/.zarray +4 -4
  32. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_south/0.0.0 +0 -0
  33. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_west/.zarray +4 -4
  34. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_west/0.0.0 +0 -0
  35. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_east/.zarray +4 -4
  36. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_east/0.0.0 +0 -0
  37. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_north/.zarray +4 -4
  38. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_north/0.0.0 +0 -0
  39. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_south/.zarray +4 -4
  40. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_south/0.0.0 +0 -0
  41. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_west/.zarray +4 -4
  42. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_west/0.0.0 +0 -0
  43. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_east/.zarray +4 -4
  44. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_east/0.0.0 +0 -0
  45. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_north/.zarray +4 -4
  46. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_north/0.0.0 +0 -0
  47. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_south/.zarray +4 -4
  48. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_south/0.0.0 +0 -0
  49. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_west/.zarray +4 -4
  50. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_west/0.0.0 +0 -0
  51. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_east/.zarray +4 -4
  52. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_east/0.0 +0 -0
  53. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_north/.zarray +4 -4
  54. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_north/0.0 +0 -0
  55. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_south/.zarray +4 -4
  56. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_south/0.0 +0 -0
  57. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_west/.zarray +4 -4
  58. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_west/0.0 +0 -0
  59. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_east/.zarray +4 -4
  60. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_east/0.0.0 +0 -0
  61. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_north/.zarray +4 -4
  62. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_north/0.0.0 +0 -0
  63. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_south/.zarray +4 -4
  64. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_south/0.0.0 +0 -0
  65. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_west/.zarray +4 -4
  66. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_west/0.0.0 +0 -0
  67. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_east/.zarray +4 -4
  68. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_east/0.0 +0 -0
  69. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_north/.zarray +4 -4
  70. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_north/0.0 +0 -0
  71. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_south/.zarray +4 -4
  72. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_south/0.0 +0 -0
  73. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_west/.zarray +4 -4
  74. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_west/0.0 +0 -0
  75. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/.zarray +4 -4
  76. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/.zattrs +8 -0
  77. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/0.0 +0 -0
  78. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_north/.zarray +4 -4
  79. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_north/.zattrs +8 -0
  80. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_north/0.0 +0 -0
  81. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/.zarray +4 -4
  82. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/.zattrs +8 -0
  83. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/0.0 +0 -0
  84. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_west/.zarray +4 -4
  85. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_west/.zattrs +8 -0
  86. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_west/0.0 +0 -0
  87. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zattrs +4 -4
  88. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zmetadata +4 -4
  89. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/angle/0.0 +0 -0
  90. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/angle_coarse/0.0 +0 -0
  91. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/f/0.0 +0 -0
  92. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/h/0.0 +0 -0
  93. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_coarse/0.0 +0 -0
  94. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_rho/0.0 +0 -0
  95. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_u/0.0 +0 -0
  96. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_v/0.0 +0 -0
  97. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_coarse/0.0 +0 -0
  98. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_rho/0.0 +0 -0
  99. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_u/0.0 +0 -0
  100. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_v/0.0 +0 -0
  101. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_coarse/0.0 +0 -0
  102. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_rho/0.0 +0 -0
  103. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_u/0.0 +0 -0
  104. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_v/0.0 +0 -0
  105. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/pm/0.0 +0 -0
  106. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/pn/0.0 +0 -0
  107. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/.zattrs +2 -1
  108. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/.zmetadata +6 -4
  109. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/Cs_r/.zattrs +1 -1
  110. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/Cs_w/.zattrs +1 -1
  111. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/NH4/0.0.0.0 +0 -0
  112. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/NO3/0.0.0.0 +0 -0
  113. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/PO4/0.0.0.0 +0 -0
  114. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/abs_time/.zattrs +1 -0
  115. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatSi/0.0.0.0 +0 -0
  116. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ocean_time/.zattrs +1 -1
  117. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spC/0.0.0.0 +0 -0
  118. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spCaCO3/0.0.0.0 +0 -0
  119. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spFe/0.0.0.0 +0 -0
  120. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/temp/0.0.0.0 +0 -0
  121. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/u/0.0.0.0 +0 -0
  122. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ubar/0.0.0 +0 -0
  123. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/v/0.0.0.0 +0 -0
  124. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/vbar/0.0.0 +0 -0
  125. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/.zmetadata +30 -0
  126. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_location/.zarray +22 -0
  127. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_location/.zattrs +8 -0
  128. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_location/0.0 +0 -0
  129. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/.zmetadata +30 -0
  130. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_location/.zarray +22 -0
  131. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_location/.zattrs +8 -0
  132. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_location/0.0 +0 -0
  133. roms_tools/tests/test_setup/test_datasets.py +1 -1
  134. roms_tools/tests/test_setup/test_grid.py +1 -14
  135. roms_tools/tests/test_setup/test_initial_conditions.py +205 -67
  136. roms_tools/tests/test_setup/test_nesting.py +0 -16
  137. roms_tools/tests/test_setup/test_river_forcing.py +9 -37
  138. roms_tools/tests/test_setup/test_surface_forcing.py +103 -74
  139. roms_tools/tests/test_setup/test_tides.py +5 -17
  140. roms_tools/tests/test_setup/test_topography.py +1 -1
  141. roms_tools/tests/test_setup/test_utils.py +57 -1
  142. roms_tools/tests/{test_utils.py → test_tiling/test_partition.py} +1 -1
  143. roms_tools/tiling/partition.py +338 -0
  144. roms_tools/utils.py +310 -276
  145. roms_tools/vertical_coordinate.py +227 -0
  146. {roms_tools-2.2.1.dist-info → roms_tools-2.4.0.dist-info}/METADATA +1 -1
  147. {roms_tools-2.2.1.dist-info → roms_tools-2.4.0.dist-info}/RECORD +151 -142
  148. roms_tools/setup/vertical_coordinate.py +0 -109
  149. /roms_tools/{setup/regrid.py → regrid.py} +0 -0
  150. {roms_tools-2.2.1.dist-info → roms_tools-2.4.0.dist-info}/LICENSE +0 -0
  151. {roms_tools-2.2.1.dist-info → roms_tools-2.4.0.dist-info}/WHEEL +0 -0
  152. {roms_tools-2.2.1.dist-info → roms_tools-2.4.0.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,12 @@
1
1
  import time
2
- import re
3
2
  import xarray as xr
4
3
  from dataclasses import dataclass, field
5
- import glob
6
4
  from datetime import datetime, timedelta
7
5
  import numpy as np
8
6
  from typing import Dict, Optional, Union, List
9
7
  from pathlib import Path
10
8
  import logging
9
+ from roms_tools.utils import _load_data
11
10
  from roms_tools.setup.utils import (
12
11
  assign_dates_to_climatology,
13
12
  interpolate_from_climatology,
@@ -16,7 +15,7 @@ from roms_tools.setup.utils import (
16
15
  one_dim_fill,
17
16
  gc_dist,
18
17
  )
19
- from roms_tools.setup.download import (
18
+ from roms_tools.download import (
20
19
  download_correction_data,
21
20
  download_topo,
22
21
  download_river_data,
@@ -356,6 +355,47 @@ class Dataset:
356
355
  # Set the computed resolution as an attribute
357
356
  object.__setattr__(self, "resolution", resolution)
358
357
 
358
+ def compute_minimal_grid_spacing(self, ds: xr.Dataset):
359
+ """Compute the minimal grid spacing in a dataset based on latitude and longitude
360
+ spacing, considering Earth's radius.
361
+
362
+ Parameters
363
+ ----------
364
+ ds : xr.Dataset
365
+ Dataset containing latitude and longitude dimensions.
366
+
367
+ Returns
368
+ -------
369
+ minimal_spacing : float
370
+ The smallest horizontal grid spacing derived from the latitude
371
+ and longitude differences, in meters.
372
+ """
373
+
374
+ r_earth = 6371315.0
375
+ lat_dim = self.dim_names["latitude"]
376
+ lon_dim = self.dim_names["longitude"]
377
+
378
+ # Get latitude and longitude values from the dataset
379
+ latitudes = ds[lat_dim].values
380
+ longitudes = ds[lon_dim].values
381
+
382
+ # Compute differences along latitude and longitude
383
+ lat_diff = np.abs(np.diff(latitudes)).min() # Minimal latitude spacing
384
+ lon_diff = np.abs(np.diff(longitudes)).min() # Minimal longitude spacing
385
+
386
+ # Latitude spacing is constant at all longitudes
387
+ min_lat_spacing = (2 * np.pi * r_earth * lat_diff) / 360
388
+
389
+ # Longitude spacing varies with latitude
390
+ min_lon_spacing = (
391
+ 2 * np.pi * r_earth * lon_diff * np.cos(np.radians(latitudes.min()))
392
+ ) / 360
393
+
394
+ # The minimal spacing is the smaller of the two
395
+ minimal_spacing = min(min_lat_spacing, min_lon_spacing)
396
+
397
+ return minimal_spacing
398
+
359
399
  def check_if_global(self, ds) -> bool:
360
400
  """Checks if the dataset covers the entire globe in the longitude dimension.
361
401
 
@@ -471,7 +511,12 @@ class Dataset:
471
511
  pass
472
512
 
473
513
  def choose_subdomain(
474
- self, target_coords, buffer_points=20, return_copy=False, verbose=False
514
+ self,
515
+ target_coords,
516
+ buffer_points=20,
517
+ return_copy=False,
518
+ return_coords_only=False,
519
+ verbose=False,
475
520
  ):
476
521
  """Selects a subdomain from the xarray Dataset based on specified target
477
522
  coordinates, extending the selection by a defined buffer. Adjusts longitude
@@ -489,6 +534,9 @@ class Dataset:
489
534
  return_subdomain : bool, optional
490
535
  If True, returns the subset of the original dataset representing the chosen
491
536
  subdomain. If False, assigns the subset to `self.ds`. Defaults to False.
537
+ return_coords_only : bool, optional
538
+ If True, returns a new xarray.Dataset containing only the latitude and longitude
539
+ of the subdomain. Defaults to False.
492
540
  verbose : bool, optional
493
541
  If True, print message if dataset is concatenated along longitude dimension.
494
542
  Defaults to False.
@@ -593,6 +641,13 @@ class Dataset:
593
641
  else:
594
642
  subdomain[self.dim_names["longitude"]] = xr.where(lon < 0, lon + 360, lon)
595
643
 
644
+ if return_coords_only:
645
+ # Create and return a dataset with only latitudes and longitudes
646
+ coords_ds = subdomain[
647
+ [self.dim_names["latitude"], self.dim_names["longitude"]]
648
+ ]
649
+ return coords_ds
650
+
596
651
  if return_copy:
597
652
  return Dataset.from_ds(self, subdomain)
598
653
  else:
@@ -1945,138 +2000,6 @@ class DaiRiverDataset(RiverDataset):
1945
2000
  # shared functions
1946
2001
 
1947
2002
 
1948
- def _load_data(filename, dim_names, use_dask, decode_times=True):
1949
- """Load dataset from the specified file.
1950
-
1951
- Parameters
1952
- ----------
1953
- filename : Union[str, Path, List[Union[str, Path]]]
1954
- The path to the data file(s). Can be a single string (with or without wildcards), a single Path object,
1955
- or a list of strings or Path objects containing multiple files.
1956
- dim_names: Dict[str, str], optional
1957
- Dictionary specifying the names of dimensions in the dataset.
1958
- use_dask: bool
1959
- Indicates whether to use dask for chunking. If True, data is loaded with dask; if False, data is loaded eagerly. Defaults to False.
1960
- decode_times: bool, optional
1961
- If True, decode times encoded in the standard NetCDF datetime format into datetime objects. Otherwise, leave them encoded as numbers.
1962
- Defaults to True.
1963
-
1964
- Returns
1965
- -------
1966
- ds : xr.Dataset
1967
- The loaded xarray Dataset containing the forcing data.
1968
-
1969
- Raises
1970
- ------
1971
- FileNotFoundError
1972
- If the specified file does not exist.
1973
- ValueError
1974
- If a list of files is provided but dim_names["time"] is not available or use_dask=False.
1975
- """
1976
-
1977
- # Precompile the regex for matching wildcard characters
1978
- wildcard_regex = re.compile(r"[\*\?\[\]]")
1979
-
1980
- # Convert Path objects to strings
1981
- if isinstance(filename, (str, Path)):
1982
- filename_str = str(filename)
1983
- elif isinstance(filename, list):
1984
- filename_str = [str(f) for f in filename]
1985
- else:
1986
- raise ValueError("filename must be a string, Path, or a list of strings/Paths.")
1987
- # Handle the case when filename is a string
1988
- contains_wildcard = False
1989
- if isinstance(filename_str, str):
1990
- contains_wildcard = bool(wildcard_regex.search(filename_str))
1991
- if contains_wildcard:
1992
- matching_files = glob.glob(filename_str)
1993
- if not matching_files:
1994
- raise FileNotFoundError(
1995
- f"No files found matching the pattern '{filename_str}'."
1996
- )
1997
- else:
1998
- matching_files = [filename_str]
1999
-
2000
- # Handle the case when filename is a list
2001
- elif isinstance(filename_str, list):
2002
- contains_wildcard = any(wildcard_regex.search(f) for f in filename_str)
2003
- if contains_wildcard:
2004
- matching_files = []
2005
- for f in filename_str:
2006
- files = glob.glob(f)
2007
- if not files:
2008
- raise FileNotFoundError(
2009
- f"No files found matching the pattern '{f}'."
2010
- )
2011
- matching_files.extend(files)
2012
- else:
2013
- matching_files = filename_str
2014
-
2015
- # Check if time dimension is available when multiple files are provided
2016
- if isinstance(filename_str, list) and "time" not in dim_names:
2017
- raise ValueError(
2018
- "A list of files is provided, but time dimension is not available. "
2019
- "A time dimension must be available to concatenate the files."
2020
- )
2021
-
2022
- # Determine the kwargs for combining datasets
2023
- if contains_wildcard or len(matching_files) == 1:
2024
- # If there is a wildcard or just one file, use by_coords
2025
- kwargs = {"combine": "by_coords"}
2026
- else:
2027
- # Otherwise, use nested combine based on time
2028
- kwargs = {"combine": "nested", "concat_dim": dim_names["time"]}
2029
-
2030
- # Base kwargs used for dataset combination
2031
- combine_kwargs = {
2032
- "coords": "minimal",
2033
- "compat": "override",
2034
- "combine_attrs": "override",
2035
- }
2036
-
2037
- if use_dask:
2038
-
2039
- chunks = {
2040
- dim_names["latitude"]: -1,
2041
- dim_names["longitude"]: -1,
2042
- }
2043
- if "depth" in dim_names:
2044
- chunks[dim_names["depth"]] = -1
2045
- if "time" in dim_names:
2046
- chunks[dim_names["time"]] = 1
2047
-
2048
- ds = xr.open_mfdataset(
2049
- matching_files,
2050
- decode_times=decode_times,
2051
- chunks=chunks,
2052
- **combine_kwargs,
2053
- **kwargs,
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
-
2061
- else:
2062
- ds_list = []
2063
- for file in matching_files:
2064
- ds = xr.open_dataset(file, decode_times=decode_times, chunks=None)
2065
- ds_list.append(ds)
2066
-
2067
- if kwargs["combine"] == "by_coords":
2068
- ds = xr.combine_by_coords(ds_list, **combine_kwargs)
2069
- elif kwargs["combine"] == "nested":
2070
- ds = xr.combine_nested(
2071
- ds_list, concat_dim=kwargs["concat_dim"], **combine_kwargs
2072
- )
2073
-
2074
- if "time" in dim_names and dim_names["time"] not in ds.dims:
2075
- ds = ds.expand_dims(dim_names["time"])
2076
-
2077
- return ds
2078
-
2079
-
2080
2003
  def _check_dataset(
2081
2004
  ds: xr.Dataset,
2082
2005
  dim_names: Dict[str, str],
@@ -2191,6 +2114,7 @@ def _select_relevant_times(
2191
2114
  )
2192
2115
  if not end_time:
2193
2116
  # Interpolate from climatology for initial conditions
2117
+ ds["time"] = ds["time"].dt.days
2194
2118
  ds = interpolate_from_climatology(ds, time_dim, start_time)
2195
2119
  else:
2196
2120
  time_type = get_time_type(ds[time_dim])
roms_tools/setup/grid.py CHANGED
@@ -8,17 +8,18 @@ import matplotlib.pyplot as plt
8
8
  import yaml
9
9
  import importlib.metadata
10
10
  from typing import Dict, Union, List
11
+ from roms_tools.utils import save_datasets
11
12
  from roms_tools.setup.topography import _add_topography
12
13
  from roms_tools.setup.mask import _add_mask, _add_velocity_masks
13
- from roms_tools.setup.plot import _plot, _section_plot
14
+ from roms_tools.vertical_coordinate import compute_depth_coordinates, sigma_stretch
15
+ from roms_tools.plot import _plot, _section_plot
14
16
  from roms_tools.setup.utils import (
15
17
  interpolate_from_rho_to_u,
16
18
  interpolate_from_rho_to_v,
17
19
  get_target_coords,
18
20
  gc_dist,
19
21
  )
20
- from roms_tools.setup.vertical_coordinate import sigma_stretch, compute_depth
21
- from roms_tools.setup.utils import extract_single_value, save_datasets
22
+ from roms_tools.setup.utils import extract_single_value
22
23
  from pathlib import Path
23
24
 
24
25
 
@@ -392,14 +393,14 @@ class Grid:
392
393
  object.__setattr__(self, "ds", ds)
393
394
 
394
395
  def plot(
395
- self, bathymetry: bool = False, title: str = None, with_dim_names: bool = False
396
+ self, bathymetry: bool = True, title: str = None, with_dim_names: bool = False
396
397
  ) -> None:
397
398
  """Plot the grid.
398
399
 
399
400
  Parameters
400
401
  ----------
401
402
  bathymetry : bool, optional
402
- Whether or not to plot the bathymetry. Default is False.
403
+ Whether or not to plot the bathymetry. Default is True.
403
404
  title : str, optional
404
405
  The title of the plot. If not provided, it will be set to a default.
405
406
  with_dim_names : bool, optional
@@ -412,13 +413,16 @@ class Grid:
412
413
  This method does not return any value. It generates and displays a plot.
413
414
  """
414
415
 
416
+ field = self.ds.h.where(self.ds.mask_rho)
417
+ lat_deg = self.ds.lat_rho
418
+ lon_deg = self.ds.lon_rho
419
+ if self.straddle:
420
+ lon_deg = xr.where(lon_deg > 180, lon_deg - 360, lon_deg)
421
+ field = field.assign_coords({"lon": lon_deg, "lat": lat_deg})
422
+
415
423
  if bathymetry:
416
424
  if title is None:
417
425
  title = "ROMS grid and bathymetry"
418
- field = self.ds.h.where(self.ds.mask_rho)
419
- field = field.assign_coords(
420
- {"lon": self.ds.lon_rho, "lat": self.ds.lat_rho}
421
- )
422
426
 
423
427
  vmax = field.max().values
424
428
  vmin = field.min().values
@@ -427,9 +431,7 @@ class Grid:
427
431
  kwargs = {"vmax": vmax, "vmin": vmin, "cmap": cmap}
428
432
 
429
433
  _plot(
430
- self.ds,
431
434
  field=field,
432
- straddle=self.straddle,
433
435
  title=title,
434
436
  with_dim_names=with_dim_names,
435
437
  kwargs=kwargs,
@@ -438,10 +440,7 @@ class Grid:
438
440
  if title is None:
439
441
  title = "ROMS grid"
440
442
  _plot(
441
- self.ds,
442
- straddle=self.straddle,
443
- title=title,
444
- with_dim_names=with_dim_names,
443
+ field=field, title=title, with_dim_names=with_dim_names, plot_data=False
445
444
  )
446
445
 
447
446
  def plot_vertical_coordinate(
@@ -474,54 +473,48 @@ class Grid:
474
473
  If not exactly one of s, eta, xi is specified.
475
474
  """
476
475
 
477
- title = "Layer depth at rho-points"
478
-
479
476
  if sum(s is not None for s in [s, eta, xi]) != 1:
480
477
  raise ValueError("Exactly one of s, eta, or xi must be specified.")
481
478
 
482
- h = self.ds["h"]
483
- h = h.assign_coords({"lon": self.ds.lon_rho, "lat": self.ds.lat_rho})
479
+ depth = compute_depth_coordinates(
480
+ self.ds, zeta=0, depth_type="layer", location="rho", eta=eta, xi=xi
481
+ )
484
482
 
485
- # slice the bathymetry as desired
483
+ title = "Layer depth at rho-points"
486
484
  if eta is not None:
487
- title = title + f", eta_rho = {h.eta_rho[eta].item()}"
488
- h = h.isel(eta_rho=eta)
485
+ title = title + f", eta_rho = {self.ds.eta_rho[eta].item()}"
489
486
  if xi is not None:
490
- title = title + f", xi_rho = {h.xi_rho[xi].item()}"
491
- h = h.isel(xi_rho=xi)
492
-
493
- if eta is None and xi is None:
494
- layer_depth = compute_depth(0, h, self.hc, self.ds.Cs_r, self.ds.sigma_r)
495
- title = title + f", s_rho = {layer_depth.s_rho[s].item()}"
496
- layer_depth = layer_depth.isel(s_rho=s)
497
-
498
- layer_depth.attrs["long_name"] = "Layer depth"
499
- layer_depth.attrs["units"] = "m"
487
+ title = title + f", xi_rho = {self.ds.xi_rho[xi].item()}"
488
+ if s is not None:
489
+ title = title + f", s_rho = {depth.s_rho[s].item()}"
490
+ depth = depth.isel(s_rho=s)
500
491
 
501
- vmax = layer_depth.max().values
502
- vmin = layer_depth.min().values
492
+ vmax = depth.max().values
493
+ vmin = depth.min().values
503
494
  cmap = plt.colormaps.get_cmap("YlGnBu")
504
495
  cmap.set_bad(color="gray")
505
496
  kwargs = {"vmax": vmax, "vmin": vmin, "cmap": cmap}
506
497
 
498
+ lat_deg = self.ds.lat_rho
499
+ lon_deg = self.ds.lon_rho
500
+ if self.straddle:
501
+ lon_deg = xr.where(lon_deg > 180, lon_deg - 360, lon_deg)
502
+ depth = depth.assign_coords({"lon": lon_deg, "lat": lat_deg})
503
+
507
504
  _plot(
508
- self.ds,
509
- field=layer_depth.where(self.ds.mask_rho),
510
- straddle=self.straddle,
505
+ field=depth.where(self.ds.mask_rho),
511
506
  depth_contours=False,
512
507
  title=title,
513
508
  kwargs=kwargs,
514
509
  )
515
510
  else:
516
- layer_depth = compute_depth(0, h, self.hc, self.ds.Cs_r, self.ds.sigma_r)
517
- layer_depth.attrs["long_name"] = "Layer depth"
518
- layer_depth.attrs["units"] = "m"
519
- field = xr.zeros_like(layer_depth)
520
- field = field.assign_coords({"layer_depth": layer_depth})
521
-
522
- interface_depth = compute_depth(
523
- 0, h, self.hc, self.ds.Cs_w, self.ds.sigma_w
511
+ field = xr.zeros_like(depth)
512
+ field = field.assign_coords({"layer_depth": depth})
513
+
514
+ interface_depth = compute_depth_coordinates(
515
+ self.ds, zeta=0, depth_type="interface", eta=eta, xi=xi
524
516
  )
517
+
525
518
  cmap = plt.colormaps.get_cmap("YlGnBu")
526
519
  cmap.set_bad(color="gray")
527
520
  kwargs = {"vmax": 0.0, "vmin": 0.0, "cmap": cmap, "add_colorbar": False}
@@ -533,31 +526,13 @@ class Grid:
533
526
  kwargs=kwargs,
534
527
  )
535
528
 
536
- def save(
537
- self, filepath: Union[str, Path], np_eta: int = None, np_xi: int = None
538
- ) -> None:
529
+ def save(self, filepath: Union[str, Path]) -> None:
539
530
  """Save the grid information to a netCDF4 file.
540
531
 
541
- This method supports saving the dataset in two modes:
542
-
543
- 1. **Single File Mode (default)**:
544
-
545
- If both `np_eta` and `np_xi` are `None`, the entire dataset is saved as a single netCDF4 file
546
- with the base filename specified by `filepath.nc`.
547
-
548
- 2. **Partitioned Mode**:
549
-
550
- - If either `np_eta` or `np_xi` is specified, the dataset is divided into spatial tiles along the eta-axis and xi-axis.
551
- - Each spatial tile is saved as a separate netCDF4 file.
552
-
553
532
  Parameters
554
533
  ----------
555
534
  filepath : Union[str, Path]
556
535
  The base path or filename where the dataset should be saved.
557
- np_eta : int, optional
558
- The number of partitions along the `eta` direction. If `None`, no spatial partitioning is performed.
559
- np_xi : int, optional
560
- The number of partitions along the `xi` direction. If `None`, no spatial partitioning is performed.
561
536
 
562
537
  Returns
563
538
  -------
@@ -575,9 +550,7 @@ class Grid:
575
550
  dataset_list = [self.ds.load()]
576
551
  output_filenames = [str(filepath)]
577
552
 
578
- saved_filenames = save_datasets(
579
- dataset_list, output_filenames, np_eta=np_eta, np_xi=np_xi
580
- )
553
+ saved_filenames = save_datasets(dataset_list, output_filenames)
581
554
 
582
555
  return saved_filenames
583
556