pyhcal 1.1.0__py3-none-any.whl → 1.1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pyhcal/ERROR.FIL ADDED
@@ -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
pyhcal/calibrators.py CHANGED
@@ -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.targets = pd.read_csv(self.project_path.joinpath('targets.csv'))
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
- winHSPF = str(Path(__file__).resolve().parent.parent) + '\\bin\\WinHSPFLt\\WinHspfLt.exe'
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._get_data(station_ids,constituent,agg_period = time_step).sort_index(level = 'index')
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
-
pyhcal/mappers.py ADDED
@@ -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
+
pyhcal/repository.py CHANGED
@@ -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 pyhcal import modl_db
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 = modl_db.get_model_db(model_name) #self.MODL_DB.loc[self.MODL_DB['repository_name'] == model_name]
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 = modl_db.wiski_stations(model_name)
52
- self.equis_stations = modl_db.equis_stations(model_name)
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):
pyhcal/setup_utils.py CHANGED
@@ -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 pyhcal import modl_db
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 = modl_db.wplmn_station_opnids(model_name)
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,project_path):
49
- self.project_path = Path(project_path)
50
- self.project_name = Path(project_path).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.project_path = Path(project_location).joinpath(project_name)
56
- self.project_name = project_name
60
+ self.set_project_path(project_location,project_name)
57
61
  self.repository.copy(self.project_path)
58
- self.dm = dataManager(self.project_path.joinpath('data'))
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
- def download_station_data(self,start_year,end_year):#:,station_id,source_id):
85
- #%% Old approach. Store as indvidual processed station files then load to warehouse
86
- if len(equis_stations) > 0:
87
- if self.dm.credentials_exist():
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
- if len(wiski_stations) > 0:
95
- for station_id in wiski_stations:
96
- self.dm.download_station_data(station_id,'wiski', True)
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
+ # %%
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyhcal
3
- Version: 1.1.0
3
+ Version: 1.1.2
4
4
  Summary: Python package for calibrating MPCA HSPF models
5
5
  Project-URL: Homepage, https://github.com/mfratkin1/pyhcal
6
6
  Author-email: Mulu Fratkin <michael.fratkin@state.mn.us>
@@ -0,0 +1,12 @@
1
+ pyhcal/ERROR.FIL,sha256=w35iHJYKYpcU9lrmU4FqLGMlKLabOOX-_Z567kHF8CA,5018
2
+ pyhcal/__init__.py,sha256=4TEpGD-PfEY8yK-od8DpEMA4_iQ-q9y0PBvROXSPdB0,94
3
+ pyhcal/calibrators.py,sha256=J_RdArI5FAAiU-tEnvSQxiRtIBv3ol13vOJmb34CuGk,27964
4
+ pyhcal/figures.py,sha256=Iu7LaN_i2IuDA_nfxj-a8AkG-FTLZVicJ3-efIs5OiE,45534
5
+ pyhcal/mappers.py,sha256=bAeBb-_EbhegPTn4sR4elRyQNrcBpr9CFzNoZcMDng8,5674
6
+ pyhcal/metrics.py,sha256=GUGHd-op-g1Foj8wnS_JVURSms4ifcC0a5h8ketQ29I,17911
7
+ pyhcal/repository.py,sha256=cZfAZRCI99_rhCSHi58_O6pV8aAofXuvQHrao0B5CzI,4606
8
+ pyhcal/setup_utils.py,sha256=YyFpOUlyzsJySrnBBnLs3CPLs3al93Mez_H69di4yeo,31145
9
+ pyhcal/data/HUC_Names.csv,sha256=UGmd3Q5E8DyFWggXzaXWpsRze7sFyrlpYqaYpMWAiGM,18946
10
+ pyhcal-1.1.2.dist-info/METADATA,sha256=CNDp8wMXQJNlttSLA9uI_z9zjImiZrY6lRTisF7_3jQ,560
11
+ pyhcal-1.1.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
12
+ pyhcal-1.1.2.dist-info/RECORD,,