1+ '''
2+ # install pystorms from the current directory (this should be commented out in final version once pystorms source code isn't changing all the time)
3+ import subprocess
4+ import sys
5+ subprocess.check_call([sys.executable, '-m', 'pip', 'uninstall', '-y', 'pystorms'])
6+ subprocess.check_call([sys.executable, '-m', 'pip', 'cache', 'purge'])
7+ subprocess.check_call([sys.executable, '-m', 'pip', 'install', '.'])
8+ '''
9+ import pystorms # this will be the first line of the program when dev is done
10+
11+ import pyswmm
12+ import numpy as np
13+ import matplotlib .pyplot as plt
14+ import pandas as pd
15+ import dill as pickle
16+ import datetime
17+ import os
18+ import swmmio
19+
20+ np .set_printoptions (precision = 3 ,suppress = True )
21+
22+ # BETA
23+ # options are: 'mpc' (per sadler 2020) or 'uncontrolled'
24+ evaluating = 'uncontrolled'
25+ verbose = True
26+ version = "1" # options are "1" and "2"
27+ level = "1" # options are "1" , "2", and "3"
28+ plot = True # plot True significantly increases the memory usage.
29+ # set the working directory to the directory of this script
30+ os .chdir (os .path .dirname (os .path .abspath (__file__ )))
31+ print (os .getcwd ())
32+ # set the random seed
33+ rand_seed = 42
34+ np .random .seed (rand_seed )
35+
36+
37+ print ("evaluating " , evaluating , " for beta scenario" )
38+
39+ if evaluating == "mpc" :
40+ folder_path = str ("./v" + version + "/lev" + level + "/results" )
41+ elif evaluating == "uncontrolled" :
42+ folder_path = str ("./v" + version + "/results" )
43+
44+ if not os .path .exists (folder_path ):
45+ os .makedirs (folder_path )
46+
47+
48+ # project file is in english units
49+ cfs2cms = 35.315
50+ ft2meters = 3.281
51+
52+
53+
54+ mpc_df = pd .read_csv ("mpc_rules_beta.csv" )
55+ mpc_df ['datetime' ] = pd .to_datetime (mpc_df ['datetime' ])
56+
57+ # convert the dataframe of rules to an easily readable dictionary
58+ mpc_df ['P0' ].replace ({"ON" :1.0 , "OFF" :0.0 }, inplace = True )
59+ mpc_df .drop (columns = ["simtime (hr)" ], inplace = True )
60+ mpc_datetimes = mpc_df ['datetime' ].to_list ()
61+ mpc_controller = mpc_df .set_index ('datetime' ).to_dict ()
62+
63+ # initialize the scenario, and obtain the initial controller settings
64+ #env = pystorms.scenarios.beta(level=level)
65+ env = pystorms .scenarios .beta ()
66+ controller_datetime = env .env .sim .start_time
67+ actions = np .array ([mpc_controller ['R2' ][controller_datetime ],
68+ mpc_controller ['P0' ][controller_datetime ],
69+ mpc_controller ['W0' ][controller_datetime ]])
70+ done = False
71+
72+ last_eval = env .env .sim .start_time - datetime .timedelta (days = 1 ) # initto a time before the simulation starts
73+ last_read = env .env .sim .start_time - datetime .timedelta (days = 1 ) # initto a time before the simulation starts
74+
75+ states = pd .DataFrame (columns = env .config ['states' ])
76+ actions_log = pd .DataFrame (columns = env .config ['action_space' ])
77+
78+
79+ # Run the simulation
80+ while not done :
81+
82+ if evaluating == "mpc" and (env .env .sim .current_time >= controller_datetime ) and (controller_datetime in mpc_datetimes ):
83+ actions = np .array ([mpc_controller ['R2' ][controller_datetime ],
84+ mpc_controller ['P0' ][controller_datetime ],
85+ mpc_controller ['W0' ][controller_datetime ]])
86+ controller_datetime += datetime .timedelta (minutes = 15 )
87+ elif evaluating == "uncontrolled" :
88+ actions = np .array ([1.0 , 0.0 , 1.0 ]) # orifice and weir open, pump off.
89+ else :
90+ exit (1 )
91+ if (not done ) and verbose and env .env .sim .current_time .minute == 0 and env .env .sim .current_time .hour % 4 == 0 :
92+ u_print = actions .flatten ()
93+ y_measured = env .state (level = level ).reshape (- 1 ,1 )
94+ # print the names of the states and their current values side-by-side
95+ print ("state, value" )
96+ for idx in range (len (env .config ['states' ])):
97+ print (env .config ['states' ][idx ], y_measured [idx ])
98+ print ("actuator, setting" )
99+ for idx in range (len (env .config ['action_space' ])):
100+ print (env .config ['action_space' ][idx ], u_print [idx ])
101+
102+ print ("current time, end time" )
103+ print (env .env .sim .current_time , env .env .sim .end_time )
104+ print ("\n " )
105+
106+ if (not done ) and (env .env .sim .current_time > (last_read + datetime .timedelta (minutes = 1 ))) and plot : # log data
107+ last_read = env .env .sim .current_time
108+ state = env .state (level = "1" ).reshape (1 ,len (env .config ['states' ]))
109+ current_state = pd .DataFrame (data = state , columns = env .config ['states' ], index = [env .env .sim .current_time ] )
110+ states = pd .concat ((states ,current_state ))
111+ action = actions .reshape (1 ,len (env .config ['action_space' ]))
112+ current_actions = pd .DataFrame (data = action , columns = env .config ['action_space' ], index = [env .env .sim .current_time ])
113+ actions_log = pd .concat ((actions_log , current_actions ))
114+
115+
116+ if (not done ) and env .env .sim .current_time > env .env .sim .end_time - datetime .timedelta (hours = 1 ):
117+ final_depths = env .state (level = "1" )
118+
119+ #done = env.step(actions.flatten(),level=level)
120+ done = env .step (actions .flatten ())
121+ # note that noise won't affect the controller as it's predetermined
122+ # but actuator faults such as getting stuck will still affect the controller
123+ perf = sum (env .data_log ["performance_measure" ])
124+ print ("flooding" )
125+ for key ,value in env .data_log ['flooding' ].items ():
126+ # flooded nodes
127+ if sum (value ) > 0 :
128+ print (key , sum (value ))
129+
130+
131+ # save the cost and ending dpeths to a csv
132+ perf_summary = pd .DataFrame (data = {"cost" : perf , "final_depths" : final_depths })
133+ if evaluating == "uncontrolled" :
134+ perf_summary .to_csv (str (folder_path + "/costs_" + evaluating + ".csv" ))
135+ else :
136+ perf_summary .to_csv (str (folder_path + "/costs_" + evaluating + ".csv" ))
137+
138+ if plot :
139+
140+ if evaluating == "uncontrolled" :
141+ states .to_csv (str (folder_path + "/states_" + str (evaluating ) + ".csv" ))
142+ actions_log .to_csv (str (folder_path + "/actions_" + str (evaluating ) + ".csv" ))
143+ # and the data log
144+ with open (f'./v{ version } /results/{ evaluating } _data_log.pkl' , 'wb' ) as f :
145+ pickle .dump (env .data_log , f )
146+ else :
147+ # save the flows and depths
148+ states .to_csv (str (folder_path + "/states_" + str (evaluating ) + ".csv" ))
149+ actions_log .to_csv (str (folder_path + "/actions_" + str (evaluating ) + ".csv" ))
150+ # and the data log
151+ with open (f'./v{ version } /lev{ level } /results/{ str (evaluating )} _data_log.pkl' , 'wb' ) as f :
152+ pickle .dump (env .data_log , f )
153+
154+
155+ # if there are any na values in states or weir_heads32, linearly interpolate over them
156+ states .interpolate (method = 'time' ,axis = 'index' ,inplace = True )
157+ #weir_heads32.interpolate(method='time',axis='index',inplace=True)
158+ actions_log .interpolate (method = 'time' ,axis = 'index' ,inplace = True )
159+ #flows.interpolate(method='time',axis='index',inplace=True)
160+
161+ plots_high = max (len (env .config ['action_space' ]) , len (env .config ['states' ]))
162+ fig , axes = plt .subplots (plots_high , 2 , figsize = (10 ,2 * plots_high ))
163+
164+ axes [0 ,0 ].set_title ("actions" )
165+ axes [0 ,1 ].set_title ("states" )
166+ # plot the actions
167+ for idx in range (len (env .config ['action_space' ])):
168+ axes [idx ,0 ].plot (actions_log .iloc [:,idx ])
169+ axes [idx ,0 ].set_ylabel ("setting" )
170+ if idx == len (env .config ['action_space' ]) - 1 :
171+ axes [idx ,0 ].set_xlabel ("time" )
172+ # plot only the first, middle, and last x-ticks
173+ xticks = axes [idx ,0 ].get_xticks ()
174+ xticks = [xticks [0 ],xticks [int (len (xticks )/ 2 )],xticks [- 1 ]]
175+ axes [idx ,0 ].set_xticks (xticks )
176+ if idx != len (env .config ['action_space' ]) - 1 : # not the last row
177+ axes [idx ,0 ].set_xticklabels ([])
178+ axes [idx ,0 ].annotate (str (env .config ['action_space' ][idx ]), xy = (0.5 , 0.8 ), xycoords = 'axes fraction' , ha = 'center' , va = 'center' ,fontsize = 'xx-large' )
179+
180+
181+ # plot the states
182+ for idx in range (len (env .config ['states' ])):
183+ axes [idx ,1 ].plot (states .iloc [:,idx ])
184+
185+ if idx == len (env .config ['states' ]) - 1 :
186+ axes [idx ,1 ].set_xlabel ("time" )
187+ axes [idx ,1 ].annotate (str (env .config ['states' ][idx ]), xy = (0.5 , 0.4 ), xycoords = 'axes fraction' , ha = 'center' , va = 'center' ,fontsize = 'xx-large' )
188+ # plot only the first, middle, and last x-ticks
189+ xticks = axes [idx ,1 ].get_xticks ()
190+ xticks = [xticks [0 ],xticks [int (len (xticks )/ 2 )],xticks [- 1 ]]
191+ axes [idx ,1 ].set_xticks (xticks )
192+
193+ if idx != len (env .config ['states' ]) - 1 :
194+ axes [idx ,1 ].set_xticklabels ([])
195+ axes [idx ,1 ].annotate (str (env .config ['states' ][idx ]), xy = (0.5 , 0.8 ), xycoords = 'axes fraction' , ha = 'center' , va = 'center' ,fontsize = 'xx-large' )
196+
197+
198+ plt .tight_layout ()
199+ if evaluating == "uncontrolled" :
200+ plt .savefig (str (folder_path + "/evaluate_" + str (evaluating ) + ".png" ),dpi = 450 )
201+ plt .savefig (str (folder_path + "/evaluate_" + str (evaluating ) + ".svg" ),dpi = 450 )
202+ else :
203+ plt .savefig (str (folder_path + "/evaluate_" + str (evaluating ) + ".png" ),dpi = 450 )
204+ plt .savefig (str (folder_path + "/evaluate_" + str (evaluating ) + ".svg" ),dpi = 450 )
205+ #plt.show()
206+ plt .close ('all' )
0 commit comments