hspf 2.0.3__tar.gz → 2.1.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.
- {hspf-2.0.3 → hspf-2.1.0}/PKG-INFO +1 -1
- {hspf-2.0.3 → hspf-2.1.0}/pyproject.toml +1 -1
- hspf-2.1.0/src/hspf/bin/WinHSPFLt/WinHspfLt.exe +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/hbn.py +10 -35
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/hspfModel.py +32 -12
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/parser/graph.py +158 -85
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/parser/parsers.py +25 -2
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/reports.py +1 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/uci.py +62 -5
- hspf-2.1.0/tests/test_graph.py +86 -0
- hspf-2.0.3/tests/test_graph.py +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/.gitattributes +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/.gitignore +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/MANIFEST.in +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/__init__.py +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/data/ParseTable.csv +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/data/Timeseries Catalog/IMPLND/IQUAL.txt +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/data/Timeseries Catalog/IMPLND/IWATER.txt +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/data/Timeseries Catalog/IMPLND/IWTGAS.txt +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/data/Timeseries Catalog/IMPLND/SOLIDS.txt +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/data/Timeseries Catalog/PERLND/MSTLAY.txt +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/data/Timeseries Catalog/PERLND/PQUAL.txt +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/data/Timeseries Catalog/PERLND/PSTEMP.txt +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/data/Timeseries Catalog/PERLND/PWATER.txt +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/data/Timeseries Catalog/PERLND/PWATGAS.txt +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/data/Timeseries Catalog/PERLND/SEDMNT.txt +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/data/Timeseries Catalog/PERLND/SNOW.txt +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/data/Timeseries Catalog/RCHRES/CONS.txt +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/data/Timeseries Catalog/RCHRES/GQUAL.txt +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/data/Timeseries Catalog/RCHRES/HTRCH.txt +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/data/Timeseries Catalog/RCHRES/HYDR.txt +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/data/Timeseries Catalog/RCHRES/NUTRX.txt +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/data/Timeseries Catalog/RCHRES/OXRX.txt +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/data/Timeseries Catalog/RCHRES/PLANK.txt +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/data/Timeseries Catalog/RCHRES/SEDTRN.txt +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/helpers.py +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/parser/__init__.py +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/wdm.py +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/src/hspf/wdmReader.py +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/tests/__init__.py +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/tests/data/Clearwater.tpl +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/tests/data/Clearwater.uci +0 -0
- {hspf-2.0.3 → hspf-2.1.0}/tests/test_uci.py +0 -0
|
Binary file
|
|
@@ -176,6 +176,12 @@ class hbnInterface:
|
|
|
176
176
|
def get_multiple_timeseries(self,t_opn,t_code,t_con,opnids = None,activity = None,axis = 1):
|
|
177
177
|
return pd.concat([hbn.get_multiple_timeseries(t_opn,t_code,t_con,opnids,activity) for hbn in self.hbns],axis = 1)
|
|
178
178
|
|
|
179
|
+
def get_perlnd_constituent(self,constituent,perlnd_ids = None,time_step = 5):
|
|
180
|
+
return get_simulated_perlnd_constituent(self,constituent,time_step)
|
|
181
|
+
|
|
182
|
+
def get_implnd_constituent(self,constituent,implnd_ids = None,time_step = 5):
|
|
183
|
+
return get_simulated_implnd_constituent(self,constituent,time_step)
|
|
184
|
+
|
|
179
185
|
def get_reach_constituent(self,constituent,reach_ids,time_step,unit = None):
|
|
180
186
|
if constituent == 'Q':
|
|
181
187
|
df = get_simulated_flow(self,time_step,reach_ids,unit = unit)
|
|
@@ -209,48 +215,17 @@ class hbnInterface:
|
|
|
209
215
|
|
|
210
216
|
return df
|
|
211
217
|
|
|
212
|
-
|
|
218
|
+
|
|
213
219
|
def get_rchres_data(self,constituent,reach_ids,units = 'mg/l',t_code = 'daily'):
|
|
214
220
|
'''
|
|
215
221
|
Convience function for accessing the hbn time series associated with our current
|
|
216
222
|
calibration method. Assumes you are summing across all dataframes.
|
|
217
|
-
|
|
218
|
-
Parameters
|
|
219
|
-
----------
|
|
220
|
-
hbn : TYPE
|
|
221
|
-
DESCRIPTION.
|
|
222
|
-
nutrient_id : TYPE
|
|
223
|
-
DESCRIPTION.
|
|
224
|
-
reach_ids : TYPE
|
|
225
|
-
DESCRIPTION.
|
|
226
|
-
flux : TYPE, optional
|
|
227
|
-
DESCRIPTION. The default is None.
|
|
228
|
-
|
|
229
|
-
Returns
|
|
230
|
-
-------
|
|
231
|
-
df : TYPE
|
|
232
|
-
DESCRIPTION.
|
|
233
|
-
|
|
234
|
-
'''
|
|
235
|
-
|
|
236
|
-
|
|
223
|
+
'''
|
|
237
224
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
df = pd.concat([self.get_multiple_timeseries(t_opn = 'RCHRES',
|
|
243
|
-
t_code =t_code,
|
|
244
|
-
t_con = t_con,
|
|
245
|
-
opnids = reach_ids)
|
|
246
|
-
for t_con in t_cons],axis = 1).sum(1).to_frame()
|
|
247
|
-
|
|
248
|
-
if (constituent == 'Q') & (units == 'cfs'):
|
|
249
|
-
df = df/CF2CFS[t_code]*43560 #Acrfeet/invl to cubic feet/s
|
|
250
|
-
|
|
225
|
+
df = pd.concat([self.get_reach_constituent(constituent,[reach_id],t_code,units) for reach_id in reach_ids], axis = 1)
|
|
226
|
+
df.columns = reach_ids
|
|
251
227
|
df.attrs['unit'] = units
|
|
252
228
|
df.attrs['constituent'] = constituent
|
|
253
|
-
df.attrs['reach_ids'] = reach_ids
|
|
254
229
|
return df
|
|
255
230
|
|
|
256
231
|
|
|
@@ -30,7 +30,7 @@ class hspfModel():
|
|
|
30
30
|
|
|
31
31
|
# Imposed structures of an hspf model:
|
|
32
32
|
# 1. all model files are located in the same directory as the uci file.
|
|
33
|
-
def __init__(self,uci_file:str):
|
|
33
|
+
def __init__(self,uci_file:str,run_model:bool = False):
|
|
34
34
|
#wdm_files:list = None,
|
|
35
35
|
#hbn_files:str = None):
|
|
36
36
|
# Inputs
|
|
@@ -39,7 +39,7 @@ class hspfModel():
|
|
|
39
39
|
self.wdm_paths = []
|
|
40
40
|
self.uci_file = Path(uci_file).resolve()
|
|
41
41
|
# Validate and load binary data
|
|
42
|
-
self.validate_uci()
|
|
42
|
+
self.validate_uci(run_model = run_model)
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
self.hbns = hbn.hbnInterface(self.hbn_paths)
|
|
@@ -51,8 +51,28 @@ class hspfModel():
|
|
|
51
51
|
# Compositions
|
|
52
52
|
self.reports = Reports(self.uci,self.hbns,self.wdms)
|
|
53
53
|
|
|
54
|
+
|
|
55
|
+
def validate_wdms(self):
|
|
56
|
+
# Ensure wdm files exist and the folders for the other file types exist relative
|
|
57
|
+
# to the uci path
|
|
58
|
+
|
|
59
|
+
for index, row in self.uci.table('FILES',drop_comments = False).iterrows():
|
|
60
|
+
file_path = self.uci_file.parent.joinpath(Path(row['FILENAME']))
|
|
61
|
+
if file_path.suffix.lower() == '.wdm':
|
|
62
|
+
assert file_path.exists(),'File Specified in the UCI does not exist:' + file_path.as_posix()
|
|
63
|
+
self.wdm_paths.append(file_path)
|
|
54
64
|
|
|
55
|
-
def
|
|
65
|
+
def validate_pltgens(self):
|
|
66
|
+
raise NotImplementedError()
|
|
67
|
+
|
|
68
|
+
def validate_folders(self):
|
|
69
|
+
for index, row in self.uci.table('FILES',drop_comments = False).iterrows():
|
|
70
|
+
file_path = self.uci_file.parent.joinpath(Path(row['FILENAME']))
|
|
71
|
+
assert file_path.parent.exists(),'File folder Specified in the UCI does not exist: ' + file_path.as_posix()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def validate_uci(self,run_model:bool = False):
|
|
56
76
|
# Ensure wdm files exist and the folders for the other file types exist relative
|
|
57
77
|
# to the uci path
|
|
58
78
|
|
|
@@ -63,15 +83,15 @@ class hspfModel():
|
|
|
63
83
|
self.wdm_paths.append(file_path)
|
|
64
84
|
elif file_path.suffix.lower() == '.hbn':
|
|
65
85
|
assert file_path.parent.exists(),'File folder Specified in the UCI does not exist: ' + file_path.as_posix()
|
|
66
|
-
|
|
67
|
-
if file_path.exists():
|
|
68
|
-
#self.hbns[file_path.name.split('.')[0]] = hbn.hbnClass(file_path)
|
|
69
|
-
self.hbn_paths.append(file_path)
|
|
70
|
-
else:
|
|
71
|
-
self.run_model()
|
|
86
|
+
self.hbn_paths.append(file_path)
|
|
72
87
|
else:
|
|
73
88
|
assert file_path.parent.exists(),'File folder Specified in the UCI does not exist: ' + file_path.as_posix()
|
|
74
89
|
|
|
90
|
+
if (all(file_path.exists() for file_path in self.hbn_paths)) & (run_model == False):
|
|
91
|
+
pass
|
|
92
|
+
else:
|
|
93
|
+
self.run_model()
|
|
94
|
+
|
|
75
95
|
def run_model(self,new_uci_file = None):
|
|
76
96
|
|
|
77
97
|
if new_uci_file is None:
|
|
@@ -80,14 +100,14 @@ class hspfModel():
|
|
|
80
100
|
# new_uci_file = self.model_path.joinpath(uci_name)
|
|
81
101
|
# self.uci.write(new_uci_file)
|
|
82
102
|
subprocess.run([self.winHSPF,self.uci_file.as_posix()]) #, stdout=subprocess.PIPE, creationflags=0x08000000)
|
|
83
|
-
self.load_uci(new_uci_file)
|
|
103
|
+
self.load_uci(new_uci_file,run_model = False)
|
|
84
104
|
|
|
85
105
|
def load_hbn(self,hbn_name):
|
|
86
106
|
self.hbns[hbn_name] = hbn.hbnClass(self.uci_file.parent.joinpath(hbn_name).as_posix())
|
|
87
107
|
|
|
88
|
-
def load_uci(self,uci_file):
|
|
108
|
+
def load_uci(self,uci_file,run_model:bool = False):
|
|
89
109
|
self.uci = UCI(uci_file)
|
|
90
|
-
self.validate_uci()
|
|
110
|
+
self.validate_uci(run_model = run_model)
|
|
91
111
|
|
|
92
112
|
def convert_wdms(self):
|
|
93
113
|
for wdm_file in self.wdm_paths:
|
|
@@ -4,10 +4,12 @@ Created on Thu Feb 6 14:50:45 2025
|
|
|
4
4
|
|
|
5
5
|
@author: mfratki
|
|
6
6
|
"""
|
|
7
|
+
|
|
7
8
|
import networkx as nx
|
|
8
9
|
import pandas as pd
|
|
9
10
|
import numpy as np
|
|
10
11
|
import math
|
|
12
|
+
from itertools import chain
|
|
11
13
|
|
|
12
14
|
class Node(object):
|
|
13
15
|
nodes = []
|
|
@@ -18,7 +20,37 @@ class Node(object):
|
|
|
18
20
|
def __str__(self):
|
|
19
21
|
return self._label
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# G = nx.MultiDiGraph()
|
|
26
|
+
# reach_nodes = schematic[['TVOL','TVOLNO']].drop_duplicates().reset_index(drop=True).reset_index()
|
|
27
|
+
# nodes = schematic.loc[schematic['SVOL'].isin(['IMPLND','PERLND','GENER'])][['SVOL','SVOLNO']].reset_index(drop=True).reset_index()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# reach_nodes.rename(columns = {'index':'TNODE'},inplace=True)
|
|
31
|
+
# nodes.rename(columns = {'index':'SNODE','TVOL':'OPERATION','TVOLNO':'OPNID'},inplace=True)
|
|
32
|
+
# [G.add_node(row['TNODE'], id = row['TNODE'], category = 'OPERATION', type_id = row['TVOLNO'], type = row['RCHRES'] ) for node,label in reach_nodes.iterrows()]
|
|
33
|
+
|
|
34
|
+
# df = pd.merge(schematic,reach_nodes,right_on = ['TVOL','TVOLNO'],left_on = ['TVOL','TVOLNO']).reset_index()
|
|
35
|
+
# df.rename(columns = {'index':'SNODE'},inplace=True)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# for index, row in df.iterrows():
|
|
39
|
+
# if row['SVOL'] == 'GENER':
|
|
40
|
+
# G.add_edge(row['SNODE'],row['TNODE'],
|
|
41
|
+
# mlno = row['MLNO'],
|
|
42
|
+
# count = row['AFACTR'],
|
|
43
|
+
# tmemsb1 = row['TMEMSB1'],
|
|
44
|
+
# tmemsb2 = row['TMEMSB2'])
|
|
45
|
+
# else:
|
|
46
|
+
# G.add_edge(row['SNODE'],row['TNODE'],
|
|
47
|
+
# mlno = row['MLNO'],
|
|
48
|
+
# area = row['AFACTR'],
|
|
49
|
+
# tmemsb1 = row['TMEMSB1'],
|
|
50
|
+
# tmemsb2 = row['TMEMSB2'])
|
|
51
|
+
|
|
52
|
+
# G = nx.from_pandas_edgelist(df,'SNODE','TNODE',edge_attr = True,edge_key = 'SNODE', create_using=nx.MultiDiGraph())
|
|
53
|
+
|
|
22
54
|
def create_graph(uci):
|
|
23
55
|
|
|
24
56
|
|
|
@@ -46,9 +78,9 @@ def create_graph(uci):
|
|
|
46
78
|
# Nodes in the schematic block that are missing from the opn sequence block (usually the outlet reach)
|
|
47
79
|
#schematic.loc[schematic.index.map(labels).isna()]
|
|
48
80
|
schematic = schematic.loc[schematic[['snode','tnode']].dropna().index] # For now remove that missing node
|
|
49
|
-
schematic.loc[:,'TMEMSB1'].replace('',pd.NA
|
|
50
|
-
schematic.loc[:,'TMEMSB2'].replace('',pd.NA
|
|
51
|
-
schematic.loc[:,'MLNO'].replace('',pd.NA
|
|
81
|
+
schematic.loc[:,'TMEMSB1'] = schematic['TMEMSB1'].replace('',pd.NA)
|
|
82
|
+
schematic.loc[:,'TMEMSB2'] = schematic['TMEMSB2'].replace('',pd.NA)
|
|
83
|
+
schematic.loc[:,'MLNO'] = schematic['MLNO'].replace('',pd.NA)
|
|
52
84
|
|
|
53
85
|
schematic = schematic.astype({'snode': int,'tnode':int,'MLNO':pd.Int64Dtype(),'TMEMSB1':pd.Int64Dtype(),'TMEMSB2':pd.Int64Dtype()})
|
|
54
86
|
for index, row in schematic.iterrows():
|
|
@@ -169,19 +201,28 @@ def nodes(G,node_type,node_type_id,adjacent_node_type):
|
|
|
169
201
|
|
|
170
202
|
#%% Methods using node_type, node_type_id interface
|
|
171
203
|
|
|
172
|
-
def upstream_network(G,
|
|
173
|
-
|
|
174
|
-
|
|
204
|
+
def upstream_network(G,reach_ids):
|
|
205
|
+
node_ids = [get_node_id(G,'RCHRES',reach_id) for reach_id in reach_ids]
|
|
206
|
+
# Initialize an empty set to store all unique ancestors
|
|
207
|
+
|
|
208
|
+
all_ancestors = set()
|
|
209
|
+
# Iterate through the target nodes and find ancestors for each
|
|
210
|
+
for node_id in node_ids:
|
|
211
|
+
ancestors_of_node = nx.ancestors(G, node_id)
|
|
212
|
+
all_ancestors.update(ancestors_of_node) # Add ancestors to the combined set
|
|
213
|
+
|
|
214
|
+
all_ancestors.update(node_ids) # Include the target nodes themselves
|
|
215
|
+
return G.subgraph(all_ancestors).copy()
|
|
216
|
+
#return G.subgraph([node_id] + list(nx.ancestors(G,node_id))).copy()
|
|
175
217
|
|
|
176
218
|
def downstream_network(G,reach_id):
|
|
177
219
|
node_id = get_node_id(G,'RCHRES',reach_id)
|
|
178
220
|
return G.subgraph([node_id] + list(nx.descendants(G,node_id))).copy()
|
|
179
221
|
|
|
180
|
-
def subset_network(G,
|
|
181
|
-
G = upstream_network(G,
|
|
222
|
+
def subset_network(G,reach_ids,upstream_reach_ids = None):
|
|
223
|
+
G = upstream_network(G,reach_ids)
|
|
182
224
|
if upstream_reach_ids is not None:
|
|
183
|
-
|
|
184
|
-
G.remove_nodes_from(get_node_ids(upstream_network(G,upstream_reach_id),'RCHRES'))
|
|
225
|
+
G.remove_nodes_from(get_node_ids(upstream_network(G,upstream_reach_ids),'RCHRES'))
|
|
185
226
|
#assert([len(sinks(G)) == 0,sinks(G)[0] == reach_id])
|
|
186
227
|
return G
|
|
187
228
|
|
|
@@ -259,8 +300,8 @@ def routing_reachs(G):
|
|
|
259
300
|
def is_routing(G,reach_id):
|
|
260
301
|
return all([node['type'] not in ['PERLND', 'IMPLND'] for node in adjacent_nodes(G,reach_id)])
|
|
261
302
|
|
|
262
|
-
def watershed_area(G,reach_ids):
|
|
263
|
-
return float(np.nansum(list(nx.get_edge_attributes(make_watershed(G,reach_ids),'area').values())))
|
|
303
|
+
def watershed_area(G,reach_ids,upstream_reach_ids = None):
|
|
304
|
+
return float(np.nansum(list(nx.get_edge_attributes(make_watershed(G,reach_ids,upstream_reach_ids),'area').values())))
|
|
264
305
|
|
|
265
306
|
def catchment_area(G,reach_id):
|
|
266
307
|
return float(np.nansum(list(nx.get_edge_attributes(make_catchment(G,reach_id),'area').values())))
|
|
@@ -300,22 +341,47 @@ def make_catchment(G,reach_id):
|
|
|
300
341
|
nx.set_node_attributes(catchment,node_id,'catchment_id')
|
|
301
342
|
return catchment
|
|
302
343
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
def make_watershed(G,reach_ids):
|
|
344
|
+
def make_watershed(G,reach_ids,upstream_reach_ids = None):
|
|
306
345
|
'''
|
|
307
346
|
Creates a sugraph representing the the catchments upstream of the specified hspf model reaches. Note that a negative reach_ids indicate to subtract that area from the total.
|
|
308
347
|
|
|
309
348
|
|
|
310
349
|
'''
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
350
|
+
|
|
351
|
+
node_ids = set(get_node_id(G,'RCHRES',reach_id) for reach_id in reach_ids)
|
|
352
|
+
|
|
353
|
+
# Initialize an empty set to store all unique ancestors
|
|
314
354
|
|
|
355
|
+
# Iterate through the target nodes and find ancestors for each
|
|
356
|
+
all_upstream_reaches = set()
|
|
357
|
+
for node_id in node_ids:
|
|
358
|
+
ancestors_of_node = [node['id'] for node in ancestors(G, node_id,'RCHRES')]
|
|
359
|
+
all_upstream_reaches.update(ancestors_of_node) # Add ancestors to the combined set
|
|
360
|
+
all_upstream_reaches.update(node_ids) # Include the target nodes themselves
|
|
361
|
+
|
|
362
|
+
if upstream_reach_ids is not None:
|
|
363
|
+
upstream_node_ids = set(get_node_id(G,'RCHRES',reach_id) for reach_id in upstream_reach_ids)
|
|
364
|
+
for node_id in upstream_node_ids:
|
|
365
|
+
ancestors_of_node = [node['id'] for node in ancestors(G, node_id,'RCHRES')]
|
|
366
|
+
all_upstream_reaches = all_upstream_reaches - set(ancestors_of_node)
|
|
367
|
+
else:
|
|
368
|
+
upstream_node_ids = set()
|
|
369
|
+
|
|
370
|
+
nodes = set(chain.from_iterable([list(G.predecessors(node_id)) for node_id in all_upstream_reaches])) | node_ids
|
|
371
|
+
nodes = nodes - upstream_node_ids # Include the target nodes themselves
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
return G.subgraph(nodes).copy()
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
# node_ids = set([get_node_id(G,'RCHRES',reach_id) for reach_id in reach_ids if reach_id > 0])
|
|
378
|
+
# nodes_to_exclude = set([get_node_id(G,'RCHRES',abs(reach_id)) for reach_id in reach_ids if reach_id < 0])
|
|
379
|
+
# node_ids = node_ids - nodes_to_exclude
|
|
315
380
|
|
|
316
|
-
nodes = [
|
|
317
|
-
nodes
|
|
318
|
-
nodes
|
|
381
|
+
#nodes = get_opnids(G,'RCHRES',reach_ids,upstream_reach_ids) #[ancestors(G,node_id,'RCHRES')) for node_id in node_ids]
|
|
382
|
+
nodes = subset_network(G,reach_ids,upstream_reach_ids)
|
|
383
|
+
#nodes.append(node_ids)
|
|
384
|
+
#nodes = list(set(chain.from_iterable(nodes)))
|
|
319
385
|
watershed = subgraph(G, nodes)
|
|
320
386
|
catchment_id = '_'.join([str(reach_id) for reach_id in reach_ids])
|
|
321
387
|
nx.set_node_attributes(watershed,node_ids,catchment_id)
|
|
@@ -401,8 +467,17 @@ class Catchment():
|
|
|
401
467
|
def dsn(self,tmemn):
|
|
402
468
|
return [self.catchment.nodes[k[0]]['id'] for k,v in nx.get_edge_attributes(self.catchment,'tmemn').items() if v == tmemn]
|
|
403
469
|
|
|
404
|
-
def to_dataframe():
|
|
405
|
-
|
|
470
|
+
def to_dataframe(self):
|
|
471
|
+
edges = []
|
|
472
|
+
for u, v, edge_data in self.catchment.edges(data=True):
|
|
473
|
+
source_node_attributes = self.catchment.nodes[u]
|
|
474
|
+
# Add or update edge attributes with source node attributes
|
|
475
|
+
edge_data["source_type"] = source_node_attributes.get("type")
|
|
476
|
+
edge_data["source_name"] = source_node_attributes.get("name")
|
|
477
|
+
edge_data["source_type_id"] = source_node_attributes.get("type_id")
|
|
478
|
+
edges.append(edge_data)
|
|
479
|
+
|
|
480
|
+
return pd.DataFrame(edges)
|
|
406
481
|
# def _watershed(G,reach_id):
|
|
407
482
|
|
|
408
483
|
# predecessors = (list(G.predecessors(node)))
|
|
@@ -423,7 +498,17 @@ class Catchment():
|
|
|
423
498
|
|
|
424
499
|
# {source:[node for node in nx.shortest_path(G,source,reach_id)] for source in nx.ancestors(G,reach_id)}
|
|
425
500
|
|
|
426
|
-
|
|
501
|
+
def to_dataframe(G):
|
|
502
|
+
edges = []
|
|
503
|
+
for u, v, edge_data in G.edges(data=True):
|
|
504
|
+
source_node_attributes = G.nodes[u]
|
|
505
|
+
# Add or update edge attributes with source node attributes
|
|
506
|
+
edge_data["source_type"] = source_node_attributes.get("type")
|
|
507
|
+
edge_data["source_name"] = source_node_attributes.get("name")
|
|
508
|
+
edge_data["source_type_id"] = source_node_attributes.get("type_id")
|
|
509
|
+
edges.append(edge_data)
|
|
510
|
+
|
|
511
|
+
return pd.DataFrame(edges)
|
|
427
512
|
|
|
428
513
|
|
|
429
514
|
#%% Legacy Methods for Backwards compatability
|
|
@@ -457,8 +542,16 @@ class reachNetwork():
|
|
|
457
542
|
downstream.insert(0,reach_id)
|
|
458
543
|
return downstream
|
|
459
544
|
|
|
460
|
-
def calibration_order(self,
|
|
461
|
-
|
|
545
|
+
def calibration_order(self,reach_ids,upstream_reach_ids = None):
|
|
546
|
+
'''
|
|
547
|
+
Calibration order of reaches to prevent upstream influences. Equivalent to iteritivlye pruning the network remving nodes with no upstream connections.
|
|
548
|
+
A list of lists is returned where each sublist contains reaches that can be calibrated in parallel.
|
|
549
|
+
|
|
550
|
+
:param self: Description
|
|
551
|
+
:param reach_ids: Description
|
|
552
|
+
:param upstream_reach_ids: Description
|
|
553
|
+
'''
|
|
554
|
+
return calibration_order(make_watershed(self.G,reach_ids,upstream_reach_ids))
|
|
462
555
|
|
|
463
556
|
def station_order(self,reach_ids):
|
|
464
557
|
raise NotImplementedError()
|
|
@@ -478,30 +571,30 @@ class reachNetwork():
|
|
|
478
571
|
'''
|
|
479
572
|
return [node['type_id'] for node in predecessors(self.G,'RCHRES',get_node_id(self.G,'RCHRES',reach_id))]
|
|
480
573
|
|
|
481
|
-
def get_opnids(self,operation,
|
|
574
|
+
def get_opnids(self,operation,reach_ids, upstream_reach_ids = None):
|
|
482
575
|
'''
|
|
483
576
|
Operation IDs with a path to reach_id. Operations upstream of upstream_reach_ids will not be included
|
|
484
577
|
|
|
485
578
|
'''
|
|
486
|
-
return get_opnids(self.G,operation
|
|
487
|
-
|
|
579
|
+
return get_opnids(self.G,operation,reach_ids,upstream_reach_ids)
|
|
488
580
|
def operation_area(self,operation,opnids = None):
|
|
581
|
+
'''
|
|
582
|
+
Area of operation type for specified operation IDs. If None returns all operation areas.
|
|
583
|
+
Equivalent to the schematic table filtered by operation and opnids.
|
|
584
|
+
'''
|
|
585
|
+
|
|
489
586
|
return operation_area(self.uci,operation)
|
|
490
587
|
|
|
491
588
|
def drainage(self,reach_id):
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
edges = []
|
|
495
|
-
for u, v, edge_data in make_catchment(self.G,reach_id).edges(data=True):
|
|
496
|
-
source_node_attributes = self.G.nodes[u]
|
|
497
|
-
# Add or update edge attributes with source node attributes
|
|
498
|
-
edge_data["source_type"] = source_node_attributes.get("type")
|
|
499
|
-
edge_data["source_name"] = source_node_attributes.get("name")
|
|
500
|
-
edge_data["source_type_id"] = source_node_attributes.get("type_id")
|
|
501
|
-
edges.append(edge_data)
|
|
589
|
+
'''
|
|
590
|
+
Docstring for drainage
|
|
502
591
|
|
|
503
|
-
|
|
504
|
-
|
|
592
|
+
:param self: Network class instance
|
|
593
|
+
:param reach_id: Target reach id
|
|
594
|
+
'''
|
|
595
|
+
# Merge source node attributes into edge attributes
|
|
596
|
+
return to_dataframe(make_catchment(self.G,reach_id))
|
|
597
|
+
|
|
505
598
|
def subwatersheds(self,reach_ids = None):
|
|
506
599
|
df = subwatersheds(self.uci)
|
|
507
600
|
if reach_ids is None:
|
|
@@ -520,15 +613,16 @@ class reachNetwork():
|
|
|
520
613
|
def reach_contributions(self,operation,opnids):
|
|
521
614
|
return reach_contributions(self.uci,operation,opnids)
|
|
522
615
|
|
|
523
|
-
def drainage_area(self,reach_ids):
|
|
524
|
-
return watershed_area(self.G,reach_ids)
|
|
616
|
+
def drainage_area(self,reach_ids,upstream_reach_ids = None):
|
|
617
|
+
return watershed_area(self.G,reach_ids,upstream_reach_ids)
|
|
525
618
|
|
|
526
|
-
def drainage_area_landcover(self,
|
|
527
|
-
|
|
528
|
-
areas =
|
|
529
|
-
|
|
530
|
-
if group:
|
|
531
|
-
areas = pd.concat([areas[operation].groupby(
|
|
619
|
+
def drainage_area_landcover(self,reach_ids,upstream_reach_ids = None, group = True):
|
|
620
|
+
areas = to_dataframe(make_watershed(self.G,reach_ids,upstream_reach_ids))
|
|
621
|
+
areas = areas.groupby(['source_type','source_type_id','source_name'])['area'].sum()[['PERLND','IMPLND']]
|
|
622
|
+
|
|
623
|
+
if group:
|
|
624
|
+
areas = pd.concat([areas[operation].groupby('source_name').sum() for operation in ['PERLND','IMPLND']])
|
|
625
|
+
#areas = pd.concat([areas[operation].groupby(self.uci.opnid_dict[operation].loc[areas[operation].index,'LSID'].values).sum() for operation in ['PERLND','IMPLND']])
|
|
532
626
|
return areas
|
|
533
627
|
|
|
534
628
|
def outlets(self):
|
|
@@ -546,49 +640,28 @@ class reachNetwork():
|
|
|
546
640
|
def paths(self,reach_id):
|
|
547
641
|
return paths(self.G,reach_id)
|
|
548
642
|
|
|
549
|
-
|
|
550
|
-
def
|
|
643
|
+
|
|
644
|
+
def get_opnids(G,operation,reach_ids, upstream_reach_ids = None):
|
|
645
|
+
return get_node_type_ids(make_watershed(G,reach_ids,upstream_reach_ids),operation)
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
def calibration_order(G):
|
|
551
649
|
'''
|
|
552
|
-
Determines the order in which the
|
|
650
|
+
Determines the order in which the model reaches should be calibrated to
|
|
553
651
|
prevent upstream influences. Primarily helpful when calibrating sediment and
|
|
554
652
|
adjusting in channel erosion rates.
|
|
555
653
|
'''
|
|
556
654
|
|
|
655
|
+
nodes = get_node_ids(G,'RCHRES')
|
|
656
|
+
G = G.subgraph(nodes).copy()
|
|
557
657
|
order = []
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
nodes_to_remove = [node for node, in_degree in Gsub.in_degree() if in_degree == 0]
|
|
658
|
+
while(len(nodes)) > 0:
|
|
659
|
+
nodes_to_remove = [node for node in nodes if G.in_degree(node) == 0]
|
|
562
660
|
order.append([G.nodes[node]['type_id'] for node in nodes_to_remove])
|
|
563
|
-
|
|
661
|
+
nodes = [node for node in nodes if node not in nodes_to_remove]
|
|
662
|
+
G.remove_nodes_from(nodes_to_remove)
|
|
564
663
|
return order
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
def get_opnids(G,operation,reach_id = None, upstream_reach_ids = None):
|
|
570
|
-
G = subset_network(G,reach_id,upstream_reach_ids)
|
|
571
|
-
return ancestors(G,get_node_id(G,'RCHRES',reach_id),operation)
|
|
572
|
-
perlnds = [node['type_id'] for node in get_nodes(G,'PERLND')]
|
|
573
|
-
implnds = [node['type_id'] for node in get_nodes(G,'IMPLND')]
|
|
574
|
-
reachs = [node['type_id'] for node in get_nodes(G,'RCHRES')]
|
|
575
|
-
return {'RCHRES':reachs,'PERLND':perlnds,'IMPLND':implnds}[operation]
|
|
576
|
-
#return reachs,perlnds,implnds
|
|
577
|
-
|
|
578
|
-
def drainage(uci,reach_ids):
|
|
579
|
-
return subwatersheds(uci).loc[reach_ids].reset_index()[['SVOL','LSID','AFACTR']].groupby(['LSID','SVOL']).sum()
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
def drainage_area(uci,reach_ids,drng_area = 0):
|
|
584
|
-
if len(reach_ids) == 0:
|
|
585
|
-
return drng_area
|
|
586
|
-
else:
|
|
587
|
-
sign = math.copysign(1,reach_ids[0])
|
|
588
|
-
reach_id = int(reach_ids[0]*sign)
|
|
589
|
-
drng_area = drng_area + sign*uci.network.drainage_area(reach_id)
|
|
590
|
-
drainage_area(uci,reach_ids[1:],drng_area)
|
|
591
|
-
|
|
664
|
+
|
|
592
665
|
|
|
593
666
|
def reach_contributions(uci,operation,opnids):
|
|
594
667
|
schematic = uci.table('SCHEMATIC').set_index('SVOL')
|
|
@@ -6,6 +6,7 @@ Created on Fri Oct 7 12:13:23 2022
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from abc import abstractmethod
|
|
9
|
+
from multiprocessing.util import info
|
|
9
10
|
import numpy as np
|
|
10
11
|
import pandas as pd
|
|
11
12
|
from pathlib import Path
|
|
@@ -289,7 +290,29 @@ class masslinkParser(Parser):
|
|
|
289
290
|
table_lines[index] = line[-1]
|
|
290
291
|
|
|
291
292
|
return table_lines
|
|
292
|
-
|
|
293
|
+
|
|
294
|
+
class globalParser(Parser):
|
|
295
|
+
def parse(block,table_name,table_lines):
|
|
296
|
+
table_lines = [line for line in table_lines if '***' not in line]
|
|
297
|
+
data = {
|
|
298
|
+
'description' : table_lines[0].strip(),
|
|
299
|
+
'start_date' : table_lines[1].split('END')[0].split()[1],
|
|
300
|
+
'start_hour' : int(table_lines[1].split('END')[0].split()[2][:2])-1,
|
|
301
|
+
'end_date' : table_lines[1].strip().split('END')[1].split()[0],
|
|
302
|
+
'end_hour' : int(table_lines[1].strip().split('END')[1].split()[1][:2])-1,
|
|
303
|
+
'echo_flag1' : int(table_lines[2].split()[-2]),
|
|
304
|
+
'echo_flag2' : int(table_lines[3].split()[-1]),
|
|
305
|
+
'units_flag' : int(table_lines[3].split()[5]),
|
|
306
|
+
'resume_flag': int(table_lines[3].split()[1]),
|
|
307
|
+
'run_flag': int(table_lines[3].split()[3])
|
|
308
|
+
}
|
|
309
|
+
df = pd.DataFrame([data])
|
|
310
|
+
df['comments'] = ''
|
|
311
|
+
return df
|
|
312
|
+
|
|
313
|
+
def write(block,table_name,table):
|
|
314
|
+
raise NotImplementedError()
|
|
315
|
+
|
|
293
316
|
class specactionsParser(Parser):
|
|
294
317
|
def parse(block,table,lines):
|
|
295
318
|
raise NotImplementedError()
|
|
@@ -304,7 +327,7 @@ class externalsourcesParser():
|
|
|
304
327
|
def write(block,table,lines):
|
|
305
328
|
raise NotImplementedError()
|
|
306
329
|
|
|
307
|
-
parserSelector = {'GLOBAL':
|
|
330
|
+
parserSelector = {'GLOBAL':globalParser,
|
|
308
331
|
'FILES':standardParser,
|
|
309
332
|
'OPN SEQUENCE':opnsequenceParser,
|
|
310
333
|
'PERLND':operationsParser,
|
|
@@ -325,6 +325,7 @@ def ann_avg_subwatershed_loading(constituent,uci,hbn):
|
|
|
325
325
|
return df
|
|
326
326
|
|
|
327
327
|
def ann_avg_watershed_loading(constituent,reach_ids,uci,hbn, by_landcover = False):
|
|
328
|
+
reach_ids = [item for sublist in [uci.network._upstream(reach_id) for reach_id in reach_ids] for item in sublist]
|
|
328
329
|
df = ann_avg_constituent_loading(constituent,uci,hbn)
|
|
329
330
|
df = df.loc[df['TVOLNO'].isin(reach_ids)]
|
|
330
331
|
if by_landcover:
|
|
@@ -8,6 +8,7 @@ Created on Mon Jul 11 08:39:57 2022
|
|
|
8
8
|
|
|
9
9
|
#lines = reader('C:/Users/mfratki/Documents/Projects/LacQuiParle/ucis/LacQuiParle_0.uci')
|
|
10
10
|
import subprocess
|
|
11
|
+
import sys
|
|
11
12
|
import numpy as np
|
|
12
13
|
import pandas as pd
|
|
13
14
|
from .parser.parsers import Table
|
|
@@ -78,7 +79,7 @@ class UCI():
|
|
|
78
79
|
|
|
79
80
|
def table(self,block,table_name = 'na',table_id = 0,drop_comments = True):
|
|
80
81
|
# Dynamic parsing of tables when called by user
|
|
81
|
-
assert block in ['FILES','PERLND','IMPLND','RCHRES','SCHEMATIC','OPN SEQUENCE','MASS-LINK','EXT SOURCES','NETWORK','GENER','MONTH-DATA','EXT TARGETS','COPY','FTABLES']
|
|
82
|
+
assert block in ['GLOBAL','FILES','PERLND','IMPLND','RCHRES','SCHEMATIC','OPN SEQUENCE','MASS-LINK','EXT SOURCES','NETWORK','GENER','MONTH-DATA','EXT TARGETS','COPY','FTABLES']
|
|
82
83
|
|
|
83
84
|
table = self.uci[(block,table_name,table_id)] #[block][table_name][table_id]
|
|
84
85
|
#TODO move the format_opnids into the Table class?
|
|
@@ -103,7 +104,7 @@ class UCI():
|
|
|
103
104
|
self.uci[(block,table_name,table_id)].replace(table)
|
|
104
105
|
|
|
105
106
|
def table_lines(self,block,table_name = 'na',table_id = 0):
|
|
106
|
-
return self.uci[(block,table_name,table_id)].lines
|
|
107
|
+
return self.uci[(block,table_name,table_id)].lines.copy()
|
|
107
108
|
|
|
108
109
|
def comments(block,table_name = None,table_id = 0): # comments of a table
|
|
109
110
|
raise NotImplementedError()
|
|
@@ -177,6 +178,43 @@ class UCI():
|
|
|
177
178
|
lines += ['END RUN']
|
|
178
179
|
self.lines = lines
|
|
179
180
|
|
|
181
|
+
def set_simulation_period(self,start_year,end_year):
|
|
182
|
+
# Update GLOBAL table with new start and end dates very janky implementation but not a priority.
|
|
183
|
+
|
|
184
|
+
# if start_hour < 10:
|
|
185
|
+
# start_hour = f'0{int(start_hour+1)}:00'
|
|
186
|
+
# else:
|
|
187
|
+
# start_hour = f'{int(start_hour+1)}:00'
|
|
188
|
+
|
|
189
|
+
# if end_hour < 10:
|
|
190
|
+
# end_hour = f'0{int(end_hour+1)}:00'
|
|
191
|
+
# else:
|
|
192
|
+
# end_hour = f'{int(end_hour+1)}:00'
|
|
193
|
+
|
|
194
|
+
table_lines = self.table_lines('GLOBAL')
|
|
195
|
+
for index, line in enumerate(table_lines):
|
|
196
|
+
if '***' in line: #in case there are comments in the global block
|
|
197
|
+
continue
|
|
198
|
+
elif line.strip().startswith('START'):
|
|
199
|
+
table_lines[index] = line[0:14] + f'{start_year}/01/01 00:00 ' + f'END {end_year}/12/31 24:00'
|
|
200
|
+
else:
|
|
201
|
+
continue
|
|
202
|
+
|
|
203
|
+
self.uci[('GLOBAL','na',0)].lines = table_lines
|
|
204
|
+
|
|
205
|
+
def set_echo_flags(self,flag1,flag2):
|
|
206
|
+
table_lines = self.table_lines('GLOBAL')
|
|
207
|
+
for index, line in enumerate(table_lines):
|
|
208
|
+
if '***' in line: #in case there are comments in the global block
|
|
209
|
+
continue
|
|
210
|
+
elif line.strip().startswith('RUN INTERP OUTPT LEVELS'):
|
|
211
|
+
table_lines[index] = f' RUN INTERP OUTPT LEVELS {flag1} {flag2}'
|
|
212
|
+
else:
|
|
213
|
+
continue
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
self.uci[('GLOBAL','na',0)].lines = table_lines
|
|
217
|
+
|
|
180
218
|
|
|
181
219
|
def _write(self,filepath):
|
|
182
220
|
with open(filepath, 'w') as the_file:
|
|
@@ -211,6 +249,9 @@ class UCI():
|
|
|
211
249
|
self.merge_lines()
|
|
212
250
|
self._write(new_uci_path)
|
|
213
251
|
|
|
252
|
+
def _run(self,wait_for_completion=True):
|
|
253
|
+
run_model(self.filepath, wait_for_completion=wait_for_completion)
|
|
254
|
+
|
|
214
255
|
def update_bino(self,name):
|
|
215
256
|
#TODO: Move up to busniess/presentation layer
|
|
216
257
|
table = self.table('FILES',drop_comments = False) # initialize the table
|
|
@@ -325,9 +366,25 @@ class UCI():
|
|
|
325
366
|
|
|
326
367
|
#TODO: More conveince methods that should probably be in a separate module
|
|
327
368
|
|
|
328
|
-
def run_model(uci_file):
|
|
329
|
-
winHSPF = str(Path(__file__).resolve().parent.parent) + '\\bin\\
|
|
330
|
-
|
|
369
|
+
def run_model(uci_file, wait_for_completion=True):
|
|
370
|
+
winHSPF = str(Path(__file__).resolve().parent.parent) + '\\bin\\WinHSPFlt\\WinHspfLt.exe'
|
|
371
|
+
|
|
372
|
+
# Arguments for the subprocess
|
|
373
|
+
args = [winHSPF, uci_file.as_posix()]
|
|
374
|
+
|
|
375
|
+
if wait_for_completion:
|
|
376
|
+
# Use subprocess.run to wait for the process to complete (original behavior)
|
|
377
|
+
subprocess.run(args)
|
|
378
|
+
else:
|
|
379
|
+
# Use subprocess.Popen to run the process in the background without waiting
|
|
380
|
+
# On Windows, you can use creationflags to prevent a console window from appearing
|
|
381
|
+
if sys.platform.startswith('win'):
|
|
382
|
+
# Use a variable for the flag to ensure it's only used on Windows
|
|
383
|
+
creationflags = subprocess.CREATE_NO_WINDOW
|
|
384
|
+
subprocess.Popen(args, creationflags=creationflags)
|
|
385
|
+
else:
|
|
386
|
+
# For other platforms (like Linux/macOS), Popen without special flags works fine
|
|
387
|
+
subprocess.Popen(args)
|
|
331
388
|
|
|
332
389
|
def get_filepaths(uci,file_extension):
|
|
333
390
|
files = uci.table('FILES')
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from hspf.parser import graph
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from hspf.uci import UCI
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
uci = UCI(Path(__file__).parent.joinpath('data\Clearwater.uci'))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_lakes():
|
|
13
|
+
assert uci.network._lakes() == [12,
|
|
14
|
+
32,
|
|
15
|
+
52,
|
|
16
|
+
112,
|
|
17
|
+
114,
|
|
18
|
+
140,
|
|
19
|
+
152,
|
|
20
|
+
172,
|
|
21
|
+
214,
|
|
22
|
+
272,
|
|
23
|
+
434,
|
|
24
|
+
442,
|
|
25
|
+
446,
|
|
26
|
+
502,
|
|
27
|
+
504,
|
|
28
|
+
512,
|
|
29
|
+
522,
|
|
30
|
+
532,
|
|
31
|
+
542,
|
|
32
|
+
592,
|
|
33
|
+
594,
|
|
34
|
+
596,
|
|
35
|
+
636]
|
|
36
|
+
|
|
37
|
+
def test_calibration_order():
|
|
38
|
+
orders = graph.calibration_order(graph.make_watershed(uci.network.G,[90]))
|
|
39
|
+
test_orders = [[52,53,10,12,32,71],
|
|
40
|
+
[55,13],
|
|
41
|
+
[30],
|
|
42
|
+
[50],
|
|
43
|
+
[70],
|
|
44
|
+
[90]]
|
|
45
|
+
assert(len(orders) == len(test_orders))
|
|
46
|
+
for order,test_order in zip(orders,test_orders):
|
|
47
|
+
assert set(order) == set(test_order)
|
|
48
|
+
|
|
49
|
+
def test_get_opnids():
|
|
50
|
+
opnids = uci.network.get_opnids('RCHRES',[90])
|
|
51
|
+
expected_opnids = [10,12,13,30,32,50,52,53,55,70,71,90]
|
|
52
|
+
assert set(opnids) == set(expected_opnids)
|
|
53
|
+
|
|
54
|
+
'''
|
|
55
|
+
Methods of the Network class:
|
|
56
|
+
_downstream',
|
|
57
|
+
'_lakes',
|
|
58
|
+
'_routing_reaches',
|
|
59
|
+
'_upstream',
|
|
60
|
+
'calibration_order',
|
|
61
|
+
'catchment_ids',
|
|
62
|
+
'downstream',
|
|
63
|
+
'drainage',
|
|
64
|
+
'drainage_area',
|
|
65
|
+
'drainage_area_landcover',
|
|
66
|
+
'get_node_type_ids',
|
|
67
|
+
'get_opnids',
|
|
68
|
+
'lake_area',
|
|
69
|
+
'lakes',
|
|
70
|
+
'operation_area',
|
|
71
|
+
'outlets',
|
|
72
|
+
'paths',
|
|
73
|
+
'reach_contributions',
|
|
74
|
+
'routing_reaches',
|
|
75
|
+
'schematic',
|
|
76
|
+
'station_order',
|
|
77
|
+
'subwatershed',
|
|
78
|
+
'subwatershed_area',
|
|
79
|
+
'subwatersheds',
|
|
80
|
+
'uci',
|
|
81
|
+
'upstream'
|
|
82
|
+
'''
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
hspf-2.0.3/tests/test_graph.py
DELETED
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|