Custom callbacks
Sinergym lets you register custom EnergyPlus Python API runtime callbacks so your own Python code runs at chosen points in the EnergyPlus timestep loop. This is useful for logging or custom simulation control logic.
By default, Sinergym uses callbacks to manage the control flow within the simulation layer. This mechanism allows users to attach any additional processes in parallel to any of the callback points provided by the EnergyPlus API, granting full flexibility to implement custom logic tailored to specific problems. A step-by-step Jupyter walkthrough is available in the example notebook (also listed under Examples in the documentation sidebar).
Overview
When it takes effect: After you call
register_callback(), your functions are queued until the next run. They are actually connected to EnergyPlus when the simulation starts, i.e. when you callreset().Episodes: Once registered, your callbacks stay in place for later episodes until you remove them or create a fresh environment. Sinergym’s own callbacks (observations, actions, context, warmup, progress) are unrelated; clearing yours does not touch them.
Removing callbacks:
clear_callbacks()drops every user-registered callback you had added.
What to call on the environment
Typical Gymnasium usage wraps the base environment, so use get_wrapper_attr to reach these (or call them directly on an unwrapped EplusEnv):
Member |
Role |
|---|---|
|
Queue a callback for the given EnergyPlus callback point. |
|
Remove all user-registered callbacks from the internal registry. |
|
Read-only view: mapping from callback point name to a list of registered function names (for debugging and introspection). |
Callback function signature
For most callback points, the function must accept the EnergyPlus state handle:
def my_callback(state):
...
For callback_user_defined_component_model, the signature is still callback_func(state); you must also pass component_program_name so EnergyPlus can associate the callback with the correct UserDefined component model in the IDF (see below).
Valid callback_name values
The string must match one of EnergyPlus’s runtime callback registration names (same names as in the EnergyPlus Python API):
callback_begin_new_environmentcallback_after_new_environment_warmup_completecallback_begin_zone_timestep_before_init_heat_balancecallback_begin_zone_timestep_after_init_heat_balancecallback_begin_zone_timestep_before_set_current_weathercallback_begin_system_timestep_before_predictorcallback_end_zone_timestep_before_zone_reportingcallback_end_zone_timestep_after_zone_reportingcallback_end_system_timestep_before_hvac_reportingcallback_end_system_timestep_after_hvac_reportingcallback_inside_system_iteration_loopcallback_after_predictor_before_hvac_managerscallback_after_predictor_after_hvac_managerscallback_end_zone_sizingcallback_end_system_sizingcallback_unitary_system_sizingcallback_after_component_get_inputcallback_user_defined_component_modelcallback_register_external_hvac_managercallback_messagecallback_progress
You can register multiple Python callables for the same callback_name by calling register_callback several times.
UserDefined component model (callback_user_defined_component_model)
If callback_name is callback_user_defined_component_model, you must set component_program_name to the program name of the UserDefined component model as declared in the IDF (for example 'MyUserDefinedCoil'). For every other callback name, component_program_name must be None.
Reading building or simulation values inside a callback
Your callback receives EnergyPlus’s state handle. To read variables you have configured on the environment, get energyplus_simulator from the env and use exchange with var_handlers (or other handles you set up). The example below registers a callback and reads a zone temperature:
def my_custom_callback(state):
simulator = env.get_wrapper_attr("energyplus_simulator")
if simulator.var_handlers and "Zone_Temperature" in simulator.var_handlers:
temp = simulator.exchange.get_variable_value(
state,
simulator.var_handlers["Zone_Temperature"],
)
print(f"Zone temperature: {temp}")
env.get_wrapper_attr("register_callback")(
"callback_begin_system_timestep_before_predictor",
my_custom_callback,
)
UserDefined registration:
def user_defined_callback(state):
...
env.get_wrapper_attr("register_callback")(
"callback_user_defined_component_model",
user_defined_callback,
component_program_name="MyUserDefinedCoil",
)
Errors
Invalid callback_name, missing component_program_name for UserDefined callbacks, or passing component_program_name for any other callback raise ValueError. See the API reference for full signatures.
Example notebook
The notebook Custom callback functions in Sinergym walks through creating an environment, registering EnergyPlus runtime callbacks, and reading values from the simulator inside a callback. It lives in the repository as examples/custom_callbacks.ipynb and appears in the toctree under Examples alongside the other notebooks.