EPlus Model (idf file)

An EnergyPlus Model (Epm) is a python object describing a building model (idf or epjson file). This object contains tables, which contain records.

Epm

Perform imports, and prepare EPlus base directory path (to work with example files).

import os
import opyplus as op

eplus_dir_path = op.get_eplus_base_dir_path((9, 0, 1))

Load Epm

# prepare path
idf_path = os.path.join(
    eplus_dir_path,
    "ExampleFiles",
    "1ZoneEvapCooler.idf"
)

# load epm
epm = op.Epm.load(idf_path)

print(epm)
Epm
  AirLoopHVAC: 1 record
  AirLoopHVAC:ControllerList: 1 record
  AirLoopHVAC:OutdoorAirSystem: 1 record
  AirLoopHVAC:OutdoorAirSystem:EquipmentList: 1 record
  AirLoopHVAC:ReturnPath: 1 record
  AirLoopHVAC:SupplyPath: 1 record
  AirLoopHVAC:ZoneMixer: 1 record
  AirLoopHVAC:ZoneSplitter: 1 record
  AirTerminal:SingleDuct:Uncontrolled: 1 record
  AvailabilityManager:HighTemperatureTurnOn: 1 record
  AvailabilityManager:LowTemperatureTurnOff: 1 record
  AvailabilityManagerAssignmentList: 1 record
  Branch: 1 record
  BranchList: 1 record
  Building: 1 record
  BuildingSurface:Detailed: 6 records
  Construction: 3 records
  Controller:OutdoorAir: 1 record
  EvaporativeCooler:Direct:CelDekPad: 1 record
  Fan:ConstantVolume: 1 record
  GlobalGeometryRules: 1 record
  HeatBalanceAlgorithm: 1 record
  Material: 1 record
  Material:NoMass: 2 records
  OutdoorAir:Mixer: 1 record
  OutdoorAir:Node: 1 record
  Output:Constructions: 1 record
  Output:Meter:MeterFileOnly: 4 records
  Output:Surfaces:Drawing: 1 record
  Output:Table:SummaryReports: 1 record
  Output:Variable: 8 records
  Output:VariableDictionary: 1 record
  OutputControl:Table:Style: 1 record
  RunPeriod: 1 record
  Schedule:Compact: 4 records
  ScheduleTypeLimits: 2 records
  SimulationControl: 1 record
  Site:GroundTemperature:BuildingSurface: 1 record
  Site:Location: 1 record
  SizingPeriod:DesignDay: 2 records
  SurfaceConvectionAlgorithm:Inside: 1 record
  SurfaceConvectionAlgorithm:Outside: 1 record
  ThermostatSetpoint:SingleHeating: 1 record
  Timestep: 1 record
  Version: 1 record
  Zone: 1 record
  ZoneControl:Thermostat: 1 record
  ZoneHVAC:Baseboard:Convective:Electric: 1 record
  ZoneHVAC:EquipmentConnections: 1 record
  ZoneHVAC:EquipmentList: 1 record
  ZoneInfiltration:DesignFlowRate: 1 record

Save Epm in a new file

epm.save("my_idf.idf")

Table

A table is a collection of records of the same type.

Retrieve and explore the zone table.

zones = epm.Zone
print(zones)
print(f"\nzones: {len(zones)}\n")
for z in zones:
    print(z.name)
Table Zone (Zone)
  main zone

zones: 1

main zone

Queryset

A queryset is the result of a select query on a table.

# or a table
qs = epm.Zone.select(lambda x: x.name == "main zone")

# or another queryset
qs = qs.select(lambda x: x.name == "main zone")

print("records: ", qs)
print("\niter:")
for r in qs:
    print(r["name"])
print("\nget item:")
print(qs[0])
records:  <Queryset of Zone: 1 records>

iter:
main zone

