Combining EnergyPlus and EnergyHub Evaluators¶
This notebook covers different ways to use EnergyPlus and PyEHub Evaluators together.
import numpy as np
import pandas as pd
from besos import eppy_funcs as ef, pyehub_funcs as pf
from besos.evaluator import EvaluatorEH, EvaluatorEP
from besos.objectives import MeterReader
from besos.parameters import (
FieldSelector,
Parameter,
PathSelector,
RangeDescriptor,
)
from besos.problem import EHProblem, EPProblem
Custom EnergyPlus Evaluator Functions¶
First we need to define a way to get a whole time series from EnergyPlus
Evaluator, not just an objective function value. To extract a time
series from an EnergyPlus Evaluator the default summation function must
be replaced. The function timeseriesfunc
returns the entire Pandas
Series from the output of the EnergyPlus simulation.
def timeseriesfunc(result):
return result.data["Value"]
Create an EnergyPlus Evaluator¶
Here is a standard EnergyPlus Evaluator for editing the lighting power density for the default building and getting the electricity demand time series. The output is a Pandas Series, and the units are Joules.
building = ef.get_building()
EPparameters = [
Parameter(
FieldSelector("Lights", "*", "Watts per Zone Floor Area"),
value_descriptor=RangeDescriptor(8, 12),
name="Lights Watts/Area",
)
]
EPobjectives = MeterReader("Electricity:Facility", func=timeseriesfunc)
problem = EPProblem(EPparameters, EPobjectives)
evaluator = EvaluatorEP(problem, building)
result = evaluator([8])
result
/home/user/.local/lib/python3.7/site-packages/besos/parameters.py:448: FutureWarning: Use value_descriptors instead of value_descriptor.
FutureWarning("Use value_descriptors instead of value_descriptor.")
(0 5.041708e+07
1 5.142561e+07
2 5.193728e+07
3 5.111495e+07
4 4.728000e+07
5 4.371469e+07
6 5.956208e+07
7 5.870644e+07
8 6.031615e+07
9 6.127009e+07
10 6.202866e+07
11 6.294521e+07
12 6.369996e+07
13 6.431286e+07
14 6.507253e+07
15 6.522390e+07
16 6.471212e+07
17 6.365803e+07
18 6.167204e+07
19 6.648230e+07
20 6.682431e+07
21 6.601229e+07
22 4.320751e+07
23 4.910526e+07
24 1.431370e+07
25 1.310912e+07
26 1.431370e+07
27 1.310912e+07
28 1.431370e+07
29 1.310912e+07
30 1.551827e+07
31 8.609274e+06
32 7.227474e+06
33 7.227474e+06
34 7.227474e+06
35 7.227474e+06
36 7.227474e+06
37 7.227474e+06
38 7.227474e+06
39 7.227474e+06
40 9.991074e+06
41 1.551827e+07
42 1.407278e+07
43 1.310912e+07
44 1.431370e+07
45 1.310912e+07
46 1.431370e+07
47 1.310912e+07
Name: Value, dtype: float64,)
EnergyPlus Evaluator Output conversions¶
To ensure the output of the EnergyPlus evaluator is in the correct format for the Energy Hub, some conversions are required.
First the result is converted from a Pandas Series to a dataframe.
act_result = result[0].to_frame()
Splitting into days¶
Then because EnergyPlus simulated a summer design day and a winter design day, the output is split and their indexes reset.
cold_result = act_result.head(24)
cold_result = cold_result.reset_index()
warm_result = act_result.tail(24)
warm_result = warm_result.reset_index()
Unit Conversions¶
The output for an energy Output:Meter
in EnergyPlus is in Joules but
EnergyHub deals with kWh so the entire dataframe for both days is
converted. They are then turned into dictionaries with the keys being
the time series index.
cold_result = cold_result / 3600000
cold_dict = cold_result.to_dict()
cold_dict = cold_dict["Value"]
warm_result = warm_result / 3600000
warm_dict = warm_result.to_dict()
warm_dict = warm_dict["Value"]
Wrapping in Dictionaries¶
Lastly the dictionaries are wrapped as lists to match the input format for EnergyHub Evaluators.
cold_input = [cold_dict]
warm_input = [warm_dict]
Create a PyEHub Evaluator¶
Here is a standard PyEHub Evaluator for editing the electrical load of a simple energy hub, minimizing the total cost and outputting both the total cost and total carbon emissions from the optimizied hub. See EHEvaluator for more details. It is applied to the Energy Hub model specified in config
EHparameters = [Parameter(PathSelector(["LOADS", "Elec"]))]
EHobjectives = ["total_cost", "total_carbon"]
EHproblem = EHProblem(EHparameters, EHobjectives)
hub = pf.get_hub()
EHevaluator = EvaluatorEH(EHproblem, hub)
Single timeseries for PyEHub Evaluator¶
The wrapped dictionary inputs can be used directly as input for the PyEHub Evaluator.
result1 = EHevaluator(cold_input)
result1
(1856.41, 77.5222)
result2 = EHevaluator(warm_input)
result2
(1839.45, 31.8574)
Dataframe of time series¶
These inputs can be combined into a single dataframe and used as input for the evaluators.
seasons_df = pd.DataFrame(np.array([warm_input, cold_input]), columns=["p1"])
result3 = EHevaluator.df_apply(seasons_df)
result3
HBox(children=(FloatProgress(value=0.0, description='Executing', max=2.0, style=ProgressStyle(description_widt…
total_cost | total_carbon | |
---|---|---|
0 | 1839.45 | 31.8574 |
1 | 1856.41 | 77.5222 |
Energy Plus Dataframe input¶
If the input of the EnergyPlus Evaluator is a dataframe, then
df_apply
can be used to execute the evaluator.
EPdf = pd.DataFrame(np.array([[8], [9], [10], [12]]), columns=["p1"])
df_results = evaluator.df_apply(EPdf)
df_results
HBox(children=(FloatProgress(value=0.0, description='Executing', max=4.0, style=ProgressStyle(description_widt…
Electricity:Facility | |
---|---|
0 | 0 5.041708e+07 1 5.142561e+07 2 5.... |
1 | 0 5.377034e+07 1 5.481425e+07 2 5.... |
2 | 0 5.577863e+07 1 5.670179e+07 2 5.... |
3 | 0 6.187128e+07 1 5.976898e+07 2 6.... |
EnergyPlus Dataframe output conversion¶
The conversions must be done while maintaining a dataframe to be used
with df_apply
for the PyEHub Evaluator. The previous splitting of
days, unit conversions, and wrapping dictionaries must be done for every
output of the EnergyPlus Dataframe, then appended into a larger
dataframe to be used as the input to the Energy Hub. The larger
dataframe is prepared with the number of objectives from the EnergyPlus
simulator.
results_dicts = df_results.to_dict()
columnnames = []
for j in results_dicts:
columnnames.append(j)
df_input = pd.DataFrame(columns=columnnames)
for j in results_dicts:
for i in results_dicts[j]:
act_result = results_dicts[j][i].to_frame()
cold_result = act_result.head(24)
cold_result = cold_result.reset_index()
warm_result = act_result.tail(24)
warm_result = warm_result.reset_index()
cold_result = cold_result / 3600000
cold_dict = cold_result.to_dict()
cold_dict = cold_dict["Value"]
warm_result = warm_result / 3600000
warm_dict = warm_result.to_dict()
warm_dict = warm_dict["Value"]
temp_df1 = pd.DataFrame(np.array(cold_input), columns=[j])
temp_df2 = pd.DataFrame(np.array(warm_input), columns=[j])
df_input = df_input.append(temp_df1, ignore_index=True)
df_input = df_input.append(temp_df2, ignore_index=True)
df_input
Electricity:Facility | |
---|---|
0 | {0: 14.004744327135537, 1: 14.284890432124438,... |
1 | {0: 3.97602646966534, 1: 3.6414211757330794, 2... |
2 | {0: 14.004744327135537, 1: 14.284890432124438,... |
3 | {0: 3.97602646966534, 1: 3.6414211757330794, 2... |
4 | {0: 14.004744327135537, 1: 14.284890432124438,... |
5 | {0: 3.97602646966534, 1: 3.6414211757330794, 2... |
6 | {0: 14.004744327135537, 1: 14.284890432124438,... |
7 | {0: 3.97602646966534, 1: 3.6414211757330794, 2... |
EnergyPlus to EnergyHub Dataframe output¶
The converted output from the EnergyPlus Evaluator can then be used as input for the PyEHub evaluator.
result4 = EHevaluator.df_apply(df_input)
result4
HBox(children=(FloatProgress(value=0.0, description='Executing', max=8.0, style=ProgressStyle(description_widt…
total_cost | total_carbon | |
---|---|---|
0 | 1856.41 | 77.5222 |
1 | 1839.45 | 31.8574 |
2 | 1856.41 | 77.5222 |
3 | 1839.45 | 31.8574 |
4 | 1856.41 | 77.5222 |
5 | 1839.45 | 31.8574 |
6 | 1856.41 | 77.5222 |
7 | 1839.45 | 31.8574 |