roms-tools 1.7.0__py3-none-any.whl → 2.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. roms_tools/__init__.py +2 -1
  2. roms_tools/setup/boundary_forcing.py +246 -146
  3. roms_tools/setup/datasets.py +229 -69
  4. roms_tools/setup/download.py +13 -17
  5. roms_tools/setup/grid.py +777 -614
  6. roms_tools/setup/initial_conditions.py +168 -32
  7. roms_tools/setup/mask.py +115 -0
  8. roms_tools/setup/nesting.py +575 -0
  9. roms_tools/setup/plot.py +218 -63
  10. roms_tools/setup/regrid.py +4 -2
  11. roms_tools/setup/river_forcing.py +125 -29
  12. roms_tools/setup/surface_forcing.py +31 -25
  13. roms_tools/setup/tides.py +29 -14
  14. roms_tools/setup/topography.py +250 -153
  15. roms_tools/setup/utils.py +174 -44
  16. roms_tools/setup/vertical_coordinate.py +5 -16
  17. roms_tools/tests/test_setup/test_boundary_forcing.py +10 -5
  18. roms_tools/tests/test_setup/test_data/grid.zarr/.zattrs +0 -1
  19. roms_tools/tests/test_setup/test_data/grid.zarr/.zmetadata +56 -201
  20. roms_tools/tests/test_setup/test_data/grid.zarr/Cs_r/.zattrs +1 -1
  21. roms_tools/tests/test_setup/test_data/grid.zarr/Cs_w/.zattrs +1 -1
  22. roms_tools/tests/test_setup/test_data/grid.zarr/{layer_depth_rho → sigma_r}/.zarray +2 -6
  23. roms_tools/tests/test_setup/test_data/grid.zarr/sigma_r/.zattrs +7 -0
  24. roms_tools/tests/test_setup/test_data/grid.zarr/sigma_r/0 +0 -0
  25. roms_tools/tests/test_setup/test_data/grid.zarr/sigma_w/.zarray +20 -0
  26. roms_tools/tests/test_setup/test_data/grid.zarr/sigma_w/.zattrs +7 -0
  27. roms_tools/tests/test_setup/test_data/grid.zarr/sigma_w/0 +0 -0
  28. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zattrs +1 -2
  29. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zmetadata +58 -203
  30. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/Cs_r/.zattrs +1 -1
  31. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/Cs_w/.zattrs +1 -1
  32. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/h/.zattrs +1 -1
  33. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/h/0.0 +0 -0
  34. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_coarse/0.0 +0 -0
  35. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_rho/0.0 +0 -0
  36. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_u/0.0 +0 -0
  37. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_v/0.0 +0 -0
  38. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/sigma_r/.zarray +20 -0
  39. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/sigma_r/.zattrs +7 -0
  40. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/sigma_r/0 +0 -0
  41. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/sigma_w/.zarray +20 -0
  42. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/sigma_w/.zattrs +7 -0
  43. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/sigma_w/0 +0 -0
  44. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/.zmetadata +2 -3
  45. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_tracer/.zattrs +1 -2
  46. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/tracer_name/.zarray +1 -1
  47. roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/tracer_name/0 +0 -0
  48. roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/.zmetadata +5 -6
  49. roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_tracer/.zarray +2 -2
  50. roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_tracer/.zattrs +1 -2
  51. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_tracer/0.0.0 +0 -0
  52. roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/tracer_name/.zarray +2 -2
  53. roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/tracer_name/0 +0 -0
  54. roms_tools/tests/test_setup/test_datasets.py +2 -2
  55. roms_tools/tests/test_setup/test_grid.py +110 -12
  56. roms_tools/tests/test_setup/test_initial_conditions.py +2 -1
  57. roms_tools/tests/test_setup/test_nesting.py +489 -0
  58. roms_tools/tests/test_setup/test_river_forcing.py +53 -15
  59. roms_tools/tests/test_setup/test_surface_forcing.py +3 -22
  60. roms_tools/tests/test_setup/test_tides.py +2 -1
  61. roms_tools/tests/test_setup/test_topography.py +106 -1
  62. roms_tools/tests/test_setup/test_validation.py +2 -2
  63. {roms_tools-1.7.0.dist-info → roms_tools-2.1.0.dist-info}/LICENSE +1 -1
  64. {roms_tools-1.7.0.dist-info → roms_tools-2.1.0.dist-info}/METADATA +9 -4
  65. {roms_tools-1.7.0.dist-info → roms_tools-2.1.0.dist-info}/RECORD +85 -108
  66. {roms_tools-1.7.0.dist-info → roms_tools-2.1.0.dist-info}/WHEEL +1 -1
  67. roms_tools/_version.py +0 -2
  68. roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_rho/.zarray +0 -24
  69. roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_rho/.zattrs +0 -9
  70. roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_rho/0.0.0 +0 -0
  71. roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_u/.zarray +0 -24
  72. roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_u/.zattrs +0 -9
  73. roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_u/0.0.0 +0 -0
  74. roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_v/.zarray +0 -24
  75. roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_v/.zattrs +0 -9
  76. roms_tools/tests/test_setup/test_data/grid.zarr/interface_depth_v/0.0.0 +0 -0
  77. roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_rho/.zattrs +0 -9
  78. roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_rho/0.0.0 +0 -0
  79. roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_u/.zarray +0 -24
  80. roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_u/.zattrs +0 -9
  81. roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_u/0.0.0 +0 -0
  82. roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_v/.zarray +0 -24
  83. roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_v/.zattrs +0 -9
  84. roms_tools/tests/test_setup/test_data/grid.zarr/layer_depth_v/0.0.0 +0 -0
  85. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_rho/.zarray +0 -24
  86. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_rho/.zattrs +0 -9
  87. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_rho/0.0.0 +0 -0
  88. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_u/.zarray +0 -24
  89. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_u/.zattrs +0 -9
  90. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_u/0.0.0 +0 -0
  91. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_v/.zarray +0 -24
  92. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_v/.zattrs +0 -9
  93. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/interface_depth_v/0.0.0 +0 -0
  94. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_rho/.zarray +0 -24
  95. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_rho/.zattrs +0 -9
  96. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_rho/0.0.0 +0 -0
  97. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_u/.zarray +0 -24
  98. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_u/.zattrs +0 -9
  99. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_u/0.0.0 +0 -0
  100. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_v/.zarray +0 -24
  101. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_v/.zattrs +0 -9
  102. roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/layer_depth_v/0.0.0 +0 -0
  103. roms_tools/tests/test_setup/test_data/river_forcing.zarr/river_tracer/0.0.0 +0 -0
  104. roms_tools/tests/test_setup/test_data/river_forcing.zarr/tracer_name/0 +0 -0
  105. roms_tools/tests/test_setup/test_vertical_coordinate.py +0 -91
  106. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/.zattrs +0 -0
  107. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/.zgroup +0 -0
  108. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/abs_time/.zarray +0 -0
  109. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/abs_time/.zattrs +0 -0
  110. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/abs_time/0 +0 -0
  111. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/month/.zarray +0 -0
  112. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/month/.zattrs +0 -0
  113. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/month/0 +0 -0
  114. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_name/.zarray +0 -0
  115. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_name/.zattrs +0 -0
  116. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_name/0 +0 -0
  117. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_time/.zarray +0 -0
  118. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_time/.zattrs +0 -0
  119. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_time/0 +0 -0
  120. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_volume/.zarray +0 -0
  121. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_volume/.zattrs +0 -0
  122. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/river_volume/0.0 +0 -0
  123. /roms_tools/tests/test_setup/test_data/{river_forcing.zarr → river_forcing_with_bgc.zarr}/tracer_name/.zattrs +0 -0
  124. {roms_tools-1.7.0.dist-info → roms_tools-2.1.0.dist-info}/top_level.txt +0 -0