get item:
Zone,
    main zone,                     ! Name
    0.0,                           ! Direction of Relative North
    0.0,                           ! X Origin
    0.0,                           ! Y Origin
    0.0,                           ! Z Origin
    1,                             ! Type
    1,                             ! Multiplier
    autocalculate,                 ! Ceiling Height
    autocalculate;                 ! Volume

Records

Get record

# from a table
building = epm.Building.one(lambda x: x.name == "Bldg")
# or from queryset
building = epm.Building.select(lambda x: x["name"] == "Bldg").one()

Add record

# add from a table
new_sch = epm.Schedule_Compact.add(
    name="Heating Setpoint Schedule - new[1]",
    schedule_type_limits_name="Any Number",
    field_1="Through: 12/31",
    field_2="For: AllDays",
    field_3="Until: 24:00,20.0"
)

print("found: ", epm.Schedule_Compact.one(lambda x: x.name == "heating setpoint schedule - new[1]") is new_sch)

# may also add extensible fields in afterwards add from table (only for extensible records)
new_sch = epm.Schedule_Compact.add(
    name="Heating Setpoint Schedule - new[2]",
    schedule_type_limits_name="Any Number"
)
new_sch.add_fields(
    "Through: 12/31",
    "For: AllDays",
    "Until: 24:00,20.0"
)
found:  True

Remove record

new_sch.delete()
print("found: ", len(epm.Schedule_Compact.select(lambda x: x.name == "heating setpoint schedule - new[2]")) == 1)
found:  False

Batch add (and remove)

schedules = [
    dict(
        name="Heating Setpoint Schedule - 0",
        schedule_type_limits_name="Any Number",
        field_1="Through: 12/31",
        field_2="For: AllDays",
        field_3="Until: 24:00,20.0"
    ),
    dict(
        name="Heating Setpoint Schedule - 1",
        schedule_type_limits_name="Any Number",
        field_1="Through: 12/31",
        field_2="For: AllDays",
        field_3="Until: 24:00,20.0"
    ),
    dict(
        name="Heating Setpoint Schedule - 2",
        schedule_type_limits_name="Any Number",
        field_1="Through: 12/31",
        field_2="For: AllDays",
        field_3="Until: 24:00,20.0"
    ),
]

# idf syntax
added = epm.Schedule_Compact.batch_add(schedules)
print("added:")
for a in added:
    print(a["name"])

added.delete()
added:
heating setpoint schedule - 0
heating setpoint schedule - 1
heating setpoint schedule - 2

Display info

print(building.get_info())
print("")
print(building)
Building (Building)
 0: Name (name)
    * default: NONE
    * retaincase:
 1: North Axis (north_axis)
    * default: 0.0
    * note: degrees from true North
    * type: real
    * units: deg
 2: Terrain (terrain)
    * default: Suburbs
    * key: Country; Suburbs; City; Ocean; Urban
    * note: Country=FlatOpenCountry | Suburbs=CountryTownsSuburbs | City=CityCenter | Ocean=body of water (5km) | Urban=Urban-Industrial-Forest
    * type: choice
 3: Loads Convergence Tolerance Value (loads_convergence_tolerance_value)
    * default: .04
    * maximum: .5
    * minimum>: 0.0
    * note: Loads Convergence Tolerance Value is a fraction of load
    * type: real
 4: Temperature Convergence Tolerance Value (temperature_convergence_tolerance_value)
    * default: .4
    * maximum: .5
    * minimum>: 0.0
    * type: real
    * units: deltaC
 5: Solar Distribution (solar_distribution)
    * default: FullExterior
    * key: MinimalShadowing; FullExterior; FullInteriorAndExterior; FullExteriorWithReflections; FullInteriorAndExteriorWithReflections
    * note: MinimalShadowing | FullExterior | FullInteriorAndExterior | FullExteriorWithReflections | FullInteriorAndExteriorWithReflections
    * type: choice
 6: Maximum Number of Warmup Days (maximum_number_of_warmup_days)
    * default: 25
    * minimum>: 0
    * note: EnergyPlus will only use as many warmup days as needed to reach convergence tolerance.; This field's value should NOT be set less than 25.
    * type: integer
 7: Minimum Number of Warmup Days (minimum_number_of_warmup_days)
    * default: 6
    * minimum>: 0
    * note: The minimum number of warmup days that produce enough temperature and flux history; to start EnergyPlus simulation for all reference buildings was suggested to be 6.; When this field is greater than the maximum warmup days defined previous field; the maximum number of warmup days will be reset to the minimum value entered here.; Warmup days will be set to be the value you entered when it is less than the default 6.
    * type: integer


