Skip to content

Commit

Permalink
Merge pull request #3 from mechyai/get_ems_vals_dict_feature
Browse files Browse the repository at this point in the history
Get ems vals dict feature
  • Loading branch information
mechyai authored Mar 14, 2022
2 parents e564f52 a795420 commit a4c2d5e
Show file tree
Hide file tree
Showing 8 changed files with 300 additions and 170 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 8,7 @@

*.pyc

*/out
*/out

/docs
/simple_office_5zone_building_model
19 changes: 12 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 162,8 @@ idf_file_name = r'BEM_simple/simple_office_5zone_April.idf' # building energy m
# Weather Path
ep_weather_path = r'BEM_simple/5B_USA_CO_BOULDER_TMY2.epw' # EPW weather file
# Output .csv Path (optional)
cvs_output_path = r'dataframes_output_test.csv'
# STATE SPACE (& Auxiliary Simulation Data)
Expand Down Expand Up @@ -220,6 222,7 @@ sim = BcaEnv(
tc_weather=tc_weather
)
class Agent:
"""
Create agent instance, which is used to create actuation() and observation() functions (both optional) and maintain
Expand All @@ -241,20 244,23 @@ class Agent:
# Get data from simulation at current timestep (and calling point) using ToC names
var_data = self.bca.get_ems_data(list(self.bca.tc_var.keys()))
meter_data = self.bca.get_ems_data(list(self.bca.tc_meter.keys()))
weather_data = self.bca.get_ems_data(list(self.bca.tc_weather.keys()))
meter_data = self.bca.get_ems_data(list(self.bca.tc_meter.keys()), return_dict=True)
weather_data = self.bca.get_ems_data(list(self.bca.tc_weather.keys()), return_dict=True)
# get specific values from MdpManager based on name
self.zn0_temp = var_data[1] # index 1st element to get zone temps, based on EMS Variable ToC
# OR
self.zn0_temp = self.bca.get_ems_data(['zn0_temp'])
# OR if using "return_dict=True"
outdoor_temp = weather_data['oa_db'] # outdoor air dry bulb temp
# print reporting
if self.time.hour % 2 == 0 and self.time.minute == 0: # report every 2 hours
print(f'\n\nTime: {str(self.time)}')
print('\n\t* Observation Function:')
print(f'\t\tVars: {var_data}\n\t\tMeters: {meter_data}\n\t\tWeather:{weather_data}')
print(f'\t\tVars: {var_data}' # outputs ordered list
f'\n\t\tMeters: {meter_data}' # outputs dictionary
f'\n\t\tWeather:{weather_data}') # outputs dictionary
print(f'\t\tZone0 Temp: {round(self.zn0_temp,2)} C')
print(f'\t\tOutdoor Temp: {round(outdoor_temp, 2)} C')
def actuation_function(self):
work_hours_heating_setpoint = 18 # deg C
Expand Down Expand Up @@ -311,7 317,7 @@ sim.run_env(ep_weather_path)
sim.reset_state() # reset when done
# -- Sample Output Data --
output_dfs = sim.get_df() # LOOK at all the data collected here, custom DFs can be made too
output_dfs = sim.get_df(to_csv_file=cvs_output_path) # LOOK at all the data collected here, custom DFs can be made too
# -- Plot Results --
fig, ax = plt.subplots()
Expand All @@ -323,7 329,6 @@ output_dfs['actuator'].plot(y='zn0_cooling_sp', use_index=True, ax=ax)
plt.title('Zn0 Temps and Thermostat Setpoint for Year')
# Analyze results in "out" folder, DView, or directly from your Python variables and Pandas Dataframes
```
<ins>5 Zone Office Building Model</ins>

Expand Down
47 changes: 31 additions & 16 deletions emspy/bca.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 76,7 @@ def _check_ems_metric_input(self, ems_metric):
f' or emspy.ems_master_list & emspy.times_master_list for available EMS & '
f'timing metrics')

def get_ems_data(self, ems_metric_list: list, time_reverse_index=0) -> list:
def get_ems_data(self, ems_metric_list: list, time_reverse_index=0, return_dict: bool = False):
"""
This takes desired EMS metric(s) (or type) & returns the entire current data set(s) OR at specific time indices.
Expand All @@ -93,6 93,8 @@ def get_ems_data(self, ems_metric_list: list, time_reverse_index=0) -> list:
:param time_reverse_index: list (or single value) of timestep indexes, applied to all EMS/timing metrics starting
from index 0 as most recent available data point. Passing an empty list [] will return the entire current data
list for each specified EMS metric.
:param return_dict: True if you want to return data in dictionary format, with EMS name as the key, otherwise
raw list will be returned in order of EMS items in the input list or EMS ToC
:return return_data_list: nested list of data for each EMS metric at each time index specified, or entire list
"""

Expand All @@ -110,9 112,7 @@ def get_ems_data(self, ems_metric_list: list, time_reverse_index=0) -> list:
if len(time_reverse_index) == 1:
single_val = True

return_data_list = []

# check if only an EMS category called
# Check if only an EMS category called
ems_type = ems_metric_list[0]
if ems_type in self.ems_num_dict:
if single_metric:
Expand All @@ -128,40 128,55 @@ def get_ems_data(self, ems_metric_list: list, time_reverse_index=0) -> list:
f'list "var", "intvar", "meter", "weather", or "actuator. Your input was '
f'{ems_metric_list}')

if return_dict:
return_data = {} # organized by key EMS name
else:
return_data = [] # organized by order, only raw values

for ems_metric in ems_metric_list:
# verify valid input

# TODO do once, again at each timestep is redundant?
self._check_ems_metric_input(ems_metric)
self._check_ems_metric_input(ems_metric) # verify valid input
ems_type = self._get_ems_type(ems_metric) # for attribute variable name

if not time_reverse_index:
# no time index specified, return ALL current data available
return_data_list.append(getattr(self, 'data_' ems_type '_' ems_metric))
# no time index specified, return ALL current data available, #TODO this doens't compensate for time data
if return_dict:
return_data[ems_metric] = getattr(self, 'data_' ems_type '_' ems_metric)
else:
return_data.append(getattr(self, 'data_' ems_type '_' ems_metric))
else:
return_data_indexed = []
# iterate through previous time indexes
return_data_indexed = []
for time in time_reverse_index:
if ems_type != 'time':
ems_name = 'data_' ems_type '_' ems_metric
else:
ems_name = ems_metric
ems_name = ems_metric # TODO update timing metrics
try:
data_indexed = getattr(self, ems_name)[-1 - time]
# so that a single-element nested list is not returned
if single_val:
return_data_indexed = data_indexed
return_data_indexed = data_indexed # so that nested list of single-element is Not returned
else:
return_data_indexed.append(data_indexed)
except IndexError:
print('\n*NOTE: Not enough simulation time elapsed to collect data at specified index.\n')
# no unnecessarily nested lists
# TODO add feature that will add what data is available, IFF helpful

# No unnecessarily nested lists
if single_metric:
return return_data_indexed
# handle dictionary form factor if needed
if return_dict:
return {ems_metric: return_data_indexed}
else:
return return_data_indexed
else:
return_data_list.append(return_data_indexed)
if return_dict:
return_data[ems_metric] = return_data_indexed
else:
return_data.append(return_data_indexed)

return return_data_list
return return_data

def get_weather_forecast(self, weather_metrics: list, when: str, hour: int, zone_ts: int):
"""
Expand Down
4 changes: 2 additions & 2 deletions emspy/emspy.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,8 289,8 @@ def _get_handle(self, ems_type: str, ems_obj_details):
ems_obj_details[2]) # actuator key
# catch error handling by EMS E
if handle == -1:
raise Exception(f'ERROR: [{str(ems_obj_details)}]: The EMS sensor/actuator handle could not be'
' found. Please consult the .idf and/or your ToC for accuracy')
raise Exception(f'ERROR: [{str(ems_obj_details)}]: The EMS sensor/actuator handle could not be '
'found. Please consult the .idf and/or your ToC for accuracy')
else:
return handle
except IndexError:
Expand Down
7 changes: 4 additions & 3 deletions emspy/idf_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 79,15 @@ def insert_custom_data_tracking(custom_name: str, idf_file_path: str, unit_type:

output_var_obj = ['Output:Variable,',
custom_name,
'\tSchedule Value',
'\tTimestep;']
'\tSchedule Value,',
'\tTimestep;',
'! ----------------------------------------------------------------------']

# create, write, then delete temporary file, used for appending
temp_file = '_temp_write'
with open(temp_file, 'w') as idf:
idf.writelines('\n')
idf.writelines('!- Custom Schedule Tracking (next 3 objects)')
idf.writelines(f'!----------- Custom Schedule Tracking ({custom_name[:-1]}) -----------')
idf.writelines('\n')
idf.writelines('\n'.join(schedule_type_limit_obj))
idf.writelines('\n\n')
Expand Down
Loading

0 comments on commit a4c2d5e

Please sign in to comment.