@@ -6,6 +6,7 @@ from typing import Dict, Union, List, Optional
6
6
  from roms_tools.setup.grid import Grid
7
7
  from datetime import datetime
8
8
  from roms_tools.setup.datasets import GLORYSDataset, CESMBGCDataset
9
+ from roms_tools.setup.vertical_coordinate import compute_depth
9
10
  from roms_tools.setup.utils import (
10
11
  nan_check,
11
12
  substitute_nans_by_fillvalue,
@@ -15,6 +16,8 @@ from roms_tools.setup.utils import (
15
16
  rotate_velocities,
16
17
  compute_barotropic_velocity,
17
18
  transpose_dimensions,
19
+ interpolate_from_rho_to_u,
20
+ interpolate_from_rho_to_v,
18
21
  _to_yaml,
19
22
  _from_yaml,
20
23
  )
@@ -63,6 +66,10 @@ class InitialConditions:
63
66
  The reference date for the model. Defaults to January 1, 2000.
64
67
  use_dask: bool, optional
65
68
  Indicates whether to use dask for processing. If True, data is processed with dask; if False, data is processed eagerly. Defaults to False.
69
+ bypass_validation: bool, optional
70
+ Indicates whether to skip validation checks in the processed data. When set to True,
71
+ the validation process that ensures no NaN values exist at wet points
72
+ in the processed dataset is bypassed. Defaults to False.
66
73
 
67
74
  Examples
68
75
  --------
@@ -84,6 +91,7 @@ class InitialConditions:
84
91
  bgc_source: Optional[Dict[str, Union[str, Path, List[Union[str, Path]]]]] = None
85
92
  model_reference_date: datetime = datetime(2000, 1, 1)
86
93
  use_dask: bool = False
94
+ bypass_validation: bool = False
87
95
 
88
96
  ds: xr.Dataset = field(init=False, repr=False)
89
97
 
@@ -92,23 +100,18 @@ class InitialConditions:
92
100
  self._input_checks()
93
101
 
94
102
  processed_fields = {}
95
- processed_fields, variable_info = self._process_data(
96
- processed_fields, type="physics"
97
- )
103
+ processed_fields = self._process_data(processed_fields, type="physics")
98
104
 
99
105
  if self.bgc_source is not None:
100
- processed_fields, bgc_variable_info = self._process_data(
101
- processed_fields, type="bgc"
102
- )
106
+ processed_fields = self._process_data(processed_fields, type="bgc")
103
107
 
104
108
  d_meta = get_variable_metadata()
105
109
  ds = self._write_into_dataset(processed_fields, d_meta)
106
110
 
107
111
  ds = self._add_global_metadata(ds)
108
112
 
109
- if self.bgc_source is not None:
110
- variable_info = {**variable_info, **bgc_variable_info}
111
- self._validate(ds, variable_info)
113
+ if not self.bypass_validation:
114
+ self._validate(ds)
112
115
 
113
116
  # substitute NaNs over land by a fill value to avoid blow-up of ROMS
114
117
  for var_name in ds.data_vars:
@@ -133,7 +136,9 @@ class InitialConditions:
133
136
  data.extrapolate_deepest_to_bottom()
134
137
  data.apply_lateral_fill()
135
138
 
136
- variable_info = self._set_variable_info(data, type=type)
139
+ self._set_variable_info(data, type=type)
140
+ attr_name = f"variable_info_{type}"
141
+ variable_info = getattr(self, attr_name)
137
142
  var_names = variable_info.keys()
138
143
 
139
144
  # lateral regridding
@@ -153,19 +158,31 @@ class InitialConditions:
153
158
  interpolate=True,
154
159
  )
155
160
 
156
- # vertical regridding
161
+ var_names_dict = {}
157
162
  for location in ["rho", "u", "v"]:
158
- var_names = [
163
+ var_names_dict[location] = [
159
164
  name
160
165
  for name, info in variable_info.items()
161
166
  if info["location"] == location and info["is_3d"]
162
167
  ]
163
- if len(var_names) > 0:
168
+
169
+ # compute layer depth coordinates
170
+ if len(var_names_dict["u"]) > 0 or len(var_names_dict["v"]) > 0:
171
+ self._get_vertical_coordinates(
172
+ type="layer",
173
+ additional_locations=["u", "v"],
174
+ )
175
+ else:
176
+ if len(var_names_dict["rho"]) > 0:
177
+ self._get_vertical_coordinates(type="layer", additional_locations=[])
178
+ # vertical regridding
179
+ for location in ["rho", "u", "v"]:
180
+ if len(var_names_dict[location]) > 0:
164
181
  vertical_regrid = VerticalRegrid(
165
182
  self.grid.ds[f"layer_depth_{location}"],
166
183
  data.ds[data.dim_names["depth"]],
167
184
  )
168
- for var_name in var_names:
185
+ for var_name in var_names_dict[location]:
169
186
  if var_name in processed_fields:
170
187
  processed_fields[var_name] = vertical_regrid.apply(
171
188
  processed_fields[var_name]
@@ -173,10 +190,14 @@ class InitialConditions:
173
190
 
174
191
  # compute barotropic velocities
175
192
  if "u" in variable_info and "v" in variable_info:
176
- for var_name in ["u", "v"]:
177
- processed_fields[f"{var_name}bar"] = compute_barotropic_velocity(
178
- processed_fields[var_name],
179
- self.grid.ds[f"interface_depth_{var_name}"],
193
+ self._get_vertical_coordinates(
194
+ type="interface",
195
+ additional_locations=["u", "v"],
196
+ )
197
+ for location in ["u", "v"]:
198
+ processed_fields[f"{location}bar"] = compute_barotropic_velocity(
199
+ processed_fields[location],
200
+ self.grid.ds[f"interface_depth_{location}"],
180
201
  )
181
202
 
182
203
  if type == "bgc":
@@ -191,7 +212,7 @@ class InitialConditions:
191
212
  processed_fields[var_name]
192
213
  )
193
214
 
194
- return processed_fields, variable_info
215
+ return processed_fields
195
216
 
196
217
  def _input_checks(self):
197
218
 
@@ -345,7 +366,84 @@ class InitialConditions:
345
366
  else:
346
367
  variable_info[var_name] = {**default_info, "validate": False}
347
368
 
348
- return variable_info
369
+ object.__setattr__(self, f"variable_info_{type}", variable_info)
370
+
371
+ def _get_vertical_coordinates(self, type, additional_locations=["u", "v"]):
372
+ """Retrieve layer and interface depth coordinates.
373
+
374
+ This method computes and updates the layer and interface depth coordinates. It handles depth calculations for rho points and
375
+ additional specified locations (u and v).
376
+
377
+ Parameters
378
+ ----------
379
+ type : str
380
+ The type of depth coordinate to retrieve. Valid options are:
381
+ - "layer": Retrieves layer depth coordinates.
382
+ - "interface": Retrieves interface depth coordinates.
383
+
384
+ additional_locations : list of str, optional
385
+ Specifies additional locations to compute depth coordinates for. Default is ["u", "v"].
386
+ Valid options include:
387
+ - "u": Computes depth coordinates for u points.
388
+ - "v": Computes depth coordinates for v points.
389
+
390
+ Updates
391
+ -------
392
+ self.grid.ds : xarray.Dataset
393
+ The dataset is updated with the following vertical depth coordinates:
394
+ - f"{type}_depth_rho": Depth coordinates at rho points.
395
+ - f"{type}_depth_u": Depth coordinates at u points (if applicable).
396
+ - f"{type}_depth_v": Depth coordinates at v points (if applicable).
397
+ """
398
+
399
+ layer_vars = []
400
+ for location in ["rho"] + additional_locations:
401
+ layer_vars.append(f"{type}_depth_{location}")
402
+
403
+ if all(layer_var in self.grid.ds for layer_var in layer_vars):
404
+ # Vertical coordinate data already exists
405
+ pass
406
+
407
+ elif f"{type}_depth_rho" in self.grid.ds:
408
+ depth = self.grid.ds[f"{type}_depth_rho"]
409
+
410
+ if "u" in additional_locations or "v" in additional_locations:
411
+ # interpolation
412
+ if "u" in additional_locations:
413
+ depth_u = interpolate_from_rho_to_u(depth)
414
+ depth_u.attrs["long_name"] = f"{type} depth at u-points"
415
+ depth_u.attrs["units"] = "m"
416
+ self.grid.ds[f"{type}_depth_u"] = depth_u
417
+ if "v" in additional_locations:
418
+ depth_v = interpolate_from_rho_to_v(depth)
419
+ depth_v.attrs["long_name"] = f"{type} depth at v-points"
420
+ depth_v.attrs["units"] = "m"
421
+ self.grid.ds[f"{type}_depth_v"] = depth_v
422
+ else:
423
+ h = self.grid.ds["h"]
424
+ if type == "layer":
425
+ depth = compute_depth(
426
+ 0, h, self.grid.hc, self.grid.ds.Cs_r, self.grid.ds.sigma_r
427
+ )
428
+ else:
429
+ depth = compute_depth(
430
+ 0, h, self.grid.hc, self.grid.ds.Cs_w, self.grid.ds.sigma_w
431
+ )
432
+
433
+ depth.attrs["long_name"] = f"{type} depth at rho-points"
434
+ depth.attrs["units"] = "m"
435
+ self.grid.ds[f"{type}_depth_rho"] = depth
436
+
437
+ if "u" in additional_locations or "v" in additional_locations:
438
+ # interpolation
439
+ depth_u = interpolate_from_rho_to_u(depth)
440
+ depth_u.attrs["long_name"] = f"{type} depth at u-points"
441
+ depth_u.attrs["units"] = "m"
442
+ depth_v = interpolate_from_rho_to_v(depth)
443
+ depth_v.attrs["long_name"] = f"{type} depth at v-points"
444
+ depth_v.attrs["units"] = "m"
445
+ self.grid.ds[f"{type}_depth_u"] = depth_u
446
+ self.grid.ds[f"{type}_depth_v"] = depth_v
349
447
 
350
448
  def _write_into_dataset(self, processed_fields, d_meta):
351
449
 
@@ -410,7 +508,7 @@ class InitialConditions:
410
508
 
411
509
  return ds
412
510
 
413
- def _validate(self, ds, variable_info):
511
+ def _validate(self, ds):
414
512
  """Validates the dataset by checking for NaN values in SSH at wet points, which
415
513
  would indicate missing raw data coverage over the target domain.
416
514
 
@@ -418,8 +516,6 @@ class InitialConditions:
418
516
  ----------
419
517
  ds : xarray.Dataset
420
518
  The dataset to validate.
421
- variable_info : dict
422
- A dictionary containing metadata about the variables, including whether to validate them.
423
519
 
424
520
  Raises
425
521
  ------
@@ -431,6 +527,10 @@ class InitialConditions:
431
527
  -----
432
528
  This check is only applied to the 2D variable SSH to improve performance.
433
529
  """
530
+ if self.bgc_source is not None:
531
+ variable_info = {**self.variable_info_physics, **self.variable_info_bgc}
532
+ else:
533
+ variable_info = self.variable_info_physics
434
534
 
435
535
  for var_name in variable_info:
436
536
  # Only validate variables based on "validate" flag if use_dask is False
@@ -570,9 +670,17 @@ class InitialConditions:
570
670
  self.ds[var_name].load()
571
671
 
572
672
  field = self.ds[var_name].squeeze()
673
+ if s is not None:
674
+ layer_contours = False
573
675
 
574
676
  if all(dim in field.dims for dim in ["eta_rho", "xi_rho"]):
575
- interface_depth = self.grid.ds.interface_depth_rho
677
+ if layer_contours:
678
+ if "interface_depth_rho" in self.grid.ds:
679
+ interface_depth = self.grid.ds.interface_depth_rho
680
+ else:
681
+ self.get_vertical_coordinates(
682
+ type="interface", additional_locations=[]
683
+ )
576
684
  layer_depth = self.grid.ds.layer_depth_rho
577
685
  mask = self.grid.ds.mask_rho
578
686
  field = field.assign_coords(
@@ -580,7 +688,13 @@ class InitialConditions:
580
688
  )
581
689
 
582
690
  elif all(dim in field.dims for dim in ["eta_rho", "xi_u"]):
583
- interface_depth = self.grid.ds.interface_depth_u
691
+ if layer_contours:
692
+ if "interface_depth_u" in self.grid.ds:
693
+ interface_depth = self.grid.ds.interface_depth_u
694
+ else:
695
+ self.get_vertical_coordinates(
696
+ type="interface", additional_locations=["u", "v"]
697
+ )
584
698
  layer_depth = self.grid.ds.layer_depth_u
585
699
  mask = self.grid.ds.mask_u
586
700
  field = field.assign_coords(
@@ -588,7 +702,13 @@ class InitialConditions:
588
702
  )
589
703
 
590
704
  elif all(dim in field.dims for dim in ["eta_v", "xi_rho"]):
591
- interface_depth = self.grid.ds.interface_depth_v
705
+ if layer_contours:
706
+ if "interface_depth_v" in self.grid.ds:
707
+ interface_depth = self.grid.ds.interface_depth_v
708
+ else:
709
+ self.get_vertical_coordinates(
710
+ type="interface", additional_locations=["u", "v"]
711
+ )
592
712
  layer_depth = self.grid.ds.layer_depth_v
593
713
  mask = self.grid.ds.mask_v
594
714
  field = field.assign_coords(
@@ -612,14 +732,16 @@ class InitialConditions:
612
732
  title = title + f", eta_rho = {field.eta_rho[eta].item()}"
613
733
  field = field.isel(eta_rho=eta)
614
734
  layer_depth = layer_depth.isel(eta_rho=eta)
615
- interface_depth = interface_depth.isel(eta_rho=eta)
735
+ if layer_contours:
736
+ interface_depth = interface_depth.isel(eta_rho=eta)
616
737
  if "s_rho" in field.dims:
617
738
  field = field.assign_coords({"layer_depth": layer_depth})
618
739
  elif "eta_v" in field.dims:
619
740
  title = title + f", eta_v = {field.eta_v[eta].item()}"
620
741
  field = field.isel(eta_v=eta)
621
742
  layer_depth = layer_depth.isel(eta_v=eta)
622
- interface_depth = interface_depth.isel(eta_v=eta)
743
+ if layer_contours:
744
+ interface_depth = interface_depth.isel(eta_v=eta)
623
745
  if "s_rho" in field.dims:
624
746
  field = field.assign_coords({"layer_depth": layer_depth})
625
747
  else:
@@ -631,14 +753,16 @@ class InitialConditions:
631
753
  title = title + f", xi_rho = {field.xi_rho[xi].item()}"
632
754
  field = field.isel(xi_rho=xi)
633
755
  layer_depth = layer_depth.isel(xi_rho=xi)
634
- interface_depth = interface_depth.isel(xi_rho=xi)
756
+ if layer_contours:
757
+ interface_depth = interface_depth.isel(xi_rho=xi)
635
758
  if "s_rho" in field.dims:
636
759
  field = field.assign_coords({"layer_depth": layer_depth})
637
760
  elif "xi_u" in field.dims:
638
761
  title = title + f", xi_u = {field.xi_u[xi].item()}"
639
762
  field = field.isel(xi_u=xi)
640
763
  layer_depth = layer_depth.isel(xi_u=xi)
641
- interface_depth = interface_depth.isel(xi_u=xi)
764
+ if layer_contours:
765
+ interface_depth = interface_depth.isel(xi_u=xi)
642
766
  if "s_rho" in field.dims:
643
767
  field = field.assign_coords({"layer_depth": layer_depth})
644
768
  else:
@@ -764,7 +888,10 @@ class InitialConditions:
764
888
 
765
889
  @classmethod
766
890
  def from_yaml(
767
- cls, filepath: Union[str, Path], use_dask: bool = False
891
+ cls,
892
+ filepath: Union[str, Path],
893
+ use_dask: bool = False,
894
+ bypass_validation: bool = False,
768
895
  ) -> "InitialConditions":
769
896
  """Create an instance of the InitialConditions class from a YAML file.
770
897
 
@@ -774,6 +901,10 @@ class InitialConditions:
774
901
  The path to the YAML file from which the parameters will be read.
775
902
  use_dask: bool, optional
776
903
  Indicates whether to use dask for processing. If True, data is processed with dask; if False, data is processed eagerly. Defaults to False.
904
+ bypass_validation: bool, optional
905
+ Indicates whether to skip validation checks in the processed data. When set to True,
906
+ the validation process that ensures no NaN values exist at wet points
907
+ in the processed dataset is bypassed. Defaults to False.
777
908
 
778
909
  Returns
779
910
  -------
@@ -784,4 +915,9 @@ class InitialConditions:
784
915
 
785
916
  grid = Grid.from_yaml(filepath)
786
917
  initial_conditions_params = _from_yaml(cls, filepath)
787
- return cls(grid=grid, **initial_conditions_params, use_dask=use_dask)
918
+ return cls(
919
+ grid=grid,
920
+ **initial_conditions_params,
921
+ use_dask=use_dask,
922
+ bypass_validation=bypass_validation,
923
+ )
@@ -0,0 +1,115 @@
1
+ import xarray as xr
2
+ import numpy as np
3
+ import regionmask
4
+ import warnings
5
+ from scipy.ndimage import label
6
+ from roms_tools.setup.utils import (
7
+ interpolate_from_rho_to_u,
8
+ interpolate_from_rho_to_v,
9
+ handle_boundaries,
10
+ )
11
+
12
+
13
+ def _add_mask(ds):
14
+ """Adds a land/water mask to the dataset at rho-points.
15
+
16
+ Parameters
17
+ ----------
18
+ ds : xarray.Dataset
19
+ Input dataset containing latitude and longitude coordinates at rho-points.
20
+
21
+ Returns
22
+ -------
23
+ xarray.Dataset
24
+ The original dataset with an added 'mask_rho' variable, representing land/water mask.
25
+ """
26
+ land = regionmask.defined_regions.natural_earth_v5_0_0.land_10
27
+
28
+ # Suppress specific warning
29
+ with warnings.catch_warnings():
30
+ warnings.filterwarnings(
31
+ "ignore", message="No gridpoint belongs to any region.*"
32
+ )
33
+ land_mask = land.mask(ds["lon_rho"], ds["lat_rho"])
34
+ mask = land_mask.isnull()
35
+
36
+ # fill enclosed basins with land
37
+ mask = _fill_enclosed_basins(mask.values)
38
+ # adjust mask boundaries by copying values from adjacent cells
39
+ mask = handle_boundaries(mask)
40
+
41
+ ds["mask_rho"] = xr.DataArray(mask.astype(np.int32), dims=("eta_rho", "xi_rho"))
42
+ ds["mask_rho"].attrs = {
43
+ "long_name": "Mask at rho-points",
44
+ "units": "land/water (0/1)",
45
+ }
46
+ ds = _add_velocity_masks(ds)
47
+
48
+ return ds
49
+
50
+
51
+ def _fill_enclosed_basins(mask) -> np.ndarray:
52
+ """Fills enclosed basins in the mask with land (value = 1).
53
+
54
+ This function identifies the largest connected region in the mask, which is assumed to represent
55
+ the land, and sets all other regions to water (value = 0).
56
+
57
+ Parameters
58
+ ----------
59
+ mask : np.ndarray
60
+ A binary array representing the land/water mask (land = 1, water = 0).
61
+
62
+ Returns
63
+ -------
64
+ np.ndarray
65
+ The modified mask with enclosed basins filled with land (1).
66
+ """
67
+
68
+ # Label connected regions in the mask
69
+ reg, nreg = label(mask)
70
+ # Find the largest region
71
+ lint = 0
72
+ lreg = 0
73
+ for ireg in range(nreg):
74
+ int_ = np.sum(reg == ireg)
75
+ if int_ > lint and mask[reg == ireg].sum() > 0:
76
+ lreg = ireg
77
+ lint = int_
78
+
79
+ # Remove regions other than the largest one
80
+ for ireg in range(nreg):
81
+ if ireg != lreg:
82
+ mask[reg == ireg] = 0
83
+
84
+ return mask
85
+
86
+
87
+ def _add_velocity_masks(ds):
88
+ """Adds velocity masks for u- and v-points based on the rho-point mask.
89
+
90
+ This function generates masks for u- and v-points by interpolating the rho-point land/water mask.
91
+ The interpolation method used is "multiplicative", which scales the rho-point mask to the respective
92
+ u- and v-points.
93
+
94
+ Parameters
95
+ ----------
96
+ ds : xarray.Dataset
97
+ The dataset containing the land/water mask at rho-points (`mask_rho`).
98
+
99
+ Returns
100
+ -------
101
+ xarray.Dataset
102
+ The input dataset with added velocity masks (`mask_u` and `mask_v`) for u- and v-points.
103
+ """
104
+ # add u- and v-masks
105
+ ds["mask_u"] = interpolate_from_rho_to_u(
106
+ ds["mask_rho"], method="multiplicative"
107
+ ).astype(np.int32)
108
+ ds["mask_v"] = interpolate_from_rho_to_v(
109
+ ds["mask_rho"], method="multiplicative"
110
+ ).astype(np.int32)
111
+
112
+ ds["mask_u"].attrs = {"long_name": "Mask at u-points", "units": "land/water (0/1)"}
113
+ ds["mask_v"].attrs = {"long_name": "Mask at v-points", "units": "land/water (0/1)"}
114
+
115
+ return ds