roms-tools 2.2.1__py3-none-any.whl → 2.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ci/environment.yml +1 -0
- roms_tools/__init__.py +2 -0
- roms_tools/analysis/roms_output.py +590 -0
- roms_tools/{setup/download.py → download.py} +3 -0
- roms_tools/{setup/plot.py → plot.py} +34 -28
- roms_tools/setup/boundary_forcing.py +199 -203
- roms_tools/setup/datasets.py +60 -136
- roms_tools/setup/grid.py +40 -67
- roms_tools/setup/initial_conditions.py +249 -247
- roms_tools/setup/nesting.py +6 -27
- roms_tools/setup/river_forcing.py +41 -76
- roms_tools/setup/surface_forcing.py +125 -75
- roms_tools/setup/tides.py +31 -51
- roms_tools/setup/topography.py +1 -1
- roms_tools/setup/utils.py +44 -224
- roms_tools/tests/test_analysis/test_roms_output.py +269 -0
- roms_tools/tests/{test_setup/test_regrid.py → test_regrid.py} +1 -1
- roms_tools/tests/test_setup/test_boundary_forcing.py +221 -58
- 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_datasets.py +1 -1
- roms_tools/tests/test_setup/test_grid.py +1 -14
- roms_tools/tests/test_setup/test_initial_conditions.py +205 -67
- roms_tools/tests/test_setup/test_nesting.py +0 -16
- roms_tools/tests/test_setup/test_river_forcing.py +9 -37
- roms_tools/tests/test_setup/test_surface_forcing.py +103 -74
- roms_tools/tests/test_setup/test_tides.py +5 -17
- roms_tools/tests/test_setup/test_topography.py +1 -1
- roms_tools/tests/test_setup/test_utils.py +57 -1
- roms_tools/tests/{test_utils.py → test_tiling/test_partition.py} +1 -1
- roms_tools/tiling/partition.py +338 -0
- roms_tools/utils.py +310 -276
- roms_tools/vertical_coordinate.py +227 -0
- {roms_tools-2.2.1.dist-info → roms_tools-2.4.0.dist-info}/METADATA +1 -1
- {roms_tools-2.2.1.dist-info → roms_tools-2.4.0.dist-info}/RECORD +151 -142
- roms_tools/setup/vertical_coordinate.py +0 -109
- /roms_tools/{setup/regrid.py → regrid.py} +0 -0
- {roms_tools-2.2.1.dist-info → roms_tools-2.4.0.dist-info}/LICENSE +0 -0
- {roms_tools-2.2.1.dist-info → roms_tools-2.4.0.dist-info}/WHEEL +0 -0
- {roms_tools-2.2.1.dist-info → roms_tools-2.4.0.dist-info}/top_level.txt +0 -0
|
@@ -5,32 +5,34 @@ import logging
|
|
|
5
5
|
import importlib.metadata
|
|
6
6
|
from typing import Dict, Union, List
|
|
7
7
|
from dataclasses import dataclass, field
|
|
8
|
-
from roms_tools.setup.grid import Grid
|
|
9
|
-
from roms_tools.setup.regrid import LateralRegrid, VerticalRegrid
|
|
10
8
|
from datetime import datetime
|
|
9
|
+
import matplotlib.pyplot as plt
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from roms_tools import Grid
|
|
12
|
+
from roms_tools.regrid import LateralRegrid, VerticalRegrid
|
|
13
|
+
from roms_tools.utils import save_datasets
|
|
14
|
+
from roms_tools.vertical_coordinate import compute_depth
|
|
15
|
+
from roms_tools.plot import _section_plot, _line_plot
|
|
16
|
+
from roms_tools.utils import (
|
|
17
|
+
interpolate_from_rho_to_u,
|
|
18
|
+
interpolate_from_rho_to_v,
|
|
19
|
+
transpose_dimensions,
|
|
20
|
+
)
|
|
11
21
|
from roms_tools.setup.datasets import GLORYSDataset, CESMBGCDataset
|
|
12
|
-
from roms_tools.setup.vertical_coordinate import compute_depth
|
|
13
22
|
from roms_tools.setup.utils import (
|
|
14
23
|
get_variable_metadata,
|
|
15
24
|
group_dataset,
|
|
16
|
-
save_datasets,
|
|
17
25
|
get_target_coords,
|
|
18
26
|
rotate_velocities,
|
|
19
27
|
compute_barotropic_velocity,
|
|
20
|
-
transpose_dimensions,
|
|
21
28
|
one_dim_fill,
|
|
22
29
|
nan_check,
|
|
23
30
|
substitute_nans_by_fillvalue,
|
|
24
|
-
interpolate_from_rho_to_u,
|
|
25
|
-
interpolate_from_rho_to_v,
|
|
26
31
|
convert_to_roms_time,
|
|
27
32
|
get_boundary_coords,
|
|
28
33
|
_to_yaml,
|
|
29
34
|
_from_yaml,
|
|
30
35
|
)
|
|
31
|
-
from roms_tools.setup.plot import _section_plot, _line_plot
|
|
32
|
-
import matplotlib.pyplot as plt
|
|
33
|
-
from pathlib import Path
|
|
34
36
|
|
|
35
37
|
|
|
36
38
|
@dataclass(frozen=True, kw_only=True)
|
|
@@ -64,10 +66,14 @@ class BoundaryForcing:
|
|
|
64
66
|
- "physics": for physical atmospheric forcing.
|
|
65
67
|
- "bgc": for biogeochemical forcing.
|
|
66
68
|
|
|
67
|
-
apply_2d_horizontal_fill: bool, optional
|
|
69
|
+
apply_2d_horizontal_fill : bool, optional
|
|
68
70
|
Indicates whether to perform a two-dimensional horizontal fill on the source data prior to regridding to boundaries.
|
|
69
71
|
If `False`, a one-dimensional horizontal fill is performed separately on each of the four regridded boundaries.
|
|
70
72
|
Defaults to `False`.
|
|
73
|
+
adjust_depth_for_sea_surface_height : bool, optional
|
|
74
|
+
Whether to account for sea surface height (`zeta`) variations when computing depth coordinates.
|
|
75
|
+
This adjustment is only applicable for `type="physics"`, as for biogeochemical fields usually `zeta` is not available.
|
|
76
|
+
Defaults to `False`.
|
|
71
77
|
model_reference_date : datetime, optional
|
|
72
78
|
Reference date for the model. Default is January 1, 2000.
|
|
73
79
|
use_dask: bool, optional
|
|
@@ -103,6 +109,7 @@ class BoundaryForcing:
|
|
|
103
109
|
source: Dict[str, Union[str, Path, List[Union[str, Path]]]]
|
|
104
110
|
type: str = "physics"
|
|
105
111
|
apply_2d_horizontal_fill: bool = False
|
|
112
|
+
adjust_depth_for_sea_surface_height: bool = False
|
|
106
113
|
model_reference_date: datetime = datetime(2000, 1, 1)
|
|
107
114
|
use_dask: bool = False
|
|
108
115
|
bypass_validation: bool = False
|
|
@@ -112,6 +119,9 @@ class BoundaryForcing:
|
|
|
112
119
|
def __post_init__(self):
|
|
113
120
|
|
|
114
121
|
self._input_checks()
|
|
122
|
+
# Dataset for depth coordinates
|
|
123
|
+
object.__setattr__(self, "ds_depth_coords", xr.Dataset())
|
|
124
|
+
|
|
115
125
|
target_coords = get_target_coords(self.grid)
|
|
116
126
|
|
|
117
127
|
data = self._get_data()
|
|
@@ -152,13 +162,14 @@ class BoundaryForcing:
|
|
|
152
162
|
|
|
153
163
|
processed_fields = {}
|
|
154
164
|
|
|
155
|
-
#
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
165
|
+
# vector fields (velocities) are only present in physics datasets
|
|
166
|
+
if self.type == "physics":
|
|
167
|
+
# lateral regridding of vector fields
|
|
168
|
+
vector_var_names = [
|
|
169
|
+
name
|
|
170
|
+
for name, info in self.variable_info.items()
|
|
171
|
+
if info["is_vector"]
|
|
172
|
+
]
|
|
162
173
|
lon = target_coords["lon"].isel(
|
|
163
174
|
**self.bdry_coords["vector"][direction]
|
|
164
175
|
)
|
|
@@ -174,6 +185,13 @@ class BoundaryForcing:
|
|
|
174
185
|
bdry_data.ds[bdry_data.var_names[var_name]]
|
|
175
186
|
)
|
|
176
187
|
|
|
188
|
+
if self.adjust_depth_for_sea_surface_height:
|
|
189
|
+
# Regrid sea surface height ('zeta') onto a 2-cell-wide margin.
|
|
190
|
+
# This is needed to correctly infer depth coordinates at u- and v-points along the boundary.
|
|
191
|
+
zeta_vector = lateral_regrid.apply(
|
|
192
|
+
bdry_data.ds[bdry_data.var_names["zeta"]]
|
|
193
|
+
)
|
|
194
|
+
|
|
177
195
|
# lateral regridding of tracer fields
|
|
178
196
|
tracer_var_names = [
|
|
179
197
|
name
|
|
@@ -207,6 +225,9 @@ class BoundaryForcing:
|
|
|
207
225
|
angle,
|
|
208
226
|
interpolate=True,
|
|
209
227
|
)
|
|
228
|
+
if self.adjust_depth_for_sea_surface_height:
|
|
229
|
+
zeta_u = interpolate_from_rho_to_u(zeta_vector)
|
|
230
|
+
zeta_v = interpolate_from_rho_to_v(zeta_vector)
|
|
210
231
|
|
|
211
232
|
# selection of outermost margin for u/v variables
|
|
212
233
|
for var_name in self.variable_info.keys():
|
|
@@ -216,6 +237,9 @@ class BoundaryForcing:
|
|
|
216
237
|
processed_fields[var_name] = processed_fields[
|
|
217
238
|
var_name
|
|
218
239
|
].isel(**self.bdry_coords[location][direction])
|
|
240
|
+
if self.adjust_depth_for_sea_surface_height:
|
|
241
|
+
zeta_u = zeta_u.isel(**self.bdry_coords["u"][direction])
|
|
242
|
+
zeta_v = zeta_v.isel(**self.bdry_coords["v"][direction])
|
|
219
243
|
|
|
220
244
|
if not self.apply_2d_horizontal_fill:
|
|
221
245
|
self._validate_1d_fill(
|
|
@@ -223,7 +247,20 @@ class BoundaryForcing:
|
|
|
223
247
|
direction,
|
|
224
248
|
bdry_data.dim_names["depth"],
|
|
225
249
|
)
|
|
226
|
-
|
|
250
|
+
for var_name in processed_fields.keys():
|
|
251
|
+
processed_fields[var_name] = apply_1d_horizontal_fill(
|
|
252
|
+
processed_fields[var_name]
|
|
253
|
+
)
|
|
254
|
+
if self.adjust_depth_for_sea_surface_height:
|
|
255
|
+
zeta_u = apply_1d_horizontal_fill(zeta_u)
|
|
256
|
+
zeta_v = apply_1d_horizontal_fill(zeta_v)
|
|
257
|
+
|
|
258
|
+
if self.adjust_depth_for_sea_surface_height:
|
|
259
|
+
zeta = processed_fields["zeta"]
|
|
260
|
+
else:
|
|
261
|
+
zeta = 0
|
|
262
|
+
zeta_u = 0
|
|
263
|
+
zeta_v = 0
|
|
227
264
|
|
|
228
265
|
var_names_dict = {}
|
|
229
266
|
for location in ["rho", "u", "v"]:
|
|
@@ -232,24 +269,22 @@ class BoundaryForcing:
|
|
|
232
269
|
for name, info in self.variable_info.items()
|
|
233
270
|
if info["location"] == location and info["is_3d"]
|
|
234
271
|
]
|
|
272
|
+
|
|
235
273
|
# compute layer depth coordinates
|
|
274
|
+
if len(var_names_dict["rho"]) > 0:
|
|
275
|
+
self._get_depth_coordinates(zeta, direction, "rho", "layer")
|
|
276
|
+
self._get_depth_coordinates(
|
|
277
|
+
zeta, direction, "rho", "interface"
|
|
278
|
+
) # only necessary for plotting
|
|
236
279
|
if len(var_names_dict["u"]) > 0 or len(var_names_dict["v"]) > 0:
|
|
237
|
-
self.
|
|
238
|
-
|
|
239
|
-
direction=direction,
|
|
240
|
-
additional_locations=["u", "v"],
|
|
241
|
-
)
|
|
242
|
-
else:
|
|
243
|
-
if len(var_names_dict["rho"]) > 0:
|
|
244
|
-
self._get_vertical_coordinates(
|
|
245
|
-
type="layer", direction=direction, additional_locations=[]
|
|
246
|
-
)
|
|
280
|
+
self._get_depth_coordinates(zeta_u, direction, "u", "layer")
|
|
281
|
+
self._get_depth_coordinates(zeta_v, direction, "v", "layer")
|
|
247
282
|
|
|
248
283
|
# vertical regridding
|
|
249
284
|
for location in ["rho", "u", "v"]:
|
|
250
285
|
if len(var_names_dict[location]) > 0:
|
|
251
286
|
vertical_regrid = VerticalRegrid(
|
|
252
|
-
self.
|
|
287
|
+
self.ds_depth_coords[f"layer_depth_{location}_{direction}"],
|
|
253
288
|
bdry_data.ds[bdry_data.dim_names["depth"]],
|
|
254
289
|
)
|
|
255
290
|
for var_name in var_names_dict[location]:
|
|
@@ -260,17 +295,16 @@ class BoundaryForcing:
|
|
|
260
295
|
|
|
261
296
|
# compute barotropic velocities
|
|
262
297
|
if "u" in self.variable_info and "v" in self.variable_info:
|
|
263
|
-
self.
|
|
264
|
-
|
|
265
|
-
direction=direction,
|
|
266
|
-
additional_locations=["u", "v"],
|
|
267
|
-
)
|
|
298
|
+
self._get_depth_coordinates(zeta_u, direction, "u", "interface")
|
|
299
|
+
self._get_depth_coordinates(zeta_v, direction, "v", "interface")
|
|
268
300
|
for location in ["u", "v"]:
|
|
269
301
|
processed_fields[
|
|
270
302
|
f"{location}bar"
|
|
271
303
|
] = compute_barotropic_velocity(
|
|
272
304
|
processed_fields[location],
|
|
273
|
-
self.
|
|
305
|
+
self.ds_depth_coords[
|
|
306
|
+
f"interface_depth_{location}_{direction}"
|
|
307
|
+
],
|
|
274
308
|
)
|
|
275
309
|
|
|
276
310
|
# Reorder dimensions
|
|
@@ -312,6 +346,29 @@ class BoundaryForcing:
|
|
|
312
346
|
{**self.source, "climatology": self.source.get("climatology", False)},
|
|
313
347
|
)
|
|
314
348
|
|
|
349
|
+
# Ensure adjust_depth_for_sea_surface_height is only used with type="physics"
|
|
350
|
+
if self.type == "bgc" and self.adjust_depth_for_sea_surface_height:
|
|
351
|
+
logging.warning(
|
|
352
|
+
"adjust_depth_for_sea_surface_height is not applicable for BGC fields. "
|
|
353
|
+
"Setting it to False."
|
|
354
|
+
)
|
|
355
|
+
object.__setattr__(self, "adjust_depth_for_sea_surface_height", False)
|
|
356
|
+
elif self.adjust_depth_for_sea_surface_height:
|
|
357
|
+
logging.info("Sea surface height will be used to adjust depth coordinates.")
|
|
358
|
+
else:
|
|
359
|
+
logging.info(
|
|
360
|
+
"Sea surface height will NOT be used to adjust depth coordinates."
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
if self.apply_2d_horizontal_fill:
|
|
364
|
+
logging.info(
|
|
365
|
+
"Applying 2D horizontal fill to the source data before regridding."
|
|
366
|
+
)
|
|
367
|
+
else:
|
|
368
|
+
logging.info(
|
|
369
|
+
"Applying 1D horizontal fill separately to each regridded boundary."
|
|
370
|
+
)
|
|
371
|
+
|
|
315
372
|
def _get_data(self):
|
|
316
373
|
|
|
317
374
|
data_dict = {
|
|
@@ -487,118 +544,80 @@ class BoundaryForcing:
|
|
|
487
544
|
|
|
488
545
|
object.__setattr__(self, "bdry_coords", bdry_coords)
|
|
489
546
|
|
|
490
|
-
def
|
|
491
|
-
self,
|
|
492
|
-
|
|
493
|
-
|
|
547
|
+
def _get_depth_coordinates(
|
|
548
|
+
self,
|
|
549
|
+
zeta: xr.DataArray | float,
|
|
550
|
+
direction: str,
|
|
551
|
+
location: str,
|
|
552
|
+
depth_type: str = "layer",
|
|
553
|
+
) -> None:
|
|
554
|
+
"""Compute and store depth coordinates for a specified boundary direction, grid
|
|
555
|
+
location, and depth type.
|
|
494
556
|
|
|
495
|
-
This method computes
|
|
496
|
-
|
|
497
|
-
|
|
557
|
+
This method efficiently computes depth coordinates along the specified boundary without
|
|
558
|
+
interpolating the entire domain topography. The computed depth values are stored in
|
|
559
|
+
`self.ds_depth_coords`.
|
|
498
560
|
|
|
499
561
|
Parameters
|
|
500
562
|
----------
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
-
|
|
504
|
-
-
|
|
505
|
-
|
|
563
|
+
zeta : xr.DataArray or float
|
|
564
|
+
Free-surface elevation (`zeta`). Can be:
|
|
565
|
+
- A scalar float value (constant sea surface height).
|
|
566
|
+
- An `xarray.DataArray` with spatial variations. If provided as an array, it may have a
|
|
567
|
+
time dimension, but must be **1D** (varying only in time).
|
|
506
568
|
direction : str
|
|
507
|
-
The direction
|
|
569
|
+
The boundary direction for which depth coordinates are computed. Must be one of:
|
|
508
570
|
- "north"
|
|
509
571
|
- "south"
|
|
510
572
|
- "east"
|
|
511
573
|
- "west"
|
|
574
|
+
location : str
|
|
575
|
+
Grid location at which depth is computed. Must be one of:
|
|
576
|
+
- `"rho"`: Depth at scalar grid points.
|
|
577
|
+
- `"u"`: Depth at U-velocity grid points.
|
|
578
|
+
- `"v"`: Depth at V-velocity grid points.
|
|
579
|
+
depth_type : str, optional
|
|
580
|
+
Type of depth coordinate to compute, either:
|
|
581
|
+
- `"layer"` (default): Depth at vertical layer midpoints.
|
|
582
|
+
- `"interface"`: Depth at vertical layer interfaces.
|
|
512
583
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
- "v": Computes depth coordinates for v points.
|
|
518
|
-
|
|
519
|
-
Updates
|
|
520
|
-
-------
|
|
521
|
-
self.grid.ds : xarray.Dataset
|
|
522
|
-
The dataset is updated with the following vertical depth coordinates:
|
|
523
|
-
- f"{type}_depth_rho_{direction}": Depth coordinates at rho points.
|
|
524
|
-
- f"{type}_depth_u_{direction}": Depth coordinates at u points (if applicable).
|
|
525
|
-
- f"{type}_depth_v_{direction}": Depth coordinates at v points (if applicable).
|
|
584
|
+
Notes
|
|
585
|
+
-----
|
|
586
|
+
- This method is optimized for boundary computations by selecting only the relevant margin
|
|
587
|
+
(2 grid cells) instead of interpolating the entire domain.
|
|
526
588
|
"""
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
layer_vars.append(f"{type}_depth_{location}_{direction}")
|
|
531
|
-
|
|
532
|
-
if all(layer_var in self.grid.ds for layer_var in layer_vars):
|
|
533
|
-
# Vertical coordinate data already exists
|
|
534
|
-
pass
|
|
535
|
-
|
|
536
|
-
elif f"{type}_depth_rho" in self.grid.ds:
|
|
537
|
-
depth = self.grid.ds[f"{type}_depth_rho"]
|
|
538
|
-
depth.attrs["long_name"] = f"{type} depth at rho-points"
|
|
539
|
-
depth.attrs["units"] = "m"
|
|
540
|
-
self.grid.ds[f"{type}_depth_rho_{direction}"] = depth.isel(
|
|
541
|
-
**self.bdry_coords["rho"][direction]
|
|
542
|
-
)
|
|
543
|
-
|
|
544
|
-
if "u" in additional_locations or "v" in additional_locations:
|
|
589
|
+
key = f"{depth_type}_depth_{location}_{direction}"
|
|
590
|
+
if key not in self.ds_depth_coords:
|
|
591
|
+
if location in ["u", "v"]:
|
|
545
592
|
# selection of margin consisting of 2 grid cells
|
|
546
|
-
depth = depth.isel(**self.bdry_coords["vector"][direction])
|
|
547
|
-
# interpolation
|
|
548
|
-
if "u" in additional_locations:
|
|
549
|
-
depth_u = interpolate_from_rho_to_u(depth)
|
|
550
|
-
depth_u.attrs["long_name"] = f"{type} depth at u-points"
|
|
551
|
-
depth_u.attrs["units"] = "m"
|
|
552
|
-
self.grid.ds[f"{type}_depth_u_{direction}"] = depth_u.isel(
|
|
553
|
-
**self.bdry_coords["u"][direction]
|
|
554
|
-
)
|
|
555
|
-
if "v" in additional_locations:
|
|
556
|
-
depth_v = interpolate_from_rho_to_v(depth)
|
|
557
|
-
depth_v.attrs["long_name"] = f"{type} depth at v-points"
|
|
558
|
-
depth_v.attrs["units"] = "m"
|
|
559
|
-
self.grid.ds[f"{type}_depth_v_{direction}"] = depth_v.isel(
|
|
560
|
-
**self.bdry_coords["v"][direction]
|
|
561
|
-
)
|
|
562
|
-
else:
|
|
563
|
-
if "u" in additional_locations or "v" in additional_locations:
|
|
564
593
|
h = self.grid.ds["h"].isel(**self.bdry_coords["vector"][direction])
|
|
594
|
+
if location == "u":
|
|
595
|
+
h = interpolate_from_rho_to_u(h)
|
|
596
|
+
h = h.isel(**self.bdry_coords["u"][direction])
|
|
597
|
+
elif location == "v":
|
|
598
|
+
h = interpolate_from_rho_to_v(h)
|
|
599
|
+
h = h.isel(**self.bdry_coords["v"][direction])
|
|
565
600
|
else:
|
|
566
601
|
h = self.grid.ds["h"].isel(**self.bdry_coords["rho"][direction])
|
|
567
|
-
|
|
602
|
+
|
|
603
|
+
if depth_type == "layer":
|
|
568
604
|
depth = compute_depth(
|
|
569
|
-
|
|
605
|
+
zeta, h, self.grid.hc, self.grid.ds.Cs_r, self.grid.ds.sigma_r
|
|
570
606
|
)
|
|
571
607
|
else:
|
|
572
608
|
depth = compute_depth(
|
|
573
|
-
|
|
609
|
+
zeta, h, self.grid.hc, self.grid.ds.Cs_w, self.grid.ds.sigma_w
|
|
574
610
|
)
|
|
575
611
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
depth_u = interpolate_from_rho_to_u(depth)
|
|
586
|
-
depth_v = interpolate_from_rho_to_v(depth)
|
|
587
|
-
# selection of outermost margin
|
|
588
|
-
depth_u.attrs["long_name"] = f"{type} depth at u-points"
|
|
589
|
-
depth_u.attrs["units"] = "m"
|
|
590
|
-
self.grid.ds[f"{type}_depth_u_{direction}"] = depth_u.isel(
|
|
591
|
-
**self.bdry_coords["u"][direction]
|
|
592
|
-
)
|
|
593
|
-
depth_v.attrs["long_name"] = f"{type} depth at v-points"
|
|
594
|
-
depth_v.attrs["units"] = "m"
|
|
595
|
-
self.grid.ds[f"{type}_depth_v_{direction}"] = depth_v.isel(
|
|
596
|
-
**self.bdry_coords["v"][direction]
|
|
597
|
-
)
|
|
598
|
-
else:
|
|
599
|
-
depth.attrs["long_name"] = f"{type} depth at rho-points"
|
|
600
|
-
depth.attrs["units"] = "m"
|
|
601
|
-
self.grid.ds[f"{type}_depth_rho_{direction}"] = depth
|
|
612
|
+
# Add metadata
|
|
613
|
+
depth.attrs.update(
|
|
614
|
+
{
|
|
615
|
+
"long_name": f"{depth_type} depth at {location}-points along {direction}ern boundary",
|
|
616
|
+
"units": "m",
|
|
617
|
+
}
|
|
618
|
+
)
|
|
619
|
+
|
|
620
|
+
self.ds_depth_coords[key] = depth
|
|
602
621
|
|
|
603
622
|
def _add_global_metadata(self, data, ds=None):
|
|
604
623
|
|
|
@@ -615,6 +634,10 @@ class BoundaryForcing:
|
|
|
615
634
|
ds.attrs["end_time"] = str(self.end_time)
|
|
616
635
|
ds.attrs["source"] = self.source["name"]
|
|
617
636
|
ds.attrs["model_reference_date"] = str(self.model_reference_date)
|
|
637
|
+
ds.attrs["apply_2d_horizontal_fill"] = str(self.apply_2d_horizontal_fill)
|
|
638
|
+
ds.attrs["adjust_depth_for_sea_surface_height"] = str(
|
|
639
|
+
self.adjust_depth_for_sea_surface_height
|
|
640
|
+
)
|
|
618
641
|
|
|
619
642
|
ds.attrs["theta_s"] = self.grid.ds.attrs["theta_s"]
|
|
620
643
|
ds.attrs["theta_b"] = self.grid.ds.attrs["theta_b"]
|
|
@@ -827,11 +850,21 @@ class BoundaryForcing:
|
|
|
827
850
|
var_name_wo_direction, direction = var_name.split("_")
|
|
828
851
|
location = self.variable_info[var_name_wo_direction]["location"]
|
|
829
852
|
|
|
853
|
+
# Find correct mask
|
|
854
|
+
if location == "rho":
|
|
855
|
+
mask = self.grid.ds.mask_rho
|
|
856
|
+
elif location == "u":
|
|
857
|
+
mask = self.grid.ds.mask_u
|
|
858
|
+
elif location == "v":
|
|
859
|
+
mask = self.grid.ds.mask_v
|
|
860
|
+
|
|
861
|
+
mask = mask.isel(**self.bdry_coords[location][direction])
|
|
862
|
+
|
|
830
863
|
if "s_rho" in field.dims:
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
864
|
+
layer_depth = self.ds_depth_coords[f"layer_depth_{location}_{direction}"]
|
|
865
|
+
if self.adjust_depth_for_sea_surface_height:
|
|
866
|
+
layer_depth = layer_depth.isel(time=time)
|
|
867
|
+
field = field.assign_coords({"layer_depth": layer_depth})
|
|
835
868
|
if var_name.startswith(("u", "v", "ubar", "vbar", "zeta")):
|
|
836
869
|
vmax = max(field.max().values, -field.min().values)
|
|
837
870
|
vmin = -vmax
|
|
@@ -848,19 +881,11 @@ class BoundaryForcing:
|
|
|
848
881
|
|
|
849
882
|
if len(field.dims) == 2:
|
|
850
883
|
if layer_contours:
|
|
851
|
-
|
|
852
|
-
additional_locations = ["u", "v"]
|
|
853
|
-
else:
|
|
854
|
-
additional_locations = []
|
|
855
|
-
self._get_vertical_coordinates(
|
|
856
|
-
type="interface",
|
|
857
|
-
direction=direction,
|
|
858
|
-
additional_locations=additional_locations,
|
|
859
|
-
)
|
|
860
|
-
|
|
861
|
-
interface_depth = self.grid.ds[
|
|
884
|
+
interface_depth = self.ds_depth_coords[
|
|
862
885
|
f"interface_depth_{location}_{direction}"
|
|
863
886
|
]
|
|
887
|
+
if self.adjust_depth_for_sea_surface_height:
|
|
888
|
+
interface_depth = interface_depth.isel(time=time)
|
|
864
889
|
# restrict number of layer_contours to 10 for the sake of plot clearity
|
|
865
890
|
nr_layers = len(interface_depth["s_w"])
|
|
866
891
|
selected_layers = np.linspace(
|
|
@@ -879,42 +904,26 @@ class BoundaryForcing:
|
|
|
879
904
|
ax=ax,
|
|
880
905
|
)
|
|
881
906
|
else:
|
|
882
|
-
_line_plot(field, title=title, ax=ax)
|
|
907
|
+
_line_plot(field.where(mask), title=title, ax=ax)
|
|
883
908
|
|
|
884
909
|
def save(
|
|
885
910
|
self,
|
|
886
911
|
filepath: Union[str, Path],
|
|
887
|
-
|
|
888
|
-
np_xi: int = None,
|
|
889
|
-
group: bool = False,
|
|
912
|
+
group: bool = True,
|
|
890
913
|
) -> None:
|
|
891
914
|
"""Save the boundary forcing fields to one or more netCDF4 files.
|
|
892
915
|
|
|
893
|
-
This method saves the dataset
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
1. **Single File Mode (default)**:
|
|
897
|
-
- If both `np_eta` and `np_xi` are `None`, the entire dataset is saved as a single netCDF4 file.
|
|
898
|
-
- The file is named based on the `filepath`, with `.nc` automatically appended.
|
|
899
|
-
|
|
900
|
-
2. **Partitioned Mode**:
|
|
901
|
-
- If either `np_eta` or `np_xi` is specified, the dataset is partitioned into spatial tiles along the `eta` and `xi` axes.
|
|
902
|
-
- Each tile is saved as a separate netCDF4 file, and filenames are modified with an index (e.g., `"filepath_YYYYMM.0.nc"`, `"filepath_YYYYMM.1.nc"`).
|
|
903
|
-
|
|
904
|
-
Additionally, if `group` is set to `True`, the dataset is first grouped into temporal subsets, resulting in multiple grouped files before partitioning and saving.
|
|
916
|
+
This method saves the dataset to disk as either a single netCDF4 file or multiple files, depending on the `group` parameter.
|
|
917
|
+
If `group` is `True`, the dataset is divided into subsets (e.g., monthly or yearly) based on the temporal frequency
|
|
918
|
+
of the data, and each subset is saved to a separate file.
|
|
905
919
|
|
|
906
920
|
Parameters
|
|
907
921
|
----------
|
|
908
922
|
filepath : Union[str, Path]
|
|
909
|
-
The base path and filename for the output
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
The number of partitions along the `eta` direction. If `None`, no spatial partitioning is performed.
|
|
914
|
-
np_xi : int, optional
|
|
915
|
-
The number of partitions along the `xi` direction. If `None`, no spatial partitioning is performed.
|
|
916
|
-
group: bool, optional
|
|
917
|
-
If `True`, groups the dataset into multiple files based on temporal data frequency. Defaults to `False`.
|
|
923
|
+
The base path and filename for the output file(s). If `group` is `True`, the filenames will include additional
|
|
924
|
+
time-based information (e.g., year or month) to distinguish the subsets.
|
|
925
|
+
group : bool, optional
|
|
926
|
+
Whether to divide the dataset into multiple files based on temporal frequency. Defaults to `True`.
|
|
918
927
|
|
|
919
928
|
Returns
|
|
920
929
|
-------
|
|
@@ -929,12 +938,6 @@ class BoundaryForcing:
|
|
|
929
938
|
if filepath.suffix == ".nc":
|
|
930
939
|
filepath = filepath.with_suffix("")
|
|
931
940
|
|
|
932
|
-
if self.use_dask:
|
|
933
|
-
from dask.diagnostics import ProgressBar
|
|
934
|
-
|
|
935
|
-
with ProgressBar():
|
|
936
|
-
self.ds.load()
|
|
937
|
-
|
|
938
941
|
if group:
|
|
939
942
|
dataset_list, output_filenames = group_dataset(self.ds, str(filepath))
|
|
940
943
|
else:
|
|
@@ -942,7 +945,7 @@ class BoundaryForcing:
|
|
|
942
945
|
output_filenames = [str(filepath)]
|
|
943
946
|
|
|
944
947
|
saved_filenames = save_datasets(
|
|
945
|
-
dataset_list, output_filenames,
|
|
948
|
+
dataset_list, output_filenames, use_dask=self.use_dask
|
|
946
949
|
)
|
|
947
950
|
|
|
948
951
|
return saved_filenames
|
|
@@ -995,19 +998,19 @@ class BoundaryForcing:
|
|
|
995
998
|
)
|
|
996
999
|
|
|
997
1000
|
|
|
998
|
-
def apply_1d_horizontal_fill(
|
|
999
|
-
"""Forward and backward fill NaN values in horizontal
|
|
1001
|
+
def apply_1d_horizontal_fill(data_array: xr.DataArray) -> xr.DataArray:
|
|
1002
|
+
"""Forward and backward fill NaN values in a single horizontal dimension for open
|
|
1003
|
+
boundaries.
|
|
1000
1004
|
|
|
1001
1005
|
Parameters
|
|
1002
1006
|
----------
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
`xarray.DataArray`.
|
|
1007
|
+
data_array : xarray.DataArray
|
|
1008
|
+
The data array to be updated.
|
|
1006
1009
|
|
|
1007
1010
|
Returns
|
|
1008
1011
|
-------
|
|
1009
|
-
|
|
1010
|
-
The updated
|
|
1012
|
+
xarray.DataArray
|
|
1013
|
+
The updated data array with NaN values filled.
|
|
1011
1014
|
|
|
1012
1015
|
Raises
|
|
1013
1016
|
------
|
|
@@ -1016,29 +1019,22 @@ def apply_1d_horizontal_fill(processed_fields: dict) -> dict:
|
|
|
1016
1019
|
"""
|
|
1017
1020
|
|
|
1018
1021
|
horizontal_dims = ["eta_rho", "eta_v", "xi_rho", "xi_u"]
|
|
1022
|
+
selected_horizontal_dim = None
|
|
1019
1023
|
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
)
|
|
1029
|
-
selected_horizontal_dim = dim
|
|
1030
|
-
|
|
1031
|
-
if selected_horizontal_dim is None:
|
|
1032
|
-
raise ValueError(
|
|
1033
|
-
f"No valid horizontal dimension found for variable '{var_name}'."
|
|
1034
|
-
)
|
|
1024
|
+
# Determine the horizontal dimension to fill
|
|
1025
|
+
for dim in horizontal_dims:
|
|
1026
|
+
if dim in data_array.dims:
|
|
1027
|
+
if selected_horizontal_dim is not None:
|
|
1028
|
+
raise ValueError(
|
|
1029
|
+
f"More than one horizontal dimension found in variable '{data_array.name}'."
|
|
1030
|
+
)
|
|
1031
|
+
selected_horizontal_dim = dim
|
|
1035
1032
|
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
)
|
|
1040
|
-
processed_fields[var_name] = one_dim_fill(
|
|
1041
|
-
filled, selected_horizontal_dim, direction="backward"
|
|
1033
|
+
if selected_horizontal_dim is None:
|
|
1034
|
+
raise ValueError(
|
|
1035
|
+
f"No valid horizontal dimension found for variable '{data_array.name}'."
|
|
1042
1036
|
)
|
|
1043
1037
|
|
|
1044
|
-
|
|
1038
|
+
# Forward and backward fill in the horizontal direction
|
|
1039
|
+
filled = one_dim_fill(data_array, selected_horizontal_dim, direction="forward")
|
|
1040
|
+
return one_dim_fill(filled, selected_horizontal_dim, direction="backward")
|