knitr::opts_chunk$set( collapse = TRUE, comment = "#>", screenshot.force = FALSE, fig.align = "center", fig.width = 8, fig.height = 6 ) # the default output hook hook_output <- knitr::knit_hooks$get("output") knitr::knit_hooks$set(output = function(x, options) { if (!is.null(n <- options$out.lines)) { x <- unlist(strsplit(x, "\n", fixed = TRUE)) if (length(x) > n) { # truncate the output x <- c(head(x, n), "....", "") } else { x <- c(x, "") } x <- paste(x, collapse = "\n") # paste first n lines together } hook_output(x, options) }) options(crayon.enabled = FALSE) options(data.table.print.class = TRUE) library(eplusr) if (!is_avail_eplus("23.1")) install_eplus("23.1") library(dplyr)
This vignette demonstrates the process of performing parametric simulation
analyses using the ParametricJob
class. The main focuses are on showcasing
the capabilities of (1) creating parametric models by applying measures and (2)
easing the comparative analysis by reusing code snippets developed in data
exploration process.
The ParametricJob
class in eplusr is a parametric prototype that provides a
set of abstractions to ease the process of parametric model generation, design
alternative evaluation, and large parametric simulation management.
An overview of the parametric prototype implementation is shown blow:
knitr::include_graphics("parametric.png")
A parametric simulation is initialized using param_job()
giving a seed model
and a weather file.
library(eplusr) library(dplyr) path_model <- path_eplus_example("23.1", "RefBldgMediumOfficeNew2004_Chicago.idf") path_weather <- path_eplus_weather("23.1", "USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.epw") idf <- read_idf(path_model) idf$SimulationControl$Run_Simulation_for_Weather_File_Run_Periods <- "Yes" idf$OutputControl_Table_Style$Unit_Conversion <- "JtoKWH" idf$save(file.path(tempdir(), "MediumOffice.idf"), overwrite = TRUE) # create a parametric prototype of given model and weather file param <- param_job(idf, path_weather)
Design alternatives are specified by applying a measure function to the seed model. The concept of measure in the prototype is inspired by a similar concept in OpenStudio but tailored for flexibility and extensibility.
A measure is simply an R function that takes an Idf
object and any other
parameters as input, and returns a set of modified Idf
objects as output,
making it possible to leverage other modules in the framework and apply
statistical methods and libraries existing in R to generate design options.
set_lpd()
blow is a simple measure that modifies the LPD (Lighting Power
Density), and set_nightplug()
is a measure that modifies the off-work
schedule values of plug loads by multiplying a specified reduction faction
value.
# create a measure for modifying LPD set_lpd <- function(idf, lpd = NA) { # keep the original if applicable if (is.na(lpd)) return(idf) # set 'Watts per Zone Floor Area' in all 'Lights' objects as input LPD idf$set(Lights := list(watts_per_zone_floor_area = lpd)) # return the modified model idf } # create a measure for reducing plug loads during off-work time set_nightplug <- function(idf, frac = NA) { # keep the original if applicable if (is.na(frac)) return(idf) # extract the plug load schedule into a tidy table sch <- idf$to_table("bldg_equip_sch") # modify certain schedule value specified using field names sch <- sch %>% mutate(value = case_when( field %in% paste("Field", c(4,14,16,18)) ~ sprintf("%.2f", as.numeric(value) * frac), TRUE ~ value )) # update schedule object using the tidy table idf$update(sch) # return the modified model idf }
After a measure is defined, the method $apply_measure()
takes it and other
parameter values specified to create a set of models. Different measures can be
chained together.
# combine two measures into one ecm <- function(idf, lpd, nightplug_frac) { idf <- set_lpd(idf, lpd) idf <- set_nightplug(idf, nightplug_frac) idf } # apply measures and create parametric models param$apply_measure(ecm, lpd = c( NA, 7.0, 5.0, NA, NA, 5.0), nightplug_frac = c( NA, NA, NA, 0.6, 0.2, 0.2), # name of each case .names = c("Ori", "T5", "LED", "0.6Frac", "0.2Frac", "LED+0.2Frac") )
After parametric models have been created, you can retrive the summary of the
parameter values and model names using $cases()
.
param$cases()
The $run()
method will run all parametric simulations in parallel and place
each simulation outputs in a separate folder.
All simulation meta data will keep updating during the whole time and can be
retrieved using the $status()
method for further investigations.
param$run() param$status()
The ParametricJob
class leverages the tidy data interface to retrieve
parametric simulation results in a tidy format.
For all resulting tidy tables, an extra column containing the simulation job
identifiers is prepended in each table. It can be used as an index or key for
further data transformations, analyses and visualization to compare results of
different simulated design options.
# read building energy consumption from Standard Reports param_end_use <- param$tabular_data(table_name = "End Uses", wide = TRUE)[[1L]] print(param_end_use)
After calling the $run()
method to conduct parallel runs of simulations, the
tidy data interface can be used to extract any simulation outputs of interest
using $report_data()
, $tabular_data()
, etc.
In this example, the building energy consumptions of all six
models are extracted using one line of code.
The resulting data format is the same as that of a single simulation and is
equivalent to bind rows from six tables into one tidy table.
A case
column is prepended using the names specified in $apply_measure()
.
It works as an identifier to group the results by different parametric models
using group_by()
and nest()
functions from the tidyverse package.
This data structure makes it effortless to perform comparative analyses by taking the code snippets developed in data exploration for a single simulation and applying them to each of the parametric simulations.
# read building area from Standard Reports area <- param$tabular_data(table_name = "Building Area", wide = TRUE)[[1L]] # calculate EUI breakdown param_eui <- param_end_use %>% select(case, category = row_name, electricity = `Electricity [kWh]`) %>% filter(electricity > 0.0) %>% arrange(-electricity) %>% mutate(eui = round(electricity / area$'Area [m2]'[1], digits = 2)) %>% select(case, category, eui) %>% # exclude categories that did not change filter(category != "Pumps", category != "Exterior Lighting") print(param_eui) # extract the seed model, i.e. "Ori" case as the baseline ori_eui <- param_eui %>% filter(case == "Ori") %>% select(-case) # calculate energy savings based on the baseline EUI param_savings <- param_eui %>% right_join(ori_eui, by = "category", suffix = c("", "_ori")) %>% mutate(savings = (eui_ori - eui) / eui_ori * 100) %>% filter(case != "Ori") print(param_savings) # plot a bar chart to show the energy savings library(ggplot2) param_savings %>% mutate(case = factor(case, names(param$models()))) %>% ggplot(aes(case, savings, fill = category)) + geom_bar(position = "dodge", stat = "identity", width = 0.6, color = "black", show.legend = FALSE) + facet_wrap(vars(category), nrow = 2) + labs(x = NULL, y = "Energy savings / %") + coord_flip()
ParametricJob
classThe ParametricJob
class is designed to be simple yet flexible and extensible.
One good example of its extensibility is the
epluspar R package, which provides
new classes for conducting specific parametric analyses on EnergyPlus models,
including sensitivity analysis, Bayesian calibration and optimization using
Generic algorithm.
All the new classes introduced are based on the ParametricJob
class. The main
difference mainly lies in the specific statistical method used for sampling
parameter values when calling $apply_measure()
method.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.