rashdf 0.7.1__tar.gz → 0.8.0__tar.gz
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.
- {rashdf-0.7.1 → rashdf-0.8.0}/PKG-INFO +5 -3
- {rashdf-0.7.1 → rashdf-0.8.0}/pyproject.toml +14 -2
- {rashdf-0.7.1 → rashdf-0.8.0}/src/cli.py +1 -0
- {rashdf-0.7.1 → rashdf-0.8.0}/src/rashdf/geom.py +31 -2
- {rashdf-0.7.1 → rashdf-0.8.0}/src/rashdf/plan.py +122 -7
- {rashdf-0.7.1 → rashdf-0.8.0}/src/rashdf.egg-info/PKG-INFO +5 -3
- {rashdf-0.7.1 → rashdf-0.8.0}/src/rashdf.egg-info/requires.txt +2 -1
- {rashdf-0.7.1 → rashdf-0.8.0}/tests/test_geom.py +22 -4
- {rashdf-0.7.1 → rashdf-0.8.0}/tests/test_plan.py +71 -6
- {rashdf-0.7.1 → rashdf-0.8.0}/LICENSE +0 -0
- {rashdf-0.7.1 → rashdf-0.8.0}/README.md +0 -0
- {rashdf-0.7.1 → rashdf-0.8.0}/setup.cfg +0 -0
- {rashdf-0.7.1 → rashdf-0.8.0}/src/rashdf/__init__.py +0 -0
- {rashdf-0.7.1 → rashdf-0.8.0}/src/rashdf/base.py +0 -0
- {rashdf-0.7.1 → rashdf-0.8.0}/src/rashdf/utils.py +0 -0
- {rashdf-0.7.1 → rashdf-0.8.0}/src/rashdf.egg-info/SOURCES.txt +0 -0
- {rashdf-0.7.1 → rashdf-0.8.0}/src/rashdf.egg-info/dependency_links.txt +0 -0
- {rashdf-0.7.1 → rashdf-0.8.0}/src/rashdf.egg-info/entry_points.txt +0 -0
- {rashdf-0.7.1 → rashdf-0.8.0}/src/rashdf.egg-info/top_level.txt +0 -0
- {rashdf-0.7.1 → rashdf-0.8.0}/tests/test_base.py +0 -0
- {rashdf-0.7.1 → rashdf-0.8.0}/tests/test_cli.py +0 -0
- {rashdf-0.7.1 → rashdf-0.8.0}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: rashdf
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: Read data from HEC-RAS HDF files.
|
|
5
5
|
Project-URL: repository, https://github.com/fema-ffrd/rashdf
|
|
6
6
|
Classifier: Development Status :: 4 - Beta
|
|
@@ -23,15 +23,17 @@ Requires-Dist: ruff; extra == "dev"
|
|
|
23
23
|
Requires-Dist: pytest; extra == "dev"
|
|
24
24
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
25
25
|
Requires-Dist: kerchunk; extra == "dev"
|
|
26
|
-
Requires-Dist: zarr; extra == "dev"
|
|
26
|
+
Requires-Dist: zarr==2.18.2; extra == "dev"
|
|
27
27
|
Requires-Dist: dask; extra == "dev"
|
|
28
28
|
Requires-Dist: fsspec; extra == "dev"
|
|
29
29
|
Requires-Dist: s3fs; extra == "dev"
|
|
30
30
|
Requires-Dist: fiona==1.9.6; extra == "dev"
|
|
31
|
+
Requires-Dist: numcodecs<0.16; extra == "dev"
|
|
31
32
|
Provides-Extra: docs
|
|
32
33
|
Requires-Dist: sphinx; extra == "docs"
|
|
33
34
|
Requires-Dist: numpydoc; extra == "docs"
|
|
34
35
|
Requires-Dist: sphinx_rtd_theme; extra == "docs"
|
|
36
|
+
Dynamic: license-file
|
|
35
37
|
|
|
36
38
|
# rashdf
|
|
37
39
|
[](https://github.com/fema-ffrd/rashdf/actions/workflows/continuous-integration.yml)
|
|
@@ -12,11 +12,23 @@ classifiers = [
|
|
|
12
12
|
"Programming Language :: Python :: 3.11",
|
|
13
13
|
"Programming Language :: Python :: 3.12",
|
|
14
14
|
]
|
|
15
|
-
version = "0.
|
|
15
|
+
version = "0.8.0"
|
|
16
16
|
dependencies = ["h5py", "geopandas>=1.0,<2.0", "pyarrow", "xarray"]
|
|
17
17
|
|
|
18
18
|
[project.optional-dependencies]
|
|
19
|
-
dev = [
|
|
19
|
+
dev = [
|
|
20
|
+
"pre-commit",
|
|
21
|
+
"ruff",
|
|
22
|
+
"pytest",
|
|
23
|
+
"pytest-cov",
|
|
24
|
+
"kerchunk",
|
|
25
|
+
"zarr==2.18.2",
|
|
26
|
+
"dask",
|
|
27
|
+
"fsspec",
|
|
28
|
+
"s3fs",
|
|
29
|
+
"fiona==1.9.6",
|
|
30
|
+
"numcodecs<0.16"
|
|
31
|
+
]
|
|
20
32
|
docs = ["sphinx", "numpydoc", "sphinx_rtd_theme"]
|
|
21
33
|
|
|
22
34
|
[project.urls]
|
|
@@ -41,6 +41,7 @@ class RasGeomHdf(RasHdf):
|
|
|
41
41
|
GEOM_STRUCTURES_PATH = f"{GEOM_PATH}/Structures"
|
|
42
42
|
FLOW_AREA_2D_PATH = f"{GEOM_PATH}/2D Flow Areas"
|
|
43
43
|
BC_LINES_PATH = f"{GEOM_PATH}/Boundary Condition Lines"
|
|
44
|
+
IC_POINTS_PATH = f"{GEOM_PATH}/IC Points"
|
|
44
45
|
BREAKLINES_PATH = f"{GEOM_PATH}/2D Flow Area Break Lines"
|
|
45
46
|
REFERENCE_LINES_PATH = f"{GEOM_PATH}/Reference Lines"
|
|
46
47
|
REFERENCE_POINTS_PATH = f"{GEOM_PATH}/Reference Points"
|
|
@@ -408,7 +409,10 @@ class RasGeomHdf(RasHdf):
|
|
|
408
409
|
GeoDataFrame
|
|
409
410
|
A GeoDataFrame containing the model structures if they exist.
|
|
410
411
|
"""
|
|
411
|
-
if
|
|
412
|
+
if (
|
|
413
|
+
self.GEOM_STRUCTURES_PATH not in self
|
|
414
|
+
or f"{self.GEOM_STRUCTURES_PATH}/Attributes" not in self
|
|
415
|
+
):
|
|
412
416
|
return GeoDataFrame()
|
|
413
417
|
struct_data = self[self.GEOM_STRUCTURES_PATH]
|
|
414
418
|
v_conv_val = np.vectorize(convert_ras_hdf_value)
|
|
@@ -438,7 +442,32 @@ class RasGeomHdf(RasHdf):
|
|
|
438
442
|
raise NotImplementedError
|
|
439
443
|
|
|
440
444
|
def ic_points(self) -> GeoDataFrame: # noqa D102
|
|
441
|
-
|
|
445
|
+
"""Return initial conditions points.
|
|
446
|
+
|
|
447
|
+
Returns
|
|
448
|
+
-------
|
|
449
|
+
GeoDataFrame
|
|
450
|
+
A GeoDataFrame containing the initial condition points if they exist.
|
|
451
|
+
"""
|
|
452
|
+
if self.IC_POINTS_PATH not in self:
|
|
453
|
+
return GeoDataFrame()
|
|
454
|
+
ic_data = self[self.IC_POINTS_PATH]
|
|
455
|
+
v_conv_str = np.vectorize(convert_ras_hdf_string)
|
|
456
|
+
names = v_conv_str(ic_data["Attributes"][()]["Name"])
|
|
457
|
+
mesh_names = v_conv_str(ic_data["Attributes"][()]["SA/2D"])
|
|
458
|
+
cell_ids = ic_data["Attributes"][()]["Cell Index"]
|
|
459
|
+
points = ic_data["Points"][()]
|
|
460
|
+
return GeoDataFrame(
|
|
461
|
+
{
|
|
462
|
+
"icpt_id": range(len(names)),
|
|
463
|
+
"icpt_name": names,
|
|
464
|
+
"mesh_name": mesh_names,
|
|
465
|
+
"cell_id": cell_ids,
|
|
466
|
+
"geometry": list(map(Point, points)),
|
|
467
|
+
},
|
|
468
|
+
geometry="geometry",
|
|
469
|
+
crs=self.projection(),
|
|
470
|
+
)
|
|
442
471
|
|
|
443
472
|
def _reference_lines_points_names(
|
|
444
473
|
self, reftype: str = "lines", mesh_name: Optional[str] = None
|
|
@@ -160,7 +160,7 @@ class RasPlanHdf(RasGeomHdf):
|
|
|
160
160
|
OBS_DATA_PATH = "Event Conditions/Observed Data"
|
|
161
161
|
RESULTS_UNSTEADY_PATH = "Results/Unsteady"
|
|
162
162
|
RESULTS_UNSTEADY_SUMMARY_PATH = f"{RESULTS_UNSTEADY_PATH}/Summary"
|
|
163
|
-
VOLUME_ACCOUNTING_PATH = f"{
|
|
163
|
+
VOLUME_ACCOUNTING_PATH = f"{RESULTS_UNSTEADY_SUMMARY_PATH}/Volume Accounting"
|
|
164
164
|
BASE_OUTPUT_PATH = f"{RESULTS_UNSTEADY_PATH}/Output/Output Blocks/Base Output"
|
|
165
165
|
SUMMARY_OUTPUT_2D_FLOW_AREAS_PATH = (
|
|
166
166
|
f"{BASE_OUTPUT_PATH}/Summary Output/2D Flow Areas"
|
|
@@ -168,6 +168,7 @@ class RasPlanHdf(RasGeomHdf):
|
|
|
168
168
|
UNSTEADY_TIME_SERIES_PATH = f"{BASE_OUTPUT_PATH}/Unsteady Time Series"
|
|
169
169
|
REFERENCE_LINES_OUTPUT_PATH = f"{UNSTEADY_TIME_SERIES_PATH}/Reference Lines"
|
|
170
170
|
REFERENCE_POINTS_OUTPUT_PATH = f"{UNSTEADY_TIME_SERIES_PATH}/Reference Points"
|
|
171
|
+
BOUNDARY_CONDITIONS_OUTPUT_PATH = f"{UNSTEADY_TIME_SERIES_PATH}/Boundary Conditions"
|
|
171
172
|
OBS_FLOW_OUTPUT_PATH = f"{OBS_DATA_PATH}/Flow"
|
|
172
173
|
OBS_STAGE_OUTPUT_PATH = f"{OBS_DATA_PATH}/Stage"
|
|
173
174
|
|
|
@@ -1121,6 +1122,81 @@ class RasPlanHdf(RasGeomHdf):
|
|
|
1121
1122
|
"""
|
|
1122
1123
|
return self.reference_timeseries_output(reftype="lines")
|
|
1123
1124
|
|
|
1125
|
+
def bc_line_timeseries_output(self, bc_line_name: str) -> xr.Dataset:
|
|
1126
|
+
"""Return timeseries output data for a specific boundary condition line from a HEC-RAS HDF plan file.
|
|
1127
|
+
|
|
1128
|
+
Parameters
|
|
1129
|
+
----------
|
|
1130
|
+
bc_line_name : str
|
|
1131
|
+
The name of the boundary condition line.
|
|
1132
|
+
|
|
1133
|
+
Returns
|
|
1134
|
+
-------
|
|
1135
|
+
xr.Dataset
|
|
1136
|
+
An xarray Dataset with timeseries output data for the specified boundary condition line.
|
|
1137
|
+
"""
|
|
1138
|
+
path = f"{self.BOUNDARY_CONDITIONS_OUTPUT_PATH}/{bc_line_name}"
|
|
1139
|
+
dataset = self.get(path)
|
|
1140
|
+
if dataset is None:
|
|
1141
|
+
raise RasPlanHdfError(
|
|
1142
|
+
f"Could not find HDF group at path '{path}'."
|
|
1143
|
+
f" Does the Plan HDF file contain boundary condition output data for '{bc_line_name}'?"
|
|
1144
|
+
)
|
|
1145
|
+
columns = [c.decode("utf-8") for c in dataset.attrs["Columns"]]
|
|
1146
|
+
ds = xr.Dataset()
|
|
1147
|
+
try:
|
|
1148
|
+
import dask.array as da
|
|
1149
|
+
|
|
1150
|
+
# TODO: user-specified chunks?
|
|
1151
|
+
values = da.from_array(dataset, chunks=dataset.chunks)
|
|
1152
|
+
except ImportError:
|
|
1153
|
+
values = dataset[:]
|
|
1154
|
+
for i, col in enumerate(columns):
|
|
1155
|
+
units = dataset.attrs.get(col, None)
|
|
1156
|
+
if units is not None:
|
|
1157
|
+
units = units.decode("utf-8")
|
|
1158
|
+
da = xr.DataArray(
|
|
1159
|
+
values[:, i],
|
|
1160
|
+
name=col,
|
|
1161
|
+
dims=["time"],
|
|
1162
|
+
coords={
|
|
1163
|
+
"time": self.unsteady_datetimes(),
|
|
1164
|
+
},
|
|
1165
|
+
attrs={
|
|
1166
|
+
"units": units,
|
|
1167
|
+
"hdf_path": f"{path}",
|
|
1168
|
+
},
|
|
1169
|
+
)
|
|
1170
|
+
ds[col] = da
|
|
1171
|
+
return ds
|
|
1172
|
+
|
|
1173
|
+
def bc_lines_timeseries_output(self) -> xr.Dataset:
|
|
1174
|
+
"""Return timeseries output data for boundary conditions lines from a HEC-RAS HDF plan file.
|
|
1175
|
+
|
|
1176
|
+
Returns
|
|
1177
|
+
-------
|
|
1178
|
+
xr.Dataset
|
|
1179
|
+
An xarray Dataset with timeseries output data for boundary conditions lines.
|
|
1180
|
+
"""
|
|
1181
|
+
df_bc_lines = self.bc_lines()
|
|
1182
|
+
bc_lines_names = df_bc_lines["name"]
|
|
1183
|
+
datasets = []
|
|
1184
|
+
for bc_line_name in bc_lines_names:
|
|
1185
|
+
ds_bc_line = self.bc_line_timeseries_output(bc_line_name)
|
|
1186
|
+
datasets.append(ds_bc_line)
|
|
1187
|
+
bc_line_ids = df_bc_lines["bc_line_id"].values
|
|
1188
|
+
ds: xr.Dataset = xr.concat(
|
|
1189
|
+
datasets, dim=pd.Index(bc_line_ids, name="bc_line_id")
|
|
1190
|
+
)
|
|
1191
|
+
ds = ds.assign_coords(
|
|
1192
|
+
{
|
|
1193
|
+
"bc_line_name": ("bc_line_id", bc_lines_names),
|
|
1194
|
+
"bc_line_type": ("bc_line_id", df_bc_lines["type"]),
|
|
1195
|
+
"mesh_name": ("bc_line_id", df_bc_lines["mesh_name"]),
|
|
1196
|
+
}
|
|
1197
|
+
)
|
|
1198
|
+
return ds
|
|
1199
|
+
|
|
1124
1200
|
def observed_timeseries_input(self, vartype: str = "Flow") -> xr.DataArray:
|
|
1125
1201
|
"""Return observed timeseries input data for reference lines and points from a HEC-RAS HDF plan file.
|
|
1126
1202
|
|
|
@@ -1381,10 +1457,49 @@ class RasPlanHdf(RasGeomHdf):
|
|
|
1381
1457
|
"""
|
|
1382
1458
|
return self.get_attrs(self.VOLUME_ACCOUNTING_PATH)
|
|
1383
1459
|
|
|
1384
|
-
def
|
|
1385
|
-
|
|
1460
|
+
def encroachment_points(self, profile_name: str) -> GeoDataFrame:
|
|
1461
|
+
"""Return encroachment points from a HEC-RAS plan HDF file based on a user-specified flow profile.
|
|
1462
|
+
|
|
1463
|
+
Returns
|
|
1464
|
+
-------
|
|
1465
|
+
GeoDataframe
|
|
1466
|
+
A GeoDataFrame with cross-section encroachments represented as Point geometry features along with pertinent attributes.
|
|
1467
|
+
"""
|
|
1468
|
+
cross_sections = self.cross_sections()
|
|
1469
|
+
cross_sections["Enc_Profile"] = profile_name
|
|
1470
|
+
|
|
1471
|
+
leftmost_sta = self.cross_sections_elevations()["elevation info"].apply(
|
|
1472
|
+
lambda x: x[0][0]
|
|
1473
|
+
)
|
|
1474
|
+
left_enc_sta = self.cross_sections_additional_enc_station_left()[profile_name]
|
|
1475
|
+
left_enc_points = GeoDataFrame(
|
|
1476
|
+
pd.concat(
|
|
1477
|
+
[
|
|
1478
|
+
cross_sections[["River", "Reach", "RS", "Enc_Profile"]],
|
|
1479
|
+
left_enc_sta.rename("Enc_Sta", inplace=False),
|
|
1480
|
+
],
|
|
1481
|
+
axis=1,
|
|
1482
|
+
),
|
|
1483
|
+
geometry=cross_sections.geometry.interpolate(left_enc_sta - leftmost_sta),
|
|
1484
|
+
)
|
|
1485
|
+
left_enc_points["Side"] = "Left"
|
|
1486
|
+
|
|
1487
|
+
right_enc_sta = self.cross_sections_additional_enc_station_right()[profile_name]
|
|
1488
|
+
right_enc_points = GeoDataFrame(
|
|
1489
|
+
pd.concat(
|
|
1490
|
+
[
|
|
1491
|
+
cross_sections[["River", "Reach", "RS", "Enc_Profile"]],
|
|
1492
|
+
right_enc_sta.rename("Enc_Sta", inplace=False),
|
|
1493
|
+
],
|
|
1494
|
+
axis=1,
|
|
1495
|
+
),
|
|
1496
|
+
geometry=cross_sections.geometry.interpolate(right_enc_sta - leftmost_sta),
|
|
1497
|
+
)
|
|
1498
|
+
right_enc_points["Side"] = "Right"
|
|
1499
|
+
|
|
1500
|
+
return GeoDataFrame(pd.concat([left_enc_points, right_enc_points]))
|
|
1386
1501
|
|
|
1387
|
-
def steady_flow_names(self) ->
|
|
1502
|
+
def steady_flow_names(self) -> List[str]:
|
|
1388
1503
|
"""Return the profile information for each steady flow event.
|
|
1389
1504
|
|
|
1390
1505
|
Returns
|
|
@@ -1393,7 +1508,7 @@ class RasPlanHdf(RasGeomHdf):
|
|
|
1393
1508
|
A Dataframe containing the profile names for each event
|
|
1394
1509
|
"""
|
|
1395
1510
|
if self.STEADY_PROFILES_PATH not in self:
|
|
1396
|
-
return
|
|
1511
|
+
return []
|
|
1397
1512
|
|
|
1398
1513
|
profile_data = self[self.STEADY_PROFILES_PATH]
|
|
1399
1514
|
profile_attrs = profile_data["Profile Names"][()]
|
|
@@ -1511,7 +1626,7 @@ class RasPlanHdf(RasGeomHdf):
|
|
|
1511
1626
|
def _zmeta(self, ds: xr.Dataset) -> Dict:
|
|
1512
1627
|
"""Given a xarray Dataset, return kerchunk-style zarr reference metadata."""
|
|
1513
1628
|
from kerchunk.hdf import SingleHdf5ToZarr
|
|
1514
|
-
import
|
|
1629
|
+
from zarr.storage import MemoryStore
|
|
1515
1630
|
import base64
|
|
1516
1631
|
|
|
1517
1632
|
encoding = {}
|
|
@@ -1546,7 +1661,7 @@ class RasPlanHdf(RasGeomHdf):
|
|
|
1546
1661
|
chunk_meta[chunk_key] = [str(self._loc), value["offset"], value["size"]]
|
|
1547
1662
|
# "Write" the Dataset to a temporary in-memory zarr store (which
|
|
1548
1663
|
# is the same a Python dictionary)
|
|
1549
|
-
zarr_tmp =
|
|
1664
|
+
zarr_tmp = MemoryStore()
|
|
1550
1665
|
# Use compute=False here because we don't _actually_ want to write
|
|
1551
1666
|
# the data to the zarr store, we just want to generate the metadata.
|
|
1552
1667
|
ds.to_zarr(zarr_tmp, mode="w", compute=False, encoding=encoding)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: rashdf
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: Read data from HEC-RAS HDF files.
|
|
5
5
|
Project-URL: repository, https://github.com/fema-ffrd/rashdf
|
|
6
6
|
Classifier: Development Status :: 4 - Beta
|
|
@@ -23,15 +23,17 @@ Requires-Dist: ruff; extra == "dev"
|
|
|
23
23
|
Requires-Dist: pytest; extra == "dev"
|
|
24
24
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
25
25
|
Requires-Dist: kerchunk; extra == "dev"
|
|
26
|
-
Requires-Dist: zarr; extra == "dev"
|
|
26
|
+
Requires-Dist: zarr==2.18.2; extra == "dev"
|
|
27
27
|
Requires-Dist: dask; extra == "dev"
|
|
28
28
|
Requires-Dist: fsspec; extra == "dev"
|
|
29
29
|
Requires-Dist: s3fs; extra == "dev"
|
|
30
30
|
Requires-Dist: fiona==1.9.6; extra == "dev"
|
|
31
|
+
Requires-Dist: numcodecs<0.16; extra == "dev"
|
|
31
32
|
Provides-Extra: docs
|
|
32
33
|
Requires-Dist: sphinx; extra == "docs"
|
|
33
34
|
Requires-Dist: numpydoc; extra == "docs"
|
|
34
35
|
Requires-Dist: sphinx_rtd_theme; extra == "docs"
|
|
36
|
+
Dynamic: license-file
|
|
35
37
|
|
|
36
38
|
# rashdf
|
|
37
39
|
[](https://github.com/fema-ffrd/rashdf/actions/workflows/continuous-integration.yml)
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import geopandas as gpd
|
|
1
2
|
from pathlib import Path
|
|
2
3
|
import h5py
|
|
3
4
|
from pyproj import CRS
|
|
4
5
|
from src.rashdf import RasGeomHdf
|
|
6
|
+
from pandas.testing import assert_frame_equal
|
|
5
7
|
|
|
6
8
|
from . import _create_hdf_with_group_attrs, _gdf_matches_json, _gdf_matches_json_alt
|
|
7
9
|
|
|
@@ -11,6 +13,8 @@ COAL_G01 = TEST_DATA / "ras/Coal.g01.hdf"
|
|
|
11
13
|
BAXTER_P01 = TEST_DATA / "ras_1d/Baxter.p01.hdf"
|
|
12
14
|
TEST_JSON = TEST_DATA / "json"
|
|
13
15
|
BALD_EAGLE_P18_REF = TEST_DATA / "ras/BaldEagleDamBrk.reflines-refpts.p18.hdf"
|
|
16
|
+
LOWER_KANAWHA_P01_IC_POINTS = TEST_DATA / "ras/LowerKanawha.p01.icpoints.hdf"
|
|
17
|
+
LOWER_KANAWHA_P01_IC_POINTS_JSON = TEST_JSON / "LowerKanawha.p01.icpoints.geojson"
|
|
14
18
|
|
|
15
19
|
TEST_ATTRS = {"test_attribute1": "test_str1", "test_attribute2": 500}
|
|
16
20
|
|
|
@@ -151,7 +155,7 @@ def test_reference_points_names():
|
|
|
151
155
|
|
|
152
156
|
def test_structs_not_found():
|
|
153
157
|
with RasGeomHdf(COAL_G01) as ghdf:
|
|
154
|
-
assert
|
|
158
|
+
assert ghdf.structures().empty
|
|
155
159
|
|
|
156
160
|
|
|
157
161
|
def test_cross_sections():
|
|
@@ -164,7 +168,7 @@ def test_cross_sections():
|
|
|
164
168
|
|
|
165
169
|
def test_cross_sections_not_found():
|
|
166
170
|
with RasGeomHdf(COAL_G01) as ghdf:
|
|
167
|
-
assert
|
|
171
|
+
assert ghdf.cross_sections().empty
|
|
168
172
|
|
|
169
173
|
|
|
170
174
|
def test_river_reaches():
|
|
@@ -175,7 +179,7 @@ def test_river_reaches():
|
|
|
175
179
|
|
|
176
180
|
def test_river_reaches_not_found():
|
|
177
181
|
with RasGeomHdf(COAL_G01) as ghdf:
|
|
178
|
-
assert
|
|
182
|
+
assert ghdf.river_reaches().empty
|
|
179
183
|
|
|
180
184
|
|
|
181
185
|
def test_cross_sections_elevations():
|
|
@@ -186,4 +190,18 @@ def test_cross_sections_elevations():
|
|
|
186
190
|
|
|
187
191
|
def test_cross_sections_elevations_not_found():
|
|
188
192
|
with RasGeomHdf(COAL_G01) as ghdf:
|
|
189
|
-
assert
|
|
193
|
+
assert ghdf.cross_sections_elevations().empty
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def test_ic_points():
|
|
197
|
+
with RasGeomHdf(LOWER_KANAWHA_P01_IC_POINTS) as ghdf:
|
|
198
|
+
gdf_ic_points = ghdf.ic_points()
|
|
199
|
+
valid_gdf = gpd.read_file(
|
|
200
|
+
LOWER_KANAWHA_P01_IC_POINTS_JSON,
|
|
201
|
+
crs=ghdf.projection(),
|
|
202
|
+
)
|
|
203
|
+
assert_frame_equal(
|
|
204
|
+
gdf_ic_points,
|
|
205
|
+
valid_gdf,
|
|
206
|
+
check_dtype=False,
|
|
207
|
+
)
|
|
@@ -5,9 +5,11 @@ from src.rashdf.plan import (
|
|
|
5
5
|
TimeSeriesOutputVar,
|
|
6
6
|
)
|
|
7
7
|
|
|
8
|
+
import builtins
|
|
8
9
|
import filecmp
|
|
9
10
|
import json
|
|
10
11
|
from pathlib import Path
|
|
12
|
+
from unittest import mock
|
|
11
13
|
|
|
12
14
|
import numpy as np
|
|
13
15
|
import pandas as pd
|
|
@@ -32,6 +34,7 @@ BALD_EAGLE_P18_REF = TEST_DATA / "ras/BaldEagleDamBrk.reflines-refpts.p18.hdf"
|
|
|
32
34
|
DENTON = TEST_DATA / "ras/Denton.hdf"
|
|
33
35
|
MUNCIE_G05 = TEST_DATA / "ras/Muncie.g05.hdf"
|
|
34
36
|
COAL_G01 = TEST_DATA / "ras/Coal.g01.hdf"
|
|
37
|
+
LOWER_KANAWHA_P01_BC_LINES = TEST_DATA / "ras/LowerKanawha.p01.bclines.hdf"
|
|
35
38
|
BAXTER_P01 = TEST_DATA / "ras_1d/Baxter.p01.hdf"
|
|
36
39
|
FLODENCR_P01 = TEST_DATA / "ras_1d/FLODENCR.p01.hdf"
|
|
37
40
|
|
|
@@ -322,6 +325,59 @@ def test_reference_lines_timeseries(tmp_path: Path):
|
|
|
322
325
|
assert_frame_equal(df, valid_df)
|
|
323
326
|
|
|
324
327
|
|
|
328
|
+
def test_bc_lines_timeseries(tmp_path: Path):
|
|
329
|
+
plan_hdf = RasPlanHdf(LOWER_KANAWHA_P01_BC_LINES)
|
|
330
|
+
ds = plan_hdf.bc_lines_timeseries_output()
|
|
331
|
+
assert "time" in ds.coords
|
|
332
|
+
assert "bc_line_id" in ds.coords
|
|
333
|
+
assert "bc_line_name" in ds.coords
|
|
334
|
+
assert "mesh_name" in ds.coords
|
|
335
|
+
assert "Flow" in ds.variables
|
|
336
|
+
assert "Stage" in ds.variables
|
|
337
|
+
|
|
338
|
+
q = ds["Flow"]
|
|
339
|
+
assert q.chunks is not None # Ensure Dask chunks are set
|
|
340
|
+
assert q.shape == (10, 577)
|
|
341
|
+
assert q.attrs["units"] == "cfs"
|
|
342
|
+
|
|
343
|
+
stage = ds["Stage"]
|
|
344
|
+
assert stage.chunks is not None # Ensure Dask chunks are set
|
|
345
|
+
assert stage.shape == (10, 577)
|
|
346
|
+
assert stage.attrs["units"] == "ft"
|
|
347
|
+
|
|
348
|
+
df = ds.sel(bc_line_id=7).to_dataframe()
|
|
349
|
+
valid_df = pd.read_csv(
|
|
350
|
+
TEST_CSV / "LowerKanawha.p01.bclines.7.csv",
|
|
351
|
+
index_col="time",
|
|
352
|
+
parse_dates=True,
|
|
353
|
+
dtype={"Flow": np.float32, "Stage": np.float32},
|
|
354
|
+
)
|
|
355
|
+
assert_frame_equal(df, valid_df)
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def test_bc_lines_timeseries_no_dask(monkeypatch):
|
|
359
|
+
"""Test that the bc_lines_timeseries_output method works without Dask."""
|
|
360
|
+
original_import = builtins.__import__
|
|
361
|
+
|
|
362
|
+
def mocked_import(name, *args, **kwargs):
|
|
363
|
+
if name == "dask.array":
|
|
364
|
+
raise ImportError("Dask is not available")
|
|
365
|
+
return original_import(name, *args, **kwargs)
|
|
366
|
+
|
|
367
|
+
monkeypatch.setattr(builtins, "__import__", mocked_import)
|
|
368
|
+
|
|
369
|
+
plan_hdf = RasPlanHdf(LOWER_KANAWHA_P01_BC_LINES)
|
|
370
|
+
ds = plan_hdf.bc_lines_timeseries_output()
|
|
371
|
+
assert ds["Flow"].chunks is None # Ensure no Dask chunks are set
|
|
372
|
+
assert ds["Stage"].chunks is None
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def test_bc_line_timeseries_error():
|
|
376
|
+
plan_hdf = RasPlanHdf(LOWER_KANAWHA_P01_BC_LINES)
|
|
377
|
+
with pytest.raises(RasPlanHdfError):
|
|
378
|
+
plan_hdf.bc_line_timeseries_output("asdf")
|
|
379
|
+
|
|
380
|
+
|
|
325
381
|
def test_reference_points(tmp_path: Path):
|
|
326
382
|
plan_hdf = RasPlanHdf(BALD_EAGLE_P18_REF)
|
|
327
383
|
gdf = plan_hdf.reference_points(datetime_to_str=True)
|
|
@@ -371,7 +427,7 @@ def test_cross_sections_additional_velocity_total():
|
|
|
371
427
|
|
|
372
428
|
def test_cross_sections_additional_velocity_total_not_found():
|
|
373
429
|
with RasPlanHdf(COAL_G01) as phdf:
|
|
374
|
-
assert
|
|
430
|
+
assert phdf.cross_sections_additional_velocity_total().empty
|
|
375
431
|
|
|
376
432
|
|
|
377
433
|
def test_cross_sections_additional_area_total():
|
|
@@ -384,7 +440,7 @@ def test_cross_sections_additional_area_total():
|
|
|
384
440
|
|
|
385
441
|
def test_cross_sections_additional_area_total_not_found():
|
|
386
442
|
with RasPlanHdf(COAL_G01) as phdf:
|
|
387
|
-
assert
|
|
443
|
+
assert phdf.cross_sections_additional_area_total().empty
|
|
388
444
|
|
|
389
445
|
|
|
390
446
|
def test_steady_flow_names():
|
|
@@ -394,7 +450,7 @@ def test_steady_flow_names():
|
|
|
394
450
|
|
|
395
451
|
def test_steady_flow_names_not_found():
|
|
396
452
|
with RasPlanHdf(COAL_G01) as phdf:
|
|
397
|
-
assert
|
|
453
|
+
assert phdf.steady_flow_names() == []
|
|
398
454
|
|
|
399
455
|
|
|
400
456
|
def test_cross_sections_wsel():
|
|
@@ -405,7 +461,7 @@ def test_cross_sections_wsel():
|
|
|
405
461
|
|
|
406
462
|
def test_cross_sections_wsel_not_found():
|
|
407
463
|
with RasPlanHdf(COAL_G01) as phdf:
|
|
408
|
-
assert
|
|
464
|
+
assert phdf.cross_sections_wsel().empty
|
|
409
465
|
|
|
410
466
|
|
|
411
467
|
def test_cross_sections_additional_enc_station_right():
|
|
@@ -419,7 +475,7 @@ def test_cross_sections_additional_enc_station_right():
|
|
|
419
475
|
|
|
420
476
|
def test_cross_sections_additional_enc_station_right_not_found():
|
|
421
477
|
with RasPlanHdf(COAL_G01) as phdf:
|
|
422
|
-
assert
|
|
478
|
+
assert phdf.cross_sections_additional_enc_station_right().empty
|
|
423
479
|
|
|
424
480
|
|
|
425
481
|
def test_cross_sections_additional_enc_station_left():
|
|
@@ -432,7 +488,7 @@ def test_cross_sections_additional_enc_station_left():
|
|
|
432
488
|
|
|
433
489
|
def test_cross_sections_additional_enc_station_left_not_found():
|
|
434
490
|
with RasPlanHdf(COAL_G01) as phdf:
|
|
435
|
-
assert
|
|
491
|
+
assert phdf.cross_sections_additional_enc_station_left().empty
|
|
436
492
|
|
|
437
493
|
|
|
438
494
|
def test_cross_sections_flow():
|
|
@@ -650,3 +706,12 @@ def test_observed_timeseries_input_rasplanhdf_error():
|
|
|
650
706
|
with RasPlanHdf(BALD_EAGLE_P18) as phdf:
|
|
651
707
|
with pytest.raises(RasPlanHdfError):
|
|
652
708
|
phdf.observed_timeseries_input(vartype="Flow")
|
|
709
|
+
|
|
710
|
+
|
|
711
|
+
def test_encroachment_points():
|
|
712
|
+
enc_pnts_json = TEST_JSON / "encroachment_points.json"
|
|
713
|
+
with RasPlanHdf(FLODENCR_P01) as phdf:
|
|
714
|
+
assert _gdf_matches_json_alt(
|
|
715
|
+
phdf.encroachment_points(profile_name="PF#2"),
|
|
716
|
+
enc_pnts_json,
|
|
717
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|