Building,
    Bldg,                          ! Name
    0.0,                           ! North Axis
    suburbs,                       ! Terrain
    0.05,                          ! Loads Convergence Tolerance Value
    0.05,                          ! Temperature Convergence Tolerance Value
    minimalshadowing,              ! Solar Distribution
    30,                            ! Maximum Number of Warmup Days
    6;                             ! Minimum Number of Warmup Days

Get field value

print("name: ", building.name)
print("name: ", building["name"])
print("name: ", building[0])
name:  Bldg
name:  Bldg
name:  Bldg

Set basic field

old_name = building.terrain
print(f"old name: {old_name}")

building.terrain = "Downtown"
print(f"new name: {building.terrain}")

building.terrain = old_name
old name: suburbs
new name: downtown

Replace basic fields

sch = epm.Schedule_Compact.one(lambda x: x.name == "heating setpoint schedule")

sch.name = "Heating Setpoint Schedule"
sch.field_1 = "Through: 12/31"
sch[3] = "For: AllDays"  # index syntax

print(sch)

sch.name = "Heating Setpoint Schedule new_name"

print(sch)
Schedule:Compact,
    heating setpoint schedule,     ! Name
    any number,                    ! Schedule Type Limits Name
    through: 12/31,                ! Field 1
    for: alldays,                  ! Field 2
    until: 24:00,                  ! Field 3
    20.0;                          ! Field 4

Schedule:Compact,
    heating setpoint schedule new_name,    ! Name
    any number,                    ! Schedule Type Limits Name
    through: 12/31,                ! Field 1
    for: alldays,                  ! Field 2
    until: 24:00,                  ! Field 3
    20.0;                          ! Field 4

Set record fields

# work with setpoint record
setpoint = epm.ThermostatSetpoint_SingleHeating.one(lambda x: x.name == "heating setpoint")
print(setpoint)

# can set directly by name
setpoint.setpoint_temperature_schedule_name = "zone control type sched"
print(setpoint)

# or set record
new_sch = epm.Schedule_Compact.one(lambda x: x["name"] == "heating setpoint schedule new_name")
setpoint.setpoint_temperature_schedule_name = new_sch
print(setpoint)

# reset old value
setpoint.setpoint_temperature_schedule_name = sch
ThermostatSetpoint:SingleHeating,
    heating setpoint,              ! Name
    heating setpoint schedule new_name;    ! Setpoint Temperature Schedule Name

ThermostatSetpoint:SingleHeating,
    heating setpoint,              ! Name
    zone control type sched;       ! Setpoint Temperature Schedule Name

ThermostatSetpoint:SingleHeating,
    heating setpoint,              ! Name
    heating setpoint schedule new_name;    ! Setpoint Temperature Schedule Name

Add fields (only for extensibles)

sch.add_fields(
    "Until: 24:00",
    "25"
)
print(sch)
Schedule:Compact,
    heating setpoint schedule new_name,    ! Name
    any number,                    ! Schedule Type Limits Name
    through: 12/31,                ! Field 1
    for: alldays,                  ! Field 2
    until: 24:00,                  ! Field 3
    20.0,                          ! Field 4
    until: 24:00,                  ! Field 5
    25;                            ! Field 6

Case sensitivity

Table names

