Motivation

EnergyPlus provides a preprocessor call IDFVersionUpdater to update IDF to the latest version. It is written in Fortran and the program itself evolves along with EnergyPlus. Once a new version of EnergyPlus is published, IDFVersionUpdater will add the support of that version.

The original IDFVersionUpdater program is a GUI program and does not provide command line interface. This makes it hard to update lots of IDF files programmatically. The good news is that IDFVersionUpdater itself is a wrapper to call different standalone transition programs for each single version update. For example, when updating your model from EnergyPlus v8.7 to v8.9, Transition-V8-7-0-to-V8-8-0 will be called, and then Transition-V8-8-0-to-V8-9-0.

Introduce version_updater()

version_updater() is eplusr’s version of IDFVersionUpdater. version_updater() itself is not complicated. Compared to IDFVersionUpdater, version_updater() has some improvements:

  • IDFVersionUpdater in EnergyPlus v9.0 (both v9.0.0 and v9.01) fails to translate FenesreationSurface:Detailed objects. version_updater() will try to use the transition programs that come from the latest EnergyPlus installed on your computer, but will skip v9.0 if possible.

  • Like IDFVersionUpdater, version_updater() also renames all output file with suffix VXY0, where X and Y indicates the major and minor version of input IDF file. However, version_updater() always keep the original input file untouched and save a copy and rename it in the same pattern.

  • All messages generated from each transition program are parsed, stored in a list and saved as an attributes named errors. So that you can easily extract all messages by attr(results, "errors"). The error messages are parsed and stored as data.tables which make it easy for post-processing.

  • A dir argument can be given to further specify where the updated models to be saved.

Unfortunately, even though the transition programs are written in Fortran, it could still take several minutes to complete the update process

path <- "RefBldgLargeOfficeNew2004_Chicago.idf"
read_idf(path)$version()
#> IDD v8.2.0 has not been parsed before.
#> Try to locate 'Energy+.idd' in EnergyPlus v8.2.0 installation folder '/usr/local/EnergyPlus-8-2-0'.
#> Failed to locate 'Energy+.idd' because EnergyPlus v8.2.0 is not available.
#> Try to locate 'V8-2-0-Energy+.idd' in EnergyPlus v9.4.0 IDFVersionUpdater folder '/usr/local/EnergyPlus-9-4-0/PreProcess/IDFVersionUpdater'. --> Succeeded
#> IDD file found: '/usr/local/EnergyPlus-9-4-0/PreProcess/IDFVersionUpdater/V8-2-0-Energy+.idd'.
#> Start parsing...
#> Parsing completed.
#> [1] '8.2.0'

