-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathREADME.Rmd
466 lines (351 loc) · 13.8 KB
/
README.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
---
output:
github_document
---
<!-- README.md is generated from README.Rmd. Please edit that file -->
```{r setup, include = FALSE}
library(epluspar)
library(knitr)
# 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)
})
knitr::opts_knit$set(root.dir = tempdir())
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "README-",
out.lines = 20
)
# Make sure the date is shown in English format not Chinese.
invisible(Sys.setlocale(category = "LC_TIME", locale = "en_US.UTF-8"))
```
# epluspar
Conduct sensitivity analysis and Bayesian calibration of EnergyPlus models.
[![Travis-CI Build Status](https://api.travis-ci.com/ideas-lab-nus/epluspar.svg?token=1LqeFok1d6q5niBF8Hqr&branch=master)](https://travis-ci.com/ideas-lab-nus/epluspar)
[![CRAN_Status_Badge](http://www.r-pkg.org/badges/version/epluspar)](https://cran.r-project.org/package=epluspar)
## Installation
Currently, epluspar is not on CRAN. You can install the development version from
GitHub.
```{r gh-installation, eval = FALSE}
install.packages("epluspar", repos = "https://hongyuanjia.r-universe.dev")
```
<!-- TOC GFM -->
* [Sensitivity Analysis](#sensitivity-analysis)
* [Basic workflow](#basic-workflow)
* [Examples](#examples)
* [Bayesian Calibration](#bayesian-calibration)
* [Basic workflow](#basic-workflow-1)
* [Examples](#examples-1)
* [Get RDD and MDD](#get-rdd-and-mdd)
* [Setting Input and Output Variables](#setting-input-and-output-variables)
* [Adding Parameters to Calibrate](#adding-parameters-to-calibrate)
* [Getting Sample Values and Parametric Models](#getting-sample-values-and-parametric-models)
* [Run simulations and gather data](#run-simulations-and-gather-data)
* [Specify Measured Data](#specify-measured-data)
* [Specify Input Data for Stan](#specify-input-data-for-stan)
* [Get Stan file](#get-stan-file)
* [Run Bayesian Calibration Using Stan](#run-bayesian-calibration-using-stan)
<!-- /TOC -->
## Sensitivity Analysis
### Basic workflow
The basic workflow is basically:
1. Adding parameters for sensitivity analysis using `$param()` or
`$apply_measure()`.
1. Check parameter sampled values and generated parametric models using
`$samples()` and `$models()` respectively.
1. Run EnergyPlus simulations in parallel using `$run()`.
1. Gather EnergyPlus simulated data using `$report_data()` or `$tabular_data()`.
1. Evaluate parameter sensitivity using `$evaluate()`.
### Examples
Create a `SensitivityJob` object:
```{r}
# use an example file from EnergyPlus v8.8 for demonstration here
path_idf <- file.path(eplusr::eplus_config(8.8)$dir, "ExampleFiles", "5Zone_Transformer.idf")
path_epw <- file.path(eplusr::eplus_config(8.8)$dir, "WeatherData", "USA_CA_San.Francisco.Intl.AP.724940_TMY3.epw")
# create a `SensitivityJob` class which inheris from eplusr::ParametricJob class
sen <- sensi_job(path_idf, path_epw)
```
Set sensitivity parameters using `$param()` or `$apply_measure()`.
* Using `$param()`
```{r, eval = FALSE}
# set parameter using similar syntax to `Idf$set()` in eplusr
sen$param(
# For adding a single object field as parameter
# Syntax: Object = list(Field = c(Min, Max, Levels))
`Supply Fan 1` = list(Fan_Total_Efficiency = c(0.1, 1.0, 5)),
# For adding a class field as parameter
Material := list(
Thickness = c(min = 0.01, max = 0.08, levels = 5),
Conductivity = c(min = 0.01, max = 0.6, levels = 6)
),
# use `.names` to give names to each parameter
.names = c("efficiency", "thickness", "conductivity"),
# See `r` and `grid_jump` in `sensitivity::morris`
.r = 8, .grid_jump = 1
)
```
* Using `$apply_measure()`
```{r}
# first define a "measure"
my_actions <- function (idf, efficiency, thickness, conducitivy) {
idf$set(
`Supply Fan 1` = list(Fan_Total_Efficiency = efficiency),
Material := list(Thickness = thickness, Conductivity = conducitivy)
)
idf
}
# then apply that measure with parameter space definitions as function arguments
sen$apply_measure(my_actions,
efficiency = c(0.1, 1.0, 5),
thickness = c(0.01, 0.08, 5),
conducitivy = c(0.1, 0.6, 6),
.r = 8, .grid_jump = 1
)
```
Get samples
```{r}
sen$samples()
```
Run simulations and calculate statistic indicators
```{r}
# run simulations in temporary directory
sen$run(dir = tempdir(), echo = FALSE)
# extract output
# here is just am example
eng <- sen$tabular_data(table_name = "site and source energy",
column_name = "energy per total building area",
row_name = "total site energy")[, as.numeric(value)]
# calculate sensitivity
(result <- sen$evaluate(eng))
# extract data
attr(result, "data")
```
Plot
```{r get-started, fig.path = "man/figures/"}
# plot
plot(result)
```
## Bayesian Calibration
### Basic workflow
1. Setting input and output variables using `$input()` and `$output()`
respectively.
Input variables should be variables listed in RDD while output variables
should be variables listed in RDD and MDD.
1. Adding parameters to calibrate using `$param()` or `$apply_measure()`.
1. Check parameter sampled values and generated parametric models using
`$samples()` and `$models()` respectively.
1. Run EnergyPlus simulations in parallel using `$eplus_run()`.
1. Gather simulated data of input and output parameters using `$data_sim()`.
1. Specify field measured data of input and output parameters using
`$data_field()`.
1. Specify field measured data of input and output parameters using `$data_field()`.
1. Specify input data for Stan for Bayesian calibration using `$data_bc()`.
1. Run bayesian calibration and get predictions using stan using `$stan_run()`.
### Examples
Create a `BayesCalibJob` object:
```{r}
# use an example file from EnergyPlus v8.8 for demonstration here
path_idf <- file.path(eplusr::eplus_config(8.8)$dir, "ExampleFiles", "RefBldgLargeOfficeNew2004_Chicago.idf")
path_epw <- file.path(eplusr::eplus_config(8.8)$dir, "WeatherData", "USA_CA_San.Francisco.Intl.AP.724940_TMY3.epw")
# create a `SensitivityJob` class which inherits from eplusr::ParametricJob class
bc <- bayes_job(path_idf, path_epw)
```
#### Get RDD and MDD
`$read_rdd()` and `$read_mdd()` can be used to get RDD and MDD for current seed
model.
```{r}
(rdd <- bc$read_rdd())
(mdd <- bc$read_mdd())
```
#### Setting Input and Output Variables
Input variables and output variables can be set by using `$input()` and
`$output()`, respectively. For `$input()`, only variables listed in RDD are
supported. For `$output()`, variables listed in RDD and MDD are both supported.
By default, they are all empty and `$input()`, `$output()` will return `NULL`.
```{r}
bc$input()
bc$output()
bc$models()
```
You can specify input and output parameters using `RddFile`, `MddFile` and
data.frames.
```{r}
# using RDD and MDD
bc$input(rdd[1:3])
bc$output(mdd[1])
# using data.frame
bc$input(eplusr::rdd_to_load(rdd[1:3]))
bc$output(eplusr::mdd_to_load(mdd[1]))
```
You can set `append` to `NULL` to remove all existing input and output
parameters.
```{r}
bc$input(append = NULL)
bc$output(append = NULL)
```
You can also directly specify variable names:
```{r}
bc$input("CoolSys1 Chiller 1", paste("chiller evaporator", c("inlet temperature", "outlet temperature", "mass flow rate")), "hourly")
bc$output("CoolSys1 Chiller 1", "chiller electric power", "hourly")
```
Note that variable cannot be set as both an input and output variable.
```{r, error = TRUE}
bc$output("CoolSys1 Chiller 1", name = "chiller evaporator inlet temperature", reporting_frequency = "hourly")
```
Also, note that input and output variables should have the same reporting
frequency.
```{r, error = TRUE}
bc$output(mdd[1], reporting_frequency = "daily")
```
For `$output()`, both variables in RDD and MDD are supported. However, for
`$input()`, only variables in RDD are allowed.
#### Adding Parameters to Calibrate
Similarly like `SensitivityJob`, parameters can be added using either `$param()`
or `$apply_measure()`.
Here use `$param()` for demonstration. Basically there are 3 format of defining
a parameter:
* `object = list(field1 = c(min, max), field2 = c(min, max), ...)`
This is the basic format. `field1` and `field2` in `object` will be added as
two different parameters, with minimum and maximum value specified as `min`
and `max`.
* `class := list(field1 = c(min, max), field2 = c(min, max), ...)`
This is useful when you want to treat `field1` and `field2` in all objects in
`class` as two different parameters. Please note the use of special notion of
`:=` instead of `=`.
* `.(objects) := list(field1 = c(min, max), field2 = c(min, max), ...)`
Sometimes you may not want to treat a field in all objects in a class but only
a subset of objects. You can use a special notation on the left hand side
`.()`. In the parentheses can be object names or IDs.
```{r}
bc$param(
`CoolSys1 Chiller 1` = list(reference_cop = c(4, 6), reference_capacity = c(2.5e6, 3.0e6)),
.names = c("cop1", "cap1"), .num_sim = 5
)
```
#### Getting Sample Values and Parametric Models
Parameter values can be retrieved using `$samples()`.
```{r}
bc$samples()
```
Generated `Idf`s can be retrieved using `$models()`.
```{r}
names(bc$models())
```
#### Run simulations and gather data
`$eplus_run()` runs all parametric models in parallel. Parameter `run_period`
can be given to insert a new `RunPeriod` object. In this case, all existing
`RunPeriod` objects in the seed model will be commented out.
```{r}
bc$eplus_run(dir = tempdir(), run_period = list("example", 7, 1, 7, 3), echo = FALSE)
```
`$data_sim()` returns a `data.table` (when `merge` is `TRUE`) or a list of 2
`data.table` (when `merge` is `FALSE`) which contains the simulated data of
input and output parameters. These data will be stored internally and used
during Bayesian calibration using Stan.
The `resolution` parameter can be used to specify the time resolution of
returned data. Note that input time resolution cannot be smaller than the
reporting frequency, otherwise an error will be issued.
```{r, error = TRUE}
bc$data_sim("1 min")
```
```{r}
bc$data_sim("6 hour")
```
#### Specify Measured Data
`$data_field()` takes a `data.frame` of measured value of output parameters and
returns a list of `data.table`s which contains the measured value of input and
output parameters, and newly measured value of input if applicable.
For input parameters, the values of simulation data for the first case are
directly used as the measured values.
For demonstration, we use the seed model to generate fake measured output data.
```{r}
# clone the seed model
seed <- bc$seed()$clone()
# remove existing RunPeriod objects
seed$RunPeriod <- NULL
# set run period as the same as in `$eplus_run()`
seed$add(RunPeriod = list("test", 7, 1, 7, 3))
seed$SimulationControl$set(
`Run Simulation for Sizing Periods` = "No",
`Run Simulation for Weather File Run Periods` = "Yes"
)
# save the model to tempdir
seed$save(tempfile(fileext = ".idf"))
# run
job <- seed$run(bc$weather(), echo = FALSE)
# get output data
fan_power <- epluspar:::report_dt_aggregate(job$report_data(name = bc$output()$variable_name, all = TRUE, day_type = "normalday"), "6 hour")
fan_power <- epluspar:::report_dt_to_wide(fan_power)
# add Gaussian noice
fan_power <- fan_power[, -"Date/Time"][
, lapply(.SD, function (x) x + rnorm(length(x), sd = 0.05 * sd(x)))][
, lapply(.SD, function (x) {x[x < 0] <- 0; x})
]
# set field data
bc$data_field(fan_power)
```
#### Specify Input Data for Stan
`$data_bc()` takes a list of field data and simulated data, and returns a
list that contains data input for Bayesian calibration using the Stan model
* `n`: Number of measured parameter observations.
* `m`: Number of simulated observations.
* `p`: Number of input parameters.
* `q`: Number of calibration parameters.
* `yf`: Data of measured output after z-score standardization using data of
simulated output.
* `yc`: Data of simulated output after z-score standardization.
* `xf`: Data of measured input after min-max normalization.
* `xc`: Data of simulated input after min-max normalization.
* `tc`: Data of calibration parameters after min-max normalization.
```{r}
str(bc$data_bc())
```
You can also supply your own field data and simulated data and using
`$data_bc()` to construct the input for the Stan model. Input `data_field` and
`data_sim` should have the same structure as the output from `$data_field()` and
`$data_sim()`. If `data_field` and `data_sim` is not specified, the output from
`$data_field()` and `$data_sim()` will be used.
#### Get Stan file
You can save the internal Stan code using `$stan_file()`. If no path is
specified, a character vector that contains the stan code will be returned.
```{r, out.lines = 20}
bc$stan_file()
```
#### Run Bayesian Calibration Using Stan
You can run Bayesian calibration using Stan using `$stan_run()`.
If `data` argument is not specified, the output of `$data_bc()` is directly
used.
```{r, eval = FALSE}
options(mc.cores = parallel::detectCores())
bc$stan_run(iter = 300, chains = 3)
```
Instead of using builtin Stan model, you can also provide your own Stan code
using `file` argument.
```{r, eval = FALSE}
bc$stan_run(file = bc$stan_file(tempfile(fileext = ".stan")), iter = 300, chains = 3)
```
You can also use custom data set
```{r}
res <- bc$stan_run(data = bc$data_bc(), iter = 300, chains = 3)
```
The stan model is store in `fit`
```{r stan, fig.path = "man/figures/"}
rstan::stan_trace(res$fit)
rstan::stan_hist(res$fit)
```
The predicted values is stored in `y_pred`.
```{r}
str(res$y_pred)
```