Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
e50efba
Address bugs in GQUAL and HTRCH
tredder75 Aug 5, 2021
7300202
Updates to original PLANK module
tredder75 Aug 5, 2021
e70d476
Classify OXRX + RQUAL updates
tredder75 Aug 5, 2021
3e1ede3
Customize main to handle revised RQUAL
tredder75 Aug 5, 2021
e215b68
Cumulative updates for 8/10/21
tredder75 Aug 10, 2021
70d7d30
Revisions based on testing of OXRX
tredder75 Aug 16, 2021
bae97ba
Address issue with initial reach outflow rates; implement unit conv. …
tredder75 Aug 16, 2021
d40fce3
GQUAL module - correct handling of metric units case
tredder75 Aug 16, 2021
2be4d96
GQUAL: address numba issues with OSEDx lists; simplify conversion calcs
tredder75 Aug 17, 2021
8636a6e
GQUAL - fix conflict / overwrite issue for "SSED4" time series
tredder75 Aug 23, 2021
f61daf2
HTRCH - restore njit decorator
tredder75 Aug 23, 2021
10bc67a
Nearly complete implementation of WQ classes and rqual wrapper
tredder75 Aug 23, 2021
03e0332
Update main and configuration for revised WQ implementation
tredder75 Aug 23, 2021
990dac0
Clean-up in OXRX, PLANK
tredder75 Aug 23, 2021
f0f642c
GQUAL - adjustments to address merge conflicts w/ Respec's develop br…
tredder75 Aug 25, 2021
7873f3e
Attempt to address errors generated by RQUAL jitclass implementation
tredder75 Aug 25, 2021
cf378fe
Merge pull request #40 from LimnoTech/develop
tredder75 Aug 25, 2021
3d14c79
GQUAL - address conditionals and simplify unit conversions for sed-as…
tredder75 Aug 26, 2021
94a7c2b
GENER and COPY support in readUCI
ptomasula Aug 26, 2021
c6f2f0f
Move get_timeseries function to utilities
ptomasula Aug 26, 2021
65166f3
general clean up
ptomasula Aug 26, 2021
5285309
Read GENER in get_uci
ptomasula Aug 26, 2021
cec0fae
partial implementation of COPY module
ptomasula Aug 26, 2021
bde423f
modify main to use COPY module
ptomasula Aug 26, 2021
d782c26
Fix Copy type hints
ptomasula Aug 27, 2021
2f977b3
Fix types in COPY doc strings
ptomasula Aug 27, 2021
d8a1fa2
RQUAL - successful numba compilation of all WQ modules
tredder75 Aug 31, 2021
5a9d79e
revised doc string for COPY
ptomasula Sep 1, 2021
3414a31
partial GENER module implementation
ptomasula Sep 1, 2021
0c8ae9c
ReadUCI fix NETWORK SMEMSB1
ptomasula Sep 1, 2021
71d4c37
add leading zeros on GENER UCI dictionary keys
ptomasula Sep 1, 2021
7e740e9
Merge branch 'develop' into GENER
ptomasula Sep 1, 2021
78d9c5d
clean up testing pieces
ptomasula Sep 1, 2021
9d33072
function to get timeseries from Gener instances based on links table
ptomasula Sep 1, 2021
c6f215d
RQUAL / WQ: updates to support WQ mass links and expanded output
tredder75 Sep 2, 2021
e8c60af
HTRCH: fix issue with (lack of) conversion to metric (tgrnd, tstop, m…
tredder75 Sep 2, 2021
b1aeb48
Merge branch 'wq-updates-tmr' into GENER
ptomasula Sep 2, 2021
8863002
Fix missed merge conflict change
ptomasula Sep 2, 2021
e8adff6
Remove leftover debugging print
ptomasula Sep 2, 2021
89e647e
PLANK: fix bug related to benthic algae
tredder75 Sep 2, 2021
bbf8805
PLANK: fixed sloughing initialization issue
tredder75 Sep 2, 2021
1708a8b
Merge branch 'wq-updates-tmr' into GENER
ptomasula Sep 2, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions HSP2/ADCALC.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
License: LGPL2
'''

from numpy import zeros
from numpy import zeros, array
from numba import njit
from HSP2.utilities import make_numba_dict

Expand All @@ -29,6 +29,7 @@ def adcalc(store, siminfo, uci, ts):
nexits = int(ui['NEXITS']) # table type GEN-INFO
ui['simlen'] = siminfo['steps']
ui['delts'] = siminfo['delt'] * 60.0 # delts is the simulation interval in seconds
ui['uunits'] = siminfo['units']

# calculated timeseries for advect()
if 'SROVOL' not in ts:
Expand Down Expand Up @@ -67,13 +68,23 @@ def _adcalc_(ui, ts):
simlen = int(ui['simlen'])
nexits = int(ui['NEXITS'])
delts = ui['delts']
uunits = ui['uunits']

# units conversion constants, 1 ACRE is 43560 sq ft. assumes input in acre-ft
VFACT = 43560.0
AFACT = 43560.0
if uunits == 2:
# si units conversion constants, 1 hectare is 10000 sq m, assumes area input in hectares, vol in Mm3
VFACT = 1.0e6
AFACT = 10000.0

# table ADCALC-DATA
if 'CRRAT' in ui:
crrat = ui['CRRAT']
else:
crrat = 1.5
if 'VOL' in ui:
vol = ui['VOL']
vol = ui['VOL'] * VFACT
else:
vol = 0.0

Expand All @@ -98,6 +109,8 @@ def _adcalc_(ui, ts):
for index in range(nexits):
ts['EOVOL' + str(index + 1)] = EOVOL[:, index]

ROS = ui['ROS']

# external time series
O = zeros((simlen, nexits))
if nexits > 1:
Expand All @@ -107,10 +120,10 @@ def _adcalc_(ui, ts):
O[:, 0] = ts['RO']

for loop in range(simlen):
vols = VOL[loop-1] * 43560 if loop > 0 else vol
vols = VOL[loop-1] * VFACT if loop > 0 else vol

o = O[loop]
os = O[loop-1] if loop > 0 else O[loop]
os = O[loop-1] if loop > 0 else array([ROS])
ro = 0.0
ros= 0.0
for index in range(nexits):
Expand Down
48 changes: 48 additions & 0 deletions HSP2/COPY.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from HSP2.utilities import get_timeseries
import pandas as pd
from typing import List, Dict

class Copy():
"""
Partial implementation of the COPY module.
In HSPF, COPY module supports ability able to 'copy' in this case output timeseries to
locations specified in NETWORK block or to EXTERNAL SOURCES.
This functionality is not currently implemented, presently only loading from EXT SOURCES
"""

_ts = {}
_ts['MEAN'] = {}
_ts['POINT'] = {}

def __init__(self, store: pd.HDFStore, sim_info: Dict, ext_sources: List) -> None:
ts = get_timeseries(store, ext_sources, sim_info)
for source in ext_sources:
themn = source.TMEMN
themsb = source.TMEMSB
self.set_ts(ts[f'{themn}{themsb}'], themn, themsb)

def set_ts(self, ts: pd.Series, themn: str, themsb: str) -> None:
"""Set the provided timeseries to ts dictionary

ts: pd.Series
pandas Series class instance corresponding to a timeseries
tmemn: str, {'MEAN', 'POINT'}
Target member name, specifies if target timeseries is in mean-valued
or point-valued dictionaries
tmemsb: str,
Target member name subscripts, acts as key for mean-valued and point-valued dictionaries
Original HSPF restricts this to 0-20 but no limit enforced in HSP2
"""
self._ts[themn][themsb] = ts

def get_ts(self, tmemn: str, tmemsb: str) -> pd.Series:
"""Gets the specified timeseries from copy class instance based

tmemn: str, {'MEAN', 'POINT'}
Target member name, specifies if target timeseries is in mean-valued
or point-valued dictionaries
tmemsb: str,
Target member name subscripts, acts as key for mean-valued and point-valued dictionaries
Original HSPF restricts this to 0-20 but no limit enforced in HSP2
"""
return self._ts[tmemn][tmemsb]
147 changes: 147 additions & 0 deletions HSP2/GENER.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
from numba import njit
import numpy as np
import pandas as pd
from typing import Dict, List

from HSP2.utilities import get_timeseries

class Gener():
"""
Partial implementation of the GENER module.
Currently supports OPCODES 1-7, 9-23, & 25-26
"""

ts_input_1 = pd.Series() # type: pd.Series
ts_input_2 = pd.Series() # type: pd.Series
ts_output = pd.Series() # type: pd.Series
opcode = 0 # type: int
k = -1.0E30 # type: float

def __init__(self, segment: str, copies: Dict, geners: Dict, ddlinks: Dict, ddgener: Dict) -> None:
self.opcode = ddgener['OPCODE'][segment]
if self.opcode in [9,10,11,24,25,26]:
self.k = ddgener['PARM'][segment]

for link in ddlinks[segment]:
ts = pd.Series()
if link.SVOL == 'COPY':
copy = copies[link.SVOLNO]
ts = copy.get_ts(link.SMEMN,link.SMEMSB1)
elif link.SVOL == 'GENER':
gener = geners[link.SVOLNO]
ts = gener.get_ts()
else:
raise NotImplementedError(f"Invalid SVOL. GENER module does not currently support reading TimeSeries for '{link.SVOL}'")

if link.TGRPN == 'INPUT' and link.TMEMN == 'ONE':
self.ts_input_1 = ts
elif link.TGRPN == 'INPUT' and link.TMEMN == 'TWO':
self.ts_input_2 = ts
else:
raise AttributeError(f"No attribute {link.TGRPN}{link.THEMN} to assign TimeSeries. Should be either 'INPUTONE' or 'INPUTWO'")

self._execute_gener()

def get_ts(self) -> pd.Series:
"""
Returns the result TimeSeries generated from executing the operation specified by the OPCODE.
"""
return self.ts_output

def _execute_gener(self) -> None:
gener_op = getattr(self, f'_opcode{self.opcode}')
ts_result = gener_op()
#May need additional logic here to set default of 1.0E30 to be consistent with FORTRAN code
self.ts_output = ts_result

def _opcode1(self) -> pd.Series:
return np.abs(self.ts_input_1)

def _opcode2(self) -> pd.Series:
return np.sqrt(self.ts_input_1)

def _opcode3(self) -> pd.Series:
return np.trunc(self.ts_input_1)

def _opcode4(self) -> pd.Series:
return np.ceil(self.ts_input_1)

def _opcode5(self) -> pd.Series:
return np.floor(self.ts_input_1)

def _opcode6(self) -> pd.Series:
return np.log(self.ts_input_1)

def _opcode7(self) -> pd.Series:
return np.log10(self.ts_input_1)

def _opcode8(self) -> pd.Series:
#Not presently implemented, read UCI would need to modify to
#process NTERMS and COEFFS sub blocks of GENER block
raise NotImplementedError("GENER OPCODE 8 is not currently supported")

def _opcode9(self) -> pd.Series:
return np.power(self.k , self.ts_input_1)

def _opcode10(self) -> pd.Series:
return np.power(self.ts_input_1, self.k)

def _opcode11(self) -> pd.Series:
return np.add(self.ts_input_1, self.k)

def _opcode12(self) -> pd.Series:
return np.sin(self.ts_input_1)

def _opcode13(self) -> pd.Series:
return np.cos(self.ts_input_1)

def _opcode14(self) -> pd.Series:
return np.tan(self.ts_input_1)

def _opcode15(self) -> pd.Series:
return np.cumsum(self.ts_input_1)

def _opcode16(self) -> pd.Series:
return np.add(self.ts_input_1, self.ts_input_2)

def _opcode17(self) -> pd.Series:
return np.subtract(self.ts_input_1, self.ts_input_2)

def _opcode18(self) -> pd.Series:
return np.multiply(self.ts_input_1, self.ts_input_2)

def _opcode19(self) -> pd.Series:
return np.divide(self.ts_input_1, self.ts_input_2)

def _opcode20(self) -> pd.Series:
return np.maximum(self.ts_input_1, self.ts_input_2)

def _opcode21(self) -> pd.Series:
return np.minimum(self.ts_input_1, self.ts_input_2)

def _opcode22(self) -> pd.Series:
return np.power(self.ts_input_1, self.ts_input_2)

def _opcode23(self) -> pd.Series:
ts_out = pd.Series(index=self.ts_input_1.index)
s = 0
for idx in self.ts_input_1.index:
result = self.ts_input_1[idx] - self.ts_input_2[idx] - s
if result < 0:
s = s - self.ts_input_1[idx] + self.ts_input_2[idx]
result = 0
else:
s = 0
ts_out[idx] = result
return ts_out

def _opcode24(self) -> pd.Series:
#skip for now
#would need to figure out timeseries length component
raise NotImplementedError("GENER OPCODE 24 is not currently supported")

def _opcode25(self) -> pd.Series:
return np.maximum(self.ts_input_1, self.k)

def _opcode26(self) -> pd.Series:
return np.minimum(self.ts_input_1, self.k)
Loading