pyhcal 1.1.0__tar.gz → 1.1.2__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.
- pyhcal-1.1.2/ERROR.FIL +6 -0
- {pyhcal-1.1.0 → pyhcal-1.1.2}/PKG-INFO +1 -1
- {pyhcal-1.1.0 → pyhcal-1.1.2}/pyproject.toml +6 -1
- pyhcal-1.1.2/src/pyhcal/ERROR.FIL +78 -0
- {pyhcal-1.1.0 → pyhcal-1.1.2}/src/pyhcal/calibrators.py +54 -117
- pyhcal-1.1.2/src/pyhcal/mappers.py +113 -0
- {pyhcal-1.1.0 → pyhcal-1.1.2}/src/pyhcal/repository.py +4 -4
- {pyhcal-1.1.0 → pyhcal-1.1.2}/src/pyhcal/setup_utils.py +38 -27
- pyhcal-1.1.0/src/pyhcal/data/WISKI_EQUIS_XREF.csv +0 -25789
- pyhcal-1.1.0/src/pyhcal/data/outlets.duckdb +0 -0
- pyhcal-1.1.0/src/pyhcal/data/stations_EQUIS.gpkg +0 -0
- pyhcal-1.1.0/src/pyhcal/data/stations_wiski.gpkg +0 -0
- pyhcal-1.1.0/src/pyhcal/modl_db.py +0 -319
- {pyhcal-1.1.0 → pyhcal-1.1.2}/.gitattributes +0 -0
- {pyhcal-1.1.0 → pyhcal-1.1.2}/.gitignore +0 -0
- {pyhcal-1.1.0 → pyhcal-1.1.2}/src/pyhcal/__init__.py +0 -0
- {pyhcal-1.1.0 → pyhcal-1.1.2}/src/pyhcal/data/HUC_Names.csv +0 -0
- {pyhcal-1.1.0 → pyhcal-1.1.2}/src/pyhcal/figures.py +0 -0
- {pyhcal-1.1.0 → pyhcal-1.1.2}/src/pyhcal/metrics.py +0 -0
pyhcal-1.1.2/ERROR.FIL
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
11:01:39.908 : LOG_MSG:ERROR.FIL OPENED
|
|
2
|
+
11:01:39.909 : HASS_ENT:F90_WDBOPNR:entr:WDMSFL,RWFLG: 100 1 C:\Users\mfratki\Documents\github\pyHSPF\src\hspf\bin\WinHSPFLt\hspfmsg.wdm
|
|
3
|
+
11:01:39.910 : HASS_ENT:F90_WDBOPNR:exit:WDMSFL,RETCOD 100 0
|
|
4
|
+
FILBLK RETCOD 0
|
|
5
|
+
wdmfl 0 0 0 0
|
|
6
|
+
FILBLK RETCOD 0
|
|
@@ -5,7 +5,7 @@ build-backend = "hatchling.build"
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pyhcal"
|
|
7
7
|
urls = { "Homepage" = "https://github.com/mfratkin1/pyhcal" } # ? Add this!
|
|
8
|
-
version = "1.1.
|
|
8
|
+
version = "1.1.2"
|
|
9
9
|
dependencies = [
|
|
10
10
|
"hspf",
|
|
11
11
|
"mpcaHydro",
|
|
@@ -27,4 +27,9 @@ keywords = ["HSPF","Hydrology"]
|
|
|
27
27
|
classifiers = [
|
|
28
28
|
"Development Status :: 3 - Alpha",
|
|
29
29
|
"Programming Language :: Python"
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[tool.hatch.build]
|
|
33
|
+
exclude = [
|
|
34
|
+
"/tests"
|
|
30
35
|
]
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
16:15:00.416 : LOG_MSG:ERROR.FIL OPENED
|
|
2
|
+
16:15:00.417 : HASS_ENT:F90_WDBOPNR:entr:WDMSFL,RWFLG: 100 1 C:\Users\mfratki\Documents\github\pyHSPF\src\hspf\bin\WinHSPFLt\hspfmsg.wdm
|
|
3
|
+
16:15:00.417 : HASS_ENT:F90_WDBOPNR:exit:WDMSFL,RETCOD 100 0
|
|
4
|
+
FILBLK RETCOD 0
|
|
5
|
+
wdmfl 0 0 0 0
|
|
6
|
+
FILBLK RETCOD 0
|
|
7
|
+
18:06:13.144 : LOG_MSG:ERROR.FIL OPENED
|
|
8
|
+
18:06:13.145 : HASS_ENT:F90_WDBOPNR:entr:WDMSFL,RWFLG: 100 1 C:\Users\mfratki\Documents\github\pyHSPF\src\hspf\bin\WinHSPFLt\hspfmsg.wdm
|
|
9
|
+
18:06:13.145 : HASS_ENT:F90_WDBOPNR:exit:WDMSFL,RETCOD 100 0
|
|
10
|
+
FILBLK RETCOD 0
|
|
11
|
+
wdmfl 0 0 0 0
|
|
12
|
+
FILBLK RETCOD 0
|
|
13
|
+
18:09:53.704 : LOG_MSG:ERROR.FIL OPENED
|
|
14
|
+
18:09:53.705 : HASS_ENT:F90_WDBOPNR:entr:WDMSFL,RWFLG: 100 1 C:\Users\mfratki\Documents\github\pyHSPF\src\hspf\bin\WinHSPFLt\hspfmsg.wdm
|
|
15
|
+
18:09:53.705 : HASS_ENT:F90_WDBOPNR:exit:WDMSFL,RETCOD 100 0
|
|
16
|
+
FILBLK RETCOD 0
|
|
17
|
+
wdmfl 0 0 0 0
|
|
18
|
+
FILBLK RETCOD 0
|
|
19
|
+
18:19:02.896 : LOG_MSG:ERROR.FIL OPENED
|
|
20
|
+
18:19:02.896 : HASS_ENT:F90_WDBOPNR:entr:WDMSFL,RWFLG: 100 1 C:\Users\mfratki\Documents\github\pyHSPF\src\hspf\bin\WinHSPFLt\hspfmsg.wdm
|
|
21
|
+
18:19:02.896 : HASS_ENT:F90_WDBOPNR:exit:WDMSFL,RETCOD 100 0
|
|
22
|
+
FILBLK RETCOD 0
|
|
23
|
+
wdmfl 0 0 0 0
|
|
24
|
+
FILBLK RETCOD 0
|
|
25
|
+
18:19:05.790 : LOG_MSG:ERROR.FIL OPENED
|
|
26
|
+
18:19:05.790 : HASS_ENT:F90_WDBOPNR:entr:WDMSFL,RWFLG: 100 1 C:\Users\mfratki\Documents\github\pyHSPF\src\hspf\bin\WinHSPFLt\hspfmsg.wdm
|
|
27
|
+
18:19:05.790 : HASS_ENT:F90_WDBOPNR:exit:WDMSFL,RETCOD 100 0
|
|
28
|
+
FILBLK RETCOD 0
|
|
29
|
+
wdmfl 0 0 0 0
|
|
30
|
+
FILBLK RETCOD 0
|
|
31
|
+
18:32:46.775 : LOG_MSG:ERROR.FIL OPENED
|
|
32
|
+
18:32:46.776 : HASS_ENT:F90_WDBOPNR:entr:WDMSFL,RWFLG: 100 1 C:\Users\mfratki\Documents\github\pyHSPF\src\hspf\bin\WinHSPFLt\hspfmsg.wdm
|
|
33
|
+
18:32:46.776 : HASS_ENT:F90_WDBOPNR:exit:WDMSFL,RETCOD 100 0
|
|
34
|
+
FILBLK RETCOD 0
|
|
35
|
+
wdmfl 0 0 0 0
|
|
36
|
+
FILBLK RETCOD 0
|
|
37
|
+
18:38:43.431 : LOG_MSG:ERROR.FIL OPENED
|
|
38
|
+
18:38:43.432 : HASS_ENT:F90_WDBOPNR:entr:WDMSFL,RWFLG: 100 1 C:\Users\mfratki\Documents\github\pyHSPF\src\hspf\bin\WinHSPFLt\hspfmsg.wdm
|
|
39
|
+
18:38:43.432 : HASS_ENT:F90_WDBOPNR:exit:WDMSFL,RETCOD 100 0
|
|
40
|
+
FILBLK RETCOD 0
|
|
41
|
+
wdmfl 0 0 0 0
|
|
42
|
+
FILBLK RETCOD 0
|
|
43
|
+
18:44:23.798 : LOG_MSG:ERROR.FIL OPENED
|
|
44
|
+
18:44:23.799 : HASS_ENT:F90_WDBOPNR:entr:WDMSFL,RWFLG: 100 1 C:\Users\mfratki\Documents\github\pyHSPF\src\hspf\bin\WinHSPFLt\hspfmsg.wdm
|
|
45
|
+
18:44:23.800 : HASS_ENT:F90_WDBOPNR:exit:WDMSFL,RETCOD 100 0
|
|
46
|
+
FILBLK RETCOD 0
|
|
47
|
+
wdmfl 0 0 0 0
|
|
48
|
+
FILBLK RETCOD 0
|
|
49
|
+
18:52:12.607 : LOG_MSG:ERROR.FIL OPENED
|
|
50
|
+
18:52:12.608 : HASS_ENT:F90_WDBOPNR:entr:WDMSFL,RWFLG: 100 1 C:\Users\mfratki\Documents\github\pyHSPF\src\hspf\bin\WinHSPFLt\hspfmsg.wdm
|
|
51
|
+
18:52:12.608 : HASS_ENT:F90_WDBOPNR:exit:WDMSFL,RETCOD 100 0
|
|
52
|
+
FILBLK RETCOD 0
|
|
53
|
+
wdmfl 0 0 0 0
|
|
54
|
+
FILBLK RETCOD 0
|
|
55
|
+
18:59:18.608 : LOG_MSG:ERROR.FIL OPENED
|
|
56
|
+
18:59:18.609 : HASS_ENT:F90_WDBOPNR:entr:WDMSFL,RWFLG: 100 1 C:\Users\mfratki\Documents\github\pyHSPF\src\hspf\bin\WinHSPFLt\hspfmsg.wdm
|
|
57
|
+
18:59:18.609 : HASS_ENT:F90_WDBOPNR:exit:WDMSFL,RETCOD 100 0
|
|
58
|
+
FILBLK RETCOD 0
|
|
59
|
+
wdmfl 0 0 0 0
|
|
60
|
+
FILBLK RETCOD 0
|
|
61
|
+
19:05:51.046 : LOG_MSG:ERROR.FIL OPENED
|
|
62
|
+
19:05:51.047 : HASS_ENT:F90_WDBOPNR:entr:WDMSFL,RWFLG: 100 1 C:\Users\mfratki\Documents\github\pyHSPF\src\hspf\bin\WinHSPFLt\hspfmsg.wdm
|
|
63
|
+
19:05:51.047 : HASS_ENT:F90_WDBOPNR:exit:WDMSFL,RETCOD 100 0
|
|
64
|
+
FILBLK RETCOD 0
|
|
65
|
+
wdmfl 0 0 0 0
|
|
66
|
+
FILBLK RETCOD 0
|
|
67
|
+
19:14:05.088 : LOG_MSG:ERROR.FIL OPENED
|
|
68
|
+
19:14:05.088 : HASS_ENT:F90_WDBOPNR:entr:WDMSFL,RWFLG: 100 1 C:\Users\mfratki\Documents\github\pyHSPF\src\hspf\bin\WinHSPFLt\hspfmsg.wdm
|
|
69
|
+
19:14:05.089 : HASS_ENT:F90_WDBOPNR:exit:WDMSFL,RETCOD 100 0
|
|
70
|
+
FILBLK RETCOD 0
|
|
71
|
+
wdmfl 0 0 0 0
|
|
72
|
+
FILBLK RETCOD 0
|
|
73
|
+
19:18:33.712 : LOG_MSG:ERROR.FIL OPENED
|
|
74
|
+
19:18:33.713 : HASS_ENT:F90_WDBOPNR:entr:WDMSFL,RWFLG: 100 1 C:\Users\mfratki\Documents\github\pyHSPF\src\hspf\bin\WinHSPFLt\hspfmsg.wdm
|
|
75
|
+
19:18:33.713 : HASS_ENT:F90_WDBOPNR:exit:WDMSFL,RETCOD 100 0
|
|
76
|
+
FILBLK RETCOD 0
|
|
77
|
+
wdmfl 0 0 0 0
|
|
78
|
+
FILBLK RETCOD 0
|
|
@@ -10,6 +10,7 @@ import subprocess
|
|
|
10
10
|
#non-standard imports
|
|
11
11
|
import pandas as pd
|
|
12
12
|
import numpy as np
|
|
13
|
+
import geopandas as gpd
|
|
13
14
|
from pathlib import Path
|
|
14
15
|
|
|
15
16
|
#My packages
|
|
@@ -20,7 +21,7 @@ from mpcaHydro import data_manager as dm
|
|
|
20
21
|
from pyhcal import metrics
|
|
21
22
|
from pyhcal import figures
|
|
22
23
|
from pyhcal import setup_utils
|
|
23
|
-
|
|
24
|
+
from pyhcal.mappers import Mapper
|
|
24
25
|
|
|
25
26
|
def new_calibration(project_folder,model_name,download_station_data = True,run_model = True,convert_wdms = True):
|
|
26
27
|
return setup_utils.create_calibration_project(model_name,project_folder,download_station_data,run_model,convert_wdms)
|
|
@@ -37,6 +38,7 @@ def validate_project_folder(project_folder):
|
|
|
37
38
|
assert project_path.joinpath('data').exists(), 'Data folder does not exist'
|
|
38
39
|
assert project_path.joinpath('output').exists(), 'Output folder does not exist'
|
|
39
40
|
assert project_path.joinpath('targets.csv').exists(), 'targets.csv file does not exist in project folder'
|
|
41
|
+
assert project_path.joinpath('gis').exists(), 'GIS folder does not exist'
|
|
40
42
|
return True
|
|
41
43
|
|
|
42
44
|
class calibrator:
|
|
@@ -45,6 +47,7 @@ class calibrator:
|
|
|
45
47
|
self.project_name = self.project_path.name
|
|
46
48
|
self.model_path = self.project_path.joinpath('model')
|
|
47
49
|
self.output_path = self.project_path.joinpath('output')
|
|
50
|
+
self.gis_path = self.project_path.joinpath('gis')
|
|
48
51
|
self.run = None
|
|
49
52
|
self.start_date = '1996-01-01'
|
|
50
53
|
self.end_date = '2100-01-01'
|
|
@@ -54,29 +57,58 @@ class calibrator:
|
|
|
54
57
|
|
|
55
58
|
self.targets = None
|
|
56
59
|
if self.project_path.joinpath('targets.csv').exists():
|
|
57
|
-
self.
|
|
58
|
-
|
|
60
|
+
self._load_targets()
|
|
61
|
+
|
|
59
62
|
self.MODL_DB = pd.read_csv(self.project_path.joinpath('_'.join([self.project_name ,'MODL_DB.csv'])))
|
|
60
63
|
|
|
64
|
+
if 'repo_name' in self.MODL_DB.columns:
|
|
65
|
+
self.model_name =self.MODL_DB['repo_name'].to_list()[0]
|
|
66
|
+
|
|
67
|
+
# Alot of effort to try and include the subwatershed gdf if it exists. TODO: refactor
|
|
68
|
+
self.subwatershed_gdf_filepath = self.gis_path.joinpath('_'.join([self.project_name ,'Subwatersheds.shp']))
|
|
69
|
+
if self.subwatershed_gdf_filepath.exists():
|
|
70
|
+
self.subwatershed_gdf = gpd.read_file(self.subwatershed_gdf_filepath)
|
|
71
|
+
if 'SubID' in self.subwatershed_gdf.columns:
|
|
72
|
+
self.subwatershed_gdf = self.subwatershed_gdf.set_index('SubID')
|
|
73
|
+
else:
|
|
74
|
+
print("Warning: 'SubID' column not found in subwatershed shapefile. GIS operations will not function correctly.")
|
|
75
|
+
else:
|
|
76
|
+
self.subwatershed_gdf = None
|
|
77
|
+
|
|
61
78
|
self.model = None
|
|
62
79
|
self._wdms = None
|
|
63
80
|
self.uci = None
|
|
64
|
-
|
|
81
|
+
self.mapper = None
|
|
65
82
|
## Input/Output methods
|
|
83
|
+
|
|
84
|
+
|
|
66
85
|
def initialize(self,reach_ids,default = 4):
|
|
67
86
|
|
|
68
87
|
self.uci.update_table(default,'RCHRES','BINARY-INFO',0,columns = ['HEATPR','HYDRPR','SEDPR','OXRXPR','NUTRPR','PLNKPR'],operator = 'set')
|
|
69
88
|
self.uci.update_table(2,'RCHRES','BINARY-INFO',0,columns = ['HEATPR','HYDRPR','SEDPR','OXRXPR','NUTRPR','PLNKPR'],opnids = reach_ids,operator = 'set')
|
|
70
89
|
|
|
71
90
|
self.uci.write(self.model.uci_file)
|
|
72
|
-
|
|
73
|
-
subprocess.run([winHSPF,self.model.uci_file]) #, stdout=subprocess.PIPE, creationflags=0x08000000)
|
|
91
|
+
subprocess.run([self.model.winHSPF,self.model.uci_file]) #, stdout=subprocess.PIPE, creationflags=0x08000000)
|
|
74
92
|
|
|
75
93
|
def set_dates(self, start_date = '1996-01-01',end_date ='2100-01-01'):
|
|
76
94
|
self.start_date = start_date
|
|
77
95
|
self.end_date = end_date
|
|
78
96
|
|
|
79
|
-
|
|
97
|
+
def _load_gdf(self,catchment_id_column = 'SubID'):
|
|
98
|
+
self.subwatershed_gdf_filepath = self.gis_path.joinpath('_'.join([self.project_name ,'Subwatersheds.shp']))
|
|
99
|
+
if self.subwatershed_gdf_filepath.exists():
|
|
100
|
+
self.subwatershed_gdf = gpd.read_file(self.subwatershed_gdf_filepath)
|
|
101
|
+
self.subwatershed_gdf = self.subwatershed_gdf.set_index(catchment_id_column)
|
|
102
|
+
else:
|
|
103
|
+
self.subwatershed_gdf = None
|
|
104
|
+
|
|
105
|
+
def _load_targets(self):
|
|
106
|
+
self.targets = pd.read_csv(self.project_path.joinpath('targets.csv'))
|
|
107
|
+
|
|
108
|
+
def _update_mapper(self):
|
|
109
|
+
if self.subwatershed_gdf is not None:
|
|
110
|
+
self.mapper = Mapper(self.project_name,self.uci,self.subwatershed_gdf,hbn = self.model.hbns)
|
|
111
|
+
|
|
80
112
|
def load_model(self,name):
|
|
81
113
|
|
|
82
114
|
if isinstance(name,int): # Default approach
|
|
@@ -98,8 +130,10 @@ class calibrator:
|
|
|
98
130
|
|
|
99
131
|
self.model.wdms = self._wdms
|
|
100
132
|
self.model.reports.wdms = self._wdms
|
|
101
|
-
self.uci = deepcopy(self.model.uci) #uci to be manipulated
|
|
133
|
+
self.uci = deepcopy(self.model.uci) #uci to be manipulated
|
|
102
134
|
|
|
135
|
+
if self.subwatershed_gdf is not None:
|
|
136
|
+
self.mapper = Mapper(self.project_name,self.uci,self.subwatershed_gdf,hbn = self.model.hbns)
|
|
103
137
|
|
|
104
138
|
def run_model(self,name = None,overwrite_hbn = False): # NO STATE CHANGE
|
|
105
139
|
|
|
@@ -118,14 +152,24 @@ class calibrator:
|
|
|
118
152
|
#winHSPF = str(Path(__file__).resolve().parent.parent) + '\\bin\\WinHSPFLt\\WinHspfLt.exe'
|
|
119
153
|
subprocess.run([self.model.winHSPF,uci_file]) #, stdout=subprocess.PIPE, creationflags=0x08000000)
|
|
120
154
|
|
|
121
|
-
|
|
155
|
+
def get_outlets(self):
|
|
156
|
+
df = self.dm.get_outlets(self.model_name)
|
|
157
|
+
outlets = {}
|
|
158
|
+
for outlet_id in df['outlet_id'].unique():
|
|
159
|
+
outlets[int(outlet_id)] = {}
|
|
160
|
+
df_outlet = df.loc[df['outlet_id'] == outlet_id]
|
|
161
|
+
outlets[int(outlet_id)]['station_ids'] = list(set(df_outlet['station_id']))
|
|
162
|
+
outlets[int(outlet_id)]['reach_ids'] = list(set(df_outlet['reach_id']))
|
|
163
|
+
outlets[int(outlet_id)]['model_name'] = df_outlet['repository_name'].iloc[0]
|
|
164
|
+
return outlets
|
|
165
|
+
|
|
122
166
|
def get_simulated_output(self,reach_ids,constituent,time_step = 'YE'):
|
|
123
167
|
sim = self.model.hbns.get_reach_constituent(constituent,reach_ids,time_step)
|
|
124
168
|
sim.name = 'simulated'
|
|
125
169
|
return sim
|
|
126
170
|
|
|
127
171
|
def get_observed_data(self,station_ids,constituent,time_step = 'YE',baseflow_percentage = None):
|
|
128
|
-
obs = self.dm.
|
|
172
|
+
obs = self.dm.get_observation_data(station_ids,constituent,agg_period = time_step)['observed'].sort_index(level = 'index')
|
|
129
173
|
obs.name = 'observed'
|
|
130
174
|
return obs
|
|
131
175
|
|
|
@@ -533,110 +577,3 @@ def threshold(adjustment,threshold,max_change):
|
|
|
533
577
|
#Note that in uci.update_table() there is further screening to account for adjustments below the model precision
|
|
534
578
|
return adjustment
|
|
535
579
|
|
|
536
|
-
|
|
537
|
-
#class hydrologyCalibrator(calibrator):
|
|
538
|
-
|
|
539
|
-
#class nutrientCalibrator(calibrator):
|
|
540
|
-
|
|
541
|
-
class sedimentCalibrator(calibrator):
|
|
542
|
-
|
|
543
|
-
def update_kser(self,method,opnid = None):
|
|
544
|
-
#TODO account for the additional comment column
|
|
545
|
-
assert method in ['load','landcover','sftl']
|
|
546
|
-
|
|
547
|
-
table = self.uci.table('PERLND','SED-PARM3',0,False)
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
if method == 'load':
|
|
551
|
-
adjustment = self.compare(0,aggregate = True).loc['Mean']['ratio']
|
|
552
|
-
elif method == 'landcover':
|
|
553
|
-
adjustment = self.landcover(0)['target']
|
|
554
|
-
table = self.uci.table('PERLND','SED-PARM3',0)
|
|
555
|
-
if opnid == None:
|
|
556
|
-
opnid = table.index
|
|
557
|
-
adjustment = np.array(adjustment.loc[opnid])[:,None]
|
|
558
|
-
elif method == 'sftl':
|
|
559
|
-
adjustment = self.sftl()
|
|
560
|
-
|
|
561
|
-
self.uci.replace_table('PERLND','SED-PARM3',0)
|
|
562
|
-
|
|
563
|
-
def update_erosivity(self,param = 'M',opnid = None,update_alg = '*'):
|
|
564
|
-
adjustment = self.scour()
|
|
565
|
-
table = self.uci.table('RCHRES','SILT-CLAY-PM',0)
|
|
566
|
-
if opnid == None:
|
|
567
|
-
opnid = table.index
|
|
568
|
-
adjustment = np.array(adjustment.loc[opnid])[:,None]
|
|
569
|
-
self.uci.update_table(adjustment,'RCHRES','SILT-CLAY-PM',table_id = 0,opnid = opnid,columns = [param],update_alg = update_alg)
|
|
570
|
-
|
|
571
|
-
adjustment = self.scour()
|
|
572
|
-
adjustment = np.array(adjustment.loc[opnid])[:,None]
|
|
573
|
-
self.uci.update_table(adjustment,'RCHRES','SILT-CLAY-PM',table_id = 1,opnid = opnid,columns = [param],update_alg = update_alg)
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
def fit_param(self,param,m_factor,N = 2,opnid = None,run = None):
|
|
577
|
-
bounds = {'M':[.000000001,.01,2,5], #maxlow,low,high,maxhigh
|
|
578
|
-
'TAUCD':[.001,.01,.3,1],
|
|
579
|
-
'TAUCS':[.01,.05,.5,3]}
|
|
580
|
-
if run == None:
|
|
581
|
-
run = self.run
|
|
582
|
-
|
|
583
|
-
data = self.load_data('scour',N=10000)
|
|
584
|
-
data = data.loc[:,range(run-N+1,run+1),:]
|
|
585
|
-
|
|
586
|
-
if opnid == None:
|
|
587
|
-
opnid = data.reset_index(level=[1]).index.unique() # assumes multiindex
|
|
588
|
-
|
|
589
|
-
for index in opnid:
|
|
590
|
-
if any(data.loc[index]['LKFG'] == 0):
|
|
591
|
-
x = data.loc[index]['depscour']
|
|
592
|
-
y = data.loc[index][param]
|
|
593
|
-
linear_model=np.polyfit(x,y,1)
|
|
594
|
-
linear_model_fn=np.poly1d(linear_model)
|
|
595
|
-
m = linear_model_fn(-data.loc[index]['nonpoint'].iloc[1]*.25)
|
|
596
|
-
if m < bounds[param][0]:
|
|
597
|
-
m = bounds[param][0]
|
|
598
|
-
if m > bounds[param][3]:
|
|
599
|
-
m = bounds[param][3]
|
|
600
|
-
self.update_table('RCHRES','SILT-CLAY-PM',0,m,'set',opnid = index,columns = [param]) #mod.update_table(operation,table_name,table_id,adjustment,operator,opnids,columns)
|
|
601
|
-
self.update_table('RCHRES','SILT-CLAY-PM',1,m*m_factor,'set',opnid = index,columns = [param]) #mod.update_table(operation,table_name,table_id,adjustment,operator,opnids,columns)
|
|
602
|
-
|
|
603
|
-
def erosivity(self,m_factor,param = 'M',opnid = None,run = None,iterations = 1):
|
|
604
|
-
|
|
605
|
-
if run == None:
|
|
606
|
-
run = self.run
|
|
607
|
-
|
|
608
|
-
# run model updating erosivity for N iterations
|
|
609
|
-
for iteration in range(iterations):
|
|
610
|
-
self.update_erosivity(param = param,opnid = opnid)
|
|
611
|
-
self.run_model() # creates the run+1 uci file and runs it using WinHspfLT
|
|
612
|
-
run = run + 1
|
|
613
|
-
self.load_model(run)
|
|
614
|
-
self.save_data()
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
self.fit_param(param,m_factor,iterations+1,opnid,run)
|
|
618
|
-
self.run_model() # creates the run+1 uci file and runs it using WinHspfLT
|
|
619
|
-
|
|
620
|
-
run = run + 1
|
|
621
|
-
self.load_model(run)
|
|
622
|
-
self.save_data()
|
|
623
|
-
|
|
624
|
-
def scour(hbn,uci):
|
|
625
|
-
# Erosivity adjustment only
|
|
626
|
-
scour = reports.scour_report(hbn,uci)
|
|
627
|
-
#TODO: add check for this
|
|
628
|
-
# Assume all nonpoint values are greater than 0...
|
|
629
|
-
# if depscour is greater than 0
|
|
630
|
-
target = scour['nonpoint']*.25 # Assuming nonpoint load is set
|
|
631
|
-
adjustment = np.abs(scour['depscour'])/target
|
|
632
|
-
adjustment[(adjustment < 1.05) & (adjustment > .95)] = 1 # Don't change reaches where the depscour is close to the target
|
|
633
|
-
adjustment[adjustment > 1.05] = .95 # Since depscour is negative we have to swap this. I think if I do target/depscour this line would be less confusing
|
|
634
|
-
adjustment[adjustment < .95] = 1.05
|
|
635
|
-
adjustment[scour['depscour'] > 0] = 2 # Double any values where the depscour is positive
|
|
636
|
-
adjustment[scour['LKFG'] == 1] = 1 # Ignore lake flags
|
|
637
|
-
adjustment[np.isnan(adjustment)] = 1
|
|
638
|
-
|
|
639
|
-
return adjustment
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Created on Mon Jan 27 11:20:47 2025
|
|
4
|
+
|
|
5
|
+
@author: mfratki
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from pyhcal.repository import Repository
|
|
9
|
+
from hspf.uci import UCI
|
|
10
|
+
import geopandas as gpd
|
|
11
|
+
import matplotlib.pyplot as plt
|
|
12
|
+
import pandas as pd
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
# Try to load statewide subwatersheds from data folder,
|
|
16
|
+
|
|
17
|
+
# try:
|
|
18
|
+
# SUBWATERSHEDS = gpd.read_file(str(Path(__file__).resolve().parent/'data\\statewide_subwatersheds.gpkg'))
|
|
19
|
+
# except:
|
|
20
|
+
# print("Could not load statewide subwatersheds. Please ensure the file 'statewide_subwatersheds.gpkg' is located in the 'data' folder of the pyhcal package.")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class uciMapper():
|
|
25
|
+
def __init__(self,model_names,gis_layer,huc6 = False):
|
|
26
|
+
self.mappers = []
|
|
27
|
+
|
|
28
|
+
if huc6:
|
|
29
|
+
model_names = Repository.HUC_DIRECTORY.loc[Repository.HUC_DIRECTORY['Repository_HUC6 Name'].isin(model_names),'Repository_HUC8 Name']
|
|
30
|
+
model_names = model_names.loc[model_names.isin(Repository.valid_models())].values
|
|
31
|
+
|
|
32
|
+
for model_name in model_names:
|
|
33
|
+
repo = Repository(model_name)
|
|
34
|
+
uci = UCI(repo.uci_file)
|
|
35
|
+
#gis_layer = SUBWATERSHEDS.loc[SUBWATERSHEDS['repo_name'] == model_name,:]
|
|
36
|
+
#gis_layer.set_index('SubID',inplace=True)
|
|
37
|
+
self.mappers.append(Mapper(model_name,uci,gis_layer))
|
|
38
|
+
|
|
39
|
+
def map_parameter(self,operation,table_name,parameter,table_id):
|
|
40
|
+
table = self.join_table(operation,table_name,table_id)
|
|
41
|
+
fig, ax = plt.subplots()
|
|
42
|
+
#[table.plot(column = parameter,ax = ax) for table in tables]
|
|
43
|
+
table.plot(column = parameter,ax = ax,cmap='viridis',legend=True)
|
|
44
|
+
plt.title(parameter)
|
|
45
|
+
|
|
46
|
+
def join_table(self,operation,table_name,table_id):
|
|
47
|
+
tables = [mapper.join_table(operation,table_name,table_id) for mapper in self.mappers]
|
|
48
|
+
table = pd.concat(tables)
|
|
49
|
+
return table
|
|
50
|
+
|
|
51
|
+
class Mapper():
|
|
52
|
+
def __init__(self,model_name,uci,subwatershed_gdf,hbn = None):
|
|
53
|
+
self.model_name = model_name
|
|
54
|
+
self.uci = uci
|
|
55
|
+
self.hbn = hbn
|
|
56
|
+
# if subwatershed_gdf is None:
|
|
57
|
+
# subwatershed_gdf = SUBWATERSHEDS.loc[SUBWATERSHEDS['repo_name'] == model_name,:]
|
|
58
|
+
# subwatershed_gdf.set_index('SubID',inplace=True)
|
|
59
|
+
|
|
60
|
+
self.subwatershed_gdf = subwatershed_gdf
|
|
61
|
+
self.subwatersheds = uci.network.subwatersheds()
|
|
62
|
+
self.subwatershed_ids = list(set(self.subwatersheds.index))
|
|
63
|
+
|
|
64
|
+
def map_parameter(self,operation,table_name,parameter,table_id=0,weight_by_area = True):
|
|
65
|
+
fig, ax = plt.subplots()
|
|
66
|
+
self.join_table(operation,table_name,parameter,table_id).plot(column = parameter,ax = ax,cmap='viridis',legend=True)
|
|
67
|
+
plt.title(parameter)
|
|
68
|
+
|
|
69
|
+
def join_table(self,operation,table_name,parameter,table_id=0,weight_by_area = True):
|
|
70
|
+
table = self.uci.table(operation,table_name,table_id)
|
|
71
|
+
subwatersheds = self.uci.network.subwatersheds()
|
|
72
|
+
subwatersheds = subwatersheds.loc[subwatersheds['SVOL'] == 'PERLND'].reset_index(drop=False).set_index('SVOLNO').join(table,how = 'left')
|
|
73
|
+
subwatersheds.index.name = 'SVOLNO' # Sometimes the index name gets dropped. I'm guessing when there are missing joins.
|
|
74
|
+
subwatersheds = subwatersheds.reset_index('SVOLNO').set_index(['SVOLNO','TVOLNO','SVOL','MLNO'])
|
|
75
|
+
|
|
76
|
+
#weight by area factor:
|
|
77
|
+
if weight_by_area:
|
|
78
|
+
subwatersheds['weighted_param'] = subwatersheds['AFACTR']*subwatersheds[parameter]
|
|
79
|
+
subwatersheds = subwatersheds.groupby(subwatersheds.index.get_level_values('TVOLNO'))['weighted_param'].sum()/subwatersheds.groupby(subwatersheds.index.get_level_values('TVOLNO'))['AFACTR'].sum()
|
|
80
|
+
subwatersheds.name = parameter
|
|
81
|
+
else:
|
|
82
|
+
subwatersheds = subwatersheds.groupby(subwatersheds.index.get_level_values('TVOLNO'))[parameter].mean()
|
|
83
|
+
subwatersheds.name = parameter
|
|
84
|
+
return self.subwatershed_gdf.join(subwatersheds)
|
|
85
|
+
|
|
86
|
+
def map_flag():
|
|
87
|
+
raise NotImplementedError()
|
|
88
|
+
|
|
89
|
+
def map_output(self,operation,output_name,t_code=5,agg_func = 'mean'):
|
|
90
|
+
subwatersheds = self.subwatersheds.loc[(self.subwatersheds['SVOL'] == operation),:].copy()
|
|
91
|
+
opnids = list(subwatersheds['SVOLNO'].unique())
|
|
92
|
+
output = self.hbn.get_multiple_timeseries(operation,t_code,output_name,opnids = opnids).agg(agg_func)
|
|
93
|
+
if operation in ['PERLND','IMPLND']:
|
|
94
|
+
subwatersheds = pd.merge(subwatersheds,output.to_frame(output_name),right_index = True,left_on = 'SVOLNO')
|
|
95
|
+
subwatersheds['area_output'] = subwatersheds['AFACTR']*subwatersheds[output_name]
|
|
96
|
+
subwatersheds = subwatersheds[['AFACTR','area_output']].groupby(subwatersheds.index).sum()
|
|
97
|
+
subwatersheds[output_name] = subwatersheds['area_output']/subwatersheds['AFACTR']
|
|
98
|
+
|
|
99
|
+
fig, ax = plt.subplots()
|
|
100
|
+
#[table.plot(column = parameter,ax = ax) for table in tables]
|
|
101
|
+
self.subwatershed_gdf.join(subwatersheds).plot(column = output_name,ax = ax,cmap='viridis',legend=True)
|
|
102
|
+
plt.title(output_name)
|
|
103
|
+
|
|
104
|
+
def map_table(self,df, mapping_col):
|
|
105
|
+
'''Maps a dataframe column to the subwatershed geodataframe based on subwatershed IDs.
|
|
106
|
+
Assumes the dataframe index contains the subwatershed IDs.'''
|
|
107
|
+
fig, ax = plt.subplots()
|
|
108
|
+
#[table.plot(column = parameter,ax = ax) for table in tables]
|
|
109
|
+
self.subwatershed_gdf.join(df).plot(column = mapping_col,ax = ax,cmap='viridis',legend=True)
|
|
110
|
+
plt.title(mapping_col)
|
|
111
|
+
#return self.subwatershed_gdf.join(subwatersheds)
|
|
112
|
+
|
|
113
|
+
|
|
@@ -6,7 +6,7 @@ Created on Wed Nov 27 09:16:30 2024
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import pandas as pd
|
|
9
|
-
from
|
|
9
|
+
from mpcaHydro import outlets
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
import shutil
|
|
12
12
|
|
|
@@ -38,7 +38,7 @@ class Repository():
|
|
|
38
38
|
|
|
39
39
|
self.REPOSITORY_PATH = repository_path
|
|
40
40
|
huc_directory = self.HUC_DIRECTORY.loc[self.HUC_DIRECTORY['Repository_HUC8 Name'] == model_name]
|
|
41
|
-
self.modl_db =
|
|
41
|
+
self.modl_db = outlets.get_model_db(model_name) #self.MODL_DB.loc[self.MODL_DB['repository_name'] == model_name]
|
|
42
42
|
#self.modl_db = pd.concat([self.MODL_DB.loc[self.MODL_DB['repository_name'].str.startswith(huc8_id,na=False)] for huc8_id in huc8_ids])
|
|
43
43
|
self.model_name = model_name
|
|
44
44
|
self.huc8_ids = list(huc_directory['USGS HUC-8'])
|
|
@@ -48,8 +48,8 @@ class Repository():
|
|
|
48
48
|
self.uci_file = self.repo_folder.joinpath('HSPF','.'.join([self.model_name,'uci']))
|
|
49
49
|
self.wdm_files = [item for item in self.repo_folder.joinpath('HSPF').iterdir() if (item.name.endswith('.wdm')) | (item.name.endswith('.WDM'))]
|
|
50
50
|
self.shapefiles = {item.name.split('.')[0].split('_')[-1]:item for item in self.repo_folder.joinpath('GIS').iterdir() if (item.name.endswith('.shp')) | (item.name.endswith('.SHP'))}
|
|
51
|
-
self.wiski_stations =
|
|
52
|
-
self.equis_stations =
|
|
51
|
+
self.wiski_stations = outlets.wiski_stations(model_name)
|
|
52
|
+
self.equis_stations = outlets.equis_stations(model_name)
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
def copy(self,copy_path):
|
|
@@ -8,7 +8,7 @@ from mpcaHydro.data_manager import dataManager
|
|
|
8
8
|
from hspf.wdmReader import readWDM
|
|
9
9
|
from hspf.uci import UCI
|
|
10
10
|
from pyhcal.repository import Repository
|
|
11
|
-
from
|
|
11
|
+
from mpcaHydro import outlets
|
|
12
12
|
|
|
13
13
|
import numpy as np
|
|
14
14
|
import pandas as pd
|
|
@@ -16,8 +16,8 @@ from pathlib import Path
|
|
|
16
16
|
import subprocess
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
def create_calibration_project(model_name,project_location,download_station_data = True,run_model = True,convert_wdms = True):
|
|
20
|
-
project = Builder(model_name)
|
|
19
|
+
def create_calibration_project(model_name,project_location, download_station_data = True,run_model = True,convert_wdms = True,oracle_username = None, oracle_password = None):
|
|
20
|
+
project = Builder(model_name,oracle_username = oracle_username, oracle_password = oracle_password)
|
|
21
21
|
project.copy(project_location,model_name)
|
|
22
22
|
project.load_uci()
|
|
23
23
|
project.format_uci()
|
|
@@ -25,6 +25,7 @@ def create_calibration_project(model_name,project_location,download_station_data
|
|
|
25
25
|
if convert_wdms: project.convert_wdms()
|
|
26
26
|
if download_station_data: project.download_station_data()
|
|
27
27
|
if run_model: project.run_model()
|
|
28
|
+
return project
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
|
|
@@ -32,32 +33,34 @@ def create_calibration_project(model_name,project_location,download_station_data
|
|
|
32
33
|
|
|
33
34
|
class Builder():
|
|
34
35
|
|
|
35
|
-
def __init__(self,model_name):
|
|
36
|
+
def __init__(self,model_name,oracle_username = None, oracle_password = None):
|
|
36
37
|
self.repository = Repository(model_name)
|
|
38
|
+
self.model_name = model_name
|
|
37
39
|
self.project_path = None
|
|
38
40
|
self.project_name = None
|
|
39
41
|
self.new_uci = None
|
|
40
42
|
self.uci = None
|
|
41
43
|
self.dm = None
|
|
42
|
-
self.calibration_reaches =
|
|
43
|
-
|
|
44
|
+
self.calibration_reaches = outlets.wplmn_station_opnids(model_name)
|
|
45
|
+
self.oracle_username = oracle_username
|
|
46
|
+
self.oracle_password = oracle_password
|
|
44
47
|
|
|
45
48
|
def valid_models():
|
|
46
49
|
return Repository.valid_models()
|
|
47
50
|
|
|
48
|
-
def set_project_path(self,
|
|
49
|
-
self.project_path = Path(
|
|
50
|
-
self.project_name =
|
|
51
|
+
def set_project_path(self,project_location,project_name):
|
|
52
|
+
self.project_path = Path(project_location).joinpath(project_name)
|
|
53
|
+
self.project_name = project_name
|
|
54
|
+
self.dm = dataManager(self.project_path.joinpath('data'),oracle_username = self.oracle_username, oracle_password = self.oracle_password)
|
|
55
|
+
self.dm._build_warehouse()
|
|
51
56
|
#self.new_uci = self.project_path.joinpath('model','_'.join([self.project_name,'0.uci']))
|
|
52
57
|
#self.uci = UCI(self.project_path.joinpath('model','.'.join([self.project_name,'uci'])))
|
|
53
58
|
|
|
54
59
|
def copy(self,project_location,project_name):
|
|
55
|
-
self.
|
|
56
|
-
self.project_name = project_name
|
|
60
|
+
self.set_project_path(project_location,project_name)
|
|
57
61
|
self.repository.copy(self.project_path)
|
|
58
|
-
|
|
62
|
+
|
|
59
63
|
|
|
60
|
-
|
|
61
64
|
def load_uci(self):
|
|
62
65
|
self.new_uci = self.project_path.joinpath('model','_'.join([self.project_name,'0.uci']))
|
|
63
66
|
self.uci = UCI(self.project_path.joinpath('model','.'.join([self.project_name,'uci'])))
|
|
@@ -80,21 +83,27 @@ class Builder():
|
|
|
80
83
|
setup_qualid(self.uci)
|
|
81
84
|
self.uci.write(self.new_uci)
|
|
82
85
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
station_origin = 'equis'
|
|
89
|
-
else: station_origin = 'swd'
|
|
90
|
-
|
|
91
|
-
for station_id in equis_stations:
|
|
92
|
-
self.dm.download_station_data(station_id,station_origin, True)
|
|
86
|
+
def download_wiski_data(self,station_ids):
|
|
87
|
+
if len(station_ids) > 0:
|
|
88
|
+
self.dm._download_wiski_data(station_ids)
|
|
89
|
+
else:
|
|
90
|
+
print("No Wiski stations have been manually matched to modeled reaches.")
|
|
93
91
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
92
|
+
def download_equis_data(self,station_ids):
|
|
93
|
+
if len(station_ids) > 0:
|
|
94
|
+
if self.dm.credentials_exist():
|
|
95
|
+
self.dm.connect_to_oracle()
|
|
96
|
+
self.dm._download_equis_data(station_ids)
|
|
97
|
+
else:
|
|
98
|
+
print("Oracle credentials not provided. Cannot download Equis data.")
|
|
99
|
+
else:
|
|
100
|
+
print("No Equis stations have been manually matched to modeled reaches.")
|
|
101
|
+
|
|
102
|
+
def download_station_data(self):
|
|
103
|
+
equis_stations = self.dm.outlets.mapped_equis_stations(self.model_name)
|
|
104
|
+
wiski_stations = self.dm.outlets.mapped_wiski_stations(self.model_name)
|
|
105
|
+
self.download_equis_data(equis_stations)
|
|
106
|
+
self.download_wiski_data(wiski_stations)
|
|
98
107
|
|
|
99
108
|
def convert_wdms(self):
|
|
100
109
|
copy_path = Path(self.project_path.joinpath('model'))
|
|
@@ -584,3 +593,5 @@ def setup_qualid(uci):
|
|
|
584
593
|
|
|
585
594
|
|
|
586
595
|
|
|
596
|
+
|
|
597
|
+
# %%
|