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
ci/environment.yml
CHANGED
roms_tools/__init__.py
CHANGED
|
@@ -15,6 +15,7 @@ from roms_tools.setup.initial_conditions import InitialConditions # noqa: F401
|
|
|
15
15
|
from roms_tools.setup.boundary_forcing import BoundaryForcing # noqa: F401
|
|
16
16
|
from roms_tools.setup.river_forcing import RiverForcing # noqa: F401
|
|
17
17
|
from roms_tools.setup.nesting import Nesting # noqa: F401
|
|
18
|
+
from roms_tools.tiling.partition import partition_netcdf # noqa: F401
|
|
18
19
|
from roms_tools.analysis.roms_output import ROMSOutput # noqa: F401
|
|
19
20
|
|
|
20
21
|
# Configure logging when the package is imported
|
|
@@ -12,7 +12,6 @@ from datetime import datetime, timedelta
|
|
|
12
12
|
from roms_tools import Grid
|
|
13
13
|
from roms_tools.plot import _plot, _section_plot, _profile_plot, _line_plot
|
|
14
14
|
from roms_tools.vertical_coordinate import (
|
|
15
|
-
add_depth_coordinates_to_dataset,
|
|
16
15
|
compute_depth_coordinates,
|
|
17
16
|
)
|
|
18
17
|
|
|
@@ -186,24 +185,26 @@ class ROMSOutput:
|
|
|
186
185
|
|
|
187
186
|
if compute_layer_depth:
|
|
188
187
|
layer_depth = compute_depth_coordinates(
|
|
189
|
-
self.ds.isel(time=time),
|
|
190
188
|
self.grid.ds,
|
|
189
|
+
self.ds.zeta.isel(time=time),
|
|
191
190
|
depth_type="layer",
|
|
192
191
|
location=loc,
|
|
193
|
-
s=s,
|
|
194
192
|
eta=eta,
|
|
195
193
|
xi=xi,
|
|
196
194
|
)
|
|
195
|
+
if s is not None:
|
|
196
|
+
layer_depth = layer_depth.isel(s_rho=s)
|
|
197
197
|
if compute_interface_depth:
|
|
198
198
|
interface_depth = compute_depth_coordinates(
|
|
199
|
-
self.ds.isel(time=time),
|
|
200
199
|
self.grid.ds,
|
|
200
|
+
self.ds.zeta.isel(time=time),
|
|
201
201
|
depth_type="interface",
|
|
202
202
|
location=loc,
|
|
203
|
-
s=s,
|
|
204
203
|
eta=eta,
|
|
205
204
|
xi=xi,
|
|
206
205
|
)
|
|
206
|
+
if s is not None:
|
|
207
|
+
interface_depth = interface_depth.isel(s_w=s)
|
|
207
208
|
|
|
208
209
|
# Slice the field as desired
|
|
209
210
|
title = field.long_name
|
|
@@ -331,7 +332,10 @@ class ROMSOutput:
|
|
|
331
332
|
This method uses the `compute_and_update_depth_coordinates` function to perform calculations and updates.
|
|
332
333
|
"""
|
|
333
334
|
|
|
334
|
-
|
|
335
|
+
for location in locations:
|
|
336
|
+
self.ds[f"{depth_type}_depth_{location}"] = compute_depth_coordinates(
|
|
337
|
+
self.grid.ds, self.ds.zeta, depth_type, location
|
|
338
|
+
)
|
|
335
339
|
|
|
336
340
|
def _load_model_output(self) -> xr.Dataset:
|
|
337
341
|
"""Load the model output based on the type."""
|
|
@@ -10,6 +10,7 @@ import matplotlib.pyplot as plt
|
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
from roms_tools import Grid
|
|
12
12
|
from roms_tools.regrid import LateralRegrid, VerticalRegrid
|
|
13
|
+
from roms_tools.utils import save_datasets
|
|
13
14
|
from roms_tools.vertical_coordinate import compute_depth
|
|
14
15
|
from roms_tools.plot import _section_plot, _line_plot
|
|
15
16
|
from roms_tools.utils import (
|
|
@@ -21,7 +22,6 @@ from roms_tools.setup.datasets import GLORYSDataset, CESMBGCDataset
|
|
|
21
22
|
from roms_tools.setup.utils import (
|
|
22
23
|
get_variable_metadata,
|
|
23
24
|
group_dataset,
|
|
24
|
-
save_datasets,
|
|
25
25
|
get_target_coords,
|
|
26
26
|
rotate_velocities,
|
|
27
27
|
compute_barotropic_velocity,
|
|
@@ -66,10 +66,14 @@ class BoundaryForcing:
|
|
|
66
66
|
- "physics": for physical atmospheric forcing.
|
|
67
67
|
- "bgc": for biogeochemical forcing.
|
|
68
68
|
|
|
69
|
-
apply_2d_horizontal_fill: bool, optional
|
|
69
|
+
apply_2d_horizontal_fill : bool, optional
|
|
70
70
|
Indicates whether to perform a two-dimensional horizontal fill on the source data prior to regridding to boundaries.
|
|
71
71
|
If `False`, a one-dimensional horizontal fill is performed separately on each of the four regridded boundaries.
|
|
72
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`.
|
|
73
77
|
model_reference_date : datetime, optional
|
|
74
78
|
Reference date for the model. Default is January 1, 2000.
|
|
75
79
|
use_dask: bool, optional
|
|
@@ -105,6 +109,7 @@ class BoundaryForcing:
|
|
|
105
109
|
source: Dict[str, Union[str, Path, List[Union[str, Path]]]]
|
|
106
110
|
type: str = "physics"
|
|
107
111
|
apply_2d_horizontal_fill: bool = False
|
|
112
|
+
adjust_depth_for_sea_surface_height: bool = False
|
|
108
113
|
model_reference_date: datetime = datetime(2000, 1, 1)
|
|
109
114
|
use_dask: bool = False
|
|
110
115
|
bypass_validation: bool = False
|
|
@@ -114,6 +119,9 @@ class BoundaryForcing:
|
|
|
114
119
|
def __post_init__(self):
|
|
115
120
|
|
|
116
121
|
self._input_checks()
|
|
122
|
+
# Dataset for depth coordinates
|
|
123
|
+
object.__setattr__(self, "ds_depth_coords", xr.Dataset())
|
|
124
|
+
|
|
117
125
|
target_coords = get_target_coords(self.grid)
|
|
118
126
|
|
|
119
127
|
data = self._get_data()
|
|
@@ -154,13 +162,14 @@ class BoundaryForcing:
|
|
|
154
162
|
|
|
155
163
|
processed_fields = {}
|
|
156
164
|
|
|
157
|
-
#
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
+
]
|
|
164
173
|
lon = target_coords["lon"].isel(
|
|
165
174
|
**self.bdry_coords["vector"][direction]
|
|
166
175
|
)
|
|
@@ -176,6 +185,13 @@ class BoundaryForcing:
|
|
|
176
185
|
bdry_data.ds[bdry_data.var_names[var_name]]
|
|
177
186
|
)
|
|
178
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
|
+
|
|
179
195
|
# lateral regridding of tracer fields
|
|
180
196
|
tracer_var_names = [
|
|
181
197
|
name
|
|
@@ -209,6 +225,9 @@ class BoundaryForcing:
|
|
|
209
225
|
angle,
|
|
210
226
|
interpolate=True,
|
|
211
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)
|
|
212
231
|
|
|
213
232
|
# selection of outermost margin for u/v variables
|
|
214
233
|
for var_name in self.variable_info.keys():
|
|
@@ -218,6 +237,9 @@ class BoundaryForcing:
|
|
|
218
237
|
processed_fields[var_name] = processed_fields[
|
|
219
238
|
var_name
|
|
220
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])
|
|
221
243
|
|
|
222
244
|
if not self.apply_2d_horizontal_fill:
|
|
223
245
|
self._validate_1d_fill(
|
|
@@ -225,7 +247,20 @@ class BoundaryForcing:
|
|
|
225
247
|
direction,
|
|
226
248
|
bdry_data.dim_names["depth"],
|
|
227
249
|
)
|
|
228
|
-
|
|
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
|
|
229
264
|
|
|
230
265
|
var_names_dict = {}
|
|
231
266
|
for location in ["rho", "u", "v"]:
|
|
@@ -234,24 +269,22 @@ class BoundaryForcing:
|
|
|
234
269
|
for name, info in self.variable_info.items()
|
|
235
270
|
if info["location"] == location and info["is_3d"]
|
|
236
271
|
]
|
|
272
|
+
|
|
237
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
|
|
238
279
|
if len(var_names_dict["u"]) > 0 or len(var_names_dict["v"]) > 0:
|
|
239
|
-
self.
|
|
240
|
-
|
|
241
|
-
direction=direction,
|
|
242
|
-
additional_locations=["u", "v"],
|
|
243
|
-
)
|
|
244
|
-
else:
|
|
245
|
-
if len(var_names_dict["rho"]) > 0:
|
|
246
|
-
self._get_vertical_coordinates(
|
|
247
|
-
type="layer", direction=direction, additional_locations=[]
|
|
248
|
-
)
|
|
280
|
+
self._get_depth_coordinates(zeta_u, direction, "u", "layer")
|
|
281
|
+
self._get_depth_coordinates(zeta_v, direction, "v", "layer")
|
|
249
282
|
|
|
250
283
|
# vertical regridding
|
|
251
284
|
for location in ["rho", "u", "v"]:
|
|
252
285
|
if len(var_names_dict[location]) > 0:
|
|
253
286
|
vertical_regrid = VerticalRegrid(
|
|
254
|
-
self.
|
|
287
|
+
self.ds_depth_coords[f"layer_depth_{location}_{direction}"],
|
|
255
288
|
bdry_data.ds[bdry_data.dim_names["depth"]],
|
|
256
289
|
)
|
|
257
290
|
for var_name in var_names_dict[location]:
|
|
@@ -262,17 +295,16 @@ class BoundaryForcing:
|
|
|
262
295
|
|
|
263
296
|
# compute barotropic velocities
|
|
264
297
|
if "u" in self.variable_info and "v" in self.variable_info:
|
|
265
|
-
self.
|
|
266
|
-
|
|
267
|
-
direction=direction,
|
|
268
|
-
additional_locations=["u", "v"],
|
|
269
|
-
)
|
|
298
|
+
self._get_depth_coordinates(zeta_u, direction, "u", "interface")
|
|
299
|
+
self._get_depth_coordinates(zeta_v, direction, "v", "interface")
|
|
270
300
|
for location in ["u", "v"]:
|
|
271
301
|
processed_fields[
|
|
272
302
|
f"{location}bar"
|
|
273
303
|
] = compute_barotropic_velocity(
|
|
274
304
|
processed_fields[location],
|
|
275
|
-
self.
|
|
305
|
+
self.ds_depth_coords[
|
|
306
|
+
f"interface_depth_{location}_{direction}"
|
|
307
|
+
],
|
|
276
308
|
)
|
|
277
309
|
|
|
278
310
|
# Reorder dimensions
|
|
@@ -314,6 +346,29 @@ class BoundaryForcing:
|
|
|
314
346
|
{**self.source, "climatology": self.source.get("climatology", False)},
|
|
315
347
|
)
|
|
316
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
|
+
|
|
317
372
|
def _get_data(self):
|
|
318
373
|
|
|
319
374
|
data_dict = {
|
|
@@ -489,118 +544,80 @@ class BoundaryForcing:
|
|
|
489
544
|
|
|
490
545
|
object.__setattr__(self, "bdry_coords", bdry_coords)
|
|
491
546
|
|
|
492
|
-
def
|
|
493
|
-
self,
|
|
494
|
-
|
|
495
|
-
|
|
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.
|
|
496
556
|
|
|
497
|
-
This method computes
|
|
498
|
-
|
|
499
|
-
|
|
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`.
|
|
500
560
|
|
|
501
561
|
Parameters
|
|
502
562
|
----------
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
-
|
|
506
|
-
-
|
|
507
|
-
|
|
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).
|
|
508
568
|
direction : str
|
|
509
|
-
The direction
|
|
569
|
+
The boundary direction for which depth coordinates are computed. Must be one of:
|
|
510
570
|
- "north"
|
|
511
571
|
- "south"
|
|
512
572
|
- "east"
|
|
513
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.
|
|
514
583
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
- "v": Computes depth coordinates for v points.
|
|
520
|
-
|
|
521
|
-
Updates
|
|
522
|
-
-------
|
|
523
|
-
self.grid.ds : xarray.Dataset
|
|
524
|
-
The dataset is updated with the following vertical depth coordinates:
|
|
525
|
-
- f"{type}_depth_rho_{direction}": Depth coordinates at rho points.
|
|
526
|
-
- f"{type}_depth_u_{direction}": Depth coordinates at u points (if applicable).
|
|
527
|
-
- 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.
|
|
528
588
|
"""
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
layer_vars.append(f"{type}_depth_{location}_{direction}")
|
|
533
|
-
|
|
534
|
-
if all(layer_var in self.grid.ds for layer_var in layer_vars):
|
|
535
|
-
# Vertical coordinate data already exists
|
|
536
|
-
pass
|
|
537
|
-
|
|
538
|
-
elif f"{type}_depth_rho" in self.grid.ds:
|
|
539
|
-
depth = self.grid.ds[f"{type}_depth_rho"]
|
|
540
|
-
depth.attrs["long_name"] = f"{type} depth at rho-points"
|
|
541
|
-
depth.attrs["units"] = "m"
|
|
542
|
-
self.grid.ds[f"{type}_depth_rho_{direction}"] = depth.isel(
|
|
543
|
-
**self.bdry_coords["rho"][direction]
|
|
544
|
-
)
|
|
545
|
-
|
|
546
|
-
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"]:
|
|
547
592
|
# selection of margin consisting of 2 grid cells
|
|
548
|
-
depth = depth.isel(**self.bdry_coords["vector"][direction])
|
|
549
|
-
# interpolation
|
|
550
|
-
if "u" in additional_locations:
|
|
551
|
-
depth_u = interpolate_from_rho_to_u(depth)
|
|
552
|
-
depth_u.attrs["long_name"] = f"{type} depth at u-points"
|
|
553
|
-
depth_u.attrs["units"] = "m"
|
|
554
|
-
self.grid.ds[f"{type}_depth_u_{direction}"] = depth_u.isel(
|
|
555
|
-
**self.bdry_coords["u"][direction]
|
|
556
|
-
)
|
|
557
|
-
if "v" in additional_locations:
|
|
558
|
-
depth_v = interpolate_from_rho_to_v(depth)
|
|
559
|
-
depth_v.attrs["long_name"] = f"{type} depth at v-points"
|
|
560
|
-
depth_v.attrs["units"] = "m"
|
|
561
|
-
self.grid.ds[f"{type}_depth_v_{direction}"] = depth_v.isel(
|
|
562
|
-
**self.bdry_coords["v"][direction]
|
|
563
|
-
)
|
|
564
|
-
else:
|
|
565
|
-
if "u" in additional_locations or "v" in additional_locations:
|
|
566
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])
|
|
567
600
|
else:
|
|
568
601
|
h = self.grid.ds["h"].isel(**self.bdry_coords["rho"][direction])
|
|
569
|
-
|
|
602
|
+
|
|
603
|
+
if depth_type == "layer":
|
|
570
604
|
depth = compute_depth(
|
|
571
|
-
|
|
605
|
+
zeta, h, self.grid.hc, self.grid.ds.Cs_r, self.grid.ds.sigma_r
|
|
572
606
|
)
|
|
573
607
|
else:
|
|
574
608
|
depth = compute_depth(
|
|
575
|
-
|
|
609
|
+
zeta, h, self.grid.hc, self.grid.ds.Cs_w, self.grid.ds.sigma_w
|
|
576
610
|
)
|
|
577
611
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
depth_u = interpolate_from_rho_to_u(depth)
|
|
588
|
-
depth_v = interpolate_from_rho_to_v(depth)
|
|
589
|
-
# selection of outermost margin
|
|
590
|
-
depth_u.attrs["long_name"] = f"{type} depth at u-points"
|
|
591
|
-
depth_u.attrs["units"] = "m"
|
|
592
|
-
self.grid.ds[f"{type}_depth_u_{direction}"] = depth_u.isel(
|
|
593
|
-
**self.bdry_coords["u"][direction]
|
|
594
|
-
)
|
|
595
|
-
depth_v.attrs["long_name"] = f"{type} depth at v-points"
|
|
596
|
-
depth_v.attrs["units"] = "m"
|
|
597
|
-
self.grid.ds[f"{type}_depth_v_{direction}"] = depth_v.isel(
|
|
598
|
-
**self.bdry_coords["v"][direction]
|
|
599
|
-
)
|
|
600
|
-
else:
|
|
601
|
-
depth.attrs["long_name"] = f"{type} depth at rho-points"
|
|
602
|
-
depth.attrs["units"] = "m"
|
|
603
|
-
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
|
|
604
621
|
|
|
605
622
|
def _add_global_metadata(self, data, ds=None):
|
|
606
623
|
|
|
@@ -617,6 +634,10 @@ class BoundaryForcing:
|
|
|
617
634
|
ds.attrs["end_time"] = str(self.end_time)
|
|
618
635
|
ds.attrs["source"] = self.source["name"]
|
|
619
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
|
+
)
|
|
620
641
|
|
|
621
642
|
ds.attrs["theta_s"] = self.grid.ds.attrs["theta_s"]
|
|
622
643
|
ds.attrs["theta_b"] = self.grid.ds.attrs["theta_b"]
|
|
@@ -840,9 +861,10 @@ class BoundaryForcing:
|
|
|
840
861
|
mask = mask.isel(**self.bdry_coords[location][direction])
|
|
841
862
|
|
|
842
863
|
if "s_rho" in field.dims:
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
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})
|
|
846
868
|
if var_name.startswith(("u", "v", "ubar", "vbar", "zeta")):
|
|
847
869
|
vmax = max(field.max().values, -field.min().values)
|
|
848
870
|
vmin = -vmax
|
|
@@ -859,19 +881,11 @@ class BoundaryForcing:
|
|
|
859
881
|
|
|
860
882
|
if len(field.dims) == 2:
|
|
861
883
|
if layer_contours:
|
|
862
|
-
|
|
863
|
-
additional_locations = ["u", "v"]
|
|
864
|
-
else:
|
|
865
|
-
additional_locations = []
|
|
866
|
-
self._get_vertical_coordinates(
|
|
867
|
-
type="interface",
|
|
868
|
-
direction=direction,
|
|
869
|
-
additional_locations=additional_locations,
|
|
870
|
-
)
|
|
871
|
-
|
|
872
|
-
interface_depth = self.grid.ds[
|
|
884
|
+
interface_depth = self.ds_depth_coords[
|
|
873
885
|
f"interface_depth_{location}_{direction}"
|
|
874
886
|
]
|
|
887
|
+
if self.adjust_depth_for_sea_surface_height:
|
|
888
|
+
interface_depth = interface_depth.isel(time=time)
|
|
875
889
|
# restrict number of layer_contours to 10 for the sake of plot clearity
|
|
876
890
|
nr_layers = len(interface_depth["s_w"])
|
|
877
891
|
selected_layers = np.linspace(
|
|
@@ -883,7 +897,7 @@ class BoundaryForcing:
|
|
|
883
897
|
interface_depth = None
|
|
884
898
|
|
|
885
899
|
_section_plot(
|
|
886
|
-
field
|
|
900
|
+
field,
|
|
887
901
|
interface_depth=interface_depth,
|
|
888
902
|
title=title,
|
|
889
903
|
kwargs=kwargs,
|
|
@@ -895,37 +909,21 @@ class BoundaryForcing:
|
|
|
895
909
|
def save(
|
|
896
910
|
self,
|
|
897
911
|
filepath: Union[str, Path],
|
|
898
|
-
|
|
899
|
-
np_xi: int = None,
|
|
900
|
-
group: bool = False,
|
|
912
|
+
group: bool = True,
|
|
901
913
|
) -> None:
|
|
902
914
|
"""Save the boundary forcing fields to one or more netCDF4 files.
|
|
903
915
|
|
|
904
|
-
This method saves the dataset
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
1. **Single File Mode (default)**:
|
|
908
|
-
- If both `np_eta` and `np_xi` are `None`, the entire dataset is saved as a single netCDF4 file.
|
|
909
|
-
- The file is named based on the `filepath`, with `.nc` automatically appended.
|
|
910
|
-
|
|
911
|
-
2. **Partitioned Mode**:
|
|
912
|
-
- If either `np_eta` or `np_xi` is specified, the dataset is partitioned into spatial tiles along the `eta` and `xi` axes.
|
|
913
|
-
- 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"`).
|
|
914
|
-
|
|
915
|
-
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.
|
|
916
919
|
|
|
917
920
|
Parameters
|
|
918
921
|
----------
|
|
919
922
|
filepath : Union[str, Path]
|
|
920
|
-
The base path and filename for the output
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
The number of partitions along the `eta` direction. If `None`, no spatial partitioning is performed.
|
|
925
|
-
np_xi : int, optional
|
|
926
|
-
The number of partitions along the `xi` direction. If `None`, no spatial partitioning is performed.
|
|
927
|
-
group: bool, optional
|
|
928
|
-
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`.
|
|
929
927
|
|
|
930
928
|
Returns
|
|
931
929
|
-------
|
|
@@ -940,12 +938,6 @@ class BoundaryForcing:
|
|
|
940
938
|
if filepath.suffix == ".nc":
|
|
941
939
|
filepath = filepath.with_suffix("")
|
|
942
940
|
|
|
943
|
-
if self.use_dask:
|
|
944
|
-
from dask.diagnostics import ProgressBar
|
|
945
|
-
|
|
946
|
-
with ProgressBar():
|
|
947
|
-
self.ds.load()
|
|
948
|
-
|
|
949
941
|
if group:
|
|
950
942
|
dataset_list, output_filenames = group_dataset(self.ds, str(filepath))
|
|
951
943
|
else:
|
|
@@ -953,7 +945,7 @@ class BoundaryForcing:
|
|
|
953
945
|
output_filenames = [str(filepath)]
|
|
954
946
|
|
|
955
947
|
saved_filenames = save_datasets(
|
|
956
|
-
dataset_list, output_filenames,
|
|
948
|
+
dataset_list, output_filenames, use_dask=self.use_dask
|
|
957
949
|
)
|
|
958
950
|
|
|
959
951
|
return saved_filenames
|
|
@@ -1006,19 +998,19 @@ class BoundaryForcing:
|
|
|
1006
998
|
)
|
|
1007
999
|
|
|
1008
1000
|
|
|
1009
|
-
def apply_1d_horizontal_fill(
|
|
1010
|
-
"""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.
|
|
1011
1004
|
|
|
1012
1005
|
Parameters
|
|
1013
1006
|
----------
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
`xarray.DataArray`.
|
|
1007
|
+
data_array : xarray.DataArray
|
|
1008
|
+
The data array to be updated.
|
|
1017
1009
|
|
|
1018
1010
|
Returns
|
|
1019
1011
|
-------
|
|
1020
|
-
|
|
1021
|
-
The updated
|
|
1012
|
+
xarray.DataArray
|
|
1013
|
+
The updated data array with NaN values filled.
|
|
1022
1014
|
|
|
1023
1015
|
Raises
|
|
1024
1016
|
------
|
|
@@ -1027,29 +1019,22 @@ def apply_1d_horizontal_fill(processed_fields: dict) -> dict:
|
|
|
1027
1019
|
"""
|
|
1028
1020
|
|
|
1029
1021
|
horizontal_dims = ["eta_rho", "eta_v", "xi_rho", "xi_u"]
|
|
1022
|
+
selected_horizontal_dim = None
|
|
1030
1023
|
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
)
|
|
1040
|
-
selected_horizontal_dim = dim
|
|
1041
|
-
|
|
1042
|
-
if selected_horizontal_dim is None:
|
|
1043
|
-
raise ValueError(
|
|
1044
|
-
f"No valid horizontal dimension found for variable '{var_name}'."
|
|
1045
|
-
)
|
|
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
|
|
1046
1032
|
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
)
|
|
1051
|
-
processed_fields[var_name] = one_dim_fill(
|
|
1052
|
-
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}'."
|
|
1053
1036
|
)
|
|
1054
1037
|
|
|
1055
|
-
|
|
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")
|