roms-tools 2.3.0__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 (143) hide show
  1. ci/environment.yml +1 -0
  2. roms_tools/__init__.py +1 -0
  3. roms_tools/analysis/roms_output.py +10 -6
  4. roms_tools/setup/boundary_forcing.py +178 -193
  5. roms_tools/setup/datasets.py +58 -1
  6. roms_tools/setup/grid.py +31 -97
  7. roms_tools/setup/initial_conditions.py +172 -126
  8. roms_tools/setup/nesting.py +2 -23
  9. roms_tools/setup/river_forcing.py +34 -67
  10. roms_tools/setup/surface_forcing.py +111 -61
  11. roms_tools/setup/tides.py +7 -30
  12. roms_tools/setup/utils.py +24 -70
  13. roms_tools/tests/test_setup/test_boundary_forcing.py +220 -57
  14. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/.zattrs +5 -3
  15. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/.zmetadata +156 -121
  16. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/abs_time/.zarray +2 -2
  17. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/abs_time/.zattrs +2 -1
  18. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/abs_time/0 +0 -0
  19. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/bry_time/.zarray +2 -2
  20. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/bry_time/.zattrs +1 -1
  21. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/bry_time/0 +0 -0
  22. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_east/.zarray +4 -4
  23. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_east/0.0.0 +0 -0
  24. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_north/.zarray +4 -4
  25. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_north/0.0.0 +0 -0
  26. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_south/.zarray +4 -4
  27. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_south/0.0.0 +0 -0
  28. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_west/.zarray +4 -4
  29. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_west/0.0.0 +0 -0
  30. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_east/.zarray +4 -4
  31. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_east/0.0.0 +0 -0
  32. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_north/.zarray +4 -4
  33. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_north/0.0.0 +0 -0
  34. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_south/.zarray +4 -4
  35. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_south/0.0.0 +0 -0
  36. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_west/.zarray +4 -4
  37. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_west/0.0.0 +0 -0
  38. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_east/.zarray +4 -4
  39. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_east/0.0.0 +0 -0
  40. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_north/.zarray +4 -4
  41. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_north/0.0.0 +0 -0
  42. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_south/.zarray +4 -4
  43. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_south/0.0.0 +0 -0
  44. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_west/.zarray +4 -4
  45. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_west/0.0.0 +0 -0
  46. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_east/.zarray +4 -4
  47. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_east/0.0 +0 -0
  48. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_north/.zarray +4 -4
  49. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_north/0.0 +0 -0
  50. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_south/.zarray +4 -4
  51. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_south/0.0 +0 -0
  52. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_west/.zarray +4 -4
  53. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_west/0.0 +0 -0
  54. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_east/.zarray +4 -4
  55. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_east/0.0.0 +0 -0
  56. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_north/.zarray +4 -4
  57. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_north/0.0.0 +0 -0
  58. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_south/.zarray +4 -4
  59. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_south/0.0.0 +0 -0
  60. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_west/.zarray +4 -4
  61. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_west/0.0.0 +0 -0
  62. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_east/.zarray +4 -4
  63. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_east/0.0 +0 -0
  64. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_north/.zarray +4 -4
  65. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_north/0.0 +0 -0
  66. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_south/.zarray +4 -4
  67. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_south/0.0 +0 -0
  68. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_west/.zarray +4 -4
  69. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_west/0.0 +0 -0
  70. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/.zarray +4 -4
  71. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/.zattrs +8 -0
  72. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/0.0 +0 -0
  73. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_north/.zarray +4 -4
  74. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_north/.zattrs +8 -0
  75. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_north/0.0 +0 -0
  76. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/.zarray +4 -4
  77. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/.zattrs +8 -0
  78. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/0.0 +0 -0
  79. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_west/.zarray +4 -4
  80. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_west/.zattrs +8 -0
  81. roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_west/0.0 +0 -0
  82. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zattrs +4 -4
  83. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zmetadata +4 -4
  84. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/angle/0.0 +0 -0
  85. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/angle_coarse/0.0 +0 -0
  86. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/f/0.0 +0 -0
  87. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/h/0.0 +0 -0
  88. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_coarse/0.0 +0 -0
  89. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_rho/0.0 +0 -0
  90. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_u/0.0 +0 -0
  91. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_v/0.0 +0 -0
  92. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_coarse/0.0 +0 -0
  93. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_rho/0.0 +0 -0
  94. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_u/0.0 +0 -0
  95. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_v/0.0 +0 -0
  96. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_coarse/0.0 +0 -0
  97. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_rho/0.0 +0 -0
  98. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_u/0.0 +0 -0
  99. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_v/0.0 +0 -0
  100. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/pm/0.0 +0 -0
  101. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/pn/0.0 +0 -0
  102. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/.zattrs +2 -1
  103. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/.zmetadata +6 -4
  104. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/Cs_r/.zattrs +1 -1
  105. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/Cs_w/.zattrs +1 -1
  106. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/NH4/0.0.0.0 +0 -0
  107. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/NO3/0.0.0.0 +0 -0
  108. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/PO4/0.0.0.0 +0 -0
  109. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/abs_time/.zattrs +1 -0
  110. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatSi/0.0.0.0 +0 -0
  111. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ocean_time/.zattrs +1 -1
  112. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spC/0.0.0.0 +0 -0
  113. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spCaCO3/0.0.0.0 +0 -0
  114. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spFe/0.0.0.0 +0 -0
  115. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/temp/0.0.0.0 +0 -0
  116. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/u/0.0.0.0 +0 -0
  117. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ubar/0.0.0 +0 -0
  118. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/v/0.0.0.0 +0 -0
  119. roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/vbar/0.0.0 +0 -0
  120. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/.zmetadata +30 -0
  121. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_location/.zarray +22 -0
  122. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_location/.zattrs +8 -0
  123. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_location/0.0 +0 -0
  124. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/.zmetadata +30 -0
  125. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_location/.zarray +22 -0
  126. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_location/.zattrs +8 -0
  127. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_location/0.0 +0 -0
  128. roms_tools/tests/test_setup/test_grid.py +0 -13
  129. roms_tools/tests/test_setup/test_initial_conditions.py +204 -66
  130. roms_tools/tests/test_setup/test_nesting.py +0 -16
  131. roms_tools/tests/test_setup/test_river_forcing.py +8 -36
  132. roms_tools/tests/test_setup/test_surface_forcing.py +102 -73
  133. roms_tools/tests/test_setup/test_tides.py +4 -16
  134. roms_tools/tests/test_setup/test_utils.py +1 -0
  135. roms_tools/tests/{test_utils.py → test_tiling/test_partition.py} +1 -1
  136. roms_tools/tiling/partition.py +338 -0
  137. roms_tools/utils.py +66 -333
  138. roms_tools/vertical_coordinate.py +54 -133
  139. {roms_tools-2.3.0.dist-info → roms_tools-2.4.0.dist-info}/METADATA +1 -1
  140. {roms_tools-2.3.0.dist-info → roms_tools-2.4.0.dist-info}/RECORD +143 -136
  141. {roms_tools-2.3.0.dist-info → roms_tools-2.4.0.dist-info}/LICENSE +0 -0
  142. {roms_tools-2.3.0.dist-info → roms_tools-2.4.0.dist-info}/WHEEL +0 -0
  143. {roms_tools-2.3.0.dist-info → roms_tools-2.4.0.dist-info}/top_level.txt +0 -0
