This vignette introduces the Epw class which is designed to read and modify EnergyPlus Weather Files (EPWs).


Download EnergyPlus Weather File (EPW) and Design Day File (DDY)

eplusr contains a helper function download_weather() which can be used to download EnergyPlus EPW and DDY files from EnergyPlus weather website.

download_weather() takes a regular expression to search weather specifications and gives you a menu to choose files to download from matched results. Note that the matching is case-insensitive. Files downloaded will be renamed according to input filename argument.

download_weather("los angeles", filename = "Los Angeles", dir = tempdir())

#> 3 matched results found. Please select which one to download:
#>
#> ── [1] USA_CA_Los.Angeles.722950_TMY2 ───────────────────
#>  * Country: United States of America
#>  * State or Province: CA
#>  * Location: CA_Los.Angeles
#>  * WMO number: 722950
#>  * Source type: TMY2
#>  * Longitude: -118.4
#>  * Latitude: 33.93
#>
#> ── [2] USA_CA_Los.Angeles.Intl.AP.722950_TMY ────────────
#>  * Country: United States of America
#>  * State or Province: CA
#>  * Location: CA_Los.Angeles.Intl.AP
#>  * WMO number: 722950
#>  * Source type: TMY
#>  * Longitude: -118.4
#>  * Latitude: 33.93
#>
#> ── [3] USA_CA_Los.Angeles.Intl.AP.722950_TMY3 ───────────
#>  * Country: United States of America
#>  * State or Province: CA
#>  * Location: CA_Los.Angeles.Intl.AP
#>  * WMO number: 722950
#>  * Source type: TMY3
#>  * Longitude: -118.4
#>  * Latitude: 33.93
#>
#> 1: USA_CA_Los.Angeles.722950_TMY2
#> 2: USA_CA_Los.Angeles.Intl.AP.722950_TMY
#> 3: USA_CA_Los.Angeles.Intl.AP.722950_TMY3
#> 4: All
#>
#> Selection:

By default, it will download both EPW and DDY files of selected results. You can change that by modifying the type argument to only download either EPW files or DDY files.

Read and parse EPW

Reading an EPW file starts with function read_epw(), which parses an EPW file and returns an Epw object. The parsing process is basically the same as EnergyPlus/WeatherManager.cc in EnergyPlus, with some simplifications.

An EPW file can be divided into two parts, headers and weather data. The first eight lines of a standard EPW file are normally headers which contains data of location, design conditions, typical/extreme periods, ground temperatures, holidays/daylight savings, data periods and other comments.

Epw class provides methods to directly extract those data. For details on the data structure of EPW file, please see “Chapter 2 - Weather Converter Program” in EnergyPlus “Auxiliary Programs” documentation. An online version can be found here.

path <- path_eplus_weather("23.1", "USA_CA_San.Francisco.Intl.AP.724940_TMY3.epw")

epw <- read_epw(path)
epw
#> ══ EnergyPlus Weather File ═════════════════════════════════════════════════════
#> [Location ]: San Francisco Intl Ap, CA, USA
#>              {N 37°37'}, {W 122°24'}, {UTC-08:00}
#> [Elevation]: 2m above see level
#> [Data Src ]: TMY3
#> [WMO Stat ]: 724940
#> [Leap Year]: No
#> [Interval ]: 60 mins
#> 
#> ── Data Periods ────────────────────────────────────────────────────────────────
#>    Name StartDayOfWeek StartDay EndDay
#> 1: Data         Sunday     1/ 1  12/31
#> 
#> ────────────────────────────────────────────────────────────────────────────────

Extract and modify header data

Epw class provides 10 methods to extract and modify EPW header data, including:

Methods of Epw class to extract and modify headers
Header Method
LOCATION $location()
DESIGN CONDITIONS $design_condition()
TYPICAL/EXTREME PERIODS $typical_extreme_period()
GROUND TEMPERATURES $ground_temperature()
HOLIDAYS/DAYLIGHT SAVINGS $holiday()
COMMENTS 1 $comment1()
COMMENTS 2 $comment2()
DATA PERIODS $num_period(), $interval() & $period()

Extract weather data

Core data

$data() can be used to extract the core weather data. Usually, EPW file downloaded from EnergyPlus website contains TMY weather data. As years of weather data is not consecutive, it may be more convenient to align the year values to be consecutive, which will makes it possible to direct analyze and plot weather data. The start_year argument in $data() method can help to achieve this. However, randomly setting the year may result in a date time series that does not have the same start day of week as specified in the DATA PERIODS header. eplusr provides a simple solution for this. By setting year to NULL and align_wday to TRUE, eplusr will calculate a year value (from current year backwards) for each data period that compliance with the start day of week restriction.

