hydrotoolbox 2.2.0__tar.gz → 2.3.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.
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/.pre-commit-config.yaml +4 -4
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/CHANGELOG.md +6 -0
- {hydrotoolbox-2.2.0/src/hydrotoolbox.egg-info → hydrotoolbox-2.3.0}/PKG-INFO +1 -1
- hydrotoolbox-2.3.0/VERSION +1 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/pyproject.toml +1 -1
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/baseflow/methods/lyne_hollick.py +7 -2
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/baseflow/separation.py +3 -2
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/baseflow_sep.py +6 -1
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/toolbox_utils/src/toolbox_utils/readers/hbn.py +32 -172
- hydrotoolbox-2.3.0/src/hydrotoolbox/toolbox_utils/src/toolbox_utils/readers/utils.py +179 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/toolbox_utils/src/toolbox_utils/tsutils.py +69 -49
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/toolbox_utils/src/toolbox_utils/utils.py +52 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/toolbox_utils/tests/test_asbestfreq.py +5 -3
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0/src/hydrotoolbox.egg-info}/PKG-INFO +1 -1
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox.egg-info/SOURCES.txt +1 -0
- hydrotoolbox-2.2.0/VERSION +0 -1
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/.deepsource.toml +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/.github/dependabot.yml +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/.github/workflows/clean-workflow-runs.yml +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/.github/workflows/pypi-package.yml +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/.github/workflows/tests.yml +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/.gitignore +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/.gitmodules +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/.sourcery.yaml +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/AUTHORS.rst +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/BADGES.rst +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/CONTRIBUTING.rst +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/LICENSE.txt +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/README.rst +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/docs/Makefile +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/docs/authors.rst +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/docs/command_line.rst +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/docs/conf.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/docs/contributing.rst +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/docs/function_summary.rst +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/docs/index.rst +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/docs/license.rst +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/docs/make.bat +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/docs/readme.rst +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/pytest.ini +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/setup.cfg +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/__init__.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/baseflow/__init__.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/baseflow/comparison.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/baseflow/example.csv +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/baseflow/methods/__init__.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/baseflow/methods/boughton.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/baseflow/methods/chapman.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/baseflow/methods/chapman_maxwell.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/baseflow/methods/eckhardt.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/baseflow/methods/ewma.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/baseflow/methods/five_day.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/baseflow/methods/fixed.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/baseflow/methods/furey.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/baseflow/methods/general_form.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/baseflow/methods/ihacres.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/baseflow/methods/local.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/baseflow/methods/slide.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/baseflow/methods/strict.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/baseflow/methods/ukih.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/baseflow/methods/willems.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/baseflow/param_estimate.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/baseflow/utils.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/hydrotoolbox.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/indices/__init__.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/indices/indices.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/toolbox_utils/docs/conf.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/toolbox_utils/src/toolbox_utils/__init__.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/toolbox_utils/src/toolbox_utils/readers/__init__.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/toolbox_utils/src/toolbox_utils/readers/plotgen.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/toolbox_utils/src/toolbox_utils/readers/wdm.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/toolbox_utils/tests/test_date_slice.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/toolbox_utils/tests/test_dateparse.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/toolbox_utils/tests/test_extract.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/toolbox_utils/tests/test_hbn.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/toolbox_utils/tests/test_make_list.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/toolbox_utils/tests/test_period_timestamp.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/toolbox_utils/tests/test_plotgen.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/toolbox_utils/tests/test_range_to_numlist.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/toolbox_utils/tests/test_read.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/toolbox_utils/tests/test_wdm.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox/utils.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox.egg-info/dependency_links.txt +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox.egg-info/entry_points.txt +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox.egg-info/requires.txt +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/src/hydrotoolbox.egg-info/top_level.txt +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/BEC_test.sh +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/Q_BEC_BE_6500.csv +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/Q_BEC_BE_6500.ssf +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/black_earth_creek_05406500_flow.csv +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/black_earth_creek_05406500_test.sh +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/data.csv +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/data_02239501.csv +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/data_baseflow_boughton.csv +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/data_baseflow_chapman.csv +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/data_baseflow_chapman_maxwell.csv +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/data_baseflow_eckhardt.csv +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/data_baseflow_ewma.csv +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/data_baseflow_five_day.csv +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/data_baseflow_furey.csv +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/data_baseflow_ihacres.csv +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/data_baseflow_lyne_hollick.csv +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/data_baseflow_strict.csv +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/data_baseflow_ukih.csv +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/data_baseflow_usgs_hysep_fixed.csv +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/data_baseflow_usgs_hysep_local.csv +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/data_baseflow_usgs_hysep_slide.csv +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/data_baseflow_willems.csv +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/data_percentile_test.csv +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/data_short.csv +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/data_test_storm_events.csv +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/gtable.out +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/gtable_1955.out +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/hydrotoolbox_indices.txt +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/run_test.sh +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/test_baseflow_sep.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/test_exceedance_time.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/test_flow_duration.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/test_indices.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/test_recession.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/test_storm_events.py +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/tsproc_hydrologic_indices.inp +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/tsproc_hydrologic_indices.rec +0 -0
- {hydrotoolbox-2.2.0 → hydrotoolbox-2.3.0}/tests/tsproc_hydrologic_indices_percentile_test.inp +0 -0
|
@@ -29,7 +29,7 @@ repos:
|
|
|
29
29
|
|
|
30
30
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
31
31
|
# Ruff version.
|
|
32
|
-
rev: v0.15.
|
|
32
|
+
rev: v0.15.16
|
|
33
33
|
hooks:
|
|
34
34
|
# Run the linter.
|
|
35
35
|
- id: ruff
|
|
@@ -40,7 +40,7 @@ repos:
|
|
|
40
40
|
types_or: [python, pyi, jupyter]
|
|
41
41
|
|
|
42
42
|
- repo: https://github.com/pycqa/isort
|
|
43
|
-
rev:
|
|
43
|
+
rev: 9.0.0a3
|
|
44
44
|
hooks:
|
|
45
45
|
- id: isort
|
|
46
46
|
name: isort (python)
|
|
@@ -52,7 +52,7 @@ repos:
|
|
|
52
52
|
types: [pyi]
|
|
53
53
|
|
|
54
54
|
- repo: https://github.com/pappasam/toml-sort
|
|
55
|
-
rev: v0.24.
|
|
55
|
+
rev: v0.24.4
|
|
56
56
|
hooks:
|
|
57
57
|
- id: toml-sort-fix
|
|
58
58
|
args: [--in-place, --spaces-indent-inline-array, '4']
|
|
@@ -91,7 +91,7 @@ repos:
|
|
|
91
91
|
- id: pyupgrade
|
|
92
92
|
|
|
93
93
|
- repo: https://github.com/commitizen-tools/commitizen
|
|
94
|
-
rev: v4.
|
|
94
|
+
rev: v4.16.3
|
|
95
95
|
hooks:
|
|
96
96
|
- id: commitizen
|
|
97
97
|
stages: [commit-msg]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.3.0
|
|
@@ -5,10 +5,15 @@ __all__ = [
|
|
|
5
5
|
from hydrotoolbox.baseflow.methods.general_form import general_form_digital_filter
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
def lyne_hollick(flow, k):
|
|
8
|
+
def lyne_hollick(flow, k, passes=1):
|
|
9
9
|
"""LH digital filter (Lyne & Hollick, 1979)"""
|
|
10
10
|
alpha = k
|
|
11
11
|
beta = (1 - k) / 2
|
|
12
12
|
gamma = 1.0
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
for pss in range(passes):
|
|
15
|
+
if pss > 0:
|
|
16
|
+
flow = flow[::-1]
|
|
17
|
+
flow, count = general_form_digital_filter(flow, alpha, beta, gamma)
|
|
18
|
+
|
|
19
|
+
return flow, count
|
|
@@ -37,6 +37,7 @@ def separation(
|
|
|
37
37
|
C=None,
|
|
38
38
|
a=None,
|
|
39
39
|
bfi_max=None,
|
|
40
|
+
passes=1,
|
|
40
41
|
):
|
|
41
42
|
if method == "all":
|
|
42
43
|
method = [
|
|
@@ -76,7 +77,7 @@ def separation(
|
|
|
76
77
|
"furey",
|
|
77
78
|
"willems",
|
|
78
79
|
]:
|
|
79
|
-
b_lh = lyne_hollick(Q, k=k)[0]
|
|
80
|
+
b_lh = lyne_hollick(Q, k=k, passes=passes)[0]
|
|
80
81
|
|
|
81
82
|
if m == "ukih":
|
|
82
83
|
b[m] = ukih(Q, b_lh)
|
|
@@ -91,7 +92,7 @@ def separation(
|
|
|
91
92
|
b[m] = slide(Q, area=area, num_days=num_days)
|
|
92
93
|
|
|
93
94
|
if m == "lyne_hollick":
|
|
94
|
-
b[m] = lyne_hollick(Q, k=k)[0]
|
|
95
|
+
b[m] = lyne_hollick(Q, k=k, passes=passes)[0]
|
|
95
96
|
|
|
96
97
|
if m == "chapman":
|
|
97
98
|
b[m] = chapman(Q, k)[0]
|
|
@@ -104,6 +104,7 @@ def bfsep(
|
|
|
104
104
|
C=None,
|
|
105
105
|
a=None,
|
|
106
106
|
bfi_max=None,
|
|
107
|
+
passes=1,
|
|
107
108
|
):
|
|
108
109
|
complete_index = pd.date_range(start=flow.index[0], end=flow.index[-1], freq="D")
|
|
109
110
|
flow = flow.reindex(complete_index, fill_value=np.nan)
|
|
@@ -154,6 +155,7 @@ def bfsep(
|
|
|
154
155
|
C=C,
|
|
155
156
|
a=a,
|
|
156
157
|
bfi_max=bfi_max,
|
|
158
|
+
passes=passes,
|
|
157
159
|
)[bfi],
|
|
158
160
|
index=ncol.index,
|
|
159
161
|
)
|
|
@@ -664,6 +666,7 @@ def furey(
|
|
|
664
666
|
def lyne_hollick(
|
|
665
667
|
input_ts="-",
|
|
666
668
|
alpha=0.925,
|
|
669
|
+
passes=1,
|
|
667
670
|
columns=None,
|
|
668
671
|
source_units=None,
|
|
669
672
|
start_date=None,
|
|
@@ -696,6 +699,8 @@ def lyne_hollick(
|
|
|
696
699
|
${columns}
|
|
697
700
|
alpha
|
|
698
701
|
Catchment constant (value between 0 and 1). Default is 0.925.
|
|
702
|
+
passes : int
|
|
703
|
+
The number of passes to use for the filter.
|
|
699
704
|
${source_units}
|
|
700
705
|
${start_date}
|
|
701
706
|
${end_date}
|
|
@@ -731,7 +736,7 @@ def lyne_hollick(
|
|
|
731
736
|
source_units=source_units,
|
|
732
737
|
target_units=target_units,
|
|
733
738
|
)
|
|
734
|
-
return bfsep(flow, "lyne_hollick", print_input, k=alpha)
|
|
739
|
+
return bfsep(flow, "lyne_hollick", print_input, k=alpha, passes=passes)
|
|
735
740
|
|
|
736
741
|
|
|
737
742
|
@tsutils.doc(tsutils.docstrings)
|
|
@@ -1,23 +1,26 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""function to read HSPF binary files."""
|
|
2
2
|
|
|
3
3
|
import datetime
|
|
4
4
|
import struct
|
|
5
5
|
import sys
|
|
6
|
-
|
|
7
|
-
try:
|
|
8
|
-
from typing import Literal
|
|
9
|
-
except ImportError:
|
|
10
|
-
from typing import Literal
|
|
6
|
+
from typing import Literal
|
|
11
7
|
|
|
12
8
|
import pandas as pd
|
|
13
9
|
|
|
14
10
|
from .. import tsutils
|
|
11
|
+
from ..utils import pandas_period_by_version
|
|
12
|
+
from . import utils
|
|
15
13
|
|
|
16
14
|
code2intervalmap = {5: "yearly", 4: "monthly", 3: "daily", 2: "bivl"}
|
|
17
15
|
|
|
18
16
|
interval2codemap = {"yearly": 5, "monthly": 4, "daily": 3, "bivl": 2}
|
|
19
17
|
|
|
20
|
-
code2freqmap = {
|
|
18
|
+
code2freqmap = {
|
|
19
|
+
5: pandas_period_by_version("Y"),
|
|
20
|
+
4: "M",
|
|
21
|
+
3: "D",
|
|
22
|
+
2: None,
|
|
23
|
+
}
|
|
21
24
|
|
|
22
25
|
|
|
23
26
|
_LOCAL_DOCSTRINGS = {
|
|
@@ -27,168 +30,20 @@ _LOCAL_DOCSTRINGS = {
|
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
|
|
30
|
-
def tuple_match(findme, hay):
|
|
31
|
-
"""Part of partial ordered matching.
|
|
32
|
-
See http://stackoverflow.com/a/4559604
|
|
33
|
-
"""
|
|
34
|
-
return len(findme) == len(hay) and all(
|
|
35
|
-
i is None or j is None or i == j for i, j in zip(findme, hay)
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def tuple_combine(findme, hay):
|
|
40
|
-
"""Part of partial ordered matching.
|
|
41
|
-
See http://stackoverflow.com/a/4559604
|
|
42
|
-
"""
|
|
43
|
-
return tuple(i is None and j or i for i, j in zip(findme, hay))
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def tuple_search(findme, haystack):
|
|
47
|
-
"""Partial ordered matching with 'None' as wildcard
|
|
48
|
-
See http://stackoverflow.com/a/4559604
|
|
49
|
-
"""
|
|
50
|
-
return [
|
|
51
|
-
(index, tuple_combine(findme, hay))
|
|
52
|
-
for index, hay in enumerate(haystack)
|
|
53
|
-
if tuple_match(findme, hay)
|
|
54
|
-
]
|
|
55
|
-
|
|
56
|
-
|
|
57
33
|
def _get_data(binfilename, interval="daily", labels=None, catalog_only=True):
|
|
58
34
|
"""Underlying function to read from the binary file. Used by
|
|
59
35
|
'extract', 'catalog'.
|
|
60
36
|
"""
|
|
61
|
-
if labels is None:
|
|
62
|
-
labels = [",,,"]
|
|
63
|
-
testem = {
|
|
64
|
-
"PERLND": [
|
|
65
|
-
"ATEMP",
|
|
66
|
-
"SNOW",
|
|
67
|
-
"PWATER",
|
|
68
|
-
"SEDMNT",
|
|
69
|
-
"PSTEMP",
|
|
70
|
-
"PWTGAS",
|
|
71
|
-
"PQUAL",
|
|
72
|
-
"MSTLAY",
|
|
73
|
-
"PEST",
|
|
74
|
-
"NITR",
|
|
75
|
-
"PHOS",
|
|
76
|
-
"TRACER",
|
|
77
|
-
"",
|
|
78
|
-
],
|
|
79
|
-
"IMPLND": ["ATEMP", "SNOW", "IWATER", "SOLIDS", "IWTGAS", "IQUAL", ""],
|
|
80
|
-
"RCHRES": [
|
|
81
|
-
"HYDR",
|
|
82
|
-
"CONS",
|
|
83
|
-
"HTRCH",
|
|
84
|
-
"SEDTRN",
|
|
85
|
-
"GQUAL",
|
|
86
|
-
"OXRX",
|
|
87
|
-
"NUTRX",
|
|
88
|
-
"PLANK",
|
|
89
|
-
"PHCARB",
|
|
90
|
-
"INFLOW",
|
|
91
|
-
"OFLOW",
|
|
92
|
-
"ROFLOW",
|
|
93
|
-
"",
|
|
94
|
-
],
|
|
95
|
-
"BMPRAC": [""],
|
|
96
|
-
"": [""],
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
collect_dict = {}
|
|
100
|
-
lablist = []
|
|
101
|
-
|
|
102
37
|
# Normalize interval code
|
|
103
38
|
try:
|
|
104
39
|
intervalcode = interval2codemap[interval.lower()]
|
|
105
40
|
except AttributeError:
|
|
106
41
|
intervalcode = None
|
|
107
42
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
# turn into a list of lists
|
|
112
|
-
nlabels = []
|
|
113
|
-
for label in labels:
|
|
114
|
-
if isinstance(label, str):
|
|
115
|
-
nlabels.append(label.split(","))
|
|
116
|
-
else:
|
|
117
|
-
nlabels.append(label)
|
|
118
|
-
labels = nlabels
|
|
119
|
-
|
|
120
|
-
# Check the list members for valid values
|
|
121
|
-
for label in labels:
|
|
122
|
-
if len(label) != 4:
|
|
123
|
-
raise ValueError(
|
|
124
|
-
tsutils.error_wrapper(
|
|
125
|
-
f"""The label '{label}' has the wrong number of entries.
|
|
126
|
-
"""
|
|
127
|
-
)
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
# replace empty fields with None
|
|
131
|
-
# operation,lue_number,group,variable
|
|
132
|
-
words = [None if (i in ("", "None")) else i for i in label]
|
|
133
|
-
|
|
134
|
-
# first word must be a valid operation type or None
|
|
135
|
-
if words[0] is not None:
|
|
136
|
-
# force uppercase before comparison
|
|
137
|
-
words[0] = words[0].upper()
|
|
138
|
-
if words[0] not in testem:
|
|
139
|
-
raise ValueError(
|
|
140
|
-
tsutils.error_wrapper(
|
|
141
|
-
f"""Operation type must be one of 'PERLND', 'IMPLND',
|
|
142
|
-
'RCHRES', or 'BMPRAC', or missing (to get all) instead
|
|
143
|
-
of {words[0]}.
|
|
144
|
-
"""
|
|
145
|
-
)
|
|
146
|
-
)
|
|
147
|
-
|
|
148
|
-
# second word must be integer 1-999 or None or range to parse
|
|
149
|
-
if words[1] is not None:
|
|
150
|
-
try:
|
|
151
|
-
words[1] = int(words[1])
|
|
152
|
-
luelist = [words[1]]
|
|
153
|
-
except ValueError:
|
|
154
|
-
luelist = tsutils.range_to_numlist(words[1])
|
|
155
|
-
for luenum in luelist:
|
|
156
|
-
if luenum < 1 or luenum > 999:
|
|
157
|
-
raise ValueError(
|
|
158
|
-
tsutils.error_wrapper(
|
|
159
|
-
f"""The land use element must be an integer from
|
|
160
|
-
1 to 999 inclusive, instead of {luenum}.
|
|
161
|
-
"""
|
|
162
|
-
)
|
|
163
|
-
)
|
|
164
|
-
else:
|
|
165
|
-
luelist = [None]
|
|
166
|
-
|
|
167
|
-
# third word must be a valid group name or None
|
|
168
|
-
if words[2] is not None:
|
|
169
|
-
words[2] = words[2].upper()
|
|
170
|
-
if words[2] not in testem[words[0]]:
|
|
171
|
-
raise ValueError(
|
|
172
|
-
tsutils.error_wrapper(
|
|
173
|
-
f"""The {words[0]} operation type only allows the
|
|
174
|
-
variable groups: {testem[words[0]][:-1]}, instead you
|
|
175
|
-
gave {words[2]}.
|
|
176
|
-
"""
|
|
177
|
-
)
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
# fourth word is currently not checked - assumed to be a variable name
|
|
181
|
-
# if not, it will simply never be found in the file, so ok
|
|
182
|
-
# but no warning for the user - add check?
|
|
183
|
-
|
|
184
|
-
# add interval code as fifth word in list
|
|
185
|
-
words.append(intervalcode)
|
|
186
|
-
|
|
187
|
-
# add to new list of checked and expanded lists
|
|
188
|
-
for luenum in luelist:
|
|
189
|
-
words[1] = luenum
|
|
190
|
-
lablist.append(list(words))
|
|
43
|
+
lablist = utils.normalize_labels(labels)
|
|
44
|
+
lablist = [i + [intervalcode] for i in lablist]
|
|
191
45
|
|
|
46
|
+
collect_dict = {}
|
|
192
47
|
# Now read through the binary file and collect the data matching the labels
|
|
193
48
|
with open(binfilename, "rb") as binfp:
|
|
194
49
|
labeltest = set()
|
|
@@ -200,7 +55,8 @@ def _get_data(binfilename, interval="daily", labels=None, catalog_only=True):
|
|
|
200
55
|
# not a valid HSPF binary file
|
|
201
56
|
raise ValueError(
|
|
202
57
|
tsutils.error_wrapper(
|
|
203
|
-
f"""
|
|
58
|
+
f"""
|
|
59
|
+
{binfilename} is not a valid HSPF binary output file
|
|
204
60
|
(.hbn), The first byte must be FD hexadecimal, but it was
|
|
205
61
|
{magicbyte}.
|
|
206
62
|
"""
|
|
@@ -251,9 +107,7 @@ def _get_data(binfilename, interval="daily", labels=None, catalog_only=True):
|
|
|
251
107
|
# read the variable name
|
|
252
108
|
variable_name = struct.unpack(f"{length}s", binfp.read(length))[0]
|
|
253
109
|
|
|
254
|
-
# add variable name to the
|
|
255
|
-
# why a set instead of a list? There should never be
|
|
256
|
-
# a duplicate anyway
|
|
110
|
+
# add variable name to the vnames list
|
|
257
111
|
vnames.setdefault((lue, group), []).append(variable_name)
|
|
258
112
|
|
|
259
113
|
# update how far along the record we are
|
|
@@ -275,11 +129,15 @@ def _get_data(binfilename, interval="daily", labels=None, catalog_only=True):
|
|
|
275
129
|
vals = struct.unpack(f"{numvals}f", binfp.read(4 * numvals))
|
|
276
130
|
recpos += 4 * numvals
|
|
277
131
|
|
|
132
|
+
# delta accounts for HSPF's use of hour 24 to represent
|
|
133
|
+
# the end of the last interval of the day.
|
|
278
134
|
delta = datetime.timedelta(hours=0)
|
|
279
|
-
if
|
|
280
|
-
|
|
135
|
+
if level == interval2codemap["bivl"]:
|
|
136
|
+
delta = datetime.timedelta(hours=hour) + datetime.timedelta(
|
|
137
|
+
minutes=minute
|
|
138
|
+
)
|
|
281
139
|
|
|
282
|
-
ndate = datetime.datetime(year, month, day
|
|
140
|
+
ndate = datetime.datetime(year, month, day) + delta
|
|
283
141
|
|
|
284
142
|
# Go through labels to see if these values need to be
|
|
285
143
|
# collected
|
|
@@ -291,9 +149,8 @@ def _get_data(binfilename, interval="daily", labels=None, catalog_only=True):
|
|
|
291
149
|
vname.decode("ascii"),
|
|
292
150
|
level,
|
|
293
151
|
)
|
|
294
|
-
|
|
295
152
|
for lbl in lablist:
|
|
296
|
-
res = tuple_search(tmpkey, [lbl])
|
|
153
|
+
res = utils.tuple_search(tmpkey, [lbl])
|
|
297
154
|
if not res:
|
|
298
155
|
continue
|
|
299
156
|
labeltest.add(tuple(lbl))
|
|
@@ -322,8 +179,9 @@ def _get_data(binfilename, interval="daily", labels=None, catalog_only=True):
|
|
|
322
179
|
if not collect_dict:
|
|
323
180
|
raise ValueError(
|
|
324
181
|
tsutils.error_wrapper(
|
|
325
|
-
f"""
|
|
326
|
-
binary
|
|
182
|
+
f"""
|
|
183
|
+
The label specifications below matched no records in the binary
|
|
184
|
+
file.
|
|
327
185
|
|
|
328
186
|
{lablist}
|
|
329
187
|
"""
|
|
@@ -337,8 +195,9 @@ def _get_data(binfilename, interval="daily", labels=None, catalog_only=True):
|
|
|
337
195
|
if tuple(lbl) not in labeltest:
|
|
338
196
|
sys.stderr.write(
|
|
339
197
|
tsutils.error_wrapper(
|
|
340
|
-
f"""
|
|
341
|
-
|
|
198
|
+
f"""
|
|
199
|
+
Warning: The label '{lbl}' matched no records in the
|
|
200
|
+
binary file.
|
|
342
201
|
"""
|
|
343
202
|
)
|
|
344
203
|
)
|
|
@@ -365,7 +224,8 @@ def hbn_extract(
|
|
|
365
224
|
if interval not in ("bivl", "daily", "monthly", "yearly"):
|
|
366
225
|
raise ValueError(
|
|
367
226
|
tsutils.error_wrapper(
|
|
368
|
-
f"""
|
|
227
|
+
f"""
|
|
228
|
+
The "interval" argument must be one of "bivl", "daily",
|
|
369
229
|
"monthly", or "yearly". You supplied "{interval}".
|
|
370
230
|
"""
|
|
371
231
|
)
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""A collection of functions used by toolbox_utils, wdmtoolbox, ...etc."""
|
|
2
|
+
|
|
3
|
+
from typing import List, Optional, Union
|
|
4
|
+
|
|
5
|
+
import pint_pandas # not used directly, but required to use pint in pandas
|
|
6
|
+
|
|
7
|
+
from .. import tsutils
|
|
8
|
+
|
|
9
|
+
# This is here so that linters don't remove the pint_pandas import which is
|
|
10
|
+
# needed to use pint in pandas
|
|
11
|
+
_ = pint_pandas.version("pint")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def normalize_labels(labels: Optional[Union[str, List[str]]]) -> List[str]:
|
|
15
|
+
"""
|
|
16
|
+
Process labels for the hbn function.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
labels
|
|
21
|
+
The labels to be processed.
|
|
22
|
+
|
|
23
|
+
Returns
|
|
24
|
+
-------
|
|
25
|
+
process_labels
|
|
26
|
+
A list of processed labels.
|
|
27
|
+
"""
|
|
28
|
+
if labels is None:
|
|
29
|
+
labels = [",,,"]
|
|
30
|
+
|
|
31
|
+
testem = {
|
|
32
|
+
"PERLND": [
|
|
33
|
+
"ATEMP",
|
|
34
|
+
"SNOW",
|
|
35
|
+
"PWATER",
|
|
36
|
+
"SEDMNT",
|
|
37
|
+
"PSTEMP",
|
|
38
|
+
"PWTGAS",
|
|
39
|
+
"PQUAL",
|
|
40
|
+
"MSTLAY",
|
|
41
|
+
"PEST",
|
|
42
|
+
"NITR",
|
|
43
|
+
"PHOS",
|
|
44
|
+
"TRACER",
|
|
45
|
+
"",
|
|
46
|
+
],
|
|
47
|
+
"IMPLND": ["ATEMP", "SNOW", "IWATER", "SOLIDS", "IWTGAS", "IQUAL", ""],
|
|
48
|
+
"RCHRES": [
|
|
49
|
+
"HYDR",
|
|
50
|
+
"CONS",
|
|
51
|
+
"HTRCH",
|
|
52
|
+
"SEDTRN",
|
|
53
|
+
"GQUAL",
|
|
54
|
+
"OXRX",
|
|
55
|
+
"NUTRX",
|
|
56
|
+
"PLANK",
|
|
57
|
+
"PHCARB",
|
|
58
|
+
"INFLOW",
|
|
59
|
+
"OFLOW",
|
|
60
|
+
"ROFLOW",
|
|
61
|
+
"",
|
|
62
|
+
],
|
|
63
|
+
"BMPRAC": [""],
|
|
64
|
+
"": [""],
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
lablist = []
|
|
68
|
+
|
|
69
|
+
# convert label tuples to lists
|
|
70
|
+
labels = list(labels)
|
|
71
|
+
|
|
72
|
+
# turn into a list of lists
|
|
73
|
+
nlabels = []
|
|
74
|
+
for label in labels:
|
|
75
|
+
if isinstance(label, str):
|
|
76
|
+
nlabels.append(label.split(","))
|
|
77
|
+
else:
|
|
78
|
+
nlabels.append(label)
|
|
79
|
+
labels = nlabels
|
|
80
|
+
|
|
81
|
+
# Check the list members for valid values
|
|
82
|
+
for label in labels:
|
|
83
|
+
if len(label) != 4:
|
|
84
|
+
raise ValueError(
|
|
85
|
+
tsutils.error_wrapper(
|
|
86
|
+
f"""
|
|
87
|
+
The label '{label}' has the wrong number of entries.
|
|
88
|
+
"""
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# replace empty fields with None
|
|
93
|
+
words = [None if i == "" else i for i in label]
|
|
94
|
+
|
|
95
|
+
# first word must be a valid operation type or None
|
|
96
|
+
if words[0] is not None:
|
|
97
|
+
# force uppercase before comparison
|
|
98
|
+
words[0] = words[0].upper()
|
|
99
|
+
if words[0] not in testem:
|
|
100
|
+
raise ValueError(
|
|
101
|
+
tsutils.error_wrapper(
|
|
102
|
+
f"""
|
|
103
|
+
Operation type must be one of 'PERLND', 'IMPLND',
|
|
104
|
+
'RCHRES', or 'BMPRAC', or missing (to get all) instead
|
|
105
|
+
of {words[0]}.
|
|
106
|
+
"""
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# second word must be integer 1-999 or None or range to parse
|
|
111
|
+
if words[1] is not None:
|
|
112
|
+
try:
|
|
113
|
+
words[1] = int(words[1])
|
|
114
|
+
luelist = [words[1]]
|
|
115
|
+
except ValueError:
|
|
116
|
+
luelist = tsutils.range_to_numlist(words[1])
|
|
117
|
+
for luenum in luelist:
|
|
118
|
+
if luenum < 1 or luenum > 999:
|
|
119
|
+
raise ValueError(
|
|
120
|
+
tsutils.error_wrapper(
|
|
121
|
+
f"""
|
|
122
|
+
The land use element must be an integer from 1 to
|
|
123
|
+
999 inclusive, instead of {luenum}.
|
|
124
|
+
"""
|
|
125
|
+
)
|
|
126
|
+
)
|
|
127
|
+
else:
|
|
128
|
+
luelist = [None]
|
|
129
|
+
|
|
130
|
+
# third word must be a valid group name or None
|
|
131
|
+
if words[2] is not None:
|
|
132
|
+
words[2] = words[2].upper()
|
|
133
|
+
if (words[0] is not None) and (words[2] not in testem[words[0]]):
|
|
134
|
+
raise ValueError(
|
|
135
|
+
tsutils.error_wrapper(
|
|
136
|
+
f"""
|
|
137
|
+
The {words[0]} operation type only allows the variable
|
|
138
|
+
groups: {testem[words[0]][:-1]},
|
|
139
|
+
instead you gave {words[2]}.
|
|
140
|
+
"""
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# fourth word is currently not checked - assumed to be a variable name
|
|
145
|
+
# if not, it will simply never be found in the file, so ok
|
|
146
|
+
# but no warning for the user - add check?
|
|
147
|
+
|
|
148
|
+
# add to new list of checked and expanded lists
|
|
149
|
+
for luenum in luelist:
|
|
150
|
+
words[1] = luenum
|
|
151
|
+
lablist.append(list(words))
|
|
152
|
+
return lablist
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def tuple_match(findme, hay):
|
|
156
|
+
"""Part of partial ordered matching.
|
|
157
|
+
See http://stackoverflow.com/a/4559604
|
|
158
|
+
"""
|
|
159
|
+
return len(findme) == len(hay) and all(
|
|
160
|
+
i is None or j is None or i == j for i, j in zip(findme, hay)
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def tuple_combine(findme, hay):
|
|
165
|
+
"""Part of partial ordered matching.
|
|
166
|
+
See http://stackoverflow.com/a/4559604
|
|
167
|
+
"""
|
|
168
|
+
return tuple(i is None and j or i for i, j in zip(findme, hay))
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def tuple_search(findme, haystack):
|
|
172
|
+
"""Partial ordered matching with 'None' as wildcard
|
|
173
|
+
See http://stackoverflow.com/a/4559604
|
|
174
|
+
"""
|
|
175
|
+
return [
|
|
176
|
+
(index, tuple_combine(findme, hay))
|
|
177
|
+
for index, hay in enumerate(haystack)
|
|
178
|
+
if tuple_match(findme, hay)
|
|
179
|
+
]
|