pyhcal 1.0.0__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/setup_utils.py ADDED
@@ -0,0 +1,573 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Wed Jun 15 15:21:35 2022
4
+
5
+ @author: mfratki
6
+ """
7
+ from mpcaHydro.data_manager import dataManager
8
+ from pyhspf.wdmReader import readWDM
9
+ from pyhspf.uci import UCI
10
+ from pyhcal.repository import Repository
11
+
12
+ import numpy as np
13
+ import pandas as pd
14
+ from pathlib import Path
15
+ import subprocess
16
+
17
+
18
+ def create_calibration_project(model_name,project_location,download_station_data = True,run_model = True,convert_wdms = True):
19
+ project = Builder(model_name)
20
+ project.copy(project_location,model_name)
21
+ project.load_uci()
22
+ project.format_uci()
23
+ project.uci.build_targets().to_csv(project.project_path.joinpath('targets.csv'))
24
+ if convert_wdms: project.convert_wdms()
25
+ if download_station_data: project.download_station_data()
26
+ if run_model: project.run_model()
27
+
28
+
29
+
30
+
31
+
32
+ class Builder():
33
+
34
+ def __init__(self,model_name):
35
+ self.repository = Repository(model_name)
36
+ self.project_path = None
37
+ self.project_name = None
38
+ self.new_uci = None
39
+ self.uci = None
40
+ self.dm = None
41
+
42
+ def valid_models():
43
+ return Repository.valid_models()
44
+
45
+ def set_project_path(self,project_path):
46
+ self.project_path = Path(project_path)
47
+ self.project_name = Path(project_path).name
48
+ #self.new_uci = self.project_path.joinpath('model','_'.join([self.project_name,'0.uci']))
49
+ #self.uci = UCI(self.project_path.joinpath('model','.'.join([self.project_name,'uci'])))
50
+
51
+ def copy(self,project_location,project_name):
52
+ self.project_path = Path(project_location).joinpath(project_name)
53
+ self.project_name = project_name
54
+ self.repository.copy(self.project_path)
55
+ self.dm = dataManager(self.project_path.joinpath('data'))
56
+
57
+
58
+ def load_uci(self):
59
+ self.new_uci = self.project_path.joinpath('model','_'.join([self.project_name,'0.uci']))
60
+ self.uci = UCI(self.project_path.joinpath('model','.'.join([self.project_name,'uci'])))
61
+
62
+ def process(self):
63
+ self.load_uci()
64
+ self.format_uci()
65
+ self.download_station_data()
66
+ self.uci.build_targets().to_csv(self.project_path.joinpath('targets.csv'))
67
+
68
+ # if not self.project_path.joinpath('model',self.project_name + '_0-0.hbn').exists():
69
+ # self.run_model()
70
+
71
+ #TODO move to UCI class
72
+
73
+ def format_uci(self,calibration_reaches = None):
74
+
75
+
76
+ setup_files(self.uci,self.project_name,run = 0)
77
+ setup_geninfo(self.uci)
78
+
79
+ if calibration_reaches is None:
80
+ calibration_reaches = self.repository.modl_db.loc[self.repository.modl_db['wplmn_flag'] == 1]['opnids'].str.split(',').to_list()
81
+ calibration_reaches = [abs(int(j)) for i in calibration_reaches for j in i]
82
+ #calibration_reaches = self.uci.network.station_order(calibration_reaches)[-1][0]
83
+
84
+
85
+ self.uci.initialize(name = self.project_name + '_0')
86
+ # for reaches in self._MODL_DB['opnids'].str.split('+').to_list():
87
+ # [calibration_reaches.append(int(reach)) for reach in reaches if ~pd.isna(reach)]
88
+
89
+ setup_binaryinfo(self.uci,reach_ids = calibration_reaches)
90
+ setup_qualid(self.uci)
91
+ self.uci.write(self.new_uci)
92
+
93
+ # Download observation data
94
+ # Sources/Databases WISKI and EQUIS (DELTA databases?)
95
+ #TODO: use a single WISKI etl script for csg and wplmn data
96
+
97
+
98
+ def download_station_data(self):#:,station_id,source_id):
99
+ stations = self.repository.modl_db.drop_duplicates(subset = ['source','station_id'])
100
+ if not stations.empty:
101
+ for index, row in stations.iterrows():
102
+ assert(row['source'].lower() in ['wiski','equis','swd'])
103
+ station_id = row['station_id']
104
+ self.dm.download_station_data(station_id,row['source'].lower())
105
+
106
+
107
+ def convert_wdms(self):
108
+ copy_path = Path(self.project_path.joinpath('model'))
109
+ wdm_files = [copy_path.joinpath(wdm_file.name) for wdm_file in self.repository.wdm_files]
110
+ for wdm_file in wdm_files:
111
+ readWDM(wdm_file,
112
+ copy_path.joinpath(wdm_file.name.replace('.wdm','.hdf5').replace('.WDM','hdf5')))
113
+
114
+ def run_model(self):
115
+ # Run the uci file
116
+ winHSPF = str(Path(__file__).resolve().parent.parent) + '\\bin\\WinHSPFLt\\WinHspfLt.exe'
117
+ subprocess.run([winHSPF,self.new_uci]) #, stdout=subprocess.PIPE, creationflags=0x08000000)
118
+
119
+
120
+
121
+
122
+ ### functions for setting up the UCI file properly
123
+ def setup(uci,name,run = 0,reach_ids = None,n = 1,time_step = 3):
124
+ uci = setup_files(uci,name,run,n)
125
+ uci = setup_geninfo(uci)
126
+ uci = setup_binaryinfo(uci,reach_ids = reach_ids)
127
+ uci = setup_qualid(uci)
128
+
129
+
130
+ #def setup_files(uci,name,run = 0,n=5): # Add as method to a Files class?
131
+ # # Update paths in Files block
132
+
133
+ def setup_files(uci,name,run,n = 5):
134
+ table = uci.table('FILES',drop_comments = False)
135
+ for index, row in table.iterrows():
136
+ filename = Path(row['FILENAME'])
137
+ if filename.suffix in ['.wdm','.ech','.out']:
138
+ table.loc[index,'FILENAME'] = filename.name
139
+ if filename.suffix in ['.hbn']:
140
+ table.loc[index,'FILENAME'] = filename.name
141
+ if filename.suffix in ['.plt']:
142
+ table.drop(index,inplace = True)
143
+
144
+ # Get new binary number and create new BINO rows
145
+ bino_nums = []
146
+ invalid = table['UNIT'].values
147
+ for num in range(15,100):
148
+ if num not in invalid:
149
+ bino_nums.append(num)
150
+ if len(bino_nums) == n:
151
+ break
152
+ binary_names = ['_'.join([name,str(run)])+ '-' + str(num) + '.hbn' for num in range(len( bino_nums))]
153
+ rows = [['BINO',bino_num,binary_name,''] for bino_num,binary_name in zip(bino_nums,binary_names)]
154
+ rows = pd.DataFrame(rows, columns = table.columns).astype({'FTYPE':'string','UNIT':'Int64','FILENAME':'string','comments':'string'} )
155
+ # Drop old BINO rows and insert new BINO rows
156
+ table = table.loc[table['FTYPE'] != 'BINO'].reset_index(drop=True)
157
+ rows = pd.DataFrame(rows, columns = table.columns).astype(table.dtypes) #{'FTYPE':'string','UNIT':'Int64','FILENAME':'string','comments':'string'} )
158
+ table = pd.concat([table,rows])
159
+ table.reset_index(drop=True,inplace=True)
160
+
161
+ # Update table in the uci
162
+ uci.replace_table(table,'FILES')
163
+
164
+ return uci
165
+
166
+ def setup_geninfo(uci):
167
+ # Initialize Gen-Info
168
+ bino_nums = uci.table('FILES').set_index('FTYPE').loc['BINO','UNIT'].tolist()
169
+ if isinstance(bino_nums,int): #Pands is poorly designed. Why would tolist not return a goddamn list...?
170
+ bino_nums = [bino_nums]
171
+
172
+ #opnids = uci.table(operation,'GEN-INFO').index
173
+ for operation in ['RCHRES','PERLND','IMPLND']:
174
+ opnids = np.array_split(uci.table(operation,'GEN-INFO').index.to_list(),len(bino_nums))
175
+
176
+ for opnid,bino_num in zip(opnids,bino_nums):
177
+ if operation == 'RCHRES': #TODO convert BUNITE to BUNIT1 to get rid of this if statement
178
+ uci.update_table(bino_num,'RCHRES','GEN-INFO',0,opnids = opnid,columns = 'BUNITE',operator = 'set')
179
+ else:
180
+ uci.update_table(bino_num,operation,'GEN-INFO',0,opnids = opnid,columns = 'BUNIT1',operator = 'set')
181
+ return uci
182
+
183
+ def setup_binaryinfo(uci,default_output = 4,reach_ids = None):
184
+ # Initialize Binary-Info
185
+ uci.update_table(default_output,'PERLND','BINARY-INFO',0,
186
+ columns = ['AIRTPR', 'SNOWPR', 'PWATPR', 'SEDPR', 'PSTPR', 'PWGPR', 'PQALPR','MSTLPR', 'PESTPR', 'NITRPR', 'PHOSPR', 'TRACPR'],
187
+ operator = 'set')
188
+ uci.update_table(default_output,'IMPLND','BINARY-INFO',0,
189
+ columns = ['ATMPPR', 'SNOWPR', 'IWATPR', 'SLDPR', 'IWGPR', 'IQALPR'],
190
+ operator = 'set')
191
+ uci.update_table(default_output,'RCHRES','BINARY-INFO',0,
192
+ columns = ['HYDRPR', 'ADCAPR', 'CONSPR', 'HEATPR', 'SEDPR', 'GQLPR', 'OXRXPR', 'NUTRPR', 'PLNKPR', 'PHCBPR'],
193
+ operator = 'set')
194
+
195
+ uci.update_table(default_output,'PERLND','BINARY-INFO',0,columns = ['SNOWPR','SEDPR','PWATPR','PQALPR'],operator = 'set')
196
+ uci.update_table(default_output,'IMPLND','BINARY-INFO',0,columns = ['SNOWPR','IWATPR','SLDPR','IQALPR'],operator = 'set')
197
+ uci.update_table(default_output,'RCHRES','BINARY-INFO',0,columns = ['HYDRPR','SEDPR','HEATPR','OXRXPR','NUTRPR','PLNKPR'],operator = 'set')
198
+ if reach_ids is not None:
199
+ uci.update_table(3,'RCHRES','BINARY-INFO',0,columns = ['SEDPR','OXRXPR','NUTRPR','PLNKPR'],opnids = reach_ids,operator = 'set')
200
+ uci.update_table(2,'RCHRES','BINARY-INFO',0,columns = ['HEATPR','HYDRPR'],opnids = reach_ids,operator = 'set')
201
+ return uci
202
+
203
+
204
+ def setup_qualid(uci):
205
+ #### Standardize QUAL-ID Names
206
+ # Perlands
207
+ uci.update_table('NH3+NH4','PERLND','QUAL-PROPS',0,columns = 'QUALID',operator = 'set')
208
+ uci.update_table('NO3','PERLND','QUAL-PROPS',1,columns = 'QUALID',operator = 'set')
209
+ uci.update_table('ORTHO P','PERLND','QUAL-PROPS',2,columns = 'QUALID',operator = 'set')
210
+ uci.update_table('BOD','PERLND','QUAL-PROPS',3,columns = 'QUALID',operator = 'set')
211
+
212
+ # Implands
213
+ uci.update_table('NH3+NH4','IMPLND','QUAL-PROPS',0,columns = 'QUALID',operator = 'set')
214
+ uci.update_table('NO3','IMPLND','QUAL-PROPS',1,columns = 'QUALID',operator = 'set')
215
+ uci.update_table('ORTHO P','IMPLND','QUAL-PROPS',2,columns = 'QUALID',operator = 'set')
216
+ uci.update_table('BOD','IMPLND','QUAL-PROPS',3,columns = 'QUALID',operator = 'set')
217
+ return uci
218
+
219
+
220
+
221
+ # def build_targets(uci,reach_ids,wplmn_map,wplmn_path,wplmn = None):
222
+ # targets = uci.lc_info()
223
+ # uci_names = targets['uci_name'].values
224
+ # if wplmn is None:
225
+ # wplmn = get_yields(reach_ids,uci,wplmn_path)
226
+
227
+ # npsl_names = [wplmn_map[name] for name in targets['uci_name']]
228
+ # targets['npsl_name'] = npsl_names #targets['npsl_name'] = npsl_names
229
+
230
+
231
+
232
+ # npsl_rates = pd.concat([uci.npsl_targets.loc[name].mean() for name in npsl_names],axis = 1).transpose()
233
+ # npsl_rates['uci_name'] = uci_names
234
+ # npsl_rates = npsl_rates.melt(id_vars = 'uci_name',var_name = 'parameter', value_name = 'npsl_yield')
235
+
236
+ # targets = npsl_rates.merge(targets,on = 'uci_name')
237
+ # targets = targets.set_index(['uci_name','parameter'])
238
+
239
+ # # normalize by dominant landcover of the model
240
+ # dom_lc_name = targets.index.get_level_values(0)[np.argmax(targets['area'])]
241
+ # targets['npsl_yield_ratio'] = np.nan
242
+ # for parameter in ['TSS','TKN','N','OP','BOD']:
243
+ # values = targets.loc[:,parameter,:]['npsl_yield']/targets.loc[dom_lc_name,parameter]['npsl_yield']
244
+ # targets.loc[values.index,'npsl_yield_ratio'] = values
245
+
246
+ # # landcover area ratio compared to total area of watershed
247
+ # targets['lc_area_ratio']= targets['area']/np.sum(targets.loc[:,'TSS',:]['area'])
248
+
249
+
250
+ # targets['wplmn_yield'] = np.nan
251
+ # for parameter in ['TSS','TKN','N','OP','BOD']:
252
+ # targets.loc[targets.loc[:,parameter,:].index,'wplmn_yield'] = wplmn[parameter]
253
+
254
+ # # interative solution to determine target landcover loading rates
255
+ # targets['lc_yield'] = np.nan
256
+ # threshold = .05
257
+ # for parameter in ['TSS','TKN','N','OP','BOD']:
258
+ # print(parameter)
259
+ # diff = 1
260
+ # scaling_factor = 1
261
+ # new_factor = 1
262
+ # while diff > threshold:
263
+ # scaling_factor = new_factor
264
+ # lc_yield = (targets['lc_area_ratio']*targets['npsl_yield_ratio']*scaling_factor).loc[:,parameter,:]
265
+ # new_factor = wplmn[parameter]/np.sum(lc_yield)*scaling_factor
266
+ # diff = np.abs((np.nansum(lc_yield)-wplmn[parameter])/wplmn[parameter])
267
+ # print(scaling_factor)
268
+ # values = targets.loc[targets.loc[:,parameter,:].index,'npsl_yield_ratio']*scaling_factor
269
+ # targets.loc[values.index,'lc_yield'] = values.values
270
+
271
+
272
+ # targets = uci.lc_info().set_index('uci_name').join(targets.unstack()['lc_yield'])
273
+ # targets['dom_lc'] = np.nan
274
+ # targets.loc[targets.index[np.argmax(targets['area'])],'dom_lc'] = 1
275
+ # return targets
276
+
277
+ # def get_yields(reach_ids,uci,wplmn_path):
278
+ # loadNetwork = dm.loadNetwork(wplmn_path,reach_ids)
279
+
280
+ # schematic = uci.table('SCHEMATIC')
281
+ # schematic = schematic.astype({'TVOLNO': int, "SVOLNO": int, 'AFACTR':float})
282
+ # schematic = schematic[(schematic['SVOL'] == 'PERLND')| (schematic['SVOL'] == 'IMPLND')]
283
+ # schematic = schematic[schematic['TVOL'] == 'RCHRES']
284
+ # reaches = np.concatenate([nu.get_opnids(uci,reach_id)[0] for reach_id in reach_ids])
285
+ # area = np.sum([schematic['AFACTR'][schematic['TVOLNO'] == reach].sum() for reach in reaches])
286
+
287
+
288
+ # # wplmn2 = {'TSS': 18.01,
289
+ # # 'TP': 0.23,
290
+ # # 'OP':0.082,
291
+ # # 'TKN':1.921,
292
+ # # 'N':2.304}
293
+ # # wplmn2['BOD'] = 50*(wplmn['TP'] - wplmn['OP'])
294
+ # # wplmn2['TKN'] = wplmn['TKN'] - wplmn['BOD']*.144
295
+
296
+ # wplmn = {'TSS': 0,
297
+ # 'TP': 0,
298
+ # 'OP':0,
299
+ # 'TKN':0,
300
+ # 'N':0}
301
+
302
+ # for key in wplmn.keys():
303
+ # if key == 'TSS':
304
+ # wplmn[key] = np.mean(loadNetwork.get_timeseries(key,'Load').groupby('year').sum()['Ts Value']/area*2000)
305
+ # else:
306
+ # wplmn[key] = np.mean(loadNetwork.get_timeseries(key,'Load').groupby('year').sum()['Ts Value']/area)
307
+
308
+
309
+ # wplmn['BOD'] = 50*(wplmn['TP'] - wplmn['OP'])
310
+ # wplmn['TKN'] = wplmn['TKN'] - wplmn['BOD']*.144
311
+ # return wplmn
312
+
313
+ # def lc_info(uci):
314
+ # uci.get_metzones2()
315
+ # geninfo = uci.table('PERLND','GEN-INFO')
316
+ # targets = uci.opnid_dict['PERLND'].loc[:,['LSID','landcover']] #.drop_duplicates(subset = 'landcover').loc[:,['LSID','landcover']].reset_index(drop = True)
317
+ # targets.columns = ['LSID','lc_number']
318
+ # schematic = uci.table('SCHEMATIC')
319
+ # schematic = schematic.astype({'TVOLNO': int, "SVOLNO": int, 'AFACTR':float})
320
+ # schematic = schematic[(schematic['SVOL'] == 'PERLND')]
321
+ # schematic = schematic[(schematic['TVOL'] == 'PERLND') | (schematic['TVOL'] == 'IMPLND') | (schematic['TVOL'] == 'RCHRES')]
322
+ # areas = []
323
+ # for lc_number in targets['lc_number'].unique():
324
+ # areas.append(np.sum([schematic['AFACTR'][schematic['SVOLNO'] == perland].sum() for perland in targets.index[targets['lc_number'] == lc_number]]))
325
+ # areas = np.array(areas)
326
+
327
+
328
+ # lc_number = targets['lc_number'].drop_duplicates()
329
+ # uci_names = geninfo.loc[targets['lc_number'].drop_duplicates().index]['LSID']
330
+ # targets = pd.DataFrame([uci_names.values,lc_number.values,areas]).transpose()
331
+ # targets.columns = ['uci_name','lc_number','area']
332
+ # targets['npsl_name'] = ''
333
+
334
+ # targets[['TSS','N','TKN','OP','BOD']] = ''
335
+
336
+ # targets['dom_lc'] = ''
337
+ # targets.loc[targets['area'].astype('float').argmax(),'dom_lc'] = 1
338
+ # return targets
339
+
340
+
341
+
342
+
343
+
344
+ #WPLMN = pd.read_csv()
345
+ # HUC_DIRECTORY = pd.read_csv('C:/Users/mfratki/Documents/Github/hspf_tools/calibrator/HUC_Names.csv',dtype = {'USGS HUC-8':'string',
346
+ # 'USGS HUC-6':'string',
347
+ # 'USGS HUC-4':'string',
348
+ # 'USGS HUC-2':'string'})
349
+ #MODL_DB = pd.read_csv('C:/Users/mfratki/Documents/Github/hspf_tools/calibrator/WPLMN.csv',dtype = {'stn_HUC12':'string'})
350
+
351
+
352
+ # REPO = Path('X:\Databases2\Water_Quality\Watershed_Modeling\MPCA_HSPF_Model_Repository') #could point to the github website if it becomes public
353
+ # huc8_id = '10170204'
354
+ # huc6_name = HUC_DIRECTORY.loc[HUC_DIRECTORY['USGS HUC-6'] == huc8_id[:6],'Repository_HUC6 Name'].iloc[0]
355
+ # model_name = HUC_DIRECTORY.loc[HUC_DIRECTORY['USGS HUC-8'] == huc8_id[:8],'Repository_HUC8 Name'].iloc[0]
356
+ # repo_folder = [item for item in REPO.joinpath('_'.join([huc6_name,huc8_id[:6]])).iterdir() if item.name.startswith(model_name)]
357
+ # repo_folder = repo_folder[0]
358
+
359
+ # stations = gpd.read_file('C:/Users/mfratki/Documents/Projects/MODL Database/stations_WISKI2.gpkg')
360
+ # stations.dropna(subset='stn_HUC12',inplace=True)
361
+ # stations['opnids'] = stations['opnids'].str.replace('-','+')
362
+ # HUC_DIRECTORY = pd.read_csv('C:/Users/mfratki/Documents/Github/hspf_tools/calibrator/HUC_Names.csv',dtype = {'USGS HUC-8':'string',
363
+ # 'USGS HUC-6':'string',
364
+ # 'USGS HUC-4':'string',
365
+ # 'USGS HUC-2':'string'})
366
+ # test = pd.merge(wiski, HUC_DIRECTORY, left_on= 'HUC8',
367
+ # right_on= 'USGS HUC-8',
368
+ # how = 'left')
369
+ # class Builder():
370
+ # # HUC_DIRECTORY = pd.read_csv('C:/Users/mfratki/Documents/Github/hspf_tools/calibrator/HUC_Names.csv',dtype = {'USGS HUC-8':'string',
371
+ # # 'USGS HUC-6':'string',
372
+ # # 'USGS HUC-4':'string',
373
+ # # 'USGS HUC-2':'string'})
374
+ # # MODL_DB = pd.read_csv('C:/Users/mfratki/Documents/Github/hspf_tools/calibrator/WPLMN.csv',dtype = {'stn_HUC12':'string',
375
+ # # 'USGS HUC-8':'string',
376
+ # # 'USGS HUC-6':'string',
377
+ # # 'USGS HUC-4':'string',
378
+ # # 'USGS HUC-2':'string'}
379
+
380
+ # # MODL_DB = pd.read_csv(Path(__file__).parent/'stations_WISKI.csv',dtype = {'stn_HUC12':'string',
381
+ # # 'USGS HUC-8':'string',
382
+ # # 'USGS HUC-6':'string',
383
+ # # 'USGS HUC-2':'string',
384
+ # # 'USGS HUC-4':'string'})
385
+ # # MODL_DB = pd.read_csv('C:/Users/mfratki/Documents/Projects/MODL Database/stations_WISKI.csv', dtype = {'stn_HUC12':'string',
386
+ # # 'USGS HUC-8':'string',
387
+ # # 'USGS HUC-6':'string',
388
+ # # 'USGS HUC-4':'string',
389
+ # # 'USGS HUC-2':'string'})
390
+
391
+ # MODL_DB = gpd.read_file('C:/Users/mfratki/Documents/Projects/MODL Database/gis/stations_csg.gpkg').dropna(subset='opnids')
392
+ # #MODL_DB = gpd.read_file('C:/Users/mfratki/Documents/Projects/MODL Database/stations_WISKI2.gpkg') #.dropna(subset='opnids')
393
+ # #MODL_DB['opnids'] = MODL_DB['opnids'].str.replace('-','+')
394
+ # MODL_DB = MODL_DB.dropna(subset='opnids')
395
+ # MODL_DB = MODL_DB.loc[~(MODL_DB['opnids'] == -1)]
396
+ # REPO = Path('X:\Databases2\Water_Quality\Watershed_Modeling\MPCA_HSPF_Model_Repository') #could point to the github website if it becomes public
397
+
398
+ # @classmethod
399
+ # def valid_models(self):
400
+ # return list(self.MODL_DB['Repository_HUC8 Name'].dropna()) #replace('NO MODEL',pd.NA).dropna())
401
+
402
+
403
+ # def __init__(self,model_name):# = None, huc8_id = None)
404
+ # if model_name not in self.valid_models():
405
+ # print('Please provide a valid model name (see .valid_models)')
406
+ # return
407
+
408
+ # self.model_name = model_name
409
+ # #self._HUC_DIRECTORY = self.HUC_DIRECTORY.loc[self.HUC_DIRECTORY['Repository_HUC8 Name'] == self.model_name]
410
+ # #self.huc6_name = self._HUC_DIRECTORY['Repository_HUC6 Name'].iloc[0]
411
+ # #self.huc8_id = self._HUC_DIRECTORY['USGS HUC-8'].iloc[0] #HUC_DIRECTORY.loc[HUC_DIRECTORY['USGS HUC-6'] == huc8_id[:6],'Repository_HUC6 Name'].iloc[0]
412
+
413
+ # self._MODL_DB = self.MODL_DB.loc[self.MODL_DB['Repository_HUC8 Name'] == self.model_name]
414
+ # self.huc6_name = self._MODL_DB['Repository_HUC6 Name'].iloc[0]
415
+ # self.huc6_id = self._MODL_DB['USGS HUC-6'].iloc[0]
416
+ # self.huc8_ids = self._MODL_DB['USGS HUC-8'].unique()
417
+ # self.repo_folder = [item for item in self.REPO.joinpath('_'.join([self.huc6_name,self.huc6_id])).iterdir() if item.name.startswith(self.model_name)][0]
418
+ # self.uci_file = self.repo_folder.joinpath('HSPF','.'.join([self.model_name,'uci']))
419
+ # self.wdm_files = [item for item in self.repo_folder.joinpath('HSPF').iterdir() if item.name.endswith('.wdm')]
420
+
421
+ # def copy(self,project_location):
422
+
423
+
424
+ # build_folders(self.model_name,project_location)
425
+ # self.project_path = Path(project_location).joinpath(self.model_name)
426
+ # self.new_uci = self.project_path.joinpath('model','_'.join([self.model_name,'0.uci']))
427
+ # shutil.copyfile(self.uci_file, self.project_path.joinpath('model','.'.join([self.model_name,'uci'])))
428
+ # self.uci = UCI(self.project_path.joinpath('model','.'.join([self.model_name,'uci'])))
429
+ # for wdm_file in self.wdm_files:
430
+ # shutil.copyfile(wdm_file,self.project_path.joinpath('model',Path(wdm_file).name))
431
+
432
+
433
+ # self.format_uci()
434
+ # self.download_wplmn()
435
+ # self.download_csg()
436
+ # self.build_lc_targets()
437
+ # if not self.project_path.joinpath('model',self.model_name + '_0-0.hbn').exists():
438
+ # self.run_model()
439
+ # self._MODL_DB.to_csv(self.project_path.joinpath('_'.join([self.model_name,'MODL_DB.csv'])))
440
+
441
+ # def format_uci(self):
442
+
443
+ # setup_files(self.uci,self.model_name,run = 0)
444
+ # setup_geninfo(self.uci)
445
+
446
+ # calibration_reaches = self._MODL_DB['opnids'].astype('int').to_list()
447
+ # # for reaches in self._MODL_DB['opnids'].str.split('+').to_list():
448
+ # # [calibration_reaches.append(int(reach)) for reach in reaches if ~pd.isna(reach)]
449
+
450
+ # setup_binaryinfo(self.uci,reach_ids = calibration_reaches)
451
+ # setup_qualid(self.uci)
452
+ # self.uci.write(self.new_uci)
453
+
454
+ # # Download observation data
455
+ # # Sources/Databases WISKI and EQUIS (DELTA databases?)
456
+ # #TODO: use a single WISKI etl script for csg and wplmn data
457
+ # def download_csg(self):
458
+ # if self.project_path.joinpath('csg.csv').exists():
459
+ # print('CSG data already downloaded')
460
+ # station_nos = (self._MODL_DB.loc[self._MODL_DB['WPLMN'] == 0,'station_no'].unique())
461
+ # if len(station_nos) == 0:
462
+ # print('No CSG stations linked to Watershed yet')
463
+ # return
464
+ # data = pd.concat([etlCSG.download(station_no).pipe(etlCSG.transform) for station_no in station_nos])
465
+ # #reach_map = dict(self._MODL_DB.loc[self._MODL_DB['station_no'].isin(station_nos),['station_no','opnids']].values)
466
+ # #data['reach_id'] = data['station_no'].map(reach_map)
467
+ # etlCSG.load(data,self.project_path.joinpath('csg.csv'))
468
+
469
+ # def download_wplmn(self):
470
+ # if self.project_path.joinpath('wplmn.csv').exists():
471
+ # print('WPLMN data already downloaded')
472
+ # station_nos = list(self._MODL_DB.loc[self._MODL_DB['WPLMN'] == 1,'station_no'].unique())
473
+ # data = pd.concat([etlWPLMN.download(station_no).pipe(etlWPLMN.transform) for station_no in station_nos])
474
+ # #reach_map = dict(self._MODL_DB.loc[self._MODL_DB['station_no'].isin(station_nos),['station_no','opnids']].values)
475
+ # #data['reach_id'] = data['Station number'].map(reach_map)
476
+ # etlCSG.load(data,self.project_path.joinpath('wplmn.csv'))
477
+
478
+ # def download_swd(self):
479
+ # if self.project_path.joinpath('swd.csv').exists():
480
+ # print('SWD data already downloaded')
481
+ # station_nos = list(self._MODL_DB.loc[self._MODL_DB['source'] == 'swd','station_no'].unique())
482
+ # data = pd.concat([etlSWD.download(station_no).pipe(etlSWD.transform) for station_no in station_nos])
483
+ # etlSWD.load(data,self.project_path.joinpath('swd.csv'))
484
+
485
+ # # Create landcover targets spreadsheet
486
+ # def build_lc_targets(self):
487
+ # lc_info(self.uci).to_csv(self.project_path.joinpath('targets.csv'))
488
+
489
+ # def run_model(self):
490
+ # # Run the uci file
491
+ # winHSPF = str(Path(__file__).resolve().parent.parent) + '\\bin\\WinHSPFLt\\WinHspfLt.exe'
492
+ # subprocess.run([winHSPF,self.new_uci]) #, stdout=subprocess.PIPE, creationflags=0x08000000)
493
+
494
+
495
+ # # if (model_name is None) & (huc8_id is None):
496
+ # # print('Please provide Either a HUC8 ID or a valid model name. To see valid model names see .valid_models')
497
+ # # return
498
+ # #if model_name is None:
499
+ # # else:
500
+ # # self._HUC_DIRECTORY = self.HUC_DIRECTORY.loc[self.HUC_DIRECTORY['Repository_HUC8 Name',:]
501
+ # # self.huc6_name = self._HUC_DIRECTORY.loc[0,'Repository_HUC6 Name']
502
+ # # self.model_name = self._HUC_DIRECTORY.loc[0,'Repository_HUC8 Name']
503
+ # # self.repo_folder =
504
+
505
+
506
+
507
+
508
+
509
+ # load original uci
510
+
511
+ # def setup_files(uci,project_name): # Add as method to a Files class?
512
+ # # Update paths in Files block
513
+ # table = uci.table('FILES',drop_comments = False)
514
+ # for index, row in table.iterrows():
515
+ # name = Path(row['FILENAME'])
516
+ # if name.suffix in ['ech','out','wdm']:
517
+ # table.loc[index,'FILENAME'] = name
518
+ # if name.suffix in ['hbn']:
519
+ # table.loc[index,'FILENAME'] = name
520
+ # if name.suffix in ['plt']:
521
+ # table.drop(index)
522
+
523
+ # table = table.set_index(table.columns[0])
524
+
525
+
526
+ # # Rename hbn in Files block
527
+ # #TODO: dynamic size management of hbn files
528
+ # new_bino = table.loc['BINO'][~table.loc['BINO'].index.duplicated()]
529
+ # table = table.drop('BINO',axis=0)
530
+ # table = table.append(new_bino)
531
+ # table.loc['BINO','FILENAME'] = project_name + '_0.hbn'
532
+ # table = table.reset_index()
533
+ # uci.replace_table(table,'FILES')
534
+ # return uci
535
+
536
+
537
+ # def setup_geninfo(uci):
538
+ # # Initialize Gen-Info
539
+ # bino_num = uci.table('FILES').set_index('FTYPE').loc['BINO','UNIT']
540
+ # uci.update_table(bino_num,'PERLND','GEN-INFO',0,columns = 'BUNIT1',operator = 'set')
541
+ # uci.update_table(bino_num,'RCHRES','GEN-INFO',0,columns = 'BUNITE',operator = 'set')
542
+ # uci.update_table(bino_num,'IMPLND','GEN-INFO',0,columns = 'BUNIT1',operator = 'set')
543
+ # return uci
544
+
545
+ # def setup_binaryinfo(uci,reach_ids = None):
546
+ # # Initialize Binary-Info
547
+ # uci.update_table(4,'PERLND','BINARY-INFO',0,columns = ['SNOWPR','SEDPR','PWATPR','PQALPR'],operator = 'set')
548
+ # uci.update_table(4,'IMPLND','BINARY-INFO',0,columns = ['SNOWPR','IWATPR','SLDPR','IQALPR'],operator = 'set')
549
+ # uci.update_table(4,'RCHRES','BINARY-INFO',0,columns = ['HYDRPR','SEDPR','HEATPR','OXRXPR','NUTRPR','PLNKPR'],operator = 'set')
550
+ # if reach_ids is not None:
551
+ # uci.update_table(3,'RCHRES','BINARY-INFO',0,columns = ['HYDRPR','SEDPR','HEATPR','OXRXPR','NUTRPR','PLNKPR'],opnids = reach_ids,operator = 'set')
552
+ # return uci
553
+
554
+ # def setup_qualid(uci):
555
+ # #### Standardize QUAL-ID Names
556
+ # # Perlands
557
+ # uci.update_table('NH3+NH4','PERLND','QUAL-PROPS',0,columns = 'QUALID',operator = 'set')
558
+ # uci.update_table('NO3','PERLND','QUAL-PROPS',1,columns = 'QUALID',operator = 'set')
559
+ # uci.update_table('ORTHO P','PERLND','QUAL-PROPS',2,columns = 'QUALID',operator = 'set')
560
+ # uci.update_table('BOD','PERLND','QUAL-PROPS',3,columns = 'QUALID',operator = 'set')
561
+
562
+ # # Implands
563
+ # uci.update_table('NH3+NH4','IMPLND','QUAL-PROPS',0,columns = 'QUALID',operator = 'set')
564
+ # uci.update_table('NO3','IMPLND','QUAL-PROPS',1,columns = 'QUALID',operator = 'set')
565
+ # uci.update_table('ORTHO P','IMPLND','QUAL-PROPS',2,columns = 'QUALID',operator = 'set')
566
+ # uci.update_table('BOD','IMPLND','QUAL-PROPS',3,columns = 'QUALID',operator = 'set')
567
+ # return uci
568
+
569
+
570
+
571
+
572
+
573
+
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyhcal
3
+ Version: 1.0.0
4
+ Summary: Python package for calibrating MPCA HSPF models
5
+ Project-URL: Homepage, https://github.com/mfratkin1/pyhcal
6
+ Author-email: Mulu Fratkin <michael.fratkin@state.mn.us>
7
+ Maintainer-email: Mulu Fratkin <michael.fratkin@state.mn.us>
8
+ License-Expression: MIT
9
+ Keywords: HSPF,Hydrology
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Programming Language :: Python
12
+ Requires-Python: >=3.8
13
+ Requires-Dist: geopandas
14
+ Requires-Dist: hspf
15
+ Requires-Dist: mpcahydro
@@ -0,0 +1,14 @@
1
+ pyhcal/__init__.py,sha256=4TEpGD-PfEY8yK-od8DpEMA4_iQ-q9y0PBvROXSPdB0,94
2
+ pyhcal/calibrators.py,sha256=DuJHBpleICf6f9CbMQBB1CQuiGNVOIHY4U9olOHEObE,29964
3
+ pyhcal/figures.py,sha256=Iu7LaN_i2IuDA_nfxj-a8AkG-FTLZVicJ3-efIs5OiE,45534
4
+ pyhcal/metrics.py,sha256=GUGHd-op-g1Foj8wnS_JVURSms4ifcC0a5h8ketQ29I,17911
5
+ pyhcal/modl_db.py,sha256=0NoWNNxfbF1fy43geOO4EJt-Uev6Og4Hw-KNn9xvAys,3163
6
+ pyhcal/repository.py,sha256=4mbWqTHnVWpfSt8Rs8PBMLePrjPVakOMRuZse0LGY54,4456
7
+ pyhcal/setup_utils.py,sha256=KWWygZc9KLOGonzwMEOWNl5G2Zhq62Esuy7AKGdpIxA,29865
8
+ pyhcal/data/HUC_Names.csv,sha256=UGmd3Q5E8DyFWggXzaXWpsRze7sFyrlpYqaYpMWAiGM,18946
9
+ pyhcal/data/WISKI_EQUIS_XREF.csv,sha256=bPYq-f4-Qc6jsvUgl81lwXBeFamfDe5TjohqUV1XJlg,1244704
10
+ pyhcal/data/stations_EQUIS.gpkg,sha256=KyWMRbNoSSMDB1IweoLhyFeKu1P-YoYpQbXna6VYp8I,3284992
11
+ pyhcal/data/stations_wiski.gpkg,sha256=HlH5EwUo9qEEoxtbEpL0cJZHEW57XEUA-ROy4sN7eE4,892928
12
+ pyhcal-1.0.0.dist-info/METADATA,sha256=MiHmRB0kKf207o1IijXt7VHuY8-0zdPXv8AyBeSmVjQ,513
13
+ pyhcal-1.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
+ pyhcal-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any