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.
- ci/environment.yml +1 -0
- roms_tools/__init__.py +1 -0
- roms_tools/analysis/roms_output.py +10 -6
- roms_tools/setup/boundary_forcing.py +178 -193
- roms_tools/setup/datasets.py +58 -1
- roms_tools/setup/grid.py +31 -97
- roms_tools/setup/initial_conditions.py +172 -126
- roms_tools/setup/nesting.py +2 -23
- roms_tools/setup/river_forcing.py +34 -67
- roms_tools/setup/surface_forcing.py +111 -61
- roms_tools/setup/tides.py +7 -30
- roms_tools/setup/utils.py +24 -70
- roms_tools/tests/test_setup/test_boundary_forcing.py +220 -57
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/.zattrs +5 -3
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/.zmetadata +156 -121
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/abs_time/.zarray +2 -2
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/abs_time/.zattrs +2 -1
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/abs_time/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/bry_time/.zarray +2 -2
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/bry_time/.zattrs +1 -1
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/bry_time/0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_east/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_north/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_south/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_west/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/salt_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_east/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_north/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_south/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_west/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/temp_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_east/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_north/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_south/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_west/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/u_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_east/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_east/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_north/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_north/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_south/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_south/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_west/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/ubar_west/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_east/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_east/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_north/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_north/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_south/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_south/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_west/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/v_west/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_east/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_east/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_north/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_north/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_south/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_south/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_west/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/vbar_west/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/.zattrs +8 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_east/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_north/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_north/.zattrs +8 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_north/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/.zattrs +8 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_south/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_west/.zarray +4 -4
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_west/.zattrs +8 -0
- roms_tools/tests/test_setup/test_data/boundary_forcing.zarr/zeta_west/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zattrs +4 -4
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/.zmetadata +4 -4
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/angle/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/angle_coarse/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/f/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/h/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_coarse/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_rho/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_u/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lat_v/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_coarse/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_rho/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_u/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/lon_v/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_coarse/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_rho/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_u/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/mask_v/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/pm/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/grid_that_straddles_dateline.zarr/pn/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/.zattrs +2 -1
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/.zmetadata +6 -4
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/Cs_r/.zattrs +1 -1
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/Cs_w/.zattrs +1 -1
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/NH4/0.0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/NO3/0.0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/PO4/0.0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/abs_time/.zattrs +1 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/diatSi/0.0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ocean_time/.zattrs +1 -1
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spC/0.0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spCaCO3/0.0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/spFe/0.0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/temp/0.0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/u/0.0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/ubar/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/v/0.0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/vbar/0.0.0 +0 -0
- roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/.zmetadata +30 -0
- roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_location/.zarray +22 -0
- roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_location/.zattrs +8 -0
- roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_location/0.0 +0 -0
- roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/.zmetadata +30 -0
- roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_location/.zarray +22 -0
- roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_location/.zattrs +8 -0
- roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_location/0.0 +0 -0
- roms_tools/tests/test_setup/test_grid.py +0 -13
- roms_tools/tests/test_setup/test_initial_conditions.py +204 -66
- roms_tools/tests/test_setup/test_nesting.py +0 -16
- roms_tools/tests/test_setup/test_river_forcing.py +8 -36
- roms_tools/tests/test_setup/test_surface_forcing.py +102 -73
- roms_tools/tests/test_setup/test_tides.py +4 -16
- roms_tools/tests/test_setup/test_utils.py +1 -0
- roms_tools/tests/{test_utils.py → test_tiling/test_partition.py} +1 -1
- roms_tools/tiling/partition.py +338 -0
- roms_tools/utils.py +66 -333
- roms_tools/vertical_coordinate.py +54 -133
- {roms_tools-2.3.0.dist-info → roms_tools-2.4.0.dist-info}/METADATA +1 -1
- {roms_tools-2.3.0.dist-info → roms_tools-2.4.0.dist-info}/RECORD +143 -136
- {roms_tools-2.3.0.dist-info → roms_tools-2.4.0.dist-info}/LICENSE +0 -0
- {roms_tools-2.3.0.dist-info → roms_tools-2.4.0.dist-info}/WHEEL +0 -0
- {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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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.
|
|
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]
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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.
|
|
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
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
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
|
-
|
|
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["
|
|
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.
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
813
|
-
A
|
|
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,
|
|
879
|
+
dataset_list, output_filenames, use_dask=self.use_dask
|
|
834
880
|
)
|
|
835
881
|
|
|
836
882
|
return saved_filenames
|
roms_tools/setup/nesting.py
CHANGED
|
@@ -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
|
|