str(head(epw$data()))
#> Classes 'data.table' and 'data.frame':   6 obs. of  36 variables:
#>  $ datetime                                        : POSIXct, format: "2023-01-01 01:00:00" "2023-01-01 02:00:00" ...
#>  $ year                                            : int  1999 1999 1999 1999 1999 1999
#>  $ month                                           : int  1 1 1 1 1 1
#>  $ day                                             : int  1 1 1 1 1 1
#>  $ hour                                            : int  1 2 3 4 5 6
#>  $ minute                                          : int  0 0 0 0 0 0
#>  $ data_source                                     : chr  "?9?9?9?9E0?9?9?9?9?9?9?9?9?9?9?9?9?9?9?9*9*9?9?9?9" "?9?9?9?9E0?9?9?9?9?9?9?9?9?9?9?9?9?9?9?9*9*9?9?9?9" "?9?9?9?9E0?9?9?9?9?9?9?9?9?9?9?9?9?9?9?9*9*9?9?9?9" "?9?9?9?9E0?9?9?9?9?9?9?9?9?9?9?9?9?9?9?9*9*9?9?9?9" ...
#>  $ dry_bulb_temperature                            : num  7.2 7.2 6.7 6.1 4.4 4.4
#>  $ dew_point_temperature                           : num  5.6 5.6 5 5 3.9 3.9
#>  $ relative_humidity                               : num  90 90 89 93 97 97
#>  $ atmospheric_pressure                            : num  102200 102100 102200 102200 102200 ...
#>  $ extraterrestrial_horizontal_radiation           : num  0 0 0 0 0 0
#>  $ extraterrestrial_direct_normal_radiation        : num  0 0 0 0 0 0
#>  $ horizontal_infrared_radiation_intensity_from_sky: num  290 296 291 276 280 280
#>  $ global_horizontal_radiation                     : num  0 0 0 0 0 0
#>  $ direct_normal_radiation                         : num  0 0 0 0 0 0
#>  $ diffuse_horizontal_radiation                    : num  0 0 0 0 0 0
#>  $ global_horizontal_illuminance                   : num  0 0 0 0 0 0
#>  $ direct_normal_illuminance                       : num  0 0 0 0 0 0
#>  $ diffuse_horizontal_illuminance                  : num  0 0 0 0 0 0
#>  $ zenith_luminance                                : num  0 0 0 0 0 0
#>  $ wind_direction                                  : num  0 170 210 200 260 180
#>  $ wind_speed                                      : num  0 2.1 2.1 1.5 3.1 2.1
#>  $ total_sky_cover                                 : int  2 4 3 0 3 3
#>  $ opaque_sky_cover                                : int  2 4 3 0 3 3
#>  $ visibility                                      : num  16 16 16 16 16 16
#>  $ ceiling_height                                  : num  77777 77777 77777 77777 77777 ...
#>  $ present_weather_observation                     : int  9 9 9 9 9 9
#>  $ present_weather_codes                           : chr  "999999999" "999999999" "999999999" "999999999" ...
#>  $ precipitable_water                              : num  129 120 120 120 120 120
#>  $ aerosol_optical_depth                           : num  0.108 0.108 0.108 0.108 0.108 0.108
#>  $ snow_depth                                      : num  0 0 0 0 0 0
#>  $ days_since_last_snow                            : int  88 88 88 88 88 88
#>  $ albedo                                          : num  0.16 0.16 0.16 0.16 0.16 0.16
#>  $ liquid_precip_depth                             : num  0 0 0 0 0 0
#>  $ liquid_precip_rate                              : num  1 1 1 1 1 1
#>  - attr(*, ".internal.selfref")=<externalptr>

Abnormal data

$abnormal_data() returns abnormal data of specific data period. Basically, there are 2 types of abnormal data in Epw, i.e. missing values and out-of-range values. Sometimes, it may be useful to extract and inspect those data especially when inserting measured weather data. $abnormal_data() does this.

In the returned data.table, a column named line is created indicating the line numbers where abnormal data occur in the actual EPW file.

print(epw$abnormal_data(cols = "albedo", keep_all = FALSE))
#>        line            datetime  year month   day  hour minute albedo
#>       <int>              <POSc> <int> <int> <int> <int>  <int>  <num>
#>    1:   753 1977-02-01 01:00:00  1977     2     1     1      0    999
#>    2:   754 1977-02-01 02:00:00  1977     2     1     2      0    999
#>    3:   755 1977-02-01 03:00:00  1977     2     1     3      0    999
#>    4:   756 1977-02-01 04:00:00  1977     2     1     4      0    999
#>    5:   757 1977-02-01 05:00:00  1977     2     1     5      0    999
#>   ---                                                                
#> 2156:  5836 1985-08-31 20:00:00  1985     8    31    20      0    999
#> 2157:  5837 1985-08-31 21:00:00  1985     8    31    21      0    999
#> 2158:  5838 1985-08-31 22:00:00  1985     8    31    22      0    999
#> 2159:  5839 1985-08-31 23:00:00  1985     8    31    23      0    999
#> 2160:  5840 1985-09-01 00:00:00  1985     8    31    24      0    999