(t_vu <- system.time(verup <- version_updater(path, 9.2)))
#> IDFVersionUpdater: /usr/local/EnergyPlus-9-4-0/PreProcess/IDFVersionUpdater
#> Input file: /home/runner/work/eplusr/eplusr/vignettes/RefBldgLargeOfficeNew2004_ChicagoV820.idf
#>  From  Ver: 8.2.0
#> Toward Ver: 8.3.0
#> [8.2.0 --> 8.3.0] SUCCEEDED.
#> IDD v8.3.0 has not been parsed before.
#> Try to locate 'Energy+.idd' in EnergyPlus v8.3.0 installation folder '/usr/local/EnergyPlus-8-3-0'.
#> Failed to locate 'Energy+.idd' because EnergyPlus v8.3.0 is not available.
#> Try to locate 'V8-3-0-Energy+.idd' in EnergyPlus v9.4.0 IDFVersionUpdater folder '/usr/local/EnergyPlus-9-4-0/PreProcess/IDFVersionUpdater'. --> Succeeded
#> IDD file found: '/usr/local/EnergyPlus-9-4-0/PreProcess/IDFVersionUpdater/V8-3-0-Energy+.idd'.
#> Start parsing...
#> Parsing completed.
#> Input file: /home/runner/work/eplusr/eplusr/vignettes/RefBldgLargeOfficeNew2004_ChicagoV830.idf
#>  From  Ver: 8.3.0
#> Toward Ver: 8.4.0
#> [8.3.0 --> 8.4.0] SUCCEEDED.
#> IDD v8.4.0 has not been parsed before.
#> Try to locate 'Energy+.idd' in EnergyPlus v8.4.0 installation folder '/usr/local/EnergyPlus-8-4-0'.
#> Failed to locate 'Energy+.idd' because EnergyPlus v8.4.0 is not available.
#> Try to locate 'V8-4-0-Energy+.idd' in EnergyPlus v9.4.0 IDFVersionUpdater folder '/usr/local/EnergyPlus-9-4-0/PreProcess/IDFVersionUpdater'. --> Succeeded
#> IDD file found: '/usr/local/EnergyPlus-9-4-0/PreProcess/IDFVersionUpdater/V8-4-0-Energy+.idd'.
#> Start parsing...
#> Parsing completed.
#> Input file: /home/runner/work/eplusr/eplusr/vignettes/RefBldgLargeOfficeNew2004_ChicagoV840.idf
#>  From  Ver: 8.4.0
#> Toward Ver: 8.5.0
#> [8.4.0 --> 8.5.0] SUCCEEDED.
#> IDD v8.5.0 has not been parsed before.
#> Try to locate 'Energy+.idd' in EnergyPlus v8.5.0 installation folder '/usr/local/EnergyPlus-8-5-0'.
#> Failed to locate 'Energy+.idd' because EnergyPlus v8.5.0 is not available.
#> Try to locate 'V8-5-0-Energy+.idd' in EnergyPlus v9.4.0 IDFVersionUpdater folder '/usr/local/EnergyPlus-9-4-0/PreProcess/IDFVersionUpdater'. --> Succeeded
#> IDD file found: '/usr/local/EnergyPlus-9-4-0/PreProcess/IDFVersionUpdater/V8-5-0-Energy+.idd'.
#> Start parsing...
#> Parsing completed.
#> Input file: /home/runner/work/eplusr/eplusr/vignettes/RefBldgLargeOfficeNew2004_ChicagoV850.idf
#>  From  Ver: 8.5.0
#> Toward Ver: 8.6.0
#> [8.5.0 --> 8.6.0] SUCCEEDED.
#> IDD v8.6.0 has not been parsed before.
#> Try to locate 'Energy+.idd' in EnergyPlus v8.6.0 installation folder '/usr/local/EnergyPlus-8-6-0'.
#> Failed to locate 'Energy+.idd' because EnergyPlus v8.6.0 is not available.
#> Try to locate 'V8-6-0-Energy+.idd' in EnergyPlus v9.4.0 IDFVersionUpdater folder '/usr/local/EnergyPlus-9-4-0/PreProcess/IDFVersionUpdater'. --> Succeeded
#> IDD file found: '/usr/local/EnergyPlus-9-4-0/PreProcess/IDFVersionUpdater/V8-6-0-Energy+.idd'.
#> Start parsing...
#> Parsing completed.
#> Input file: /home/runner/work/eplusr/eplusr/vignettes/RefBldgLargeOfficeNew2004_ChicagoV860.idf
#>  From  Ver: 8.6.0
#> Toward Ver: 8.7.0
#> [8.6.0 --> 8.7.0] SUCCEEDED.
#> IDD v8.7.0 has not been parsed before.
#> Try to locate 'Energy+.idd' in EnergyPlus v8.7.0 installation folder '/usr/local/EnergyPlus-8-7-0'.
#> Failed to locate 'Energy+.idd' because EnergyPlus v8.7.0 is not available.
#> Try to locate 'V8-7-0-Energy+.idd' in EnergyPlus v9.4.0 IDFVersionUpdater folder '/usr/local/EnergyPlus-9-4-0/PreProcess/IDFVersionUpdater'. --> Succeeded
#> IDD file found: '/usr/local/EnergyPlus-9-4-0/PreProcess/IDFVersionUpdater/V8-7-0-Energy+.idd'.
#> Start parsing...
#> Parsing completed.
#> Input file: /home/runner/work/eplusr/eplusr/vignettes/RefBldgLargeOfficeNew2004_ChicagoV870.idf
#>  From  Ver: 8.7.0
#> Toward Ver: 8.8.0
#> [8.7.0 --> 8.8.0] SUCCEEDED.
#> IDD v8.8.0 has not been parsed before.
#> Try to locate 'Energy+.idd' in EnergyPlus v8.8.0 installation folder '/usr/local/EnergyPlus-8-8-0'.
#> IDD file found: '/home/runner/.local/EnergyPlus-8-8-0/Energy+.idd'.
#> Start parsing...
#> Parsing completed.
#> Input file: /home/runner/work/eplusr/eplusr/vignettes/RefBldgLargeOfficeNew2004_ChicagoV880.idf
#>  From  Ver: 8.8.0
#> Toward Ver: 8.9.0
#> [8.8.0 --> 8.9.0] SUCCEEDED.
#> IDD v8.9.0 has not been parsed before.
#> Try to locate 'Energy+.idd' in EnergyPlus v8.9.0 installation folder '/usr/local/EnergyPlus-8-9-0'.
#> Failed to locate 'Energy+.idd' because EnergyPlus v8.9.0 is not available.
#> Try to locate 'V8-9-0-Energy+.idd' in EnergyPlus v9.4.0 IDFVersionUpdater folder '/usr/local/EnergyPlus-9-4-0/PreProcess/IDFVersionUpdater'. --> Succeeded
#> IDD file found: '/usr/local/EnergyPlus-9-4-0/PreProcess/IDFVersionUpdater/V8-9-0-Energy+.idd'.
#> Start parsing...
#> Parsing completed.
#> Input file: /home/runner/work/eplusr/eplusr/vignettes/RefBldgLargeOfficeNew2004_ChicagoV890.idf
#>  From  Ver: 8.9.0
#> Toward Ver: 9.0.0
#> [8.9.0 --> 9.0.0] SUCCEEDED.
#> Multiple versions found for IDD v9.0: 'v9.0.1'. The last patched version v9.0.1 will be used. Please explicitly give the full version if you want to use the other versions.
#> IDD v9.0.1 has not been parsed before.
#> Try to locate 'Energy+.idd' in EnergyPlus v9.0.1 installation folder '/usr/local/EnergyPlus-9-0-1'.
#> Failed to locate 'Energy+.idd' because EnergyPlus v9.0.1 is not available.
#> Try to locate 'V9-0-0-Energy+.idd' in EnergyPlus v9.4.0 IDFVersionUpdater folder '/usr/local/EnergyPlus-9-4-0/PreProcess/IDFVersionUpdater'. --> Succeeded
#> IDD file found: '/usr/local/EnergyPlus-9-4-0/PreProcess/IDFVersionUpdater/V9-0-0-Energy+.idd'.
#> Start parsing...
#> Parsing completed.
#> Input file: /home/runner/work/eplusr/eplusr/vignettes/RefBldgLargeOfficeNew2004_ChicagoV900.idf
#>  From  Ver: 9.0.1
#> Toward Ver: 9.1.0
#> [9.0.1 --> 9.1.0] SUCCEEDED.
#> IDD v9.1.0 has not been parsed before.
#> Try to locate 'Energy+.idd' in EnergyPlus v9.1.0 installation folder '/usr/local/EnergyPlus-9-1-0'.
#> Failed to locate 'Energy+.idd' because EnergyPlus v9.1.0 is not available.
#> Try to locate 'V9-1-0-Energy+.idd' in EnergyPlus v9.4.0 IDFVersionUpdater folder '/usr/local/EnergyPlus-9-4-0/PreProcess/IDFVersionUpdater'. --> Succeeded
#> IDD file found: '/usr/local/EnergyPlus-9-4-0/PreProcess/IDFVersionUpdater/V9-1-0-Energy+.idd'.
#> Start parsing...
#> Parsing completed.
#> Input file: /home/runner/work/eplusr/eplusr/vignettes/RefBldgLargeOfficeNew2004_ChicagoV910.idf
#>  From  Ver: 9.1.0
#> Toward Ver: 9.2.0
#> [9.1.0 --> 9.2.0] SUCCEEDED.
#> IDD v9.2.0 has not been parsed before.
#> Try to locate 'Energy+.idd' in EnergyPlus v9.2.0 installation folder '/usr/local/EnergyPlus-9-2-0'.
#> IDD file found: '/usr/local/EnergyPlus-9-2-0/Energy+.idd'.
#> Start parsing...
#> Parsing completed.
#>    user  system elapsed 
#> 278.431   2.557 280.629