@@ -5,17 +5,27 @@ from dataclasses import dataclass, field
5
5
  from typing import Dict, Union, List, Optional
6
6
  import matplotlib.pyplot as plt
7
7
  from pathlib import Path
8
+ import logging
8
9
  from datetime import datetime
9
10
  from roms_tools import Grid
10
11
  from roms_tools.regrid import LateralRegrid, VerticalRegrid
11
12
  from roms_tools.plot import _plot, _section_plot, _profile_plot, _line_plot
12
- from roms_tools.utils import transpose_dimensions
13
+ from roms_tools.utils import (
14
+ transpose_dimensions,
15
+ save_datasets,
16
+ get_dask_chunks,
17
+ interpolate_from_rho_to_u,
18
+ interpolate_from_rho_to_v,
19
+ )
20
+ from roms_tools.vertical_coordinate import (
21
+ compute_depth_coordinates,
22
+ compute_depth,
23
+ )
13
24
  from roms_tools.setup.datasets import GLORYSDataset, CESMBGCDataset
14
25
  from roms_tools.setup.utils import (
15
26
  nan_check,
16
27
  substitute_nans_by_fillvalue,
17
28
  get_variable_metadata,
18
- save_datasets,
19
29
  get_target_coords,
20
30
  rotate_velocities,
21
31
  compute_barotropic_velocity,
@@ -59,10 +69,17 @@ class InitialConditions:
59
69
  - A list of strings or Path objects containing multiple files.
60
70
  - "climatology" (bool): Indicates if the data is climatology data. Defaults to False.
61
71
 
72
+ adjust_depth_for_sea_surface_height : bool, optional
73
+ Whether to account for sea surface height variations when computing depth coordinates.
74
+ Defaults to `False`.
62
75
  model_reference_date : datetime, optional
63
76
  The reference date for the model. Defaults to January 1, 2000.
64
77
  use_dask: bool, optional
65
78
  Indicates whether to use dask for processing. If True, data is processed with dask; if False, data is processed eagerly. Defaults to False.
79
+ horizontal_chunk_size : int, optional
80
+ The chunk size used for horizontal partitioning for the vertical regridding when `use_dask = True`. Defaults to 50.
81
+ A larger number results in a bigger memory footprint but faster computations.
82
+ A smaller number results in a smaller memory footprint but slower computations.
66
83
  bypass_validation: bool, optional
67
84
  Indicates whether to skip validation checks in the processed data. When set to True,
68
85
  the validation process that ensures no NaN values exist at wet points
@@ -87,7 +104,9 @@ class InitialConditions:
87
104
  source: Dict[str, Union[str, Path, List[Union[str, Path]]]]
88
105
  bgc_source: Optional[Dict[str, Union[str, Path, List[Union[str, Path]]]]] = None
89
106
  model_reference_date: datetime = datetime(2000, 1, 1)
107
+ adjust_depth_for_sea_surface_height: bool = False
90
108
  use_dask: bool = False
109
+ horizontal_chunk_size: int = 50
91
110
  bypass_validation: bool = False
92
111
 
93
112
  ds: xr.Dataset = field(init=False, repr=False)
@@ -95,6 +114,8 @@ class InitialConditions:
95
114
  def __post_init__(self):
96
115
 
97
116
  self._input_checks()
117
+ # Dataset for depth coordinates
118
+ object.__setattr__(self, "ds_depth_coords", xr.Dataset())
98
119
 
99
120
  processed_fields = {}
100
121
  processed_fields = self._process_data(processed_fields, type="physics")
@@ -129,7 +150,6 @@ class InitialConditions:
129
150
  target_coords,
130
151
  buffer_points=20, # lateral fill needs good buffer from data margin
131
152
  )
