Skip to content

script_tutorial

Weili edited this page Jul 26, 2019 · 7 revisions

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

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: DB image and HVAC

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.

Upload

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.

upgrade idf

Once upgraded, we can then upload this model to the platform - simply drop it to the page. drop the model

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. APIs

Script!

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.

Run 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
'''

View model

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: geometry and we can also view the HVAC systems that connects to the geometry: hvac

Building data

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
'''

Modify the LPD

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: comparison 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)

compare daylit 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!

HVAC system? No problem!

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()

DOAS + VRF

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. model improvement

Script to do parametric

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!

Make a copy

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

Start a parametric agent

new_param = bsh.new_parametric_job(project_api_key)

Let's add some designs

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!

Standard design template

# 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)

Customize designs

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:

  1. Continuous variables: design involves updating a numeric value
  2. 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

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:

  1. 2 designs of WWR
  2. 2 designs of daylight sensor
  3. 4 designs of HVAC
  4. 2 designs of infiltration
  5. 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.

View Results

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: results