Table refs have a case, but getitem on idf is case insensitive

print("tables:")
print(epm.Zone)
print(epm.zOnE)
tables:
Table Zone (Zone)
  main zone
Table Zone (Zone)
  main zone

Record field keys

Record field keys are lower case with underscores instead of spaces

# todo: put an example with spaces
print("\nbuilding name:")
print(building.name)
print(building["name"])
building name:
Bldg
Bldg

Record field values

In Energy Plus, some record field values retain case (are case sensitive) and others don’t

print(building.get_info())
# => building name retains case, terrain doesn't
Building (Building)
 0: Name (name)
    * default: NONE
    * retaincase:
 1: North Axis (north_axis)
    * default: 0.0
    * note: degrees from true North
    * type: real
    * units: deg
 2: Terrain (terrain)
    * default: Suburbs
    * key: Country; Suburbs; City; Ocean; Urban
    * note: Country=FlatOpenCountry | Suburbs=CountryTownsSuburbs | City=CityCenter | Ocean=body of water (5km) | Urban=Urban-Industrial-Forest
    * type: choice
 3: Loads Convergence Tolerance Value (loads_convergence_tolerance_value)
    * default: .04
    * maximum: .5
    * minimum>: 0.0
    * note: Loads Convergence Tolerance Value is a fraction of load
    * type: real
 4: Temperature Convergence Tolerance Value (temperature_convergence_tolerance_value)
    * default: .4
    * maximum: .5
    * minimum>: 0.0
    * type: real
    * units: deltaC
 5: Solar Distribution (solar_distribution)
    * default: FullExterior
    * key: MinimalShadowing; FullExterior; FullInteriorAndExterior; FullExteriorWithReflections; FullInteriorAndExteriorWithReflections
    * note: MinimalShadowing | FullExterior | FullInteriorAndExterior | FullExteriorWithReflections | FullInteriorAndExteriorWithReflections
    * type: choice
 6: Maximum Number of Warmup Days (maximum_number_of_warmup_days)
    * default: 25
    * minimum>: 0
    * note: EnergyPlus will only use as many warmup days as needed to reach convergence tolerance.; This field's value should NOT be set less than 25.
    * type: integer
 7: Minimum Number of Warmup Days (minimum_number_of_warmup_days)
    * default: 6
    * minimum>: 0
    * note: The minimum number of warmup days that produce enough temperature and flux history; to start EnergyPlus simulation for all reference buildings was suggested to be 6.; When this field is greater than the maximum warmup days defined previous field; the maximum number of warmup days will be reset to the minimum value entered here.; Warmup days will be set to be the value you entered when it is less than the default 6.
    * type: integer

Note

Field values that don’t retain case are always forced to lowercase. Field values that retain case keep their case sensitive value.

building.name = "StaysCamelCase"
building.terrain = "Suburbs"  # will be set to lowercase
print(building)
Building,
    StaysCamelCase,                ! Name
    0.0,                           ! North Axis
    suburbs,                       ! Terrain
    0.05,                          ! Loads Convergence Tolerance Value
    0.05,                          ! Temperature Convergence Tolerance Value
    minimalshadowing,              ! Solar Distribution
    30,                            ! Maximum Number of Warmup Days
    6;                             ! Minimum Number of Warmup Days

Note

Don’t forget these rules when filtering

print("retains, case not respected:", len(epm.Building.select(lambda x: x.name == "stayscamelcase")))  # not ok
print("retains, case respected:", len(epm.Building.select(lambda x: x.name == "StaysCamelCase")))  # ok
print("doesn't retain, uppercase: ", len(epm.Building.select(lambda x: x.terrain == "Suburbs")))  # not ok
print("doesn't retain, lowercase: ", len(epm.Building.select(lambda x: x.terrain == "suburbs")))  # ok
retains, case not respected: 0
retains, case respected: 1
doesn't retain, uppercase:  0
doesn't retain, lowercase:  1