hspf 2.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.
hspf/reports.py ADDED
@@ -0,0 +1,1230 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Mon Apr 11 08:26:04 2022
4
+
5
+ @author: mfratki
6
+ """
7
+ import numpy as np
8
+ import pandas as pd
9
+ from pyhspf import helpers
10
+ from pathlib import Path
11
+
12
+ #timeseries_catalog = pd.read_csv(Path(__file__).parent/'TIMESERIES_CATALOG.csv')
13
+
14
+ #ts_catalog = pd.read_csv('C:/Users/mfratki/Documents/GitHub/hspf_tools/parser/TIMESERIES_CATALOG.csv')
15
+
16
+
17
+ class Reports():
18
+ def __init__(self,uci,hbns,wdms):
19
+ self.hbns = hbns
20
+ self.uci = uci
21
+ self.wdms = wdms
22
+
23
+ #Sediment Reports
24
+ def scour(self,start_year = '1996',end_year = '2030'):
25
+ return scour(self.hbns,self.uci,start_year = start_year,end_year=end_year)
26
+
27
+ # Hydrology Reports
28
+ def landcover_area(self):
29
+ return landcover_areas(self.uci)
30
+
31
+ def annual_water_budget(self,operation):
32
+ assert operation in ['PERLND','RCHRES','IMPLND']
33
+ if operation =='PERLND':
34
+ return annual_perlnd_water_budget(self.uci,self.hbns)
35
+ elif operation == 'IMPLND':
36
+ return annual_implnd_water_budget(self.uci,self.hbns)
37
+ else:
38
+ return annual_reach_water_budget(self.uci,self.hbns)
39
+
40
+ # def annual_runoff(self):
41
+ # #assert operation in ['PERLND','IMPLND']
42
+ # #if operation == 'PERLND':
43
+ # return annual_perlnd_runoff(self.uci,self.hbns)
44
+ # #else:
45
+ # # raise NotImplementedError()
46
+
47
+ # def monthly_runoff(self,landcover=None):
48
+ # df = monthly_perlnd_runoff(self.uci,self.hbns).unstack().T
49
+ # if landcover is None:
50
+ # return df
51
+ # else:
52
+ # return df.loc[landcover]
53
+
54
+ def annual_sediment_budget(self):
55
+ return annual_sediment_budget(self.uci,self.hbns)
56
+
57
+ def ann_avg_subwatershed_loading(self,constituent):
58
+ return ann_avg_subwatershed_loading(constituent,self.uci, self.hbns)
59
+
60
+ def ann_avg_watershed_loading(self,constituent,reach_ids):
61
+ landcovers = ann_avg_watershed_loading(constituent,reach_ids,self.uci, self.hbns, True)
62
+ total = ann_avg_watershed_loading(constituent,reach_ids,self.uci, self.hbns, False)
63
+ total.index = ['Total']
64
+ total = pd.concat([landcovers,total])
65
+ total['volume'] = total['area']*total[f'weighted_mean_{constituent}']
66
+ total['volume_percent'] = total['volume']/total.loc['Total','volume']*100
67
+ total['area_percent'] = total['area']/total.loc['Total','area']*100
68
+ total['share'] = total['volume_percent']/total['area_percent']
69
+ return total
70
+
71
+ # def monthly_avg_subwatershed_loading(self,constituent,month):
72
+ # return monthly_avg_subwatershed_loading(constituent,month,self.uci, self.hbns)
73
+
74
+ # def monthly_avg_watershed_loading(self,constituent,reach_ids,month,by_landcover = True):
75
+ # return monthly_avg_watershed_loading(constituent,reach_ids,month,self.uci, self.hbns,by_landcover = by_landcover)
76
+
77
+
78
+ def ann_avg_yield(self,constituent,reach_ids):
79
+ df= avg_ann_yield(self.uci,self.hbns,constituent,reach_ids)
80
+ return df
81
+
82
+ def annual_precip(self):
83
+ return avg_annual_precip(self.uci,self.wdms)
84
+
85
+ # def water_balance(self,reach_ids = None):
86
+ # if reach_ids is None:
87
+ # reach_ids = self.uci.network.outlets()
88
+ # return water_balance(self.uci,self.hbns,self.wdms,reach_ids)
89
+
90
+ def simulated_et(self):
91
+ return simulated_et(self.uci,self.hbns)
92
+
93
+ # def inflows(self):
94
+ # return inflows(self.uci,self.wdms)
95
+
96
+
97
+
98
+
99
+ #%% Channel Reports
100
+ def scour(hbn,uci,start_year = '1996',end_year = '2030'):
101
+ # Should eventually create an entire reports module or class indorder to calculate all of the different model checks
102
+ # TODO: Incorporate IMPLNDS
103
+ schematic = uci.table('SCHEMATIC').copy()
104
+ schematic = schematic.astype({'TVOLNO': int, "SVOLNO": int, 'AFACTR':float})
105
+ schematic = schematic[(schematic['SVOL'] == 'PERLND') | (schematic['SVOL'] == 'IMPLND')]
106
+ schematic = schematic[schematic['TVOL'] == 'RCHRES']
107
+
108
+ sosed = hbn.get_multiple_timeseries(t_opn = 'PERLND',
109
+ t_con = 'SOSED',
110
+ activity = 'SEDMNT',
111
+ t_code = 'yearly',
112
+ opnids = None)
113
+ sosed = sosed.loc[(sosed.index > start_year) & (sosed.index < end_year)].mean().rename('mean').to_frame()
114
+
115
+ sosld = hbn.get_multiple_timeseries(t_opn = 'IMPLND',
116
+ t_con = 'SOSLD',
117
+ activity = 'SOLIDS',
118
+ t_code = 'yearly',
119
+ opnids = None)
120
+ sosld = sosld.loc[(sosld.index > start_year) & (sosld.index < end_year)].mean().rename('mean').to_frame()
121
+
122
+ depscr = hbn.get_multiple_timeseries(t_opn = 'RCHRES',
123
+ t_con = 'DEPSCOURTOT',
124
+ activity = 'SEDTRN',
125
+ t_code = 'yearly',
126
+ opnids = None)
127
+ depscr = depscr.loc[(depscr.index > start_year) & (depscr.index < end_year)].mean().rename('mean').to_frame()
128
+
129
+ lakeflag = uci.table('RCHRES','GEN-INFO').copy()[['RCHID','LKFG']]
130
+
131
+ scour_report = []
132
+ # schematic block will have all the possible perlands while sosed only has perlands that were simulated
133
+ # in other words information from sosed is a subset of schematic
134
+ for tvolno in lakeflag.index: #schematic['TVOLNO'].unique():
135
+ reach_load = depscr.loc[tvolno].values[0]
136
+ schem_sub = schematic[schematic['TVOLNO'] == tvolno]
137
+ if len(schem_sub) == 0:
138
+ scour_report.append((tvolno,np.nan,reach_load))
139
+ else:
140
+ #Only consider perlands that wer actually simulated in the model (binary flag in set to 0)
141
+ # Calculate contributions from PERLNDS
142
+ if 'PERLND' in schem_sub['SVOL'].values:
143
+ schem_prlnd = schem_sub[schem_sub['SVOL'] == 'PERLND'].copy()
144
+ sosed_match = [x for x in schem_prlnd['SVOLNO'] if x in sosed.index]
145
+ schem_prlnd = schem_prlnd[schem_prlnd['SVOLNO'].isin(sosed_match)]
146
+ sosed_sub = sosed.loc[sosed_match]
147
+ prlnd_load = np.sum(schem_prlnd['AFACTR'].values*sosed_sub['mean'].values)#lb/yr
148
+
149
+ # Calculate contributions from IMPLNDS
150
+ if 'IMPLND' in schem_sub['SVOL'].values:
151
+ schem_implnd = schem_sub[schem_sub['SVOL'] == 'IMPLND'].copy()
152
+ sosld_match = [x for x in schem_implnd['SVOLNO'] if x in sosld.index]
153
+ schem_implnd = schem_implnd[schem_implnd['SVOLNO'].isin(sosld_match)]
154
+ sosld_sub = sosld.loc[sosld_match]
155
+ implnd_load = np.sum(schem_implnd['AFACTR'].values*sosld_sub['mean'].values)#lb/yr
156
+
157
+ watershed_load = prlnd_load + implnd_load
158
+ scour_report.append((tvolno,watershed_load,reach_load))
159
+
160
+ scour_report = pd.DataFrame(scour_report,columns = ['TVOLNO','nonpoint','depscour'])
161
+
162
+ scour_report['ratio'] = scour_report['nonpoint']/(scour_report['nonpoint']+np.abs(scour_report['depscour']))
163
+
164
+ scour_report = pd.merge(lakeflag, scour_report, left_index=True, right_on='TVOLNO').set_index('TVOLNO')
165
+ return scour_report
166
+
167
+
168
+ def get_catchments(uci,reach_ids):
169
+ # Grab metadata information
170
+ subwatersheds = uci.network.subwatersheds().loc[reach_ids].reset_index()
171
+ landcover = subwatersheds.set_index('SVOL').loc['PERLND',:].set_index('SVOLNO')
172
+ landcover = landcover.join(uci.opnid_dict['PERLND'])
173
+ landcover = landcover[['AFACTR','LSID','metzone','TVOLNO','MLNO']]
174
+ landcover['AFACTR'] = landcover['AFACTR'].replace(0,pd.NA)
175
+ return landcover
176
+
177
+
178
+ #%% Catchment Loading (ie. Direct contributions from perlnds/implnds, no losses)
179
+ # Q
180
+ # Subwatershed Weighted Mean Timeseries Output
181
+ # operation = 'PERLND'
182
+ # ts_name = 'PERO',
183
+ # time_code = 5
184
+
185
+
186
+ LOADING_MAP = {'Q' : [{'t_opn':'PERLND',
187
+ 't_con': 'PERO',
188
+ 't_code': 'yearly',
189
+ 'activity': 'PWATER'}],
190
+ 'TSS': [{'t_opn':'PERLND',
191
+ 't_con': 'SOSED',
192
+ 't_code': 'yearly',
193
+ 'activity': 'SEDMNT'},
194
+ {'t_opn':'IMPLND',
195
+ 't_con': 'SOSED',
196
+ 't_code': 'yearly',
197
+ 'activity': 'SEDMNT'}]}
198
+
199
+
200
+ # def annual_average_subwatershed_loading(constituent,uci,hbn,reach_ids):
201
+ # '''
202
+
203
+ # For each subwatershed the annual average loading rate
204
+
205
+ # For each subwatershed the average loading rate for a specific month
206
+
207
+
208
+ # '''
209
+
210
+ def avg_subwatershed_loading(constituent,t_code,uci,hbn):
211
+ dfs = []
212
+ for t_opn in ['PERLND','IMPLND']:
213
+ t_cons = helpers.get_tcons(constituent,t_opn,'lb')
214
+ df = sum([hbn.get_multiple_timeseries(t_opn=t_opn,
215
+ t_con= t_con,
216
+ t_code = t_code) for t_con in t_cons])
217
+ if constituent == 'TSS':
218
+ df*2000
219
+
220
+ df = df.T.reset_index()
221
+ df.loc[:,'SVOL'] = t_opn
222
+ df = df.rename(columns = {'index':'OPNID'})
223
+ dfs.append(df)
224
+
225
+ df = pd.concat(dfs)
226
+ df.set_index(['SVOL','OPNID'],inplace=True)
227
+
228
+ subwatersheds = uci.network.subwatersheds()
229
+
230
+
231
+
232
+ loading_rates = []
233
+ for catchment_id in set(subwatersheds.index):
234
+ subwatershed = subwatersheds.loc[catchment_id].set_index(['SVOL','SVOLNO'])
235
+ loading_rates.append(df.loc[subwatershed.index].sum().agg(agg_func)/subwatershed['AFACTR'].sum())
236
+
237
+
238
+
239
+
240
+ def weighted_describe(df, value_col, weight_col):
241
+ weighted_mean = (df[value_col] * df[weight_col]).sum() / df[weight_col].sum()
242
+ weighted_var = ((df[value_col] - weighted_mean) ** 2 * df[weight_col]).sum() / df[weight_col].sum()
243
+ weighted_std = np.sqrt(weighted_var)
244
+
245
+
246
+ return pd.DataFrame({
247
+ 'area' : df[weight_col].sum(),
248
+ f'weighted_mean_{value_col}': [weighted_mean],
249
+ f'weighted_std_{value_col}': [weighted_std]})
250
+ # 'min': [df[value_col].min()],
251
+ # 'max': [df[value_col].max()]
252
+ # })
253
+
254
+
255
+ def monthly_avg_constituent_loading(constituent,uci,hbn):
256
+ dfs = []
257
+ for t_opn in ['PERLND','IMPLND']:
258
+ t_cons = helpers.get_tcons(constituent,t_opn,'lb')
259
+ df = sum([hbn.get_multiple_timeseries(t_opn=t_opn,
260
+ t_con= t_con,
261
+ t_code = 'monthly') for t_con in t_cons])
262
+ df = df.groupby(df.index.month).mean().T.reset_index()
263
+ if constituent == 'TSS':
264
+ df*2000
265
+
266
+ df.loc[:,'SVOL'] = t_opn
267
+ df = df.rename(columns = {'index':'OPNID'})
268
+ dfs.append(df)
269
+
270
+ df = pd.concat(dfs)
271
+
272
+
273
+ subwatersheds = uci.network.subwatersheds().reset_index()
274
+
275
+ df = pd.merge(subwatersheds,df,left_on = ['SVOL','SVOLNO'], right_on=['SVOL','OPNID'],how='left')
276
+ return df
277
+
278
+ def monthly_avg_subwatershed_loading(constituent,month,uci,hbn):
279
+ df = monthly_avg_constituent_loading(constituent,uci,hbn)
280
+ df = df.groupby(df['TVOLNO'])[[month,'AFACTR']].apply(lambda x: weighted_describe(x,month,'AFACTR')).droplevel(1)
281
+ return df
282
+
283
+ def monthly_avg_watershed_loading(constituent,reach_ids,month,uci,hbn, by_landcover = False):
284
+ df = monthly_avg_constituent_loading(constituent,uci,hbn)
285
+ df = df.loc[df['TVOLNO'].isin(reach_ids)]
286
+ if by_landcover:
287
+ df = df.groupby(df['LSID'])[[month,'AFACTR']].apply(lambda x: weighted_describe(x,month,'AFACTR')).droplevel(1)
288
+ else:
289
+
290
+ df = weighted_describe(df,month,'AFACTR')
291
+
292
+ return df
293
+
294
+
295
+ def ann_avg_constituent_loading(constituent,uci,hbn):
296
+
297
+ if constituent == 'TP':
298
+ df = total_phosphorous(uci,hbn,5).mean().reset_index()
299
+ df.loc[:,'OPN'] = 'PERLND'
300
+ df.columns = ['OPNID',constituent,'SVOL']
301
+
302
+ else:
303
+ dfs = []
304
+ for t_opn in ['PERLND','IMPLND']:
305
+ t_cons = helpers.get_tcons(constituent,t_opn)
306
+ df = sum([hbn.get_multiple_timeseries(t_opn=t_opn,
307
+ t_con= t_con,
308
+ t_code = 'yearly') for t_con in t_cons]).mean().reset_index()
309
+ df.loc[:,'OPN'] = t_opn
310
+ df.columns = ['OPNID',constituent,'SVOL']
311
+ dfs.append(df)
312
+
313
+ df = pd.concat(dfs)
314
+ if constituent == 'TSS':
315
+ df[constituent] = df[constituent]*2000
316
+
317
+ subwatersheds = uci.network.subwatersheds().reset_index()
318
+
319
+ df = pd.merge(subwatersheds,df,left_on = ['SVOL','SVOLNO'], right_on=['SVOL','OPNID'],how='left')
320
+ return df
321
+
322
+ def ann_avg_subwatershed_loading(constituent,uci,hbn):
323
+ df = ann_avg_constituent_loading(constituent,uci,hbn)
324
+ df = df.groupby(df['TVOLNO'])[[constituent,'AFACTR']].apply(lambda x: weighted_describe(x,constituent,'AFACTR')).droplevel(1)
325
+ return df
326
+
327
+ def ann_avg_watershed_loading(constituent,reach_ids,uci,hbn, by_landcover = False):
328
+ df = ann_avg_constituent_loading(constituent,uci,hbn)
329
+ df = df.loc[df['TVOLNO'].isin(reach_ids)]
330
+ if by_landcover:
331
+ df = df.groupby(df['LSID'])[[constituent,'AFACTR']].apply(lambda x: weighted_describe(x,constituent,'AFACTR')).droplevel(1)
332
+ else:
333
+
334
+ df = weighted_describe(df,constituent,'AFACTR')
335
+
336
+ return df
337
+
338
+
339
+
340
+ # ds = xr.
341
+ # coords = ['time']
342
+ # dims = ['operation','activity','opnid','time_step','time','catchment_id']
343
+ # def _insert_col(col_name,value,df):
344
+ # if col_name not in df.columns:
345
+ # df.insert(0,col_name,value)
346
+
347
+ # dfs = []
348
+ # for hbn in hbns.hbns:
349
+ # for key, df in hbn.data_frames.items():
350
+ # operation,activity,opnid,t_code = key.split('_')
351
+ # t_code = int(t_code)
352
+ # opnid = int(opnid)
353
+ # df = hbn.data_frames[key]
354
+ # df.index.name = 'date'
355
+ # df.index = df.index.tz_localize(None)
356
+ # _insert_col('t_code',t_code,df)
357
+ # _insert_col('OPNID',opnid,df)
358
+ # _insert_col('activity',activity,df)
359
+ # df = df.reset_index().set_index(['date','OPNID','t_code','activity'])
360
+ # dfs.append(xr.Dataset.from_dataframe(df))
361
+
362
+ # ds = xr.merge(dfs)
363
+
364
+ # query = {
365
+ # 'date': (142.41, 142.51),
366
+ # 'y': (-32.22, -32.32),
367
+ # 'time': ('2015-01-01', '2016-12-31'),
368
+ # 'measurements': ['nbart_nir', 'fmask'],
369
+ # 'output_crs': 'EPSG:3577',
370
+ # 'resolution': (-30, 30)
371
+ # }
372
+
373
+ # dfs = []
374
+ # for activity, ts_names in hbn.output_names().items():
375
+ # dfs
376
+
377
+ # for hbn in hbn.hbns: data_frames['PERLND_SEDMNT_201_5']
378
+
379
+
380
+ # time_steps = [2,3,4,5]
381
+ # operations = ['PERLND','IMPLND','RCHRES']
382
+
383
+
384
+
385
+ # # def flow_loading(uci,hbn,reach_ids,time_step='yearly',weighted = True):
386
+
387
+ # t_con = 'PERO'
388
+ # t_opn = 'PERLND'
389
+ # time_step = 'yearly'
390
+ # activity = 'PWATER'
391
+
392
+
393
+
394
+ #def total_phosphorous_loading:
395
+ # def phosphorous_loading(uci,hbns,reach_ids,time_tep = 'yearly'):
396
+ # catchments = get_catchments(uci,reach_ids)
397
+ # df = total_phosphorous(uci,hbns)
398
+
399
+ # subwatershed = uci.network.subwatershed(reach_id)
400
+ # perlnds = subwatershed.loc[subwatershed['SVOL'] == 'PERLND']
401
+ # perlnds = perlnds.set_index('SVOLNO').drop_duplicates()
402
+ # mlno = subwatershed.loc[subwatershed['SVOL'] == 'PERLND','MLNO'].iloc[0]
403
+ # total = total_phosphorous(uci,hbn,mlno,t_code,perlnds.index)
404
+
405
+
406
+
407
+
408
+ #%% Landscape Yields
409
+
410
+ def yield_flow(uci,hbn,constituent,reach_id):
411
+ hbn.get_rchres_data('Q',reach_id,'cfs','yearly')/uci.network.drainage_area(reach_id)
412
+
413
+
414
+ def yield_sediment(uci,hbn,constituent,reach_id):
415
+ hbn.get_rchres_data('TSS',reach_id,'lb','yearly').mean()*2000/uci.network.drainage_area(reach_id)
416
+
417
+ def avg_ann_yield(uci,hbn,constituent,reach_ids):
418
+ #reach_ids = uci.network.G.nodes
419
+
420
+
421
+ _reach_ids = [uci.network._upstream(reach) for reach in reach_ids]
422
+ _reach_ids = list(set([num for row in _reach_ids for num in row]))
423
+ subwatersheds = uci.network.subwatersheds().loc[_reach_ids]
424
+ area = subwatersheds['AFACTR'].sum()
425
+
426
+ if constituent == 'Q':
427
+ units = 'acrft'
428
+ else:
429
+ units = 'lb'
430
+
431
+ df = hbn.get_reach_constituent(constituent,reach_ids,5,unit =units).mean() # Gross
432
+
433
+ return df/area
434
+
435
+
436
+ #%% Allocations
437
+ allocation_selector = {'Q': {'input': ['IVOL'],
438
+ 'output': ['ROVOL']},
439
+ 'TP': {'input': ['PTOTIN'],
440
+ 'output': ['PTOTOUT']},
441
+ 'TSS': {'input': ['ISEDTOT'],
442
+ 'output': ['ROSEDTOT']},
443
+ 'OP': {'input': ['PO4INDIS'],
444
+ 'output': ['PO4OUTDIS']},
445
+ 'N': {'input': ['NO3INTOT','NO2INTOT'],
446
+ 'output': ['NO2OUTTOT','NO3OUTTOT']},
447
+ 'TKN': {'input': [],
448
+ 'output': ['TAMOUTTOT', 'NTOTORGOUT']}
449
+ }
450
+
451
+ def fate(hbn,constituent,t_code,reach_ids = None):
452
+ if constituent == 'Q':
453
+ fate_in = hbn.get_multiple_timeseries('RCHRES',t_code,'ROVOL',opnids=reach_ids)
454
+ fate_out = hbn.get_multiple_timeseries('RCHRES',t_code,'IVOL',opnids=reach_ids)
455
+ elif constituent == 'TP':
456
+ fate_in = hbn.get_multiple_timeseries('RCHRES',t_code,'PTOTOUT',opnids = reach_ids)
457
+ fate_out = hbn.get_multiple_timeseries('RCHRES',t_code,'PTOTIN',opnids = reach_ids)
458
+ elif constituent == 'TSS':
459
+ fate_in = hbn.get_multiple_timeseries('RCHRES',t_code,'ISEDTOT',opnids = reach_ids)
460
+ fate_out = hbn.get_multiple_timeseries('RCHRES',t_code,'ROSEDTOT',opnids = reach_ids)
461
+ return fate_out/fate_in
462
+
463
+ def loading(uci,hbn,constituent,t_code = 5):
464
+ if constituent =='TP':
465
+ loads = total_phosphorous(uci,hbn,t_code=t_code)
466
+ else:
467
+ #dfs = []
468
+ # df_implnd = hbn.get_implnd_constituent(constituent,t_code,'lb').T.reset_index().rename(columns = {'index':'OPNID'})
469
+ # df_implnd['SVOL'] = 'IMPLND'
470
+
471
+ loads = hbn.get_perlnd_constituent(constituent,t_code,'lb')
472
+
473
+
474
+ # .T.reset_index().rename(columns = {'index':'OPNID'})
475
+ # df_perlnd['SVOL'] = 'PERLND'
476
+
477
+ # df = pd.concat([df_perlnd,df_implnd])
478
+ # df.set_index(['SVOL','OPNID'],inplace=True)
479
+
480
+ if constituent == 'TSS':
481
+ loads = loads*2000
482
+
483
+ return loads
484
+
485
+ def subwatershed_loading(uci,hbn,constituent,t_code,group_landcover = True,as_load = True):
486
+ loads = loading(uci,hbn,constituent,t_code)
487
+
488
+ subwatersheds = uci.network.subwatersheds()
489
+ perlnds = subwatersheds.loc[subwatersheds['SVOL'] == 'PERLND'].reset_index()
490
+
491
+ total = loads[perlnds['SVOLNO'].to_list()]
492
+ total = total.mul(perlnds['AFACTR'].values,axis=1)
493
+ total = total.transpose()
494
+ total['reach_id'] = perlnds['TVOLNO'].values
495
+ total['landcover'] = uci.table('PERLND','GEN-INFO').loc[total.index,'LSID'].to_list()
496
+ total['area'] = perlnds['AFACTR'].to_list() #perlnds.loc[total.index,'AFACTR'].to_list()
497
+ total = total.reset_index().set_index(['index','landcover','area','reach_id']).transpose()
498
+ total.columns.names = ['perlnd_id','landcover','area','reach_id']
499
+
500
+ if group_landcover:
501
+ total.columns = total.columns.droplevel(['landcover','perlnd_id'])
502
+ total = total.T.reset_index().groupby('reach_id').sum().reset_index().set_index(['reach_id','area']).T
503
+
504
+ if not as_load:
505
+ total = total.div(total.columns.get_level_values('area').values,axis=1)
506
+
507
+ total.index = pd.to_datetime(total.index)
508
+ return total
509
+
510
+
511
+ def losses(uci,hbn,constituent, t_code = 5):
512
+ upstream_reachs = {reach_id: uci.network.upstream(reach_id) for reach_id in uci.network.get_node_type_ids('RCHRES')}
513
+ totout = sum([hbn.get_multiple_timeseries('RCHRES',
514
+ t_code,
515
+ t_cons,
516
+ opnids = list(upstream_reachs.keys()))
517
+ for t_cons in allocation_selector[constituent]['output']])
518
+
519
+ totin = sum([hbn.get_multiple_timeseries('RCHRES',
520
+ t_code,
521
+ t_cons,
522
+ opnids = list(upstream_reachs.keys()))
523
+ for t_cons in allocation_selector[constituent]['input']])
524
+
525
+
526
+ #totin = totout.copy().astype('Float64')
527
+ #totin[:] = pd.NA
528
+
529
+ for reach_id in totin.columns:
530
+ reach_ids = upstream_reachs[reach_id]
531
+ if len(reach_ids) > 0:
532
+ totin[reach_id] = totout[reach_ids].sum(axis=1)
533
+
534
+ #totin.columns = totout.columns
535
+ return (totout-totin)/totin*100
536
+
537
+ def allocations(uci,hbn,constituent,reach_id,t_code,group_landcover = True):
538
+ p = uci.network.paths(reach_id)
539
+ p[reach_id] = [reach_id]
540
+ loss = losses(uci,hbn,constituent,t_code)
541
+ loads = subwatershed_loading(uci,hbn,constituent,t_code,group_landcover = group_landcover)
542
+ loss_factors = pd.concat([loss[v].prod(axis=1) for k,v in p.items()],axis=1)
543
+ loss_factors.columns = list(p.keys())
544
+ allocations = loads.mul(loss_factors[loads.columns.get_level_values('reach_id')].values)
545
+ return allocations
546
+
547
+
548
+ def total_phosphorous_losses(uci,hbn,t_code = 5):
549
+ upstream_reachs = {reach_id: [reach_id] + uci.network.upstream(reach_id) for reach_id in uci.network.get_node_type_ids('RCHRES')}
550
+ ptotout = hbn.get_multiple_timeseries('RCHRES',t_code,'PTOTOUT',opnids = list(upstream_reachs.keys()))
551
+ ptotin = pd.concat([ptotout[reach_ids].sum(axis=1) for reach_id,reach_ids in upstream_reachs.items()],axis=1)
552
+ ptotin.columns = list(upstream_reachs.keys())
553
+ return 1-(ptotin-ptotout)/ptotin
554
+
555
+
556
+ def total_phosphorous_allocations(uci,hbn,reach_id,t_code=5,group_landcover = True):
557
+ p = uci.network.paths(reach_id)
558
+ p[reach_id] = [reach_id]
559
+ losses = total_phosphorous_losses(uci,hbn,t_code)
560
+ loads = subwatershed_total_phosphorous_loading(uci,hbn,t_code=t_code,group_landcover = group_landcover)
561
+ loss_factors = pd.concat([losses[v].prod(axis=1) for k,v in p.items()],axis=1)
562
+ loss_factors.columns = list(p.keys())
563
+ allocations = loads.mul(loss_factors[loads.columns.get_level_values('reach_id')].values)
564
+ return allocations
565
+
566
+ #loads[loads.index.get_level_values('reach_id').isin(loss_factors.columns)].mul(loss_factors.values,axis=1)
567
+ #return loads[loss_factors.columns].mul(loss_factors.values,axis=1)
568
+
569
+
570
+ def flow_allocations(uci,hbn,reach_id,t_code = 5):
571
+ raise NotImplementedError()
572
+
573
+ def total_suspended_sediment_allocations(uci,hbn,reach_id,t_code):
574
+ raise NotImplementedError()
575
+
576
+ #%% Water Balance
577
+ def pevt_balance(mod,operation,opnid):
578
+ extsources = mod.uci.table('EXT SOURCES')
579
+
580
+ pevt_dsn = mod.uci.get_dsns(operation,opnid,'PEVT').reset_index()
581
+ pevt_mfactor = extsources.loc[(extsources['TOPFST'] == opnid) &
582
+ (extsources['TVOL'] == operation) &
583
+ (extsources['SMEMN'] == 'PEVT'),'MFACTOR'].iat[0]
584
+ pevt = mod.wdms.series(pevt_dsn.loc[0,'FILENAME'],pevt_dsn.loc[0,'SVOLNO'])
585
+
586
+ prec_dsn = mod.uci.get_dsns(operation,opnid,'PREC').reset_index()
587
+ prec_mfactor = extsources.loc[(extsources['TOPFST'] == opnid) &
588
+ (extsources['TVOL'] == operation) &
589
+ (extsources['SMEMN'] == 'PREC'),'MFACTOR'].iat[0]
590
+ prec = mod.wdms.series(prec_dsn.loc[0,'FILENAME'],prec_dsn.loc[0,'SVOLNO'])
591
+
592
+ df = pd.concat([(prec*prec_mfactor).resample('Y').sum(),
593
+ (pevt*pevt_mfactor).resample('Y').sum()],axis=1)
594
+ df = df[df>0]
595
+ df.columns = ['PREC','PEVT']
596
+ return df
597
+
598
+
599
+ # #simulate ET from perlnds
600
+ # taet = hbn.get_multiple_timeseries(t_opn='PERLND',
601
+ # t_con='TAET',
602
+ # t_code = 'monthly',
603
+ # activity = 'PWATER')
604
+
605
+ # taet = taet.groupby(taet.index.month).mean()
606
+
607
+ # precip = hbn.get_multiple_timeseries(t_opn='PERLND',
608
+ # t_con='SUPY',
609
+ # t_code = 'monthly',
610
+ # activity = 'PWATER')
611
+
612
+ # precip = precip.groupby(precip.index.month).mean()
613
+
614
+
615
+
616
+ # df = (precip-taet).T.join(uci.opnid_dict['PERLND'])
617
+
618
+
619
+
620
+ # precip = cal.model.wdms.series('Nemadji_Met.wdm',1100)
621
+ # precip = precip.loc[precip >=0]
622
+ # pevt = cal.model.wdms.series('Nemadji_Met.wdm',711)
623
+ # pevt = pevt.loc[pevt >=0]
624
+
625
+ # taet['Operation'] = 'PERLND' # without specifying the opnid, it grabs them all.
626
+ # taet = taet.join(uci.network.operation_area('PERLND'))
627
+ # taet['PET'] = taet['EVAP']*taet['AFACTR']/12
628
+ # taet = taet.reset_index().rename(columns = {'index' : 'OPNID'})[['OPNID','Operation','PET']]
629
+
630
+
631
+ def simulated_et(uci,hbn):
632
+
633
+
634
+ #simulate ET from perlnds
635
+ taet = hbn.get_multiple_timeseries(t_opn='PERLND',
636
+ t_con='TAET',
637
+ t_code = 'yearly',
638
+ activity = 'PWATER').mean().to_frame().rename(columns = {0:'EVAP'})
639
+ taet['Operation'] = 'PERLND' # without specifying the opnid, it grabs them all.
640
+ taet = taet.join(uci.network.operation_area('PERLND'))
641
+ taet['PET'] = taet['EVAP']*taet['AFACTR']/12
642
+ taet = taet.reset_index().rename(columns = {'index' : 'OPNID'})[['OPNID','Operation','PET']]
643
+
644
+ #simulate ET from implnds
645
+ impev = hbn.get_multiple_timeseries(t_opn='IMPLND',
646
+ t_con='IMPEV',
647
+ t_code = 'yearly').mean().to_frame().rename(columns = {0:'EVAP'})
648
+ impev['Operation'] = 'IMPLND' # without specifying the opnid, it grabs them all.
649
+ impev = impev.join(uci.network.operation_area('IMPLND'))
650
+ impev['PET'] = impev['EVAP']*impev['AFACTR']/12
651
+ impev = impev.reset_index().rename(columns = {'index' : 'OPNID'})[['OPNID','Operation','PET']]
652
+
653
+ # sum of agwo for each perlnd
654
+ volev = hbn.get_multiple_timeseries(t_opn='RCHRES', t_con='VOLEV', t_code = 'yearly').mean().to_frame().rename(columns = {0:'PET'})
655
+ volev['Operation'] = 'RCHRES'
656
+ volev = volev.reset_index().rename(columns = {'index' : 'OPNID'})
657
+
658
+ return pd.concat([taet,impev,volev])
659
+
660
+
661
+
662
+ def inflows(uci,wdm):
663
+ # External inflows
664
+ files = uci.table('FILES')
665
+ ext_sources = uci.table('EXT SOURCES')
666
+
667
+ ext_sources = ext_sources.loc[(ext_sources['TVOL'].isin(['PERLND','IMPLND','RCHRES']))]
668
+ ext_sources = ext_sources.merge(files, left_on = 'SVOL',
669
+ right_on= 'FTYPE',
670
+ how = 'left')
671
+
672
+ ext_sources = ext_sources[ext_sources['SMEMN'].isin(['ROVL','Flow'])]
673
+
674
+ if len(ext_sources) == 0:
675
+ inflows = pd.DataFrame(columns = ['OPNID','Operation','ROVL'])
676
+ else:
677
+
678
+ dsns = ext_sources[['SVOLNO','FILENAME']].drop_duplicates().reset_index(drop=True)
679
+ dfs = [wdm.wdms[row['FILENAME']].series(row['SVOLNO']) for index,row in dsns.iterrows()]
680
+ dsns['ROVL'] = pd.concat(dfs,axis=1).resample('Y').sum().mean()
681
+
682
+
683
+ inflows = ext_sources.merge(dsns,left_on='SVOLNO',
684
+ right_on = 'SVOLNO')[['TOPFST','TVOL','ROVL']].rename(columns = {'TOPFST':'OPNID','TVOL':'Operation'})
685
+ return inflows
686
+
687
+ def water_balance(uci,hbn,wdm,reach_ids):
688
+
689
+ areas = []
690
+ for operation in ['PERLND','IMPLND','RCHRES']:
691
+ area = uci.network.operation_area(operation).reset_index()
692
+ area.loc[:,'Operation'] = operation
693
+ areas.append(area)
694
+ areas = pd.concat(areas).set_index(['Operation','SVOLNO'])
695
+ areas.index.names = ['Operation','OPNID']
696
+
697
+ #areas = pd.concat([uci.network.operation_area(operation) for operation in ['PERLND','IMPLND','RCHRES']])
698
+
699
+ pets = simulated_et(uci,hbn)
700
+ _inflows = inflows(uci,wdm)
701
+ precips = avg_annual_precip(uci,wdm)
702
+ precips = precips.set_index(['Operation','OPNID']).join(areas)
703
+ precips['PREC'] = precips['avg_ann_prec'] / 12 * precips['AFACTR']
704
+ precips.reset_index(inplace=True)
705
+ #outlets = uci.network.outlets()
706
+ rovols = hbn.get_multiple_timeseries(opnids = reach_ids,t_opn='RCHRES', t_con='ROVOL', t_code = 'yearly').mean().to_frame()
707
+ #igwi = hbn.get_multiple_timeseries(t_opn='PERLND', t_con='IGWI', t_code = 'yearly').mean().to_frame()
708
+ #igwi = igwi.join(areas['PERLND'])
709
+ #igwi = igwi[0]/ 12 * igwi['AFACTR']
710
+
711
+ rows = []
712
+ for outlet in reach_ids:
713
+ precip = 0
714
+ inflow = 0
715
+ pet = 0
716
+ for operation in ['PERLND','IMPLND','RCHRES']:
717
+ opnids = uci.network.get_opnids(operation,outlet)
718
+ precip = precip + precips.loc[(precips['Operation'] == operation) & (precips['OPNID'].isin(opnids))]['PREC'].sum()
719
+ inflow = inflow + _inflows.loc[(_inflows['Operation'] == operation) & (_inflows['OPNID'].isin(opnids))]['ROVL'].sum()
720
+ pet = pet + pets.loc[(pets['Operation'] == operation) & (pets['OPNID'].isin(opnids))]['PET'].sum()
721
+ rovol = rovols.loc[outlet].sum()
722
+ balance = ((precip-pet)-(rovol - inflow))/(precip-pet)*100
723
+
724
+ rows.append([outlet,precip,inflow,pet,rovol,balance])
725
+ return pd.DataFrame(rows,columns = ['reach_id','precip','inflow','saet','rovol','balance'])
726
+
727
+
728
+ def meteorlogical(uci,wdm,operation,ts_name,time_step = 'Y',opnids = None,):
729
+ files = uci.table('FILES')
730
+ files['FTYPE'].replace('WDM','WDM1',inplace=True)
731
+
732
+ # Total preciptiation
733
+ ext_sources = uci.table('EXT SOURCES')
734
+ ext_sources['SVOL'].replace('WDM','WDM1',inplace=True)
735
+ ext_sources = ext_sources.loc[(ext_sources['TVOL'] == operation) & (ext_sources['SMEMN'] == ts_name)]
736
+ ext_sources = ext_sources.merge(files, left_on = 'SVOL',
737
+ right_on= 'FTYPE',
738
+ how = 'left')
739
+
740
+ dsns = ext_sources[['SVOLNO','FILENAME']].drop_duplicates().reset_index(drop=True)
741
+ dfs = [wdm.wdms[row['FILENAME']].series(row['SVOLNO']) for index,row in dsns.iterrows()]
742
+ dfs = [df.loc[df>=0] for df in dfs]
743
+ df = pd.concat(dfs,axis=1).resample(time_step).sum()
744
+ df.columns = dsns['SVOLNO']
745
+
746
+ df = df[ext_sources['SVOLNO']]
747
+ df.columns = ext_sources['TOPFST']
748
+
749
+ if opnids is not None:
750
+ df = df[opnids]
751
+
752
+ return df
753
+
754
+
755
+ def avg_annual_precip(uci,wdm):
756
+ #assert(var in ['PREC','WIND','PEVT','ATEM','DEWP','CLOU','SOLAR',])
757
+ # average annual precipitation across for each PERLND, IMPLND, and Reach
758
+
759
+
760
+
761
+ files = uci.table('FILES')
762
+ files['FTYPE'].replace('WDM','WDM1',inplace=True)
763
+
764
+ # Total preciptiation
765
+ ext_sources = uci.table('EXT SOURCES')
766
+ ext_sources['SVOL'].replace('WDM','WDM1',inplace=True)
767
+ ext_sources = ext_sources.loc[(ext_sources['TVOL'].isin(['PERLND','IMPLND','RCHRES'])) & (ext_sources['SMEMN'] == 'PREC')]
768
+ ext_sources = ext_sources.merge(files, left_on = 'SVOL',
769
+ right_on= 'FTYPE',
770
+ how = 'left')
771
+
772
+ dsns = ext_sources[['SVOLNO','FILENAME']].drop_duplicates().reset_index(drop=True)
773
+ dfs = [wdm.wdms[row['FILENAME']].series(row['SVOLNO']) for index,row in dsns.iterrows()]
774
+ dfs = [df.loc[df>=0] for df in dfs]
775
+ df = pd.concat(dfs,axis=1).resample('Y').sum().mean()
776
+
777
+ dsns['avg_ann_prec'] = pd.concat(dfs,axis=1).resample('Y').sum().mean()
778
+ df = ext_sources.merge(dsns,left_on = 'SVOLNO',
779
+ right_on = 'SVOLNO',
780
+ how = 'left')
781
+ df = df[['SVOLNO','TVOL','TOPFST','avg_ann_prec']]
782
+ df.rename(columns = {'SVOLNO':'DSN','TVOL':'Operation','TOPFST':'OPNID'}, inplace=True)
783
+
784
+ return df
785
+
786
+
787
+
788
+
789
+
790
+
791
+ #%%
792
+ #%%% Report Tablewater_s
793
+
794
+
795
+ def landcover_areas(uci):
796
+ df = uci.network.operation_area('PERLND').groupby('LSID').sum()
797
+ df['percent'] = 100*(df['AFACTR']/df['AFACTR'].sum())
798
+ return df
799
+
800
+ # def area_weighted_output(uci,hbn,ts_name,operation,time_step,opnids):
801
+ # assert(operation in ['PERLND','IMPLND'])
802
+ # df = hbn.get_multiple_timeseries(operation,5,ts_name,opnids = opnids).T
803
+ # df.index.name = 'SVOLNO'
804
+ # areas = uci.network.operation_area(operation)
805
+ # df = df.join(areas).reset_index().set_index(['SVOLNO','AFACTR','LSID'])
806
+ # df = df.T*df.index.get_level_values('AFACTR').values
807
+
808
+ # if grouped:
809
+ # df.columns.get_level_values('AFACTR').groupby(df.get_level_values['LSIDE'])
810
+
811
+ '''
812
+ Output for each PERLND
813
+ - Sometimes a rate
814
+ - Sometimes a mass or volume
815
+
816
+ - Group by Landcover no without weighting
817
+ - rate take the mean
818
+ - mass or volum us the sum
819
+ - Group by Landcover with weighting
820
+ - rate convert to mass/volume sum then divide by grouped area
821
+ - mass sum then divide by grouped area
822
+
823
+
824
+ Output for a catchment
825
+ - For a single catchment
826
+ - if timeseries is a rate
827
+ - rate is raw output
828
+ - mass/volume is rate * area of contributing operations
829
+ - if timeseries is a mass/volume
830
+ - rate is mass/volume / area of contributing operations
831
+ - mass/volume is raw output
832
+ - No ability to aggregate by Landcover
833
+ - For 2 or more catchments
834
+ - if weighted
835
+ - if timeseries is a rate
836
+ - rate is rate*area of contributing operations summed by landcover and divided by each landcover area
837
+ - mass/volume is rate*area summed by landcover and area
838
+ - if timeseries is a mass/volume
839
+ - rate is mass/volume summed by landcover and divided by landcover area
840
+ - mass/volume is mass/volume summed by landcover
841
+ - if not weighted
842
+ - if timeseries is a rate
843
+ - rate is the raw output of each catchment concatenated
844
+ - mass/volume is rate*area of each contributing landcover and concatenated for each catchment
845
+ - if timeseries is a mass/volume
846
+ - rate is mass/volume / area of each contributing landcover and concatenated for each catchment
847
+ - mass/volume is raw output of each chatchment concatenated
848
+
849
+
850
+ '''
851
+
852
+ # class Catchment:
853
+ # def __init__(reach_id,uci,hbn = None):
854
+ # id = reach_id
855
+
856
+ # def loading_rate(constituent):
857
+
858
+ # def loading(constituent):
859
+
860
+ # def yield(constituent):
861
+
862
+ # def load(constituent):
863
+
864
+
865
+
866
+
867
+ '''
868
+ The area of each landcategory in the catchment
869
+
870
+ Loading rate of each landuse (lb/acre/intvl)
871
+ TSS, TP, N, TKN, BOD, OP
872
+
873
+ Loading of from each landuse (lb/intvl)
874
+ TSS, TP, N, TKN, BOD, OP
875
+
876
+ Yield at the catchment outlet (lb/acr/intvl)
877
+ TSS, TP, N, TKN, BOD, OP
878
+
879
+ Load at the catchment outlet (lb/intvl)
880
+ TSS, TP, N, TKN, BOD, OP
881
+
882
+ In channel losses of a constituent (lb/intvl)
883
+ TSS, TP, N, TKN, BOD, OP
884
+
885
+ Allocation of a constituent from catchment to downstream catchment
886
+ TSS, TP, N, TKN, BOD, OP
887
+
888
+
889
+
890
+
891
+ '''
892
+
893
+ #reach_id = 103
894
+ #def make_catchment(reach_id,uci,hbn):
895
+
896
+
897
+
898
+
899
+ # class Reach:
900
+
901
+
902
+ # class Perlnd():
903
+ # def __init__(catchment_id,perlnd_id,area,mlno,landcover,metzone):
904
+
905
+
906
+
907
+ # # class Implnd:
908
+ # def annual_weighted_perlnd_output(uci,hbn,ts_name,t_code = 4,opnids = None):
909
+
910
+ # df = hbn.get_multiple_timeseries('PERLND',5,ts_name,opnids = opnids)
911
+ # subwatersheds = uci.network.subwatersheds().reset_index()
912
+ # subwatersheds = subwatersheds.loc[subwatersheds['SVOL'] == 'PERLND']
913
+ # df = df[subwatersheds['SVOLNO']].T
914
+ # df = pd.merge(df, subwatersheds, left_index = True, right_on='SVOLNO', how='inner')
915
+ # df = df.set_index(['TVOLNO','SVOL','SVOLNO','AFACTR','LSID','MLNO']).T
916
+
917
+ # def annual_weighted_output(ts_name,operation,opnids):
918
+ # assert(operation in ['PERLND','IMPLND'])
919
+ # subwatersheds = uci.network.subwatersheds()
920
+ # subwatersheds = subwatersheds.loc[subwatersheds['SVOL'] == operation].reset_index()
921
+ # df = cal.model.hbns.get_multiple_timeseries('PERLND',5,'PERO',test['SVOLNO'].values).mean().reset_index()
922
+ # df.columns = ['OPNID','value']
923
+ # df = pd.merge(subwatersheds,df,left_on = 'SVOLNO', right_on='OPNID')
924
+ # weighted_mean = df.groupby('TVOLNO').apply(lambda x: (x['value'] * x['AFACTR']).sum() / x['AFACTR'].sum())
925
+
926
+
927
+ def weighted_mean(df,value_col,weight_col):
928
+ weighted_mean = (df[value_col] * df[weight_col]).sum() / df[weight_col].sum()
929
+ return pd.DataFrame({
930
+ 'AFACTR' : df[weight_col].sum(),
931
+ value_col: [weighted_mean]})
932
+
933
+ def annual_weighted_output(uci,hbn,ts_name,operation = 'PERLND',opnids = None,group_by = None):
934
+ assert (group_by in [None,'landcover','opnid'])
935
+ df = hbn.get_multiple_timeseries(operation,5,ts_name,opnids = opnids).mean().reset_index()
936
+ df.columns = ['SVOLNO',ts_name]
937
+ subwatersheds = uci.network.subwatersheds().reset_index()
938
+ subwatersheds = subwatersheds.loc[subwatersheds['SVOL'] == operation]
939
+
940
+
941
+ df = pd.merge(subwatersheds,df,left_on = 'SVOLNO', right_on='SVOLNO',how='left')
942
+
943
+
944
+ if group_by is None:
945
+ df = weighted_mean(df,ts_name,'AFACTR')
946
+ elif group_by == 'landcover':
947
+ df = df.groupby('LSID')[[ts_name,'AFACTR']].apply(lambda x: weighted_mean(x,ts_name,'AFACTR')).droplevel(1)
948
+ elif group_by == 'opnid':
949
+ df = df.groupby(df['SVOLNO'])[[ts_name,'AFACTR']].apply(lambda x: weighted_mean(x,ts_name,'AFACTR')).droplevel(1)
950
+
951
+ df = df.set_index([df.index,'AFACTR'])
952
+ return df
953
+
954
+
955
+
956
+ def monthly_weighted_output(uci,hbn,ts_name,operation = 'PERLND',opnids = None, as_rate = False, by_landcover = True, months = [1,2,3,4,5,6,7,8,9,10,11,12]):
957
+ df = hbn.get_multiple_timeseries(operation,4,ts_name,opnids = opnids)
958
+ df = df.loc[df.index.month.isin(months)]
959
+
960
+ areas = uci.network.operation_area(operation)
961
+ areas.loc[areas.index.intersection(df.columns)]
962
+ df = df[areas.index.intersection(df.columns)]
963
+
964
+ df = (df.groupby(df.index.month).mean()*areas['AFACTR'])
965
+
966
+ if by_landcover:
967
+ df = df.T.groupby(areas['LSID']).sum().T
968
+ if as_rate:
969
+ df = df/areas['AFACTR'].groupby(areas['LSID']).sum().to_list()
970
+ else:
971
+ if as_rate:
972
+ df = df/areas['AFACTR'].sum()
973
+
974
+ df.columns.name = ts_name
975
+
976
+ return df
977
+
978
+ def monthly_perlnd_runoff(uci,hbn):
979
+ ts_names = ['PRECIP','PERO','AGWO','IFWO','SURO']
980
+ df = pd.concat({ts_name:monthly_weighted_output(uci,hbn,ts_name,by_landcover=True,as_rate=True) for ts_name in ts_names},keys =ts_names)
981
+ suro_perc = (df.loc['SURO']/df.loc['PERO'])*100
982
+ suro_perc = suro_perc.reset_index()
983
+ suro_perc['name'] = 'SURO_perc'
984
+ suro_perc = suro_perc.set_index(['name','index'])
985
+ return pd.concat([df,suro_perc])
986
+
987
+
988
+ def annual_perlnd_runoff(uci,hbn):
989
+ ts_names = ['PRECIP','PERO','AGWO','IFWO','SURO']
990
+ df = pd.concat([annual_weighted_output(uci,hbn,ts_name,group_by='landcover') for ts_name in ts_names],axis = 1)
991
+ df.columns = ts_names
992
+ df['suro_perc'] = (df['SURO']/df['PERO'])*100
993
+ return df
994
+
995
+
996
+ def annual_reach_water_budget(uci,hbn):
997
+ ts_names = ['PRSUPY','IVOL','ROVOL','VOLEV']
998
+ #df = pd.concat([annual_weighted_output(uci,hbn,ts_name,as_rate = True,by_landcover=True).mean() for ts_name in ts_names],axis = 1)
999
+ df = pd.concat([hbn.get_multiple_timeseries('RCHRES',5,ts_name).mean() for ts_name in ts_names],axis=1)
1000
+ df.columns = ts_names
1001
+ # df = pd.concat([hbn.get_multiple_timeseries('RCHRES',5,'PRSUPY').mean(), #Inflow from Precipitation
1002
+ # hbn.get_multiple_timeseries('RCHRES',5,'IVOL').mean(), #Total Inflow
1003
+ # hbn.get_multiple_timeseries('RCHRES',5,'ROVOL').mean(), #Total Outflow
1004
+ # hbn.get_multiple_timeseries('RCHRES',5,'VOLEV').mean()],axis=1) #Loss from Evaporation
1005
+ geninfo = uci.table('RCHRES','GEN-INFO')[['LKFG']]
1006
+ reach_intersection = geninfo.index.intersection(df.index)
1007
+
1008
+
1009
+ df = geninfo.loc[reach_intersection].join(df.loc[reach_intersection])
1010
+ #df.columns = ['LKFG','PRSUPY','IVOL','ROVOL','VOLEV']
1011
+
1012
+
1013
+ df['ROVOL_Input'] = 0.
1014
+
1015
+ for reach_id in df.index:
1016
+ if reach_id in uci.network.G.nodes:
1017
+ upstream_ids = uci.network.upstream(reach_id)
1018
+ if len(upstream_ids) > 0:
1019
+ df.loc[reach_id,'ROVOL_Input'] = df.loc[upstream_ids]['ROVOL'].sum()
1020
+
1021
+ df.index.name = 'OPNID'
1022
+ return df.reset_index()
1023
+
1024
+
1025
+ def annual_implnd_water_budget(uci,hbn):
1026
+ ts_names = ['SUPY','SURO','IMPEV']
1027
+ #df = pd.concat([annual_weighted_output(uci,hbn,ts_name,as_rate = True,by_landcover=True).mean() for ts_name in ts_names],axis = 1)
1028
+ df = pd.concat([hbn.get_multiple_timeseries('IMPLND',5,ts_name).mean() for ts_name in ts_names],axis=1)
1029
+ df.columns = ts_names
1030
+ return df
1031
+
1032
+ def annual_perlnd_water_budget(uci,hbn):
1033
+ ts_names = ['PRECIP','TAET','PERO']
1034
+ df = pd.concat([annual_weighted_output(uci,hbn,ts_name,group_by='landcover') for ts_name in ts_names],axis = 1)
1035
+ df.columns = ts_names
1036
+ return df
1037
+
1038
+ def annual_sediment_budget(uci,hbn):
1039
+ ts_names = ['SOSED']
1040
+ df = pd.concat([annual_weighted_output(uci,hbn,ts_name,'PERLND', group_by='landcover') for ts_name in ts_names],axis = 1)
1041
+ #df_rate = pd.concat([annual_weighted_output(uci,hbn,ts_name,'PERLND',as_rate = True, by_landcover=True).mean() for ts_name in ts_names],axis = 1)
1042
+
1043
+ ts_names = ['SOSLD']
1044
+ sosld = pd.concat([annual_weighted_output(uci,hbn,ts_name,'IMPLND',group_by='landcover') for ts_name in ts_names],axis = 1)
1045
+ sosld.columns = ['SOSED']
1046
+
1047
+ df = pd.concat([df,sosld])
1048
+ #df_rate = annual_weighted_output(uci,hbn,ts_name,'PERLND',as_rate = True, by_landcover=True).mean()
1049
+
1050
+ df['Percentage'] = 100*(df['SOSED']*df.index.get_level_values('AFACTR')/sum(df['SOSED']*df.index.get_level_values('AFACTR')))
1051
+
1052
+ df.columns = ['Sediment','Percentage']
1053
+ return df
1054
+
1055
+ # def annual_loading_rate():
1056
+
1057
+
1058
+ # def annual_yield(uci,hbn,constituent):
1059
+
1060
+
1061
+ def subwatershed_weighted_output(uci,hbn,reach_ids,ts_name,time_step,by_landcover=False,as_rate = True):
1062
+ subwatersheds = uci.network.subwatersheds(reach_ids)
1063
+ subwatersheds = subwatersheds.loc[subwatersheds['SVOL'] == 'PERLND']
1064
+
1065
+ areas = subwatersheds[['SVOLNO','AFACTR']].set_index('SVOLNO')
1066
+ areas = areas.join( uci.table('PERLND','GEN-INFO')['LSID'])
1067
+ opnids = subwatersheds['SVOLNO'].to_list()
1068
+
1069
+ df = hbn.get_multiple_timeseries('PERLND',time_step,ts_name,opnids = opnids)
1070
+
1071
+ areas.loc[areas.index.intersection(df.columns)]
1072
+ df = df[areas.index.intersection(df.columns)]
1073
+
1074
+ if by_landcover:
1075
+ df = (df*areas['AFACTR'].values).T.groupby(areas['LSID']).sum()
1076
+ if as_rate:
1077
+ df = df.T/areas['AFACTR'].groupby(areas['LSID']).sum().to_list()
1078
+ df.columns.name = ts_name
1079
+ else:
1080
+ df = (df * areas['AFACTR'].values).sum(axis=1)
1081
+ if as_rate:
1082
+ df = df/areas['AFACTR'].sum()
1083
+ df.name = ts_name
1084
+
1085
+ return df
1086
+
1087
+
1088
+
1089
+
1090
+
1091
+ # def perlnd_water_budget(uci,hbn,time_step = 5):
1092
+
1093
+ # ts_names = ['SUPY','SURO','IFWO','AGWO','PERO','AGWI','IGWI','PET','UZET','LZET','AGWET','BASET','TAET']
1094
+ # dfs = [area_weighted_output(uci,hbn,ts_name,time_step) for ts_name in ts_names]
1095
+
1096
+
1097
+
1098
+
1099
+ #%% Phosphorous Loading
1100
+ def subwatershed_total_phosphorous_loading(uci,hbn,reach_ids = None,t_code=5, as_load = True,group_landcover = True):
1101
+ tp_loading = total_phosphorous(uci,hbn,t_code)
1102
+ if reach_ids is None:
1103
+ subwatersheds = uci.network.subwatersheds()
1104
+ else:
1105
+ subwatersheds = uci.network.subwatersheds(reach_ids)
1106
+
1107
+ perlnds = subwatersheds.loc[subwatersheds['SVOL'] == 'PERLND']
1108
+ perlnds = perlnds['AFACTR'].groupby([perlnds.index,perlnds['SVOLNO']]).sum().reset_index()
1109
+
1110
+
1111
+ #perlnds = perlnds.set_index('SVOLNO').drop_duplicates()
1112
+ total = tp_loading[perlnds['SVOLNO']]
1113
+
1114
+ total = total.mul(perlnds['AFACTR'].values,axis=1)
1115
+
1116
+ total = total.transpose()
1117
+ total['reach_id'] = perlnds['TVOLNO'].values
1118
+ total['landcover'] = uci.table('PERLND','GEN-INFO').loc[total.index,'LSID'].to_list()
1119
+ total['area'] = perlnds['AFACTR'].to_list() #perlnds.loc[total.index,'AFACTR'].to_list()
1120
+ total = total.reset_index().set_index(['index','landcover','area','reach_id']).transpose()
1121
+ total.columns.names = ['perlnd_id','landcover','area','reach_id']
1122
+
1123
+ if group_landcover:
1124
+ total.columns = total.columns.droplevel(['landcover','perlnd_id'])
1125
+ total = total.T.reset_index().groupby('reach_id').sum().reset_index().set_index(['reach_id','area']).T
1126
+
1127
+ if not as_load:
1128
+ total = total.div(total.columns.get_level_values('area').values,axis=1)
1129
+
1130
+ total.index = pd.to_datetime(total.index)
1131
+ return total
1132
+
1133
+ def total_phosphorous(uci,hbn,t_code):
1134
+ #assert(isinstance(perlnd_ids (int,list,None)))
1135
+ perlnds = uci.network.subwatersheds()
1136
+ perlnds = perlnds.loc[perlnds['SVOL'] == 'PERLND'].drop_duplicates(subset = ['SVOLNO','MLNO'])
1137
+
1138
+ totals = []
1139
+ for mlno in perlnds['MLNO'].unique():
1140
+ perlnd_ids = perlnds['SVOLNO'].loc[perlnds['MLNO'] == mlno].to_list()
1141
+ total = dissolved_orthophosphate(uci,hbn,mlno,t_code) + particulate_orthophosphate(uci,hbn,mlno, t_code) + organic_refactory_phosphorous(uci,hbn,mlno,t_code) + labile_oxygen_demand(uci,hbn,mlno,t_code)*0.007326 # Conversation factor to P
1142
+ totals.append(total[perlnd_ids])
1143
+
1144
+ total = pd.concat(totals,axis=1)
1145
+ total = total.T.groupby(total.columns).sum().T
1146
+ return total
1147
+
1148
+ MASSLINK_SCHEME = {'dissolved_orthophosphate': {'tmemn': 'NUIF1',
1149
+ 'tmemsb1': '4',
1150
+ 'tmemsb2':''},
1151
+ 'particulate_orthophosphate_sand': {'tmemn': 'NUIF2',
1152
+ 'tmemsb1': '1',
1153
+ 'tmemsb2':'2'},
1154
+ 'particulate_orthophosphate_silt': {'tmemn': 'NUIF2',
1155
+ 'tmemsb1': '2',
1156
+ 'tmemsb2':'2'},
1157
+ 'particulate_orthophosphate_clay': {'tmemn': 'NUIF2',
1158
+ 'tmemsb1': '3',
1159
+ 'tmemsb2':'2'},
1160
+ 'organic_refactory_phosphorous': {'tmemn': 'PKIF',
1161
+ 'tmemsb1' : '4',
1162
+ 'tmemsb2':''},
1163
+ 'organic_refactory_carbon':{'tmemn' : 'PKIF',
1164
+ 'tmemsb1': '5',
1165
+ 'tmemsb2':''},
1166
+ 'labile_oxygen_demand': {'tmemn': 'OXIF',
1167
+ 'tmemsb1': '2',
1168
+ 'tmemsb2':''}}
1169
+
1170
+
1171
+ def qualprop_transform(uci,hbn,mlno,tmemn,tmemsb1,tmemsb2 = '',t_code = 4):
1172
+ masslink = uci.table('MASS-LINK',f'MASS-LINK{mlno}')
1173
+ masslink = masslink.loc[(masslink['TMEMN'] == tmemn) & (masslink['TMEMSB1'] == tmemsb1) & (masslink['TMEMSB2'] == tmemsb2)]
1174
+ ts = 0
1175
+ for index,row in masslink.iterrows():
1176
+ hbn_name = uci.table('PERLND','QUAL-PROPS', int(row['SMEMSB1']) - 1).iloc[0]['QUALID']
1177
+ hbn_name = row['SMEMN'] + hbn_name
1178
+ mfactor = row['MFACTOR']
1179
+ ts = hbn.get_multiple_timeseries(row['SVOL'],t_code,hbn_name)*mfactor + ts
1180
+ return ts
1181
+
1182
+
1183
+
1184
+
1185
+ def dissolved_orthophosphate(uci,hbn,mlno,t_code = 4):
1186
+ tmemn = MASSLINK_SCHEME['dissolved_orthophosphate']['tmemn']
1187
+ tmemsb1 = MASSLINK_SCHEME['dissolved_orthophosphate']['tmemsb1']
1188
+ tmemsb2 = MASSLINK_SCHEME['dissolved_orthophosphate']['tmemsb2']
1189
+ return qualprop_transform(uci,hbn,mlno,tmemn,tmemsb1,tmemsb2,t_code)
1190
+
1191
+ def particulate_orthophosphate(uci,hbn,mlno,t_code = 4):
1192
+ ts = particulate_orthophosphate_sand(uci,hbn,mlno,t_code) + particulate_orthophosphate_silt(uci,hbn,mlno,t_code) + particulate_orthophosphate_clay(uci,hbn,mlno,t_code)
1193
+ return ts
1194
+
1195
+ def particulate_orthophosphate_sand(uci,hbn, mlno,t_code = 4):
1196
+ tmemn = MASSLINK_SCHEME['particulate_orthophosphate_sand']['tmemn']
1197
+ tmemsb1 = MASSLINK_SCHEME['particulate_orthophosphate_sand']['tmemsb1']
1198
+ tmemsb2 = MASSLINK_SCHEME['particulate_orthophosphate_sand']['tmemsb2']
1199
+ return qualprop_transform(uci,hbn,mlno,tmemn,tmemsb1,tmemsb2,t_code)
1200
+
1201
+ def particulate_orthophosphate_silt(uci,hbn, mlno,t_code = 4):
1202
+ tmemn = MASSLINK_SCHEME['particulate_orthophosphate_silt']['tmemn']
1203
+ tmemsb1 = MASSLINK_SCHEME['particulate_orthophosphate_silt']['tmemsb1']
1204
+ tmemsb2 = MASSLINK_SCHEME['particulate_orthophosphate_silt']['tmemsb2']
1205
+ return qualprop_transform(uci,hbn,mlno,tmemn,tmemsb1,tmemsb2,t_code)
1206
+
1207
+ def particulate_orthophosphate_clay(uci,hbn, mlno,t_code = 4):
1208
+ tmemn = MASSLINK_SCHEME['particulate_orthophosphate_clay']['tmemn']
1209
+ tmemsb1 = MASSLINK_SCHEME['particulate_orthophosphate_clay']['tmemsb1']
1210
+ tmemsb2 = MASSLINK_SCHEME['particulate_orthophosphate_clay']['tmemsb2']
1211
+ return qualprop_transform(uci,hbn,mlno,tmemn,tmemsb1,tmemsb2,t_code)
1212
+
1213
+ def organic_refactory_phosphorous(uci,hbn, mlno,t_code = 4):
1214
+ tmemn = MASSLINK_SCHEME['organic_refactory_phosphorous']['tmemn']
1215
+ tmemsb1 = MASSLINK_SCHEME['organic_refactory_phosphorous']['tmemsb1']
1216
+ tmemsb2 = MASSLINK_SCHEME['organic_refactory_phosphorous']['tmemsb2']
1217
+ return qualprop_transform(uci,hbn,mlno,tmemn,tmemsb1,tmemsb2,t_code)
1218
+
1219
+ def organic_refactory_carbon(uci,hbn, mlno,t_code = 4):
1220
+ tmemn = MASSLINK_SCHEME['organic_refactory_carbon']['tmemn']
1221
+ tmemsb1 = MASSLINK_SCHEME['organic_refactory_carbon']['tmemsb1']
1222
+ tmemsb2 = MASSLINK_SCHEME['organic_refactory_carbon']['tmemsb2']
1223
+ return qualprop_transform(uci,hbn,mlno,tmemn,tmemsb1,tmemsb2,t_code)
1224
+
1225
+ def labile_oxygen_demand(uci,hbn,mlno,t_code = 4):
1226
+ tmemn = MASSLINK_SCHEME['labile_oxygen_demand']['tmemn']
1227
+ tmemsb1 = MASSLINK_SCHEME['labile_oxygen_demand']['tmemsb1']
1228
+ tmemsb2 = MASSLINK_SCHEME['labile_oxygen_demand']['tmemsb2']
1229
+ return qualprop_transform(uci,hbn,mlno,tmemn,tmemsb1,tmemsb2,t_code)
1230
+