roms-tools 3.4.0__py3-none-any.whl → 3.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- roms_tools/datasets/lat_lon_datasets.py +12 -0
- roms_tools/datasets/roms_dataset.py +140 -53
- roms_tools/datasets/utils.py +14 -2
- roms_tools/regrid.py +76 -0
- roms_tools/setup/boundary_forcing.py +2 -2
- roms_tools/setup/grid.py +17 -3
- roms_tools/setup/initial_conditions.py +314 -55
- roms_tools/setup/mask.py +2 -5
- roms_tools/setup/nesting.py +6 -3
- roms_tools/setup/surface_forcing.py +1 -2
- roms_tools/setup/tides.py +6 -5
- roms_tools/setup/utils.py +220 -142
- roms_tools/tests/test_datasets/test_roms_dataset.py +225 -21
- roms_tools/tests/test_regrid.py +120 -1
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/ALK/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/ALK/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/ALK_ALT_CO2/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/ALK_ALT_CO2/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/Cs_r/c/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/Cs_r/zarr.json +47 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/Cs_w/c/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/Cs_w/zarr.json +47 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DIC/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DIC/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DIC_ALT_CO2/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DIC_ALT_CO2/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DOC/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DOC/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DOCr/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DOCr/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DON/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DON/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DONr/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DONr/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DOP/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DOP/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DOPr/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/DOPr/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/Fe/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/Fe/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/Lig/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/Lig/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/NH4/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/NH4/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/NO3/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/NO3/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/O2/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/O2/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/PO4/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/PO4/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/SiO3/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/SiO3/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/abs_time/zarr.json +47 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diatC/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diatC/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diatChl/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diatChl/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diatFe/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diatFe/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diatP/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diatP/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diatSi/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diatSi/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diazC/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diazC/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diazChl/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diazChl/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diazFe/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diazFe/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diazP/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/diazP/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/ocean_time/c/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/ocean_time/zarr.json +47 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/salt/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/salt/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/spC/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/spC/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/spCaCO3/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/spCaCO3/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/spChl/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/spChl/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/spFe/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/spFe/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/spP/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/spP/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/temp/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/temp/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/u/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/u/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/ubar/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/ubar/zarr.json +54 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/v/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/v/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/vbar/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/vbar/zarr.json +54 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/w/zarr.json +57 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/zarr.json +2481 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/zeta/c/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/zeta/zarr.json +54 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/zooC/c/0/0/0/0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_from_roms.zarr/zooC/zarr.json +57 -0
- roms_tools/tests/test_setup/test_grid.py +24 -0
- roms_tools/tests/test_setup/test_initial_conditions.py +128 -11
- roms_tools/tests/test_setup/test_validation.py +15 -0
- roms_tools/tests/test_utils.py +287 -0
- roms_tools/utils.py +177 -72
- {roms_tools-3.4.0.dist-info → roms_tools-3.5.0.dist-info}/METADATA +2 -3
- {roms_tools-3.4.0.dist-info → roms_tools-3.5.0.dist-info}/RECORD +111 -24
- {roms_tools-3.4.0.dist-info → roms_tools-3.5.0.dist-info}/WHEEL +1 -1
- {roms_tools-3.4.0.dist-info → roms_tools-3.5.0.dist-info}/licenses/LICENSE +0 -0
- {roms_tools-3.4.0.dist-info → roms_tools-3.5.0.dist-info}/top_level.txt +0 -0
|
@@ -17,8 +17,14 @@ from roms_tools.datasets.lat_lon_datasets import (
|
|
|
17
17
|
LatLonDataset,
|
|
18
18
|
UnifiedBGCDataset,
|
|
19
19
|
)
|
|
20
|
+
from roms_tools.datasets.roms_dataset import ROMSDataset, choose_subdomain
|
|
20
21
|
from roms_tools.plot import plot
|
|
21
|
-
from roms_tools.regrid import
|
|
22
|
+
from roms_tools.regrid import (
|
|
23
|
+
LateralRegridFromROMS,
|
|
24
|
+
LateralRegridToROMS,
|
|
25
|
+
VerticalRegrid,
|
|
26
|
+
VerticalRegridToROMS,
|
|
27
|
+
)
|
|
22
28
|
from roms_tools.setup.utils import (
|
|
23
29
|
RawDataSource,
|
|
24
30
|
compute_barotropic_velocity,
|
|
@@ -27,7 +33,7 @@ from roms_tools.setup.utils import (
|
|
|
27
33
|
get_target_coords,
|
|
28
34
|
get_variable_metadata,
|
|
29
35
|
nan_check,
|
|
30
|
-
|
|
36
|
+
pop_grid_data,
|
|
31
37
|
substitute_nans_by_fillvalue,
|
|
32
38
|
to_dict,
|
|
33
39
|
write_to_yaml,
|
|
@@ -35,6 +41,7 @@ from roms_tools.setup.utils import (
|
|
|
35
41
|
from roms_tools.utils import (
|
|
36
42
|
interpolate_from_rho_to_u,
|
|
37
43
|
interpolate_from_rho_to_v,
|
|
44
|
+
rotate_velocities,
|
|
38
45
|
save_datasets,
|
|
39
46
|
transpose_dimensions,
|
|
40
47
|
)
|
|
@@ -112,6 +119,18 @@ class InitialConditions:
|
|
|
112
119
|
... "climatology": False,
|
|
113
120
|
... },
|
|
114
121
|
... )
|
|
122
|
+
|
|
123
|
+
>>> initial_conditions = InitialConditions(
|
|
124
|
+
... grid=grid,
|
|
125
|
+
... ini_time=datetime(2022, 1, 1),
|
|
126
|
+
... source={"name": "ROMS", "grid": parent_grid, "path": "restart.nc"},
|
|
127
|
+
... bgc_source={
|
|
128
|
+
... "name": "ROMS",
|
|
129
|
+
... "grid": parent_grid,
|
|
130
|
+
... "path": "restart.nc",
|
|
131
|
+
... },
|
|
132
|
+
... )
|
|
133
|
+
|
|
115
134
|
"""
|
|
116
135
|
|
|
117
136
|
grid: Grid
|
|
@@ -145,7 +164,6 @@ class InitialConditions:
|
|
|
145
164
|
|
|
146
165
|
def __post_init__(self):
|
|
147
166
|
# Initialize depth coordinates
|
|
148
|
-
self.adjust_depth_for_sea_surface_height = False
|
|
149
167
|
self.ds_depth_coords = xr.Dataset()
|
|
150
168
|
|
|
151
169
|
self._input_checks()
|
|
@@ -180,7 +198,6 @@ class InitialConditions:
|
|
|
180
198
|
target_coords = get_target_coords(self.grid)
|
|
181
199
|
|
|
182
200
|
data = self._get_data(forcing_type=type)
|
|
183
|
-
|
|
184
201
|
data.choose_subdomain(
|
|
185
202
|
target_coords,
|
|
186
203
|
)
|
|
@@ -188,6 +205,7 @@ class InitialConditions:
|
|
|
188
205
|
data.convert_to_float64()
|
|
189
206
|
data.extrapolate_deepest_to_bottom()
|
|
190
207
|
data.apply_lateral_fill()
|
|
208
|
+
data.rotate_velocities_to_east_and_north()
|
|
191
209
|
|
|
192
210
|
self._set_variable_info(data, type=type)
|
|
193
211
|
attr_name = f"variable_info_{type}"
|
|
@@ -202,6 +220,7 @@ class InitialConditions:
|
|
|
202
220
|
var: {
|
|
203
221
|
"name": data.var_names[var],
|
|
204
222
|
"location": variable_info[var]["location"],
|
|
223
|
+
"is_3d": variable_info[var]["is_3d"],
|
|
205
224
|
}
|
|
206
225
|
for var in data.var_names.keys()
|
|
207
226
|
if data.var_names[var] in data.ds.data_vars
|
|
@@ -212,27 +231,24 @@ class InitialConditions:
|
|
|
212
231
|
var: {
|
|
213
232
|
"name": data.opt_var_names[var],
|
|
214
233
|
"location": variable_info[var]["location"],
|
|
234
|
+
"is_3d": variable_info[var]["is_3d"],
|
|
215
235
|
}
|
|
216
236
|
for var in data.opt_var_names.keys()
|
|
217
237
|
if data.opt_var_names[var] in data.ds.data_vars
|
|
218
238
|
}
|
|
219
239
|
)
|
|
220
240
|
|
|
221
|
-
#
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
data.ds[var_names[var_name]["name"]]
|
|
227
|
-
)
|
|
228
|
-
|
|
229
|
-
# rotation of velocities and interpolation to u/v points
|
|
241
|
+
# Lateral regridding
|
|
242
|
+
processed_fields = self._regrid_laterally(
|
|
243
|
+
data, target_coords, processed_fields, var_names
|
|
244
|
+
)
|
|
245
|
+
# Rotation of velocities and interpolation to u/v points
|
|
230
246
|
if "u" in var_names and "v" in var_names:
|
|
231
247
|
processed_fields["u"], processed_fields["v"] = rotate_velocities(
|
|
232
248
|
processed_fields["u"],
|
|
233
249
|
processed_fields["v"],
|
|
234
250
|
target_coords["angle"],
|
|
235
|
-
|
|
251
|
+
interpolate_after=True,
|
|
236
252
|
)
|
|
237
253
|
|
|
238
254
|
if type == "bgc":
|
|
@@ -246,32 +262,11 @@ class InitialConditions:
|
|
|
246
262
|
zeta = (
|
|
247
263
|
processed_fields["zeta"] if self.adjust_depth_for_sea_surface_height else 0
|
|
248
264
|
)
|
|
249
|
-
|
|
250
265
|
for location in ["rho", "u", "v"]:
|
|
251
|
-
|
|
252
|
-
filtered_vars = [
|
|
253
|
-
var_name
|
|
254
|
-
for var_name, info in var_names.items()
|
|
255
|
-
if info["location"] == location and variable_info[var_name]["is_3d"]
|
|
256
|
-
]
|
|
266
|
+
self._get_depth_coordinates(zeta, location, "layer")
|
|
257
267
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
self._get_depth_coordinates(zeta, location, "layer")
|
|
261
|
-
|
|
262
|
-
# Vertical regridding
|
|
263
|
-
vertical_regrid = VerticalRegridToROMS(
|
|
264
|
-
self.ds_depth_coords[f"layer_depth_{location}"],
|
|
265
|
-
data.ds[data.dim_names["depth"]],
|
|
266
|
-
)
|
|
267
|
-
for var_name in filtered_vars:
|
|
268
|
-
if var_name in processed_fields:
|
|
269
|
-
field = processed_fields[var_name]
|
|
270
|
-
if self.use_dask:
|
|
271
|
-
field = field.chunk(
|
|
272
|
-
_set_dask_chunks(location, self.horizontal_chunk_size)
|
|
273
|
-
)
|
|
274
|
-
processed_fields[var_name] = vertical_regrid.apply(field)
|
|
268
|
+
# Vertical regridding
|
|
269
|
+
processed_fields = self._regrid_vertically(data, processed_fields, var_names)
|
|
275
270
|
|
|
276
271
|
# Compute barotropic velocities
|
|
277
272
|
if "u" in var_names and "v" in var_names:
|
|
@@ -284,13 +279,157 @@ class InitialConditions:
|
|
|
284
279
|
|
|
285
280
|
return processed_fields
|
|
286
281
|
|
|
287
|
-
def
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
282
|
+
def _regrid_laterally(
|
|
283
|
+
self,
|
|
284
|
+
data: ROMSDataset | LatLonDataset,
|
|
285
|
+
target_coords: dict[str, xr.DataArray],
|
|
286
|
+
processed_fields: dict[str, xr.DataArray],
|
|
287
|
+
var_names: dict[str, dict[str, str]],
|
|
288
|
+
):
|
|
289
|
+
"""Regrid variables in data.ds laterally to target coordinates.
|
|
290
|
+
|
|
291
|
+
Parameters
|
|
292
|
+
----------
|
|
293
|
+
data : ROMSDataset or LatLonDataset
|
|
294
|
+
The dataset containing variables to regrid.
|
|
295
|
+
target_coords : dict[str, xr.DataArray]
|
|
296
|
+
Dictionary of target coordinates for regridding.
|
|
297
|
+
processed_fields : dict[str, xr.DataArray]
|
|
298
|
+
Dictionary where regridded variables will be stored.
|
|
299
|
+
var_names : dict[str, dict[str, str]]
|
|
300
|
+
Mapping from variable keys to dataset variable names and metadata.
|
|
301
|
+
|
|
302
|
+
Returns
|
|
303
|
+
-------
|
|
304
|
+
processed_fields : dict[str, xr.DataArray]
|
|
305
|
+
Updated dictionary with regridded variables.
|
|
306
|
+
"""
|
|
307
|
+
if isinstance(data, ROMSDataset):
|
|
308
|
+
# Compute depth coordinates on source data for rho
|
|
309
|
+
data._get_depth_coordinates(depth_type="layer", locations=["rho"])
|
|
310
|
+
# Subset depth coordinate to target subdomain
|
|
311
|
+
data.ds_depth_coords = choose_subdomain(
|
|
312
|
+
data.ds_depth_coords,
|
|
313
|
+
data.grid.ds,
|
|
314
|
+
target_coords,
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
# Regrid all rho variables
|
|
318
|
+
ds_rho = data.ds[[var_names[var]["name"] for var in var_names]].rename(
|
|
319
|
+
{"lat_rho": "lat", "lon_rho": "lon"}
|
|
320
|
+
)
|
|
321
|
+
lateral_regrid_from_roms = LateralRegridFromROMS(ds_rho, target_coords)
|
|
322
|
+
ds_rho = lateral_regrid_from_roms.apply(ds_rho)
|
|
323
|
+
|
|
324
|
+
for var_name in var_names:
|
|
325
|
+
processed_fields[var_name] = ds_rho[var_name]
|
|
326
|
+
|
|
327
|
+
# Regrid depth coordinates
|
|
328
|
+
processed_fields["layer_depth_rho"] = lateral_regrid_from_roms.apply(
|
|
329
|
+
data.ds_depth_coords["layer_depth_rho"]
|
|
292
330
|
)
|
|
293
331
|
|
|
332
|
+
else:
|
|
333
|
+
lateral_regrid_to_roms = LateralRegridToROMS(target_coords, data.dim_names)
|
|
334
|
+
for var_name in var_names:
|
|
335
|
+
processed_fields[var_name] = lateral_regrid_to_roms.apply(
|
|
336
|
+
data.ds[var_names[var_name]["name"]]
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
return processed_fields
|
|
340
|
+
|
|
341
|
+
def _regrid_vertically(
|
|
342
|
+
self,
|
|
343
|
+
data: ROMSDataset | LatLonDataset,
|
|
344
|
+
processed_fields: dict[str, xr.DataArray],
|
|
345
|
+
var_names: dict[str, dict[str, str | bool]],
|
|
346
|
+
) -> dict[str, xr.DataArray]:
|
|
347
|
+
"""
|
|
348
|
+
Perform vertical regridding of 3D variables to the model's vertical grid.
|
|
349
|
+
|
|
350
|
+
For each vertical location ('rho', 'u', 'v'), this method regrids variables
|
|
351
|
+
that are flagged as 3D in `var_names`. The regridding procedure differs
|
|
352
|
+
depending on whether the source dataset is a ROMSDataset or a LatLonDataset.
|
|
353
|
+
|
|
354
|
+
Parameters
|
|
355
|
+
----------
|
|
356
|
+
data : ROMSDataset or LatLonDataset
|
|
357
|
+
Dataset containing the variables to regrid.
|
|
358
|
+
processed_fields : dict[str, xarray.DataArray]
|
|
359
|
+
Dictionary containing fields that have already been regridded laterally.
|
|
360
|
+
This method updates the entries in-place with vertically regridded fields.
|
|
361
|
+
var_names : dict[str, dict[str, str | bool]]
|
|
362
|
+
Mapping of variable keys to dataset variable metadata:
|
|
363
|
+
- 'name': dataset variable name
|
|
364
|
+
- 'location': vertical location ('rho', 'u', 'v')
|
|
365
|
+
- 'is_3d': whether the variable is 3D and requires vertical regridding
|
|
366
|
+
|
|
367
|
+
Returns
|
|
368
|
+
-------
|
|
369
|
+
processed_fields : dict[str, xarray.DataArray]
|
|
370
|
+
Dictionary containing the same variables as `processed_fields`, now updated
|
|
371
|
+
with vertically regridded values.
|
|
372
|
+
"""
|
|
373
|
+
for location in ["rho", "u", "v"]:
|
|
374
|
+
# Select variables for this vertical location that are 3D
|
|
375
|
+
filtered_vars = [
|
|
376
|
+
var_name
|
|
377
|
+
for var_name, info in var_names.items()
|
|
378
|
+
if info["location"] == location and info["is_3d"]
|
|
379
|
+
]
|
|
380
|
+
|
|
381
|
+
if not filtered_vars:
|
|
382
|
+
continue
|
|
383
|
+
|
|
384
|
+
if isinstance(data, ROMSDataset):
|
|
385
|
+
# Interpolate depth coordinates from rho to u/v points if needed
|
|
386
|
+
if location == "u":
|
|
387
|
+
processed_fields["layer_depth_u"] = interpolate_from_rho_to_u(
|
|
388
|
+
processed_fields["layer_depth_rho"]
|
|
389
|
+
)
|
|
390
|
+
elif location == "v":
|
|
391
|
+
processed_fields["layer_depth_v"] = interpolate_from_rho_to_v(
|
|
392
|
+
processed_fields["layer_depth_rho"]
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
# Use the first variable to initialize VerticalRegrid
|
|
396
|
+
ds_tmp = xr.Dataset(
|
|
397
|
+
{filtered_vars[0]: processed_fields[filtered_vars[0]]}
|
|
398
|
+
)
|
|
399
|
+
vertical_regrid = VerticalRegrid(ds_tmp)
|
|
400
|
+
|
|
401
|
+
for var_name in filtered_vars:
|
|
402
|
+
if var_name in processed_fields:
|
|
403
|
+
processed_fields[var_name] = vertical_regrid.apply(
|
|
404
|
+
processed_fields[var_name],
|
|
405
|
+
source_depth_coords=processed_fields[
|
|
406
|
+
f"layer_depth_{location}"
|
|
407
|
+
],
|
|
408
|
+
target_depth_coords=self.ds_depth_coords[
|
|
409
|
+
f"layer_depth_{location}"
|
|
410
|
+
],
|
|
411
|
+
mask_edges=False,
|
|
412
|
+
)
|
|
413
|
+
else:
|
|
414
|
+
# LatLonDataset: create a regrid object for all variables
|
|
415
|
+
vertical_regrid_to_roms = VerticalRegridToROMS(
|
|
416
|
+
self.ds_depth_coords[f"layer_depth_{location}"],
|
|
417
|
+
data.ds[data.dim_names["depth"]],
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
for var_name in filtered_vars:
|
|
421
|
+
if var_name not in processed_fields:
|
|
422
|
+
continue
|
|
423
|
+
field = processed_fields[var_name]
|
|
424
|
+
if getattr(self, "use_dask", False):
|
|
425
|
+
field = field.chunk(
|
|
426
|
+
_set_dask_chunks(location, self.horizontal_chunk_size)
|
|
427
|
+
)
|
|
428
|
+
processed_fields[var_name] = vertical_regrid_to_roms.apply(field)
|
|
429
|
+
|
|
430
|
+
return processed_fields
|
|
431
|
+
|
|
432
|
+
def _input_checks(self):
|
|
294
433
|
if "name" not in self.source.keys():
|
|
295
434
|
raise ValueError("`source` must include a 'name'.")
|
|
296
435
|
if "path" not in self.source.keys():
|
|
@@ -318,8 +457,14 @@ class InitialConditions:
|
|
|
318
457
|
**self.bgc_source,
|
|
319
458
|
"climatology": self.bgc_source.get("climatology", False),
|
|
320
459
|
}
|
|
460
|
+
if not isinstance(self.ini_time, datetime):
|
|
461
|
+
raise TypeError(
|
|
462
|
+
f"`ini_time` must be a datetime object, got {type(self.ini_time).__name__} instead."
|
|
463
|
+
)
|
|
321
464
|
|
|
322
|
-
def _get_data(
|
|
465
|
+
def _get_data(
|
|
466
|
+
self, forcing_type=Literal["physics", "bgc"]
|
|
467
|
+
) -> LatLonDataset | ROMSDataset:
|
|
323
468
|
"""Determine the correct `Dataset` type and return an instance.
|
|
324
469
|
|
|
325
470
|
forcing_type : str
|
|
@@ -330,18 +475,22 @@ class InitialConditions:
|
|
|
330
475
|
Returns
|
|
331
476
|
-------
|
|
332
477
|
Dataset
|
|
333
|
-
The `
|
|
478
|
+
The `LatLonDataset` or `ROMSDataset` instance
|
|
334
479
|
"""
|
|
335
|
-
dataset_map: dict[
|
|
480
|
+
dataset_map: dict[
|
|
481
|
+
str, dict[str, dict[str, type[LatLonDataset | ROMSDataset]]]
|
|
482
|
+
] = {
|
|
336
483
|
"physics": {
|
|
337
484
|
"GLORYS": {
|
|
338
485
|
"external": GLORYSDataset,
|
|
339
486
|
"default": GLORYSDefaultDataset,
|
|
340
487
|
},
|
|
488
|
+
"ROMS": defaultdict(lambda: ROMSDataset),
|
|
341
489
|
},
|
|
342
490
|
"bgc": {
|
|
343
491
|
"CESM_REGRIDDED": defaultdict(lambda: CESMBGCDataset),
|
|
344
492
|
"UNIFIED": defaultdict(lambda: UnifiedBGCDataset),
|
|
493
|
+
"ROMS": defaultdict(lambda: ROMSDataset),
|
|
345
494
|
},
|
|
346
495
|
}
|
|
347
496
|
|
|
@@ -369,13 +518,31 @@ class InitialConditions:
|
|
|
369
518
|
if isinstance(source_dict["path"], bool):
|
|
370
519
|
raise ValueError('source["path"] cannot be a boolean here')
|
|
371
520
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
521
|
+
if source_dict["name"] == "ROMS":
|
|
522
|
+
var_names = _set_required_vars(forcing_type)
|
|
523
|
+
self.adjust_depth_for_sea_surface_height = True
|
|
524
|
+
|
|
525
|
+
data = data_type(
|
|
526
|
+
path=source_dict["path"], # type: ignore
|
|
527
|
+
grid=source_dict["grid"], # type: ignore
|
|
528
|
+
var_names=var_names,
|
|
529
|
+
start_time=self.ini_time,
|
|
530
|
+
allow_flex_time=self.allow_flex_time,
|
|
531
|
+
adjust_depth_for_sea_surface_height=True,
|
|
532
|
+
use_dask=self.use_dask,
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
else:
|
|
536
|
+
self.adjust_depth_for_sea_surface_height = False
|
|
537
|
+
data = data_type(
|
|
538
|
+
filename=source_dict["path"], # type: ignore
|
|
539
|
+
start_time=self.ini_time,
|
|
540
|
+
climatology=source_dict["climatology"], # type: ignore
|
|
541
|
+
allow_flex_time=self.allow_flex_time,
|
|
542
|
+
use_dask=self.use_dask,
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
return data
|
|
379
546
|
|
|
380
547
|
def _set_variable_info(self, data, type="physics"):
|
|
381
548
|
"""Sets up a dictionary with metadata for variables based on the type.
|
|
@@ -466,7 +633,16 @@ class InitialConditions:
|
|
|
466
633
|
if var_name == "ALK":
|
|
467
634
|
variable_info[var_name] = {**default_info, "validate": True}
|
|
468
635
|
else:
|
|
469
|
-
|
|
636
|
+
if var_name == "zeta":
|
|
637
|
+
variable_info[var_name] = {
|
|
638
|
+
"location": "rho",
|
|
639
|
+
"is_vector": False,
|
|
640
|
+
"vector_pair": None,
|
|
641
|
+
"is_3d": False,
|
|
642
|
+
"validate": False,
|
|
643
|
+
}
|
|
644
|
+
else:
|
|
645
|
+
variable_info[var_name] = {**default_info, "validate": False}
|
|
470
646
|
|
|
471
647
|
object.__setattr__(self, f"variable_info_{type}", variable_info)
|
|
472
648
|
|
|
@@ -579,7 +755,7 @@ class InitialConditions:
|
|
|
579
755
|
model_reference_date = np.datetime64(self.model_reference_date)
|
|
580
756
|
|
|
581
757
|
# Convert the time coordinate to the format expected by ROMS (seconds since model reference date)
|
|
582
|
-
ocean_time = (ds["time"] - model_reference_date).
|
|
758
|
+
ocean_time = (ds["time"] - model_reference_date).dt.total_seconds()
|
|
583
759
|
ds = ds.assign_coords(ocean_time=("time", ocean_time.data.astype("float64")))
|
|
584
760
|
ds["ocean_time"].attrs["long_name"] = (
|
|
585
761
|
f"relative time: seconds since {self.model_reference_date!s}"
|
|
@@ -853,6 +1029,14 @@ class InitialConditions:
|
|
|
853
1029
|
|
|
854
1030
|
grid = Grid.from_yaml(filepath)
|
|
855
1031
|
initial_conditions_params = from_yaml(cls, filepath)
|
|
1032
|
+
|
|
1033
|
+
# Deserialize nested grids inside 'source' and 'bgc_source'
|
|
1034
|
+
for name in ["source", "bgc_source"]:
|
|
1035
|
+
src_dict = initial_conditions_params.get(name)
|
|
1036
|
+
if src_dict and "grid" in src_dict and src_dict["grid"] is not None:
|
|
1037
|
+
grid_data = pop_grid_data(src_dict["grid"])
|
|
1038
|
+
src_dict["grid"] = Grid(**grid_data)
|
|
1039
|
+
|
|
856
1040
|
return cls(
|
|
857
1041
|
grid=grid,
|
|
858
1042
|
**initial_conditions_params,
|
|
@@ -881,3 +1065,78 @@ def _set_dask_chunks(location: str, chunk_size: int):
|
|
|
881
1065
|
"v": {"eta_v": chunk_size, "xi_rho": chunk_size},
|
|
882
1066
|
}
|
|
883
1067
|
return chunk_mapping.get(location, {})
|
|
1068
|
+
|
|
1069
|
+
|
|
1070
|
+
def _set_required_vars(var_type: str = "physics") -> dict[str, str]:
|
|
1071
|
+
"""
|
|
1072
|
+
Return the canonical variable-name mapping for a ROMS dataset.
|
|
1073
|
+
|
|
1074
|
+
Parameters
|
|
1075
|
+
----------
|
|
1076
|
+
var_type : str, optional
|
|
1077
|
+
Category of variables. Supported values:
|
|
1078
|
+
- "physics": physical variables (temperature, salinity, currents, etc.)
|
|
1079
|
+
- "bgc": biogeochemical variables (nutrients, pigments, carbon, etc.)
|
|
1080
|
+
Default is "physics".
|
|
1081
|
+
|
|
1082
|
+
Returns
|
|
1083
|
+
-------
|
|
1084
|
+
dict[str, str]
|
|
1085
|
+
Mapping from logical variable names to dataset variable names.
|
|
1086
|
+
|
|
1087
|
+
Raises
|
|
1088
|
+
------
|
|
1089
|
+
ValueError
|
|
1090
|
+
If an unsupported `var_type` is provided.
|
|
1091
|
+
"""
|
|
1092
|
+
var_mappings = {
|
|
1093
|
+
"physics": {
|
|
1094
|
+
"zeta": "zeta",
|
|
1095
|
+
"temp": "temp",
|
|
1096
|
+
"salt": "salt",
|
|
1097
|
+
"u": "u",
|
|
1098
|
+
"v": "v",
|
|
1099
|
+
},
|
|
1100
|
+
"bgc": {
|
|
1101
|
+
"zeta": "zeta", # to infer vertical coordinate
|
|
1102
|
+
"PO4": "PO4",
|
|
1103
|
+
"NO3": "NO3",
|
|
1104
|
+
"SiO3": "SiO3",
|
|
1105
|
+
"NH4": "NH4",
|
|
1106
|
+
"Fe": "Fe",
|
|
1107
|
+
"Lig": "Lig",
|
|
1108
|
+
"O2": "O2",
|
|
1109
|
+
"DIC": "DIC",
|
|
1110
|
+
"DIC_ALT_CO2": "DIC_ALT_CO2",
|
|
1111
|
+
"ALK": "ALK",
|
|
1112
|
+
"ALK_ALT_CO2": "ALK_ALT_CO2",
|
|
1113
|
+
"DOC": "DOC",
|
|
1114
|
+
"DON": "DON",
|
|
1115
|
+
"DOP": "DOP",
|
|
1116
|
+
"DOPr": "DOPr",
|
|
1117
|
+
"DONr": "DONr",
|
|
1118
|
+
"DOCr": "DOCr",
|
|
1119
|
+
"spChl": "spChl",
|
|
1120
|
+
"spC": "spC",
|
|
1121
|
+
"spP": "spP",
|
|
1122
|
+
"spFe": "spFe",
|
|
1123
|
+
"diatChl": "diatChl",
|
|
1124
|
+
"diatC": "diatC",
|
|
1125
|
+
"diatP": "diatP",
|
|
1126
|
+
"diatFe": "diatFe",
|
|
1127
|
+
"diatSi": "diatSi",
|
|
1128
|
+
"diazChl": "diazChl",
|
|
1129
|
+
"diazC": "diazC",
|
|
1130
|
+
"diazP": "diazP",
|
|
1131
|
+
"diazFe": "diazFe",
|
|
1132
|
+
"spCaCO3": "spCaCO3",
|
|
1133
|
+
"zooC": "zooC",
|
|
1134
|
+
},
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
if var_type not in var_mappings:
|
|
1138
|
+
raise ValueError(
|
|
1139
|
+
f"Unsupported var_type '{var_type}'. Choose from {list(var_mappings.keys())}."
|
|
1140
|
+
)
|
|
1141
|
+
|
|
1142
|
+
return var_mappings[var_type]
|
roms_tools/setup/mask.py
CHANGED
|
@@ -8,11 +8,8 @@ import regionmask
|
|
|
8
8
|
import xarray as xr
|
|
9
9
|
from scipy.ndimage import label
|
|
10
10
|
|
|
11
|
-
from roms_tools.setup.utils import
|
|
12
|
-
|
|
13
|
-
interpolate_from_rho_to_u,
|
|
14
|
-
interpolate_from_rho_to_v,
|
|
15
|
-
)
|
|
11
|
+
from roms_tools.setup.utils import handle_boundaries
|
|
12
|
+
from roms_tools.utils import interpolate_from_rho_to_u, interpolate_from_rho_to_v
|
|
16
13
|
|
|
17
14
|
|
|
18
15
|
def add_mask(ds: xr.Dataset, shapefile: str | Path | None = None) -> xr.Dataset:
|
roms_tools/setup/nesting.py
CHANGED
|
@@ -16,13 +16,16 @@ from roms_tools.setup.utils import (
|
|
|
16
16
|
check_and_set_boundaries,
|
|
17
17
|
from_yaml,
|
|
18
18
|
get_boundary_coords,
|
|
19
|
-
interpolate_from_rho_to_u,
|
|
20
|
-
interpolate_from_rho_to_v,
|
|
21
19
|
pop_grid_data,
|
|
22
20
|
to_dict,
|
|
23
21
|
write_to_yaml,
|
|
24
22
|
)
|
|
25
|
-
from roms_tools.utils import
|
|
23
|
+
from roms_tools.utils import (
|
|
24
|
+
interpolate_from_rho_to_u,
|
|
25
|
+
interpolate_from_rho_to_v,
|
|
26
|
+
save_datasets,
|
|
27
|
+
wrap_longitudes,
|
|
28
|
+
)
|
|
26
29
|
|
|
27
30
|
|
|
28
31
|
@dataclass(kw_only=True)
|
|
@@ -28,13 +28,13 @@ from roms_tools.setup.utils import (
|
|
|
28
28
|
group_dataset,
|
|
29
29
|
min_dist_to_land,
|
|
30
30
|
nan_check,
|
|
31
|
-
rotate_velocities,
|
|
32
31
|
substitute_nans_by_fillvalue,
|
|
33
32
|
to_dict,
|
|
34
33
|
write_to_yaml,
|
|
35
34
|
)
|
|
36
35
|
from roms_tools.utils import (
|
|
37
36
|
interpolate_from_climatology,
|
|
37
|
+
rotate_velocities,
|
|
38
38
|
save_datasets,
|
|
39
39
|
transpose_dimensions,
|
|
40
40
|
)
|
|
@@ -201,7 +201,6 @@ class SurfaceForcing:
|
|
|
201
201
|
processed_fields["uwnd"],
|
|
202
202
|
processed_fields["vwnd"],
|
|
203
203
|
target_coords["angle"],
|
|
204
|
-
interpolate=False,
|
|
205
204
|
)
|
|
206
205
|
|
|
207
206
|
if self.type == "physics":
|
roms_tools/setup/tides.py
CHANGED
|
@@ -15,15 +15,17 @@ from roms_tools.setup.utils import (
|
|
|
15
15
|
get_target_coords,
|
|
16
16
|
get_variable_metadata,
|
|
17
17
|
get_vector_pairs,
|
|
18
|
-
interpolate_from_rho_to_u,
|
|
19
|
-
interpolate_from_rho_to_v,
|
|
20
18
|
nan_check,
|
|
21
|
-
rotate_velocities,
|
|
22
19
|
substitute_nans_by_fillvalue,
|
|
23
20
|
to_dict,
|
|
24
21
|
write_to_yaml,
|
|
25
22
|
)
|
|
26
|
-
from roms_tools.utils import
|
|
23
|
+
from roms_tools.utils import (
|
|
24
|
+
interpolate_from_rho_to_u,
|
|
25
|
+
interpolate_from_rho_to_v,
|
|
26
|
+
rotate_velocities,
|
|
27
|
+
save_datasets,
|
|
28
|
+
)
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
@dataclass(kw_only=True)
|
|
@@ -141,7 +143,6 @@ class TidalForcing:
|
|
|
141
143
|
processed_fields[u_component],
|
|
142
144
|
processed_fields[v_component],
|
|
143
145
|
target_coords["angle"],
|
|
144
|
-
interpolate=False,
|
|
145
146
|
)
|
|
146
147
|
|
|
147
148
|
# convert to barotropic velocity
|