132
-
133
153
  data.extrapolate_deepest_to_bottom()
134
154
  data.apply_lateral_fill()
135
155
 
@@ -140,6 +160,7 @@ class InitialConditions:
140
160
 
141
161
  # lateral regridding
142
162
  lateral_regrid = LateralRegrid(target_coords, data.dim_names)
163
+
143
164
  for var_name in var_names:
144
165
  if var_name in data.var_names.keys():
145
166
  processed_fields[var_name] = lateral_regrid.apply(
@@ -148,60 +169,61 @@ class InitialConditions:
148
169
 
149
170
  # rotation of velocities and interpolation to u/v points
150
171
  if "u" in variable_info and "v" in variable_info:
151
- (processed_fields["u"], processed_fields["v"],) = rotate_velocities(
172
+ processed_fields["u"], processed_fields["v"] = rotate_velocities(
152
173
  processed_fields["u"],
153
174
  processed_fields["v"],
154
175
  target_coords["angle"],
155
176
  interpolate=True,
156
177
  )
157
178
 
158
- var_names_dict = {}
159
- for location in ["rho", "u", "v"]:
160
- var_names_dict[location] = [
179
+ var_names_dict = {
180
+ location: [
161
181
  name
162
182
  for name, info in variable_info.items()
163
183
  if info["location"] == location and info["is_3d"]
164
184
  ]
185
+ for location in ["rho", "u", "v"]
186
+ }
187
+
188
+ if type == "bgc":
189
+ # Ensure time coordinate matches that of physical variables
190
+ for var_name in variable_info.keys():
191
+ processed_fields[var_name] = processed_fields[var_name].assign_coords(
192
+ {"time": processed_fields["temp"]["time"]}
193
+ )
194
+
195
+ # Get depth coordinates
196
+ zeta = (
197
+ processed_fields["zeta"] if self.adjust_depth_for_sea_surface_height else 0
198
+ )
165
199
 
166
- # compute layer depth coordinates
167
- if len(var_names_dict["u"]) > 0 or len(var_names_dict["v"]) > 0:
168
- self._get_vertical_coordinates(
169
- type="layer",
170
- additional_locations=["u", "v"],
171
- )
172
- else:
173
- if len(var_names_dict["rho"]) > 0:
174
- self._get_vertical_coordinates(type="layer", additional_locations=[])
175
- # vertical regridding
200
+ for location in ["rho", "u", "v"]:
201
+ if len(var_names_dict[location]) > 0:
202
+ self._get_depth_coordinates(zeta, location, "layer")
203
+
204
+ # Vertical regridding
176
205
  for location in ["rho", "u", "v"]:
177
206
  if len(var_names_dict[location]) > 0:
178
207
  vertical_regrid = VerticalRegrid(
179
- self.grid.ds[f"layer_depth_{location}"],
208
+ self.ds_depth_coords[f"layer_depth_{location}"],
180
209
  data.ds[data.dim_names["depth"]],
181
210
  )
182
211
  for var_name in var_names_dict[location]:
183
212
  if var_name in processed_fields:
184
- processed_fields[var_name] = vertical_regrid.apply(
185
- processed_fields[var_name]
186
- )
187
-
188
- # compute barotropic velocities
213
+ field = processed_fields[var_name]
214
+ if self.use_dask:
215
+ field = field.chunk(
216
+ get_dask_chunks(location, self.horizontal_chunk_size)
217
+ )
218
+ processed_fields[var_name] = vertical_regrid.apply(field)
219
+
220
+ # Compute barotropic velocities
189
221
  if "u" in variable_info and "v" in variable_info:
190
- self._get_vertical_coordinates(
191
- type="interface",
192
- additional_locations=["u", "v"],
193
- )
194
222
  for location in ["u", "v"]:
223
+ self._get_depth_coordinates(zeta, location, "interface")
195
224
  processed_fields[f"{location}bar"] = compute_barotropic_velocity(
196
225
  processed_fields[location],
197
- self.grid.ds[f"interface_depth_{location}"],
198
- )
199
-
200
- if type == "bgc":
201
- # Ensure time coordinate matches that of physical variables
202
- for var_name in variable_info.keys():
203
- processed_fields[var_name] = processed_fields[var_name].assign_coords(
204
- {"time": processed_fields["temp"]["time"]}
226
+ self.ds_depth_coords[f"interface_depth_{location}"],
205
227
  )
206
228
 
207
229
  for var_name in processed_fields.keys():
@@ -244,6 +266,12 @@ class InitialConditions:
244
266
  "climatology": self.bgc_source.get("climatology", False),
245
267
  },
246
268
  )
269
+ if self.adjust_depth_for_sea_surface_height:
270
+ logging.info("Sea surface height will be used to adjust depth coordinates.")
271
+ else:
272
+ logging.info(
273
+ "Sea surface height will NOT be used to adjust depth coordinates."
274
+ )
247
275
 
248
276
  def _get_data(self):
249
277
 
@@ -365,35 +393,61 @@ class InitialConditions:
365
393
 
366
394
  object.__setattr__(self, f"variable_info_{type}", variable_info)
367
395
 
368
- def _get_vertical_coordinates(self, type, additional_locations=["u", "v"]):
369
- """Retrieve layer and interface depth coordinates.
370
-
371
- This method computes and updates the layer and interface depth coordinates. It handles depth calculations for rho points and
372
- additional specified locations (u and v).
396
+ def _get_depth_coordinates(
397
+ self, zeta: xr.DataArray | float, location: str, depth_type: str = "layer"
398
+ ) -> None:
399
+ """Ensure depth coordinates are computed and stored for a given location and
400
+ depth type.
373
401
 
374
402
  Parameters
375
403
  ----------
376
- type : str
377
- The type of depth coordinate to retrieve. Valid options are:
378
- - "layer": Retrieves layer depth coordinates.
379
- - "interface": Retrieves interface depth coordinates.
380
-
381
- additional_locations : list of str, optional
382
- Specifies additional locations to compute depth coordinates for. Default is ["u", "v"].
383
- Valid options include:
384
- - "u": Computes depth coordinates for u points.
385
- - "v": Computes depth coordinates for v points.
386
-
387
- Updates
388
- -------
389
- self.grid.ds : xarray.Dataset
390
- The dataset is updated with the following vertical depth coordinates:
391
- - f"{type}_depth_rho": Depth coordinates at rho points.
392
- - f"{type}_depth_u": Depth coordinates at u points (if applicable).
393
- - f"{type}_depth_v": Depth coordinates at v points (if applicable).
404
+ zeta : xr.DataArray or float
405
+ Free-surface elevation (can be a scalar or a DataArray).
406
+ location : str
407
+ Grid location for depth computation ("rho", "u", or "v").
408
+ depth_type : str, optional
409
+ Type of depth coordinates to compute, by default "layer".
410
+
411
+ Notes
412
+ ------
413
+ Rather than calling compute_depth_coordinates from the vertical_coordinate.py module,
414
+ this method computes the depth coordinates from scratch because of optional chunking.
394
415
  """
416
+ key = f"{depth_type}_depth_{location}"
417
+
418
+ if key not in self.ds_depth_coords:
419
+ # Select the appropriate depth computation parameters
420
+ if depth_type == "layer":
421
+ Cs = self.grid.ds["Cs_r"]
422
+ sigma = self.grid.ds["sigma_r"]
423
+ elif depth_type == "interface":
424
+ Cs = self.grid.ds["Cs_w"]
425
+ sigma = self.grid.ds["sigma_w"]
426
+ else:
427
+ raise ValueError(
428
+ f"Invalid depth_type: {depth_type}. Choose 'layer' or 'interface'."
429
+ )
395
430
 
396
- self.grid.compute_depth_coordinates(type, additional_locations)
431
+ h = self.grid.ds["h"]
432
+
433
+ # Interpolate h and zeta to the specified location
434
+ if location == "u":
435
+ h = interpolate_from_rho_to_u(h)
436
+ if isinstance(zeta, xr.DataArray):
437
+ zeta = interpolate_from_rho_to_u(zeta)
438
+ elif location == "v":
439
+ h = interpolate_from_rho_to_v(h)
440
+ if isinstance(zeta, xr.DataArray):
441
+ zeta = interpolate_from_rho_to_v(zeta)
442
+
443
+ if self.use_dask:
444
+ h = h.chunk(get_dask_chunks(location, self.horizontal_chunk_size))
445
+ if self.adjust_depth_for_sea_surface_height:
446
+ zeta = zeta.chunk(
447
+ get_dask_chunks(location, self.horizontal_chunk_size)
448
+ )
449
+ depth = compute_depth(zeta, h, self.grid.ds.attrs["hc"], Cs, sigma)
450
+ self.ds_depth_coords[key] = depth
397
451
 
398
452
  def _write_into_dataset(self, processed_fields, d_meta):
399
453
 
@@ -407,7 +461,7 @@ class InitialConditions:
407
461
 
408
462
  # initialize vertical velocity to zero
409
463
  ds["w"] = xr.zeros_like(
410
- self.grid.ds["interface_depth_rho"].expand_dims(
464
+ (self.grid.ds["Cs_w"] * self.grid.ds["h"]).expand_dims(
411
465
  time=processed_fields["u"].time
412
466
  )
413
467
  ).astype(np.float32)
@@ -504,6 +558,9 @@ class InitialConditions:
504
558
  ds.attrs["roms_tools_version"] = roms_tools_version
505
559
  ds.attrs["ini_time"] = str(self.ini_time)
506
560
  ds.attrs["model_reference_date"] = str(self.model_reference_date)
561
+ ds.attrs["adjust_depth_for_sea_surface_height"] = str(
562
+ self.adjust_depth_for_sea_surface_height
563
+ )
507
564
  ds.attrs["source"] = self.source["name"]
508
565
  if self.bgc_source is not None:
509
566
  ds.attrs["bgc_source"] = self.bgc_source["name"]
@@ -637,7 +694,6 @@ class InitialConditions:
637
694
  loc = "rho"
638
695
  elif all(dim in field.dims for dim in ["eta_rho", "xi_u"]):
639
696
  loc = "u"
640
-
641
697
  elif all(dim in field.dims for dim in ["eta_v", "xi_rho"]):
642
698
  loc = "v"
643
699
  else:
@@ -656,47 +712,30 @@ class InitialConditions:
656
712
  if s is not None:
657
713
  layer_contours = False
658
714
  # Note that `layer_depth_{loc}` has already been computed during `__post_init__`.
659
- layer_depth = self.grid.ds[f"layer_depth_{loc}"]
660
- if layer_contours:
661
- if f"interface_depth_{loc}" not in self.grid.ds:
662
- if loc == "rho":
663
- self.get_vertical_coordinates(
664
- type="interface", additional_locations=[]
665
- )
666
- else:
667
- self.get_vertical_coordinates(
668
- type="interface", additional_locations=["u", "v"]
669
- )
670
- interface_depth = self.grid.ds[f"interface_depth_{loc}"]
671
- else:
672
- interface_depth = None
715
+ layer_depth = self.ds_depth_coords[f"layer_depth_{loc}"].squeeze()
673
716
 
674
717
  # Slice the field as desired
675
718
  def _slice_and_assign(
676
719
  field,
677
720
  mask,
678
721
  layer_depth,
679
- interface_depth,
680
722
  title,
681
723
  dim_name,
682
724
  dim_values,
683
725
  idx,
684
- layer_contours=False,
685
726
  ):
686
727
  if dim_name in field.dims:
687
728
  title = title + f", {dim_name} = {dim_values[idx].item()}"
688
729
  field = field.isel(**{dim_name: idx})
689
730
  mask = mask.isel(**{dim_name: idx})
690
731
  layer_depth = layer_depth.isel(**{dim_name: idx})
691
- if layer_contours:
692
- interface_depth = interface_depth.isel(**{dim_name: idx})
693
732
  if "s_rho" in field.dims:
694
733
  field = field.assign_coords({"layer_depth": layer_depth})
695
734
  else:
696
735
  raise ValueError(
697
736
  f"None of the expected dimensions ({dim_name}) found in field."
698
737
  )
699
- return field, mask, layer_depth, interface_depth, title
738
+ return field, mask, layer_depth, title
700
739
 
701
740
  title = field.long_name
702
741
  if s is not None:
@@ -708,29 +747,25 @@ class InitialConditions:
708
747
  depth_contours = False
709
748
 
710
749
  if eta is not None:
711
- field, mask, layer_depth, interface_depth, title = _slice_and_assign(
750
+ field, mask, layer_depth, title = _slice_and_assign(
712
751
  field,
713
752
  mask,
714
753
  layer_depth,
715
- interface_depth,
716
754
  title,
717
755
  "eta_rho" if "eta_rho" in field.dims else "eta_v",
718
756
  field.eta_rho if "eta_rho" in field.dims else field.eta_v,
719
757
  eta,
720
- layer_contours,
721
758
  )
722
759
 
723
760
  if xi is not None:
724
- field, mask, layer_depth, interface_depth, title = _slice_and_assign(
761
+ field, mask, layer_depth, title = _slice_and_assign(
725
762
  field,
726
763
  mask,
727
764
  layer_depth,
728
- interface_depth,
729
765
  title,
730
766
  "xi_rho" if "xi_rho" in field.dims else "xi_u",
731
767
  field.xi_rho if "xi_rho" in field.dims else field.xi_u,
732
768
  xi,
733
- layer_contours,
734
769
  )
735
770
 
736
771
  # Choose colorbar
@@ -757,19 +792,54 @@ class InitialConditions:
757
792
  c="g",
758
793
  )
759
794
  else:
760
- if not layer_contours:
761
- interface_depth = None
762
- else:
763
- # restrict number of layer_contours to 10 for the sake of plot clearity
764
- nr_layers = len(interface_depth["s_w"])
765
- selected_layers = np.linspace(
766
- 0, nr_layers - 1, min(nr_layers, 10), dtype=int
767
- )
768
- interface_depth = interface_depth.isel(s_w=selected_layers)
769
-
770
795
  if len(field.dims) == 2:
796
+ if layer_contours:
797
+ if loc == "rho":
798
+ # interface_depth_rho has not been computed yet
799
+ interface_depth = compute_depth_coordinates(
800
+ self.grid.ds,
801
+ self.ds.zeta,
802
+ depth_type="interface",
803
+ location=loc,
804
+ eta=eta,
805
+ xi=xi,
806
+ )
807
+ elif loc == "u":
808
+ index_kwargs = {}
809
+ if eta is not None:
810
+ index_kwargs["eta_rho"] = eta
811
+ if xi is not None:
812
+ index_kwargs["xi_u"] = xi
813
+
814
+ interface_depth = (
815
+ self.ds_depth_coords[f"interface_depth_{loc}"]
816
+ .isel(**index_kwargs)
817
+ .squeeze()
818
+ )
819
+ elif loc == "v":
820
+ index_kwargs = {}
821
+ if eta is not None:
822
+ index_kwargs["eta_v"] = eta
823
+ if xi is not None:
824
+ index_kwargs["xi_rho"] = xi
825
+
826
+ interface_depth = (
827
+ self.ds_depth_coords[f"interface_depth_{loc}"]
828
+ .isel(**index_kwargs)
829
+ .squeeze()
830
+ )
831
+
832
+ # restrict number of layer_contours to 10 for the sake of plot clearity
833
+ nr_layers = len(interface_depth["s_w"])
834
+ selected_layers = np.linspace(
835
+ 0, nr_layers - 1, min(nr_layers, 10), dtype=int
836
+ )
837
+ interface_depth = interface_depth.isel(s_w=selected_layers)
838
+ else:
839
+ interface_depth = None
840
+
771
841
  _section_plot(
772
- field.where(mask),
842
+ field,
773
843
  interface_depth=interface_depth,
774
844
  title=title,
775
845
  kwargs=kwargs,
@@ -781,36 +851,18 @@ class InitialConditions:
781
851
  else:
782
852
  _line_plot(field.where(mask), title=title, ax=ax)
783
853
 
784
- def save(
785
- self, filepath: Union[str, Path], np_eta: int = None, np_xi: int = None
786
- ) -> None:
787
- """Save the initial conditions information to a netCDF4 file.
788
-
789
- This method supports saving the dataset in two modes:
790
-
791
- 1. **Single File Mode (default)**:
792
-
793
- If both `np_eta` and `np_xi` are `None`, the entire dataset is saved as a single netCDF4 file
794
- with the base filename specified by `filepath.nc`.
795
-
796
- 2. **Partitioned Mode**:
797
-
798
- - If either `np_eta` or `np_xi` is specified, the dataset is divided into spatial tiles along the eta-axis and xi-axis.
799
- - Each spatial tile is saved as a separate netCDF4 file.
854
+ def save(self, filepath: Union[str, Path]) -> None:
855
+ """Save the initial conditions information to one netCDF4 file.
800
856
 
801
857
  Parameters
802
858
  ----------
803
859
  filepath : Union[str, Path]
804
860
  The base path or filename where the dataset should be saved.
805
- np_eta : int, optional
806
- The number of partitions along the `eta` direction. If `None`, no spatial partitioning is performed.
807
- np_xi : int, optional
808
- The number of partitions along the `xi` direction. If `None`, no spatial partitioning is performed.
809
861
 
810
862
  Returns
811
863
  -------
812
- List[Path]
813
- A list of Path objects for the filenames that were saved.
864
+ Path
865
+ A `Path` object representing the location of the saved file.
814
866
  """
815
867
 
816
868
  # Ensure filepath is a Path object
@@ -820,17 +872,11 @@ class InitialConditions:
820
872
  if filepath.suffix == ".nc":
821
873
  filepath = filepath.with_suffix("")
822
874
 
823
- if self.use_dask:
824
- from dask.diagnostics import ProgressBar
825
-
826
- with ProgressBar():
827
- self.ds.load()
828
-
829
875
  dataset_list = [self.ds]
830
876
  output_filenames = [str(filepath)]
831
877
 
832
878
  saved_filenames = save_datasets(
833
- dataset_list, output_filenames, np_eta=np_eta, np_xi=np_xi
879
+ dataset_list, output_filenames, use_dask=self.use_dask
834
880
  )
835
881
 
836
882
  return saved_filenames
@@ -8,12 +8,12 @@ import logging
8
8
  from scipy.interpolate import interp1d
9
9
  from roms_tools import Grid
10
10
  from roms_tools.plot import _plot_nesting
11
+ from roms_tools.utils import save_datasets
11
12
  from roms_tools.setup.utils import (
12
13
  interpolate_from_rho_to_u,
13
14
  interpolate_from_rho_to_v,
14
15
  get_boundary_coords,
15
16
  wrap_longitudes,
16
- save_datasets,
17
17
  _to_yaml,
18
18
  _from_yaml,
19
19
  )
@@ -114,37 +114,18 @@ class Nesting:
114
114
  self,
115
115
  filepath: Union[str, Path],
116
116
  filepath_child_grid: Union[str, Path],
117
- np_eta: int = None,
118
- np_xi: int = None,
119
117
  ) -> None:
