-
Notifications
You must be signed in to change notification settings - Fork 10
script_tutorial
BuildSim Cloud is one of the quickest methods to unlock the potential of the energy modelings with public or commercial design software. In this example, we will start with an energy model from DesignBuilder and then move to BuildSim for parametric analysis.
Before the tutorial, make sure you have the latest BuildSimHub python API downloaded.
pip install BuildSimHubAPI
We also recommend to have pandas
package installed as some of the JSON requests can be easily converted into pandas dataframe, which are much nicer to view and easier to access.
Ok, without further due, let's begin!
DesignBuilder (DB) is a whole building energy modeling software that is capable of performing energy, lighting and CFD simulations with one geometry. With its powerful geometry and HVAC editors, creating a full mass model takes less than 10 minutes and here is what I built with DB:
This is a two-story courtyard shape office building with 5 zone layout. I also build one VAV with terminal reheat system per floor in the model.
We can safely export this model to EnergyPlus using DB's export function.
The EnergyPlus exported from DesignBuilder v5.x is still 8.6. However, it is recommended to upgrade the version to 8.7 as the majority of default data templates on BuildSim Cloud are stored as 8.7 and above. You can find the IDFVersionUpgrade in the downloaded EnergyPlus package.
Once upgraded, we can then upload this model to the platform - simply drop it to the page.
Once the project is created, you can then find the API keys to the project. Copy the project api key and model api key for the scriptings.
Now we have both keys, we can start the initial setup for script!
import BuildSimHubAPI as bshapi
project_api_key = "1a920abf-ca01-4ead-9dde-dce1c629cd2c"
model_api_key = "d8aa9a0c-2529-4c6b-b9d2-949e0ebddf71"
bsh = bshapi.BuildSimHubAPIClient()
Remember to keep these four lines of script on top of all the script below. Now All set! Let's run our first simulation.
newsj = bsh.new_simulation_job(project_api_key)
model = newsj.run_model_simulation(track_token=model_api_key, track=True, unit='si')
print(str(model.net_site_eui()) + " " + model.last_parameter_unit)
'''
Submitting simulation request...
Received server response
Looking for available simulation engine... 1%
Initializing Simulation... 17%
February... 28%
March... 33%
May... 44%
July... 56%
August... 61%
October... 72%
December... 83%
Writing Simulation Results... 89%
Collecting Simulation Results... 94%
Simulation finished successfully
Completed! You can retrieve results using the key: 513-7702-783981
344.77 kWh/m2
'''
After a successful simulation, let's take a look at the model.
model = bsh.model_results(project_api_key, model_api_key)
model.bldg_geo()
This immediately opens the 3D viewer in a broswer. We can view the geometry of the model: and we can also view the HVAC systems that connects to the geometry:
There are a number of building level data that we can extract from the model using python API library
model = bsh.model_results(project_api_key, model_api_key)
print("EUI: " + str(model.net_site_eui()) + " " + model.last_parameter_unit)
print("Orientation: " + str(model.bldg_orientation()))
print("Floors above ground: " + str(model.num_above_ground_floor()))
print("Total floors: " + str(model.num_total_floor()))
print("Number of zones: " + str(model.num_zones()))
print("Number of conditioned zones: " + str(model.num_condition_zones()))
print("Conditioned floor area: " + str(model.condition_floor_area(unit='si')) + " " + model.last_parameter_unit)
print("Gross floor area: " + str(model.gross_floor_area(unit='si')) + " " + model.last_parameter_unit)
print("Gross Window wall ratio: " + str(model.window_wall_ratio()))
print("Cooling unmet hours: " + str(model.not_met_hour_cooling()) + " " + model.last_parameter_unit)
print("Heating unmet hours: " + str(model.not_met_hour_heating()) + " " + model.last_parameter_unit)
print("Electricity: " + str(model.total_end_use_electricity()) + " " + model.last_parameter_unit)
print("Natural Gas: " + str(model.total_end_use_naturalgas()) + " " + model.last_parameter_unit)
print("Cooling Electricity: " + str(model.cooling_electricity()) + " " + model.last_parameter_unit)
print("Fan Electricity: " + str(model.fan_electricity()) + " " + model.last_parameter_unit)
print("Heating Natural Gas: " + str(model.heating_naturalgas()) + " " + model.last_parameter_unit)
print("Interior Equipment Electricity: " + str(model.interior_equipment_electricity()) + " " + model.last_parameter_unit)
print("Interior Lighting Electricity: " + str(model.interior_lighting_electricity()) + " " + model.last_parameter_unit)
print("Pump Electricity: " + str(model.pumps_electricity()) + " " + model.last_parameter_unit)
print("Bldg LPD: " + str(model.bldg_lpd()) + " " + model.last_parameter_unit)
print("Bldg EPD: " + str(model.bldg_epd()) + " " + model.last_parameter_unit)
print("Wall R value: " + str(model.wall_rvalue()) + " " + model.last_parameter_unit)
print("Roof R value: " + str(model.roof_rvalue()) + " " + model.last_parameter_unit)
print("Window U value: " + str(model.window_uvalue()) + " " + model.last_parameter_unit)
print("Window SHGC: " + str(model.window_shgc()))
print("Roof solar absorption: " + str(model.roof_absorption()))
print("Bldg infiltration: " + str(model.bldg_infiltration()) + " " + model.last_parameter_unit)
print("Cooling DX coil COP: " + str(model.bldg_dx_cooling_efficiency()))
print("Electric boiler efficiency: " + str(model.bldg_electric_boiler_efficiency()))
print("Fuel boiler efficiency: " + str(model.bldg_fuel_boiler_efficiency()))
print("Heating DX coil COP: " + str(model.bldg_dx_heating_efficiency()))
'''
EUI: 344.77 kWh/m2
Orientation: 0.0
Floors above ground: 2
Total floors: 2
Number of zones: 8.0
Number of conditioned zones: 8.0
Conditioned floor area: 575.957888 m2
Gross floor area: 575.957888 m2
Gross Window wall ratio: 27.380142745039414
Cooling unmet hours: 2.83 Hours
Heating unmet hours: 0.0 Hours
Electricity: 156467.79 kWh
Natural Gas: 42103.67 kWh
Cooling Electricity: 72525.23 kWh
Fan Electricity: 31987.92 kWh
Heating Natural Gas: 42103.67 kWh
Interior Equipment Electricity: 24880.34 kWh
Interior Lighting Electricity: 26954.82 kWh
Pump Electricity: 109.67 kWh
Bldg LPD: 14.999999999999996 W/m2
Bldg EPD: 11.77 W/m2
Wall R value: 3.8022813688212924
Roof R value: 2.7472527472527473
Window U value: 1.96 kWh
Window SHGC: 0.691
Roof solar absorption: 0.15000000000000002
Bldg infiltration: 0.052 ACH
Cooling DX coil COP: -1
Electric boiler efficiency: -1
Fuel boiler efficiency: 0.89
Heating DX coil COP: -1
'''
It seems the LPD of the building is at the high end with 15 W/m2. Let's firstly analyze how the LPD assigned to different zones. To view zone infomations, we can use a little help from pandas to organize data
import pandas as pd
model = bsh.model_results(project_api_key, model_api_key)
zone_df = pd.DataFrame(model.zone_list())
print(zone_df.to_string())
'''
This will give us a nicely printed pandas data frame:
conditioned floor zone_cool zone_exhaust zone_heat zone_name zone_vent
0 Yes 1 Floor1AirLoop Floor1AirLoop Floor1:South Floor1AirLoop
1 Yes 1 Floor1AirLoop Floor1AirLoop Floor1:North Floor1AirLoop
2 Yes 1 Floor1AirLoop Floor1AirLoop Floor1:West Floor1AirLoop
3 Yes 1 Floor1AirLoop Floor1AirLoop Floor1:East Floor1AirLoop
4 Yes 2 Floor2AirLoop Floor2AirLoop Floor2:West Floor2AirLoop
5 Yes 2 Floor2AirLoop Floor2AirLoop Floor2:South Floor2AirLoop
6 Yes 2 Floor2AirLoop Floor2AirLoop Floor2:North Floor2AirLoop
7 Yes 2 Floor2AirLoop Floor2AirLoop Floor2:East Floor2AirLoop
'''
With the zone list, we can then investigate the LPD assigned to each zone
model = bsh.model_results(project_api_key, model_api_key)
zone_df = pd.DataFrame(model.zone_list())
# we will iterate through rows, get the conditioned zone name
for idx, row in zone_df.iterrows():
if row['conditioned'] == 'Yes':
zone_name = row['zone_name']
info = model.zone_info(zone_name)
#print(info)
print(zone_name + ": " + str(float(info['zone_lpd']) / float(info['zone_area'])))
'''
Floor1:South: 15.0
Floor1:North: 14.999999999999998
Floor1:West: 15.0
Floor1:East: 15.0
Floor2:West: 15.0
Floor2:South: 15.0
Floor2:North: 14.999999999999998
Floor2:East: 15.0
'''
It seems all the zones have 15W/m2 lighting power density. Let's install LPDs on the first floor first:
model = bsh.model_results(project_api_key, model_api_key)
# apply lighting power density
# lsit all the zones we want to update
zone_list = ['Floor1:South', 'Floor1:North', 'Floor1:West', 'Floor1:East']
# sets the lights template for the zones
import BuildSimHubAPI.healpers as hp
light = hp.EnergyPlusObject("Lights")
light.add_field_template('Design Level Calculation Method','Watts/Area']
light.add_field_template('Watts per Zone Floor Area','6.5']
# apply design
updated_model_api = model.add_modify_zone(zone_list, [light])
# get updated model
updated_model = bsh.model_results(project_api_key, updated_model_api)
# compare the differences
bsh.compare_models(model, updated_model)
The last line of the script opens the comparison of the two models in your browser: Clearly, we can see the lights in floor 1 zones are all updated according to the template, and all the zone lights on second floor are remain the same. Let's add daylight sensor to all the zones to reduce the LPD further!
model = bsh.model_results(project_api_key, model_api_key)
# define design option: daylight sensors
daylit_sensor = bshapi.measures.DaylightingSensor()
daylit_sensor.set_data(1)
updated_model_api = model.apply_measures([daylit_sensor])
updated_model = bsh.model_results(project_api_key, updated_model_api)
# compare the differences
bsh.compare_models(model, updated_model)
By comparing these two models, we see the daylight sensors are successfully added to the model! Let's now run the simulation again and compare the results.
# let reuse the run simulation code and LPD code
newsj = bsh.new_simulation_job(project_api_key)
model = newsj.run_model_simulation(track_token=model_api_key, track=True, unit='si')
print(str(model.net_site_eui()) + " " + model.last_parameter_unit)
zone_df = pd.DataFrame(model.zone_list())
# we will iterate through rows, get the conditioned zone name
for idx, row in zone_df.iterrows():
if row['conditioned'] == 'Yes':
zone_name = row['zone_name']
info = model.zone_info(zone_name)
#print(info)
print(zone_name + ": " + str(float(info['zone_lpd']) / float(info['zone_area'])))
'''
......
Completed! You can retrieve results using the key: 513-7702-783983
282.46 kWh/m2
Floor1:South: 6.5
Floor1:North: 6.5
Floor1:West: 6.500000000000001
Floor1:East: 6.5
Floor2:West: 15.0
Floor2:South: 15.0
Floor2:North: 14.999999999999998
Floor2:East: 15.0
'''
Yes, we successfully reduce the energy consumption from 344.77 kWh/m2 to 282.46 kWh/m2, that is a huge jump! Next, how about reducing the LPD of the second floor zones programmatically?
model = bsh.model_results(project_api_key, model_api_key)
lit_df = pd.DataFrame(model.get_class('Lights'))
print(lit_df.to_string())
for idx, row in lit_df.iterrows():
# if there is lights object that has higher than 10 W/m2, we will then update it.
if float(row['Watts per Person Watts per Zone Floor Area']) > 10:
updated_model_api = model.parameter_batch_modification('Lights', field_label='Watts per Zone Floor Area', value=6.5, class_name=row['Name'])
model = bsh.model_results(project_api_key, updated_model_api)
# re-read the lights classes
lit_df = pd.DataFrame(model.get_class('Lights'))
print(lit_df['Watts per Zone Floor Area'].to_string())
'''
0 6.5
1 6.5
2 6.5
3 6.5
4 6.5
5 6.5
6 6.5
7 6.5
'''
We have successfully updated all the lights class in this model programmatically!
The end sue comparison has shown that HVAC consumed a lot of energy. So how about switching to another HVAC system? Currently, there are 14 HVAC system models in the HVAC design options. We can also know the information programmatically.
hvac = bshapi.measures.HVACTemplate()
print(hvac.measure_help())
'''
measure name: HVAC
Unit: not required
Minimum: 0
Maximum: 13
Type: categorical
This measure will change the HVAC system in the idf file
The HVAC system types are:
0. sys1: PTAC
1. sys2: PTHP
2. sys3: PSZ-AC
3. sys4: PSZ-HP
4. sys5: Packaged VAV with Reheat
5. sys6: Packaged VAV with PFP Boxes
6. sys7: VAV with Reheat
7. sys8: VAV with PFP Boxes
8. sys9: Warm air furnace, gas fired
9. sys10: Warm air furnace, electric
10. doasvrf: DOAS with variable refrigerant flow
11. doasfancoil: DOAS with Fan coils
12. doaswshp: DOAS with water source heat pump (ground as condenser)
13. doascbcb: DOAS with active cool beam + convective baseboard
'''
So how about lets switch system to DOAS + VRF?
model = bsh.model_results(project_api_key, model_api_key)
# set HVAC to DOAS + VRF
hvac = bshapi.measures.HVACTemplate()
hvac.set_data(10)
updated_model_api = model.apply_measures([hvac])
# Run a new simulation
newsj = bsh.new_simulation_job(project_api_key)
model = newsj.run_model_simulation(track_token=updated_model_api, track=True, unit='si')
print(str(model.net_site_eui()) + " " + model.last_parameter_unit)
'''
Applying measure to model: 513-7702-783987
HVAC: doasvrf
Submitting simulation request...
Received server response
Looking for available simulation engine... 1%
Initializing model... 6%
January... 22%
February... 28%
March... 33%
April... 39%
June... 50%
June... 50%
August... 61%
September... 67%
October... 72%
November... 78%
Writing Simulation Results... 89%
Collecting Simulation Results... 94%
Simulation finished successfully
Completed! You can retrieve results using the key: 513-7702-783988
158.86 kWh/m2
'''
Let's also view how the VRF systems are plotted?
model.bldg_geo()
With the help of DOAS + VRF, we successfully reduce the energy consumption from 344.77 kWh/m2 to 158.86 kWh/m2!
The BuildSim Dash can quickly plot the improvement of the model along this design. The plot implies that the DOAS + VRF system can reduce the cooling and heating energy consumption significantly.
We learned how to improve the model design by programmatically change the parameters or systems in a model based on our experiences. But what if we don't know what is good for improving energy efficiency? Typically, this can be addressed by performing a parametric study.
So can we do it through script? How fast we can develop such a script? Let's take a look!
First, lets find the initial model and make a copy of this model.
project_model_list = pd.DataFrame(bsh.project_model_list(project_api_key))
print(project_model_list.to_string())
# We can see now we have only one model branch under this project
'''
api_key branch_description branch_id branch_name branch_type last_modified_time
0 d8aa9a0c-2529-4c6b-b9d2-949e0ebddf71 INIT 7702 demoProject.idf idf 2018-10-23 17:51:24
'''
# Request this one model branch
model_list = pd.DataFrame(bsh.model_list(project_api_key, "d8aa9a0c-2529-4c6b-b9d2-949e0ebddf71"))
print(model_list.to_string())
'''
commit_date commit_id commit_msg
0 2018-10-23 513-7702-783988 HVAC: doasvrf
1 2018-10-23 513-7702-783987 Lights : Watts per Zone Floor Area change to: 6.5
2 2018-10-23 513-7702-783986 Lights : Watts per Zone Floor Area change to: 6.5
3 2018-10-23 513-7702-783985 Lights : Watts per Zone Floor Area change to: 6.5
4 2018-10-23 513-7702-783984 Lights : Watts per Zone Floor Area change to: 6.5
5 2018-10-23 513-7702-783983 DaylitSensor: 1.0
6 2018-10-23 513-7702-783982 Lights
7 2018-10-23 513-7702-783981 INIT
'''
The INIT stands for the initial model that we submitted, and all the others are the modifications we made through the previous practices. Let's take the commit_id of the INIT model and make a copy
init_model_id = "513-7702-783981"
model = bsh.model_results(project_api_key, init_model_id)
bsh.copy_model(model)
project_model_list = pd.DataFrame(bsh.project_model_list(project_api_key))
print(project_model_list.to_string())
'''
api_key branch_description branch_id branch_name branch_type last_modified_time
0 57eb67a4-5c1e-4fbd-8c9c-74ee5b48544a INIT 7703 demoProject.idf Copied idf 2018-10-23 20:57:55
1 d8aa9a0c-2529-4c6b-b9d2-949e0ebddf71 INIT 7702 demoProject.idf idf 2018-10-23 17:51:24
'''
Immediately, we saw two model branches are created. So now, lets work on the newly created model branch - 57eb67a4-5c1e-4fbd-8c9c-74ee5b48544a
new_param = bsh.new_parametric_job(project_api_key)
measure_list = list()
wwr = bshapi.measures.WindowWallRatio()
wwr.set_datalist([0.2, 0.6])
measure_list.append(wwr)
daylit_sensor = bshapi.measures.DaylightingSensor()
measure_list.append(daylit_sensor)
hvac = bshapi.measures.HVACTemplate()
hvac.set_datalist([1, 5, 7, 11])
measure_list.append(hvac)
There are a list of standard designs we can add to this list. But can we specify the parameters in those standard designs? For example, can we specify the control strategy in the daylight sensors? The answer is SURE!
# building the template
# You just need to add fields that are critical in your study
daylit_template = bshapi.measures.DesignTemplate()
daylit_template.set_class_label("Daylighting:Controls")
daylit_template.set_template_field("Lighting Control Type", "Continuous")
daylit_template.set_template_field("Minimum Input Power Fraction for Continuous Dimming Control", "0.2")
daylit_template.set_template_field("Minimum Light Output Fraction for Continuous Dimming Control", "0.3")
#add the template to the design
daylit_sensor.set_custom_template(daylit_template)
Editing the parameters under a design is great! However, what if the things I want to change is not included in the library? For that, we can always do a customized design. There are two types of customized design:
- Continuous variables: design involves updating a numeric value
- Discrete variables: design involves updating a group of values each time.
Let's build our first continuous variable design. For example, the current infiltration design is defined by a zone/flow based method:
Flow/zone, !- Design Flow Rate Calculation Method .004644, !- Design Flow Rate {m3/s}
We want to change the design flow rates to 0.002644 m3/s
and 0.006644 m3/s
. The available infiltration design only changes the value by percentage. So we have to build one our own.
infiltration_custom_design = bshapi.measures.CustomizedMeasure('infiltration_zone', 'si')
infiltration_custom_design.add_continuous_template('ZoneInfiltration:DesignFlowRate', 'Design Flow Rate', '0.004644', '0.06266')
measure_list.append(infiltration_custom_design)
Discrete design requires a bit of work as we are defining a group of parameters for each design. Again, let's come back to the lighting power. Let's say we have three packages of lighting systems: Fluorescent (12.0 W/m2
), compact Fluorescent (8.0 W/m2
and LED (6.4 W/m2
).
# Discrete custom design
lit_pack_design = bshapi.measures.CustomizedMeasure('lit_pack', 'si')
# we will use DiscreteMeasureOptionTemplate to assist creating a discrete design
# Create template for Flourescent package
lit_template = bshapi.measures.DiscreteMeasureOptionTemplate()
light_1_temp = dict()
light_1_temp['Watts per Zone Floor Area'] = "12.0"
lit_template.add_class_template_modify('Lights', light_1_temp)
lit_template.set_option_name('FLO')
# add the first template group to the design
lit_pack_design.add_discrete_template_options(lit_template.get_template_group())
# Create template for Compact Flourescent package
lit_template.clear()
light_2_temp = dict()
light_2_temp['Watts per Zone Floor Area'] = "8.0"
lit_template.add_class_template_modify('Lights', light_2_temp)
lit_template.set_option_name('CFLO')
# add the second template group to the design
lit_pack_design.add_discrete_template_options(lit_template.get_template_group())
# Create template for LED package
lit_template.clear()
light_3_temp = dict()
light_3_temp['Watts per Zone Floor Area'] = "6.4"
lit_template.add_class_template_modify('Lights', light_3_temp)
lit_template.set_option_name('LED')
# add the third template group to the design
lit_pack_design.add_discrete_template_options(lit_template.get_template_group())
# DONE
# now lets add this design back to the list
measure_list.append(lit_pack_design)
Completed! Let's issue a request for a parametric run to the server. We will use the default brute-force algorithm to combine the design options. So we have:
- 2 designs of WWR
- 2 designs of daylight sensor
- 4 designs of HVAC
- 2 designs of infiltration
- 3 designs of lighting system
So the brute-force algorithm will automatically generate 2x2x4x2x3 = 96
models to run the parametric study.
new_param.add_model_measures(measure_list)
param_study = new_param.submit_parametric_study(model_api_key=model_api_key, track=True)
'''
Submitting parametric simulation job request...
Received server response
You can track the parametric using API key: 3874a72d-4ca6-48b3-b4fa-507835cd602e
Building parametric sets generation
Building parametric sets generation
Total progress 0%, success: 0, failure: 0, running: 3, queue: 93
Total progress 0%, success: 0, failure: 0, running: 7, queue: 89
Total progress 0%, success: 0, failure: 0, running: 11, queue: 85
Total progress 4%, success: 4, failure: 0, running: 11, queue: 81
...
Total progress 11%, success: 11, failure: 0, running: 11, queue: 74
...
Total progress 98%, success: 95, failure: 0, running: 1, queue: 0
Total progress 98%, success: 95, failure: 0, running: 1, queue: 0
Total progress 100%, success: 96, failure: 0, running: 0, queue: 0
Completed! You can retrieve results using the key: 186832e0-760c-4d90-872c-9b8f380f59a5
'''
Just two lines of additional code, your first parametric study is on its way! Click here to see the full script of this example.
Programmatically, you can pull the results down easily - for example, the EUIs
# as our parametric run generates a new model branch id: 186832e0-760c-4d90-872c-9b8f380f59a5, we will then pull results from this id
param_id = "186832e0-760c-4d90-872c-9b8f380f59a5"
param_res = bsh.parametric_results(project_api_key, param_id)
eui = param_res.net_site_eui()
eui_pp = pp.ParametricPlot(eui)
eui_df = eui_pp.pandas_df()
print(eui_df.to_string())
'''
DaylitSensor HVAC WWR infiltration_zone lit_pack Value
case1 0 doasfancoil 0.6 0.06266 LED 69.40
case2 0 doasfancoil 0.6 0.06266 CFLO 70.87
...
'''
Then, we can fully utilize python's rich plotting libraries to generate beautiful graphs like this one: