roms-tools 2.6.0__py3-none-any.whl → 2.6.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- roms_tools/analysis/roms_output.py +218 -10
- roms_tools/analysis/utils.py +41 -9
- roms_tools/regrid.py +9 -2
- roms_tools/setup/river_forcing.py +5 -5
- roms_tools/tests/test_analysis/test_roms_output.py +55 -0
- roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/.zmetadata +2 -2
- roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/.zmetadata +2 -2
- roms_tools/tests/test_setup/test_river_forcing.py +3 -3
- roms_tools/tests/test_vertical_coordinate.py +73 -0
- roms_tools/vertical_coordinate.py +6 -0
- {roms_tools-2.6.0.dist-info → roms_tools-2.6.1.dist-info}/METADATA +1 -1
- {roms_tools-2.6.0.dist-info → roms_tools-2.6.1.dist-info}/RECORD +21 -20
- /roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/{river_location → river_flux}/.zarray +0 -0
- /roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/{river_location → river_flux}/.zattrs +0 -0
- /roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/{river_location → river_flux}/0.0 +0 -0
- /roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/{river_location → river_flux}/.zarray +0 -0
- /roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/{river_location → river_flux}/.zattrs +0 -0
- /roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/{river_location → river_flux}/0.0 +0 -0
- {roms_tools-2.6.0.dist-info → roms_tools-2.6.1.dist-info}/WHEEL +0 -0
- {roms_tools-2.6.0.dist-info → roms_tools-2.6.1.dist-info}/licenses/LICENSE +0 -0
- {roms_tools-2.6.0.dist-info → roms_tools-2.6.1.dist-info}/top_level.txt +0 -0
|
@@ -9,11 +9,13 @@ from typing import Union, Optional
|
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
import re
|
|
11
11
|
import logging
|
|
12
|
+
import warnings
|
|
12
13
|
from datetime import datetime, timedelta
|
|
13
14
|
from roms_tools import Grid
|
|
14
15
|
from roms_tools.vertical_coordinate import (
|
|
15
16
|
compute_depth_coordinates,
|
|
16
17
|
)
|
|
18
|
+
from roms_tools.utils import interpolate_from_rho_to_u, interpolate_from_rho_to_v
|
|
17
19
|
from roms_tools.analysis.utils import _validate_plot_inputs, _generate_coordinate_range
|
|
18
20
|
|
|
19
21
|
|
|
@@ -415,6 +417,145 @@ class ROMSOutput:
|
|
|
415
417
|
if save_path:
|
|
416
418
|
plt.savefig(save_path, dpi=300, bbox_inches="tight")
|
|
417
419
|
|
|
420
|
+
def regrid(self, var_names=None, horizontal_resolution=None, depth_levels=None):
|
|
421
|
+
"""Regrid the dataset both horizontally and vertically.
|
|
422
|
+
|
|
423
|
+
This method selects the specified variables, interpolates them onto a lat-lon-z horizontal grid. The horizontal target resolution and vertical target depth levels are either specified or inferred dynamically.
|
|
424
|
+
|
|
425
|
+
Parameters
|
|
426
|
+
----------
|
|
427
|
+
var_names : list of str, optional
|
|
428
|
+
List of variable names to be regridded. If None, all variables in the dataset
|
|
429
|
+
are used.
|
|
430
|
+
horizontal_resolution : float, optional
|
|
431
|
+
Target horizontal resolution in degrees. If None, the nominal horizontal resolution is inferred from the grid.
|
|
432
|
+
depth_levels : xarray.DataArray, numpy.ndarray, list, optional
|
|
433
|
+
Target depth levels. If None, depth levels are determined dynamically.
|
|
434
|
+
If provided as a list or numpy array, it is safely converted to an `xarray.DataArray`.
|
|
435
|
+
|
|
436
|
+
Returns
|
|
437
|
+
-------
|
|
438
|
+
xarray.Dataset
|
|
439
|
+
The regridded dataset.
|
|
440
|
+
"""
|
|
441
|
+
|
|
442
|
+
if var_names is None:
|
|
443
|
+
var_names = list(self.ds.data_vars)
|
|
444
|
+
|
|
445
|
+
# Check that all var_names exist in self.ds
|
|
446
|
+
missing_vars = [var for var in var_names if var not in self.ds.data_vars]
|
|
447
|
+
if missing_vars:
|
|
448
|
+
raise ValueError(
|
|
449
|
+
f"The following variables are not found in the dataset: {', '.join(missing_vars)}"
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
# Retain only the variables in var_names and drop others
|
|
453
|
+
ds = self.ds[var_names]
|
|
454
|
+
|
|
455
|
+
# Prepare lateral regrid
|
|
456
|
+
lat_deg = self.grid.ds["lat_rho"]
|
|
457
|
+
lon_deg = self.grid.ds["lon_rho"]
|
|
458
|
+
if self.grid.straddle:
|
|
459
|
+
lon_deg = xr.where(lon_deg > 180, lon_deg - 360, lon_deg)
|
|
460
|
+
|
|
461
|
+
if horizontal_resolution is None:
|
|
462
|
+
horizontal_resolution = self._infer_nominal_horizontal_resolution()
|
|
463
|
+
lons = _generate_coordinate_range(
|
|
464
|
+
lon_deg.min().values, lon_deg.max().values, horizontal_resolution
|
|
465
|
+
)
|
|
466
|
+
lons = xr.DataArray(lons, dims=["lon"], attrs={"units": "°E"})
|
|
467
|
+
lats = _generate_coordinate_range(
|
|
468
|
+
lat_deg.min().values, lat_deg.max().values, horizontal_resolution
|
|
469
|
+
)
|
|
470
|
+
lats = xr.DataArray(lats, dims=["lat"], attrs={"units": "°N"})
|
|
471
|
+
target_coords = {"lat": lats, "lon": lons}
|
|
472
|
+
|
|
473
|
+
# Prepare vertical regrid
|
|
474
|
+
if depth_levels is None:
|
|
475
|
+
depth_levels, _ = self._compute_exponential_depth_levels()
|
|
476
|
+
|
|
477
|
+
# Ensure depth_levels is an xarray.DataArray
|
|
478
|
+
if not isinstance(depth_levels, xr.DataArray):
|
|
479
|
+
depth_levels = xr.DataArray(
|
|
480
|
+
np.asarray(depth_levels),
|
|
481
|
+
dims=["depth"],
|
|
482
|
+
attrs={"long_name": "Depth", "units": "m"},
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
depth_levels = depth_levels.astype(np.float32)
|
|
486
|
+
|
|
487
|
+
# Initialize list to hold regridded datasets
|
|
488
|
+
regridded_datasets = []
|
|
489
|
+
|
|
490
|
+
for loc, dims in [
|
|
491
|
+
("rho", ("eta_rho", "xi_rho")),
|
|
492
|
+
("u", ("eta_rho", "xi_u")),
|
|
493
|
+
("v", ("eta_v", "xi_rho")),
|
|
494
|
+
]:
|
|
495
|
+
var_names_loc = [
|
|
496
|
+
var_name
|
|
497
|
+
for var_name in var_names
|
|
498
|
+
if all(dim in ds[var_name].dims for dim in dims)
|
|
499
|
+
]
|
|
500
|
+
if var_names_loc:
|
|
501
|
+
ds_loc = (
|
|
502
|
+
ds[var_names_loc]
|
|
503
|
+
.rename({f"lat_{loc}": "lat", f"lon_{loc}": "lon"})
|
|
504
|
+
.where(self.grid.ds[f"mask_{loc}"])
|
|
505
|
+
)
|
|
506
|
+
self._get_depth_coordinates(depth_type="layer", locations=[loc])
|
|
507
|
+
layer_depth_loc = self.ds_depth_coords[f"layer_depth_{loc}"]
|
|
508
|
+
h_loc = self.grid.ds.h
|
|
509
|
+
if loc == "u":
|
|
510
|
+
h_loc = interpolate_from_rho_to_u(h_loc)
|
|
511
|
+
elif loc == "v":
|
|
512
|
+
h_loc = interpolate_from_rho_to_v(h_loc)
|
|
513
|
+
|
|
514
|
+
# Exclude the horizontal boundary cells since diagnostic variables may contain zeros there
|
|
515
|
+
ds_loc = ds_loc.isel({dims[0]: slice(1, -1), dims[1]: slice(1, -1)})
|
|
516
|
+
layer_depth_loc = layer_depth_loc.isel(
|
|
517
|
+
{dims[0]: slice(1, -1), dims[1]: slice(1, -1)}
|
|
518
|
+
)
|
|
519
|
+
h_loc = h_loc.isel({dims[0]: slice(1, -1), dims[1]: slice(1, -1)})
|
|
520
|
+
|
|
521
|
+
# Lateral regridding
|
|
522
|
+
lateral_regrid = LateralRegridFromROMS(ds_loc, target_coords)
|
|
523
|
+
ds_loc = lateral_regrid.apply(ds_loc)
|
|
524
|
+
layer_depth_loc = lateral_regrid.apply(layer_depth_loc)
|
|
525
|
+
h_loc = lateral_regrid.apply(h_loc)
|
|
526
|
+
# Vertical regridding
|
|
527
|
+
vertical_regrid = VerticalRegridFromROMS(ds_loc)
|
|
528
|
+
for var_name in var_names_loc:
|
|
529
|
+
if "s_rho" in ds_loc[var_name].dims:
|
|
530
|
+
attrs = ds_loc[var_name].attrs
|
|
531
|
+
regridded = vertical_regrid.apply(
|
|
532
|
+
ds_loc[var_name],
|
|
533
|
+
layer_depth_loc,
|
|
534
|
+
depth_levels,
|
|
535
|
+
mask_edges=False,
|
|
536
|
+
)
|
|
537
|
+
regridded = regridded.where(regridded.depth < h_loc)
|
|
538
|
+
ds_loc[var_name] = regridded
|
|
539
|
+
ds_loc[var_name].attrs = attrs
|
|
540
|
+
|
|
541
|
+
ds_loc = ds_loc.assign_coords({"depth": depth_levels})
|
|
542
|
+
|
|
543
|
+
# Collect regridded dataset for merging
|
|
544
|
+
regridded_datasets.append(ds_loc)
|
|
545
|
+
|
|
546
|
+
# Merge all regridded datasets
|
|
547
|
+
if regridded_datasets:
|
|
548
|
+
ds = xr.merge(regridded_datasets)
|
|
549
|
+
|
|
550
|
+
with warnings.catch_warnings():
|
|
551
|
+
warnings.filterwarnings("ignore", category=UserWarning)
|
|
552
|
+
ds = ds.rename({"abs_time": "time"}).set_index(time="time")
|
|
553
|
+
ds["time"].attrs = {"long_name": "Time"}
|
|
554
|
+
ds["lon"].attrs = {"long_name": "Longitude", "units": "Degrees East"}
|
|
555
|
+
ds["lat"].attrs = {"long_name": "Latitude", "units": "Degrees North"}
|
|
556
|
+
|
|
557
|
+
return ds
|
|
558
|
+
|
|
418
559
|
def _get_depth_coordinates(self, depth_type="layer", locations=["rho"]):
|
|
419
560
|
"""Ensure depth coordinates are stored for a given location and depth type.
|
|
420
561
|
|
|
@@ -460,9 +601,11 @@ class ROMSOutput:
|
|
|
460
601
|
zeta = 0
|
|
461
602
|
|
|
462
603
|
for location in locations:
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
604
|
+
var_name = f"{depth_type}_depth_{location}"
|
|
605
|
+
if var_name not in self.ds_depth_coords:
|
|
606
|
+
self.ds_depth_coords[var_name] = compute_depth_coordinates(
|
|
607
|
+
self.grid.ds, zeta, depth_type, location
|
|
608
|
+
)
|
|
466
609
|
|
|
467
610
|
def _load_model_output(self) -> xr.Dataset:
|
|
468
611
|
"""Load the model output."""
|
|
@@ -630,24 +773,40 @@ class ROMSOutput:
|
|
|
630
773
|
return ds
|
|
631
774
|
|
|
632
775
|
def _add_lat_lon_coords(self, ds: xr.Dataset) -> xr.Dataset:
|
|
633
|
-
"""Add latitude and longitude coordinates to the dataset.
|
|
776
|
+
"""Add latitude and longitude coordinates to the dataset based on the grid.
|
|
634
777
|
|
|
635
|
-
|
|
778
|
+
This method assigns latitude and longitude coordinates from the grid to the dataset.
|
|
779
|
+
It always adds the "lat_rho" and "lon_rho" coordinates. If the dataset contains the
|
|
780
|
+
"xi_u" or "eta_v" dimensions, it also adds the corresponding "lat_u", "lon_u",
|
|
781
|
+
"lat_v", and "lon_v" coordinates.
|
|
636
782
|
|
|
637
783
|
Parameters
|
|
638
784
|
----------
|
|
639
785
|
ds : xarray.Dataset
|
|
640
|
-
|
|
786
|
+
Input dataset to which latitude and longitude coordinates will be added.
|
|
641
787
|
|
|
642
788
|
Returns
|
|
643
789
|
-------
|
|
644
790
|
xarray.Dataset
|
|
645
|
-
|
|
791
|
+
Updated dataset with the appropriate latitude and longitude coordinates
|
|
792
|
+
assigned to "rho", "u", and "v" points if applicable.
|
|
646
793
|
"""
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
794
|
+
coords_to_add = {
|
|
795
|
+
"lat_rho": self.grid.ds["lat_rho"],
|
|
796
|
+
"lon_rho": self.grid.ds["lon_rho"],
|
|
797
|
+
}
|
|
650
798
|
|
|
799
|
+
if "xi_u" in ds.dims:
|
|
800
|
+
coords_to_add.update(
|
|
801
|
+
{"lat_u": self.grid.ds["lat_u"], "lon_u": self.grid.ds["lon_u"]}
|
|
802
|
+
)
|
|
803
|
+
if "eta_v" in ds.dims:
|
|
804
|
+
coords_to_add.update(
|
|
805
|
+
{"lat_v": self.grid.ds["lat_v"], "lon_v": self.grid.ds["lon_v"]}
|
|
806
|
+
)
|
|
807
|
+
|
|
808
|
+
# Add all necessary coordinates in one go
|
|
809
|
+
ds = ds.assign_coords(coords_to_add)
|
|
651
810
|
return ds
|
|
652
811
|
|
|
653
812
|
def _infer_nominal_horizontal_resolution(self, lat=None):
|
|
@@ -693,3 +852,52 @@ class ROMSOutput:
|
|
|
693
852
|
resolution_in_degrees = resolution_in_m / (meters_per_degree * np.cos(lat_rad))
|
|
694
853
|
|
|
695
854
|
return resolution_in_degrees
|
|
855
|
+
|
|
856
|
+
def _compute_exponential_depth_levels(self, Nz=None, depth=None, h=None):
|
|
857
|
+
"""Compute vertical grid center and face depths using an exponential profile.
|
|
858
|
+
|
|
859
|
+
Parameters
|
|
860
|
+
----------
|
|
861
|
+
Nz : int, optional
|
|
862
|
+
Number of vertical levels. Defaults to `len(self.ds.s_rho)`.
|
|
863
|
+
|
|
864
|
+
depth : float, optional
|
|
865
|
+
Total depth of the domain. Defaults to `grid.ds.h.max().values`.
|
|
866
|
+
|
|
867
|
+
h : float, optional
|
|
868
|
+
Scaling parameter for the exponential profile. Defaults to `Nz / 4.5`.
|
|
869
|
+
|
|
870
|
+
Returns
|
|
871
|
+
-------
|
|
872
|
+
tuple of numpy.ndarray
|
|
873
|
+
Depth values at the vertical grid centers (`z_centers`) and grid faces (`z_faces`),
|
|
874
|
+
both rounded to two decimal places.
|
|
875
|
+
"""
|
|
876
|
+
if Nz is None:
|
|
877
|
+
Nz = len(self.ds.s_rho)
|
|
878
|
+
if depth is None:
|
|
879
|
+
depth = self.grid.ds.h.max().values
|
|
880
|
+
if h is None:
|
|
881
|
+
h = Nz / 4.5
|
|
882
|
+
|
|
883
|
+
k = np.arange(1, Nz + 2)
|
|
884
|
+
|
|
885
|
+
# Define the exponential profile function
|
|
886
|
+
def exponential_profile(k, Nz, h):
|
|
887
|
+
return np.exp(k / h)
|
|
888
|
+
|
|
889
|
+
z_faces = np.vectorize(exponential_profile)(k, Nz, h)
|
|
890
|
+
|
|
891
|
+
# Normalize
|
|
892
|
+
z_faces -= z_faces[0]
|
|
893
|
+
z_faces *= depth / z_faces[-1]
|
|
894
|
+
z_faces[0] = 0.0
|
|
895
|
+
|
|
896
|
+
# Calculate center depths (average between adjacent face depths)
|
|
897
|
+
z_centers = (z_faces[:-1] + z_faces[1:]) / 2
|
|
898
|
+
|
|
899
|
+
# Round both z_faces and z_centers to two decimal places
|
|
900
|
+
z_faces = np.round(z_faces, 2)
|
|
901
|
+
z_centers = np.round(z_centers, 2)
|
|
902
|
+
|
|
903
|
+
return z_centers, z_faces
|
roms_tools/analysis/utils.py
CHANGED
|
@@ -107,12 +107,13 @@ def _validate_plot_inputs(field, s, eta, xi, depth, lat, lon, include_boundary):
|
|
|
107
107
|
|
|
108
108
|
|
|
109
109
|
def _generate_coordinate_range(min, max, resolution):
|
|
110
|
-
"""Generate an array of target latitude or longitude
|
|
111
|
-
range
|
|
110
|
+
"""Generate an array of target coordinates (e.g., latitude or longitude) within a
|
|
111
|
+
specified range, with a resolution that is rounded to the nearest value of the form
|
|
112
|
+
`1/n` (or integer).
|
|
112
113
|
|
|
113
114
|
This method generates an array of target coordinates between the provided `min` and `max`
|
|
114
|
-
values,
|
|
115
|
-
|
|
115
|
+
values, ensuring that both `min` and `max` are included in the resulting range. The resolution
|
|
116
|
+
is rounded to the nearest fraction of the form `1/n` or an integer, based on the input.
|
|
116
117
|
|
|
117
118
|
Parameters
|
|
118
119
|
----------
|
|
@@ -123,15 +124,46 @@ def _generate_coordinate_range(min, max, resolution):
|
|
|
123
124
|
The maximum value (in degrees) of the coordinate range (inclusive).
|
|
124
125
|
|
|
125
126
|
resolution : float
|
|
126
|
-
The spacing (in degrees) between each coordinate in the array.
|
|
127
|
+
The spacing (in degrees) between each coordinate in the array. The resolution will
|
|
128
|
+
be rounded to the nearest value of the form `1/n` or an integer, depending on the size
|
|
129
|
+
of the resolution value.
|
|
127
130
|
|
|
128
131
|
Returns
|
|
129
132
|
-------
|
|
130
133
|
numpy.ndarray
|
|
131
|
-
An array of target coordinates generated from the specified range
|
|
134
|
+
An array of target coordinates generated from the specified range, with the resolution
|
|
135
|
+
rounded to a suitable fraction (e.g., `1/n`) or integer, depending on the input resolution.
|
|
132
136
|
"""
|
|
133
137
|
|
|
134
|
-
#
|
|
135
|
-
|
|
138
|
+
# Find the closest fraction of the form 1/n or integer to match the resolution
|
|
139
|
+
resolution_rounded = None
|
|
140
|
+
min_diff = float("inf") # Initialize the minimum difference as infinity
|
|
136
141
|
|
|
137
|
-
|
|
142
|
+
# Search for the best fraction or integer approximation to the resolution
|
|
143
|
+
for n in range(1, 1000): # Try fractions 1/n, where n ranges from 1 to 999
|
|
144
|
+
if resolution <= 1:
|
|
145
|
+
fraction = (
|
|
146
|
+
1 / n
|
|
147
|
+
) # For small resolutions (<= 1), consider fractions of the form 1/n
|
|
148
|
+
else:
|
|
149
|
+
fraction = n # For larger resolutions (>1), consider integers (n)
|
|
150
|
+
|
|
151
|
+
diff = abs(
|
|
152
|
+
fraction - resolution
|
|
153
|
+
) # Calculate the difference between the fraction and the resolution
|
|
154
|
+
|
|
155
|
+
if diff < min_diff: # If the current fraction is a better approximation
|
|
156
|
+
min_diff = diff
|
|
157
|
+
resolution_rounded = fraction # Update the best fraction (or integer) found
|
|
158
|
+
|
|
159
|
+
# Adjust the start and end of the range to include integer values
|
|
160
|
+
start_int = np.floor(min) # Round the minimum value down to the nearest integer
|
|
161
|
+
end_int = np.ceil(max) # Round the maximum value up to the nearest integer
|
|
162
|
+
|
|
163
|
+
# Generate the array of target coordinates, including both the min and max values
|
|
164
|
+
target = np.arange(start_int, end_int + resolution_rounded, resolution_rounded)
|
|
165
|
+
|
|
166
|
+
# Truncate any values that exceed max (including small floating point errors)
|
|
167
|
+
target = target[target <= end_int + 1e-10]
|
|
168
|
+
|
|
169
|
+
return target.astype(np.float32)
|
roms_tools/regrid.py
CHANGED
|
@@ -257,7 +257,7 @@ class VerticalRegridFromROMS:
|
|
|
257
257
|
"""
|
|
258
258
|
self.grid = xgcm.Grid(ds, coords={"s_rho": {"center": "s_rho"}}, periodic=False)
|
|
259
259
|
|
|
260
|
-
def apply(self, da, depth_coords, target_depth_levels):
|
|
260
|
+
def apply(self, da, depth_coords, target_depth_levels, mask_edges=True):
|
|
261
261
|
"""Applies vertical regridding from ROMS to the specified target depth levels.
|
|
262
262
|
|
|
263
263
|
This method transforms the input data array `da` from the ROMS vertical coordinate (`s_rho`)
|
|
@@ -275,6 +275,9 @@ class VerticalRegridFromROMS:
|
|
|
275
275
|
target_depth_levels : array-like
|
|
276
276
|
The target depth levels to which the input data `da` will be regridded.
|
|
277
277
|
|
|
278
|
+
mask_edges: bool, optional
|
|
279
|
+
If activated, target values outside the range of depth_coords are masked with nan. Defaults to True.
|
|
280
|
+
|
|
278
281
|
Returns
|
|
279
282
|
-------
|
|
280
283
|
xarray.DataArray
|
|
@@ -284,7 +287,11 @@ class VerticalRegridFromROMS:
|
|
|
284
287
|
with warnings.catch_warnings():
|
|
285
288
|
warnings.filterwarnings("ignore", category=FutureWarning, module="xgcm")
|
|
286
289
|
transformed = self.grid.transform(
|
|
287
|
-
da,
|
|
290
|
+
da,
|
|
291
|
+
"s_rho",
|
|
292
|
+
target_depth_levels,
|
|
293
|
+
target_data=depth_coords,
|
|
294
|
+
mask_edges=mask_edges,
|
|
288
295
|
)
|
|
289
296
|
|
|
290
297
|
return transformed
|
|
@@ -441,21 +441,21 @@ class RiverForcing:
|
|
|
441
441
|
return river_indices
|
|
442
442
|
|
|
443
443
|
def _write_indices_into_dataset(self, ds):
|
|
444
|
-
"""Adds river location indices to the dataset as the "
|
|
444
|
+
"""Adds river location indices to the dataset as the "river_flux" variable.
|
|
445
445
|
|
|
446
|
-
This method creates a new "
|
|
446
|
+
This method creates a new "river_flux" variable
|
|
447
447
|
using river station indices from `self.indices` and assigns it to the dataset.
|
|
448
448
|
The indices specify the river station locations in terms of eta_rho and xi_rho grid cell indices.
|
|
449
449
|
|
|
450
450
|
Parameters
|
|
451
451
|
----------
|
|
452
452
|
ds : xarray.Dataset
|
|
453
|
-
The dataset to which the "
|
|
453
|
+
The dataset to which the "river_flux" variable will be added.
|
|
454
454
|
|
|
455
455
|
Returns
|
|
456
456
|
-------
|
|
457
457
|
xarray.Dataset
|
|
458
|
-
The modified dataset with the "
|
|
458
|
+
The modified dataset with the "river_flux" variable added.
|
|
459
459
|
"""
|
|
460
460
|
|
|
461
461
|
river_locations = xr.zeros_like(self.grid.ds.h)
|
|
@@ -474,7 +474,7 @@ class RiverForcing:
|
|
|
474
474
|
|
|
475
475
|
river_locations.attrs["long_name"] = "River ID plus local volume fraction"
|
|
476
476
|
river_locations.attrs["units"] = "none"
|
|
477
|
-
ds["
|
|
477
|
+
ds["river_flux"] = river_locations
|
|
478
478
|
|
|
479
479
|
ds = ds.drop_vars(["lat_rho", "lon_rho"])
|
|
480
480
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
import xarray as xr
|
|
4
|
+
import numpy as np
|
|
4
5
|
import os
|
|
5
6
|
import logging
|
|
6
7
|
from datetime import datetime
|
|
@@ -486,3 +487,57 @@ def test_figure_gets_saved(roms_output_from_restart_file, tmp_path):
|
|
|
486
487
|
|
|
487
488
|
assert filename.exists()
|
|
488
489
|
filename.unlink()
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
@pytest.mark.skipif(xesmf is None, reason="xesmf required")
|
|
493
|
+
def test_regrid_all_variables(roms_output_from_restart_file):
|
|
494
|
+
ds_regridded = roms_output_from_restart_file.regrid()
|
|
495
|
+
assert isinstance(ds_regridded, xr.Dataset)
|
|
496
|
+
assert set(ds_regridded.data_vars).issubset(
|
|
497
|
+
set(roms_output_from_restart_file.ds.data_vars)
|
|
498
|
+
)
|
|
499
|
+
assert "lon" in ds_regridded.coords
|
|
500
|
+
assert "lat" in ds_regridded.coords
|
|
501
|
+
assert "depth" in ds_regridded.coords
|
|
502
|
+
assert "time" in ds_regridded.coords
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
@pytest.mark.skipif(xesmf is None, reason="xesmf required")
|
|
506
|
+
def test_regrid_specific_variables(roms_output_from_restart_file):
|
|
507
|
+
var_names = ["temp", "salt"]
|
|
508
|
+
ds_regridded = roms_output_from_restart_file.regrid(var_names=var_names)
|
|
509
|
+
assert isinstance(ds_regridded, xr.Dataset)
|
|
510
|
+
assert set(ds_regridded.data_vars) == set(var_names)
|
|
511
|
+
|
|
512
|
+
ds = roms_output_from_restart_file.regrid(var_names=[])
|
|
513
|
+
assert ds is None
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
@pytest.mark.skipif(xesmf is None, reason="xesmf required")
|
|
517
|
+
def test_regrid_missing_variable_raises_error(roms_output_from_restart_file):
|
|
518
|
+
with pytest.raises(
|
|
519
|
+
ValueError, match="The following variables are not found in the dataset"
|
|
520
|
+
):
|
|
521
|
+
roms_output_from_restart_file.regrid(var_names=["fake_variable"])
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
@pytest.mark.skipif(xesmf is None, reason="xesmf required")
|
|
525
|
+
def test_regrid_with_custom_horizontal_resolution(roms_output_from_restart_file):
|
|
526
|
+
ds_regridded = roms_output_from_restart_file.regrid(horizontal_resolution=0.1)
|
|
527
|
+
assert isinstance(ds_regridded, xr.Dataset)
|
|
528
|
+
assert "lon" in ds_regridded.coords
|
|
529
|
+
assert "lat" in ds_regridded.coords
|
|
530
|
+
|
|
531
|
+
assert np.allclose(ds_regridded.lon.diff(dim="lon"), 0.1, atol=1e-4)
|
|
532
|
+
assert np.allclose(ds_regridded.lat.diff(dim="lat"), 0.1, atol=1e-4)
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
@pytest.mark.skipif(xesmf is None, reason="xesmf required")
|
|
536
|
+
def test_regrid_with_custom_depth_levels(roms_output_from_restart_file):
|
|
537
|
+
depth_levels = xr.DataArray(
|
|
538
|
+
np.linspace(0, 500, 51), dims=["depth"], attrs={"units": "m"}
|
|
539
|
+
)
|
|
540
|
+
ds_regridded = roms_output_from_restart_file.regrid(depth_levels=depth_levels)
|
|
541
|
+
assert isinstance(ds_regridded, xr.Dataset)
|
|
542
|
+
assert "depth" in ds_regridded.coords
|
|
543
|
+
np.allclose(ds_regridded.depth, depth_levels, atol=0.0)
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
],
|
|
59
59
|
"long_name": "River ID (1-based Fortran indexing)"
|
|
60
60
|
},
|
|
61
|
-
"
|
|
61
|
+
"river_flux/.zarray": {
|
|
62
62
|
"chunks": [
|
|
63
63
|
20,
|
|
64
64
|
20
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
],
|
|
81
81
|
"zarr_format": 2
|
|
82
82
|
},
|
|
83
|
-
"
|
|
83
|
+
"river_flux/.zattrs": {
|
|
84
84
|
"_ARRAY_DIMENSIONS": [
|
|
85
85
|
"eta_rho",
|
|
86
86
|
"xi_rho"
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
],
|
|
87
87
|
"long_name": "River ID (1-based Fortran indexing)"
|
|
88
88
|
},
|
|
89
|
-
"
|
|
89
|
+
"river_flux/.zarray": {
|
|
90
90
|
"chunks": [
|
|
91
91
|
20,
|
|
92
92
|
20
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
],
|
|
109
109
|
"zarr_format": 2
|
|
110
110
|
},
|
|
111
|
-
"
|
|
111
|
+
"river_flux/.zattrs": {
|
|
112
112
|
"_ARRAY_DIMENSIONS": [
|
|
113
113
|
"eta_rho",
|
|
114
114
|
"xi_rho"
|
|
@@ -236,7 +236,7 @@ class TestRiverForcingGeneral:
|
|
|
236
236
|
for name in indices.keys():
|
|
237
237
|
for (eta_rho, xi_rho) in indices[name]:
|
|
238
238
|
assert coast[eta_rho, xi_rho]
|
|
239
|
-
assert river_forcing.ds["
|
|
239
|
+
assert river_forcing.ds["river_flux"][eta_rho, xi_rho] > 0
|
|
240
240
|
|
|
241
241
|
def test_missing_source_name(self, iceland_test_grid):
|
|
242
242
|
with pytest.raises(ValueError, match="`source` must include a 'name'."):
|
|
@@ -475,14 +475,14 @@ class TestRiverForcingWithPrescribedIndices:
|
|
|
475
475
|
|
|
476
476
|
# check that all values are integers for single cell rivers
|
|
477
477
|
non_zero_values = river_forcing_with_prescribed_single_cell_indices.ds[
|
|
478
|
-
"
|
|
478
|
+
"river_flux"
|
|
479
479
|
]
|
|
480
480
|
is_integer = non_zero_values == np.floor(non_zero_values)
|
|
481
481
|
assert (is_integer).all()
|
|
482
482
|
|
|
483
483
|
# check that not all values are integers for multi cell rivers
|
|
484
484
|
non_zero_values = river_forcing_with_prescribed_multi_cell_indices.ds[
|
|
485
|
-
"
|
|
485
|
+
"river_flux"
|
|
486
486
|
]
|
|
487
487
|
is_integer = non_zero_values == np.floor(non_zero_values)
|
|
488
488
|
assert not (is_integer).all()
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import numpy as np
|
|
3
|
+
import xarray as xr
|
|
4
|
+
|
|
5
|
+
from roms_tools.vertical_coordinate import (
|
|
6
|
+
compute_cs,
|
|
7
|
+
sigma_stretch,
|
|
8
|
+
compute_depth,
|
|
9
|
+
compute_depth_coordinates,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_compute_cs():
|
|
14
|
+
sigma = np.linspace(-1, 0, 10)
|
|
15
|
+
theta_s, theta_b = 5, 2
|
|
16
|
+
cs = compute_cs(sigma, theta_s, theta_b)
|
|
17
|
+
assert cs.shape == sigma.shape
|
|
18
|
+
assert np.all(cs <= 0) and np.all(cs >= -1)
|
|
19
|
+
|
|
20
|
+
with pytest.raises(ValueError, match="theta_s must be between 0 and 10"):
|
|
21
|
+
compute_cs(sigma, 15, 2)
|
|
22
|
+
|
|
23
|
+
with pytest.raises(ValueError, match="theta_b must be between 0 and 4"):
|
|
24
|
+
compute_cs(sigma, 5, 5)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_sigma_stretch():
|
|
28
|
+
theta_s, theta_b, N = 5, 2, 10
|
|
29
|
+
cs, sigma = sigma_stretch(theta_s, theta_b, N, "r")
|
|
30
|
+
assert cs.shape == sigma.shape
|
|
31
|
+
assert isinstance(cs, xr.DataArray)
|
|
32
|
+
assert isinstance(sigma, xr.DataArray)
|
|
33
|
+
|
|
34
|
+
with pytest.raises(
|
|
35
|
+
ValueError,
|
|
36
|
+
match="Type must be either 'w' for vertical velocity points or 'r' for rho-points.",
|
|
37
|
+
):
|
|
38
|
+
sigma_stretch(theta_s, theta_b, N, "invalid")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_compute_depth():
|
|
42
|
+
zeta = xr.DataArray(0.5)
|
|
43
|
+
h = xr.DataArray(10.0)
|
|
44
|
+
hc = 5.0
|
|
45
|
+
cs = xr.DataArray(np.linspace(-1, 0, 10), dims="s_rho")
|
|
46
|
+
sigma = xr.DataArray(np.linspace(-1, 0, 10), dims="s_rho")
|
|
47
|
+
|
|
48
|
+
depth = compute_depth(zeta, h, hc, cs, sigma)
|
|
49
|
+
assert depth.shape == sigma.shape
|
|
50
|
+
assert isinstance(depth, xr.DataArray)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_compute_depth_coordinates():
|
|
54
|
+
grid_ds = xr.Dataset(
|
|
55
|
+
{
|
|
56
|
+
"h": xr.DataArray([[10, 20], [30, 40]], dims=("eta_rho", "xi_rho")),
|
|
57
|
+
"Cs_r": xr.DataArray(np.linspace(-1, 0, 10), dims="s_rho"),
|
|
58
|
+
"sigma_r": xr.DataArray(np.linspace(-1, 0, 10), dims="s_rho"),
|
|
59
|
+
"Cs_w": xr.DataArray(np.linspace(-1, 0, 11), dims="s_w"),
|
|
60
|
+
"sigma_w": xr.DataArray(np.linspace(-1, 0, 11), dims="s_w"),
|
|
61
|
+
},
|
|
62
|
+
attrs={"hc": 5.0},
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
depth = compute_depth_coordinates(grid_ds, depth_type="layer", location="rho")
|
|
66
|
+
assert isinstance(depth, xr.DataArray)
|
|
67
|
+
assert "eta_rho" in depth.dims and "xi_rho" in depth.dims
|
|
68
|
+
|
|
69
|
+
with pytest.raises(ValueError, match="Invalid depth_type"):
|
|
70
|
+
compute_depth_coordinates(grid_ds, depth_type="invalid")
|
|
71
|
+
|
|
72
|
+
with pytest.raises(ValueError, match="Invalid location"):
|
|
73
|
+
compute_depth_coordinates(grid_ds, location="invalid")
|
|
@@ -171,6 +171,12 @@ def compute_depth_coordinates(
|
|
|
171
171
|
- Spatial slicing (`eta`, `xi`) is performed before depth computation to optimize efficiency.
|
|
172
172
|
- Depth calculations rely on the ROMS vertical stretching curves (`Cs`) and sigma-layers.
|
|
173
173
|
"""
|
|
174
|
+
# Validate location
|
|
175
|
+
valid_locations = {"rho", "u", "v"}
|
|
176
|
+
if location not in valid_locations:
|
|
177
|
+
raise ValueError(
|
|
178
|
+
f"Invalid location: {location}. Must be one of {valid_locations}."
|
|
179
|
+
)
|
|
174
180
|
|
|
175
181
|
# Select the appropriate depth computation parameters
|
|
176
182
|
if depth_type == "layer":
|
|
@@ -3,11 +3,11 @@ ci/environment.yml,sha256=Ehxy6nYiVQXoS7EGlmNm2G0ZPHg6VFBGY1IflApIhIY,207
|
|
|
3
3
|
roms_tools/__init__.py,sha256=jRghiteCoPjJvJjkFI36ocGyqzcTN5m-5eCa_DNQ9Dw,988
|
|
4
4
|
roms_tools/download.py,sha256=W6S_DFVJDXat2w9MfyyHyushrswbpUI2hxegSuua1XE,6248
|
|
5
5
|
roms_tools/plot.py,sha256=33ft1wN0kc_vIvyy_sIoY-nc0k4THXWLc_k7wEavNq8,17578
|
|
6
|
-
roms_tools/regrid.py,sha256=
|
|
6
|
+
roms_tools/regrid.py,sha256=av9fROSNxlDeczOB544zUjFRUTiUKO42wbfZ90mpuD0,10476
|
|
7
7
|
roms_tools/utils.py,sha256=eveBkWuDsXNJADFMWFgRMHdbXkZlTyVK9dN2YAnjYJ4,13323
|
|
8
|
-
roms_tools/vertical_coordinate.py,sha256=
|
|
9
|
-
roms_tools/analysis/roms_output.py,sha256=
|
|
10
|
-
roms_tools/analysis/utils.py,sha256=
|
|
8
|
+
roms_tools/vertical_coordinate.py,sha256=uIxZl7rwY-fSCqXWhm6TXrOsLK4pMOMXOZB8VuP9xwg,7253
|
|
9
|
+
roms_tools/analysis/roms_output.py,sha256=DUSmT2YRoqd1fowuPnoCxdukuO_NC3ONphMWsu43_nE,36560
|
|
10
|
+
roms_tools/analysis/utils.py,sha256=K1Z1VyZUWKth1GMT8dumE1uUVcxrcO6rAm0Yfh8DtIg,7207
|
|
11
11
|
roms_tools/setup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
12
|
roms_tools/setup/boundary_forcing.py,sha256=LpNBurvcUhiemmubglI5aEgbOuPpjCDBj3C8XmRV60k,43609
|
|
13
13
|
roms_tools/setup/datasets.py,sha256=WDEkMbLrzmdHxU5G9x_ECJASYVpmPa2i3oXWg9Rvxc4,84822
|
|
@@ -16,14 +16,15 @@ roms_tools/setup/grid.py,sha256=DWNPXoG2D76u6QMjOf_f5byvzGXT8gFOfiantUvnVjg,4940
|
|
|
16
16
|
roms_tools/setup/initial_conditions.py,sha256=uVLkqpo1l1uw_LNjugypNVX2Tio5Hmi4txJoGC7P8G8,36012
|
|
17
17
|
roms_tools/setup/mask.py,sha256=IK2SrVnMJwZjE4jNFtzMQhp1c5c8SUO45OIpCIrNSis,3413
|
|
18
18
|
roms_tools/setup/nesting.py,sha256=j8l2zVCfhxNNtN4ZiSpipSlPXYmaoZWIPw6zImafsuk,26511
|
|
19
|
-
roms_tools/setup/river_forcing.py,sha256=
|
|
19
|
+
roms_tools/setup/river_forcing.py,sha256=G-WoajtMloqS9gzt3aERSC8NObvC5ODi8XD_Q0jhYFU,32804
|
|
20
20
|
roms_tools/setup/surface_forcing.py,sha256=HwIiA0qS64gBcq2ktqZx8ZhTIFRjweRZZe3haMTjCOg,23980
|
|
21
21
|
roms_tools/setup/tides.py,sha256=2eFzQMVnlcHsG83AEhTUfGgQbO0CqI_pDb8YeZ5APTU,27088
|
|
22
22
|
roms_tools/setup/topography.py,sha256=s1dSF0ZWCNdrZ25yv-pUcCgufxRmGgy3yr_VhdRM7LU,14126
|
|
23
23
|
roms_tools/setup/utils.py,sha256=S9_alsNEORLjEA74LVbgaxhWyVKa-PqFSPida9MQNxw,40911
|
|
24
24
|
roms_tools/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
25
|
roms_tools/tests/test_regrid.py,sha256=-wzZ31BkUdSn81jq5NF1wnuaBRfd0aiYgQZEv5E_h9w,4682
|
|
26
|
-
roms_tools/tests/
|
|
26
|
+
roms_tools/tests/test_vertical_coordinate.py,sha256=4D2jruuxBwUYk1fSeaJKICgSjzpixhjd03Y4lyivMkQ,2348
|
|
27
|
+
roms_tools/tests/test_analysis/test_roms_output.py,sha256=EtxS9b5JHoREch99jbhs4QDg_3zmXDmVzXplbVrFocA,18811
|
|
27
28
|
roms_tools/tests/test_setup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
29
|
roms_tools/tests/test_setup/test_boundary_forcing.py,sha256=gET86PiGnMBejTBe69-inXpaEZL8YH4FMqyT_b8V-Zk,21183
|
|
29
30
|
roms_tools/tests/test_setup/test_datasets.py,sha256=EInjmCLFNWJoO8nIQJhDtmFmMFmjcrr5L_5vJYm-qno,16435
|
|
@@ -31,7 +32,7 @@ roms_tools/tests/test_setup/test_fill.py,sha256=gDHuM58d3ECQE317ZntChYt4hWCfo4eV
|
|
|
31
32
|
roms_tools/tests/test_setup/test_grid.py,sha256=e1S8TYt21TVczaEfyeYoAU-qxUhAgiugkHA3EDAGtIQ,14095
|
|
32
33
|
roms_tools/tests/test_setup/test_initial_conditions.py,sha256=4LiULWmuktHoOty94asiFXgGNysag2okiosxG00om9M,15697
|
|
33
34
|
roms_tools/tests/test_setup/test_nesting.py,sha256=WUhyP9mlMYwAJQgbgqjEU1zOyb8QD3XMgSZvHR_9LE8,18764
|
|
34
|
-
roms_tools/tests/test_setup/test_river_forcing.py,sha256=
|
|
35
|
+
roms_tools/tests/test_setup/test_river_forcing.py,sha256=on3j7pSMESVlFHKXNVBL4H7qqMc_DmMupAtDcUmZ014,23827
|
|
35
36
|
roms_tools/tests/test_setup/test_surface_forcing.py,sha256=uZAZLbX6zK_e5CYOVZvZLp_i28A1o372naeaOeF83vE,27626
|
|
36
37
|
roms_tools/tests/test_setup/test_tides.py,sha256=ACFXytda3Am984QMKGxtML00KPX1LvLWlSL0FpZTqyc,8085
|
|
37
38
|
roms_tools/tests/test_setup/test_topography.py,sha256=EAF-zCHfo6XnXQfBTrSZLuZDVzegWHHlrf9oBmvY0hM,4963
|
|
@@ -948,16 +949,16 @@ roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatolo
|
|
|
948
949
|
roms_tools/tests/test_setup/test_data/initial_conditions_with_bgc_from_climatology.zarr/zooC/0.0.0.0,sha256=t8ItMb1eQSD9ZijnctYDpcZSvDigD2-5YYGHMEMmnHM,194
|
|
949
950
|
roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/.zattrs,sha256=RBNvo1WzZ4oRRq0W9-hknpT7T8If536DEMBg9hyq_4o,2
|
|
950
951
|
roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/.zgroup,sha256=I4N0bme0vMJ2Kz8QDwbD-i1fFJq1qOXaXTNSFGSgGVk,24
|
|
951
|
-
roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/.zmetadata,sha256=
|
|
952
|
+
roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/.zmetadata,sha256=yUONMD7prhzTzs4Haer6nX06txMntOJilUf6iaBMEJU,6133
|
|
952
953
|
roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/abs_time/.zarray,sha256=AVrYaEeCVeJWUi2FbvisrHwjFyifr4Vt3Yag7Q-n33g,312
|
|
953
954
|
roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/abs_time/.zattrs,sha256=0hG3RGr8ZCkuViKEvgrkYvnehkAk3wndb4vesT1jYeI,177
|
|
954
955
|
roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/abs_time/0,sha256=PvO30V_gimagXglEZjQSDgwcOrEIoWmt9JQbY4toiic,48
|
|
955
956
|
roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/nriver/.zarray,sha256=zti2U0p_i_zbPjPqTErqhxNnmnUtoQPZ_4r1-9yDH7Y,312
|
|
956
957
|
roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/nriver/.zattrs,sha256=Ng-_DbPNix2DMxTo-GGKyyBnH6mo3teb2SuqtQWmokE,109
|
|
957
958
|
roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/nriver/0,sha256=irfTfL897AhvCrDL98Vo7ZX6zJZhKFiktdZNWOdDlSU,64
|
|
958
|
-
roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/
|
|
959
|
-
roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/
|
|
960
|
-
roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/
|
|
959
|
+
roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_flux/.zarray,sha256=jYVuViCHVLNS6VB-K_4O32zkhyPcFnoUdLM8pA1nGXk,339
|
|
960
|
+
roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_flux/.zattrs,sha256=Ulaw9zPnPcNer1cdhavwQtQ_tUPPTDzTrQGcHP8u9y4,149
|
|
961
|
+
roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_flux/0.0,sha256=_gdp3GhMsDlFGIuL7bmmlXcm1aD6AFF1fAmIjanpwWI,196
|
|
961
962
|
roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_name/.zarray,sha256=IiztAgS5VBT0bsRnDObags8I7F_tyQbMajEszHXfHb8,364
|
|
962
963
|
roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_name/.zattrs,sha256=EPrut8rW6fC_MGEIOeqFacMyRlxf36MZsFCgP-nZ9d4,84
|
|
963
964
|
roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/river_name/0,sha256=fuD1GoUrOX7tb_9WSj42rwDVUExgniEIJ9XrblUos0E,95
|
|
@@ -975,7 +976,7 @@ roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/tracer_n
|
|
|
975
976
|
roms_tools/tests/test_setup/test_data/river_forcing_no_climatology.zarr/tracer_name/0,sha256=m5RTy6Ka6ScsKdL_pma6qpevroqCf8LJzuYXCFl9Mds,48
|
|
976
977
|
roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/.zattrs,sha256=jFoQJqWC46fw5uLFNg8B7WYcGSGnc9XJAWTk3ZKejOY,29
|
|
977
978
|
roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/.zgroup,sha256=I4N0bme0vMJ2Kz8QDwbD-i1fFJq1qOXaXTNSFGSgGVk,24
|
|
978
|
-
roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/.zmetadata,sha256=
|
|
979
|
+
roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/.zmetadata,sha256=zKm4eTj9Anuc0qm_Ip8PXeBofwHA5cnl9J1CQyVlT7Q,6893
|
|
979
980
|
roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/abs_time/.zarray,sha256=bRSAng1vaRGTtFKNWhv1GcxwVKYhRsJnjbtncEbdWNA,314
|
|
980
981
|
roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/abs_time/.zattrs,sha256=gDDHVjAFysUhH72DUxR7UwPLMOXFxalOA1fCWa6ImCQ,177
|
|
981
982
|
roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/abs_time/0,sha256=SZIHK8deA_aE9wtgz1iLQ8ybjbVQK4Ym1FEsVryztu0,112
|
|
@@ -985,9 +986,9 @@ roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/month/0,sha256
|
|
|
985
986
|
roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/nriver/.zarray,sha256=zti2U0p_i_zbPjPqTErqhxNnmnUtoQPZ_4r1-9yDH7Y,312
|
|
986
987
|
roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/nriver/.zattrs,sha256=Ng-_DbPNix2DMxTo-GGKyyBnH6mo3teb2SuqtQWmokE,109
|
|
987
988
|
roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/nriver/0,sha256=irfTfL897AhvCrDL98Vo7ZX6zJZhKFiktdZNWOdDlSU,64
|
|
988
|
-
roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/
|
|
989
|
-
roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/
|
|
990
|
-
roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/
|
|
989
|
+
roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_flux/.zarray,sha256=jYVuViCHVLNS6VB-K_4O32zkhyPcFnoUdLM8pA1nGXk,339
|
|
990
|
+
roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_flux/.zattrs,sha256=Ulaw9zPnPcNer1cdhavwQtQ_tUPPTDzTrQGcHP8u9y4,149
|
|
991
|
+
roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_flux/0.0,sha256=_gdp3GhMsDlFGIuL7bmmlXcm1aD6AFF1fAmIjanpwWI,196
|
|
991
992
|
roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_name/.zarray,sha256=IiztAgS5VBT0bsRnDObags8I7F_tyQbMajEszHXfHb8,364
|
|
992
993
|
roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_name/.zattrs,sha256=EPrut8rW6fC_MGEIOeqFacMyRlxf36MZsFCgP-nZ9d4,84
|
|
993
994
|
roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/river_name/0,sha256=fuD1GoUrOX7tb_9WSj42rwDVUExgniEIJ9XrblUos0E,95
|
|
@@ -1065,8 +1066,8 @@ roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/v_Re/.zattrs,sha256=2z7
|
|
|
1065
1066
|
roms_tools/tests/test_setup/test_data/tidal_forcing.zarr/v_Re/0.0.0,sha256=33Gl8otBmgqVarmAnZuEqTYS2_hVJUJh-iN1HzvaDuo,96
|
|
1066
1067
|
roms_tools/tests/test_tiling/test_partition.py,sha256=b6EepZndVDv1B6Qt5_MbDfrFF2LtR0BF7i1t30xHEvA,7977
|
|
1067
1068
|
roms_tools/tiling/partition.py,sha256=ZxDNGIKXZf_7eEzw9cxGP2XR_WBhZ4WCeIMl7_IdskA,12302
|
|
1068
|
-
roms_tools-2.6.
|
|
1069
|
-
roms_tools-2.6.
|
|
1070
|
-
roms_tools-2.6.
|
|
1071
|
-
roms_tools-2.6.
|
|
1072
|
-
roms_tools-2.6.
|
|
1069
|
+
roms_tools-2.6.1.dist-info/licenses/LICENSE,sha256=yiff76E4xRioW2bHhlPpyYpstmePQBx2bF8HhgQhSsg,11318
|
|
1070
|
+
roms_tools-2.6.1.dist-info/METADATA,sha256=-nmxtLvdMwgledQl2Fuf1ir4Se9NsYEi5wTWx8rMQGo,4698
|
|
1071
|
+
roms_tools-2.6.1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
1072
|
+
roms_tools-2.6.1.dist-info/top_level.txt,sha256=aAf4T4nYQSkay5iKJ9kmTjlDgd4ETdp9OSlB4sJdt8Y,19
|
|
1073
|
+
roms_tools-2.6.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/roms_tools/tests/test_setup/test_data/river_forcing_with_bgc.zarr/{river_location → river_flux}/0.0
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|