Logging unused variables

Custom logger wrappers in Sinergym enable recording any metric based on interaction data with the environment (as shown here). However, there may be cases where you want to monitor data directly from the simulator.

Until version v3.3.2 of Sinergym, all variables monitored using the LoggerWrapper had to be present in the observation space. This represents a drawback when we want to monitor certain variables of the simulation that are not used for control (i.e., that are not present in the environment’s observation space).

Including extra variables that are not directly part of the observation space requires certain internal changes that break the minimalist structure of the classes and EnergyPlus API usage that make up the tool.

This notebook explains the correct way to do it, which is possible since Sinergym v3.3.3. It involves the use of the ReduceObservationWrapper in combination with the LoggerWrapper.

The idea is to define all the variables to be observed, whether they are part of the final observation space or not, and monitor everything with the LoggerWrapper, to later add a layer that removes the desired variables from the observation space (just when they are already being monitored, which is our goal).

[5]:
import gymnasium as gym
import numpy as np

import sinergym
from sinergym.utils.wrappers import (
    LoggerWrapper,
    NormalizeAction,
    NormalizeObservation,
    ReduceObservationWrapper,
    CSVLogger,
    WandBLogger)

# Creating environment and applying wrappers for normalization and logging
env = gym.make('Eplus-5zone-hot-continuous-stochastic-v1')
env = NormalizeAction(env)
env = NormalizeObservation(env)
env = LoggerWrapper(env)
print('###########################################################################')
print('Old observation space shape: ', env.observation_space.shape[0])
print('Old observation variables: ',
      env.get_wrapper_attr('observation_variables'))
print('###########################################################################')

# Here we can add the CSVLogger or WandBLogger
env = CSVLogger(env)
# env = WandBLogger(env,
#                   entity='alejandro-campoy',
#                   project_name='test-project',
#                   run_name='test-reduction')

env = ReduceObservationWrapper(env, obs_reduction=[
                               'outdoor_temperature', 'outdoor_humidity', 'air_temperature'])
print('###########################################################################')
print('Wrapped observation space shape: ', env.observation_space.shape[0])
print('Wrapped observation variables: ',
      env.get_wrapper_attr('observation_variables'))
print('###########################################################################')
#==============================================================================================#
[ENVIRONMENT] (INFO) : Creating Gymnasium environment.
[ENVIRONMENT] (INFO) : Name: Eplus-5zone-hot-continuous-stochastic-v1
#==============================================================================================#
[MODEL] (INFO) : Working directory created: /workspaces/sinergym/examples/Eplus-5zone-hot-continuous-stochastic-v1-res1
[MODEL] (INFO) : Model Config is correct.
[MODEL] (INFO) : Building model Output:Variable updated with defined variable names.
[MODEL] (INFO) : Updated building model Output:Meter with meter names.
[MODEL] (INFO) : Runperiod established.
[MODEL] (INFO) : Episode length (seconds): 31536000.0
[MODEL] (INFO) : timestep size (seconds): 900.0
[MODEL] (INFO) : timesteps per episode: 35040
[REWARD] (INFO) : Reward function initialized.
[ENVIRONMENT] (INFO) : Environment created successfully.
[WRAPPER NormalizeAction] (INFO) : New normalized action space: Box(-1.0, 1.0, (2,), float32)
[WRAPPER NormalizeAction] (INFO) : Wrapper initialized.
[WRAPPER NormalizeObservation] (INFO) : Wrapper initialized.
[WRAPPER LoggerWrapper] (INFO) : Wrapper initialized.
###########################################################################
Old observation space shape:  17
Old observation variables:  ['month', 'day_of_month', 'hour', 'outdoor_temperature', 'outdoor_humidity', 'wind_speed', 'wind_direction', 'diffuse_solar_radiation', 'direct_solar_radiation', 'htg_setpoint', 'clg_setpoint', 'air_temperature', 'air_humidity', 'people_occupant', 'co2_emission', 'HVAC_electricity_demand_rate', 'total_electricity_HVAC']
###########################################################################
[WRAPPER CSVLogger] (INFO) : Wrapper initialized.
[WRAPPER ReduceObservationWrapper] (INFO) : Wrapper initialized.
###########################################################################
Wrapped observation space shape:  14
Wrapped observation variables:  ['month', 'day_of_month', 'hour', 'wind_speed', 'wind_direction', 'diffuse_solar_radiation', 'direct_solar_radiation', 'htg_setpoint', 'clg_setpoint', 'air_humidity', 'people_occupant', 'co2_emission', 'HVAC_electricity_demand_rate', 'total_electricity_HVAC']
###########################################################################

The order of the wrappers is important! By applying normalization first, for example, we ensure that this transformation is subsequently monitored.

As we apply the logger before reducing the observation space, we also record these variables before they are removed from the observations. If we use the logger wrapper at the end, these variables would not be monitored. This can be verified by reviewing the CSV files generated for the observation variables.

Note that even if we remove a variable that is used in the reward function, as this value is used in the core of the environment (before any wrapper), it still works as expected.

[7]:
env.close()
[WRAPPER CSVLogger] (INFO) : Environment closed, data updated in monitor and progress.csv.
[WRAPPER NormalizeObservation] (INFO) : Normalization calibration saved.
[ENVIRONMENT] (INFO) : Environment closed. [Eplus-5zone-hot-continuous-stochastic-v1]
Simulation Progress [Episode 1]: 100%|██████████| 100/100 [00:00<00:00, 5946.16%/s, 100% completed]