Parametric simulations¶
This example shows how to do a sensitivity study using opyplus.
Prepare imports and paths¶
import os
import opyplus as op
eplus_dir_path = op.get_eplus_base_dir_path((9, 0, 1))
Define input files¶
# idf (building model)
base_idf_path = os.path.join(
eplus_dir_path,
"ExampleFiles",
"RefBldgFullServiceRestaurantNew2004_Chicago.idf"
)
# epw (weather)
epw_path = os.path.join(
eplus_dir_path,
"WeatherData",
"USA_CO_Golden-NREL.724666_TMY3.epw"
)
Define the study plan¶
Variables and values¶
sensitivity_plan = {
"light" : [0.5, 1.5], # modify Lights value
"electric_equipment" : [0.5, 1.5], # modify ElectricEquipment level
"infiltration" : [0.5, 1.5], # modify ZoneInfiltration_DesignFlowRate level
"heating_setpoint" : [ -1, 1], # modify schedule's upper value
"cooling_setpoint" : [ -1, 1], # modify schedule's lower value
}
Define the function that will modify the model¶
def modify_model(epm, parameter, value):
if parameter == "light":
# modify light level
for light in epm.Lights:
light.watts_per_zone_floor_area *= value
return
if parameter == "electric_equipment":
# modify electric level
for ee in epm.ElectricEquipment:
if ee.design_level_calculation_method == "equipmentlevel":
ee.design_level *= value
elif ee.design_level_calculation_method == "watts/area":
ee.watts_per_zone_floor_area *= value
return
if parameter == "infiltration":
# modify infiltration flow
for inf in epm.ZoneInfiltration_DesignFlowRate:
if inf.design_flow_rate_calculation_method == "flow/exteriorarea":
inf.flow_per_exterior_surface_area *= value
elif inf.design_flow_rate_calculation_method == "airchanges/hour":
inf.air_changes_per_hour *= value
return
if parameter == "heating_setpoint":
# create a list with all heating setpoint temperature schedules
# found in ThermostatSetpoint_DualSetpoint objects
heating_setpoint_temperature_list = []
# loop on ThermostatSetpoint_DualSetpoint object
for th_ds in epm.ThermostatSetpoint_DualSetpoint:
# add heating_setpoint_temperature_schedule_name to list
heating_setpoint_temperature_list.append(
th_ds.heating_setpoint_temperature_schedule_name)
# loop on each heating_setpoint_temperature_schedule and replace value
for heating_setpoint_sch in sorted(heating_setpoint_temperature_list):
# get dict of the schedule { 0: schedule'name, 1: schedule type limits name,
# 2: extensible field 1 value, ... }
schedule_dict = heating_setpoint_sch.to_dict()
# get the index of max value
# (we transform string to float if the value is a string and a can is a digit
# not a text)
first_index = max(
schedule_dict,
key=lambda x: float(schedule_dict[x])
if isinstance(schedule_dict[x], str) and schedule_dict[x].isdigit()
else 0
)
# loop on all dict items and modify value if its the max value
for k, v in schedule_dict.items():
if v == schedule_dict[first_index]: # it is the max value ?
# update value and set it in string format
heating_setpoint_sch[k] = str(float(schedule_dict[k]) + value)
return
if parameter == "cooling_setpoint":
# create a list with all cooling setpoint temperature schedules found
# in ThermostatSetpoint_DualSetpoint objects
cooling_setpoint_temperature_list = []
# loop on ThermostatSetpoint_DualSetpoint object
for th_ds in epm.ThermostatSetpoint_DualSetpoint:
# add cooling_setpoint_temperature_schedule_name to list
cooling_setpoint_temperature_list.append(
th_ds.cooling_setpoint_temperature_schedule_name)
# loop on each cooling_setpoint_temperature_schedule and replace value
for cooling_setpoint_sch in set(cooling_setpoint_temperature_list):
# get dict of the schedule { 0: schedule'name,
# 1: schedule type limits name, 2: extensible field 1 value, ... }
schedule_dict = cooling_setpoint_sch.to_dict()
# get the index of max value
# (we transform string to float if the value is a string and a can
# is a digit not a text)
first_index = min(
schedule_dict,
key=lambda x: float(schedule_dict[x])
if isinstance(schedule_dict[x], str) and schedule_dict[x].isdigit()
else 100
)
# loop on all dict items and modify value if its the min value
for k, v in schedule_dict.items():
if v == schedule_dict[first_index]: # it is the min value ?
# update value and set it in string format
cooling_setpoint_sch[k] = str(float(schedule_dict[k]) + value)
return
raise ValueError(f"unknown parameter: {parameter}")
Create the function that will run the simulation and prepare outputs¶
In this example we will add specific output variables to the model, simulate and return consumptions.
def simulate_and_get_result(epm, epw_path, simulation_name):
# add output variables
epm.output_variable.add({
0: "*",
1: "Zone Air Terminal Sensible Heating Energy",
2: "hourly"
})
epm.output_variable.add({
0: "*",
1: "Zone Air Terminal Sensible Cooling Energy",
2: "hourly"
})
# simulate
s = op.simulate(epm, epw_path, simulation_name)
# get results
eso = s.get_out_eso()
eso.create_datetime_index(2020)
hourly_df = eso.get_data()
# filter and aggregate outputs
regex_sensible = "Zone Air Terminal Sensible"
regex_electric = "electricity:facility"
regex_gas = "gas:facility"
sensible = hourly_df.filter(regex=regex_sensible).sum(axis=1).sum()
electric = hourly_df.filter(regex=regex_electric).sum(axis=1).sum()
gas = hourly_df.filter(regex=regex_gas).sum(axis=1).sum()
# return baselines
return sensible, electric+gas
Run the study¶
# calculate baseline
epm = op.Epm.load(base_idf_path)
sensible_need_baseline, total_consumption_baseline = simulate_and_get_result(
epm,
epw_path,
"baseline"
)
# run sensitivity study
results = dict() # {simulation_name: {"total": , "sensible": }
for parameter, values in sensitivity_plan.items():
for value in values:
# reload initial model
epm = op.Epm.load(base_idf_path)
# prepare simulation name
simulation_name = f"{parameter}={str(value)}"
# modify model
modify_model(epm, parameter, value)
# simulate and calculate outputs
sensible_need, total_consumption = simulate_and_get_result(
epm,
epw_path,
simulation_name
)
# store results
results[simulation_name] = dict(
total=(total_consumption-total_consumption_baseline)/total_consumption_baseline,
sensible=(sensible_need-sensible_need_baseline)/sensible_need_baseline
)
Visualize the results¶
We use the plotly package to visualize the results.
Imports¶
import plotly.graph_objs as go
import pandas as pd
Plot¶
df = pd.DataFrame().from_dict(results).T
df.sort_values(by=["sensible"], inplace=True)
title = """'Zone Air Terminal Sensible Energy' relative difference
(compared to baseline)"""
sensible_fig = go.Figure(
data=[go.Bar(x=df.index, y=df["sensible"])],
layout=go.Layout(
title=title,
font=dict(size=11)
)
)
sensible_fig.show()
This building model is more sensible to heating setpoint than to other parameters.
df.sort_values(by=["total"], inplace=True)
cross_dependency_fig = go.Figure(
data=[go.Scatter(
x=df["sensible"],
y=df["total"],
mode="markers",
text=df.index
)],
layout=go.Layout(
title="Dependency correlation between consumption and sensible need",
font=dict(size=11),
xaxis=dict(title="relative sensibility on air sensible energy"),
yaxis=dict(title="relative sensibility on total consumption facility")
)
)
cross_dependency_fig.show()
We notice a good sensibility symmetry and a factor 3 between sensibility need and consumption.