Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Results and Control updates for testing and stepwise. #375

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2ff8c42
Adding results files to fresh branch
dbhart Aug 31, 2023
f24f670
Updating append method to use pd.concat
kbonney Aug 31, 2023
17554e7
Adding shift method to controls
dbhart Aug 31, 2023
4730568
Adding set_initial_conditions method to water network model
dbhart Aug 31, 2023
a55e2e9
rename append function
kbonney Nov 6, 2023
ff180f3
rename append function
kbonney Nov 6, 2023
97b97bf
overhauling results object by adding baseclass to allow for other res…
kbonney Nov 14, 2023
b5e1d0f
refactoring changes for results name change
kbonney Nov 14, 2023
0648d09
Updates to results object. Removed base class but kept data_attribute…
kbonney Nov 15, 2023
fa76d59
updating results object
kbonney Nov 17, 2023
6930db9
moved stopstart tests to own file
kbonney Nov 17, 2023
c136e00
fixed bugs and simplified append method
kbonney Nov 17, 2023
f3c5e80
beginning a test file for results object
kbonney Nov 17, 2023
2346d00
separating tests into more descriptive file names. simplifying node/l…
kbonney Nov 20, 2023
6069f2d
update test results
kbonney Nov 20, 2023
95a2747
bug fix results object
kbonney Nov 20, 2023
a7272a6
updating test pickle
kbonney Nov 20, 2023
3bccc34
adding ability to run as script to test
kbonney Nov 20, 2023
29fa5c9
added back in resultsstatus class
kbonney Nov 20, 2023
07b3ff8
Pulling in updates to status conversion from realtime branch
kbonney Jan 8, 2024
f575147
Merge branch 'main' into results
kbonney Apr 15, 2024
6f7f95a
Merge remote-tracking branch 'usepa/main' into results
kbonney Jun 7, 2024
a94cb7c
adding a test for stepwise simulation (24 1hr steps)
kbonney Jun 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions wntr/epanet/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -2806,11 +2806,26 @@ def read(self, filename, convergence_error=False, darcy_weisbach=False, convert=
self.results.link["headloss"] = pd.DataFrame(data=headloss, columns=linknames, index=reporttimes)

status = np.array(df['linkstatus'])

if self.convert_status:
status[status <= 2] = 0
status[status == 3] = 1
status[status >= 5] = 1
status[status == 4] = 2
"""
EPANET status codes
0 = closed (max head exceeded)
1 = temporarily closed
2 = closed
3 = open
4 = active (partially open)
5 = open (max flow exceeded)
6 = open (flow setting not met)
7 = open (press setting not met)
"""
# 0 = 0, treat closed (max head exceeded) pump as closed
# 1 = 1, treat temporarily closed pipe as open
status[status == 2] = 0 # 2 = 0, closed
status[status == 3] = 1 # 3 = 1, open
status[status == 4] = 2 # 4 = 2, active
status[status >= 5] = 1 # 5,6,7 = 1, treat valve open under different conditions as open

self.results.link['status'] = pd.DataFrame(data=status, columns=linknames, index=reporttimes)

setting = np.array(df['linksetting'])
Expand Down
42 changes: 42 additions & 0 deletions wntr/network/controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,25 @@ def __init__(self):
def _reset(self):
pass

def _shift(self, value):
"""
Shift any SimTimeConditions within larger condition rules by value seconds (backward).

I.e., if a control is scheduled at simulation time 7200 and you shift by 3600, the new
control threshold with be sim time 3600.

Parameters
----------
value : float
seconds to subtract from threshold

Returns
-------
bool
is this still a valid control?
"""
return True

@abc.abstractmethod
def requires(self):
"""
Expand Down Expand Up @@ -585,6 +604,13 @@ def _compare(self, other):
return False
return True

def _shift(self, value):
self._threshold -= value
if self._threshold >= 0:
return True
self._threshold = 0
return False

@property
def name(self):
if not self._repeat:
Expand Down Expand Up @@ -996,6 +1022,11 @@ def _reset(self):
self._condition_1._reset()
self._condition_2._reset()

def _shift(self, value):
success1 = self._condition_1._shift(value)
success2 = self._condition_2._shift(value)
return success1 or success2

def _compare(self, other):
"""
Parameters
Expand Down Expand Up @@ -1062,6 +1093,11 @@ def _reset(self):
self._condition_1._reset()
self._condition_2._reset()

def _shift(self, value):
success1 = self._condition_1._shift(value)
success2 = self._condition_2._shift(value)
return success1 or success2

def _compare(self, other):
"""
Parameters
Expand Down Expand Up @@ -2012,6 +2048,9 @@ def epanet_control_type(self):
control_type: _ControlType
"""
return self._control_type

def _shift(self, step):
return self._condition._shift(step)

def requires(self):
req = self._condition.requires()
Expand Down Expand Up @@ -2177,6 +2216,9 @@ def __init__(self, condition, then_action: BaseControlAction, priority=ControlPr
else:
self._control_type = _ControlType.postsolve

def _shift(self, step):
return self._condition._shift(step)

@classmethod
def _time_control(cls, wnm, run_at_time, time_flag, daily_flag, control_action, name=None):
"""
Expand Down
132 changes: 132 additions & 0 deletions wntr/network/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -1552,7 +1552,139 @@ def reset_initial_values(self):
for name, control in self.controls():
control._reset()

def set_initial_conditions(self, results, ts=None, remove_controls=True, warn=False):
"""
Set the initial conditions of the network based on prior simulation results.

Parameters
----------
results : SimulationResults
Results from a prior simulation
ts : int, optional
The time value (in seconds) from the results to use to select initial conditions,
by default None (which will use the final values)
remove_controls : bool, optional
If a rule or control has a SimTimeCondition that now occurs prior to simulation start, remove
the control, by default True.
warn : bool
Send a warning to the logger that the rule has been deleted, by default False.
When False, information is sent to the logger at the `info` level.


Returns
-------
list
Control names that have been, when `remove_controls is True`,
or need to be, when `remove_controls is False`,
removed from the water network model


Raises
------
NameError
If both `ts` and `idx` are passed in
IndexError
If `ts` is passed, but no such time exists in the results
ValueError
If the time selected is not a multiple of the pattern timestep


"""
if ts is None:
end_time = results.node['demand'].index[-1]
else:
ts = int(ts)
if ts in results.node['demand'].index:
end_time = ts
else:
raise IndexError('There is no time "{}" in the results'.format(ts))

# if end_time / self.options.time.pattern_timestep != end_time // self.options.time.pattern_timestep:
# raise ValueError('You must give a time step that is a multiple of the pattern_timestep ({})'.format(self.options.time.pattern_timestep))

self.sim_time = 0.0
self._prev_sim_time = None

for name, node in self.nodes(Junction):
node._head = None
node._demand = None
node._pressure = None
try: node.initial_quality = float(results.node['quality'].loc[end_time, name])
except KeyError: pass
node._leak_demand = None
node._leak_status = False
node._is_isolated = False

for name, node in self.nodes(Tank):
node._head = None
node._demand = None
node._pressure = None
node.init_level = float(results.node['head'].loc[end_time, name] - node.elevation)
try: node.initial_quality = float(results.node['quality'].loc[end_time, name])
except KeyError: pass
node._prev_head = node.head
node._leak_demand = None
node._leak_status = False
node._is_isolated = False

for name, node in self.nodes(Reservoir):
node._head = None
node._demand = None
node._pressure = None
try: node.initial_quality = float(results.node['quality'].loc[end_time, name])
except KeyError: pass
node._leak_demand = None
node._is_isolated = False

for name, link in self.links(Pipe):
link.initial_status = results.link['status'].loc[end_time, name]
try: link.initial_setting = results.link['setting'].loc[end_time, name]
except KeyError: link.initial_setting = link.setting
link._user_status = link.initial_status
link._internal_status = LinkStatus.Active
link._is_isolated = False
link._flow = None
link._prev_setting = None

for name, link in self.links(Pump):
link.initial_status = results.link['status'].loc[end_time, name]
try: link.initial_setting = results.link['setting'].loc[end_time, name]
except KeyError: link.initial_setting = link.setting
link._user_status = link.initial_status
link._setting = link.initial_setting
link._internal_status = LinkStatus.Active
link._is_isolated = False
link._flow = None
if isinstance(link, PowerPump):
link.power = link._base_power
link._prev_setting = None

for name, link in self.links(Valve):
link.initial_status = results.link['status'].loc[end_time, name]
try: link.initial_setting = results.link['setting'].loc[end_time, name]
except KeyError: link.initial_setting = link.setting
# print(name, link.initial_status, link.initial_setting)
link._user_status = link.initial_status
link._setting = link.initial_setting
link._internal_status = LinkStatus.Active
link._is_isolated = False
link._flow = None
link._prev_setting = None

to_delete = []
for name, control in self.controls():
control._reset()
still_good = control._shift(end_time)
if not still_good:
to_delete.append(name)

for name in to_delete:
msg = 'Rule {} {} removed from the network'.format(name, 'has been' if remove_controls else 'needs to be')
if warn: logger.warning(msg)
else: logger.info(msg)
if remove_controls:
self.remove_control(name)
return to_delete
class PatternRegistry(Registry):
"""A registry for patterns."""

Expand Down
Loading
Loading