-
Notifications
You must be signed in to change notification settings - Fork 461
Description
Issue overview
The EnergyPlus Python API produces significantly different simulation results when used to dynamically control window constructions at runtime, compared to achieving the same control logic with Energy Management System (EMS) objects. Both approaches were implemented at the BeginTimestepBeforePredictor calling point and always actuate all of the windows to a specific window construction type.
This discrepancy suggests that the Python API's set_actuator_value method for window construction objects might not be correctly influencing the simulation engine's calculations in the same way EMS does, leading to inaccurate energy consumption predictions.
Test Cases Performed:
The following cases were tested using the reference medium-sized office model with added two window construction materials: gsA and gsB, using WindowMaterial:SimpleGlazingSystem. A controller was implemented to change all window constructions to gsA.
-
Case 1: All windows initially set to gsA in FenestrationSurface:Detailed. No runtime controller.
-
Case 2: All windows initially set to gsB in FenestrationSurface:Detailed. No runtime controller.
-
Case 3: All windows initially set to gsB in FenestrationSurface:Detailed. An EMS controller is used to always set the window construction to gsA during runtime.
-
Case 4: All windows initially set to gsB in FenestrationSurface:Detailed. The Python API is used to always set the window construction to gsA during runtime.
-
Case 5: All windows initially set to gsA in FenestrationSurface:Detailed. The Python API is used to always set the window construction to gsA during runtime.
Observed Output (for "PERIMETER_BOT_ZN_1 VAV BOX REHEAT COIL:Heating Coil Heating Rate"):
Case 1: ≈78 mW
Case 2: ≈28 mW
Case 3: ≈77 mW
Case 4: ≈54 mW
Case 5: ≈78 mW
Steps to Reproduce:
Prepare IDF: Obtain the reference medium-size office model. Define two distinct Construction objects (e.g., gsA and gsB)
Construction, gsA, gsA_mat;Construction,
gsB,
gsB_mat;
WindowMaterial:SimpleGlazingSystem,
gsA_mat,
5.9,
0.98;
WindowMaterial:SimpleGlazingSystem,
gsB_mat,
1.9,
0.59;
Implement EMS Controller (for Case 3)
for all windows:
EnergyManagementSystem:ConstructionIndexVariable,
gsA, !- Name
gsA; !- Construction Object Name
EnergyManagementSystem:ConstructionIndexVariable,
gsB, !- Name
gsB; !- Construction Object Name
EnergyManagementSystem:Actuator,
Perimeter_bot_ZN_1_Wall_South_Window_Construct, !- Name
Perimeter_bot_ZN_1_Wall_South_Window, !- Actuated Component Unique Name
Surface, !- Actuated Component Type
Construction State; !- Actuated Component Control Type
EnergyManagementSystem:ProgramCallingManager,
Perimeter_bot_ZN_1_Wall_South_Window_emulator, !- Name
BeginTimestepBeforePredictor, !- EnergyPlus Model Calling Point
Perimeter_bot_ZN_1_Wall_South_Window_Control; !- Program Name 1
EnergyManagementSystem:Program,
Perimeter_bot_ZN_1_Wall_South_Window_Control, !- Name
Set Perimeter_bot_ZN_1_Wall_South_Window_Construct = gsA; !- Program Line
Implement Python API Controller (for Cases 4 & 5)
from pyenergyplus.api import EnergyPlusAPI
one_time = True
def controller(state):
global one_time
global gsa_construction_handle
global Perimeter_bot_ZN_1_Wall_South_Window_actuator_handle
global Perimeter_bot_ZN_2_Wall_East_Window_actuator_handle
global Perimeter_bot_ZN_3_Wall_North_Window_actuator_handle
global Perimeter_bot_ZN_4_Wall_West_Window_actuator_handle
global Perimeter_mid_ZN_1_Wall_South_Window_actuator_handle
global Perimeter_mid_ZN_2_Wall_East_Window_actuator_handle
global Perimeter_mid_ZN_3_Wall_North_Window_actuator_handle
global Perimeter_mid_ZN_4_Wall_West_Window_actuator_handle
global Perimeter_top_ZN_1_Wall_South_Window_actuator_handle
global Perimeter_top_ZN_2_Wall_East_Window_actuator_handle
global Perimeter_top_ZN_3_Wall_North_Window_actuator_handle
global Perimeter_top_ZN_4_Wall_West_Window_actuator_handle
if not api.exchange.api_data_fully_ready(state):
return
# Get the handles
if one_time:
one_time = False
gsa_construction_handle = api.exchange.get_construction_handle(state, "gsA")
Perimeter_bot_ZN_1_Wall_South_Window_actuator_handle = (
api.exchange.get_actuator_handle(
state,
"Surface",
"Construction State",
"Perimeter_bot_ZN_1_Wall_South_Window",
)
)
Perimeter_bot_ZN_2_Wall_East_Window_actuator_handle = (
api.exchange.get_actuator_handle(
state,
"Surface",
"Construction State",
"Perimeter_bot_ZN_2_Wall_East_Window",
)
)
Perimeter_bot_ZN_3_Wall_North_Window_actuator_handle = (
api.exchange.get_actuator_handle(
state,
"Surface",
"Construction State",
"Perimeter_bot_ZN_3_Wall_North_Window",
)
)
Perimeter_bot_ZN_4_Wall_West_Window_actuator_handle = (
api.exchange.get_actuator_handle(
state,
"Surface",
"Construction State",
"Perimeter_bot_ZN_4_Wall_West_Window",
)
)
Perimeter_mid_ZN_1_Wall_South_Window_actuator_handle = (
api.exchange.get_actuator_handle(
state,
"Surface",
"Construction State",
"Perimeter_mid_ZN_1_Wall_South_Window",
)
)
Perimeter_mid_ZN_2_Wall_East_Window_actuator_handle = (
api.exchange.get_actuator_handle(
state,
"Surface",
"Construction State",
"Perimeter_mid_ZN_2_Wall_East_Window",
)
)
Perimeter_mid_ZN_3_Wall_North_Window_actuator_handle = (
api.exchange.get_actuator_handle(
state,
"Surface",
"Construction State",
"Perimeter_mid_ZN_3_Wall_North_Window",
)
)
Perimeter_mid_ZN_4_Wall_West_Window_actuator_handle = (
api.exchange.get_actuator_handle(
state,
"Surface",
"Construction State",
"Perimeter_mid_ZN_4_Wall_West_Window",
)
)
Perimeter_top_ZN_1_Wall_South_Window_actuator_handle = (
api.exchange.get_actuator_handle(
state,
"Surface",
"Construction State",
"Perimeter_top_ZN_1_Wall_South_Window",
)
)
Perimeter_top_ZN_2_Wall_East_Window_actuator_handle = (
api.exchange.get_actuator_handle(
state,
"Surface",
"Construction State",
"Perimeter_top_ZN_2_Wall_East_Window",
)
)
Perimeter_top_ZN_3_Wall_North_Window_actuator_handle = (
api.exchange.get_actuator_handle(
state,
"Surface",
"Construction State",
"Perimeter_top_ZN_3_Wall_North_Window",
)
)
Perimeter_top_ZN_4_Wall_West_Window_actuator_handle = (
api.exchange.get_actuator_handle(
state,
"Surface",
"Construction State",
"Perimeter_top_ZN_4_Wall_West_Window",
)
)
api.exchange.set_actuator_value(
state,
Perimeter_bot_ZN_1_Wall_South_Window_actuator_handle,
gsa_construction_handle,
)
api.exchange.set_actuator_value(
state,
Perimeter_bot_ZN_2_Wall_East_Window_actuator_handle,
gsa_construction_handle,
)
api.exchange.set_actuator_value(
state,
Perimeter_bot_ZN_3_Wall_North_Window_actuator_handle,
gsa_construction_handle,
)
api.exchange.set_actuator_value(
state,
Perimeter_bot_ZN_4_Wall_West_Window_actuator_handle,
gsa_construction_handle,
)
api.exchange.set_actuator_value(
state,
Perimeter_mid_ZN_1_Wall_South_Window_actuator_handle,
gsa_construction_handle,
)
api.exchange.set_actuator_value(
state,
Perimeter_mid_ZN_2_Wall_East_Window_actuator_handle,
gsa_construction_handle,
)
api.exchange.set_actuator_value(
state,
Perimeter_mid_ZN_3_Wall_North_Window_actuator_handle,
gsa_construction_handle,
)
api.exchange.set_actuator_value(
state,
Perimeter_mid_ZN_4_Wall_West_Window_actuator_handle,
gsa_construction_handle,
)
api.exchange.set_actuator_value(
state,
Perimeter_top_ZN_1_Wall_South_Window_actuator_handle,
gsa_construction_handle,
)
api.exchange.set_actuator_value(
state,
Perimeter_top_ZN_2_Wall_East_Window_actuator_handle,
gsa_construction_handle,
)
api.exchange.set_actuator_value(
state,
Perimeter_top_ZN_3_Wall_North_Window_actuator_handle,
gsa_construction_handle,
)
api.exchange.set_actuator_value(
state,
Perimeter_top_ZN_4_Wall_West_Window_actuator_handle,
gsa_construction_handle,
)
api = EnergyPlusAPI()
state = api.state_manager.new_state()
api.runtime.callback_begin_system_timestep_before_predictor(state, controller)
api.runtime.run_energyplus(
state,
[
"-a",
"-w",
"Data/weather/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.epw",
"-p",
"medoffice_api_ctrlalwaysA_initB",
"Data/idf/test/RefBldgMediumOfficeNew2004_Chicago.idf",
],
)
Expected Behavior:
-
Cases 1, 3, 4, and 5 should produce very similar results for the "Heating Coil Heating Rate" since they all effectively simulate with gsA throughout the year and the controller should override the initial window construction materials defined in FenestrationSurface:Detailed(Case 1 is baseline A, Case 3 forces A via EMS, Case 4 and 5 forces A via Python API).
-
Case 3 and 4 should also produce similar results, as the Python API is expected to provide equivalent runtime control capabilities to EMS.
Actual Behavior:
-
Cases 1, 3, and 5 show similar heating rates (≈78 mW,≈77 mW,≈78 mW).
-
Case 2, which uses gsB throughout, shows a significantly different heating rate (≈28 mW). This is expected as gsB has different properties.
-
Crucially, Case 4, which uses the Python API to force gsA at runtime, reports a heating rate of ≈54 mW. This value is distinct from Cases 1, 3, and 5, and also from Case 2, indicating that the Python API's runtime control of window construction is not behaving as expected or as equivalently as EMS.
-
Difference in Error Files (eplusout.err): Unlike EMS, the Python API's intervention appears to disrupt the solver's ability to converge as shown in Maximum iterations exceeded and associated oscillating values in Case 4's error log, leading to the observed differences in heating coil rates.
Environment:
EnergyPlus Version: 23.2.0
Python Version: 3.12.6
Operating System: macOS Sonoma 14.6
Additional Notes:
-
The fact that Case 5 (Python API forcing gsA when initially gsA) matches Case 1 (baseline gsA) but differs when it attempts to change the value from a different initial state (Case 4) suggests the change is not fully or correctly propagated to the simulation engine's calculations.
-
This issue is critical for applications requiring dynamic control of building elements via the Python API, as it undermines the reliability of simulation results when compared to native EMS capabilities.
Operating System (Multiple choices)
MacOS
Operating System Version
Sonoma 14.6
Version of EnergyPlus
23.2.0
Unmethours link or helpdesk ticket number
N/A
Defect file
RefBldgMediumOfficeNew2004_Chicago_ems.in.idf.txt
RefBldgMediumOfficeNew2004_Chicago.in.idf.txt