Introduce transition()

Having a clear mental model on how the transition works, the most cumbersome work left is to translate all those actions written in Fortran into R and write tests to make sure that the R implement should be the same as IDFVersionUpdater.

transition() is a pure R implementation of transition programs. It takes similar arguments as version_updater() except it has an additional argument save to control whether to save the resultant Idf objects to IDF files or not.

(t_tr <- system.time(trans <- transition(path, 9.2)))
#>  From  Ver: 8.2.0
#> Toward Ver: 8.3.0
#> [8.2.0 --> 8.3.0] SUCCEEDED.
#>  From  Ver: 8.3.0
#> Toward Ver: 8.4.0
#> [8.3.0 --> 8.4.0] SUCCEEDED.
#>  From  Ver: 8.4.0
#> Toward Ver: 8.5.0
#> [8.4.0 --> 8.5.0] SUCCEEDED.
#>  From  Ver: 8.5.0
#> Toward Ver: 8.6.0
#> [8.5.0 --> 8.6.0] SUCCEEDED.
#>  From  Ver: 8.6.0
#> Toward Ver: 8.7.0
#> [8.6.0 --> 8.7.0] SUCCEEDED.
#>  From  Ver: 8.7.0
#> Toward Ver: 8.8.0
#> [8.7.0 --> 8.8.0] SUCCEEDED.
#>  From  Ver: 8.8.0
#> Toward Ver: 8.9.0
#> [8.8.0 --> 8.9.0] SUCCEEDED.
#>  From  Ver: 8.9.0
#> Toward Ver: 9.0.0
#> [8.9.0 --> 9.0.0] SUCCEEDED.
#>  From  Ver: 9.0.0
#> Toward Ver: 9.1.0
#> [9.0.0 --> 9.1.0] SUCCEEDED.
#>  From  Ver: 9.1.0
#> Toward Ver: 9.2.0
#> [9.1.0 --> 9.2.0] SUCCEEDED.
#>    user  system elapsed 
#>   5.320   0.007   5.329