You can use $make_na() to turn all abnormal data into NAs:

epw$make_na(missing = TRUE, out_of_range = TRUE)

print(epw$abnormal_data(cols = "albedo", keep_all = FALSE))
#>        line            datetime  year month   day  hour minute albedo
#>       <int>              <POSc> <int> <int> <int> <int>  <int>  <num>
#>    1:   753 1977-02-01 01:00:00  1977     2     1     1      0     NA
#>    2:   754 1977-02-01 02:00:00  1977     2     1     2      0     NA
#>    3:   755 1977-02-01 03:00:00  1977     2     1     3      0     NA
#>    4:   756 1977-02-01 04:00:00  1977     2     1     4      0     NA
#>    5:   757 1977-02-01 05:00:00  1977     2     1     5      0     NA
#>   ---                                                                
#> 2156:  5836 1985-08-31 20:00:00  1985     8    31    20      0     NA
#> 2157:  5837 1985-08-31 21:00:00  1985     8    31    21      0     NA
#> 2158:  5838 1985-08-31 22:00:00  1985     8    31    22      0     NA
#> 2159:  5839 1985-08-31 23:00:00  1985     8    31    23      0     NA
#> 2160:  5840 1985-09-01 00:00:00  1985     8    31    24      0     NA

Or use $fill_abnormal() to fill NAs back to missing codes:

epw$fill_abnormal(missing = TRUE, out_of_range = TRUE)

print(epw$abnormal_data(cols = "albedo", keep_all = FALSE))
#>        line            datetime  year month   day  hour minute albedo
#>       <int>              <POSc> <int> <int> <int> <int>  <int>  <num>
#>    1:   753 1977-02-01 01:00:00  1977     2     1     1      0    999
#>    2:   754 1977-02-01 02:00:00  1977     2     1     2      0    999
#>    3:   755 1977-02-01 03:00:00  1977     2     1     3      0    999
#>    4:   756 1977-02-01 04:00:00  1977     2     1     4      0    999
#>    5:   757 1977-02-01 05:00:00  1977     2     1     5      0    999
#>   ---                                                                
#> 2156:  5836 1985-08-31 20:00:00  1985     8    31    20      0    999
#> 2157:  5837 1985-08-31 21:00:00  1985     8    31    21      0    999
#> 2158:  5838 1985-08-31 22:00:00  1985     8    31    22      0    999
#> 2159:  5839 1985-08-31 23:00:00  1985     8    31    23      0    999
#> 2160:  5840 1985-09-01 00:00:00  1985     8    31    24      0    999

Modify weather data

Epw class provides $add(), $set() and $del() methods to add, update and delete a new data period, respectively.

Below we extract weather data with automatically calculated year values, and replace the existing data and turn it into an AMY weather data.

d <- epw$data()

print(d[, .(datetime)])
#>                  datetime
#>                    <POSc>
#>    1: 2023-01-01 01:00:00
#>    2: 2023-01-01 02:00:00
#>    3: 2023-01-01 03:00:00
#>    4: 2023-01-01 04:00:00
#>    5: 2023-01-01 05:00:00
#>   ---                    
#> 8756: 2023-12-31 20:00:00
#> 8757: 2023-12-31 21:00:00
#> 8758: 2023-12-31 22:00:00
#> 8759: 2023-12-31 23:00:00
#> 8760: 2024-01-01 00:00:00

epw$set(d, realyear = TRUE)
#> ── Info ────────────────────────────────────────────────────────────────────────
#> Data period #1 has been replaced with input data.
#> 
#>      Name StartDayOfWeek   StartDay     EndDay
#>  1:  Data         Sunday 2023/ 1/ 1 2023/12/31
#> ────────────────────────────────────────────────────────────────────────────────

Epw class supports multiple data periods in a single EPW file:

d <- epw$data(start_year = 2014, align_wday = FALSE)
#> Warning: Invalid 'start_year' found for Data period #1 'Data' starting from
#> 2023/ 1/ 1 to 2023/12/31. The original starting date falls in a leap year,
#> however input 'start_year' is not a leap year. Invalid date time may occur.
#> Warning: Data period #1 'Data' seems like a real-year data starting from 2023/
#> 1/ 1 to 2023/12/31. The starting date will be overwriten as 2014/ 1/ 1.

epw$add(d, after = 0L, realyear = TRUE)
#> ── Info ────────────────────────────────────────────────────────────────────────
#> New data period has been added successfully:
#> 
#>       Name StartDayOfWeek   StartDay     EndDay
#>  1:  Data1      Wednesday 2014/ 1/ 1 2014/12/31
#> ────────────────────────────────────────────────────────────────────────────────

epw$period()
#>    index   name start_day_of_week  start_day    end_day
#>    <int> <char>            <char>  <EpwDate>  <EpwDate>
#> 1:     1  Data1         Wednesday 2014/ 1/ 1 2014/12/31
#> 2:     2   Data            Sunday 2023/ 1/ 1 2023/12/31