120
118
  """Save the nesting and child grid file to netCDF4 files. The child grid file is
121
119
  required because the topography and mask of the child grid has been modified.
122
120
 
123
- This method allows saving the nesting and child grid data either each as a single file or each partitioned into multiple files, based on the provided options. The dataset can be saved in two modes:
124
-
125
- 1. **Single File Mode (default)**:
126
- - If both `np_eta` and `np_xi` are `None`, the entire dataset is saved as a single netCDF4 file.
127
- - The file is named based on the provided `filepath`, with `.nc` automatically appended to the filename.
128
-
129
- 2. **Partitioned Mode**:
130
- - If either `np_eta` or `np_xi` is specified, the dataset is partitioned spatially along the `eta` and `xi` axes into tiles.
131
- - Each tile is saved as a separate netCDF4 file. Filenames will be modified with an index to represent each partition, e.g., `"filepath_YYYYMM.0.nc"`, `"filepath_YYYYMM.1.nc"`, etc.
132
-
133
121
  Parameters
134
122
  ----------
135
123
  filepath : Union[str, Path]
136
124
  The base path and filename for the output files. The filenames will include the specified path and the `.nc` extension.
137
- If partitioning is used, additional indices will be appended to the filenames, e.g., `"filepath.0.nc"`, `"filepath.1.nc"`, etc.
138
125
 
139
126
  filepath_child_grid : Union[str, Path]
140
127
  The base path and filename for saving the childe grid file.
141
128
 
142
- np_eta : int, optional
143
- The number of partitions along the `eta` direction. If `None`, no spatial partitioning is performed along the `eta` axis.
144
-
145
- np_xi : int, optional
146
- The number of partitions along the `xi` direction. If `None`, no spatial partitioning is performed along the `xi` axis.
147
-
148
129
  Returns
149
130
  -------
150
131
  List[Path]
@@ -164,9 +145,7 @@ class Nesting:
164
145
  dataset_list = [self.ds, self.child_grid.ds]
165
146
  output_filenames = [str(filepath), str(filepath_child_grid)]
166
147
 
167
- saved_filenames = save_datasets(
168
- dataset_list, output_filenames, np_eta=np_eta, np_xi=np_xi
169
- )
148
+ saved_filenames = save_datasets(dataset_list, output_filenames)
170
149
 
171
150
  return saved_filenames
172
151