It is about 52.7 times faster.

Fast is always good. The most important thing is to ensure that transition() can provide reasonable results. Here we compare the results.

setdiff(verup$to_string(format = "sorted"), trans$to_string(format = "sorted"))
#> [1] "    Yes,                     !- Use Weather File Snow Indicators"
#> [2] "    ;                        !- Treat Weather as Actual"

As we can see from below, there is only one difference between results from transition() and version_updater():

  • An empty field Treat Weather as Actual in RunPeriod exists in version_updater() version, but not in transition() version. This is because this field is not an required field and eplusr tried to only save minimum-necessary fields.

How the transition works

During version updates, some classes in the original IDF are removed, some classes are splitted into several classes, some fields are removed and there is no way to extract those information back. For example, from EnergyPlus v8.8 to v8.9, one single object in class GroundHeatExchanger:Vertical will be splitted into 4 different objects in class:

  • GroundHeatExchanger:System,
  • GroundHeatExchanger:Vertical:Properties,
  • Site:GroundTemperature:Undisturbed:KusudaAchenbach
  • GroundHeatExchanger:ResponseFactors

A basic transition on a single class is a combination of different transition actions. Below I will describe most typical ones together with the related transition program source code:

  • Insert: Insert a new field. The code block below means that if input class is OtherEquipment, get the definition of it from IDD, fill new objects with all old field values except insert a new field #2 with value "None".

    CASE('OTHEREQUIPMENT')
      nodiff = .false.
      CALL GetNewObjectDefInIDD(ObjectName,NwNumArgs,NwAorN,NwReqFld,NwObjMinFlds,NwFldNames,NwFldDefaults,NwFldUnits)
      OutArgs(1) = InArgs(1)
      OutArgs(2) = 'None'
      OutArgs(3:11) = InArgs(2:10)
      CurArgs = CurArgs+1
  • Reset: Replace old value with new value. The code block below means that if input class is Exterior:FuelEquipment, get the definition of it from IDD, fill new objects with all old field values except replace field #2 with "NaturalGas" if old value is "Gas" and PropaneGas if LPG. Conversion for other classes looks similar.

    CASE('EXTERIOR:FUELEQUIPMENT')
        ObjectName='Exterior:FuelEquipment'
        CALL GetNewObjectDefInIDD(ObjectName,NwNumArgs,NwAorN,NwReqFld,NwObjMinFlds,NwFldNames,NwFldDefaults,NwFldUnits)
        nodiff=.false.
        OutArgs(1:CurArgs)=InArgs(1:CurArgs)
        if (samestring('Gas',InArgs(2))) then
          OutArgs(2)='NaturalGas'
        endif
        if (samestring('LPG',InArgs(2))) then
          OutArgs(2)='PropaneGas'
        endif
  • Delete: Delete the old field, offset indices of all other field by the number of fields deleted. The code block below means that if input class is HVACTemplate:System:Unitary, get the definition of it from IDD, fill new objects with all old field values except removing #40.

    CASE('HVACTEMPLATE:SYSTEM:UNITARY')
        ObjectName='HVACTemplate:System:Unitary'
        CALL GetNewObjectDefInIDD(ObjectName,NwNumArgs,NwAorN,NwReqFld,NwObjMinFlds,NwFldNames,NwFldDefaults,NwFldUnits)
        nodiff=.false.
        OutArgs(1:39)=InArgs(1:39) ! No change
        OutArgs(40:CurArgs-1)=InArgs(41:CurArgs) ! Remove Dehumidification Control Zone Name
        CurArgs = CurArgs-1
  • Offset: Set the new value of field #N as the old value of field #M. The code block means that if input class is Daylight:Controls, get the definition of it from IDD, add a suffix _DaylCtrl to old #1 as new #1, use old #1 as new #2, set new #3 as "SplitFlux" and use #20 as new #4.

    CASE('DAYLIGHTING:CONTROLS')
      nodiff=.false.
      CALL GetNewObjectDefInIDD(ObjectName,NwNUmArgs,NwAorN,NwReqFld,NwObjMinFlds,NwFldNames,NwFldDefaults,NwFldUnits)
      OutArgs(1) = TRIM(InArgs(1)) // '_DaylCtrl'
      OutArgs(2) = InArgs(1)
      OutArgs(3) = 'SplitFlux'
      OutArgs(4) = InArgs(20)
    ......

The combinations of those four actions together builds a skeleton of an EnergyPlus transition program, together with some pre-processes and post-processes.

In eplusr, all IDF data are stored as data.tables. So transition means to write a function to perform the similar actions on field values stored in data.tables. trans_action() is designed for this purpose.

For example, the equivalent transition implemented in R for Exterior:FuelEquipment objects demonstrated in the Reset code block would be:

# Insert
dt1 <- trans_action(idf, "OtherEquipment", insert = list(2L, "None"))

# Reset
dt2 <- trans_action(idf, "Exterior:FuelEquipment",
    reset = list(2L, "Gas", "NaturalGas"),
    reset = list(2L, "LPG", "PropaneGas")
)

# Delete
dt3 <- trans_action(idf, "HVACTemplate:System:Unitary", delete = list(40L))

# Offset
dt4 <- trans_action(idf, "Daylighting:Controls",
    offset = list(20L, 4L),
    reset = list(3, "SplitFlux")
)

Having the updated IDF data, we can then easily insert them to the next new version of IDF by simply doing new_idf$load(dt).