diff --git a/.gitignore b/.gitignore
index 00d01966..8cad0c9b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -68,10 +68,8 @@ tests/GLWACSO/HSP2results/hspp007.uci
tests/test_report_conversion.html
# Omit big files
-tests/land_spec/hwmA51800.h5
-tests/testcbp/HSP2results/PL3_5250_0001.h5
+tests/**/*.h5
tests/testcbp/HSP2results/*.csv
-tests/test10/HSP2results/test10.h5
# R files
.Rdata
diff --git a/HSP2/HYDR.py b/HSP2/HYDR.py
index 0ea0bb68..a5f54f60 100644
--- a/HSP2/HYDR.py
+++ b/HSP2/HYDR.py
@@ -12,14 +12,17 @@
'''
-from numpy import zeros, any, full, nan, array, int64, arange
+from numpy import zeros, any, full, nan, array, int64, arange, asarray
from pandas import DataFrame
from math import sqrt, log10
-from numba import njit
+from numba import njit, types
from numba.typed import List
from HSP2.utilities import initm, make_numba_dict
-from HSP2.state import *
-from HSP2.SPECL import specl
+
+# the following imports added by rb to handle dynamic code and special actions
+from HSP2.state import hydr_get_ix, hydr_init_ix
+from HSP2.om import pre_step_model, step_model, model_domain_dependencies
+from numba.typed import Dict
ERRMSGS =('HYDR: SOLVE equations are indeterminate', #ERRMSG0
@@ -36,7 +39,7 @@ def hydr(io_manager, siminfo, uci, ts, ftables, state):
''' find the state of the reach/reservoir at the end of the time interval
and the outflows during the interval
- CALL: hydr(store, general, ui, ts, specactions)
+ CALL: hydr(store, general, ui, ts, state)
store is the Pandas/PyTable open store
general is a dictionary with simulation level infor (OP_SEQUENCE for example)
ui is a dictionary with RID specific HSPF UCI like data
@@ -122,11 +125,15 @@ def hydr(io_manager, siminfo, uci, ts, ftables, state):
for i in range(nexits):
Olabels.append(f'O{i+1}')
OVOLlabels.append(f'OVOL{i+1}')
-
- # state_info is some generic things about the simulation
+
+ #######################################################################################
+ # the following section (1 of 3) added to HYDR by rb to handle dynamic code and special actions
+ #######################################################################################
+ # state_info is some generic things about the simulation
+ # must be numba safe, so we don't just pass the whole state which is not
state_info = Dict.empty(key_type=types.unicode_type, value_type=types.unicode_type)
state_info['operation'], state_info['segment'], state_info['activity'] = state['operation'], state['segment'], state['activity']
- state_info['domain'], state_info['state_step_hydr'] = state['domain'], state['state_step_hydr']
+ state_info['domain'], state_info['state_step_hydr'], state_info['state_step_om'] = state['domain'], state['state_step_hydr'], state['state_step_om']
hsp2_local_py = state['hsp2_local_py']
# It appears necessary to load this here, instead of from main.py, otherwise,
# _hydr_() does not recognize the function state_step_hydr()?
@@ -134,22 +141,20 @@ def hydr(io_manager, siminfo, uci, ts, ftables, state):
from hsp2_local_py import state_step_hydr
else:
from HSP2.state_fn_defaults import state_step_hydr
+ # initialize the hydr paths in case they don't already reside here
+ hydr_init_ix(state, state['domain'])
# must split dicts out of state Dict since numba cannot handle mixed-type nested Dicts
state_ix, dict_ix, ts_ix = state['state_ix'], state['dict_ix'], state['ts_ix']
state_paths = state['state_paths']
- # initialize the hydr paths in case they don't already reside here
- hydr_init_ix(state_ix, state_paths, state['domain'])
-
- ###########################################################################
- # specactions - special actions code TBD
- ###########################################################################
- specactions = make_numba_dict(state['specactions']) # Note: all values coverted to float automatically
-
- ###########################################################################
- # Do the simulation with _hydr_()
- ###########################################################################
- errors = _hydr_(ui, ts, COLIND, OUTDGT, rchtab, funct, Olabels, OVOLlabels, state_info, state_paths, state_ix, dict_ix, ts_ix, specactions, state_step_hydr) # run reaches simulation code
- ###########################################################################
+ ep_list = ["DEP","IVOL","O1","O2","O3","OVOL1","OVOL2","OVOL3","PRSUPY","RO","ROVOL","SAREA","TAU","USTAR","VOL","VOLEV"]
+ model_exec_list = model_domain_dependencies(state, state_info['domain'], ep_list)
+ model_exec_list = asarray(model_exec_list, dtype="i8") # format for use in numba
+ op_tokens = state['op_tokens']
+ #######################################################################################
+
+ # Do the simulation with _hydr_ (ie run reaches simulation code)
+ errors = _hydr_(ui, ts, COLIND, OUTDGT, rchtab, funct, Olabels, OVOLlabels,
+ state_info, state_paths, state_ix, dict_ix, ts_ix, state_step_hydr, op_tokens, model_exec_list)
if 'O' in ts: del ts['O']
if 'OVOL' in ts: del ts['OVOL']
@@ -163,7 +168,7 @@ def hydr(io_manager, siminfo, uci, ts, ftables, state):
@njit(cache=True)
-def _hydr_(ui, ts, COLIND, OUTDGT, rowsFT, funct, Olabels, OVOLlabels, state_info, state_paths, state_ix, dict_ix, ts_ix, specactions, state_step_hydr):
+def _hydr_(ui, ts, COLIND, OUTDGT, rowsFT, funct, Olabels, OVOLlabels, state_info, state_paths, state_ix, dict_ix, ts_ix, state_step_hydr, op_tokens, model_exec_list):
errors = zeros(int(ui['errlen'])).astype(int64)
steps = int(ui['steps']) # number of simulation steps
@@ -292,8 +297,11 @@ def _hydr_(ui, ts, COLIND, OUTDGT, rowsFT, funct, Olabels, OVOLlabels, state_inf
# other initial vars
rovol = 0.0
volev = 0.0
- IVOL0 = ts['IVOL'] # the actual inflow in simulation native units
- # prepare for dynamic state
+ IVOL0 = ts['IVOL'] # the actual inflow in simulation native units
+
+ #######################################################################################
+ # the following section (2 of 3) added by rb to HYDR, this one to prepare for dynamic state including special actions
+ #######################################################################################
hydr_ix = hydr_get_ix(state_ix, state_paths, state_info['domain'])
# these are integer placeholders faster than calling the array look each timestep
o1_ix, o2_ix, o3_ix, ivol_ix = hydr_ix['O1'], hydr_ix['O2'], hydr_ix['O3'], hydr_ix['IVOL']
@@ -306,15 +314,19 @@ def _hydr_(ui, ts, COLIND, OUTDGT, rowsFT, funct, Olabels, OVOLlabels, state_inf
out_ix[1] = o2_ix
if nexits > 2:
out_ix[2] = o3_ix
+ #######################################################################################
+
# HYDR (except where noted)
for step in range(steps):
- # call specl
- specl(ui, ts, step, state_info, state_paths, state_ix, specactions)
convf = CONVF[step]
outdgt[:] = OUTDGT[step, :]
colind[:] = COLIND[step, :]
roseff = ro
oseff[:] = o[:]
+
+ #######################################################################################
+ # the following section (3 of 3) added by rb to accommodate dynamic code, operations models, and special actions
+ #######################################################################################
# set state_ix with value of local state variables and/or needed vars
# Note: we pass IVOL0, not IVOL here since IVOL has been converted to different units
state_ix[ro_ix], state_ix[rovol_ix] = ro, rovol
@@ -323,17 +335,30 @@ def _hydr_(ui, ts, COLIND, OUTDGT, rowsFT, funct, Olabels, OVOLlabels, state_inf
state_ix[out_ix[oi]] = outdgt[oi]
state_ix[vol_ix], state_ix[ivol_ix] = vol, IVOL0[step]
state_ix[volev_ix] = volev
- # Execute dynamic code if enabled
+ # - these if statements may be irrelevant if default functions simply return
+ # when no objects are defined.
+ if (state_info['state_step_om'] == 'enabled'):
+ pre_step_model(model_exec_list, op_tokens, state_ix, dict_ix, ts_ix, step)
if (state_info['state_step_hydr'] == 'enabled'):
state_step_hydr(state_info, state_paths, state_ix, dict_ix, ts_ix, hydr_ix, step)
+ if (state_info['state_step_om'] == 'enabled'):
+ #print("trying to execute state_step_om()")
+ # model_exec_list contains the model exec list in dependency order
+ # now these are all executed at once, but we need to make them only for domain end points
+ step_model(model_exec_list, op_tokens, state_ix, dict_ix, ts_ix, step) # traditional 'ACTIONS' done in here
+ if ( (state_info['state_step_hydr'] == 'enabled')
+ or (state_info['state_step_om'] == 'enabled') ):
# Do write-backs for editable STATE variables
# OUTDGT is writeable
for oi in range(nexits):
outdgt[oi] = state_ix[out_ix[oi]]
- # IVOL is writeable.
- # Note: we must convert IVOL to the units expected in _hydr_
+ # IVOL is writeable.
+ # Note: we must convert IVOL to the units expected in _hydr_
# maybe routines should do this, and this is not needed (but pass VFACT in state)
IVOL[step] = state_ix[ivol_ix] * VFACT
+ # End dynamic code step()
+ #######################################################################################
+
# vols, sas variables and their initializations not needed.
if irexit >= 0: # irrigation exit is set, zero based number
if rirwdl > 0.0: # equivalent to OVOL for the irrigation exit
diff --git a/HSP2/SEDTRN.py b/HSP2/SEDTRN.py
index f78b8f83..511ec46a 100644
--- a/HSP2/SEDTRN.py
+++ b/HSP2/SEDTRN.py
@@ -3,12 +3,17 @@
License: LGPL2
'''
-from numpy import array, zeros, where, int64
+from numpy import array, zeros, where, int64, asarray
from math import log10, exp
-from numba import njit
+from numba import njit, types
from HSP2.ADCALC import advect
from HSP2.utilities import make_numba_dict
+# the following imports added to handle special actions
+from HSP2.state import sedtrn_get_ix, sedtrn_init_ix
+from HSP2.om import pre_step_model, step_model, model_domain_dependencies
+from numba.typed import Dict
+
ERRMSGS =('SEDTRN: Warning -- bed storage of sediment size fraction sand is empty', #ERRMSG0
'SEDTRN: Warning -- bed storage of sediment size fraction silt is empty', #ERRMSG1
'SEDTRN: Warning -- bed storage of sediment size fraction clay is empty', #ERRMSG2
@@ -17,7 +22,7 @@
'SEDTRN: Simulation of sediment requires all 3 "auxiliary flags" (AUX1FG, etc) in section HYDR must be turned on', #ERRMSG5
'SEDTRN: When specifying the initial composition of the bed, the fraction of sand, silt, and clay must sum to a value close to 1.0.') #ERRMSG6
-def sedtrn(io_manager, siminfo, uci, ts):
+def sedtrn(io_manager, siminfo, uci, ts, state):
''' Simulate behavior of inorganic sediment'''
# simlen = siminfo['steps']
@@ -68,8 +73,35 @@ def sedtrn(io_manager, siminfo, uci, ts):
ui['clay_taucs'] = ui_clay['TAUCS']
ui['clay_m'] = ui_clay['M'] * delt60 / 24.0 * 4.880 # convert erodibility coeff from /day to /ivl
+ #######################################################################################
+ # the following section (1 of 3) added to SEDTRN by pbd to handle special actions
+ #######################################################################################
+ # state_info is some generic things about the simulation
+ # must be numba safe, so we don't just pass the whole state which is not
+ state_info = Dict.empty(key_type=types.unicode_type, value_type=types.unicode_type)
+ state_info['operation'], state_info['segment'], state_info['activity'] = state['operation'], state['segment'], state['activity']
+ state_info['domain'], state_info['state_step_hydr'], state_info['state_step_om'] = state['domain'], state['state_step_hydr'], state['state_step_om']
+ # hsp2_local_py = state['hsp2_local_py']
+ # # It appears necessary to load this here, instead of from main.py, otherwise,
+ # # _hydr_() does not recognize the function state_step_hydr()?
+ # if (hsp2_local_py != False):
+ # from hsp2_local_py import state_step_hydr
+ # else:
+ # from HSP2.state_fn_defaults import state_step_hydr
+ # must split dicts out of state Dict since numba cannot handle mixed-type nested Dicts
+ # initialize the sedtrn paths in case they don't already reside here
+ sedtrn_init_ix(state, state['domain'])
+ state_ix, dict_ix, ts_ix = state['state_ix'], state['dict_ix'], state['ts_ix']
+ state_paths = state['state_paths']
+ op_tokens = state['op_tokens']
+ # Aggregate the list of all SEDTRN end point dependencies
+ ep_list = ['RSED4', 'RSED5', 'RSED6']
+ model_exec_list = model_domain_dependencies(state, state_info['domain'], ep_list)
+ model_exec_list = asarray(model_exec_list, dtype="i8") # format for use in
+ #######################################################################################
+
############################################################################
- errors = _sedtrn_(ui, ts) # run SEDTRN simulation code
+ errors = _sedtrn_(ui, ts, state_info, state_paths, state_ix, dict_ix, ts_ix, op_tokens, model_exec_list) # run SEDTRN simulation code
############################################################################
if nexits > 1:
@@ -91,7 +123,7 @@ def sedtrn(io_manager, siminfo, uci, ts):
return errors, ERRMSGS
@njit(cache=True)
-def _sedtrn_(ui, ts):
+def _sedtrn_(ui, ts, state_info, state_paths, state_ix, dict_ix, ts_ix, op_tokens, model_exec_list):
''' Simulate behavior of inorganic sediment'''
errorsV = zeros(int(ui['errlen'])).astype(int64)
@@ -291,8 +323,36 @@ def _sedtrn_(ui, ts):
#################### END PSED
+ #######################################################################################
+ # the following section (2 of 3) added by pbd to SEDTRN, this one to prepare for special actions
+ #######################################################################################
+ sedtrn_ix = sedtrn_get_ix(state_ix, state_paths, state_info['domain'])
+ # these are integer placeholders faster than calling the array look each timestep
+ rsed4_ix, rsed5_ix, rsed6_ix = sedtrn_ix['RSED4'], sedtrn_ix['RSED5'], sedtrn_ix['RSED6']
+ #######################################################################################
+
for loop in range(simlen):
+ #######################################################################################
+ # the following section (3 of 3) added by pbd to accommodate special actions
+ #######################################################################################
+ # set state_ix with value of local state variables and/or needed vars
+ state_ix[rsed4_ix] = sand_wt_rsed4
+ state_ix[rsed5_ix] = silt_wt_rsed5
+ state_ix[rsed6_ix] = clay_wt_rsed6
+ if (state_info['state_step_om'] == 'enabled'):
+ pre_step_model(model_exec_list, op_tokens, state_ix, dict_ix, ts_ix, step = loop)
+
+ # (todo) Insert code hook for dynamic python modification of state
+
+ if (state_info['state_step_om'] == 'enabled'):
+ step_model(model_exec_list, op_tokens, state_ix, dict_ix, ts_ix, step = loop) # traditional 'ACTIONS' done in here
+ # Do write-backs for editable STATE variables
+ sand_wt_rsed4 = state_ix[rsed4_ix]
+ silt_wt_rsed5 = state_ix[rsed5_ix]
+ clay_wt_rsed6 = state_ix[rsed6_ix]
+ #######################################################################################
+
# perform any necessary unit conversions
if uunits == 2: # uci is in metric units
avvele = AVVEL[loop] * 3.28
diff --git a/HSP2/SPECL.py b/HSP2/SPECL.py
index c7cf5e23..ac4bd95b 100644
--- a/HSP2/SPECL.py
+++ b/HSP2/SPECL.py
@@ -1,18 +1,57 @@
-''' process special actions in this domain
-
-CALL: specl(io_manager, siminfo, uci, ts, state, specl_actions)
- store is the Pandas/PyTable open store
- siminfo is a dictionary with simulation level infor (OP_SEQUENCE for example)
- ui is a dictionary with RID specific HSPF UCI like data
- ts is a dictionary with RID specific timeseries
- state is a dictionary with value of variables at ts[step - 1]
- specl_actions is a dictionary with all SPEC-ACTIONS entries
-'''
-
-from numba import njit
-
-@njit
-def specl(ui, ts, step, state_info, state_paths, state_ix, specactions):
- # ther eis no need for _specl_ because this code must already be njit
- return
+''' process special actions in this domain
+Notes:
+ - code for parsing UCI SPEC-ACTIONS is in HSP2tools/readUCI.py
+ - code for object classes that transform parsed data into OP codes for OM and STATE support
+ is in this directory tree as om_special_[action type].py,
+ - Ex: om_special_action.py contains object support and runtime functions for classic ACTIONS
+'''
+
+from numba import njit
+from pandas import DataFrame, date_range
+
+def specl_load_actions(state, io_manager, siminfo):
+ if 'ACTIONS' in state['specactions']:
+ dc = state['specactions']['ACTIONS']
+ for ix in dc.index:
+ # add the items to the state['model_data'] dict
+ speca = dc[ix:(ix+1)]
+ # need to add a name attribute
+ opname = 'SPEC' + 'ACTION' + str(ix)
+ state['model_data'][opname] = {}
+ state['model_data'][opname]['name'] = opname
+ for ik in speca.keys():
+ #print("looking for speca key ", ik)
+ state['model_data'][opname][ik] = speca.to_dict()[ik][ix] # add subscripts?
+ if ik == 'VARI':
+ if len(speca.to_dict()['S1'][ix]) > 0:
+ state['model_data'][opname][ik] += speca.to_dict()['S1'][ix]
+ if len(speca.to_dict()['S2'][ix]) > 0:
+ state['model_data'][opname][ik] += speca.to_dict()['S2'][ix]
+ state['model_data'][opname]['object_class'] = 'SpecialAction'
+ #print("model_data", ix, " = ", state['model_data'][opname])
+ return
+
+def specl_load_state(state, io_manager, siminfo):
+ specl_load_actions(state, io_manager, siminfo)
+ # others defined below, like:
+ # specl_load_uvnames(state, io_manager, siminfo)
+ # ...
+ return
+
+'''
+# the code specl() is deprecated in favor of execution inside OM
+# see om_special_action.py for example of object support and runtime functions for classic ACTIONS
+CALL: specl(ui, ts, step, state_info, state_paths, state_ix, specactions)
+ store is the Pandas/PyTable open store
+ siminfo is a dictionary with simulation level infor (OP_SEQUENCE for example)
+ ui is a dictionary with RID specific HSPF UCI like data
+ ts is a dictionary with RID specific timeseries
+ state is a dictionary with value of variables at ts[step - 1]
+ specl_actions is a dictionary with all SPEC-ACTIONS entries
+'''
+
+@njit
+def specl(ui, ts, step, state_info, state_paths, state_ix, specactions):
+ # ther eis no need for _specl_ because this code must already be njit
+ return
\ No newline at end of file
diff --git a/HSP2/main.py b/HSP2/main.py
index cd636e68..bcd350f8 100644
--- a/HSP2/main.py
+++ b/HSP2/main.py
@@ -13,14 +13,15 @@
from HSP2IO.hdf import HDF5
from HSP2.utilities import versions, get_timeseries, expand_timeseries_names, save_timeseries, get_gener_timeseries
from HSP2.configuration import activities, noop, expand_masslinks
-from HSP2.state import *
+from HSP2.state import init_state_dicts, state_siminfo_hsp2, state_load_dynamics_hsp2, state_init_hsp2, state_context_hsp2
+from HSP2.om import om_init_state, state_om_model_run_prep, state_load_dynamics_om
+from HSP2.SPECL import specl_load_state
from HSP2IO.io import IOManager, SupportsReadTS, Category
def main(io_manager:Union[str, IOManager], saveall:bool=False, jupyterlab:bool=True) -> None:
"""
Run main HSP2 program.
-
Parameters
----------
io_manager
@@ -28,16 +29,16 @@ def main(io_manager:Union[str, IOManager], saveall:bool=False, jupyterlab:bool=T
saveall: bool, default=False
Saves all calculated data ignoring SAVE tables.
jupyterlab: bool, default=True
- Flag for specific output behavior for jupyter lab.
-
- Returns
- -------
+ Flag for specific output behavior for jupyter lab.
+
+ Return
+ ------------
None
+
"""
if isinstance(io_manager, str):
hdf5_instance = HDF5(io_manager)
io_manager = IOManager(hdf5_instance)
-
hdfname = io_manager._input.file_path
if not os.path.exists(hdfname):
raise FileNotFoundError(f'{hdfname} HDF5 File Not Found')
@@ -53,28 +54,35 @@ def main(io_manager:Union[str, IOManager], saveall:bool=False, jupyterlab:bool=T
ddext_sources = uci_obj.ddext_sources
ddgener = uci_obj.ddgener
uci = uci_obj.uci
- siminfo = uci_obj.siminfo
+ siminfo = uci_obj.siminfo
ftables = uci_obj.ftables
specactions = uci_obj.specactions
monthdata = uci_obj.monthdata
- specactions = {} # placeholder till added to uci parser
-
+
start, stop = siminfo['start'], siminfo['stop']
copy_instances = {}
gener_instances = {}
#######################################################################################
- # initilize STATE dicts
+ # initialize STATE dicts
#######################################################################################
- # Set up Things in state that will be used in all modular activitis like SPECL
+ # Set up Things in state that will be used in all modular activities like SPECL
state = init_state_dicts()
state_siminfo_hsp2(uci_obj, siminfo)
- # Add support for dynamic functins to operate on STATE
- # - Load any dynamic components if present, and store variables on objects
+ # Add support for dynamic functions to operate on STATE
+ # - Load any dynamic components if present, and store variables on objects
state_load_dynamics_hsp2(state, io_manager, siminfo)
+ # Iterate through all segments and add crucial paths to state
+ # before loading dynamic components that may reference them
+ state_init_hsp2(state, opseq, activities)
# - finally stash specactions in state, not domain (segment) dependent so do it once
state['specactions'] = specactions # stash the specaction dict in state
+ om_init_state(state) # set up operational model specific state entries
+ specl_load_state(state, io_manager, siminfo) # traditional special actions
+ state_load_dynamics_om(state, io_manager, siminfo) # operational model for custom python
+ # finalize all dynamically loaded components and prepare to run the model
+ state_om_model_run_prep(state, io_manager, siminfo)
#######################################################################################
# main processing loop
@@ -87,7 +95,7 @@ def main(io_manager:Union[str, IOManager], saveall:bool=False, jupyterlab:bool=T
siminfo['steps'] = len(siminfo['tindex'])
if operation == 'COPY':
- copy_instances[segment] = activities[operation](io_manager, siminfo, ddext_sources[(operation,segment)])
+ copy_instances[segment] = activities[operation](io_manager, siminfo, ddext_sources[(operation,segment)])
elif operation == 'GENER':
try:
ts = get_timeseries(io_manager, ddext_sources[(operation, segment)], siminfo)
@@ -109,7 +117,7 @@ def main(io_manager:Union[str, IOManager], saveall:bool=False, jupyterlab:bool=T
flags['ADNHFG'] = uci[(operation, 'NUTRX', segment)]['FLAGS']['ADNHFG']
flags['PO4FG'] = uci[(operation, 'NUTRX', segment)]['FLAGS']['PO4FG']
flags['ADPOFG'] = uci[(operation, 'NUTRX', segment)]['FLAGS']['ADPOFG']
-
+
get_flows(io_manager, ts, flags, uci, segment, ddlinks, ddmasslinks, siminfo['steps'], msg)
for activity, function in activities[operation].items():
@@ -123,9 +131,9 @@ def main(io_manager:Union[str, IOManager], saveall:bool=False, jupyterlab:bool=T
continue
msg(3, f'{activity}')
- # Set context for dynamic executables.
+ # Set context for dynamic executables and special actions
state_context_hsp2(state, operation, segment, activity)
-
+
ui = uci[(operation, activity, segment)] # ui is a dictionary
if operation == 'PERLND' and activity == 'SEDMNT':
# special exception here to make CSNOFG available
@@ -189,7 +197,7 @@ def main(io_manager:Union[str, IOManager], saveall:bool=False, jupyterlab:bool=T
elif flags['PLANK']:
if 'CFSAEX' in uci[(operation, 'PLANK', segment)]['PARAMETERS']:
ui['PARAMETERS']['CFSAEX'] = uci[(operation, 'PLANK', segment)]['PARAMETERS']['CFSAEX']
-
+
if activity == 'RQUAL':
# RQUAL inputs:
ui['advectData'] = uci[(operation, 'ADCALC', segment)]['adcalcData']
@@ -209,12 +217,12 @@ def main(io_manager:Union[str, IOManager], saveall:bool=False, jupyterlab:bool=T
ui['PARAMETERS']['NCONS'] = uci[(operation, 'CONS', segment)]['PARAMETERS']['NCONS']
# OXRX module inputs:
- ui_oxrx = uci[(operation, 'OXRX', segment)]
-
+ ui_oxrx = uci[(operation, 'OXRX', segment)]
+
if flags['HYDR']:
ui_oxrx['PARAMETERS']['LEN'] = uci[(operation, 'HYDR', segment)]['PARAMETERS']['LEN']
ui_oxrx['PARAMETERS']['DELTH'] = uci[(operation, 'HYDR', segment)]['PARAMETERS']['DELTH']
-
+
if flags['HTRCH']:
ui_oxrx['PARAMETERS']['ELEV'] = uci[(operation, 'HTRCH', segment)]['PARAMETERS']['ELEV']
@@ -228,17 +236,19 @@ def main(io_manager:Union[str, IOManager], saveall:bool=False, jupyterlab:bool=T
ui['PARAMETERS']['CFSAEX'] = uci[(operation, 'HTRCH', segment)]['PARAMETERS']['CFSAEX']
# NUTRX, PLANK, PHCARB module inputs:
- ui_nutrx = uci[(operation, 'NUTRX', segment)]
- ui_plank = uci[(operation, 'PLANK', segment)]
- ui_phcarb = uci[(operation, 'PHCARB', segment)]
+ ui_nutrx = uci[(operation, 'NUTRX', segment)]
+ ui_plank = uci[(operation, 'PLANK', segment)]
+ ui_phcarb = uci[(operation, 'PHCARB', segment)]
############ calls activity function like snow() ##############
if operation not in ['COPY','GENER']:
if (activity == 'HYDR'):
errors, errmessages = function(io_manager, siminfo, ui, ts, ftables, state)
+ elif (activity == 'SEDTRN'):
+ errors, errmessages = function(io_manager, siminfo, ui, ts, state)
elif (activity != 'RQUAL'):
errors, errmessages = function(io_manager, siminfo, ui, ts)
- else:
+ else:
errors, errmessages = function(io_manager, siminfo, ui, ui_oxrx, ui_nutrx, ui_plank, ui_phcarb, ts, monthdata)
###############################################################
@@ -263,7 +273,7 @@ def main(io_manager:Union[str, IOManager], saveall:bool=False, jupyterlab:bool=T
if 'SAVE' in ui:
save_timeseries(io_manager,ts,ui['SAVE'],siminfo,saveall,operation,segment,activity,jupyterlab,outstep)
-
+
if (activity == 'RQUAL'):
if 'SAVE' in ui_oxrx: save_timeseries(io_manager,ts,ui_oxrx['SAVE'],siminfo,saveall,operation,segment,'OXRX',jupyterlab,outstep_oxrx)
if 'SAVE' in ui_nutrx and flags['NUTRX'] == 1: save_timeseries(io_manager,ts,ui_nutrx['SAVE'],siminfo,saveall,operation,segment,'NUTRX',jupyterlab,outstep_nutrx)
diff --git a/HSP2/om.py b/HSP2/om.py
new file mode 100644
index 00000000..57952504
--- /dev/null
+++ b/HSP2/om.py
@@ -0,0 +1,634 @@
+# set up libraries to import for the load_sim_dicts function
+# later, this will be drawing from the hdf5, but for now we
+# are hard-wiring a set of components for testing.
+# Note: these import calls must be done down here AFTER the helper functions
+# defined aove that are called by the object classes
+import random # this is only used for a demo so may be deprecated
+import json
+import os
+import pandas as pd
+import numpy as np
+import time
+from numba.typed import Dict
+from numpy import zeros, int32
+from numba import int8, float32, njit, types, typed # import the types
+import random # this is only used for a demo so may be deprecated
+from HSP2.state import append_state, get_ix_path
+
+
+def get_exec_order(model_exec_list, var_ix):
+ """
+ Find the integer key of a variable name in state_ix
+ """
+ model_exec_list = dict(enumerate(model_exec_list.flatten(), 1))
+ for exec_order, ix in model_exec_list.items():
+ if var_ix == ix:
+ # we need to add this to the state
+ return exec_order
+ return False
+
+def init_op_tokens(op_tokens, tops, eq_ix):
+ """
+ Iinitialize the op_tokens Dict
+ This contains the runtime op code for every dynamic operation to be used
+ """
+ for j in range(len(tops)):
+ if isinstance(tops[j], str):
+ # must add this to the state array as a constant
+ s_ix = append_state(state_ix, float(tops[j]))
+ tops[j] = s_ix
+
+ op_tokens[eq_ix] = np.asarray(tops, dtype="i8")
+
+def is_float_digit(n: str) -> bool:
+ """
+ Helper Function to determine if a variable is numeric
+ """
+ try:
+ float(n)
+ return True
+ except ValueError:
+ return False
+
+def model_element_paths(mel, state):
+ """
+ Informational. If given a list of state_ix keys, shows the operators local name and state_path
+ """
+ ixn = 1
+ for ix in mel:
+ ip = get_ix_path(state['state_paths'], ix)
+ im = state['model_object_cache'][ip]
+ print(ixn, ":", im.name, "->", im.state_path, '=', im.get_state())
+ ixn = ixn + 1
+ return
+
+
+# Import Code Classes
+from HSP2.om_model_object import ModelObject, ModelVariable, ModelRegister, pre_step_register
+from HSP2.om_sim_timer import SimTimer, step_sim_timer
+#from HSP2.om_equation import *
+from HSP2.om_model_linkage import ModelLinkage, step_model_link
+from HSP2.om_special_action import SpecialAction, step_special_action
+#from HSP2.om_data_matrix import *
+#from HSP2.om_model_broadcast import *
+#from HSP2.om_simple_channel import *
+#from HSP2.om_impoundment import *
+from HSP2.utilities import versions, get_timeseries, expand_timeseries_names, save_timeseries, get_gener_timeseries
+
+def init_om_dicts():
+ """
+ The base dictionaries used to store model object info
+ """
+ op_tokens = ModelObject.make_op_tokens() # this is just to start, layer it is resized to the object needs
+ # Was
+ #op_tokens = Dict.empty(key_type=types.int64, value_type=types.i8[:])
+ model_object_cache = {} # this does not need to be a special Dict as it is not used in numba
+ return op_tokens, model_object_cache
+
+
+def state_load_om_json(state, io_manager, siminfo):
+ # - model objects defined in file named '[model h5 base].json -- this will populate an array of object definitions that will
+ # be loadable by "model_loader_recursive()"
+ # JSON file would be in same path as hdf5
+ hdf5_path = io_manager._input.file_path
+ (fbase, fext) = os.path.splitext(hdf5_path)
+ # see if there is custom json
+ fjson = fbase + ".json"
+ print("Looking for custom om json ", fjson)
+ if (os.path.isfile(fjson)):
+ print("Found local json file", fjson)
+ jfile = open(fjson)
+ json_data = json.load(jfile)
+ # dict.update() combines the arg dict with the base
+ state['model_data'].update(json_data)
+ # merge in the json siminfo data
+ if 'siminfo' in state['model_data'].keys():
+ siminfo.update(state['model_data']['siminfo'])
+ return
+
+def state_load_om_python(state, io_manager, siminfo):
+ # Look for a [hdf5 file base].py file with specific named functions
+ # - function "om_init_model": This function can be defined in the [model h5 base].py file containing things to be done
+ # early in the model loading, like setting up model objects. This file will already have been loaded by the state module,
+ # and will be present in the module variable hsp2_local_py (we should rename to state_local_py?)
+ # - this file may also contain other dynamically redefined functions such as state_step_hydr()
+ # which can contain code that is executed every timestep inside the _hydr_() function
+ # and can literally supply hooks for any desired user customizable code
+ hdf5_path = io_manager._input.file_path
+ (fbase, fext) = os.path.splitext(hdf5_path)
+ # see if there is a code module with custom python
+ print("Looking for custom om loader in python code ", (fbase + ".py"))
+ hsp2_local_py = state['hsp2_local_py']
+ # Load a function from code if it exists
+ if 'om_init_model' in dir(hsp2_local_py):
+ hsp2_local_py.om_init_model(io_manager, siminfo, state['op_tokens'], state['state_paths'], state['state_ix'], state['dict_ix'], state['ts_ix'], state['model_object_cache'])
+
+def om_init_state(state):
+ # this function will check to see if any of the multiple paths to loading - was state_initialize_om()
+ # dynamic operational model objects has been supplied for the model.
+ # Grab globals from state for easy handling
+ op_tokens, model_object_cache = init_om_dicts()
+ state['op_tokens'], state['model_object_cache'], state['model_exec_list'] = op_tokens, model_object_cache, []
+
+
+def state_load_dynamics_om(state, io_manager, siminfo):
+ # this function will check to see if any of the multiple paths to loading
+ # dynamic operational model objects has been supplied for the model.
+ # om_init_state(state) must have been called already
+ # load dynamic coding libraries if defined by user
+ # note: this used to be inside this function, I think that the loaded module should be no problem
+ # occuring within this function call, since this function is also called from another runtime engine
+ # but if things fail post develop-specact-1 pull requests we may investigate here
+ # also, it may be that this should be loaded elsewhere?
+ # comment state_load_om_python() to disable dynamic python
+ state_load_om_python(state, io_manager, siminfo)
+ state_load_om_json(state, io_manager, siminfo)
+ return
+
+def state_om_model_root_object(state, siminfo):
+ # Create the base that everything is added to. this object does nothing except host the rest.
+ if 'model_root_object' not in state.keys():
+ model_root_object = ModelObject("", False, {}, state) # we give this no name so that it does not interfer with child paths like timer, year, etc (i.e. /STATE/year, ...)
+ state['model_root_object'] = model_root_object
+ # set up the timer as the first element
+ if '/STATE/timer' not in state['state_paths'].keys():
+ timer = SimTimer('timer', model_root_object, siminfo)
+ # add base object for the HSP2 domains and other things already added to state so they can be influenced
+ for (seg_name,seg_path) in state['hsp_segments'].items():
+ if (seg_path not in state['model_object_cache'].keys()):
+ # Create an object shell for this
+ ModelObject(seg_name, model_root_object)
+
+
+def state_om_model_run_prep(state, io_manager, siminfo):
+ # insure model base is set
+ state_om_model_root_object(state, siminfo)
+ # now instantiate and link objects
+ # state['model_data'] has alread been prepopulated from json, .py files, hdf5, etc.
+ model_root_object = state['model_root_object']
+ model_loader_recursive(state['model_data'], model_root_object)
+ print("Loaded objects & paths: insures all paths are valid, connects models as inputs")
+ # both state['model_object_cache'] and the model_object_cache property of the ModelObject class def
+ # will hold a global repo for this data this may be redundant? They DO point to the same datset?
+ # since this is a function that accepts state as an argument and these were both set in state_load_dynamics_om
+ # we can assume they are there and functioning
+ model_object_cache = model_root_object.state['model_object_cache']
+ model_path_loader(model_object_cache)
+ # len() will be 1 if we only have a simtimer, but > 1 if we have a river being added
+ model_exec_list = state['model_exec_list']
+ # put all objects in token form for fast runtime execution and sort according to dependency order
+ print("Tokenizing models")
+ if 'ops_data_type' in siminfo.keys():
+ model_root_object.ops_data_type = siminfo['ops_data_type'] # allow override of dat astructure settings
+ model_root_object.state['op_tokens'] = ModelObject.make_op_tokens(max(model_root_object.state['state_ix'].keys()) + 1)
+ model_tokenizer_recursive(model_root_object, model_object_cache, model_exec_list)
+ op_tokens = model_root_object.state['op_tokens']
+ #print("op_tokens afer tokenizing", op_tokens)
+ # model_exec_list is the ordered list of component operations
+ #print("model_exec_list(", len(model_exec_list),"items):", model_exec_list)
+ # This is used to stash the model_exec_list in the dict_ix, this might be slow, need to verify.
+ # the resulting set of objects is returned.
+ state['state_step_om'] = 'disabled'
+ state['model_object_cache'] = model_object_cache
+ state['model_exec_list'] = np.asarray(model_exec_list, dtype="i8")
+ if model_root_object.ops_data_type == 'ndarray':
+ state_keyvals = np.asarray(zeros(max(model_root_object.state['state_ix'].keys()) + 1), dtype="float64")
+ for ix, val in model_root_object.state['state_ix'].items():
+ state_keyvals[ix] = val
+ state['state_ix'] = state_keyvals
+ else:
+ state['state_ix'] = model_root_object.state['state_ix']
+ state['op_tokens'] = op_tokens # is this superfluous since the root object got op_tokens from state?
+ if len(op_tokens) > 0:
+ state['state_step_om'] = 'enabled'
+
+ #print("op_tokens is type", type(op_tokens))
+ #print("state_ix is type", type(state['state_ix']))
+ #print("op_tokens final", op_tokens)
+
+ print("Operational model status:", state['state_step_om'])
+ if len(model_exec_list) > 0:
+ print("op_tokens has", len(op_tokens),"elements, with ", len(model_exec_list),"executable elements")
+ return
+
+# model class reader
+# get model class to guess object type in this lib
+# the parent object must be known
+def model_class_loader(model_name, model_props, container = False):
+ # todo: check first to see if the model_name is an attribute on the container
+ # Use: if hasattr(container, model_name):
+ # if so, we set the value on the container, if not, we create a new subcomp on the container
+ if model_props == None:
+ return False
+ if type(model_props) is str:
+ if is_float_digit(model_props):
+ model_object = ModelVariable(model_name, container, float(model_props) )
+ return model_object
+ else:
+ return False
+ elif type(model_props) is dict:
+ object_class = model_props.get('object_class')
+ if object_class == None:
+ # return as this is likely an attribute that is used for the containing class as attribute
+ # and is handled by the container
+ # todo: we may want to handle this here? Or should this be a method on the class?
+ # Use: if hasattr(container, model_name):
+ return False
+ model_object = False
+ # Note: this routine uses the ".get()" method of the dict class type
+ # for attributes to pass in.
+ # ".get()" will return NoValue if it does not exist or the value.
+ if object_class == 'Equation':
+ model_object = Equation(model_props.get('name'), container, model_props )
+ #remove_used_keys(model_props,
+ elif object_class == 'SimpleChannel':
+ model_object = SimpleChannel(model_props.get('name'), container, model_props )
+ elif object_class == 'Impoundment':
+ model_object = Impoundment(model_props.get('name'), container, model_props )
+ elif object_class == 'Constant':
+ model_object = ModelVariable(model_props.get('name'), container, model_props.get('value') )
+ elif ( object_class.lower() == 'datamatrix'):
+ # add a matrix with the data, then add a matrix accessor for each required variable
+ has_props = DataMatrix.check_properties(model_props)
+ if has_props == False:
+ print("Matrix object must have", DataMatrix.required_properties())
+ return False
+ # create it
+ model_object = DataMatrix(model_props.get('name'), container, model_props)
+ elif object_class == 'ModelBroadcast':
+ # add a matrix with the data, then add a matrix accessor for each required variable
+ has_props = ModelBroadcast.check_properties(model_props)
+ if has_props == False:
+ print("ModelBroadcast object must have", ModelBroadcast.required_properties())
+ return False
+ # create it
+ model_object = ModelBroadcast(model_props.get('name'), container, model_props)
+ elif object_class == 'MicroWatershedModel':
+ # add a matrix with the data, then add a matrix accessor for each required variable
+ has_props = MicroWatershedModel.check_properties(model_props)
+ if has_props == False:
+ print("MicroWatershedModel object must have", MicroWatershedModel.required_properties())
+ return False
+ # create it
+ model_object = DataMatrix(model_props.get('name'), container, model_props)
+ elif object_class == 'ModelLinkage':
+ model_object = ModelLinkage(model_props.get('name'), container, model_props)
+ elif object_class == 'SpecialAction':
+ model_object = SpecialAction(model_props.get('name'), container, model_props)
+ else:
+ #print("Loading", model_props.get('name'), "with object_class", object_class,"as ModelObject")
+ model_object = ModelObject(model_props.get('name'), container, model_props)
+ # one way to insure no class attributes get parsed as sub-comps is:
+ # model_object.remove_used_keys()
+ if len(model_object.model_props_parsed) == 0:
+ # attach these to the object for posterity
+ model_object.model_props_parsed = model_props
+ # better yet to just NOT send those attributes as typed object_class arrays, instead just name : value
+ return model_object
+
+def model_class_translate(model_props, object_class):
+ # make adjustments to non-standard items
+ # this might better be moved to methods on the class handlers
+ if object_class == 'hydroImpoundment':
+ # special handling of matrix/storage_stage_area column
+ # we need to test to see if the storage table has been renamed
+ # make table from matrix or storage_stage_area
+ # then make accessors from
+ storage_stage_area = model_props.get('storage_stage_area')
+ matrix = model_props.get('matrix')
+ if ( (storage_stage_area == None) and (matrix != None)):
+ model_props['storage_stage_area'] = matrix
+ del model_props['matrix']
+ if object_class == 'broadCastObject':
+ model_props['object_class'] = 'ModelBroadcast'
+ model_props['broadcast_channel'] = model_props['broadcast_class']
+ if object_class == 'USGSChannelGeomObject_sub':
+ model_props['object_class'] = 'SimpleChannel'
+ print("Handling USGSChannelGeomObject_sub as SimpleChannel")
+ if object_class == 'hydroImpoundment':
+ model_props['object_class'] = 'Impoundment'
+ print("Handling hydroImpoundment as Impoundment")
+ if object_class == 'hydroImpSmall':
+ model_props['object_class'] = 'Impoundment'
+ print("Handling hydroImpSmall as Impoundment")
+ # now handle disabled classes - this is temporary to prevent having to comment and uncomment
+ disabled_classes = {'SimpleChannel', 'Impoundment', 'DataMatrix', 'dataMatrix'}
+ if model_props['object_class'] in disabled_classes:
+ print("Disabling class", model_props['object_class'], 'rendering as ModelObject')
+ model_props['object_class'] = 'ModelObject'
+
+def model_loader_recursive(model_data, container):
+ k_list = model_data.keys()
+ object_names = dict.fromkeys(k_list , 1)
+ if type(object_names) is not dict:
+ return False
+ for object_name in object_names:
+ #print("Handling", object_name)
+ if object_name in {'name', 'object_class', 'id', 'value', 'default'}:
+ # we should ask the class what properties are part of the class and also skips these
+ # therefore, we can assume that anything else must be a child object that needs to
+ # be handled first -- but how to do this?
+ continue
+ model_props = model_data[object_name]
+ if type(model_props) is not dict:
+ # this is a constant, the loader is built to handle this, but this causes errors with
+ # properties on the class that are expected so we just skip and trust that all constants
+ # are formally declared as type Constant
+ continue
+ if type(model_props) is dict:
+ if not ('object_class' in model_props):
+ # this is either a class attribute or an un-handleable meta-data
+ # if the class atttribute exists, we should pass it to container to load
+ #print("Skipping un-typed", object_name)
+ continue
+ #print("Translating", object_name)
+ # this is a kludge, but can be important
+ object_class = model_props['object_class']
+ model_class_translate(model_props, object_class)
+ # now we either have a constant (key and value), or a
+ # fully defined object. Either one should work OK.
+ #print("Trying to load", object_name)
+ model_object = model_class_loader(object_name, model_props, container)
+ if model_object == False:
+ print("Could not load", object_name)
+ continue # not handled, but for now we will continue, tho later we should bail?
+ # now for container type objects, go through its properties and handle
+ if type(model_props) is dict:
+ model_loader_recursive(model_props, model_object)
+
+def model_path_loader(model_object_cache):
+ k_list = model_object_cache.keys()
+ model_names = dict.fromkeys(k_list , 1)
+ for model_name in model_names:
+ #print("Loading paths for", model_name)
+ model_object = model_object_cache[model_name]
+ model_object.find_paths()
+
+
+def model_tokenizer_recursive(model_object, model_object_cache, model_exec_list, model_touch_list = None):
+ """
+ Given a root model_object, trace the inputs to load things in order
+ Store this order in model_exec_list
+ Note: All ordering is as-needed organic, except Broadcasts
+ - read from children is completed after all other inputs
+ - read from parent is completed before all other inputs
+ - could this be accomplished by more sophisticated handling of read
+ broadcasts?
+ - When loading a read broadcast, can we iterate through items
+ that are sending to that broadcast?
+ - Or is it better to let it as it is,
+ """
+ if model_touch_list is None:
+ model_touch_list = []
+ #print("Handling", model_object.name, " ", model_object.state_path)
+ if model_object.ix in model_exec_list:
+ return
+ if model_object.ix in model_touch_list:
+ #print("Already touched", model_object.name, model_object.ix, model_object.state_path)
+ return
+ # record as having been called, and will ultimately return, to prevent recursions
+ model_touch_list.append(model_object.ix)
+ k_list = model_object.inputs.keys()
+ input_names = dict.fromkeys(k_list , 1)
+ if type(input_names) is not dict:
+ return
+ # isolate broadcasts, and sort out -- what happens if an equation references a broadcast var?
+ # is this a limitation of treating all children as inputs?
+ # alternative, leave broadcasts organic, but load children first?
+ # children first, then local sub-comps is old method? old method:
+ # - read parent broadcasts
+ # - get inputs (essentially, linked vars)
+ # - send child broadcasts (will send current step parent reads, last step local proc data)
+ # - execute children
+ # - execute local sub-comps
+ for input_name in input_names:
+ #print("Checking input", input_name)
+ input_path = model_object.inputs[input_name]
+ if input_path in model_object_cache.keys():
+ input_object = model_object_cache[input_path]
+ model_tokenizer_recursive(input_object, model_object_cache, model_exec_list, model_touch_list)
+ else:
+ if input_path in model_object.state_paths.keys():
+ # this is a valid state reference without an object
+ # thus, it is likely part of internals that are manually added
+ # which should be fine. tho perhaps we should have an object for these too.
+ continue
+ print("Problem loading input", input_name, "input_path", input_path, "not in model_object_cache.keys()")
+ return
+ # now after tokenizing all inputs this should be OK to tokenize
+ model_object.add_op_tokens()
+ if model_object.optype in ModelObject.runnables:
+ model_exec_list.append(model_object.ix)
+
+
+def model_order_recursive(model_object, model_object_cache, model_exec_list, model_touch_list = None):
+ """
+ Given a root model_object, trace the inputs to load things in order
+ Store this order in model_exec_list
+ Note: All ordering is as-needed organic, except Broadcasts
+ - read from children is completed after all other inputs
+ - read from parent is completed before all other inputs
+ - could this be accomplished by more sophisticated handling of read
+ broadcasts?
+ - When loading a read broadcast, can we iterate through items
+ that are sending to that broadcast?
+ - Or is it better to let it as it is,
+ """
+ if model_touch_list is None:
+ model_touch_list = []
+ if model_object.ix in model_exec_list:
+ return
+ if model_object.ix in model_touch_list:
+ #print("Already touched", model_object.name, model_object.ix, model_object.state_path)
+ return
+ # record as having been called, and will ultimately return, to prevent recursions
+ model_touch_list.append(model_object.ix)
+ k_list = model_object.inputs.keys()
+ input_names = dict.fromkeys(k_list , 1)
+ if type(input_names) is not dict:
+ return
+ # isolate broadcasts, and sort out -- what happens if an equation references a broadcast var?
+ # is this a limitation of treating all children as inputs?
+ # alternative, leave broadcasts organic, but load children first?
+ # children first, then local sub-comps is old method? old method:
+ # - read parent broadcasts
+ # - get inputs (essentially, linked vars)
+ # - send child broadcasts (will send current step parent reads, last step local proc data)
+ # - execute children
+ # - execute local sub-comps
+ for input_name in input_names:
+ #print("Checking input", input_name)
+ input_path = model_object.inputs[input_name]
+ if input_path in model_object_cache.keys():
+ input_object = model_object_cache[input_path]
+ model_order_recursive(input_object, model_object_cache, model_exec_list, model_touch_list)
+ else:
+ if input_path in model_object.state_paths.keys():
+ # this is a valid state reference without an object
+ # thus, it is likely part of internals that are manually added
+ # which should be fine. tho perhaps we should have an object for these too.
+ continue
+ print("Problem loading input", input_name, "input_path", input_path, "not in model_object_cache.keys()")
+ return
+ # now after loading input dependencies, add this to list
+ model_exec_list.append(model_object.ix)
+
+def model_domain_dependencies(state, domain, ep_list):
+ """
+ Given an hdf5 style path to a domain, and a list of variable endpoints in that domain,
+ Find all model elements that influence the endpoints state
+ Returns them as a sorted list of index values suitable as a model_exec_list
+ """
+ mello = []
+ for ep in ep_list:
+ mel = []
+ mtl = []
+ # if the given element is NOT in model_object_cache, then nothing is acting on it, so we return empty list
+ if (domain + '/' + ep) in state['model_object_cache'].keys():
+ endpoint = state['model_object_cache'][domain + '/' + ep]
+ model_order_recursive(endpoint, state['model_object_cache'], mel, mtl)
+ mello = mello + mel
+
+ return mello
+
+def save_object_ts(io_manager, siminfo, op_tokens, ts_ix, ts):
+ # Decide on using from utilities.py:
+ # - save_timeseries(io_manager, ts, savedict, siminfo, saveall, operation, segment, activity, compress=True)
+ # Or, skip the save_timeseries wrapper and call write_ts() directly in io.py:
+ # write_ts(self, data_frame:pd.DataFrame, save_columns: List[str], category:Category, operation:Union[str,None]=None, segment:Union[str,None]=None, activity:Union[str,None]=None)
+ # see line 317 in utilities.py for use example of write_ts()
+ x = 0 # dummy
+ return
+
+@njit
+def iterate_models(model_exec_list, op_tokens, state_ix, dict_ix, ts_ix, steps, dstep = -1):
+ checksum = 0.0
+ for step in range(steps):
+ pre_step_model(model_exec_list, op_tokens, state_ix, dict_ix, ts_ix, step)
+ step_model(model_exec_list, op_tokens, state_ix, dict_ix, ts_ix, step)
+ #print("Steps completed", step)
+ return checksum
+
+@njit
+def pre_step_model(model_exec_list, op_tokens, state_ix, dict_ix, ts_ix, step):
+ for i in model_exec_list:
+ if op_tokens[i][0] == 12:
+ # register type data (like broadcast accumulators)
+ pre_step_register(op_tokens[i], state_ix)
+ return
+
+@njit
+def step_model(model_exec_list, op_tokens, state_ix, dict_ix, ts_ix, step):
+ for i in model_exec_list:
+ step_one(op_tokens, op_tokens[i], state_ix, dict_ix, ts_ix, step, 0)
+ return
+
+
+
+@njit
+def step_one(op_tokens, ops, state_ix, dict_ix, ts_ix, step, debug = 0):
+ # op_tokens is passed in for ops like matrices that have lookups from other
+ # locations. All others rely only on ops
+ # todo: decide if all step_[class() functions should set value in state_ix instead of returning value?
+ if debug > 0:
+ print("DEBUG: Operator ID", ops[1], "is op type", ops[0])
+ if ops[0] == 1:
+ pass #step_equation(ops, state_ix)
+ elif ops[0] == 2:
+ # todo: this should be moved into a single function,
+ # with the conforming name step_matrix(op_tokens, ops, state_ix, dict_ix)
+ if (ops[1] == ops[2]):
+ if debug == 1:
+ print("DEBUG: Calling exec_tbl_values", ops)
+ # this insures a matrix with variables in it is up to date
+ # only need to do this if the matrix data and matrix config are on same object
+ # otherwise, the matrix data is an input and has already been evaluated
+ pass #exec_tbl_values(ops, state_ix, dict_ix)
+ if (ops[3] > 0):
+ # this evaluates a single value from a matrix if the matrix is configured to do so.
+ if debug == 1:
+ print("DEBUG: Calling exec_tbl_eval", ops)
+ pass #exec_tbl_eval(op_tokens, ops, state_ix, dict_ix)
+ elif ops[0] == 3:
+ step_model_link(ops, state_ix, ts_ix, step)
+ elif ops[0] == 4:
+ pass
+ elif ops[0] == 5:
+ step_sim_timer(ops, state_ix, dict_ix, ts_ix, step)
+ elif ops[0] == 9:
+ pass
+ elif ops[0] == 13:
+ pass #step_simple_channel(ops, state_ix, dict_ix, step)
+ # Op 100 is Basic ACTION in Special Actions
+ elif ops[0] == 100:
+ step_special_action(ops, state_ix, dict_ix, step)
+ return
+
+@njit
+def step_model_test(model_exec_list, op_tokens, state_ix, dict_ix, ts_ix, step, debug_step = -1):
+ for i in model_exec_list:
+ ops = op_tokens[i]
+ val = 0
+ if (step == debug_step):
+ print("Exec'ing step ", step, " model ID", i)
+ # op_tokens is passed in for ops like matrices that have lookups from other
+ # locations. All others rely only on ops
+ step_one(op_tokens, op_tokens[i], state_ix, dict_ix, ts_ix, step, 0)
+ return
+
+@njit
+def step_model_pcode(model_exec_list, op_tokens, state_info, state_paths, state_ix, dict_ix, ts_ix, step):
+ '''
+ This routine includes support for dynamically loaded python code which is powerful but slow
+ This is not yet implemented anywhere, just an idea. But in theory it would allow easy switching between
+ the faster runtime without dynamic code if the user did not request it.
+ At minimum, this could be used to more efficiently enable/disable this feature for testing by simply calling
+ a separate routine.
+ - to do so we would need to add state_paths to the variables passed to step_model which should be OK?
+ '''
+ hydr_ix = hydr_get_ix(state_ix, state_paths, state_info['domain']) # could be done more efficiently, once per model run
+ state_step_hydr(state_info, state_paths, state_ix, dict_ix, ts_ix, hydr_ix, step)
+ val = 0
+ for i in model_exec_list:
+ step_one(op_tokens, op_tokens[i], state_ix, dict_ix, ts_ix, step, 0)
+ return
+
+@njit
+def post_step_model(model_exec_list, op_tokens, state_ix, dict_ix, ts_ix, step):
+ return
+
+def step_object(thisobject, step):
+ # this calls the step for a given model object and timestep
+ # this is a workaround since the object method ModelObject.step() fails to find the step_one() function ?
+ step_one(thisobject.op_tokens, thisobject.op_tokens[thisobject.ix], thisobject.state_ix, thisobject.dict_ix, thisobject.ts_ix, step)
+
+
+@njit
+def pre_step_test(model_exec_list, op_tokens, state_ix, dict_ix, ts_ix, step):
+ for i in model_exec_list:
+ ops = op_tokens[i]
+ #for i in model_exec_list:
+ # op = op_tokens[i]
+ if ops[0] == 12:
+ # register type data (like broadcast accumulators)
+ pre_step_register(ops, state_ix)
+ #continue
+ #elif ops[0] == 1:
+ # # register type data (like broadcast accumulators)
+ # continue
+ return
+
+@njit
+def iterate_perf(model_exec_list, op_tokens, state_ix, dict_ix, ts_ix, steps, debug_step = -1):
+ checksum = 0.0
+ for step in range(steps):
+ pre_step_test(model_exec_list, op_tokens, state_ix, dict_ix, ts_ix, step)
+ #step_model_test(model_exec_list, op_tokens, state_ix, dict_ix, ts_ix, step, debug_step)
+ #print("Steps completed", step)
+ return checksum
+
+def time_perf(model_exec_list, op_tokens, state_ix, dict_ix, ts_ix, steps):
+ start = time.time()
+ iterate_perf(model_exec_list, op_tokens, state_ix, dict_ix, ts_ix, steps)
+ end = time.time()
+ print(len(model_exec_list), "components iterated over", siminfo['steps'], "time steps took" , end - start, "seconds")
diff --git a/HSP2/om_model_linkage.py b/HSP2/om_model_linkage.py
new file mode 100644
index 00000000..ecae071c
--- /dev/null
+++ b/HSP2/om_model_linkage.py
@@ -0,0 +1,128 @@
+"""
+The class ModelLinkage is used to translate copy data from one state location to another.
+It is also used to make an implicit parent child link to insure that an object is loaded
+during a model simulation.
+"""
+from HSP2.state import *
+from HSP2.om import *
+from HSP2.om_model_object import ModelObject
+from numba import njit
+class ModelLinkage(ModelObject):
+ def __init__(self, name, container = False, model_props = None):
+ if model_props is None:
+ model_props = {}
+ super(ModelLinkage, self).__init__(name, container, model_props)
+ # ModelLinkage copies a values from right to left
+ # right_path: is the data source for the link
+ # left_path: is the destination of the link
+ # - is implicit in types 1-3, i.e., the ModelLinkage object path itself is the left_path
+ # - left_path parameter is only needed for pushes (type 4 and 5)
+ # - the push is functionally equivalent to a pull whose path resolves to the specified left_path
+ # - but the push allows the potential for multiple objects to set a single state
+ # This can be dangerous or difficult to debug, but essential to replicate old HSPF behaviour
+ # especially in the case of If/Then type structures.
+ # it is also useful for the broadcast objects, see om_model_broadcast for those
+ # link_type: 1 - local parent-child, 2 - local property link (state data), 3 - remote linkage (ts data only), 4 - push to accumulator (like a hub), 5 - overwrite remote value
+ self.optype = 3 # 0 - shell object, 1 - equation, 2 - datamatrix, 3 - ModelLinkage, 4 -
+ if container == False:
+ # this is required
+ print("Error: a link must have a container object to serve as the destination")
+ return False
+ self.right_path = self.handle_prop(model_props, 'right_path')
+ self.link_type = self.handle_prop(model_props, 'link_type', False, 0)
+ self.left_path = self.handle_prop(model_props, 'left_path')
+
+ if self.left_path == False:
+ # self.state_path gets set when creating at the parent level
+ self.left_path = self.state_path
+ if (self.link_type == 0):
+ # if this is a simple input we remove the object from the model_object_cache, and pass back to parent as an input
+ del self.state['model_object_cache'][self.state_path]
+ del self.state['state_ix'][self.ix]
+ container.add_input(self.name, self.right_path)
+ # this breaks for some reason, doesn't like the input name being different than the variable path ending?
+ # maybe because we should be adding the input to the container, not the self?
+ self.add_input(self.right_path, self.right_path)
+
+ def handle_prop(self, model_props, prop_name, strict = False, default_value = None ):
+ # parent method handles most cases, but subclass handles special situations.
+ prop_val = super().handle_prop(model_props, prop_name, strict, default_value)
+ if ( (prop_name == 'right_path') and (prop_val == None) or (prop_val == '')):
+ raise Exception("right_path cannot be empty. Object creation halted. Path to object with error is " + self.state_path)
+ if ( (prop_name == 'right_path')):
+ # check for special keyword [parent]
+ pre_val = prop_val
+ prop_val.replace("[parent]", self.container.state_path)
+ #print("Changed ", pre_val, " to ", prop_val)
+ return prop_val
+
+ @staticmethod
+ def required_properties():
+ # returns a list or minimum properties to create.
+ # see ModelVariable below for how to call this in a sub-class
+ # note:
+ # req_props = super(DataMatrix, DataMatrix).required_properties()
+ req_props = ['name', 'right_path']
+ return req_props
+
+ def find_paths(self):
+ # this should be needed if this is a PUSH link_type = 4 or 5
+ super().find_paths()
+ self.paths_found = False # override parent setting until we verify everything
+ # do we need to do this, or just trust it exists?
+ #self.insure_path(self, self.right_path)
+ # the left path, if this is type 4 or 5, is a push, so we must require it
+ if ( (self.link_type == 4) or (self.link_type == 5) or (self.link_type == 6) ):
+ self.insure_path(self.left_path)
+ self.paths_found = True
+ return
+
+ def tokenize(self):
+ super().tokenize()
+ # - if this is a data property link then we add op codes to do a copy of data from one state address to another
+ # - if this is simply a parent-child connection, we do not render op-codes, but we do use this for assigning
+ # - execution hierarchy
+ #print("Linkage/link_type ", self.name, self.link_type,"created with params", self.model_props_parsed)
+ if self.link_type in (2, 3):
+ src_ix = get_state_ix(self.state['state_ix'], self.state['state_paths'], self.right_path)
+ if not (src_ix == False):
+ self.ops = self.ops + [src_ix, self.link_type]
+ else:
+ print("Error: link ", self.name, "does not have a valid source path")
+ #print(self.name,"tokenize() result", self.ops)
+ if (self.link_type == 4) or (self.link_type == 5) or (self.link_type == 6):
+ # we push to the remote path in this one
+ left_ix = get_state_ix(self.state['state_ix'], self.state['state_paths'], self.left_path)
+ right_ix = get_state_ix(self.state['state_ix'], self.state['state_paths'], self.right_path)
+ if (left_ix != False) and (right_ix != False):
+ self.ops = self.ops + [left_ix, self.link_type, right_ix]
+ else:
+ print("Error: link ", self.name, "does not have valid paths", "(left = ", self.left_path, left_ix, "right = ", self.right_path, right_ix, ")")
+ #print("tokenize() result", self.ops)
+
+# Function for use during model simulations of tokenized objects
+@njit
+def step_model_link(op_token, state_ix, ts_ix, step):
+ #if step == 2:
+ #print("step_model_link() called at step 2 with op_token=", op_token)
+ if op_token[3] == 1:
+ return True
+ elif op_token[3] == 2:
+ state_ix[op_token[1]] = state_ix[op_token[2]]
+ return True
+ elif op_token[3] == 3:
+ # read from ts variable TBD
+ # state_ix[op_token[1]] = ts_ix[op_token[2]][step]
+ return True
+ elif op_token[3] == 4:
+ # add value in local state to the remote broadcast hub+register state
+ state_ix[op_token[2]] = state_ix[op_token[2]] + state_ix[op_token[4]]
+ return True
+ elif op_token[3] == 5:
+ # overwrite remote variable state with value in another paths state
+ state_ix[op_token[2]] = state_ix[op_token[4]]
+ return True
+ elif op_token[3] == 6:
+ # set value in a timerseries
+ ts_ix[op_token[2]][step] = state_ix[op_token[4]]
+ return True
diff --git a/HSP2/om_model_object.py b/HSP2/om_model_object.py
new file mode 100644
index 00000000..29149de7
--- /dev/null
+++ b/HSP2/om_model_object.py
@@ -0,0 +1,441 @@
+"""
+The class ModelObject is the base class upon which all other dynamic model objects are built on.
+It handles all Dict management functions, but provides for no runtime execution of it's own.
+All runtime exec is done by child classes.
+"""
+from HSP2.state import set_state, get_state_ix
+from numba.typed import Dict
+from HSP2.om import get_exec_order, is_float_digit
+from pandas import Series, DataFrame, concat, HDFStore, set_option, to_numeric
+from pandas import Timestamp, Timedelta, read_hdf, read_csv
+from numpy import pad, asarray, zeros, int32
+from numba import njit, types
+
+class ModelObject:
+ max_token_length = 64 # limit on complexity of tokenized objects since op_tokens must be fixed dimensions for numba
+ runnables = [1,2,3,5,6,8,9,10,11,12,13,14,15, 100] # runnable components important for optimization
+ ops_data_type = 'ndarray' # options are ndarray or Dict - Dict appears slower, but unsure of the cause, so keep as option.
+
+ def __init__(self, name, container = False, model_props = None, state = None):
+ self.name = name
+ if model_props is None:
+ model_props = {}
+ self.container = container # will be a link to another object
+ if (state == None):
+ # we must verify that we have a properly formatted state Dictionary, or that our parent does.
+ if self.container == False:
+ raise Exception("Error: State dictionary must be passed to root object. " + name + " cannot be created. See state::init_state_dicts()")
+ else:
+ state = self.container.state
+ self.state = state # make a copy here. is this efficient?
+ # Local properties
+ self.model_props_parsed = {} # a place to stash parse record for debugging
+ self.log_path = "" # Ex: "/RESULTS/RCHRES_001/SPECL"
+ self.attribute_path = "" #
+ if (hasattr(self,'state_path') == False):
+ # if the state_path has already been set, we accept it.
+ # this allows sub-classes to override the standard path guessing approach.
+ self.state_path = "" # Ex: "/STATE/RCHRES_001" # the pointer to this object state
+ self.inputs = {} # associative array with key=local_variable_name, value=hdf5_path Ex: [ 'Qin' : '/STATE/RCHRES_001/IVOL' ]
+ self.inputs_ix = {} # associative array with key=local_variable_name, value=state_ix integer key
+ self.ix = False
+ self.paths_found = False # this should be False at start
+ self.default_value = 0.0
+ self.ops = []
+ self.optype = 0 # OpTypes are as follows:
+ # 0 - model object, 1 - equation, 2 - datamatrix, 3 - input/ModelLinkage,
+ # 4 - broadcastChannel, 5 - SimTimer, 6 - Conditional, 7 - ModelVariable (numeric),
+ # 8 - matrix accessor, 9 - MicroWatershedModel, 10 - MicroWatershedNetwork, 11 - ModelTimeseries,
+ # 12 - ModelRegister, 13 - SimpleChannel, 14 - SimpleImpoundment, 15 - FlowBy
+ # 16 - ModelConstant
+ # 100 - SpecialAction, 101 - UVNAME, 102 - UVQUAN, 103 - DISTRB
+ self.register_path() # note this registers the path AND stores the object in model_object_cache
+ self.parse_model_props(model_props)
+
+ @staticmethod
+ def required_properties():
+ # returns a list or minimum properties to create.
+ # see ModelVariable below for how to call this in a sub-class
+ # note:
+ # req_props = super(DataMatrix, DataMatrix).required_properties()
+ req_props = ['name']
+ return req_props
+
+ @staticmethod
+ def make_op_tokens(num_ops = 5000):
+ if (ModelObject.ops_data_type == 'ndarray'):
+ op_tokens = int32(zeros((num_ops,64))) # was Dict.empty(key_type=types.int64, value_type=types.i8[:])
+ else:
+ op_tokens = Dict.empty(key_type=types.int64, value_type=types.i8[:])
+ return op_tokens
+
+ @staticmethod
+ def runnable_op_list(op_tokens, meo, debug = False):
+ # only return those objects that do something at runtime
+ rmeo = []
+ run_ops = {}
+ for ops in op_tokens:
+ # the base class defines the type of objects that are runnable (i.e. have a step() method)
+ if ops[0] in ModelObject.runnables:
+ run_ops[ops[1]] = ops
+ if debug == True:
+ print("Found runnable", ops[1], "type", ops[0])
+ for ix in meo:
+ if ix in run_ops.keys():
+ rmeo.append(ix)
+ rmeo = asarray(rmeo, dtype="i8")
+ return rmeo
+
+ @staticmethod
+ def model_format_ops(ops):
+ if (ModelObject.ops_data_type == 'ndarray'):
+ ops = pad(ops,(0,ModelObject.max_token_length))[0:ModelObject.max_token_length]
+ else:
+ ops = asarray(ops, dtype="i8")
+ return ops
+
+ def format_ops(self):
+ # this can be sub-classed if needed, but should not be since it is based on the ops_data_type
+ # See ModelObject.model_format_ops()
+ return ModelObject.model_format_ops(self.ops)
+
+ @classmethod
+ def check_properties(cls, model_props):
+ # this is for pre-screening properties for validity in model creation routines
+ # returns True or False and can be as simple as checking the list of required_properties
+ # or a more detailed examination of suitability of what those properties contain
+ req_props = cls.required_properties()
+ matching_props = set(model_props).intersection(set(req_props))
+ if len(matching_props) < len(req_props):
+ return False
+ return True
+
+ def handle_inputs(self, model_props):
+ if 'inputs' in model_props.keys():
+ for i_pair in model_props['inputs']:
+ i_name = i_pair[0]
+ i_target = i_pair[1]
+ i_target.replace("[parent]", self.container.state_path)
+ self.add_input(i_name, i_target)
+
+ def handle_prop(self, model_props, prop_name, strict = False, default_value = None ):
+ # this checks to see if the prop is in dict with value form, or just a value
+ # strict = True causes an exception if property is missing from model_props dict
+ prop_val = model_props.get(prop_name)
+ if type(prop_val) == list: # this doesn't work, but nothing gets passed in like this? Except broadcast params, but they are handled in the sub-class
+ prop_val = prop_val
+ elif type(prop_val) == dict:
+ prop_val = prop_val.get('value')
+ if strict and (prop_val == None):
+ raise Exception("Cannot find property " + prop_name + " in properties passed to "+ self.name + " and strict = True. Object creation halted. Path to object with error is " + self.state_path)
+ if (prop_val == None) and not (default_value == None):
+ prop_val = default_value
+ return prop_val
+
+ def parse_model_props(self, model_props, strict = False ):
+ # sub-classes will allow an create argument "model_props" and handle them here.
+ # - subclasses should insure that they call super().parse_model_props() or include all code below
+ # see also: handle_prop(), which will be called y parse_model_props
+ # for all attributes supported by the class
+ # this base object only handles inputs
+ self.handle_inputs(model_props)
+ self.model_props_parsed = model_props
+ return True
+
+ def set_state(self, set_value):
+ var_ix = set_state(self.state['state_ix'], self.state['state_paths'], self.state_path, set_value)
+ return var_ix
+
+ def load_state_dicts(self, op_tokens, state_paths, state_ix, dict_ix):
+ self.state['op_tokens'] = op_tokens
+ self.state['state_paths'] = state_paths
+ self.state['state_ix'] = state_ix
+ self.state['dict_ix'] = dict_ix
+
+ def save_object_hdf(self, hdfname, overwrite = False ):
+ # save the object in the full hdf5 path
+ # if overwrite = True replace this and all children, otherwise, just save this.
+ # note: "with" statement helps prevent unclosed resources, see: https://www.geeksforgeeks.org/with-statement-in-python/
+ with HDFStore(hdfname, mode = 'a') as store:
+ dummy_var = True
+
+ def make_paths(self, base_path = False):
+ if base_path == False: # we are NOT forcing paths
+ if not (self.container == False):
+ self.state_path = self.container.state_path + "/" + str(self.name)
+ self.attribute_path = self.container.attribute_path + "/" + str(self.name)
+ elif self.name == "":
+ self.state_path = "/STATE"
+ self.attribute_path = "/OBJECTS"
+ else:
+ self.state_path = "/STATE/" + str(self.name)
+ self.attribute_path = "/OBJECTS/" + str(self.name)
+ else:
+ # base_path is a Dict with state_path and attribute_path set
+ self.state_path = base_path['STATE'] + self.name
+ self.attribute_path = base_path['OBJECTS'] + self.name
+ return self.state_path
+
+ def get_state(self, var_name = False):
+ if var_name == False:
+ return self.state['state_ix'][self.ix]
+ else:
+ var_path = self.find_var_path(var_name)
+ var_ix = get_state_ix(self.state['state_ix'], self.state['state_paths'], var_path)
+ if (var_ix == False):
+ return False
+ return self.state['state_ix'][var_ix]
+
+ def get_exec_order(self, var_name = False):
+ if var_name == False:
+ var_ix = self.ix
+ else:
+ var_path = self.find_var_path(var_name)
+ var_ix = get_state_ix(self.state['state_ix'], self.state['state_paths'], var_path)
+ exec_order = get_exec_order(self.state['model_exec_list'],var_ix)
+ return exec_order
+
+ def get_object(self, var_name = False):
+ if var_name == False:
+ return self.state['model_object_cache'][self.state_path]
+ else:
+ var_path = self.find_var_path(var_name)
+ return self.state['model_object_cache'][var_path]
+
+
+ def find_var_path(self, var_name, local_only = False):
+ # check local inputs for name
+ if var_name in self.inputs.keys():
+ return self.inputs[var_name]
+ if local_only:
+ return False # we are limiting the scope, so just return
+ # check parent for name
+ if not (self.container == False):
+ return self.container.find_var_path(var_name)
+ # check for root state vars STATE + var_name
+ if ("/STATE/" + var_name) in self.state['state_paths'].keys():
+ #return self.state['state_paths'][("/STATE/" + var_name)]
+ return ("/STATE/" + var_name)
+ # check for root state vars
+ if var_name in self.state['state_paths'].keys():
+ #return self.state['state_paths'][var_name]
+ return var_name
+ return False
+
+ def constant_or_path(self, keyname, keyval, trust = False):
+ if is_float_digit(keyval):
+ # we are given a constant value, not a variable reference
+ k = ModelVariable(keyname, self, float(keyval))
+ kix = k.ix
+ else:
+ kix = self.add_input(keyname, keyval, 2, trust)
+ return kix
+
+ def register_path(self):
+ # initialize the path variable if not already set
+ if self.state_path == '':
+ self.make_paths()
+ self.ix = set_state(self.state['state_ix'], self.state['state_paths'], self.state_path, self.default_value)
+ # store object in model_object_cache
+ if not (self.state_path in self.state['model_object_cache'].keys()):
+ self.state['model_object_cache'][self.state_path] = self
+ # this should check to see if this object has a parent, and if so, register the name on the parent
+ # default is as a child object.
+ if not (self.container == False):
+ # since this is a request to actually create a new path, we instruct trust = True as last argument
+ return self.container.add_input(self.name, self.state_path, 1, True)
+ return self.ix
+
+ def add_input(self, var_name, var_path, input_type = 1, trust = False):
+ # this will add to the inputs, but also insure that this
+ # requested path gets added to the state/exec stack via an input object if it does
+ # not already exist.
+ # - var_name = the local name for this linked entity/attribute
+ # - var_path = the full path of the entity/attribute we are linking to
+ # - input types: 1: parent-child link, 2: state property link, 3: timeseries object property link
+ # - trust = False means fail if the path does not already exist, True means assume it will be OK which is bad policy, except for the case where the path points to an existing location
+ # do we have a path here already or can we find on the parent?
+ # how do we check if this is a path already, in which case we trust it?
+ # todo: we should be able to alias a var_name to a var_path, for example
+ # calling add_input('movar', 'month', 1, True)
+ # this *should* search for month and find the STATE/month variable
+ # BUT this only works if both var_name and var_path are month
+ # so add_input('month', 'month', 1, True) works.
+ found_path = self.find_var_path(var_path)
+ #print("Searched", var_name, "with path", var_path,"found", found_path)
+ var_ix = get_state_ix(self.state['state_ix'], self.state['state_paths'], found_path)
+ if var_ix == False:
+ if (trust == False):
+ raise Exception("Cannot find variable path: " + var_path + " when adding input to object " + self.name + " as input named " + var_name + " ... process terminated. Path to object with error is " + self.state_path)
+ var_ix = self.insure_path(var_path)
+ else:
+ # if we are to trust the path, this might be a child property just added,
+ # and therefore, we don't look further than this
+ # otherwise, we use found_path, whichever it is, as
+ # we know that this path is better, as we may have been given a simple variable name
+ # and so found_path will look more like /STATE/RCHRES_001/...
+ if trust == False:
+ var_path = found_path
+ self.inputs[var_name] = var_path
+ self.inputs_ix[var_name] = var_ix
+ # Should we create a register for the input to be reported here?
+ # i.e., if we have an input named Qin on RCHRES_R001, shouldn't we be able
+ # to find the data in /STATE/RCHRES_R001/Qin ??? It is redundant data and writing
+ # but matches a complete data model and prevents stale data?
+ return self.inputs_ix[var_name]
+
+ def add_object_input(self, var_name, var_object, link_type = 1):
+ # See above for details.
+ # this adds an object as a link to another object
+ self.inputs[var_name] = var_object.state_path
+ self.inputs_ix[var_name] = var_object.ix
+ return self.inputs_ix[var_name]
+
+ def create_parent_var(self, parent_var_name, source):
+ # see decision points: https://github.com/HARPgroup/HSPsquared/issues/78
+ # This is used when an object sets an additional property on its parent
+ # Like in simple_channel sets [channel prop name]_Qout on its parent
+ # Generally, this should have 2 components.
+ # 1 - a state variable on the child (this could be an implicit sub-comp, or a constant sub-comp, the child handles the setup of this) see constant_or_path()
+ # 2 - an input link
+ # the beauty of this is that the parent object and any of it's children will find the variable "[source_object]_varname"
+ if type(source) == str:
+ self.container.add_input(parent_var_name, source, 1, False)
+ elif isinstance(source, ModelObject):
+ self.container.add_object_input(parent_var_name, source, 1)
+
+ def insure_path(self, var_path):
+ # if this path can be found in the hdf5 make sure that it is registered in state
+ # and that it has needed object class to render it at runtime (some are automatic)
+ # RIGHT NOW THIS DOES NOTHING TO CHECK IF THE VAR EXISTS THIS MUST BE FIXED
+ var_ix = set_state(self.state['state_ix'], self.state['state_paths'], var_path, 0.0)
+ return var_ix
+
+ def get_dict_state(self, ix = -1):
+ if ix >= 0:
+ return self.state['dict_ix'][ix]
+ return self.state['dict_ix'][self.ix]
+
+ def find_paths(self):
+ # Note: every single piece of data used by objects, even constants, are resolved to a PATH in the hdf5
+ # find_paths() is called to insure that all of these can be found, and then, are added to inputs/inputs_ix
+ # - We wait to find the index values for those variables after all things have been loaded
+ # - base ModelObject does not have any "implicit" inputs, since all of its inputs are
+ # explicitly added children objects, thus we default to True
+ self.paths_found = True
+ # - But children such as Equation and DataMatrix, etc
+ # so they mark paths_found = False and then
+ # should go through their own locally defined data
+ # and call add_input() for any data variables encountered
+ # - add_input() will handle searching for the paths and ix values
+ # and should also handle deciding if this is a constant, like a numeric value
+ # or a variable data and should handle them accordingly
+ return True
+
+ def insure_register(self, var_name, default_value, register_container, register_path = False, is_accumulator = True):
+ # we send with local_only = True so it won't go upstream
+ if register_path == False:
+ register_path = register_container.find_var_path(var_name, True)
+ if (register_path == False) or (register_path not in self.state['model_object_cache'].keys()):
+ # create a register as a placeholder for the data at the hub path
+ # in case there are no senders, or in the case of a timeseries logger, we need to register it so that its path can be set to hold data
+ #print("Creating a register for data for hub ", register_container.name, "(", register_container.state_path, ")", " var name ",var_name)
+ if (is_accumulator == True):
+ var_register = ModelRegister(var_name, register_container, default_value, register_path)
+ else:
+ # this is just a standard numerical data holder so set up a constant
+ var_register = ModelVariable(var_name, register_container, default_value, register_path)
+ else:
+ var_register = self.state['model_object_cache'][register_path]
+ return var_register
+
+ def tokenize(self):
+ # renders tokens for high speed execution
+ if (self.paths_found == False):
+ raise Exception("path_found False for object" + self.name + "(" + self.state_path + "). " + "Tokens cannot be generated until method '.find_paths()' is run for all model objects ... process terminated. (see function `model_path_loader(model_object_cache)`)")
+ self.ops = [self.optype, self.ix]
+
+ def add_op_tokens(self):
+ # this puts the tokens into the global simulation queue
+ # can be customized by subclasses to add multiple lines if needed.
+ if self.ops == []:
+ self.tokenize()
+ if len(self.ops) > self.max_token_length:
+ raise Exception("op tokens cannot exceed max length of" + self.max_token_length + "(" + self.state_path + "). ")
+ self.state['op_tokens'][self.ix] = self.format_ops()
+
+ def step(self, step):
+ # this tests the model for a single timestep.
+ # this is not the method that is used for high-speed runs, but can theoretically be used for
+ # easier to understand demonstrations
+ step_one(self.state['op_tokens'], self.state['op_tokens'][self.ix], self.state['state_ix'], self.state['dict_ix'], self.state['ts_ix'], step)
+ #step_model({self.state['op_tokens'][self.ix]}, self.state['state_ix'], self.state['dict_ix'], self.state['ts_ix'], step)
+
+"""
+The class ModelVariable is a base cass for storing numerical values. Used for UVQUAN and misc numerical constants...
+"""
+class ModelVariable(ModelObject):
+ def __init__(self, name, container = False, value = 0.0, state_path = False):
+ if (state_path != False):
+ # this allows us to mandate the location. useful for placeholders, broadcasts, etc.
+ self.state_path = state_path
+ super(ModelVariable, self).__init__(name, container)
+ self.default_value = float(value)
+ self.optype = 7 # 0 - shell object, 1 - equation, 2 - datamatrix, 3 - input, 4 - broadcastChannel, 5 - SimTimer, 6 - Conditional, 7 - ModelVariable (numeric)
+ #print("ModelVariable named",self.name, "with path", self.state_path,"and ix", self.ix, "value", value)
+ var_ix = self.set_state(float(value))
+ self.paths_found = True
+ # self.state['state_ix'][self.ix] = self.default_value
+
+ def required_properties():
+ req_props = super(ModelVariable, ModelVariable).required_properties()
+ req_props.extend(['value'])
+ return req_props
+
+"""
+The class ModelConstant is for storing non-changing values.
+"""
+class ModelConstant(ModelVariable):
+ def __init__(self, name, container = False, value = 0.0, state_path = False):
+ super(ModelConstant, self).__init__(name, container, value, state_path)
+ self.optype = 16 # 0 - shell object, 1 - equation, 2 - datamatrix, 3 - input, 4 - broadcastChannel, 5 - SimTimer, 6 - Conditional, 7 - ModelVariable (numeric)
+ #print("ModelVariable named",self.name, "with path", self.state_path,"and ix", self.ix, "value", value)
+
+
+"""
+The class ModelRegister is for storing push values.
+Behavior is to zero each timestep. This could be amended later.
+Maybe combined with stack behavior? Or accumulator?
+"""
+class ModelRegister(ModelVariable):
+ def __init__(self, name, container = False, value = 0.0, state_path = False):
+ super(ModelRegister, self).__init__(name, container, value, state_path)
+ self.optype = 12 #
+ # self.state['state_ix'][self.ix] = self.default_value
+
+ def required_properties():
+ req_props = super(ModelVariable, ModelVariable).required_properties()
+ req_props.extend(['value'])
+ return req_props
+
+# njit functions for runtime
+@njit
+def pre_step_register(op, state_ix):
+ ix = op[1]
+ #print("Resetting register", ix,"to zero")
+ state_ix[ix] = 0.0
+ return
+
+# Note: ModelVariable has no runtime execution
+
+
+# njit functions for end of model run
+@njit
+def finish_model_object(op_token, state_ix, ts_ix):
+ return
+
+
+@njit
+def finish_register(op_token, state_ix, ts_ix):
+ # todo: push the values of ts_ix back to the hdf5? or does this happen in larger simulation as it is external to OM?
+ return
\ No newline at end of file
diff --git a/HSP2/om_sim_timer.py b/HSP2/om_sim_timer.py
new file mode 100644
index 00000000..3c9bc506
--- /dev/null
+++ b/HSP2/om_sim_timer.py
@@ -0,0 +1,87 @@
+"""
+The class SimTimer is used to translate copy data from one state location to another.
+It is also used to make an implicit parent child link to insure that an object is loaded
+during a model simulation.
+"""
+from HSP2.state import set_state
+from HSP2.om import ModelObject
+from HSP2.om_model_object import ModelObject
+from pandas import DataFrame, DatetimeIndex
+from numba import njit
+from numpy import int64
+
+class SimTimer(ModelObject):
+ def __init__(self, name, container, model_props = None):
+ if model_props is None:
+ model_props = {}
+ # Note: hsp2 siminfo will match model_props here
+ super(SimTimer, self).__init__(name, container, model_props)
+ self.state_path = '/STATE/timer'
+ self.time_array = self.dti_to_time_array(model_props) # creates numpy formatted array of year, mo, day, ... for each timestep
+ self.date_path_ix = [] # where are the are components stored in the state_ix Dict
+ self.optype = 5 # 0 - ModelObject, 1 - Equation, 2 - datamatrix, 3 - ModelLinkage, 4 - BroadcastChannel, 5 - SimTimer
+ self.register_components() # now that we have a time array, we set the basic state and all time comps into state
+
+ def register_components(self):
+ # initialize the path variable if not already set
+ self.ix = set_state(self.state['state_ix'], self.state['state_paths'], self.state_path, float(self.time_array[0][0]))
+ # now register all other paths.
+ # register "year", "month" "day", ...
+ year_ix = set_state(self.state['state_ix'], self.state['state_paths'], "/STATE/year", float(self.time_array[0][1]))
+ month_ix = set_state(self.state['state_ix'], self.state['state_paths'], "/STATE/month", float(self.time_array[0][2]))
+ day_ix = set_state(self.state['state_ix'], self.state['state_paths'], "/STATE/day", float(self.time_array[0][3]))
+ hr_ix = set_state(self.state['state_ix'], self.state['state_paths'], "/STATE/hour", float(self.time_array[0][4]))
+ min_ix = set_state(self.state['state_ix'], self.state['state_paths'], "/STATE/minute", float(self.time_array[0][5]))
+ sec_ix = set_state(self.state['state_ix'], self.state['state_paths'], "/STATE/second", float(self.time_array[0][6]))
+ wd_ix = set_state(self.state['state_ix'], self.state['state_paths'], "/STATE/weekday", float(self.time_array[0][7]))
+ dt_ix = set_state(self.state['state_ix'], self.state['state_paths'], "/STATE/dt", float(self.time_array[0][8]))
+ jd_ix = set_state(self.state['state_ix'], self.state['state_paths'], "/STATE/jday", float(self.time_array[0][9]))
+ md_ix = set_state(self.state['state_ix'], self.state['state_paths'], "/STATE/modays", float(self.time_array[0][10]))
+ dts_ix = set_state(self.state['state_ix'], self.state['state_paths'], "/STATE/dts", float(self.time_array[0][8] * 60.0))
+ self.date_path_ix = [year_ix, month_ix, day_ix, hr_ix, min_ix, sec_ix, wd_ix, dt_ix, jd_ix, md_ix, dts_ix]
+ self.state['dict_ix'][self.ix] = self.time_array
+
+ return self.ix
+
+ def tokenize(self):
+ # call parent method which sets standard ops
+ # returns an array of data pointers
+ super().tokenize() # resets ops to common base
+ self.ops = self.ops + self.date_path_ix # adds timer specific items
+
+ def add_op_tokens(self):
+ # this puts the tokens into the global simulation queue
+ # can be customized by subclasses to add multiple lines if needed.
+ super().add_op_tokens()
+ self.state['dict_ix'][self.ix] = self.time_array
+
+ def dti_to_time_array(self, siminfo):
+ dateindex = siminfo['tindex']
+ dt = siminfo['delt']
+ # sim timer is special, one entry for each time component for each timestep
+ # convert DateIndex to numbers [int(i) for i in dateindex.year]
+ tdi = { 0: dateindex.astype(int64), 1:[float(i) for i in dateindex.year], 2:[float(i) for i in dateindex.month], 3:[float(i) for i in dateindex.day], 4:[float(i) for i in dateindex.hour], 5:[float(i) for i in dateindex.minute], 6:[float(i) for i in dateindex.second], 7:[float(i) for i in dateindex.weekday], 8:[float(dt) for i in dateindex], 9:[float(i) for i in dateindex.day_of_year], 10:[float(i) for i in dateindex.daysinmonth], 11:[float(dt * 60.0) for i in dateindex] }
+ #tdi = { 0:dateindex.year, 1:dateindex.month, 2:dateindex.day, 3:dateindex.hour, 4:dateindex.minute, 5:dateindex.second }
+ tid = DataFrame(tdi)
+ h = 1 # added to increase row count for debug testing.
+ time_array = tid.to_numpy()
+ return time_array
+
+# Function for use during model simulations of tokenized objects
+@njit
+def step_sim_timer(op_token, state_ix, dict_ix, ts_ix, step):
+ # note: the op_token and state index are off by 1 since the dict_ix does not store type
+ #print("Exec step_sim_timer at step:", step, "jday", dict_ix[op_token[1]][step][9] )
+ state_ix[op_token[1]] = dict_ix[op_token[1]][step][0] # unix timestamp here
+ state_ix[op_token[2]] = dict_ix[op_token[1]][step][1] # year
+ state_ix[op_token[3]] = dict_ix[op_token[1]][step][2] # month
+ state_ix[op_token[4]] = dict_ix[op_token[1]][step][3] # day
+ state_ix[op_token[5]] = dict_ix[op_token[1]][step][4] # hour
+ state_ix[op_token[6]] = dict_ix[op_token[1]][step][5] # minute
+ state_ix[op_token[7]] = dict_ix[op_token[1]][step][6] # second
+ state_ix[op_token[8]] = dict_ix[op_token[1]][step][7] # weekday
+ state_ix[op_token[9]] = dict_ix[op_token[1]][step][8] # dt
+ state_ix[op_token[10]] = dict_ix[op_token[1]][step][9] # julian day
+ state_ix[op_token[11]] = dict_ix[op_token[1]][step][10] # modays
+ state_ix[op_token[12]] = dict_ix[op_token[1]][step][11] # dts
+ return
diff --git a/HSP2/om_special_action.py b/HSP2/om_special_action.py
new file mode 100644
index 00000000..c2944b69
--- /dev/null
+++ b/HSP2/om_special_action.py
@@ -0,0 +1,185 @@
+"""
+The class SpecialAction is used to support original HSPF ACTIONS.
+Draft: @tbd:
+ - CTCODE: code specifying time units of the interval between separate applications or actions -
+ (valid values: MI,HR,DY,MO,YR)
+ - CDEFFG: deferral flag - indicates how to treat deferral of the action under a conditional situation -
+ (valid values: SKIP, SHIFT, ACCUM; default = SKIP)
+ - FRACT: fractions for each of the separate applications
+
+"""
+import numpy as np
+from numba import njit
+
+from HSP2.om import is_float_digit
+from HSP2.om_model_object import ModelObject
+
+class SpecialAction(ModelObject):
+ def __init__(self, name, container = False, model_props = None):
+ if model_props is None:
+ model_props = {}
+ super(SpecialAction, self).__init__(name, container, model_props)
+
+ self.optype = 100 # Special Actions start indexing at 100
+
+ def parse_model_props(self, model_props, strict=False):
+ super().parse_model_props(model_props, strict)
+ # comes in as row from special ACTIONS table
+ # ex: {
+ # 'OPTYP': 'RCHRES', 'RANGE1': '1', 'RANGE2': '', 'DC': 'DY', 'DS': '',
+ # 'YR': '1986', 'MO': '3', 'DA': '1', 'HR': '12', 'MN': '',
+ # 'D': '2', 'T': 3, 'VARI': 'IVOL', 'S1': '', 'S2': '',
+ # 'AC': '+=', 'VALUE': 30.0, 'TC': '', 'TS': '', 'NUM': '', 'CURLVL': 0,
+ # defined by:
+ # - operand1, i.e. variable to access + update, path = /STATE/[OPTYP]_[op_abbrev][RANGE1]/[VARI]
+ # - action(operation) to perform = AC
+ # - operand2, a numeric value for simple ACTION = [VALUE]
+ # note: [op_abbrev] is *maybe* the first letter of the OPTYP? Not a very good idea to have a coded convention like that
+ self.op_type = self.handle_prop(model_props, 'OPTYP')
+ self.range1 = self.handle_prop(model_props, 'RANGE1')
+ self.range2 = self.handle_prop(model_props, 'RANGE2')
+ self.ac = '=' # set the default, and also adds a property for later testing.
+ self.ac = self.handle_prop(model_props, 'AC') # must handle this before we handle the operand VALUE to check for DIV by Zero
+ self.vari = self.handle_prop(model_props, 'VARI')
+ self.op2_val = self.handle_prop(model_props, 'VALUE')
+ self.op2_ix = self.constant_or_path('op_val', self.op2_val) # constant values must be added to STATE and thus are referenced by their state_ix number
+ self.num = self.handle_prop(model_props, 'NUM', False, 1) # number of times to perform action
+ self.timer_ix = self.handle_prop(model_props, 'when', False, 1) # when to begin the first attempt at action
+ self.ctr_ix = self.constant_or_path('ctr', 0) # this initializes the counter for how many times an action has been performed
+ # NOTE: since the spec-action modifies the same quantity that is it's input, it does *not* set it as a proper "input" since that would create a circular dependency
+ domain = self.state['model_object_cache'][('/STATE/' + self.op_type + '_' + self.op_type[0] + str(self.range1).zfill(3) )]
+ var_register = self.insure_register(self.vari, 0.0, domain, False, False)
+ #print("Created register", var_register.name, "with path", var_register.state_path)
+ # add already created objects as inputs
+ var_register.add_object_input(self.name, self, 1)
+ self.op1_ix = var_register.ix
+
+ # @tbd: support time enable/disable
+ # - check if time ops have been set and add as inputs like "year", or "month", etc could give explicit path /STATE/year ...
+ # - add the time values to match as constants i.e. self.constant_or_path()
+
+ def handle_prop(self, model_props, prop_name, strict = False, default_value = None ):
+ # Insure all values are legal ex: no DIV by Zero
+ prop_val = super().handle_prop(model_props, prop_name, strict, default_value )
+ if (prop_name == 'VALUE') and (self.ac == '/='):
+ if (prop_val == 0) or (prop_val == None):
+ raise Exception("Error: in properties passed to "+ self.name + " AC must be non-zero or non-Null . Object creation halted. Path to object with error is " + self.state_path)
+ if (prop_name == 'AC'):
+ self.handle_ac(prop_val)
+ if (prop_name == 'when'):
+ # when to perform this? timestamp or time-step index
+ prop_val = -1 # prevent a 0 indexed value from triggering return, default means execute every step
+ si = self.state['model_object_cache'][self.find_var_path('timer')]
+ if len(model_props['YR']) > 0:
+ # translate date to equivalent model step
+ datestring = model_props['YR'] + '-' + model_props['MO'] + '-' + \
+ model_props['DA'] + ' ' + model_props['HR'] + ':' + \
+ model_props['MN'] + ':00'
+ if datestring in si.model_props_parsed['tindex']:
+ prop_val = si.model_props_parsed['tindex'].get_loc(datestring)
+ if (prop_name == 'NUM') and (prop_val == ''):
+ prop_val = default_value
+ return prop_val
+
+ def handle_ac(self, ac):
+ # cop_code 0: =/eq, 1: /gt, 3: <=/le, 4: >=/ge, 5: <>/ne
+ cop_codes = {
+ '=': 1,
+ '+=': 2,
+ '-=': 3,
+ '*=': 4,
+ '/=': 5,
+ 'MIN': 6
+ }
+ # From HSPF UCI docs:
+ # 1 = T= A
+ # 2 += T= T+ A
+ # 3 -= T= T- A
+ # 4 *= T= T*A
+ # 5 /= T= T/A
+ # 6 MIN T= Min(T,A)
+ # 7 MAX T= Max(T,A)
+ # 8 ABS T= Abs(A)
+ # 9 INT T= Int(A)
+ # 10 ^= T= T^A
+ # 11 LN T= Ln(A)
+ # 12 LOG T= Log10(A)
+ # 13 MOD T= Mod(T,A)
+ if not (is_float_digit(ac)):
+ if not (ac in cop_codes.keys()):
+ raise Exception("Error: in "+ self.name + " AC (" + ac + ") not supported. Object creation halted. Path to object with error is " + self.state_path)
+ opid = cop_codes[ac]
+ self.ac = ac
+ else:
+ # this will fail catastrophically if the requested function is not supported
+ # which is a good thing
+ if not (ac in cop_codes.values()):
+ raise Exception("Error: in "+ self.name + "numeric AC (" + ac + ") not supported. Object creation halted. Path to object with error is " + self.state_path)
+ opid = ac
+ self.ac = list(cop_codes.keys())[list(cop_codes.values()).index(ac) ]
+ self.opid = opid
+
+ def tokenize(self):
+ # call parent method to set basic ops common to all
+ super().tokenize() # sets self.ops = op_type, op_ix
+ self.ops = self.ops + [self.op1_ix, self.opid, self.op2_ix, self.timer_ix, self.ctr_ix, self.num]
+ # @tbd: check if time ops have been set and tokenize accordingly
+
+ def add_op_tokens(self):
+ # this puts the tokens into the global simulation queue
+ # can be customized by subclasses to add multiple lines if needed.
+ super().add_op_tokens()
+
+ @staticmethod
+ def hdf5_load_all(hdf_source):
+ specla=hdf_source['/SPEC_ACTIONS/ACTIONS/table']
+ for idx, x in np.ndenumerate(specla):
+ print(x[1].decode("utf-8"),x[2].decode("utf-8"), x[13].decode("utf-8"), x[16].decode("utf-8"), x[17])
+
+
+# njit functions for runtime
+
+@njit(cache=True)
+def step_special_action(op, state_ix, dict_ix, step):
+ ix = op[1] # ID of this op
+ # these indices must be adjusted to reflect the number of common op tokens
+ # SpecialAction has:
+ # - type of condition (+=, -=, ...)
+ # - operand 1 (left side)
+ # - operand 2 (right side)
+ # @tbd: check if time ops have been set and enable/disable accordingly
+ # - 2 ops will be added for each time matching switch, the state_ix of the time element (year, month, ...) and the state_ix of the constant to match
+ # - matching should be as simple as if (state_ix[tix1] <> state_ix[vtix1]): return state_ix[ix1] (don't modify the value)
+ # - alternative: save the integer timestamp or timestep of the start, and if step/stamp > value, enable
+ # @tbd: add number of repeats, and save the value of repeats in a register
+ ix1 = op[2] # ID of source of data and destination of data
+ sop = op[3]
+ ix2 = op[4]
+ tix = op[5] # which slot is the time comparison in?
+ if (tix in state_ix and step < state_ix[tix]):
+ return
+ ctr_ix = op[6] # id of the counter variable
+ num_ix = op[7] # max times to complete
+ num_done = state_ix[ctr_ix]
+ num = state_ix[num_ix] # num to complete
+ if (tix in state_ix and num_done >= num):
+ return
+ else:
+ if sop == 1:
+ result = state_ix[ix2]
+ elif sop == 2:
+ result = state_ix[ix1] + state_ix[ix2]
+ elif sop == 3:
+ result = state_ix[ix1] - state_ix[ix2]
+ elif sop == 4:
+ result = state_ix[ix1] * state_ix[ix2]
+ elif sop == 5:
+ result = state_ix[ix1] / state_ix[ix2]
+
+ # set value in target
+ # tbd: handle this with a model linkage? cons: this makes a loop since the ix1 is source and destination
+
+ state_ix[ix1] = result
+ state_ix[op[1]] = result
+ return result
+
diff --git a/HSP2/state.py b/HSP2/state.py
index 67ac24a0..609c06a6 100644
--- a/HSP2/state.py
+++ b/HSP2/state.py
@@ -16,24 +16,16 @@ def init_state_dicts():
This contains the base dictionaries used to pass model state amongst modules and custom code plugins
"""
state = {} # shared state Dictionary, contains numba-ready Dicts
- state_paths = Dict.empty(key_type=types.unicode_type, value_type=types.int64)
- state_ix = Dict.empty(key_type=types.int64, value_type=types.float64)
- dict_ix = Dict.empty(key_type=types.int64, value_type=types.float64[:,:])
- ts_ix = Dict.empty(key_type=types.int64, value_type=types.float64[:])
+ state['state_paths'] = Dict.empty(key_type=types.unicode_type, value_type=types.int64)
+ state['state_ix'] = Dict.empty(key_type=types.int64, value_type=types.float64)
+ state['dict_ix'] = Dict.empty(key_type=types.int64, value_type=types.float64[:,:])
+ state['ts_ix'] = Dict.empty(key_type=types.int64, value_type=types.float64[:])
# initialize state for hydr
- # now put all of these Dicts into the state Dict
- state['state_paths'], state['state_ix'], state['dict_ix'], state['ts_ix'] = state_paths, state_ix, dict_ix, ts_ix
+ # add a generic place to stash model_data for dynamic components
+ state['model_data'] = {}
return state
-def find_state_path(state_paths, parent_path, varname):
- """
- We should get really good at using docstrings...
- """
- # this is a bandaid, we should have an object routine that searches the parent for variables or inputs
- var_path = parent_path + "/states/" + str(varname)
- return var_path
-
def op_path_name(operation, id):
"""
Used to generate hdf5 operation name in a central fashion to avoid naming convention slip-ups
@@ -42,17 +34,6 @@ def op_path_name(operation, id):
path_name = f'{operation}_{operation[0]}{tid}'
return path_name
-def get_op_state_path(operation, id, activity = ''):
- """
- Used to generate hdf5 paths in a central fashion to avoid naming convention slip-ups
- """
- op_name = op_path_name(operation, id)
- if activity == '':
- op_path = f'/STATE/{op_name}'
- else:
- op_path = f'/STATE/{op_name}/{activity}'
- return op_path
-
def get_state_ix(state_ix, state_paths, var_path):
"""
@@ -117,11 +98,20 @@ def append_state(state_ix, var_value):
return val_ix
def state_context_hsp2(state, operation, segment, activity):
+ # this establishes domain info so that a module can know its paths
state['operation'] = operation
state['segment'] = segment #
state['activity'] = activity
# give shortcut to state path for the upcoming function
- state['domain'] = "/STATE/" + operation + "_" + segment + "/" + activity
+ # insure that there is a model object container
+ seg_name = operation + "_" + segment
+ seg_path = '/STATE/' + seg_name
+ if 'hsp_segments' not in state.keys():
+ state['hsp_segments'] = {} # for later use by things that need to know hsp entities and their paths
+ if seg_name not in state['hsp_segments'].keys():
+ state['hsp_segments'][seg_name] = seg_path
+
+ state['domain'] = seg_path # + "/" + activity # may want to comment out activity?
def state_siminfo_hsp2(uci_obj, siminfo):
# Add crucial simulation info for dynamic operation support
@@ -130,6 +120,23 @@ def state_siminfo_hsp2(uci_obj, siminfo):
siminfo['tindex'] = date_range(siminfo['start'], siminfo['stop'], freq=Minute(delt))[1:]
siminfo['steps'] = len(siminfo['tindex'])
+def state_init_hsp2(state, opseq, activities):
+ # This sets up the state entries for all state compatible HSP2 model variables
+ print("STATE initializing contexts.")
+ for _, operation, segment, delt in opseq.itertuples():
+ if operation != 'GENER' and operation != 'COPY':
+ for activity, function in activities[operation].items():
+ if activity == 'HYDR':
+ state_context_hsp2(state, operation, segment, activity)
+ hydr_init_ix(state, state['domain'])
+ elif activity == 'SEDTRN':
+ state_context_hsp2(state, operation, segment, activity)
+ sedtrn_init_ix(state, state['domain'])
+
+def state_load_hdf5_components(io_manager, siminfo, op_tokens, state_paths, state_ix, dict_ix, ts_ix, model_object_cache):
+ # Implement population of model_object_cache etc from components in a hdf5 such as Special ACTIONS
+ return
+
def state_load_dynamics_hsp2(state, io_manager, siminfo):
# Load any dynamic components if present, and store variables on objects
hsp2_local_py = load_dynamics(io_manager, siminfo)
@@ -137,15 +144,25 @@ def state_load_dynamics_hsp2(state, io_manager, siminfo):
state['state_step_hydr'] = siminfo['state_step_hydr'] # enabled or disabled
state['hsp2_local_py'] = hsp2_local_py # Stores the actual function in state
-def hydr_init_ix(state_ix, state_paths, domain):
+def hydr_init_ix(state, domain):
# get a list of keys for all hydr state variables
hydr_state = ["DEP","IVOL","O1","O2","O3","OVOL1","OVOL2","OVOL3","PRSUPY","RO","ROVOL","SAREA","TAU","USTAR","VOL","VOLEV"]
hydr_ix = Dict.empty(key_type=types.unicode_type, value_type=types.int64)
for i in hydr_state:
#var_path = f'{domain}/{i}'
var_path = domain + "/" + i
- hydr_ix[i] = set_state(state_ix, state_paths, var_path, 0.0)
- return hydr_ix
+ hydr_ix[i] = set_state(state['state_ix'], state['state_paths'], var_path, 0.0)
+ return hydr_ix
+
+def sedtrn_init_ix(state, domain):
+ # get a list of keys for all sedtrn state variables
+ sedtrn_state = ["RSED4","RSED5","RSED6"]
+ sedtrn_ix = Dict.empty(key_type=types.unicode_type, value_type=types.int64)
+ for i in sedtrn_state:
+ #var_path = f'{domain}/{i}'
+ var_path = domain + "/" + i
+ sedtrn_ix[i] = set_state(state['state_ix'], state['state_paths'], var_path, 0.0)
+ return sedtrn_ix
@njit
def hydr_get_ix(state_ix, state_paths, domain):
@@ -158,6 +175,16 @@ def hydr_get_ix(state_ix, state_paths, domain):
hydr_ix[i] = state_paths[var_path]
return hydr_ix
+@njit
+def sedtrn_get_ix(state_ix, state_paths, domain):
+ # get a list of keys for all sedtrn state variables
+ sedtrn_state = ["RSED4", "RSED5", "RSED6"]
+ sedtrn_ix = Dict.empty(key_type=types.unicode_type, value_type=types.int64)
+ for i in sedtrn_state:
+ var_path = domain + "/" + i
+ sedtrn_ix[i] = state_paths[var_path]
+ return sedtrn_ix
+
# function to dynamically load module, based on "Using imp module" in https://www.tutorialspoint.com/How-I-can-dynamically-import-Python-module#
#def dynamic_module_import(module_name, class_name):
def dynamic_module_import(local_name, local_path, module_name):
@@ -200,4 +227,5 @@ def load_dynamics(io_manager, siminfo):
else:
# print("state_step_hydr function not defined. Using default")
return False
- return hsp2_local_py
\ No newline at end of file
+ return hsp2_local_py
+
diff --git a/compare_case.json b/compare_case.json
new file mode 100644
index 00000000..5cd4e8c3
--- /dev/null
+++ b/compare_case.json
@@ -0,0 +1,20 @@
+{
+ "RCHRES_R001": {
+ "name": "RCHRES_R001",
+ "object_class": "ModelObject",
+ "value": "0",
+ "wd_cfs": {
+ "name": "wd_cfs",
+ "object_class": "Constant",
+ "value": 10.0
+ },
+ "IVOLwrite": {
+ "name": "IVOLwrite",
+ "object_class": "ModelLinkage",
+ "left_path": "/STATE/RCHRES_R001/HYDR/O2",
+ "right_path": "/STATE/RCHRES_R001/wd_cfs",
+ "link_type": 5
+ }
+ }
+}
+
diff --git a/tests/test10/HSP2results/pytest10.py b/tests/test10/HSP2results/pytest10.py
deleted file mode 100644
index 87f1b7f9..00000000
--- a/tests/test10/HSP2results/pytest10.py
+++ /dev/null
@@ -1,10 +0,0 @@
-
-import pytest
-import os
-
-def test_h5_file_exists():
- assert os.path.exists('test10.h5')
-
-# uncomment if you want to see how to break the test
-#def test_that_should_fail():
-# assert os.path.exists('test100000.h5')
diff --git a/tests/test10specl/HSP2results/TEST10_hsp2_compare.ipynb b/tests/test10specl/HSP2results/TEST10_hsp2_compare.ipynb
new file mode 100644
index 00000000..c8616828
--- /dev/null
+++ b/tests/test10specl/HSP2results/TEST10_hsp2_compare.ipynb
@@ -0,0 +1,3198 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# COMPARE HSP2 TO HSPF USING TEST10"
+ ]
+ },
+ {
+ "attachments": {
+ "337b05b8-e081-4c20-b486-27045c75ea6d.png": {
+ "image/png": "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"
+ }
+ },
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "![HSP2new.png](attachment:337b05b8-e081-4c20-b486-27045c75ea6d.png)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Copyright 2020 for this Jupyter Notebook and YouTube Video by RESPEC, INC. All rights reserved.\n",
+ "\n",
+ "$\\textbf{HSP}^{\\textbf{2}}\\ \\text{and}\\ \\textbf{HSP2}\\ $ Copyright 2020 by RESPEC INC. and released under the *GNU AFFERO GENERAL PUBLIC LICENSE*"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This Notebook will compare the results of running HSPF and HSP$^2$ for the basic hydrology (PWATER, IWATER, and HYDR) to confirm the proper calculations of HSP$^2$\n",
+ "\n",
+ "Calleg is a real watershed and has\n",
+ "+ 27 IMPLND segments,\n",
+ "+ 129 PERLND segments,\n",
+ "+ 119 RCHRES segments,\n",
+ "+ 9 years of simulation time with hourly time steps (78,888 timesteps) from 1993-10-01 to 2002-10-01\n",
+ "\n",
+ "This Notebook assumes HSPF has been run and the HBN data saved to an HDF5 file in the **HSPF RUN** directory."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Required Python imports and setup"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2024-04-28T16:45:32.329949Z",
+ "iopub.status.busy": "2024-04-28T16:45:32.329813Z",
+ "iopub.status.idle": "2024-04-28T16:45:34.245103Z",
+ "shell.execute_reply": "2024-04-28T16:45:34.244424Z",
+ "shell.execute_reply.started": "2024-04-28T16:45:32.329930Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " version | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " Python | \n",
+ " 3.10.14 | packaged by conda-forge | (main, Mar... | \n",
+ "
\n",
+ " \n",
+ " HSP2 | \n",
+ " 0.11.0a1 | \n",
+ "
\n",
+ " \n",
+ " numpy | \n",
+ " 1.26.4 | \n",
+ "
\n",
+ " \n",
+ " numba | \n",
+ " 0.59.1 | \n",
+ "
\n",
+ " \n",
+ " pandas | \n",
+ " 2.2.2 | \n",
+ "
\n",
+ " \n",
+ " matplotlib | \n",
+ " 3.8.4 | \n",
+ "
\n",
+ " \n",
+ " tables | \n",
+ " 3.9.2 | \n",
+ "
\n",
+ " \n",
+ " h5py | \n",
+ " 3.11.0 | \n",
+ "
\n",
+ " \n",
+ " os | \n",
+ " Linux-5.15.0-91-generic-x86_64-with-glibc2.35 | \n",
+ "
\n",
+ " \n",
+ " processor | \n",
+ " x86_64 | \n",
+ "
\n",
+ " \n",
+ " Date/Time | \n",
+ " 2024-04-28 12:45:34 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " version\n",
+ "Python 3.10.14 | packaged by conda-forge | (main, Mar...\n",
+ "HSP2 0.11.0a1\n",
+ "numpy 1.26.4\n",
+ "numba 0.59.1\n",
+ "pandas 2.2.2\n",
+ "matplotlib 3.8.4\n",
+ "tables 3.9.2\n",
+ "h5py 3.11.0\n",
+ "os Linux-5.15.0-91-generic-x86_64-with-glibc2.35\n",
+ "processor x86_64\n",
+ "Date/Time 2024-04-28 12:45:34"
+ ]
+ },
+ "execution_count": 1,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "import os\n",
+ "\n",
+ "from matplotlib import pyplot as plt\n",
+ "from pandas import DataFrame, options, read_hdf\n",
+ "\n",
+ "# Set display options for convenience, does't change the data\n",
+ "options.display.max_rows = 150\n",
+ "options.display.max_columns = 20\n",
+ "options.display.float_format = (\n",
+ " \"{:.4f}\".format\n",
+ ") # display 4 digits after the decimal point\n",
+ "\n",
+ "%matplotlib inline\n",
+ "\n",
+ "from HSP2 import main, versions\n",
+ "from HSP2tools import readUCI, readWDM\n",
+ "\n",
+ "versions([\"matplotlib\", \"tables\", \"h5py\"])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### If you get the HSP2 Not Found error:\n",
+ "```python\n",
+ "ModuleNotFoundError: No module named 'HSP2'\n",
+ "```\n",
+ "Then run the following terminal command with your local absolute path to this repo (enabled in this notebook with the `!` magic command), AND restart the kernel.\n",
+ "See https://github.com/LimnoTech/HSPsquared#4-add-your-hspsquared-path-to-anaconda-sites-packages"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2024-04-28T16:45:34.246207Z",
+ "iopub.status.busy": "2024-04-28T16:45:34.245792Z",
+ "iopub.status.idle": "2024-04-28T16:45:34.364011Z",
+ "shell.execute_reply": "2024-04-28T16:45:34.363366Z",
+ "shell.execute_reply.started": "2024-04-28T16:45:34.246190Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "/bin/bash: line 1: conda-develop: command not found\n"
+ ]
+ }
+ ],
+ "source": [
+ "!conda-develop /Users/aaufdenkampe/Documents/Python/limno.HSPsquared/"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Convenient name strings to get to the data"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2024-04-28T16:45:34.365805Z",
+ "iopub.status.busy": "2024-04-28T16:45:34.365551Z",
+ "iopub.status.idle": "2024-04-28T16:45:34.369158Z",
+ "shell.execute_reply": "2024-04-28T16:45:34.368654Z",
+ "shell.execute_reply.started": "2024-04-28T16:45:34.365787Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'hsp2_310'"
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Confirm your active conda environment for this notebook.\n",
+ "os.environ[\"CONDA_DEFAULT_ENV\"]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2024-04-28T16:45:34.370155Z",
+ "iopub.status.busy": "2024-04-28T16:45:34.369905Z",
+ "iopub.status.idle": "2024-04-28T16:45:34.373436Z",
+ "shell.execute_reply": "2024-04-28T16:45:34.373069Z",
+ "shell.execute_reply.started": "2024-04-28T16:45:34.370138Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "PosixPath('/home/tim/programming/HSPsquared/tests/test10/HSP2results')"
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "from pathlib import Path\n",
+ "\n",
+ "# get current working directory\n",
+ "Path.cwd()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2024-04-28T16:45:34.374322Z",
+ "iopub.status.busy": "2024-04-28T16:45:34.374016Z",
+ "iopub.status.idle": "2024-04-28T16:45:34.376363Z",
+ "shell.execute_reply": "2024-04-28T16:45:34.376048Z",
+ "shell.execute_reply.started": "2024-04-28T16:45:34.374303Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "wdmname = \"test10.wdm\"\n",
+ "uciname = \"test10.uci\"\n",
+ "HBN = \"test10_hspf.h5\"\n",
+ "hdfname = \"test10_hsp2_dev2WDM_6.h5\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Run HSP2 on the calleg"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2024-04-28T16:45:34.377318Z",
+ "iopub.status.busy": "2024-04-28T16:45:34.376932Z",
+ "iopub.status.idle": "2024-04-28T16:45:34.379517Z",
+ "shell.execute_reply": "2024-04-28T16:45:34.379059Z",
+ "shell.execute_reply.started": "2024-04-28T16:45:34.377300Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# Optional setting to testing Numba\n",
+ "os.environ[\"NUMBA_DISABLE_JIT\"] = \"0\" # '1' turns off Numba for this session only"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Create HDF5 file"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2024-04-28T16:45:34.380243Z",
+ "iopub.status.busy": "2024-04-28T16:45:34.380104Z",
+ "iopub.status.idle": "2024-04-28T16:45:45.096128Z",
+ "shell.execute_reply": "2024-04-28T16:45:45.095570Z",
+ "shell.execute_reply.started": "2024-04-28T16:45:34.380230Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "CPU times: user 2 µs, sys: 0 ns, total: 2 µs\n",
+ "Wall time: 2.38 µs\n",
+ "ACTIVITY\n",
+ "PRINT-INFO\n",
+ "GEN-INFO\n",
+ "ICE-FLAG\n",
+ "SNOW-PARM1\n",
+ "SNOW-PARM2\n",
+ "SNOW-INIT1\n",
+ "SNOW-INIT2\n",
+ "PWAT-PARM1\n",
+ "PWAT-PARM2\n",
+ "PWAT-PARM3\n",
+ "PWAT-PARM4\n",
+ "MON-INTERCEP\n",
+ "MON-UZSN\n",
+ "MON-MANNING\n",
+ "MON-LZETPARM\n",
+ "PWAT-STATE1\n",
+ "PSTEMP-PARM2\n",
+ "PWT-PARM2\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:395: FutureWarning: 'T' is deprecated and will be removed in a future version. Please use 'min' instead of 'T'.\n",
+ " + pd.Timedelta(int(d['SHR']), 'h') + pd.Timedelta(int(d['SMI']), 'T'))[0:16]\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:397: FutureWarning: 'T' is deprecated and will be removed in a future version. Please use 'min' instead of 'T'.\n",
+ " + pd.Timedelta(int(d['EHR']), 'h') + pd.Timedelta(int(d['EMI']), 'T'))[0:16]\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "ACTIVITY\n",
+ "PRINT-INFO\n",
+ "GEN-INFO\n",
+ "ICE-FLAG\n",
+ "SNOW-PARM1\n",
+ "SNOW-PARM2\n",
+ "SNOW-INIT1\n",
+ "SNOW-INIT2\n",
+ "IWAT-PARM1\n",
+ "IWAT-PARM2\n",
+ "IWAT-PARM3\n",
+ "IWAT-STATE1\n",
+ "SLD-PARM2\n",
+ "SLD-STOR\n",
+ "IWT-PARM2\n",
+ "NQUALS\n",
+ "QUAL-PROPS\n",
+ "QUAL-INPUT\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "ACTIVITY\n",
+ "PRINT-INFO\n",
+ "GEN-INFO\n",
+ "HYDR-PARM1\n",
+ "HYDR-PARM2\n",
+ "HYDR-INIT\n",
+ "NCONS\n",
+ "CONS-DATA\n",
+ "HEAT-PARM\n",
+ "HEAT-INIT\n",
+ "SANDFG\n",
+ "SED-GENPARM\n",
+ "SAND-PM\n",
+ "SILT-CLAY-PM\n",
+ "SILT-CLAY-PM\n",
+ "SSED-INIT\n",
+ "BED-INIT\n",
+ "GQ-GENDATA\n",
+ "GQ-QALDATA\n",
+ "GQ-QALFG\n",
+ "GQ-HYDPM\n",
+ "GQ-ROXPM\n",
+ "GQ-PHOTPM\n",
+ "GQ-CFGAS\n",
+ "GQ-BIOPM\n",
+ "GQ-GENDECAY\n",
+ "GQ-SEDDECAY\n",
+ "GQ-KD\n",
+ "GQ-ADRATE\n",
+ "GQ-SEDCONC\n",
+ "GQ-VALUES\n",
+ "GQ-ALPHA\n",
+ "GQ-GAMMA\n",
+ "GQ-DELTA\n",
+ "GQ-CLDFACT\n",
+ "BENTH-FLAG\n",
+ "SCOUR-PARMS\n",
+ "OX-FLAGS\n",
+ "OX-GENPARM\n",
+ "OX-BENPARM\n",
+ "OX-CFOREA\n",
+ "OX-REAPARM\n",
+ "OX-INIT\n",
+ "NUT-FLAGS\n",
+ "NUT-BENPARM\n",
+ "NUT-NITDENIT\n",
+ "NUT-NH3VOLAT\n",
+ "NUT-BEDCONC\n",
+ "NUT-ADSPARM\n",
+ "NUT-DINIT\n",
+ "NUT-ADSINIT\n",
+ "PLNK-FLAGS\n",
+ "PLNK-PARM1\n",
+ "PHYTO-PARM\n",
+ "ZOO-PARM1\n",
+ "PLNK-INIT\n",
+ "PH-PARM1\n",
+ "PH-INIT\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readUCI.py:98: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
+ " df = df.apply(pd.to_numeric, errors='ignore')\n"
+ ]
+ }
+ ],
+ "source": [
+ "%time\n",
+ "readUCI(uciname, hdfname)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2024-04-28T16:45:45.097002Z",
+ "iopub.status.busy": "2024-04-28T16:45:45.096753Z",
+ "iopub.status.idle": "2024-04-28T16:45:47.570764Z",
+ "shell.execute_reply": "2024-04-28T16:45:47.570312Z",
+ "shell.execute_reply.started": "2024-04-28T16:45:45.096987Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "CPU times: user 1e+03 ns, sys: 0 ns, total: 1e+03 ns\n",
+ "Wall time: 3.34 µs\n",
+ "39 reading from wdm\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:138: FutureWarning: 'H' is deprecated and will be removed in a future version, please use 'h' instead.\n",
+ " series.index.freq = str(tstep) + freq[tcode]\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:146: FutureWarning: Starting with pandas version 3.0 all arguments of to_hdf except for the argument 'path_or_buf' will be keyword-only.\n",
+ " series.to_hdf(store, dsname, format='t', data_columns=True)\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:146: FutureWarning: Starting with pandas version 3.0 all arguments of to_hdf except for the argument 'path_or_buf' will be keyword-only.\n",
+ " series.to_hdf(store, dsname, format='t', data_columns=True)\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:146: FutureWarning: Starting with pandas version 3.0 all arguments of to_hdf except for the argument 'path_or_buf' will be keyword-only.\n",
+ " series.to_hdf(store, dsname, format='t', data_columns=True)\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:138: FutureWarning: 'H' is deprecated and will be removed in a future version, please use 'h' instead.\n",
+ " series.index.freq = str(tstep) + freq[tcode]\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:146: FutureWarning: Starting with pandas version 3.0 all arguments of to_hdf except for the argument 'path_or_buf' will be keyword-only.\n",
+ " series.to_hdf(store, dsname, format='t', data_columns=True)\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:146: FutureWarning: Starting with pandas version 3.0 all arguments of to_hdf except for the argument 'path_or_buf' will be keyword-only.\n",
+ " series.to_hdf(store, dsname, format='t', data_columns=True)\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:146: FutureWarning: Starting with pandas version 3.0 all arguments of to_hdf except for the argument 'path_or_buf' will be keyword-only.\n",
+ " series.to_hdf(store, dsname, format='t', data_columns=True)\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:138: FutureWarning: 'H' is deprecated and will be removed in a future version, please use 'h' instead.\n",
+ " series.index.freq = str(tstep) + freq[tcode]\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:146: FutureWarning: Starting with pandas version 3.0 all arguments of to_hdf except for the argument 'path_or_buf' will be keyword-only.\n",
+ " series.to_hdf(store, dsname, format='t', data_columns=True)\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:138: FutureWarning: 'H' is deprecated and will be removed in a future version, please use 'h' instead.\n",
+ " series.index.freq = str(tstep) + freq[tcode]\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:146: FutureWarning: Starting with pandas version 3.0 all arguments of to_hdf except for the argument 'path_or_buf' will be keyword-only.\n",
+ " series.to_hdf(store, dsname, format='t', data_columns=True)\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:138: FutureWarning: 'H' is deprecated and will be removed in a future version, please use 'h' instead.\n",
+ " series.index.freq = str(tstep) + freq[tcode]\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:146: FutureWarning: Starting with pandas version 3.0 all arguments of to_hdf except for the argument 'path_or_buf' will be keyword-only.\n",
+ " series.to_hdf(store, dsname, format='t', data_columns=True)\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:146: FutureWarning: Starting with pandas version 3.0 all arguments of to_hdf except for the argument 'path_or_buf' will be keyword-only.\n",
+ " series.to_hdf(store, dsname, format='t', data_columns=True)\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:146: FutureWarning: Starting with pandas version 3.0 all arguments of to_hdf except for the argument 'path_or_buf' will be keyword-only.\n",
+ " series.to_hdf(store, dsname, format='t', data_columns=True)\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:146: FutureWarning: Starting with pandas version 3.0 all arguments of to_hdf except for the argument 'path_or_buf' will be keyword-only.\n",
+ " series.to_hdf(store, dsname, format='t', data_columns=True)\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:146: FutureWarning: Starting with pandas version 3.0 all arguments of to_hdf except for the argument 'path_or_buf' will be keyword-only.\n",
+ " series.to_hdf(store, dsname, format='t', data_columns=True)\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:138: FutureWarning: 'H' is deprecated and will be removed in a future version, please use 'h' instead.\n",
+ " series.index.freq = str(tstep) + freq[tcode]\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:146: FutureWarning: Starting with pandas version 3.0 all arguments of to_hdf except for the argument 'path_or_buf' will be keyword-only.\n",
+ " series.to_hdf(store, dsname, format='t', data_columns=True)\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "41 reading from wdm\n",
+ "42 reading from wdm\n",
+ "46 reading from wdm\n",
+ "113 reading from wdm\n",
+ "119 reading from wdm\n",
+ "121 reading from wdm\n",
+ "122 reading from wdm\n",
+ "123 reading from wdm\n",
+ "124 reading from wdm\n",
+ "125 reading from wdm\n",
+ "126 reading from wdm\n",
+ "127 reading from wdm\n",
+ "131 reading from wdm\n",
+ "132 reading from wdm\n",
+ "134 reading from wdm\n",
+ "135 reading from wdm\n",
+ "136 reading from wdm\n",
+ "140 reading from wdm\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:138: FutureWarning: 'H' is deprecated and will be removed in a future version, please use 'h' instead.\n",
+ " series.index.freq = str(tstep) + freq[tcode]\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:146: FutureWarning: Starting with pandas version 3.0 all arguments of to_hdf except for the argument 'path_or_buf' will be keyword-only.\n",
+ " series.to_hdf(store, dsname, format='t', data_columns=True)\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:146: FutureWarning: Starting with pandas version 3.0 all arguments of to_hdf except for the argument 'path_or_buf' will be keyword-only.\n",
+ " series.to_hdf(store, dsname, format='t', data_columns=True)\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:146: FutureWarning: Starting with pandas version 3.0 all arguments of to_hdf except for the argument 'path_or_buf' will be keyword-only.\n",
+ " series.to_hdf(store, dsname, format='t', data_columns=True)\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:146: FutureWarning: Starting with pandas version 3.0 all arguments of to_hdf except for the argument 'path_or_buf' will be keyword-only.\n",
+ " series.to_hdf(store, dsname, format='t', data_columns=True)\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2tools/readWDM.py:146: FutureWarning: Starting with pandas version 3.0 all arguments of to_hdf except for the argument 'path_or_buf' will be keyword-only.\n",
+ " series.to_hdf(store, dsname, format='t', data_columns=True)\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " Start | \n",
+ " Stop | \n",
+ " Freq | \n",
+ " Length | \n",
+ " TSTYPE | \n",
+ " TFILL | \n",
+ " STAID | \n",
+ " STNAM | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " TS039 | \n",
+ " 1976-01-01 00:00:00 | \n",
+ " 1977-01-01 00:00:00 | \n",
+ " 1H | \n",
+ " 8784 | \n",
+ " PREC | \n",
+ " -999.0000 | \n",
+ " HOURLY PREC | \n",
+ " PRECIP TRAER IOWA | \n",
+ "
\n",
+ " \n",
+ " TS041 | \n",
+ " 1976-01-01 00:00:00 | \n",
+ " 1977-01-01 00:00:00 | \n",
+ " 1D | \n",
+ " 366 | \n",
+ " EVAP | \n",
+ " -999.0000 | \n",
+ " DAILY EVAP | \n",
+ " FARMERS COOP WEATHER STN | \n",
+ "
\n",
+ " \n",
+ " TS042 | \n",
+ " 1976-01-01 00:00:00 | \n",
+ " 1977-01-01 00:00:00 | \n",
+ " 1D | \n",
+ " 366 | \n",
+ " WIND | \n",
+ " -999.0000 | \n",
+ " DAILY WIND | \n",
+ " FARMERS COOP WEATHER STN | \n",
+ "
\n",
+ " \n",
+ " TS046 | \n",
+ " 1976-01-01 00:00:00 | \n",
+ " 1977-01-01 00:00:00 | \n",
+ " 2H | \n",
+ " 4392 | \n",
+ " SOLR | \n",
+ " -999.0000 | \n",
+ " 2 HOUR RAD | \n",
+ " FARMERS WEATHER STN | \n",
+ "
\n",
+ " \n",
+ " TS113 | \n",
+ " 1976-01-01 00:00:00 | \n",
+ " 1977-01-01 00:00:00 | \n",
+ " 1D | \n",
+ " 366 | \n",
+ " FLOW | \n",
+ " -999.0000 | \n",
+ " DAILY FLOW | \n",
+ " IOWA RIVER MARSHALLTOWN | \n",
+ "
\n",
+ " \n",
+ " TS119 | \n",
+ " 1976-01-01 00:00:00 | \n",
+ " 1977-01-01 00:00:00 | \n",
+ " 1D | \n",
+ " 366 | \n",
+ " FLOW | \n",
+ " -999.0000 | \n",
+ " DAILY FLOW | \n",
+ " IOWA RIVER MARENGO | \n",
+ "
\n",
+ " \n",
+ " TS121 | \n",
+ " 1976-01-01 00:00:00 | \n",
+ " 1977-01-01 00:00:00 | \n",
+ " 2H | \n",
+ " 4392 | \n",
+ " ATMP | \n",
+ " -999.0000 | \n",
+ " 2 HOUR AIR TEMP | \n",
+ " CEDAR RAPIDS IOWA | \n",
+ "
\n",
+ " \n",
+ " TS122 | \n",
+ " 1976-01-01 00:00:00 | \n",
+ " 1977-01-01 00:00:00 | \n",
+ " 2H | \n",
+ " 4392 | \n",
+ " ATMP | \n",
+ " -999.0000 | \n",
+ " 2 HOUR AIR TEMP | \n",
+ " IOWA FALLS IOWA | \n",
+ "
\n",
+ " \n",
+ " TS123 | \n",
+ " 1976-01-01 00:00:00 | \n",
+ " 1977-01-01 00:00:00 | \n",
+ " 2H | \n",
+ " 4392 | \n",
+ " ATMP | \n",
+ " -999.0000 | \n",
+ " 2 HOUR AIR TEMP | \n",
+ " MARSHALLTOWN IOWA | \n",
+ "
\n",
+ " \n",
+ " TS124 | \n",
+ " 1976-01-01 00:00:00 | \n",
+ " 1977-01-01 00:00:00 | \n",
+ " 1D | \n",
+ " 366 | \n",
+ " DEWP | \n",
+ " -999.0000 | \n",
+ " DAILY DEW PT | \n",
+ " CEDAR RAPIDS IOWA | \n",
+ "
\n",
+ " \n",
+ " TS125 | \n",
+ " 1976-01-01 00:00:00 | \n",
+ " 1977-01-01 00:00:00 | \n",
+ " 1D | \n",
+ " 366 | \n",
+ " DEWP | \n",
+ " -999.0000 | \n",
+ " DAILY DEW PT | \n",
+ " IOWA FALLS IOWA | \n",
+ "
\n",
+ " \n",
+ " TS126 | \n",
+ " 1976-01-01 00:00:00 | \n",
+ " 1977-01-01 00:00:00 | \n",
+ " 1D | \n",
+ " 366 | \n",
+ " DEWP | \n",
+ " -999.0000 | \n",
+ " DAILY DEW PT | \n",
+ " MARSHALLTOWN IOWA | \n",
+ "
\n",
+ " \n",
+ " TS127 | \n",
+ " 1976-01-01 00:00:00 | \n",
+ " 1977-01-01 00:00:00 | \n",
+ " 1D | \n",
+ " 366 | \n",
+ " SEDM | \n",
+ " -999.0000 | \n",
+ " SEDIMENT CONC | \n",
+ " IOWA RIVER MARENGO | \n",
+ "
\n",
+ " \n",
+ " TS131 | \n",
+ " 1976-01-01 00:00:00 | \n",
+ " 1977-01-01 00:00:00 | \n",
+ " 1H | \n",
+ " 8784 | \n",
+ " PREC | \n",
+ " -999.0000 | \n",
+ " HOURLY PREC | \n",
+ " PRECIP IOWA FALLS | \n",
+ "
\n",
+ " \n",
+ " TS132 | \n",
+ " 1976-01-01 00:00:00 | \n",
+ " 1977-01-01 00:00:00 | \n",
+ " 1H | \n",
+ " 8784 | \n",
+ " PREC | \n",
+ " -999.0000 | \n",
+ " HOURLY PREC | \n",
+ " PRECIP SHEFFIELD | \n",
+ "
\n",
+ " \n",
+ " TS134 | \n",
+ " 1976-01-01 00:00:00 | \n",
+ " 1977-01-01 00:00:00 | \n",
+ " 1D | \n",
+ " 366 | \n",
+ " WTMP | \n",
+ " -999.0000 | \n",
+ " WATER TEMP | \n",
+ " IOWA RIVER (ESTIMATED) | \n",
+ "
\n",
+ " \n",
+ " TS135 | \n",
+ " 1976-01-01 00:00:00 | \n",
+ " 1977-01-01 00:00:00 | \n",
+ " 1D | \n",
+ " 366 | \n",
+ " CLDC | \n",
+ " -999.0000 | \n",
+ " CLOUD COVER | \n",
+ " WATERLOO (TENTHS) | \n",
+ "
\n",
+ " \n",
+ " TS136 | \n",
+ " 1976-01-01 00:00:00 | \n",
+ " 1977-01-01 00:00:00 | \n",
+ " 1D | \n",
+ " 366 | \n",
+ " FLOW | \n",
+ " -999.0000 | \n",
+ " DAILY FLOW | \n",
+ " IOWA RIVER ROWAN | \n",
+ "
\n",
+ " \n",
+ " TS140 | \n",
+ " 1976-01-01 00:00:00 | \n",
+ " 1977-01-01 00:00:00 | \n",
+ " 1D | \n",
+ " 366 | \n",
+ " CLND | \n",
+ " -999.0000 | \n",
+ " COLIND | \n",
+ " COLUMN INDICATOR MEIER POND SUMMER-WINTER OUTLET | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " Start Stop Freq Length TSTYPE TFILL \\\n",
+ "TS039 1976-01-01 00:00:00 1977-01-01 00:00:00 1H 8784 PREC -999.0000 \n",
+ "TS041 1976-01-01 00:00:00 1977-01-01 00:00:00 1D 366 EVAP -999.0000 \n",
+ "TS042 1976-01-01 00:00:00 1977-01-01 00:00:00 1D 366 WIND -999.0000 \n",
+ "TS046 1976-01-01 00:00:00 1977-01-01 00:00:00 2H 4392 SOLR -999.0000 \n",
+ "TS113 1976-01-01 00:00:00 1977-01-01 00:00:00 1D 366 FLOW -999.0000 \n",
+ "TS119 1976-01-01 00:00:00 1977-01-01 00:00:00 1D 366 FLOW -999.0000 \n",
+ "TS121 1976-01-01 00:00:00 1977-01-01 00:00:00 2H 4392 ATMP -999.0000 \n",
+ "TS122 1976-01-01 00:00:00 1977-01-01 00:00:00 2H 4392 ATMP -999.0000 \n",
+ "TS123 1976-01-01 00:00:00 1977-01-01 00:00:00 2H 4392 ATMP -999.0000 \n",
+ "TS124 1976-01-01 00:00:00 1977-01-01 00:00:00 1D 366 DEWP -999.0000 \n",
+ "TS125 1976-01-01 00:00:00 1977-01-01 00:00:00 1D 366 DEWP -999.0000 \n",
+ "TS126 1976-01-01 00:00:00 1977-01-01 00:00:00 1D 366 DEWP -999.0000 \n",
+ "TS127 1976-01-01 00:00:00 1977-01-01 00:00:00 1D 366 SEDM -999.0000 \n",
+ "TS131 1976-01-01 00:00:00 1977-01-01 00:00:00 1H 8784 PREC -999.0000 \n",
+ "TS132 1976-01-01 00:00:00 1977-01-01 00:00:00 1H 8784 PREC -999.0000 \n",
+ "TS134 1976-01-01 00:00:00 1977-01-01 00:00:00 1D 366 WTMP -999.0000 \n",
+ "TS135 1976-01-01 00:00:00 1977-01-01 00:00:00 1D 366 CLDC -999.0000 \n",
+ "TS136 1976-01-01 00:00:00 1977-01-01 00:00:00 1D 366 FLOW -999.0000 \n",
+ "TS140 1976-01-01 00:00:00 1977-01-01 00:00:00 1D 366 CLND -999.0000 \n",
+ "\n",
+ " STAID STNAM \n",
+ "TS039 HOURLY PREC PRECIP TRAER IOWA \n",
+ "TS041 DAILY EVAP FARMERS COOP WEATHER STN \n",
+ "TS042 DAILY WIND FARMERS COOP WEATHER STN \n",
+ "TS046 2 HOUR RAD FARMERS WEATHER STN \n",
+ "TS113 DAILY FLOW IOWA RIVER MARSHALLTOWN \n",
+ "TS119 DAILY FLOW IOWA RIVER MARENGO \n",
+ "TS121 2 HOUR AIR TEMP CEDAR RAPIDS IOWA \n",
+ "TS122 2 HOUR AIR TEMP IOWA FALLS IOWA \n",
+ "TS123 2 HOUR AIR TEMP MARSHALLTOWN IOWA \n",
+ "TS124 DAILY DEW PT CEDAR RAPIDS IOWA \n",
+ "TS125 DAILY DEW PT IOWA FALLS IOWA \n",
+ "TS126 DAILY DEW PT MARSHALLTOWN IOWA \n",
+ "TS127 SEDIMENT CONC IOWA RIVER MARENGO \n",
+ "TS131 HOURLY PREC PRECIP IOWA FALLS \n",
+ "TS132 HOURLY PREC PRECIP SHEFFIELD \n",
+ "TS134 WATER TEMP IOWA RIVER (ESTIMATED) \n",
+ "TS135 CLOUD COVER WATERLOO (TENTHS) \n",
+ "TS136 DAILY FLOW IOWA RIVER ROWAN \n",
+ "TS140 COLIND COLUMN INDICATOR MEIER POND SUMMER-WINTER OUTLET "
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "%time\n",
+ "readWDM(wdmname, hdfname)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Run"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Using profiler"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2024-04-28T16:45:47.571561Z",
+ "iopub.status.busy": "2024-04-28T16:45:47.571360Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.789398Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.787335Z",
+ "shell.execute_reply.started": "2024-04-28T16:45:47.571546Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-04-28 12:45:47.57 Processing started for file test10_hsp2_dev2WDM_6.h5; saveall=True\n",
+ "2024-04-28 12:45:53.19 Simulation Start: 1976-01-01 00:00:00, Stop: 1977-01-01 00:00:00\n",
+ "2024-04-28 12:45:53.19 PERLND P001 DELT(minutes): 60\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2/utilities.py:93: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`\n",
+ " ts[stop] = ts[-1]\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2/utilities.py:93: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`\n",
+ " ts[stop] = ts[-1]\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2/utilities.py:93: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`\n",
+ " ts[stop] = ts[-1]\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2/utilities.py:93: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`\n",
+ " ts[stop] = ts[-1]\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2/utilities.py:93: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`\n",
+ " ts[stop] = ts[-1]\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2/utilities.py:93: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`\n",
+ " ts[stop] = ts[-1]\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2/utilities.py:93: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`\n",
+ " ts[stop] = ts[-1]\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-04-28 12:45:55.58 SNOW\n",
+ "2024-04-28 12:45:56.63 PWATER\n",
+ "2024-04-28 12:45:57.15 PSTEMP\n",
+ "2024-04-28 12:45:58.40 PWTGAS\n",
+ "2024-04-28 12:46:01.06 RCHRES R001 DELT(minutes): 60\n",
+ "2024-04-28 12:46:01.09 HYDR\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2/utilities.py:93: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`\n",
+ " ts[stop] = ts[-1]\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2/utilities.py:93: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`\n",
+ " ts[stop] = ts[-1]\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2/utilities.py:93: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`\n",
+ " ts[stop] = ts[-1]\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2/utilities.py:93: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`\n",
+ " ts[stop] = ts[-1]\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2/utilities.py:93: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`\n",
+ " ts[stop] = ts[-1]\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2/utilities.py:93: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`\n",
+ " ts[stop] = ts[-1]\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2/utilities.py:93: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`\n",
+ " ts[stop] = ts[-1]\n",
+ "/home/tim/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2/utilities.py:93: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`\n",
+ " ts[stop] = ts[-1]\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2024-04-28 12:46:12.15 ADCALC\n",
+ "2024-04-28 12:46:13.34 CONS\n",
+ "2024-04-28 12:46:15.37 HTRCH\n",
+ "2024-04-28 12:46:18.95 SEDTRN\n",
+ "2024-04-28 12:46:35.24 RQUAL\n"
+ ]
+ },
+ {
+ "ename": "SystemError",
+ "evalue": "CPUDispatcher() returned NULL without setting an exception",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mSystemError\u001b[0m Traceback (most recent call last)",
+ "Cell \u001b[0;32mIn[9], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mget_ipython\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun_cell_magic\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mprun\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43m -l 60 -T NumbaProfile.txt -q\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mmain(hdfname, saveall=True)\u001b[39;49m\u001b[38;5;130;43;01m\\n\u001b[39;49;00m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m)\u001b[49m\n",
+ "File \u001b[0;32m~/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/IPython/core/interactiveshell.py:2541\u001b[0m, in \u001b[0;36mInteractiveShell.run_cell_magic\u001b[0;34m(self, magic_name, line, cell)\u001b[0m\n\u001b[1;32m 2539\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mbuiltin_trap:\n\u001b[1;32m 2540\u001b[0m args \u001b[38;5;241m=\u001b[39m (magic_arg_s, cell)\n\u001b[0;32m-> 2541\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 2543\u001b[0m \u001b[38;5;66;03m# The code below prevents the output from being displayed\u001b[39;00m\n\u001b[1;32m 2544\u001b[0m \u001b[38;5;66;03m# when using magics with decorator @output_can_be_silenced\u001b[39;00m\n\u001b[1;32m 2545\u001b[0m \u001b[38;5;66;03m# when the last Python token in the expression is a ';'.\u001b[39;00m\n\u001b[1;32m 2546\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mgetattr\u001b[39m(fn, magic\u001b[38;5;241m.\u001b[39mMAGIC_OUTPUT_CAN_BE_SILENCED, \u001b[38;5;28;01mFalse\u001b[39;00m):\n",
+ "File \u001b[0;32m~/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/IPython/core/magics/execution.py:313\u001b[0m, in \u001b[0;36mExecutionMagics.prun\u001b[0;34m(self, parameter_s, cell)\u001b[0m\n\u001b[1;32m 311\u001b[0m arg_str \u001b[38;5;241m+\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124m'\u001b[39m \u001b[38;5;241m+\u001b[39m cell\n\u001b[1;32m 312\u001b[0m arg_str \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mshell\u001b[38;5;241m.\u001b[39mtransform_cell(arg_str)\n\u001b[0;32m--> 313\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_run_with_profiler\u001b[49m\u001b[43m(\u001b[49m\u001b[43marg_str\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mopts\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mshell\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43muser_ns\u001b[49m\u001b[43m)\u001b[49m\n",
+ "File \u001b[0;32m~/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/IPython/core/magics/execution.py:335\u001b[0m, in \u001b[0;36mExecutionMagics._run_with_profiler\u001b[0;34m(self, code, opts, namespace)\u001b[0m\n\u001b[1;32m 333\u001b[0m prof \u001b[38;5;241m=\u001b[39m profile\u001b[38;5;241m.\u001b[39mProfile()\n\u001b[1;32m 334\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 335\u001b[0m prof \u001b[38;5;241m=\u001b[39m \u001b[43mprof\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrunctx\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcode\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnamespace\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnamespace\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 336\u001b[0m sys_exit \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 337\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mSystemExit\u001b[39;00m:\n",
+ "File \u001b[0;32m~/anaconda3/envs/hsp2_310/lib/python3.10/cProfile.py:101\u001b[0m, in \u001b[0;36mProfile.runctx\u001b[0;34m(self, cmd, globals, locals)\u001b[0m\n\u001b[1;32m 99\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39menable()\n\u001b[1;32m 100\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 101\u001b[0m \u001b[43mexec\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcmd\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mglobals\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mlocals\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 102\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[1;32m 103\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdisable()\n",
+ "File \u001b[0;32m:1\u001b[0m\n",
+ "File \u001b[0;32m~/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2/main.py:242\u001b[0m, in \u001b[0;36mmain\u001b[0;34m(io_manager, saveall, jupyterlab)\u001b[0m\n\u001b[1;32m 240\u001b[0m errors, errmessages \u001b[38;5;241m=\u001b[39m function(io_manager, siminfo, ui, ts)\n\u001b[1;32m 241\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m--> 242\u001b[0m errors, errmessages \u001b[38;5;241m=\u001b[39m \u001b[43mfunction\u001b[49m\u001b[43m(\u001b[49m\u001b[43mio_manager\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msiminfo\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mui\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mui_oxrx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mui_nutrx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mui_plank\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mui_phcarb\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mts\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmonthdata\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 243\u001b[0m \u001b[38;5;66;03m###############################################################\u001b[39;00m\n\u001b[1;32m 245\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m errorcnt, errormsg \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mzip\u001b[39m(errors, errmessages):\n",
+ "File \u001b[0;32m~/anaconda3/envs/hsp2_310/lib/python3.10/site-packages/HSP2/RQUAL.py:225\u001b[0m, in \u001b[0;36mrqual\u001b[0;34m(io_manager, siminfo, uci, uci_oxrx, uci_nutrx, uci_plank, uci_phcarb, ts, monthdata)\u001b[0m\n\u001b[1;32m 218\u001b[0m \t\tts[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mBINV\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m initm(siminfo, ui_plank, binvfg, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mMONTHLY/BINV\u001b[39m\u001b[38;5;124m'\u001b[39m, binv_init)\n\u001b[1;32m 220\u001b[0m \u001b[38;5;66;03m#---------------------------------------------------------------------\u001b[39;00m\n\u001b[1;32m 221\u001b[0m \u001b[38;5;66;03m# initialize & run integerated WQ simulation:\u001b[39;00m\n\u001b[1;32m 222\u001b[0m \u001b[38;5;66;03m#---------------------------------------------------------------------\u001b[39;00m\n\u001b[1;32m 224\u001b[0m (err_oxrx, err_nutrx, err_plank, err_phcarb) \\\n\u001b[0;32m--> 225\u001b[0m \t\u001b[38;5;241m=\u001b[39m \u001b[43m_rqual_run\u001b[49m\u001b[43m(\u001b[49m\u001b[43msiminfo_\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mui\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mui_oxrx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mui_nutrx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mui_plank\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mui_phcarb\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mts\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 227\u001b[0m \u001b[38;5;66;03m#---------------------------------------------------------------------\u001b[39;00m\n\u001b[1;32m 228\u001b[0m \u001b[38;5;66;03m# compile errors & return:\u001b[39;00m\n\u001b[1;32m 229\u001b[0m \u001b[38;5;66;03m#---------------------------------------------------------------------\u001b[39;00m\n\u001b[1;32m 231\u001b[0m (errors, ERRMSGS) \u001b[38;5;241m=\u001b[39m _compile_errors(NUTFG, PLKFG, PHFG, err_oxrx, err_nutrx, err_plank, err_phcarb)\n",
+ "\u001b[0;31mSystemError\u001b[0m: CPUDispatcher() returned NULL without setting an exception"
+ ]
+ }
+ ],
+ "source": [
+ "%%prun -l 60 -T NumbaProfile.txt -q\n",
+ "main(hdfname, saveall=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.789831Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.790023Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.789935Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.789927Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "with open(\"NumbaProfile.txt\", \"r\") as f:\n",
+ " for line in f:\n",
+ " total = float(line.split()[7])\n",
+ " print(line)\n",
+ " f.readline()\n",
+ " f.readline()\n",
+ " f.readline()\n",
+ " f.readline()\n",
+ " print(f.readline().rstrip())\n",
+ " break\n",
+ " sum = 0.0\n",
+ " for line in f:\n",
+ " sum += float(line.split()[1])\n",
+ " print(line.rstrip())\n",
+ "\n",
+ " # Pareto rule, look for calls cumsum to 80% time, ignore the rest\n",
+ " if sum >= 0.8 * total:\n",
+ " break\n",
+ "print(f\"Cum Sum {100.0 * sum/total:.2f}%\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## BELOW HERE: Output from Steve's testing"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Normal run"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.790785Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.791030Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.790925Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.790915Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "main(hdfname, saveall=True)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "metadata": {},
+ "source": [
+ "Development note:\n",
+ "\n",
+ "SAVEALL = FALSE (new SAVE table for hot restart by default)\n",
+ "2017-04-16 09:05:26.10 Run completed\n",
+ "1 loop, best of 3: 32.3 s per loop\n",
+ "Ratio of HSP2/HSPF 32.3/24.4 = 1.3\n",
+ "\n",
+ "SAVEALL = TRUE\n",
+ "2017-04-11 10:18:22.86 Run completed\n",
+ "1 loop, best of 3: 38.1 s per loop\n",
+ "Ratio of HSP2/HSPF 38.1/24.4 = 1.6\n",
+ "\n",
+ "04/25/2020 New Python 3 version is about 1.8x slower than HSPF - even when all tricks used.\n",
+ "However, the new Numba Dict and List implementations are SLOW in the code outside numba - on the order\n",
+ "of us vs ns which can easily explain a slight difference. Spotted warning messages in HSP2 log file.\n",
+ "\n",
+ "05/04/2020 new Calleg testing For reference the run time of HSPF is 32.2 seconds. Using to_hdf()\n",
+ "With Flush - create test10.h5 Run time is about 01:56.9 size 2,136,787 KB\n",
+ " existing test10.h5 Run time is about 01:15.2 size 2,136,836 KB\n",
+ "NO Flush - create test10.h5 Run time is about 01:19.1 size 2,136,787 KB Warning messages seen for closed node\n",
+ " existing test10.h5 Run time is about 01:14.4 size 2,236,836 KB\n",
+ "BLOSC - create test10.h5 Run time is about 01:29.0 size 1,156,499 KB No Flush, but no warning messages (Blosc in save_timeseries only)\n",
+ " existing test10.h5 Run time is about 01:26.2 size 1,156,796 KB\n",
+ " \n",
+ "BLOSC (wdm & main)\n",
+ " - create test10.h5 Run time is about 01:30.8 size 1,050,043 KB No Flush, no warning messages.\n",
+ " existing test10.h5 Run time is about 01:26.0 size 1,060,340 KB\n",
+ " existing test10.h5 Run time is about 01:23.6 (5/8/2020)\n",
+ " existing test10.h5 Run time is about 01:19.7 (5/8/2020) - no kernel restart from last run\n",
+ " \n",
+ "NO Numba - existing test10.h5 Run time is about 13:25.1 or x9.4 slower than with Numba.\n",
+ "\n",
+ "So current timing results show HSP2 is 2.8x slower than HSPF for this problem.\n",
+ "\n",
+ "Also tried return to store.put() with BLOSC (wdm & main) instead of to_hdf()\n",
+ " - existing test10.h5 Run time is about 01:28.0 but found warning messages for closed node. Speedup not worth it.\n",
+ " "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Read Summary from HBN file"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Find the higest frequency data available."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.791692Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.791879Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.791791Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.791782Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "summary = read_hdf(HBN, \"SUMMARY\")\n",
+ "summary.Frequency.unique()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "So the best available is monthly data."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Automate checking by output flow"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.792607Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.792790Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.792705Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.792697Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "def hsp2key(hspfkey):\n",
+ " op, activity, segment, _ = hspfkey.split(\"_\")\n",
+ " return f\"RESULTS/{op}_{op[0]}{segment:0>3s}/{activity}\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.793367Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.793534Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.793455Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.793447Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "def monthlyoutflow(keys, flowname):\n",
+ " data = DataFrame()\n",
+ " for key in keys.index:\n",
+ " hsp2 = read_hdf(hdfname, hsp2key(key))[flowname].resample(\"M\").sum().to_numpy()\n",
+ " hspf = read_hdf(HBN, key)[flowname].to_numpy()\n",
+ "\n",
+ " hsp2sum = hsp2.sum()\n",
+ " hspfsum = hspf.sum()\n",
+ "\n",
+ " segment = key.split(\"_\")[2]\n",
+ "\n",
+ " data.loc[segment, \"HSP2 SUM\"] = hsp2.sum()\n",
+ " data.loc[segment, \"HSPF SUM\"] = hspf.sum()\n",
+ " data.loc[segment, \"Percent diff\"] = 100.0 * (hspfsum - hsp2sum) / hspfsum\n",
+ " data.loc[segment, \"abs(Percent diff)\"] = (\n",
+ " 100.0 * abs(hspfsum - hsp2sum) / hspfsum\n",
+ " )\n",
+ "\n",
+ " return data.sort_values(by=[\"abs(Percent diff)\"])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Define a function to plot HSPF and HSP2 data"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.793994Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.794166Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.794086Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.794078Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "def plot(operation, ils, activity, name, how=\"sum\"):\n",
+ " hspf = f\"{operation}_{activity}_{ils:0>3s}_4\"\n",
+ " tshspf = read_hdf(HBN, hspf)[name]\n",
+ "\n",
+ " hsp2 = f\"RESULTS/{operation}_{operation[0]}{ils:0>3s}/{activity}\"\n",
+ " if how == \"sum\":\n",
+ " tshsp2 = (\n",
+ " read_hdf(hdfname, hsp2)[f\"{name}_{how}\"].resample(\"M\").sum().shift(1, \"D\")\n",
+ " )\n",
+ " elif how == \"last\":\n",
+ " tshsp2 = (\n",
+ " read_hdf(hdfname, hsp2)[f\"{name}_{how}\"].resample(\"M\").last().shift(1, \"D\")\n",
+ " )\n",
+ "\n",
+ " plt.figure(figsize=(10, 8))\n",
+ " plt.plot(tshspf.index, tshspf, label=\"HSPF\", color=\"r\")\n",
+ " plt.plot(tshsp2.index, tshsp2, label=\"HSP2\", color=\"b\")\n",
+ " plt.legend()\n",
+ " plt.title(f\"{operation} {operation[0]}{ils:0>3s}, {activity} {name}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.794953Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.795205Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.795114Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.795106Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "def scatter(operation, ils, activity, name, how=\"sum\"):\n",
+ " hspf = f\"{operation}_{activity}_{ils:0>3s}_4\"\n",
+ " tshspf = read_hdf(HBN, hspf)[name]\n",
+ "\n",
+ " hsp2 = f\"RESULTS/{operation}_{operation[0]}{ils:0>3s}/{activity}\"\n",
+ " if how == \"sum\":\n",
+ " tshsp2 = (\n",
+ " read_hdf(hdfname, hsp2)[f\"{name}_{how}\"].resample(\"M\").sum().shift(1, \"D\")\n",
+ " )\n",
+ " elif how == \"last\":\n",
+ " tshsp2 = (\n",
+ " read_hdf(hdfname, hsp2)[f\"{name}_{how}\"].resample(\"M\").last().shift(1, \"D\")\n",
+ " )\n",
+ "\n",
+ " plt.scatter(tshspf, tshsp2)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### IMPLND"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.795724Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.795951Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.795866Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.795858Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "implndkeys = summary[(summary.Frequency == \"Monthly\") & (summary.Operation == \"IMPLND\")]\n",
+ "implndkeys[0:10]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.796421Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.796643Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.796560Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.796552Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "delta = monthlyoutflow(implndkeys, \"SURO\")\n",
+ "delta"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Look at the statistics for the percent difference column"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.797125Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.797416Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.797332Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.797324Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "delta.describe()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.797835Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.798053Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.797969Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.797961Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "delta[\"Percent diff\"].hist()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.798483Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.798717Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.798637Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.798629Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "ils = delta.index[-1]\n",
+ "print(\"WORST IMPLND SEGMENT IS\", ils)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### IMPLND IWATER SURO, Monthly"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.799127Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.799401Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.799320Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.799312Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"IMPLND\", ils, \"IWATER\", \"SURO\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.799811Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.799997Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.799937Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.799931Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"IMPLND\", ils, \"IWATER\", \"SURO\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### IMPLND IWATER IMPEV, Monthly"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.800364Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.800525Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.800472Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.800466Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"IMPLND\", ils, \"IWATER\", \"IMPEV\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.800820Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.800925Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.800875Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.800870Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"IMPLND\", ils, \"IWATER\", \"IMPEV\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### IMPLND IWATER PET, Monthly"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.801179Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.801281Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.801232Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.801227Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"IMPLND\", ils, \"IWATER\", \"PET\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.801629Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.801742Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.801689Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.801684Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"IMPLND\", ils, \"IWATER\", \"PET\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### IMPLND IWATER RETS, Monthly"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.802034Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.802140Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.802088Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.802084Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"IMPLND\", ils, \"IWATER\", \"RETS\", \"last\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.802456Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.802559Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.802511Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.802506Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"IMPLND\", ils, \"IWATER\", \"RETS\", \"last\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### IMPLND IWATER SUPY, Monthly"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.802805Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.802909Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.802861Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.802853Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"IMPLND\", ils, \"IWATER\", \"SUPY\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.803196Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.803295Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.803248Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.803243Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"IMPLND\", ils, \"IWATER\", \"SUPY\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### IMPLND IWATER SURS, Monthly"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.803608Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.803715Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.803666Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.803661Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"IMPLND\", ils, \"IWATER\", \"SURS\", \"last\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.804024Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.804126Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.804078Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.804073Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"IMPLND\", ils, \"IWATER\", \"SURS\", \"last\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Automate checking PERLNDs for PERO"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.804408Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.804507Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.804459Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.804455Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "perlndkeys = summary[(summary.Frequency == \"Monthly\") & (summary.Operation == \"PERLND\")]\n",
+ "perlndkeys[0:10]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.804766Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.804863Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.804817Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.804812Z"
+ },
+ "scrolled": true,
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "delta = monthlyoutflow(perlndkeys, \"PERO\")\n",
+ "delta"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Look at the statistics for the percent difference column"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.805199Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.805309Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.805260Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.805254Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "delta.describe()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.805681Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.805793Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.805743Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.805737Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "delta[\"Percent diff\"].hist()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.806219Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.806350Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.806289Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.806283Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "pls = delta.index[-1]\n",
+ "print(\"WORST PERLND SEGMENT IS\", pls)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### PERLND PWATER AGWLI"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.806740Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.806886Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.806814Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.806808Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"PERLND\", pls, \"PWATER\", \"AGWLI\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.807172Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.807318Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.807251Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.807244Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"PERLND\", pls, \"PWATER\", \"AGWLI\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### PERLND PWATER SURLI"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.807945Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.808090Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.808026Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.808020Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"PERLND\", pls, \"PWATER\", \"SURLI\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.808424Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.808562Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.808497Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.808491Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"PERLND\", pls, \"PWATER\", \"SURLI\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### PERLND PWATER AGWO"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.809143Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.809260Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.809207Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.809202Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"PERLND\", pls, \"PWATER\", \"AGWO\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.809599Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.809703Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.809653Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.809648Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"PERLND\", pls, \"PWATER\", \"AGWO\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### PERLND PWATER BASET"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.810200Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.810358Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.810277Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.810270Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"PERLND\", pls, \"PWATER\", \"BASET\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.810827Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.810995Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.810926Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.810920Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"PERLND\", pls, \"PWATER\", \"BASET\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "##### PERLND PWATER CEPE"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.811302Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.811424Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.811366Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.811361Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"PERLND\", pls, \"PWATER\", \"CEPE\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.811817Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.811925Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.811875Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.811870Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"PERLND\", pls, \"PWATER\", \"CEPE\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### PERLND PWATER IFWI"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.812350Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.812455Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.812403Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.812398Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"PERLND\", pls, \"PWATER\", \"IFWI\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.812816Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.812974Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.812909Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.812901Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"PERLND\", pls, \"PWATER\", \"IFWI\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### PERLND PWATER IFWO"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.813322Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.813469Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.813409Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.813403Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"PERLND\", pls, \"PWATER\", \"IFWO\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.814100Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.814241Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.814178Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.814171Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"PERLND\", pls, \"PWATER\", \"IFWO\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### PERLND PWATER IGWI"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.814642Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.814781Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.814719Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.814713Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"PERLND\", pls, \"PWATER\", \"IGWI\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.815196Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.815325Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.815265Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.815259Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"PERLND\", pls, \"PWATER\", \"IGWI\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### PERLND PWATER INFIL"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.815862Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.815972Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.815920Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.815915Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"PERLND\", pls, \"PWATER\", \"INFIL\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.816307Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.816446Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.816359Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.816354Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"PERLND\", pls, \"PWATER\", \"INFIL\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### PERLND PWATER LZET"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.816833Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.816974Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.816908Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.816901Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"PERLND\", pls, \"PWATER\", \"LZET\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.817354Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.817492Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.817427Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.817420Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"PERLND\", pls, \"PWATER\", \"LZET\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### PERLND PWATER PERC"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.817940Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.818074Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.818011Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.818005Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"PERLND\", pls, \"PWATER\", \"PERC\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.818461Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.818570Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.818520Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.818515Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"PERLND\", pls, \"PWATER\", \"PERC\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### PERLND PWATER PERO"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.819065Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.819219Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.819143Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.819136Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"PERLND\", pls, \"PWATER\", \"PERO\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.819926Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.820060Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.819999Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.819993Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"PERLND\", pls, \"PWATER\", \"PERO\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### PERLND PWATER PERS"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.820477Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.820590Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.820538Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.820533Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"PERLND\", pls, \"PWATER\", \"PERS\", \"last\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.821046Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.821151Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.821101Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.821096Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"PERLND\", pls, \"PWATER\", \"PERS\", \"last\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### PERLND PWATER PET, Monthly"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.821568Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.821733Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.821667Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.821660Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"PERLND\", pls, \"PWATER\", \"PET\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.822186Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.822327Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.822262Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.822256Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"PERLND\", pls, \"PWATER\", \"PET\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### PERLND PWATER SUPY"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.822942Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.823084Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.823020Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.823014Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"PERLND\", pls, \"PWATER\", \"SUPY\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.823410Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.823541Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.823481Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.823475Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"PERLND\", pls, \"PWATER\", \"SUPY\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### PERLND PWATER SURO"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.823928Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.824052Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.823994Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.823988Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"PERLND\", pls, \"PWATER\", \"SURO\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.824587Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.824696Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.824646Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.824641Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"PERLND\", pls, \"PWATER\", \"SURO\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### PERLND PWATER TAET"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.824966Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.825072Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.825022Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.825017Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"PERLND\", pls, \"PWATER\", \"TAET\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.825613Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.825757Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.825690Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.825683Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"PERLND\", pls, \"PWATER\", \"TAET\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### PERLND PWATER UZET"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.826143Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.826253Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.826203Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.826198Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"PERLND\", pls, \"PWATER\", \"UZET\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.826578Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.826714Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.826648Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.826642Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"PERLND\", pls, \"PWATER\", \"UZET\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### PERLND PWATER UZI"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.827027Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.827177Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.827115Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.827109Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"PERLND\", pls, \"PWATER\", \"UZI\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.827817Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.827945Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.827887Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.827881Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"PERLND\", pls, \"PWATER\", \"UZI\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## RCHRES"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.828529Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.828662Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.828598Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.828592Z"
+ },
+ "scrolled": true,
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "rchreskeys = summary[(summary.Frequency == \"Monthly\") & (summary.Operation == \"RCHRES\")]\n",
+ "rchreskeys[0:10]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.829109Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.829220Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.829168Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.829163Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "delta = monthlyoutflow(rchreskeys, \"ROVOL\")\n",
+ "delta"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Look at the statistics for the percent difference column"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.829520Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.829624Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.829575Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.829570Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "delta.describe()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.829967Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.830075Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.830022Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.830017Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "delta[\"Percent diff\"].hist()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.830423Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.830530Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.830480Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.830475Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "rid = delta.index[-1]\n",
+ "print(\"WORST RCHRES SEGMENT IS\", rid)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### RCHRES HYDR IVOL"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.830926Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.831031Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.830981Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.830976Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"RCHRES\", rid, \"HYDR\", \"IVOL\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.831391Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.831494Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.831445Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.831440Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"RCHRES\", rid, \"HYDR\", \"IVOL\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### RCHRES HYDR PRSUPY"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.831747Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.831846Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.831799Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.831794Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"RCHRES\", rid, \"HYDR\", \"PRSUPY\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.832177Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.832284Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.832233Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.832227Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"RCHRES\", rid, \"HYDR\", \"PRSUPY\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### RCHRES HYDR ROVOL"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.832846Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.832948Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.832899Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.832894Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"RCHRES\", rid, \"HYDR\", \"ROVOL\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.833229Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.833331Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.833282Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.833277Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"RCHRES\", rid, \"HYDR\", \"ROVOL\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### RCHRES HYDR VOL"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.833627Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.833835Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.833733Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.833723Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"RCHRES\", rid, \"HYDR\", \"VOL\", \"last\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.834395Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.834543Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.834472Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.834467Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"RCHRES\", rid, \"HYDR\", \"VOL\", \"last\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### RCHRES HYDR VOLEV"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.835103Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.835259Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.835191Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.835185Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "plot(\"RCHRES\", rid, \"HYDR\", \"VOLEV\", \"sum\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "execution": {
+ "iopub.status.busy": "2024-04-28T16:47:35.835678Z",
+ "iopub.status.idle": "2024-04-28T16:47:35.835800Z",
+ "shell.execute_reply": "2024-04-28T16:47:35.835747Z",
+ "shell.execute_reply.started": "2024-04-28T16:47:35.835742Z"
+ },
+ "vscode": {
+ "languageId": "python"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "scatter(\"RCHRES\", rid, \"HYDR\", \"VOLEV\", \"sum\")"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.14"
+ },
+ "toc-showmarkdowntxt": false
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/tests/test10specl/HSP2results/test10.wdm b/tests/test10specl/HSP2results/test10.wdm
new file mode 100644
index 00000000..8d0e2766
Binary files /dev/null and b/tests/test10specl/HSP2results/test10.wdm differ
diff --git a/tests/test10specl/HSP2results/test10specl.uci b/tests/test10specl/HSP2results/test10specl.uci
new file mode 100644
index 00000000..54b48e69
--- /dev/null
+++ b/tests/test10specl/HSP2results/test10specl.uci
@@ -0,0 +1,1023 @@
+RUN
+
+GLOBAL
+ Version 11 test run: PERLND and IMPLND w/ RCHRES (sediment, water quality)
+ START 1976 END 1976
+ RUN INTERP OUTPUT LEVEL 3
+ RESUME 0 RUN 1 UNIT SYSTEM 1
+END GLOBAL
+
+ *** This test run uses MASS-LINK and SCHEMATIC blocks ***
+
+FILES
+ ***<----FILE NAME------------------------------------------------->
+WDM 21 test10.wdm
+MESSU 22 test10.ech
+ 01 test10.out
+ 66 test10.d66
+ 94 test10.p94
+ 95 test10.p95
+BINO 81 test10P.hbn
+BINO 82 test10I.hbn
+BINO 83 test10R.hbn
+END FILES
+
+OPN SEQUENCE
+ INGRP INDELT 01:00
+ PERLND 1
+ RCHRES 1
+ DISPLY 5
+ DISPLY 1
+ GENER 1
+ DISPLY 2
+ RCHRES 2
+ RCHRES 3
+ RCHRES 4
+ PLTGEN 2
+ IMPLND 1
+ RCHRES 5
+ DISPLY 3
+ GENER 2
+ DISPLY 4
+ PLTGEN 1
+ END INGRP
+END OPN SEQUENCE
+
+PERLND
+ ACTIVITY
+ Active Sections (1=Active, 0=Inactive) ***
+ # - # ATMP SNOW PWAT SED PST PWG PQAL MSTL PEST NITR PHOS TRAC ***
+ 1 1 1 1 1
+ END ACTIVITY
+
+ PRINT-INFO
+ Print-flags *** PIVL PYR
+ # - # ATMP SNOW PWAT SED PST PWG PQAL MSTL PEST NITR PHOS TRAC ***
+ 1 4 4 4 4 12
+ END PRINT-INFO
+
+ BINARY-INFO
+*** < PLS> Binary Output Flags PIVL PYR
+*** x - x ATMP SNOW PWAT SED PST PWG PQAL MSTL PEST NITR PHOS TRAC
+ 1 2 2 2 2 2 1 12
+ END BINARY-INFO
+
+ GEN-INFO
+ <-------Name-------> Unit-systems Printer BinaryOut ***
+ # - # t-series Engl Metr Engl Metr ***
+ in out ***
+ 1 BICKNELL FARM 1 1 1 0 81 0
+ END GEN-INFO
+
+ *** Section SNOW ***
+
+ ICE-FLAG
+ 0= Ice formation not simulated, 1= Simulated ***
+ # - #ICEFG ***
+ 1 1
+ END ICE-FLAG
+
+ SNOW-PARM1
+ Snow input info: Part 1 ***
+ # - # LAT MELEV SHADE SNOWCF COVIND ***
+ 1 42. 520. 0.0 1.45 0.5
+ END SNOW-PARM1
+
+ SNOW-PARM2
+ Snow input info: Part 2 ***
+ # - # RDCSN TSNOW SNOEVP CCFACT MWATER MGMELT ***
+ 1 0.12 32. 0.05 0.5 0.08 0.0001
+ END SNOW-PARM2
+
+ SNOW-INIT1
+ Initial snow conditions: Part 1 ***
+ # - # PACKSNOW PACKICE PACKWATER RDENPF DULL PAKTMP ***
+ 1 1.4 0.2 0.1 0.2 375. 27.5
+ END SNOW-INIT1
+
+ SNOW-INIT2
+ Initial snow conditions: Part 2 ***
+ # - # COVINX XLNMLT SKYCLR ***
+ 1 0.50 0.0 1.0
+ END SNOW-INIT2
+
+ *** Section PWATER ***
+
+ PWAT-PARM1
+ PWATER variable monthly parameter value flags ***
+ # - # CSNO RTOP UZFG VCS VUZ VNN VIFW VIRC VLE ***
+ 1 1 0 0 1 1 1 0 0 1
+ END PWAT-PARM1
+
+ PWAT-PARM2
+ *** PWATER input info: Part 2
+ # - # ***FOREST LZSN INFILT LSUR SLSUR KVARY AGWRC
+ 1 0.010 8.0 0.150 250. 0.050 0.5 0.98
+ END PWAT-PARM2
+
+ PWAT-PARM3
+ *** PWATER input info: Part 3
+ # - # ***PETMAX PETMIN INFEXP INFILD DEEPFR BASETP AGWETP
+ 1 40. 35. 2.0 2.0 0.10 0.0 0.08
+ END PWAT-PARM3
+
+ PWAT-PARM4
+ PWATER input info: Part 4 ***
+ # - # CEPSC UZSN NSUR INTFW IRC LZETP ***
+ 1 0.01 0.1 1.0 0.60
+ END PWAT-PARM4
+
+ MON-INTERCEP
+ Only required if VCSFG=1 in PWAT-PARM1 ***
+ # - # Interception storage capacity at start of each month ***
+ JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC ***
+ 1 0.04 0.04 0.03 0.03 0.03 0.03 0.10 0.17 0.19 0.14 0.05 0.04
+ END MON-INTERCEP
+
+ MON-UZSN
+ Only required if VUZFG=1 in PWAT-PARM1 ***
+ # - # Upper zone storage at start of each month ***
+ JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC ***
+ 1 0.4 0.4 0.4 0.4 1.6 1.1 1.1 1.3 1.3 1.3 1.1 0.9
+ END MON-UZSN
+
+ MON-MANNING
+ Only required if VNNFG=1 in PWAT-PARM1 ***
+ # - # Manning's n for overland flow at start of each month ***
+ JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC ***
+ 1 0.30 0.30 0.30 0.30 0.27 0.25 0.25 0.25 0.25 0.25 0.35 0.33
+ END MON-MANNING
+
+ MON-LZETPARM
+ Only required if VLEFG=1 in PWAT-PARM1 ***
+ # - # Lower zone ET parameter at start of each month ***
+ JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC ***
+ 1 0.20 0.20 0.20 0.23 0.23 0.25 0.60 0.80 0.75 0.50 0.30 0.20
+ END MON-LZETPARM
+
+ PWAT-STATE1
+ *** Initial conditions at start of simulation
+ # - # *** CEPS SURS UZS IFWS LZS AGWS GWVS
+ 1 0.05 0.0 0.15 0.0 4.0 0.05 0.05
+ END PWAT-STATE1
+
+ *** Section PSTEMP ***
+
+ PSTEMP-PARM2
+ ***
+ # - # ASLT BSLT ULTP1 ULTP2 LGTP1 LGTP2 ***
+ 1 14.5 .365 1.2 4.0 1.2 6.0
+ END PSTEMP-PARM2
+
+ *** Section PWTGAS ***
+
+ PWT-PARM2
+ ***
+ # - # ELEV IDOXP ICO2P ADOXP ACO2P ***
+ 1 500. 6. .05 5. .05
+ END PWT-PARM2
+END PERLND
+
+IMPLND
+ ACTIVITY
+ Active Sections ***
+ # - # ATMP SNOW IWAT SLD IWG IQAL ***
+ 1 1 1 1 1 1
+ END ACTIVITY
+
+ PRINT-INFO
+ Print-flags ***
+ # - # ATMP SNOW IWAT SLD IWG IQAL PIVL PYR ***
+ 1 4 4 4 4 4 12
+ END PRINT-INFO
+
+ BINARY-INFO
+*** **** Binary-Output-flags **** PIVL PYR
+*** x - x ATMP SNOW IWAT SLD IWG IQAL *********
+ 1 2 2 2 2 2 1 12
+ END BINARY-INFO
+
+ GEN-INFO
+ <-------Name-------> Unit-systems Printer BinaryOut ***
+ # - # t-series Engl Metr Engl Metr ***
+ in out ***
+ 1 DONIGIAN INDUSTRY 1 1 1 0 82 0
+ END GEN-INFO
+
+ *** Section SNOW ***
+
+ ICE-FLAG
+ 0= Ice formation not simulated, 1= Simulated ***
+ # - #ICEFG ***
+ 1 1
+ END ICE-FLAG
+
+ SNOW-PARM1
+ Snow input info: Part 1 ***
+ # - # LAT MELEV SHADE SNOWCF COVIND ***
+ 1 42. 450. 0.0 1.45 0.5
+ END SNOW-PARM1
+
+ SNOW-PARM2
+ Snow input info: Part 2 ***
+ # - # RDCSN TSNOW SNOEVP CCFACT MWATER MGMELT ***
+ 1 0.12 32. 0.05 0.5 0.08 0.0001
+ END SNOW-PARM2
+
+ SNOW-INIT1
+ Initial snow conditions: Part 1 ***
+ # - # PACKSNOW PACKICE PACKWATER RDENPF DULL PAKTMP ***
+ 1 1.4 0.2 0.1 0.2 375. 27.5
+ END SNOW-INIT1
+
+ SNOW-INIT2
+ Initial snow conditions: Part 2 ***
+ # - # COVINX XLNMLT SKYCLR ***
+ 1 0.50 0.0 1.0
+ END SNOW-INIT2
+
+ *** Section IWATER ***
+
+ IWAT-PARM1
+ Flags ***
+ # - # CSNO RTOP VRS VNN RTLI ***
+ 1 1 1
+ END IWAT-PARM1
+
+ IWAT-PARM2
+ ***
+ # - # LSUR SLSUR NSUR RETSC ***
+ 1 200. .010 .010 .01
+ END IWAT-PARM2
+
+ IWAT-PARM3
+ ***
+ # - # PETMAX PETMIN ***
+ 1 40. 35.
+ END IWAT-PARM3
+
+ IWAT-STATE1
+ IWATER state variables ***
+ # - # RETS SURS ***
+ 1 .01 .01
+ END IWAT-STATE1
+
+ *** Section SOLIDS ***
+
+ SLD-PARM2
+ ***
+ # - # KEIM JEIM ACCSDP REMSDP ***
+ 1 .08 1.9 .01 .5
+ END SLD-PARM2
+
+ SLD-STOR
+ Solids storage (tons/acre) ***
+ # - # ***
+ 1 0.2
+ END SLD-STOR
+
+ *** Section IWTGAS ***
+
+ IWT-PARM2
+ ***
+ # - # ELEV AWTF BWTF ***
+ 1 410. 40. 0.8
+ END IWT-PARM2
+
+ *** Section IQUAL ***
+
+ NQUALS
+ ***
+ # - #NQUAL ***
+ 1 1
+ END NQUALS
+
+ QUAL-PROPS
+ Identifiers and Flags ***
+ # - #<--qualid--> QTID QSD VPFW QSO VQO ***
+ 1 COD LB 1 1
+ END QUAL-PROPS
+
+ QUAL-INPUT
+ Storage on surface and nonseasonal parameters ***
+ # - # SQO POTFW ACQOP SQOLIM WSQOP ***
+ 1 1.20 .175 .02 2.0 1.7
+ END QUAL-INPUT
+END IMPLND
+
+RCHRES
+ ACTIVITY
+ RCHRES Active Sections (1=Active, 0=Inactive) ***
+ # - # HYFG ADFG CNFG HTFG SDFG GQFG OXFG NUFG PKFG PHFG ***
+ 1 5 1 1 1 1 1 1 1 1 1 1
+ END ACTIVITY
+
+ PRINT-INFO
+ RCHRES Print-flags ***
+ # - # HYDR ADCA CONS HEAT SED GQL OXRX NUTR PLNK PHCB PIVL PYR ***
+ 1 4 5 5 5 5 5 5 5 5 5 5 12
+ 5 4 4 4 4 4 4 4 4 4 4 12
+ END PRINT-INFO
+
+ BINARY-INFO
+*** RCHRES Binary Output level flags
+*** x - x HYDR ADCA CONS HEAT SED GQL OXRX NUTR PLNK PHCB PIVL PYR
+ 1 5 2 2 2 2 2 2 2 2 2 2 1 12
+ END BINARY-INFO
+
+ GEN-INFO
+ RCHRES<-------Name------->Nexit Unit Systems Printer BinaryOut ***
+ # - # t-series Engl Metr LKFG Engl Metr ***
+ in out ***
+ 1 MEIER POND 2 1 1 1 0 1 83 0
+ 2 OUTLET 1 1 1 1 0 0 83 0
+ 3 SPILLWAY 1 1 1 1 0 0 83 0
+ 4 UPPER KITTLE CREEK 1 1 1 1 0 0 83 0
+ 5 LOWER KITTLE CREEK 1 1 1 1 0 0 83 0
+ END GEN-INFO
+
+ *** Section HYDR ***
+
+ HYDR-PARM1
+ RCHRES Flags for HYDR section ***
+ # - # VC A1 A2 A3 ODFVFG for each ODGTFG for each *** FUNCT for each
+ FG FG FG FG possible exit possible exit *** possible exit
+ 1 2 3 4 5 1 2 3 4 5 ***
+
+ 1 1 1 1 -1 6
+ 2 5 1 1 1 4
+ END HYDR-PARM1
+
+ HYDR-PARM2
+ RCHRES ***
+ # - # DSN FTBN LEN DELTH STCOR KS DB50 ***
+ 1 00 1 0.5 1. .5
+ 2 00 2 0.25 20. .5
+ 3 00 3 0.25 30. .5
+ 4 00 4 2.0 40. .5
+ 5 00 5 3.0 40. .5
+ END HYDR-PARM2
+
+ HYDR-INIT
+ RCHRES Initial conditions for HYDR section ***
+ # - # VOL Initial value of COLIND *** Initial value of OUTDGT
+ (ac-ft) for each possible exit *** for each possible exit
+ EX1 EX2 EX3 EX4 EX5 *** EX1 EX2 EX3 EX4 EX5
+ 1 30. 4.0 5.0
+ 2 5 0.0 4.0
+ END HYDR-INIT
+
+ *** Section CONS ***
+
+ NCONS
+ RCHRES ***
+ # - #NCONS ***
+ 1 5 1
+ END NCONS
+
+ CONS-DATA
+ RCHRES Data for conservative constituent No. 3 ***
+ # - #<---Substance-id---> Conc ID CONV QTYID ***
+ 1 5 ALKALINITY 1000. MG/L 35.31 KG
+ END CONS-DATA
+
+ *** Section HTRCH ***
+
+ HEAT-PARM
+ RCHRES ELEV ELDAT CFSAEX KATRAD KCOND KEVAP ***
+ # - # ***
+ 1 5 450. 100. .95
+ END HEAT-PARM
+
+ HEAT-INIT
+ RCHRES TW AIRTMP ***
+ # - # ***
+ 1 5 60. 40.
+ END HEAT-INIT
+
+ *** Section SEDTRN ***
+
+ SANDFG
+ RCHRES ***
+ # - # SDFG ***
+ 1 2 1
+ 3 4 2
+ 5 3
+ END SANDFG
+
+ SED-GENPARM
+ RCHRES BEDWID BEDWRN POR ***
+ # - # ***
+ 1 200. 4.
+ 2 3 1.33 3.
+ 4 2.0 2.
+ 5 2.66 2.
+ END SED-GENPARM
+
+ SAND-PM
+ RCHRES D W RHO KSAND EXPSND ***
+ # - # ***
+ 1 5 .014 2.5 1.5 1.2
+ END SAND-PM
+
+ SILT-CLAY-PM
+ RCHRES D W RHO TAUCD TAUCS M ***
+ # - # ***
+ 1 .00063 .0066 2.2 .2 .4 .5
+ 2 3 .00063 .0066 2.2 1.E-10 500. .5
+ 4 5 .00063 .0066 2.2 .2 .4 .5
+ END SILT-CLAY-PM
+
+ SILT-CLAY-PM
+ RCHRES D W RHO TAUCD TAUCS M ***
+ # - # ***
+ 1 .000055 .000034 2.0 .15 .3 .75
+ 2 3 .000055 .000034 2.0 1.E-10 500. .75
+ 4 5 .000055 .000034 2.0 .15 .3 .75
+ END SILT-CLAY-PM
+
+ SSED-INIT
+ RCHRES Suspended sed concs (mg/l) ***
+ # - # Sand Silt Clay ***
+ 1 5 5. 20. 30.
+ END SSED-INIT
+
+ BED-INIT
+ RCHRES BEDDEP Initial bed composition ***
+ # - # (ft) Sand Silt Clay ***
+ 1 2. .8 .1 .1
+ 2 3 2. .8 .1 .1
+ 4 5 1. .8 .1 .1
+ END BED-INIT
+
+ *** Section GQUAL ***
+
+ GQ-GENDATA
+ RCHRES NGQL TPFG PHFG ROFG CDFG SDFG PYFG LAT ***
+ # - # ***
+ 1 5 1 1 1 2 1 1 1 42
+ END GQ-GENDATA
+
+ GQ-QALDATA
+ RCHRES<-------GQID-------> DQAL CONCID CONV QTYID ***
+ # - # ***
+ 1 5 PESTICIDE B4 10. UG 1.E6 G
+ END GQ-QALDATA
+
+ GQ-QALFG
+ RCHRES HDRL OXID PHOT VOLT BIOD GEN SDAS ***
+ # - # ***
+ 1 5 1 1 1 1 1 1 1
+ END GQ-QALFG
+
+ GQ-HYDPM
+ RCHRES KA KB KN THHYD ***
+ # - # ***
+ 1 5 .001 .01 .001 1.03
+ END GQ-HYDPM
+
+ GQ-ROXPM
+ RCHRES KOX THOX ***
+ # - # ***
+ 1 5 .1 1.03
+ END GQ-ROXPM
+
+ GQ-PHOTPM
+ # - #*** K1 K2 K3 K4 K5 K6 K7
+ # - #*** K8 K9 K10 K11 K12 K13 K14
+ # - #*** K15 K16 K17 K18 PHI THETA
+ 1 5 848. 544. 330. 195. 120. 68. 41.
+ 1 5 23. 13. 7. 4. 1. .1
+ 1 5 .3 1.1
+ END GQ-PHOTPM
+
+ GQ-CFGAS
+ RCHRES CFGAS ***
+ # - # ***
+ 1 5 .001
+ END GQ-CFGAS
+
+ GQ-BIOPM
+ RCHRES BIOCON THBIO BIO ***
+ # - # ***
+ 1 5 .01 10.
+ END GQ-BIOPM
+
+ GQ-GENDECAY
+ RCHRES FSTDEC THFST ***
+ # - # ***
+ 1 5 .2
+ END GQ-GENDECAY
+
+ GQ-SEDDECAY
+ RCHRES KSUSP THSUSP KBED THBED ***
+ # - # ***
+ 1 5 .002
+ END GQ-SEDDECAY
+
+ GQ-KD
+ RCHRES Partition coefficients ***
+ # - # ADPM(1,1) ADPM(2,1) ADPM(3,1) ADPM(4,1) ADPM(5,1) ADPM(6,1) ***
+ 1 .0001 .001 .001 .0001 .001 .001
+ 2 3 .0001 .001 .001 1.E-10 1.E-10 1.E-10
+ 4 5 .0001 .001 .001 .0001 .001 .001
+ END GQ-KD
+
+ GQ-ADRATE
+ RCHRES Adsorption/desorption rate parameters ***
+ # - # ADPM(1,2) ADPM(2,2) ADPM(3,2) ADPM(4,2) ADPM(5,2) ADPM(6,2) ***
+ 1 150. 150. 150. .25 .25 .25
+ 2 3 150. 150. 150. 1000. 1000. 1000.
+ 4 5 150. 150. 150. .25 .25 .25
+ END GQ-ADRATE
+
+ GQ-SEDCONC
+ RCHRES SQAL1 SQAL2 SQAL3 SQAL4 SQAL5 SQAL6 ***
+ # - # ***
+ 1 .001 .01 .01 .001 .01 .01
+ 2 3 .001 .01 .01 0. 0. 0.
+ 4 5 .001 .01 .01 .001 .01 .01
+ END GQ-SEDCONC
+
+ GQ-VALUES
+ RCHRES TWAT PHVAL ROC CLD SDCNC PHY ***
+ # - # ***
+ 1 5 1.E-5
+ END GQ-VALUES
+
+ GQ-ALPHA
+ RCHRES***
+ # - #*** K1 K2 K3 K4 K5 K6 K7
+ # - #*** K8 K9 K10 K11 K12 K13 K14
+ # - #*** K15 K16 K17 K18
+ 1 5 .008 .009 .010 .011 .011 .011 .012
+ 1 5 .013 .015 .016 .017 .018 .019 .020
+ 1 5 .021 .022 .024 .024
+ END GQ-ALPHA
+
+ GQ-GAMMA
+ RCHRES***
+ # - #*** K1 K2 K3 K4 K5 K6 K7
+ # - #*** K8 K9 K10 K11 K12 K13 K14
+ # - #*** K15 K16 K17 K18
+ 1 5 .001 .001 .001 .001 .001 .001 .001
+ 1 5 .001 .002 .002 .002 .002 .002 .002
+ 1 5 .002 .002 .002 .002
+ END GQ-GAMMA
+
+ GQ-DELTA
+ RCHRES***
+ # - #*** K1 K2 K3 K4 K5 K6 K7
+ # - #*** K8 K9 K10 K11 K12 K13 K14
+ # - #*** K15 K16 K17 K18
+ 1 5 .0007 .0007 .0007 .0007 .0007 .0007 .0007
+ 1 5 .0007 .0007 .0007 .0007 .0007 .0007 .0007
+ 1 5 .0007 .0007 .0007 .0007
+ END GQ-DELTA
+
+ GQ-CLDFACT
+ RCHRES***
+ # - #*** F1 F2 F3 F4 F5 F6 F7
+ # - #*** F8 F9 F10 F11 F12 F13 F14
+ # - #*** F15 F16 F17 F18
+ 1 5 .10 .10 .10 .15 .15 .15 .15
+ 1 5 .17 .17 .17 .17 .18 .19 .20
+ 1 5 .21 .21 .21 .21
+ END GQ-CLDFACT
+
+ *** Section RQUAL ***
+
+ BENTH-FLAG
+ RCHRES BENF ***
+ # - # ***
+ 1 1
+ 4 5 1
+ END BENTH-FLAG
+
+ SCOUR-PARMS
+ RCHRES SCRVEL SCRMUL ***
+ # - # ***
+ 1 5 3.
+ END SCOUR-PARMS
+
+ *** Section OXRX ***
+
+ OX-FLAGS
+ RCHRES REAM ***
+ # - # ***
+ 2 3 1
+ 4 3
+ 5 2
+ END OX-FLAGS
+
+ OX-GENPARM
+ RCHRES KBOD20 TCBOD KODSET SUPSAT ***
+ # - # /hr ***
+ 1 5 .1 8.
+ END OX-GENPARM
+
+ OX-BENPARM
+ RCHRES BENOD TCBEN EXPOD BRBOD(1) BRBOD(2) EXPREL ***
+ # - # mg/m2.hr mg/m2.hr mg/m2.hr ***
+ 1 5 10. 1.1 1.2 20. 25. 1.3
+ END OX-BENPARM
+
+ OX-CFOREA
+ RCHRES CFOREA ***
+ # - # ***
+ 1 5.
+ END OX-CFOREA
+
+ OX-REAPARM
+ RCHRES TCGINV REAK EXPRED EXPREV ***
+ # - # /hr ***
+ 4 2.0 -1.1 1.1
+ END OX-REAPARM
+
+ OX-INIT
+ RCHRES DOX BOD SATDO ***
+ # - # mg/l mg/l mg/l ***
+ 1 5 8. 100.
+ END OX-INIT
+
+ *** Section NUTRX ***
+
+ NUT-FLAGS
+ RCHRES TAM NO2 PO4 AMV DEN ADNH ADPO PHFL ***
+ # - # ***
+ 1 5 1 1 1 1 1 0 0
+ END NUT-FLAGS
+
+ NUT-BENPARM
+ RCHRES BRTAM(1) BRTAM(2) BRPO4(1) BRPO4(2) ANAER ***
+ # - # mg/m2.hr mg/m2.hr mg/m2.hr mg/m2.hr mg/l ***
+ 1 5 11.0 33.0 1.1 2.2 0.0005
+ END NUT-BENPARM
+
+ NUT-NITDENIT
+ RCHRES KTAM20 KNO220 TCNIT KNO320 TCDEN DENOXT ***
+ # - # /hr /hr /hr mg/l ***
+ 1 5 .002 .004 1.07 .001 1.04 0.2
+ END NUT-NITDENIT
+
+ NUT-NH3VOLAT
+ RCHRES EXPNVG EXPNVL ***
+ # - # ***
+ 1 5 .50 0.6667
+ END NUT-NH3VOLAT
+
+ NUT-BEDCONC
+ RCHRES Bed concentrations of NH4 & PO4 (mg/mg) ***
+ # - # NH4-sand NH4-silt NH4-clay PO4-sand PO4-silt PO4-clay ***
+ 1 5 0.00001 0.00001 0.00001 0.00001 0.00001 0.00001
+ END NUT-BEDCONC
+
+ NUT-ADSPARM
+ RCHRES Partition coefficients for NH4 AND PO4 (l/mg) ***
+ # - # NH4-sand NH4-silt NH4-clay PO4-sand PO4-silt PO4-clay ***
+ 1 5 0.0001 0.0001 0.0001 10. 10. 10.
+ END NUT-ADSPARM
+
+ NUT-DINIT
+ RCHRES NO3 TAM NO2 PO4 PHVAL ***
+ # - # mg/l mg/l mg/l mg/l ph units ***
+ 1 5 40. 10. 1. 50. 7.0
+ END NUT-DINIT
+
+ NUT-ADSINIT
+ RCHRES Initial suspended NH4 and PO4 concentrations (mg/mg) ***
+ # - # NH4-sand NH4-silt NH4-clay PO4-sand PO4-silt PO4-clay ***
+ 1 5 0. 0. 0. 0. 0. 0.
+ END NUT-ADSINIT
+
+ *** Section PLANK ***
+
+ PLNK-FLAGS
+ RCHRES PHYF ZOOF BALF SDLT AMRF DECF NSFG ZFOO ***
+ # - # ***
+ 1 5 1 1 1 1 1 1
+ END PLNK-FLAGS
+
+ PLNK-PARM1
+ RCHRES RATCLP NONREF LITSED ALNPR EXTB MALGR ***
+ # - # /ft /hr ***
+ 1 5 4.5
+ END PLNK-PARM1
+
+ PHYTO-PARM
+ RCHRES SEED MXSTAY OREF CLALDH PHYSET REFSET ***
+ # - # mg/l mg/l ug/l ***
+ 1 5 .1 .1 .5 .5
+ END PHYTO-PARM
+
+ ZOO-PARM1
+ RCHRES MZOEAT ZFIL20 ZRES20 ZD OXZD ***
+ # - # mg/l.hr l/mgzoo.hr /hr /hr /hr ***
+ 1 5 .2
+ END ZOO-PARM1
+
+ PLNK-INIT
+ RCHRES PHYTO ZOO BENAL ORN ORP ORC ***
+ # - # mg/l org/l mg/m2 mg/l mg/l mg/l ***
+ 1 5 40. 200. 5. 20. 20. 20.
+ END PLNK-INIT
+
+ *** Section PHCARB ***
+
+ PH-PARM1
+ RCHRES PHCN ALKC ***
+ # - # ***
+ 1 5 50
+ END PH-PARM1
+
+ PH-INIT
+ RCHRES TIC CO2 PH ***
+ # - # mg/l mg/l ***
+ 1 5 20. 5. 8.5
+ END PH-INIT
+END RCHRES
+
+FTABLES
+ FTABLE 1
+ ROWS COLS ***
+ 14 6
+ WINTER SUMMER SPLWAY ***
+ DEPTH AREA VOLUME OUTLET OUTLET DISCH ***
+ (FT) (ACRES) (AC-FT) DISCH DISCH (CFS) ***
+ (CFS) (CFS) ***
+ .000 .000 .0000 .0000 .0000 .0000
+ 2.000 1.212 1.2120 0.0000 .0000 .0000
+ 4.000 2.424 4.8480 0.0000 .0000 .0000
+ 6.000 3.636 10.9080 0.0000 .0000 .0000
+ 8.000 4.848 19.3920 0.0000 .0000 .0000
+ 10.000 6.061 30.3050 0.0000 .0000 .0000
+ 12.000 7.273 43.6380 5.0000 3.5000 .0000
+ 14.000 8.485 59.3950 6.2500 4.3750 .0000
+ 16.000 9.697 77.5760 7.5000 5.2500 .0000
+ 18.000 10.909 98.1810 8.7500 6.1250 .0000
+ 20.000 12.121 121.2100 10.0000 7.0000 .0000
+ 21.000 12.727 133.6360 10.6250 7.4375 50.0000
+ 22.000 13.333 146.6630 11.2500 7.8750 100.0000
+ 23.000 13.939 160.3030 11.8750 8.3125 500.0000
+ END FTABLE 1
+
+ FTABLE 2
+ ROWS COLS ***
+ 13 4
+ DEPTH AREA VOLUME DISCH FLO-THRU ***
+ (FT) (ACRES) (AC-FT) (CFS) (MIN) ***
+ .000 .000 .0000 .000 0.0
+ .167 .071 .0109 1.2241 6.5
+ .333 .081 .0236 3.9148 4.4
+ .500 .091 .0379 7.8193 3.5
+ .667 .101 .0539 12.9032 3.0
+ .833 .111 .0715 19.1853 2.7
+ 1.000 .121 .0909 26.7046 2.5
+ 1.333 .141 .1347 45.6529 2.1
+ 1.667 .162 .1852 70.1757 1.9
+ 2.000 .182 .2424 100.7192 1.7
+ 2.667 .586 .4983 201.9005 1.8
+ 3.333 .990 1.0236 344.6344 2.2
+ 4.000 1.394 1.8182 537.0775 2.5
+ END FTABLE 2
+
+ FTABLE 3
+ ROWS COLS ***
+ 13 4
+ DEPTH AREA VOLUME DISCH FLO-THRU ***
+ (FT) (ACRES) (AC-FT) (CFS) (MIN) ***
+ .000 .000 .0000 .000 0.0
+ .167 .071 .0109 1.4992 5.3
+ .333 .081 .0236 4.7947 3.6
+ .500 .091 .0379 9.5766 2.9
+ .667 .101 .0539 15.8032 2.5
+ .833 .111 .0715 23.4971 2.2
+ 1.000 .121 .0909 32.7063 2.0
+ 1.333 .141 .1347 55.9132 1.7
+ 1.667 .162 .1852 85.9474 1.6
+ 2.000 .182 .2424 123.3553 1.4
+ 2.667 .586 .4983 247.2766 1.5
+ 3.333 .990 1.0236 422.0892 1.8
+ 4.000 1.394 1.8182 657.7828 2.0
+ END FTABLE 3
+
+ FTABLE 4
+ ROWS COLS ***
+ 13 4
+ DEPTH AREA VOLUME DISCH FLO-THRU ***
+ (FT) (ACRES) (AC-FT) (CFS) (MIN) ***
+ .000 .000 .0000 .000 0.0
+ .250 .848 .1970 .9024 158.5
+ .500 .970 .4242 2.8860 106.7
+ .750 1.091 .6818 5.7642 85.9
+ 1.000 1.212 .9697 9.5120 74.0
+ 1.250 1.333 1.2879 14.1431 66.1
+ 1.500 1.455 1.6364 19.6862 60.3
+ 2.000 1.697 2.4242 33.6545 52.3
+ 2.500 1.939 3.3333 51.7323 46.8
+ 3.000 2.182 4.3636 74.2486 42.7
+ 4.000 11.879 11.3939 155.5774 53.2
+ 5.000 21.576 28.1212 296.8633 68.8
+ 6.000 31.273 54.5454 522.1440 75.8
+ END FTABLE 4
+
+ FTABLE 5
+ ROWS COLS ***
+ 13 4
+ DEPTH AREA VOLUME DISCH FLO-THRU ***
+ (FT) (ACRES) (AC-FT) (CFS) (MIN) ***
+ .000 .000 .0000 .000 0.0
+ .333 1.697 .5253 1.5869 240.3
+ .667 1.939 1.1313 5.0752 161.8
+ 1.000 2.182 1.8182 10.1370 130.2
+ 1.333 2.424 2.5859 16.7279 112.2
+ 1.667 2.667 3.4343 24.8719 100.2
+ 2.000 2.909 4.3636 34.6200 91.5
+ 2.667 3.394 6.4646 59.1848 79.3
+ 3.333 3.879 8.8889 90.9763 70.9
+ 4.000 4.364 11.6364 130.5731 64.7
+ 5.333 36.687 39.0034 284.8886 99.4
+ 6.667 69.010 109.4680 593.7734 133.8
+ 8.000 101.333 223.0302 1129.6948 143.3
+ END FTABLE 5
+END FTABLES
+
+DISPLY
+ DISPLY-INFO1
+ # - #<----------Title----------->***TRAN PIVL DIG1 FIL1 PYR DIG2 FIL2 YRND
+ 1 O2 CONC, MEIER POND (mg/l) AVER 1 2 66 12
+ 2 PEST SED CONC, POND (mg/kg) AVER 1 2 66 12
+ 3 O2 CONC,LOWER KITTLE C(mg/l) AVER 1 2 66 12
+ 4 PEST SED CONC,L KTL C(mg/kg) AVER 1 2 66 12
+ 5 WATER TEMP,MEIER POND (DEGF) AVER 1 2 66 12
+ END DISPLY-INFO1
+END DISPLY
+
+GENER
+ OPCODE
+ # - # Op- ***
+ code ***
+ 1 2 19
+ END OPCODE
+END GENER
+
+PLTGEN
+ PLOTINFO
+ # - # FILE NPT NMN LABL PYR PIVL ***
+ 1 94 2 24
+ 2 95 3 1 6
+ END PLOTINFO
+
+ GEN-LABELS
+ # - #<----------------Title ----------------> *** <------Y axis------>
+ 1 SIMULATED FLOWS (CFS) CFS
+ 2 SIMULATED VALS RELATED TO TEMP&PH,RCH 4
+ END GEN-LABELS
+
+ SCALING
+ # - # YMIN YMAX IVLIN ***
+ 1 2 0. 150. 20.
+ END SCALING
+
+ CURV-DATA (first curve)
+ <-Curve label--> Line Intg Col Tran ***
+ # - # type eqv code code ***
+ 1 TOTAL POND OUTFL 7 1 AVER
+ 2 AVDEP FOR RCH 4 7 1 LAST
+ END CURV-DATA
+
+ CURV-DATA (second curve)
+ <-Curve label--> Line Intg Col Tran ***
+ # - # type eqv code code ***
+ 1 LOWER KITTLE CR 8 2 AVER
+ 2 TW FOR RCH 4 8 2 LAST
+ END CURV-DATA
+
+ CURV-DATA (third curve)
+ <-Curve label--> Line Intg Col Tran ***
+ # - # type eqv code code ***
+ 2 PH FOR RCH 4 9 2 LAST
+ END CURV-DATA
+
+ CURV-DATA (fourth curve)
+ <-Curve label--> Line Intg Col Tran ***
+ # - # type eqv code code ***
+ 2 HTEXCH FOR RCH 4 10 2
+ END CURV-DATA
+END PLTGEN
+
+EXT SOURCES
+<-Volume-> SsysSgap<--Mult-->Tran <-Target vols> <-Grp> <-Member-> ***
+ # # tem strg<-factor->strg # # # # ***
+WDM 39 PREC ENGLZERO SAME PERLND 1 EXTNL PREC
+WDM 131 PREC ENGLZERO SAME IMPLND 1 EXTNL PREC
+WDM 39 PREC ENGLZERO SAME RCHRES 1 3 EXTNL PREC
+WDM 131 PREC ENGLZERO SAME RCHRES 4 5 EXTNL PREC
+WDM 123 ATMP ENGL SAME PERLND 1 ATEMP AIRTMP
+WDM 122 ATMP ENGL SAME IMPLND 1 ATEMP AIRTMP
+WDM 123 ATMP ENGL SAME RCHRES 1 3 EXTNL GATMP
+WDM 122 ATMP ENGL SAME RCHRES 4 5 EXTNL GATMP
+WDM 41 EVAP ENGL .7 DIV PERLND 1 EXTNL PETINP
+WDM 41 EVAP ENGL .7 DIV IMPLND 1 EXTNL PETINP
+WDM 41 EVAP ENGL .7 DIV RCHRES 1 5 EXTNL POTEV
+WDM 42 WIND ENGL DIV PERLND 1 EXTNL WINMOV
+WDM 42 WIND ENGL DIV IMPLND 1 EXTNL WINMOV
+WDM 42 WIND ENGL DIV RCHRES 1 5 EXTNL WIND
+WDM 46 SOLR ENGL DIV PERLND 1 EXTNL SOLRAD
+WDM 46 SOLR ENGL DIV IMPLND 1 EXTNL SOLRAD
+WDM 46 SOLR ENGL DIV RCHRES 1 5 EXTNL SOLRAD
+WDM 126 DEWP ENGL SAME PERLND 1 EXTNL DTMPG
+WDM 125 DEWP ENGL SAME IMPLND 1 EXTNL DTMPG
+WDM 126 DEWP ENGL SAME RCHRES 1 3 EXTNL DEWTMP
+WDM 125 DEWP ENGL SAME RCHRES 4 5 EXTNL DEWTMP
+WDM 140 CLND ENGL SAME RCHRES 1 EXTNL COLIND
+WDM 135 CLDC ENGL SAME PERLND 1 EXTNL CLOUD
+WDM 135 CLDC ENGL SAME IMPLND 1 EXTNL CLOUD
+WDM 135 CLDC ENGL SAME RCHRES 1 5 EXTNL CLOUD
+END EXT SOURCES
+
+SCHEMATIC
+<-Source-> <--Area--> <-Target-> ***
+ # <-factor-> # # ***
+PERLND 1 6000. RCHRES 1 1
+IMPLND 1 3000. RCHRES 5 2
+RCHRES 1 RCHRES 2 3
+RCHRES 1 RCHRES 3 4
+RCHRES 2 RCHRES 4 5
+RCHRES 3 RCHRES 4 5
+RCHRES 4 RCHRES 5 5
+END SCHEMATIC
+
+MASS-LINK
+
+ MASS-LINK 1
+ <-Grp> <-Member-><--Mult--> <-Grp> <-Member-> ***
+ # #<-factor-> # # ***
+PERLND PWATER PERO 0.0833333 RCHRES INFLOW IVOL
+PERLND PWTGAS POHT RCHRES INFLOW IHEAT
+PERLND PWTGAS PODOXM RCHRES INFLOW OXIF 1
+PERLND PWTGAS POCO2M RCHRES INFLOW PHIF 2
+ END MASS-LINK 1
+
+ MASS-LINK 2
+ <-Grp> <-Member-><--Mult--> <-Grp> <-Member-> ***
+ # #<-factor-> # # ***
+IMPLND IWATER SURO 0.0833333 RCHRES INFLOW IVOL
+IMPLND SOLIDS SOSLD 0.10 RCHRES INFLOW ISED 1
+IMPLND SOLIDS SOSLD 0.46 RCHRES INFLOW ISED 2
+IMPLND SOLIDS SOSLD 0.44 RCHRES INFLOW ISED 3
+IMPLND IWTGAS SOHT RCHRES INFLOW IHEAT
+IMPLND IWTGAS SODOXM RCHRES INFLOW OXIF 1
+IMPLND IWTGAS SOCO2M RCHRES INFLOW PHIF 2
+IMPLND IQUAL SOQUAL RCHRES INFLOW OXIF 2
+ END MASS-LINK 2
+
+ MASS-LINK 3
+ <-Grp> <-Member-><--Mult--> <-Grp> <-Member-> ***
+ # #<-factor-> # # ***
+RCHRES OFLOW 1 RCHRES INFLOW
+ END MASS-LINK 3
+
+ MASS-LINK 4
+ <-Grp> <-Member-><--Mult--> <-Grp> <-Member-> ***
+ # #<-factor-> # # ***
+RCHRES OFLOW 2 RCHRES INFLOW
+ END MASS-LINK 4
+
+ MASS-LINK 5
+ <-Grp> <-Member-><--Mult--> <-Grp> <-Member-> ***
+ # #<-factor-> # # ***
+RCHRES ROFLOW RCHRES INFLOW
+ END MASS-LINK 5
+
+END MASS-LINK
+
+NETWORK
+<-Volume-> <-Grp> <-Member-><--Mult-->Tran <-Target vols> <-Grp> <-Member-> ***
+ # # #<-factor->strg # # # # ***
+RCHRES 1 HTRCH TW 1. DISPLY 5 INPUT TIMSER
+RCHRES 1 OXRX DOX 1. DISPLY 1 INPUT TIMSER
+RCHRES 1 GQUAL RSQAL 12 1. GENER 1 INPUT ONE
+RCHRES 1 SEDTRN RSED 10 1. GENER 1 INPUT TWO
+GENER 1 OUTPUT TIMSER 1.1 DISPLY 2 INPUT TIMSER
+RCHRES 1 HYDR ROVOL 12.1 PLTGEN 1 INPUT MEAN 1
+RCHRES 4 HYDR AVDEP 1. PLTGEN 2 INPUT POINT 1
+RCHRES 4 HTRCH TW 1. PLTGEN 2 INPUT POINT 2
+RCHRES 4 PHCARB PHST 3 1. PLTGEN 2 INPUT POINT 3
+RCHRES 4 HTRCH HTEXCH 1. PLTGEN 2 INPUT MEAN 1
+RCHRES 5 OXRX DOX 1. DISPLY 3 INPUT TIMSER
+RCHRES 5 GQUAL RSQAL 12 1. GENER 2 INPUT ONE
+RCHRES 5 SEDTRN RSED 10 1. GENER 2 INPUT TWO
+GENER 2 OUTPUT TIMSER 1.1 DISPLY 4 INPUT TIMSER
+RCHRES 5 HYDR ROVOL 12.1 PLTGEN 1 INPUT MEAN 2
+END NETWORK
+
+SPEC-ACTIONS
+*** test special actions
+ RCHRES 5 RSED 4 += 2.50E+05
+ RCHRES 5 RSED 5 += 6.89E+05
+ RCHRES 5 RSED 6 += 4.01E+05
+END SPEC-ACTIONS
+
+
+END RUN
diff --git a/tests/test10specl/HSPFresults/test10specl.uci b/tests/test10specl/HSPFresults/test10specl.uci
new file mode 100644
index 00000000..20e6eaf0
--- /dev/null
+++ b/tests/test10specl/HSPFresults/test10specl.uci
@@ -0,0 +1,1023 @@
+RUN
+
+GLOBAL
+ Version 11 test run: PERLND and IMPLND w/ RCHRES (sediment, water quality)
+ START 1976 END 1976
+ RUN INTERP OUTPUT LEVEL 3
+ RESUME 0 RUN 1 UNIT SYSTEM 1
+END GLOBAL
+
+ *** This test run uses MASS-LINK and SCHEMATIC blocks ***
+
+FILES
+ ***<----FILE NAME------------------------------------------------->
+WDM 21 test10specl.wdm
+MESSU 22 test10specl.ech
+ 01 test10specl.out
+ 66 test10specl.d66
+ 94 test10specl.p94
+ 95 test10specl.p95
+BINO 81 test10speclP.hbn
+BINO 82 test10speclI.hbn
+BINO 83 test10speclR.hbn
+END FILES
+
+OPN SEQUENCE
+ INGRP INDELT 01:00
+ PERLND 1
+ RCHRES 1
+ DISPLY 5
+ DISPLY 1
+ GENER 1
+ DISPLY 2
+ RCHRES 2
+ RCHRES 3
+ RCHRES 4
+ PLTGEN 2
+ IMPLND 1
+ RCHRES 5
+ DISPLY 3
+ GENER 2
+ DISPLY 4
+ PLTGEN 1
+ END INGRP
+END OPN SEQUENCE
+
+PERLND
+ ACTIVITY
+ Active Sections (1=Active, 0=Inactive) ***
+ # - # ATMP SNOW PWAT SED PST PWG PQAL MSTL PEST NITR PHOS TRAC ***
+ 1 1 1 1 1
+ END ACTIVITY
+
+ PRINT-INFO
+ Print-flags *** PIVL PYR
+ # - # ATMP SNOW PWAT SED PST PWG PQAL MSTL PEST NITR PHOS TRAC ***
+ 1 4 4 4 4 12
+ END PRINT-INFO
+
+ BINARY-INFO
+*** < PLS> Binary Output Flags PIVL PYR
+*** x - x ATMP SNOW PWAT SED PST PWG PQAL MSTL PEST NITR PHOS TRAC
+ 1 2 2 2 2 2 1 12
+ END BINARY-INFO
+
+ GEN-INFO
+ <-------Name-------> Unit-systems Printer BinaryOut ***
+ # - # t-series Engl Metr Engl Metr ***
+ in out ***
+ 1 BICKNELL FARM 1 1 1 0 81 0
+ END GEN-INFO
+
+ *** Section SNOW ***
+
+ ICE-FLAG
+ 0= Ice formation not simulated, 1= Simulated ***
+ # - #ICEFG ***
+ 1 1
+ END ICE-FLAG
+
+ SNOW-PARM1
+ Snow input info: Part 1 ***
+ # - # LAT MELEV SHADE SNOWCF COVIND ***
+ 1 42. 520. 0.0 1.45 0.5
+ END SNOW-PARM1
+
+ SNOW-PARM2
+ Snow input info: Part 2 ***
+ # - # RDCSN TSNOW SNOEVP CCFACT MWATER MGMELT ***
+ 1 0.12 32. 0.05 0.5 0.08 0.0001
+ END SNOW-PARM2
+
+ SNOW-INIT1
+ Initial snow conditions: Part 1 ***
+ # - # PACKSNOW PACKICE PACKWATER RDENPF DULL PAKTMP ***
+ 1 1.4 0.2 0.1 0.2 375. 27.5
+ END SNOW-INIT1
+
+ SNOW-INIT2
+ Initial snow conditions: Part 2 ***
+ # - # COVINX XLNMLT SKYCLR ***
+ 1 0.50 0.0 1.0
+ END SNOW-INIT2
+
+ *** Section PWATER ***
+
+ PWAT-PARM1
+ PWATER variable monthly parameter value flags ***
+ # - # CSNO RTOP UZFG VCS VUZ VNN VIFW VIRC VLE ***
+ 1 1 0 0 1 1 1 0 0 1
+ END PWAT-PARM1
+
+ PWAT-PARM2
+ *** PWATER input info: Part 2
+ # - # ***FOREST LZSN INFILT LSUR SLSUR KVARY AGWRC
+ 1 0.010 8.0 0.150 250. 0.050 0.5 0.98
+ END PWAT-PARM2
+
+ PWAT-PARM3
+ *** PWATER input info: Part 3
+ # - # ***PETMAX PETMIN INFEXP INFILD DEEPFR BASETP AGWETP
+ 1 40. 35. 2.0 2.0 0.10 0.0 0.08
+ END PWAT-PARM3
+
+ PWAT-PARM4
+ PWATER input info: Part 4 ***
+ # - # CEPSC UZSN NSUR INTFW IRC LZETP ***
+ 1 0.01 0.1 1.0 0.60
+ END PWAT-PARM4
+
+ MON-INTERCEP
+ Only required if VCSFG=1 in PWAT-PARM1 ***
+ # - # Interception storage capacity at start of each month ***
+ JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC ***
+ 1 0.04 0.04 0.03 0.03 0.03 0.03 0.10 0.17 0.19 0.14 0.05 0.04
+ END MON-INTERCEP
+
+ MON-UZSN
+ Only required if VUZFG=1 in PWAT-PARM1 ***
+ # - # Upper zone storage at start of each month ***
+ JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC ***
+ 1 0.4 0.4 0.4 0.4 1.6 1.1 1.1 1.3 1.3 1.3 1.1 0.9
+ END MON-UZSN
+
+ MON-MANNING
+ Only required if VNNFG=1 in PWAT-PARM1 ***
+ # - # Manning's n for overland flow at start of each month ***
+ JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC ***
+ 1 0.30 0.30 0.30 0.30 0.27 0.25 0.25 0.25 0.25 0.25 0.35 0.33
+ END MON-MANNING
+
+ MON-LZETPARM
+ Only required if VLEFG=1 in PWAT-PARM1 ***
+ # - # Lower zone ET parameter at start of each month ***
+ JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC ***
+ 1 0.20 0.20 0.20 0.23 0.23 0.25 0.60 0.80 0.75 0.50 0.30 0.20
+ END MON-LZETPARM
+
+ PWAT-STATE1
+ *** Initial conditions at start of simulation
+ # - # *** CEPS SURS UZS IFWS LZS AGWS GWVS
+ 1 0.05 0.0 0.15 0.0 4.0 0.05 0.05
+ END PWAT-STATE1
+
+ *** Section PSTEMP ***
+
+ PSTEMP-PARM2
+ ***
+ # - # ASLT BSLT ULTP1 ULTP2 LGTP1 LGTP2 ***
+ 1 14.5 .365 1.2 4.0 1.2 6.0
+ END PSTEMP-PARM2
+
+ *** Section PWTGAS ***
+
+ PWT-PARM2
+ ***
+ # - # ELEV IDOXP ICO2P ADOXP ACO2P ***
+ 1 500. 6. .05 5. .05
+ END PWT-PARM2
+END PERLND
+
+IMPLND
+ ACTIVITY
+ Active Sections ***
+ # - # ATMP SNOW IWAT SLD IWG IQAL ***
+ 1 1 1 1 1 1
+ END ACTIVITY
+
+ PRINT-INFO
+ Print-flags ***
+ # - # ATMP SNOW IWAT SLD IWG IQAL PIVL PYR ***
+ 1 4 4 4 4 4 12
+ END PRINT-INFO
+
+ BINARY-INFO
+*** **** Binary-Output-flags **** PIVL PYR
+*** x - x ATMP SNOW IWAT SLD IWG IQAL *********
+ 1 2 2 2 2 2 1 12
+ END BINARY-INFO
+
+ GEN-INFO
+ <-------Name-------> Unit-systems Printer BinaryOut ***
+ # - # t-series Engl Metr Engl Metr ***
+ in out ***
+ 1 DONIGIAN INDUSTRY 1 1 1 0 82 0
+ END GEN-INFO
+
+ *** Section SNOW ***
+
+ ICE-FLAG
+ 0= Ice formation not simulated, 1= Simulated ***
+ # - #ICEFG ***
+ 1 1
+ END ICE-FLAG
+
+ SNOW-PARM1
+ Snow input info: Part 1 ***
+ # - # LAT MELEV SHADE SNOWCF COVIND ***
+ 1 42. 450. 0.0 1.45 0.5
+ END SNOW-PARM1
+
+ SNOW-PARM2
+ Snow input info: Part 2 ***
+ # - # RDCSN TSNOW SNOEVP CCFACT MWATER MGMELT ***
+ 1 0.12 32. 0.05 0.5 0.08 0.0001
+ END SNOW-PARM2
+
+ SNOW-INIT1
+ Initial snow conditions: Part 1 ***
+ # - # PACKSNOW PACKICE PACKWATER RDENPF DULL PAKTMP ***
+ 1 1.4 0.2 0.1 0.2 375. 27.5
+ END SNOW-INIT1
+
+ SNOW-INIT2
+ Initial snow conditions: Part 2 ***
+ # - # COVINX XLNMLT SKYCLR ***
+ 1 0.50 0.0 1.0
+ END SNOW-INIT2
+
+ *** Section IWATER ***
+
+ IWAT-PARM1
+ Flags ***
+ # - # CSNO RTOP VRS VNN RTLI ***
+ 1 1 1
+ END IWAT-PARM1
+
+ IWAT-PARM2
+ ***
+ # - # LSUR SLSUR NSUR RETSC ***
+ 1 200. .010 .010 .01
+ END IWAT-PARM2
+
+ IWAT-PARM3
+ ***
+ # - # PETMAX PETMIN ***
+ 1 40. 35.
+ END IWAT-PARM3
+
+ IWAT-STATE1
+ IWATER state variables ***
+ # - # RETS SURS ***
+ 1 .01 .01
+ END IWAT-STATE1
+
+ *** Section SOLIDS ***
+
+ SLD-PARM2
+ ***
+ # - # KEIM JEIM ACCSDP REMSDP ***
+ 1 .08 1.9 .01 .5
+ END SLD-PARM2
+
+ SLD-STOR
+ Solids storage (tons/acre) ***
+ # - # ***
+ 1 0.2
+ END SLD-STOR
+
+ *** Section IWTGAS ***
+
+ IWT-PARM2
+ ***
+ # - # ELEV AWTF BWTF ***
+ 1 410. 40. 0.8
+ END IWT-PARM2
+
+ *** Section IQUAL ***
+
+ NQUALS
+ ***
+ # - #NQUAL ***
+ 1 1
+ END NQUALS
+
+ QUAL-PROPS
+ Identifiers and Flags ***
+ # - #<--qualid--> QTID QSD VPFW QSO VQO ***
+ 1 COD LB 1 1
+ END QUAL-PROPS
+
+ QUAL-INPUT
+ Storage on surface and nonseasonal parameters ***
+ # - # SQO POTFW ACQOP SQOLIM WSQOP ***
+ 1 1.20 .175 .02 2.0 1.7
+ END QUAL-INPUT
+END IMPLND
+
+RCHRES
+ ACTIVITY
+ RCHRES Active Sections (1=Active, 0=Inactive) ***
+ # - # HYFG ADFG CNFG HTFG SDFG GQFG OXFG NUFG PKFG PHFG ***
+ 1 5 1 1 1 1 1 1 1 1 1 1
+ END ACTIVITY
+
+ PRINT-INFO
+ RCHRES Print-flags ***
+ # - # HYDR ADCA CONS HEAT SED GQL OXRX NUTR PLNK PHCB PIVL PYR ***
+ 1 4 5 5 5 5 5 5 5 5 5 5 12
+ 5 4 4 4 4 4 4 4 4 4 4 12
+ END PRINT-INFO
+
+ BINARY-INFO
+*** RCHRES Binary Output level flags
+*** x - x HYDR ADCA CONS HEAT SED GQL OXRX NUTR PLNK PHCB PIVL PYR
+ 1 5 2 2 2 2 2 2 2 2 2 2 1 12
+ END BINARY-INFO
+
+ GEN-INFO
+ RCHRES<-------Name------->Nexit Unit Systems Printer BinaryOut ***
+ # - # t-series Engl Metr LKFG Engl Metr ***
+ in out ***
+ 1 MEIER POND 2 1 1 1 0 1 83 0
+ 2 OUTLET 1 1 1 1 0 0 83 0
+ 3 SPILLWAY 1 1 1 1 0 0 83 0
+ 4 UPPER KITTLE CREEK 1 1 1 1 0 0 83 0
+ 5 LOWER KITTLE CREEK 1 1 1 1 0 0 83 0
+ END GEN-INFO
+
+ *** Section HYDR ***
+
+ HYDR-PARM1
+ RCHRES Flags for HYDR section ***
+ # - # VC A1 A2 A3 ODFVFG for each ODGTFG for each *** FUNCT for each
+ FG FG FG FG possible exit possible exit *** possible exit
+ 1 2 3 4 5 1 2 3 4 5 ***
+
+ 1 1 1 1 -1 6
+ 2 5 1 1 1 4
+ END HYDR-PARM1
+
+ HYDR-PARM2
+ RCHRES ***
+ # - # DSN FTBN LEN DELTH STCOR KS DB50 ***
+ 1 00 1 0.5 1. .5
+ 2 00 2 0.25 20. .5
+ 3 00 3 0.25 30. .5
+ 4 00 4 2.0 40. .5
+ 5 00 5 3.0 40. .5
+ END HYDR-PARM2
+
+ HYDR-INIT
+ RCHRES Initial conditions for HYDR section ***
+ # - # VOL Initial value of COLIND *** Initial value of OUTDGT
+ (ac-ft) for each possible exit *** for each possible exit
+ EX1 EX2 EX3 EX4 EX5 *** EX1 EX2 EX3 EX4 EX5
+ 1 30. 4.0 5.0
+ 2 5 0.0 4.0
+ END HYDR-INIT
+
+ *** Section CONS ***
+
+ NCONS
+ RCHRES ***
+ # - #NCONS ***
+ 1 5 1
+ END NCONS
+
+ CONS-DATA
+ RCHRES Data for conservative constituent No. 3 ***
+ # - #<---Substance-id---> Conc ID CONV QTYID ***
+ 1 5 ALKALINITY 1000. MG/L 35.31 KG
+ END CONS-DATA
+
+ *** Section HTRCH ***
+
+ HEAT-PARM
+ RCHRES ELEV ELDAT CFSAEX KATRAD KCOND KEVAP ***
+ # - # ***
+ 1 5 450. 100. .95
+ END HEAT-PARM
+
+ HEAT-INIT
+ RCHRES TW AIRTMP ***
+ # - # ***
+ 1 5 60. 40.
+ END HEAT-INIT
+
+ *** Section SEDTRN ***
+
+ SANDFG
+ RCHRES ***
+ # - # SDFG ***
+ 1 2 1
+ 3 4 2
+ 5 3
+ END SANDFG
+
+ SED-GENPARM
+ RCHRES BEDWID BEDWRN POR ***
+ # - # ***
+ 1 200. 4.
+ 2 3 1.33 3.
+ 4 2.0 2.
+ 5 2.66 2.
+ END SED-GENPARM
+
+ SAND-PM
+ RCHRES D W RHO KSAND EXPSND ***
+ # - # ***
+ 1 5 .014 2.5 1.5 1.2
+ END SAND-PM
+
+ SILT-CLAY-PM
+ RCHRES D W RHO TAUCD TAUCS M ***
+ # - # ***
+ 1 .00063 .0066 2.2 .2 .4 .5
+ 2 3 .00063 .0066 2.2 1.E-10 500. .5
+ 4 5 .00063 .0066 2.2 .2 .4 .5
+ END SILT-CLAY-PM
+
+ SILT-CLAY-PM
+ RCHRES D W RHO TAUCD TAUCS M ***
+ # - # ***
+ 1 .000055 .000034 2.0 .15 .3 .75
+ 2 3 .000055 .000034 2.0 1.E-10 500. .75
+ 4 5 .000055 .000034 2.0 .15 .3 .75
+ END SILT-CLAY-PM
+
+ SSED-INIT
+ RCHRES Suspended sed concs (mg/l) ***
+ # - # Sand Silt Clay ***
+ 1 5 5. 20. 30.
+ END SSED-INIT
+
+ BED-INIT
+ RCHRES BEDDEP Initial bed composition ***
+ # - # (ft) Sand Silt Clay ***
+ 1 2. .8 .1 .1
+ 2 3 2. .8 .1 .1
+ 4 5 1. .8 .1 .1
+ END BED-INIT
+
+ *** Section GQUAL ***
+
+ GQ-GENDATA
+ RCHRES NGQL TPFG PHFG ROFG CDFG SDFG PYFG LAT ***
+ # - # ***
+ 1 5 1 1 1 2 1 1 1 42
+ END GQ-GENDATA
+
+ GQ-QALDATA
+ RCHRES<-------GQID-------> DQAL CONCID CONV QTYID ***
+ # - # ***
+ 1 5 PESTICIDE B4 10. UG 1.E6 G
+ END GQ-QALDATA
+
+ GQ-QALFG
+ RCHRES HDRL OXID PHOT VOLT BIOD GEN SDAS ***
+ # - # ***
+ 1 5 1 1 1 1 1 1 1
+ END GQ-QALFG
+
+ GQ-HYDPM
+ RCHRES KA KB KN THHYD ***
+ # - # ***
+ 1 5 .001 .01 .001 1.03
+ END GQ-HYDPM
+
+ GQ-ROXPM
+ RCHRES KOX THOX ***
+ # - # ***
+ 1 5 .1 1.03
+ END GQ-ROXPM
+
+ GQ-PHOTPM
+ # - #*** K1 K2 K3 K4 K5 K6 K7
+ # - #*** K8 K9 K10 K11 K12 K13 K14
+ # - #*** K15 K16 K17 K18 PHI THETA
+ 1 5 848. 544. 330. 195. 120. 68. 41.
+ 1 5 23. 13. 7. 4. 1. .1
+ 1 5 .3 1.1
+ END GQ-PHOTPM
+
+ GQ-CFGAS
+ RCHRES CFGAS ***
+ # - # ***
+ 1 5 .001
+ END GQ-CFGAS
+
+ GQ-BIOPM
+ RCHRES BIOCON THBIO BIO ***
+ # - # ***
+ 1 5 .01 10.
+ END GQ-BIOPM
+
+ GQ-GENDECAY
+ RCHRES FSTDEC THFST ***
+ # - # ***
+ 1 5 .2
+ END GQ-GENDECAY
+
+ GQ-SEDDECAY
+ RCHRES KSUSP THSUSP KBED THBED ***
+ # - # ***
+ 1 5 .002
+ END GQ-SEDDECAY
+
+ GQ-KD
+ RCHRES Partition coefficients ***
+ # - # ADPM(1,1) ADPM(2,1) ADPM(3,1) ADPM(4,1) ADPM(5,1) ADPM(6,1) ***
+ 1 .0001 .001 .001 .0001 .001 .001
+ 2 3 .0001 .001 .001 1.E-10 1.E-10 1.E-10
+ 4 5 .0001 .001 .001 .0001 .001 .001
+ END GQ-KD
+
+ GQ-ADRATE
+ RCHRES Adsorption/desorption rate parameters ***
+ # - # ADPM(1,2) ADPM(2,2) ADPM(3,2) ADPM(4,2) ADPM(5,2) ADPM(6,2) ***
+ 1 150. 150. 150. .25 .25 .25
+ 2 3 150. 150. 150. 1000. 1000. 1000.
+ 4 5 150. 150. 150. .25 .25 .25
+ END GQ-ADRATE
+
+ GQ-SEDCONC
+ RCHRES SQAL1 SQAL2 SQAL3 SQAL4 SQAL5 SQAL6 ***
+ # - # ***
+ 1 .001 .01 .01 .001 .01 .01
+ 2 3 .001 .01 .01 0. 0. 0.
+ 4 5 .001 .01 .01 .001 .01 .01
+ END GQ-SEDCONC
+
+ GQ-VALUES
+ RCHRES TWAT PHVAL ROC CLD SDCNC PHY ***
+ # - # ***
+ 1 5 1.E-5
+ END GQ-VALUES
+
+ GQ-ALPHA
+ RCHRES***
+ # - #*** K1 K2 K3 K4 K5 K6 K7
+ # - #*** K8 K9 K10 K11 K12 K13 K14
+ # - #*** K15 K16 K17 K18
+ 1 5 .008 .009 .010 .011 .011 .011 .012
+ 1 5 .013 .015 .016 .017 .018 .019 .020
+ 1 5 .021 .022 .024 .024
+ END GQ-ALPHA
+
+ GQ-GAMMA
+ RCHRES***
+ # - #*** K1 K2 K3 K4 K5 K6 K7
+ # - #*** K8 K9 K10 K11 K12 K13 K14
+ # - #*** K15 K16 K17 K18
+ 1 5 .001 .001 .001 .001 .001 .001 .001
+ 1 5 .001 .002 .002 .002 .002 .002 .002
+ 1 5 .002 .002 .002 .002
+ END GQ-GAMMA
+
+ GQ-DELTA
+ RCHRES***
+ # - #*** K1 K2 K3 K4 K5 K6 K7
+ # - #*** K8 K9 K10 K11 K12 K13 K14
+ # - #*** K15 K16 K17 K18
+ 1 5 .0007 .0007 .0007 .0007 .0007 .0007 .0007
+ 1 5 .0007 .0007 .0007 .0007 .0007 .0007 .0007
+ 1 5 .0007 .0007 .0007 .0007
+ END GQ-DELTA
+
+ GQ-CLDFACT
+ RCHRES***
+ # - #*** F1 F2 F3 F4 F5 F6 F7
+ # - #*** F8 F9 F10 F11 F12 F13 F14
+ # - #*** F15 F16 F17 F18
+ 1 5 .10 .10 .10 .15 .15 .15 .15
+ 1 5 .17 .17 .17 .17 .18 .19 .20
+ 1 5 .21 .21 .21 .21
+ END GQ-CLDFACT
+
+ *** Section RQUAL ***
+
+ BENTH-FLAG
+ RCHRES BENF ***
+ # - # ***
+ 1 1
+ 4 5 1
+ END BENTH-FLAG
+
+ SCOUR-PARMS
+ RCHRES SCRVEL SCRMUL ***
+ # - # ***
+ 1 5 3.
+ END SCOUR-PARMS
+
+ *** Section OXRX ***
+
+ OX-FLAGS
+ RCHRES REAM ***
+ # - # ***
+ 2 3 1
+ 4 3
+ 5 2
+ END OX-FLAGS
+
+ OX-GENPARM
+ RCHRES KBOD20 TCBOD KODSET SUPSAT ***
+ # - # /hr ***
+ 1 5 .1 8.
+ END OX-GENPARM
+
+ OX-BENPARM
+ RCHRES BENOD TCBEN EXPOD BRBOD(1) BRBOD(2) EXPREL ***
+ # - # mg/m2.hr mg/m2.hr mg/m2.hr ***
+ 1 5 10. 1.1 1.2 20. 25. 1.3
+ END OX-BENPARM
+
+ OX-CFOREA
+ RCHRES CFOREA ***
+ # - # ***
+ 1 5.
+ END OX-CFOREA
+
+ OX-REAPARM
+ RCHRES TCGINV REAK EXPRED EXPREV ***
+ # - # /hr ***
+ 4 2.0 -1.1 1.1
+ END OX-REAPARM
+
+ OX-INIT
+ RCHRES DOX BOD SATDO ***
+ # - # mg/l mg/l mg/l ***
+ 1 5 8. 100.
+ END OX-INIT
+
+ *** Section NUTRX ***
+
+ NUT-FLAGS
+ RCHRES TAM NO2 PO4 AMV DEN ADNH ADPO PHFL ***
+ # - # ***
+ 1 5 1 1 1 1 1 0 0
+ END NUT-FLAGS
+
+ NUT-BENPARM
+ RCHRES BRTAM(1) BRTAM(2) BRPO4(1) BRPO4(2) ANAER ***
+ # - # mg/m2.hr mg/m2.hr mg/m2.hr mg/m2.hr mg/l ***
+ 1 5 11.0 33.0 1.1 2.2 0.0005
+ END NUT-BENPARM
+
+ NUT-NITDENIT
+ RCHRES KTAM20 KNO220 TCNIT KNO320 TCDEN DENOXT ***
+ # - # /hr /hr /hr mg/l ***
+ 1 5 .002 .004 1.07 .001 1.04 0.2
+ END NUT-NITDENIT
+
+ NUT-NH3VOLAT
+ RCHRES EXPNVG EXPNVL ***
+ # - # ***
+ 1 5 .50 0.6667
+ END NUT-NH3VOLAT
+
+ NUT-BEDCONC
+ RCHRES Bed concentrations of NH4 & PO4 (mg/mg) ***
+ # - # NH4-sand NH4-silt NH4-clay PO4-sand PO4-silt PO4-clay ***
+ 1 5 0.00001 0.00001 0.00001 0.00001 0.00001 0.00001
+ END NUT-BEDCONC
+
+ NUT-ADSPARM
+ RCHRES Partition coefficients for NH4 AND PO4 (l/mg) ***
+ # - # NH4-sand NH4-silt NH4-clay PO4-sand PO4-silt PO4-clay ***
+ 1 5 0.0001 0.0001 0.0001 10. 10. 10.
+ END NUT-ADSPARM
+
+ NUT-DINIT
+ RCHRES NO3 TAM NO2 PO4 PHVAL ***
+ # - # mg/l mg/l mg/l mg/l ph units ***
+ 1 5 40. 10. 1. 50. 7.0
+ END NUT-DINIT
+
+ NUT-ADSINIT
+ RCHRES Initial suspended NH4 and PO4 concentrations (mg/mg) ***
+ # - # NH4-sand NH4-silt NH4-clay PO4-sand PO4-silt PO4-clay ***
+ 1 5 0. 0. 0. 0. 0. 0.
+ END NUT-ADSINIT
+
+ *** Section PLANK ***
+
+ PLNK-FLAGS
+ RCHRES PHYF ZOOF BALF SDLT AMRF DECF NSFG ZFOO ***
+ # - # ***
+ 1 5 1 1 1 1 1 1
+ END PLNK-FLAGS
+
+ PLNK-PARM1
+ RCHRES RATCLP NONREF LITSED ALNPR EXTB MALGR ***
+ # - # /ft /hr ***
+ 1 5 4.5
+ END PLNK-PARM1
+
+ PHYTO-PARM
+ RCHRES SEED MXSTAY OREF CLALDH PHYSET REFSET ***
+ # - # mg/l mg/l ug/l ***
+ 1 5 .1 .1 .5 .5
+ END PHYTO-PARM
+
+ ZOO-PARM1
+ RCHRES MZOEAT ZFIL20 ZRES20 ZD OXZD ***
+ # - # mg/l.hr l/mgzoo.hr /hr /hr /hr ***
+ 1 5 .2
+ END ZOO-PARM1
+
+ PLNK-INIT
+ RCHRES PHYTO ZOO BENAL ORN ORP ORC ***
+ # - # mg/l org/l mg/m2 mg/l mg/l mg/l ***
+ 1 5 40. 200. 5. 20. 20. 20.
+ END PLNK-INIT
+
+ *** Section PHCARB ***
+
+ PH-PARM1
+ RCHRES PHCN ALKC ***
+ # - # ***
+ 1 5 50
+ END PH-PARM1
+
+ PH-INIT
+ RCHRES TIC CO2 PH ***
+ # - # mg/l mg/l ***
+ 1 5 20. 5. 8.5
+ END PH-INIT
+END RCHRES
+
+FTABLES
+ FTABLE 1
+ ROWS COLS ***
+ 14 6
+ WINTER SUMMER SPLWAY ***
+ DEPTH AREA VOLUME OUTLET OUTLET DISCH ***
+ (FT) (ACRES) (AC-FT) DISCH DISCH (CFS) ***
+ (CFS) (CFS) ***
+ .000 .000 .0000 .0000 .0000 .0000
+ 2.000 1.212 1.2120 0.0000 .0000 .0000
+ 4.000 2.424 4.8480 0.0000 .0000 .0000
+ 6.000 3.636 10.9080 0.0000 .0000 .0000
+ 8.000 4.848 19.3920 0.0000 .0000 .0000
+ 10.000 6.061 30.3050 0.0000 .0000 .0000
+ 12.000 7.273 43.6380 5.0000 3.5000 .0000
+ 14.000 8.485 59.3950 6.2500 4.3750 .0000
+ 16.000 9.697 77.5760 7.5000 5.2500 .0000
+ 18.000 10.909 98.1810 8.7500 6.1250 .0000
+ 20.000 12.121 121.2100 10.0000 7.0000 .0000
+ 21.000 12.727 133.6360 10.6250 7.4375 50.0000
+ 22.000 13.333 146.6630 11.2500 7.8750 100.0000
+ 23.000 13.939 160.3030 11.8750 8.3125 500.0000
+ END FTABLE 1
+
+ FTABLE 2
+ ROWS COLS ***
+ 13 4
+ DEPTH AREA VOLUME DISCH FLO-THRU ***
+ (FT) (ACRES) (AC-FT) (CFS) (MIN) ***
+ .000 .000 .0000 .000 0.0
+ .167 .071 .0109 1.2241 6.5
+ .333 .081 .0236 3.9148 4.4
+ .500 .091 .0379 7.8193 3.5
+ .667 .101 .0539 12.9032 3.0
+ .833 .111 .0715 19.1853 2.7
+ 1.000 .121 .0909 26.7046 2.5
+ 1.333 .141 .1347 45.6529 2.1
+ 1.667 .162 .1852 70.1757 1.9
+ 2.000 .182 .2424 100.7192 1.7
+ 2.667 .586 .4983 201.9005 1.8
+ 3.333 .990 1.0236 344.6344 2.2
+ 4.000 1.394 1.8182 537.0775 2.5
+ END FTABLE 2
+
+ FTABLE 3
+ ROWS COLS ***
+ 13 4
+ DEPTH AREA VOLUME DISCH FLO-THRU ***
+ (FT) (ACRES) (AC-FT) (CFS) (MIN) ***
+ .000 .000 .0000 .000 0.0
+ .167 .071 .0109 1.4992 5.3
+ .333 .081 .0236 4.7947 3.6
+ .500 .091 .0379 9.5766 2.9
+ .667 .101 .0539 15.8032 2.5
+ .833 .111 .0715 23.4971 2.2
+ 1.000 .121 .0909 32.7063 2.0
+ 1.333 .141 .1347 55.9132 1.7
+ 1.667 .162 .1852 85.9474 1.6
+ 2.000 .182 .2424 123.3553 1.4
+ 2.667 .586 .4983 247.2766 1.5
+ 3.333 .990 1.0236 422.0892 1.8
+ 4.000 1.394 1.8182 657.7828 2.0
+ END FTABLE 3
+
+ FTABLE 4
+ ROWS COLS ***
+ 13 4
+ DEPTH AREA VOLUME DISCH FLO-THRU ***
+ (FT) (ACRES) (AC-FT) (CFS) (MIN) ***
+ .000 .000 .0000 .000 0.0
+ .250 .848 .1970 .9024 158.5
+ .500 .970 .4242 2.8860 106.7
+ .750 1.091 .6818 5.7642 85.9
+ 1.000 1.212 .9697 9.5120 74.0
+ 1.250 1.333 1.2879 14.1431 66.1
+ 1.500 1.455 1.6364 19.6862 60.3
+ 2.000 1.697 2.4242 33.6545 52.3
+ 2.500 1.939 3.3333 51.7323 46.8
+ 3.000 2.182 4.3636 74.2486 42.7
+ 4.000 11.879 11.3939 155.5774 53.2
+ 5.000 21.576 28.1212 296.8633 68.8
+ 6.000 31.273 54.5454 522.1440 75.8
+ END FTABLE 4
+
+ FTABLE 5
+ ROWS COLS ***
+ 13 4
+ DEPTH AREA VOLUME DISCH FLO-THRU ***
+ (FT) (ACRES) (AC-FT) (CFS) (MIN) ***
+ .000 .000 .0000 .000 0.0
+ .333 1.697 .5253 1.5869 240.3
+ .667 1.939 1.1313 5.0752 161.8
+ 1.000 2.182 1.8182 10.1370 130.2
+ 1.333 2.424 2.5859 16.7279 112.2
+ 1.667 2.667 3.4343 24.8719 100.2
+ 2.000 2.909 4.3636 34.6200 91.5
+ 2.667 3.394 6.4646 59.1848 79.3
+ 3.333 3.879 8.8889 90.9763 70.9
+ 4.000 4.364 11.6364 130.5731 64.7
+ 5.333 36.687 39.0034 284.8886 99.4
+ 6.667 69.010 109.4680 593.7734 133.8
+ 8.000 101.333 223.0302 1129.6948 143.3
+ END FTABLE 5
+END FTABLES
+
+DISPLY
+ DISPLY-INFO1
+ # - #<----------Title----------->***TRAN PIVL DIG1 FIL1 PYR DIG2 FIL2 YRND
+ 1 O2 CONC, MEIER POND (mg/l) AVER 1 2 66 12
+ 2 PEST SED CONC, POND (mg/kg) AVER 1 2 66 12
+ 3 O2 CONC,LOWER KITTLE C(mg/l) AVER 1 2 66 12
+ 4 PEST SED CONC,L KTL C(mg/kg) AVER 1 2 66 12
+ 5 WATER TEMP,MEIER POND (DEGF) AVER 1 2 66 12
+ END DISPLY-INFO1
+END DISPLY
+
+GENER
+ OPCODE
+ # - # Op- ***
+ code ***
+ 1 2 19
+ END OPCODE
+END GENER
+
+PLTGEN
+ PLOTINFO
+ # - # FILE NPT NMN LABL PYR PIVL ***
+ 1 94 2 24
+ 2 95 3 1 6
+ END PLOTINFO
+
+ GEN-LABELS
+ # - #<----------------Title ----------------> *** <------Y axis------>
+ 1 SIMULATED FLOWS (CFS) CFS
+ 2 SIMULATED VALS RELATED TO TEMP&PH,RCH 4
+ END GEN-LABELS
+
+ SCALING
+ # - # YMIN YMAX IVLIN ***
+ 1 2 0. 150. 20.
+ END SCALING
+
+ CURV-DATA (first curve)
+ <-Curve label--> Line Intg Col Tran ***
+ # - # type eqv code code ***
+ 1 TOTAL POND OUTFL 7 1 AVER
+ 2 AVDEP FOR RCH 4 7 1 LAST
+ END CURV-DATA
+
+ CURV-DATA (second curve)
+ <-Curve label--> Line Intg Col Tran ***
+ # - # type eqv code code ***
+ 1 LOWER KITTLE CR 8 2 AVER
+ 2 TW FOR RCH 4 8 2 LAST
+ END CURV-DATA
+
+ CURV-DATA (third curve)
+ <-Curve label--> Line Intg Col Tran ***
+ # - # type eqv code code ***
+ 2 PH FOR RCH 4 9 2 LAST
+ END CURV-DATA
+
+ CURV-DATA (fourth curve)
+ <-Curve label--> Line Intg Col Tran ***
+ # - # type eqv code code ***
+ 2 HTEXCH FOR RCH 4 10 2
+ END CURV-DATA
+END PLTGEN
+
+EXT SOURCES
+<-Volume-> SsysSgap<--Mult-->Tran <-Target vols> <-Grp> <-Member-> ***
+ # # tem strg<-factor->strg # # # # ***
+WDM 39 PREC ENGLZERO SAME PERLND 1 EXTNL PREC
+WDM 131 PREC ENGLZERO SAME IMPLND 1 EXTNL PREC
+WDM 39 PREC ENGLZERO SAME RCHRES 1 3 EXTNL PREC
+WDM 131 PREC ENGLZERO SAME RCHRES 4 5 EXTNL PREC
+WDM 123 ATMP ENGL SAME PERLND 1 ATEMP AIRTMP
+WDM 122 ATMP ENGL SAME IMPLND 1 ATEMP AIRTMP
+WDM 123 ATMP ENGL SAME RCHRES 1 3 EXTNL GATMP
+WDM 122 ATMP ENGL SAME RCHRES 4 5 EXTNL GATMP
+WDM 41 EVAP ENGL .7 DIV PERLND 1 EXTNL PETINP
+WDM 41 EVAP ENGL .7 DIV IMPLND 1 EXTNL PETINP
+WDM 41 EVAP ENGL .7 DIV RCHRES 1 5 EXTNL POTEV
+WDM 42 WIND ENGL DIV PERLND 1 EXTNL WINMOV
+WDM 42 WIND ENGL DIV IMPLND 1 EXTNL WINMOV
+WDM 42 WIND ENGL DIV RCHRES 1 5 EXTNL WIND
+WDM 46 SOLR ENGL DIV PERLND 1 EXTNL SOLRAD
+WDM 46 SOLR ENGL DIV IMPLND 1 EXTNL SOLRAD
+WDM 46 SOLR ENGL DIV RCHRES 1 5 EXTNL SOLRAD
+WDM 126 DEWP ENGL SAME PERLND 1 EXTNL DTMPG
+WDM 125 DEWP ENGL SAME IMPLND 1 EXTNL DTMPG
+WDM 126 DEWP ENGL SAME RCHRES 1 3 EXTNL DEWTMP
+WDM 125 DEWP ENGL SAME RCHRES 4 5 EXTNL DEWTMP
+WDM 140 CLND ENGL SAME RCHRES 1 EXTNL COLIND
+WDM 135 CLDC ENGL SAME PERLND 1 EXTNL CLOUD
+WDM 135 CLDC ENGL SAME IMPLND 1 EXTNL CLOUD
+WDM 135 CLDC ENGL SAME RCHRES 1 5 EXTNL CLOUD
+END EXT SOURCES
+
+SCHEMATIC
+<-Source-> <--Area--> <-Target-> ***
+ # <-factor-> # # ***
+PERLND 1 6000. RCHRES 1 1
+IMPLND 1 3000. RCHRES 5 2
+RCHRES 1 RCHRES 2 3
+RCHRES 1 RCHRES 3 4
+RCHRES 2 RCHRES 4 5
+RCHRES 3 RCHRES 4 5
+RCHRES 4 RCHRES 5 5
+END SCHEMATIC
+
+MASS-LINK
+
+ MASS-LINK 1
+ <-Grp> <-Member-><--Mult--> <-Grp> <-Member-> ***
+ # #<-factor-> # # ***
+PERLND PWATER PERO 0.0833333 RCHRES INFLOW IVOL
+PERLND PWTGAS POHT RCHRES INFLOW IHEAT
+PERLND PWTGAS PODOXM RCHRES INFLOW OXIF 1
+PERLND PWTGAS POCO2M RCHRES INFLOW PHIF 2
+ END MASS-LINK 1
+
+ MASS-LINK 2
+ <-Grp> <-Member-><--Mult--> <-Grp> <-Member-> ***
+ # #<-factor-> # # ***
+IMPLND IWATER SURO 0.0833333 RCHRES INFLOW IVOL
+IMPLND SOLIDS SOSLD 0.10 RCHRES INFLOW ISED 1
+IMPLND SOLIDS SOSLD 0.46 RCHRES INFLOW ISED 2
+IMPLND SOLIDS SOSLD 0.44 RCHRES INFLOW ISED 3
+IMPLND IWTGAS SOHT RCHRES INFLOW IHEAT
+IMPLND IWTGAS SODOXM RCHRES INFLOW OXIF 1
+IMPLND IWTGAS SOCO2M RCHRES INFLOW PHIF 2
+IMPLND IQUAL SOQUAL RCHRES INFLOW OXIF 2
+ END MASS-LINK 2
+
+ MASS-LINK 3
+ <-Grp> <-Member-><--Mult--> <-Grp> <-Member-> ***
+ # #<-factor-> # # ***
+RCHRES OFLOW 1 RCHRES INFLOW
+ END MASS-LINK 3
+
+ MASS-LINK 4
+ <-Grp> <-Member-><--Mult--> <-Grp> <-Member-> ***
+ # #<-factor-> # # ***
+RCHRES OFLOW 2 RCHRES INFLOW
+ END MASS-LINK 4
+
+ MASS-LINK 5
+ <-Grp> <-Member-><--Mult--> <-Grp> <-Member-> ***
+ # #<-factor-> # # ***
+RCHRES ROFLOW RCHRES INFLOW
+ END MASS-LINK 5
+
+END MASS-LINK
+
+NETWORK
+<-Volume-> <-Grp> <-Member-><--Mult-->Tran <-Target vols> <-Grp> <-Member-> ***
+ # # #<-factor->strg # # # # ***
+RCHRES 1 HTRCH TW 1. DISPLY 5 INPUT TIMSER
+RCHRES 1 OXRX DOX 1. DISPLY 1 INPUT TIMSER
+RCHRES 1 GQUAL RSQAL 12 1. GENER 1 INPUT ONE
+RCHRES 1 SEDTRN RSED 10 1. GENER 1 INPUT TWO
+GENER 1 OUTPUT TIMSER 1.1 DISPLY 2 INPUT TIMSER
+RCHRES 1 HYDR ROVOL 12.1 PLTGEN 1 INPUT MEAN 1
+RCHRES 4 HYDR AVDEP 1. PLTGEN 2 INPUT POINT 1
+RCHRES 4 HTRCH TW 1. PLTGEN 2 INPUT POINT 2
+RCHRES 4 PHCARB PHST 3 1. PLTGEN 2 INPUT POINT 3
+RCHRES 4 HTRCH HTEXCH 1. PLTGEN 2 INPUT MEAN 1
+RCHRES 5 OXRX DOX 1. DISPLY 3 INPUT TIMSER
+RCHRES 5 GQUAL RSQAL 12 1. GENER 2 INPUT ONE
+RCHRES 5 SEDTRN RSED 10 1. GENER 2 INPUT TWO
+GENER 2 OUTPUT TIMSER 1.1 DISPLY 4 INPUT TIMSER
+RCHRES 5 HYDR ROVOL 12.1 PLTGEN 1 INPUT MEAN 2
+END NETWORK
+
+SPEC-ACTIONS
+*** test special actions
+ RCHRES 5 RSED 4 += 2.50E+05
+ RCHRES 5 RSED 5 += 6.89E+05
+ RCHRES 5 RSED 6 += 4.01E+05
+END SPEC-ACTIONS
+
+
+END RUN
diff --git a/tests/test10specl/HSPFresults/test10specl.wdm b/tests/test10specl/HSPFresults/test10specl.wdm
new file mode 100644
index 00000000..8d0e2766
Binary files /dev/null and b/tests/test10specl/HSPFresults/test10specl.wdm differ
diff --git a/tests/test10specl/HSPFresults/test10speclI.hbn b/tests/test10specl/HSPFresults/test10speclI.hbn
new file mode 100644
index 00000000..a4529a11
Binary files /dev/null and b/tests/test10specl/HSPFresults/test10speclI.hbn differ
diff --git a/tests/test10specl/HSPFresults/test10speclP.hbn b/tests/test10specl/HSPFresults/test10speclP.hbn
new file mode 100644
index 00000000..bfe0d3b0
Binary files /dev/null and b/tests/test10specl/HSPFresults/test10speclP.hbn differ
diff --git a/tests/test10specl/HSPFresults/test10speclR.hbn b/tests/test10specl/HSPFresults/test10speclR.hbn
new file mode 100644
index 00000000..45fa25d7
Binary files /dev/null and b/tests/test10specl/HSPFresults/test10speclR.hbn differ
diff --git a/tests/test_regression.py b/tests/test_regression.py
index 2573b6b3..51a4ed91 100644
--- a/tests/test_regression.py
+++ b/tests/test_regression.py
@@ -14,7 +14,7 @@ def _get_hsp2_data(self, test_root) -> None:
hspf_uci = test_root_hspf.resolve() / f"{self.compare_case}.uci"
assert hspf_uci.exists()
- temp_h5file = test_root_hspf / f"_temp_{self.compare_case}.h5"
+ temp_h5file = test_root_hspf / f"{self.compare_case}.h5"
if temp_h5file.exists():
temp_h5file.unlink()
@@ -41,26 +41,28 @@ def _init_files(self):
# "test05",
# "test09",
"test10",
+ "test10specl",
# "test10b",
],
)
-class TestRegression:
- results: dict[tuple, tuple] = {}
-
- @pytest.fixture(autouse=True)
- def setup_and_teardown(self, case):
- test = RegressTest(case, threads=1)
- self.results = test.run_test()
- yield
- test.temp_h5file.unlink()
-
- def test_case(self, case):
- found = False
- for key, results in self.results.items():
- no_data_hsp2, no_data_hspf, match, diff = results
- if any([no_data_hsp2, no_data_hspf]):
- continue
-
- assert match, (case, key, f"{diff:0.00%}")
- found = True
- assert found
+def test_case(case):
+ test = RegressTest(case, threads=1)
+ results = test.run_test()
+ test.temp_h5file.unlink()
+
+ found = False
+ mismatches = []
+ for key, results in results.items():
+ no_data_hsp2, no_data_hspf, match, diff = results
+ if any([no_data_hsp2, no_data_hspf]):
+ continue
+ if not match:
+ mismatches.append((case, key, results))
+ found = True
+ assert found
+
+ if mismatches:
+ for case, key, results in mismatches:
+ _, _, _, diff = results
+ print(case, key, f"{diff:0.00%}")
+ raise ValueError("results don't match hspf output")
diff --git a/tests/testcbp/HSP2results/PL3_5250_0001.json.constant_wd b/tests/testcbp/HSP2results/PL3_5250_0001.json.constant_wd
new file mode 100644
index 00000000..8a6d387e
--- /dev/null
+++ b/tests/testcbp/HSP2results/PL3_5250_0001.json.constant_wd
@@ -0,0 +1,20 @@
+{
+ "RCHRES_R001": {
+ "name": "RCHRES_R001",
+ "object_class": "ModelObject",
+ "value": "0",
+ "wd_cfs": {
+ "name": "wd_cfs",
+ "object_class": "Constant",
+ "value": 10.0
+ },
+ "IVOLwrite": {
+ "name": "IVOLwrite",
+ "object_class": "ModelLinkage",
+ "left_path": "/STATE/RCHRES_R001/O2",
+ "right_path": "/STATE/RCHRES_R001/wd_cfs",
+ "link_type": 5
+ }
+ }
+}
+
diff --git a/tests/testcbp/HSP2results/check_endpoint_ts.py b/tests/testcbp/HSP2results/check_endpoint_ts.py
new file mode 100644
index 00000000..1bfce487
--- /dev/null
+++ b/tests/testcbp/HSP2results/check_endpoint_ts.py
@@ -0,0 +1,116 @@
+# Must be run from the HSPsquared source directory, the h5 file has already been setup with hsp import_uci test10.uci
+# bare bones tester - must be run from the HSPsquared source directory
+import os
+from HSP2.main import *
+from HSP2.om import *
+import HSP2IO
+import numpy
+from HSP2IO.hdf import HDF5
+from HSP2IO.io import IOManager
+fpath = './tests/test10/HSP2results/test10.h5'
+# try also:
+# fpath = './tests/testcbp/HSP2results/PL3_5250_0001.h5'
+# sometimes when testing you may need to close the file, so try:
+# f = h5py.File(fpath,'a') # use mode 'a' which allows read, write, modify
+# # f.close()
+hdf5_instance = HDF5(fpath)
+io_manager = IOManager(hdf5_instance)
+uci_obj = io_manager.read_uci()
+siminfo = uci_obj.siminfo
+opseq = uci_obj.opseq
+# Note: now that the UCI is read in and hdf5 loaded, you can see things like:
+# - hdf5_instance._store.keys() - all the paths in the UCI/hdf5
+# - finally stash specactions in state, not domain (segment) dependent so do it once
+# now load state and the special actions
+state = init_state_dicts()
+state_initialize_om(state)
+state['specactions'] = uci_obj.specactions # stash the specaction dict in state
+
+state_siminfo_hsp2(uci_obj, siminfo)
+# Add support for dynamic functions to operate on STATE
+# - Load any dynamic components if present, and store variables on objects
+state_load_dynamics_hsp2(state, io_manager, siminfo)
+# Iterate through all segments and add crucial paths to state
+# before loading dynamic components that may reference them
+state_init_hsp2(state, opseq, activities)
+state_load_dynamics_specl(state, io_manager, siminfo) # traditional special actions
+state_load_dynamics_om(state, io_manager, siminfo) # operational model for custom python
+state_om_model_run_prep(state, io_manager, siminfo) # this creates all objects from the UCI and previous loads
+# state['model_root_object'].find_var_path('RCHRES_R001')
+# Get the timeseries naked, without an object
+rchres1 = state['model_object_cache']['/STATE/RCHRES_R001']
+precip_ts = ModelLinkage('PRCP', rchres1, {'right_path':'/TIMESERIES/TS039', 'SVOLNO':'TS039', 'link_type':3})
+ts1 = precip_ts.read_ts() # same as precip_ts.ts_ix[precip_ts.ix], same as state['ts_ix'][precip_ts.ix]
+
+# NOTE: THIS SEEMS TO FAIL RIGHT NOW DUE TO WRITING THE TS VALUES BACK WITH A NAME OF tsvalue_0, and is now embeded
+
+
+# is the same as:
+# - ts = precip_ts.io_manager.read_ts(Category.INPUTS, None, precip_ts.ts_name)
+# - ts = transform(ts, precip_ts.ts_name, 'SAME', precip_ts.siminfo)
+# - ts = precip_ts.io_manager.read_ts(Category.INPUTS, None, precip_ts.ts_name).columns
+# - ts = np.transpose(ts)[0]
+# precip_ts.io_manager._input._store
+# write it back. We can give an arbitrary name or it will default to write back to the source path in right_path variable
+# we can specify a custom path to write this TS to
+precip_ts.write_path = '/RESULTS/test_TS039'
+precip_ts.write_ts()
+# precip_ts.write_ts is same as:
+# ts4 = precip_ts.format_ts(ts1, ['tsvalue'], siminfo['tindex'])
+# ts4.to_hdf(precip_ts.io_manager._output._store, precip_ts.write_path, format='t', data_columns=True, complevel=precip_ts.complevel)
+
+tsdf = pd.DataFrame(data=ts1, index=siminfo['tindex'],columns=None)
+# verify
+ts1 = precip_ts.read_ts() # same as precip_ts.ts_ix[precip_ts.ix], same as state['ts_ix'][precip_ts.ix]
+# Calls:
+# - ts = precip_ts.io_manager.read_ts(Category.INPUTS, None, precip_ts.ts_name)
+# - ts = transform(ts, self.ts_name, 'SAME', self.siminfo)
+# - ts = np.transpose(ts)[0]
+# should yield equivalent of:
+ts2 = hdf5_instance._store[precip_ts.ts_path]
+# data_frame.to_hdf(self._store, path, format='t', data_columns=True, complevel=complevel)
+ts3 = hdf5_instance._store[precip_ts.write_path]
+# and is same as
+ts4 = precip_ts.io_manager._output._store[precip_ts.write_path]
+
+exdd = defaultdict(list)
+exdd[(precip_ts.SVOL, precip_ts.SVOLNO)].append(pd.DataFrame({'SVOL':precip_ts.SVOLNO, 'SVOLNO':precip_ts.SVOLNO, 'MFACTOR':precip_ts.MFACTOR, 'TMEMN':precip_ts.TMEMN, 'TMEMSB':precip_ts.TMEMSB}))
+exdd[(precip_ts.SVOL, precip_ts.SVOLNO)].append({'SVOL':precip_ts.SVOLNO, 'SVOLNO':precip_ts.SVOLNO, 'MFACTOR':precip_ts.MFACTOR, 'TMEMN':precip_ts.TMEMN, 'TMEMSB':precip_ts.TMEMSB})
+
+exdd = defaultdict(list)
+df = pd.DataFrame({'SVOL':[precip_ts.SVOLNO], 'SVOLNO':[precip_ts.SVOLNO], 'MFACTOR':[precip_ts.MFACTOR], 'TMEMN':[precip_ts.TMEMN], 'TMEMSB':[precip_ts.TMEMSB]})
+for row in df.itertuples():
+ exdd[(precip_ts.SVOL, precip_ts.SVOLNO)].append(row)
+for row in exdd:
+ data_frame = precip_ts.io_manager.read_ts(category=Category.INPUTS,segment=row.SVOLNO)
+
+xdr = list(df.itertuples())
+xdr = precip_ts.ext_sourcesdd
+for row in xdr:
+ data_frame = precip_ts.io_manager.read_ts(category=Category.INPUTS,segment=row.SVOLNO)
+ clean_name(row.TMEMN,row.TMEMSB)
+
+ext_sourcesdd = XDR
+ts = Dict.empty(key_type=types.unicode_type, value_type=types.float64[:])
+data_frame = precip_ts.io_manager.read_ts(category=Category.INPUTS,segment=row.SVOLNO)
+
+if row.MFACTOR != 1.0:
+ data_frame *= row.MFACTOR
+
+t = transform(data_frame, row.TMEMN, row.TRAN, siminfo)
+
+tname = clean_name(row.TMEMN,row.TMEMSB)
+if tname in ts:
+ ts[tname] += t
+else:
+ ts[tname] = t
+
+
+# Debug: def read_ts(self, category:Category, operation:Union[str,None]=None, segment:Union[str,None]=None, activity:Union[str,None]=None, ...)
+ts = get_timeseries(precip_ts.io_manager, precip_ts.ext_sourcesdd, precip_ts.siminfo)
+for row in precip_ts.ext_sourcesdd[(precip_ts.SVOL, precip_ts.SVOLNO)]:
+ print(str(row.SVOLNO))
+precip_ts.io_manager.read_ts(Category.INPUTS, None, precip_ts.ts_name)
+precip_ts.io_manager.read_ts(category=Category.INPUTS,segment=row.SVOLNO)
+precip_ts.io_manager.read_ts(category=Category.INPUTS,segment=str(row.SVOLNO))
+precip_ts.io_manager.read_ts(category=Category.INPUTS, operation=None,segment=row.SVOLNO)
diff --git a/tests/testcbp/HSP2results/check_equation.py b/tests/testcbp/HSP2results/check_equation.py
new file mode 100644
index 00000000..eee26c2a
--- /dev/null
+++ b/tests/testcbp/HSP2results/check_equation.py
@@ -0,0 +1,62 @@
+# Must be run from the HSPsquared source directory, the h5 file has already been setup with hsp import_uci test10.uci
+# bare bones tester - must be run from the HSPsquared source directory
+import os
+from HSP2.main import *
+from HSP2.om import *
+import HSP2IO
+import numpy
+from HSP2IO.hdf import HDF5
+from HSP2IO.io import IOManager
+fpath = './tests/testcbp/HSP2results/JL1_6562_6560.h5'
+# try also:
+# fpath = './tests/testcbp/HSP2results/JL1_6562_6560.h5'
+# sometimes when testing you may need to close the file, so try:
+# f = h5py.File(fpath,'a') # use mode 'a' which allows read, write, modify
+# # f.close()
+hdf5_instance = HDF5(fpath)
+io_manager = IOManager(hdf5_instance)
+uci_obj = io_manager.read_uci()
+siminfo = uci_obj.siminfo
+opseq = uci_obj.opseq
+# Note: now that the UCI is read in and hdf5 loaded, you can see things like:
+# - hdf5_instance._store.keys() - all the paths in the UCI/hdf5
+# - finally stash specactions in state, not domain (segment) dependent so do it once
+# now load state and the special actions
+state = init_state_dicts()
+state_initialize_om(state)
+state['specactions'] = uci_obj.specactions # stash the specaction dict in state
+
+state_siminfo_hsp2(uci_obj, siminfo)
+# Add support for dynamic functions to operate on STATE
+# - Load any dynamic components if present, and store variables on objects
+state_load_dynamics_hsp2(state, io_manager, siminfo)
+# Iterate through all segments and add crucial paths to state
+# before loading dynamic components that may reference them
+state_init_hsp2(state, opseq, activities)
+state_load_dynamics_specl(state, io_manager, siminfo) # traditional special actions
+state_load_dynamics_om(state, io_manager, siminfo) # operational model for custom python
+state_om_model_run_prep(state, io_manager, siminfo) # this creates all objects from the UCI and previous loads
+# state['model_root_object'].find_var_path('RCHRES_R001')
+# Get the timeseries naked, without an object
+Rlocal = state['model_object_cache']['/STATE/RCHRES_R001/Rlocal']
+Rlocal_ts = Rlocal.read_ts()
+rchres1 = state['model_object_cache']['/STATE/RCHRES_R001']
+Rlocal_check = ModelLinkage('Rlocal1', rchres1, {'right_path':'/TIMESERIES/TS010', 'link_type':3})
+# Calls:
+# - ts = Rlocal.io_manager.read_ts(Category.INPUTS, None, Rlocal.ts_name)
+# - ts = transform(ts, Rlocal.ts_name, 'SAME', Rlocal.siminfo)
+Rlocal.io_manager._output._store.keys()
+# write it back. We can give an arbitrary name or it will default to write back to the source path in right_path variable
+ts1 = precip_ts.read_ts() # same as precip_ts.ts_ix[precip_ts.ix], same as state['ts_ix'][precip_ts.ix]
+# we can specify a custom path to write this TS to
+precip_ts.write_path = '/RESULTS/test_TS039'
+precip_ts.write_ts()
+# precip_ts.write_ts is same as:
+# ts4 = precip_ts.format_ts(ts1, ['tsvalue'], siminfo['tindex'])
+# ts4.to_hdf(precip_ts.io_manager._output._store, precip_ts.write_path, format='t', data_columns=True, complevel=precip_ts.complevel)
+
+start = time.time()
+iterate_models(model_exec_list, op_tokens, state_ix, dict_ix, ts_ix, siminfo['steps'], -1)
+end = time.time()
+print(len(model_exec_list), "components iterated over state_ix", siminfo['steps'], "time steps took" , end - start, "seconds")
+
diff --git a/tests/testcbp/HSP2results/jk_tester.R b/tests/testcbp/HSP2results/check_h5.R
similarity index 100%
rename from tests/testcbp/HSP2results/jk_tester.R
rename to tests/testcbp/HSP2results/check_h5.R
diff --git a/tests/testcbp/HSP2results/jk_tester.py b/tests/testcbp/HSP2results/jk_tester.py
deleted file mode 100755
index 569dbd2f..00000000
--- a/tests/testcbp/HSP2results/jk_tester.py
+++ /dev/null
@@ -1,76 +0,0 @@
-#!/usr/bin/python3
-
-import numpy as np
-
-# from re import S
-# from numpy import float64, float32
-# from pandas import DataFrame, date_range
-# from pandas.tseries.offsets import Minute
-# from datetime import datetime as dt
-# import os
-# from HSP2.utilities import versions, get_timeseries, expand_timeseries_names, save_timeseries, get_gener_timeseries
-# from HSP2.configuration import activities, noop, expand_masslinks
-
-# from HSP2IO.io import IOManager, SupportsReadTS, Category
-
-print("Running jk_tester.py")
-
-# from pandas import read_hdf
-
-# from pandas import options, read_hdf, DataFrame
-# import sys, os
-# print '\n'.join(sys.path)
-
-# home_dir = os.path.expanduser("~")
-# print "My home directory:", home_dir
-
-# python -m site
-
-# sys.path.append('/usr/local/lib/python3.8')
-# sys.path.append('/usr/local/lib/python3.8/dist-packages')
-# print '\n'.join(sys.path)
-
-################################
-
-# import h5py
-# filename = "PL3_5250_0001.h5"
-
-# with h5py.File(filename, "r") as f:
-# # Print all root level object names (aka keys)
-# # these can be group or dataset names
-# print("Keys: %s" % f.keys())
-
-# from HSP2 import versions, main as run
-# from HSP2tools import read_UCI, read_WDM
-
-################################################################
-# from pandas import read_hdf
-
-# HBN = 'PL3_5250_0001.h5'
-# read_hdf(HBN,'PL3_5250_0001')
-
-
-################################################################
-################################################################
-a = np.array([[1,2,3],[2,4,6]])
-# b = a[1,2] # extracts the 6
-# print(b)
-
-# b = 77
-# print(b)
-# print(a)
-
-# b = a[1:2,2:3]
-# print(b)
-
-# b[[0]] = 77
-# print(a)
-
-# b = 99
-# print(a)
-
-b = a
-print(b)
-# b[[0]] = 77
-b[1,2] = 77
-print(a)
\ No newline at end of file