23. Logger Wrapper personalization/configuration

In this notebook, we’ll demonstrate how to customize the logger wrapper defined by Sinergym.

[1]:
import gymnasium as gym
import numpy as np
import sinergym
from sinergym.utils.wrappers import (BaseLoggerWrapper, LoggerWrapper, CSVLogger, WandBLogger)

23.1. Step 1: Inherit and complete abstract methods from BaseLoggerWrapper

We simply need to inherit from this class and define both the custom metrics we desire and the summary metrics that are calculated from the logger data for each simulated episode. Additionally, you can change the back-end where the information is stored by modifying the logger_class, instead of using the default one implemented by Sinergym. Sinergym use this structure to implement its default logger wrapper called LoggerWrapper, an example is shown below:

[3]:
from sinergym.utils.logger import LoggerStorage, TerminalLogger
from sinergym.utils.constants import LOG_WRAPPERS_LEVEL
from typing import Any, Dict, Optional, Union, List, Callable

class CustomLoggerWrapper(BaseLoggerWrapper):

    logger = TerminalLogger().getLogger(name='WRAPPER CustomLoggerWrapper',
                                        level=LOG_WRAPPERS_LEVEL)

    def __init__(
            self,
            env: gym.Env,
            logger_class : Callable = LoggerStorage):

        super(CustomLoggerWrapper, self).__init__(env, logger_class)
        # DEFINE CUSTOM VARIABLES AND SUMMARY VARIABLES
        self.custom_variables = ['custom_variable1','custom_variable2']
        self.summary_variables = ['episode_num','double_mean_reward','half_power_demand']

    # DEFINE ABSTRACT METHODS FOR METRICS CALCULATION

    def calculate_custom_metrics(self,
                                 obs: np.ndarray,
                                 action: Union[int, np.ndarray],
                                 reward: float,
                                 info: Dict[str, Any],
                                 terminated: bool,
                                 truncated: bool):
        # Variables combining information
        return [obs[0]*2,obs[-1]+reward]

    def get_episode_summary(self) -> Dict[str, float]:
        # Get information from logger
        power_demands = [info['total_power_demand']
                         for info in self.data_logger.infos]

        # Data summary
        data_summary = {
            'episode_num': self.get_wrapper_attr('episode'),
            'double_mean_reward': np.mean(self.data_logger.rewards)*2,
            'half_power_demand': np.mean(power_demands)/2,
        }
        return data_summary

If you wish to make deeper, more fundamental changes to the logging system, you can always create your own BaseLoggerWrapper. However, this requires a more advanced understanding of the tool.

23.2. Step 2: Use CustomLoggerWrapper to save information

Now we can combine this new wrapper with any of Sinergym’s output systems, and the data will be saved correctly. For instance, let’s combine it with CSVLogger to save the data in CSV files. However, it can also be used with WandBLogger or any other logger created by the user.

[4]:
env=gym.make('Eplus-demo-v1')
env=CustomLoggerWrapper(env)
env=CSVLogger(env)
#==============================================================================================#
[ENVIRONMENT] (INFO) : Creating Gymnasium environment... [demo-v1]
#==============================================================================================#
[MODELING] (INFO) : Experiment working directory created [/workspaces/sinergym/examples/Eplus-env-demo-v1-res1]
[MODELING] (INFO) : Model Config is correct.
[MODELING] (INFO) : Updated building model with whole Output:Variable available names
[MODELING] (INFO) : Updated building model with whole Output:Meter available names
[MODELING] (INFO) : Extra config: runperiod updated to {'apply_weekend_holiday_rule': 'No', 'begin_day_of_month': 1, 'begin_month': 1, 'begin_year': 1991, 'day_of_week_for_start_day': 'Monday', 'end_day_of_month': 1, 'end_month': 3, 'end_year': 1991, 'use_weather_file_daylight_saving_period': 'Yes', 'use_weather_file_holidays_and_special_days': 'Yes', 'use_weather_file_rain_indicators': 'Yes', 'use_weather_file_snow_indicators': 'Yes'}
[MODELING] (INFO) : Updated episode length (seconds): 5184000.0
[MODELING] (INFO) : Updated timestep size (seconds): 3600.0
[MODELING] (INFO) : Updated timesteps per episode: 1440
[MODELING] (INFO) : runperiod established: {'start_day': 1, 'start_month': 1, 'start_year': 1991, 'end_day': 1, 'end_month': 3, 'end_year': 1991, 'start_weekday': 0, 'n_steps_per_hour': 1}
[MODELING] (INFO) : Episode length (seconds): 5184000.0
[MODELING] (INFO) : timestep size (seconds): 3600.0
[MODELING] (INFO) : timesteps per episode: 1441
[REWARD] (INFO) : Reward function initialized.
[ENVIRONMENT] (INFO) : Environment demo-v1 created successfully.
[WRAPPER CSVLogger] (INFO) : Wrapper initialized.

Then, if we run the environment (with a random agent, for example), we can see how the files are correctly written to the Sinergym output. progress.csv contains the summary variables we have defined, and within the monitor folder of each episode, a new CSV file named custom_metrics.csv appears, capturing the new metrics we want to track.

[5]:
for i in range(1):
    obs, info = env.reset()
    rewards = []
    truncated = terminated = False
    current_month = 0
    while not (terminated or truncated):
        a = env.action_space.sample()
        obs, reward, terminated, truncated, info = env.step(a)
        rewards.append(reward)
        if info['month'] != current_month:  # display results every month
            current_month = info['month']
            print('Reward: ', sum(rewards), info)
    print('Episode ', i, 'Mean reward: ', np.mean(
        rewards), 'Cumulative reward: ', sum(rewards))
env.close()
#----------------------------------------------------------------------------------------------#
[ENVIRONMENT] (INFO) : Starting a new episode... [demo-v1] [Episode 1]
#----------------------------------------------------------------------------------------------#
[MODELING] (INFO) : Episode directory created [/workspaces/sinergym/examples/Eplus-env-demo-v1-res1/Eplus-env-sub_run1]
[MODELING] (INFO) : Weather file USA_PA_Pittsburgh-Allegheny.County.AP.725205_TMY3.epw used.
[MODELING] (INFO) : Adapting weather to building model. [USA_PA_Pittsburgh-Allegheny.County.AP.725205_TMY3.epw]
[ENVIRONMENT] (INFO) : Saving episode output path... [/workspaces/sinergym/examples/Eplus-env-demo-v1-res1/Eplus-env-sub_run1/output]
[SIMULATOR] (INFO) : Running EnergyPlus with args: ['-w', '/workspaces/sinergym/examples/Eplus-env-demo-v1-res1/Eplus-env-sub_run1/USA_PA_Pittsburgh-Allegheny.County.AP.725205_TMY3.epw', '-d', '/workspaces/sinergym/examples/Eplus-env-demo-v1-res1/Eplus-env-sub_run1/output', '/workspaces/sinergym/examples/Eplus-env-demo-v1-res1/Eplus-env-sub_run1/5ZoneAutoDXVAV.epJSON']
[ENVIRONMENT] (INFO) : Episode 1 started.
[SIMULATOR] (INFO) : handlers initialized.
[SIMULATOR] (INFO) : handlers are ready.
[SIMULATOR] (INFO) : System is ready.
Reward:  -43.96143518328036 {'time_elapsed(hours)': 2.5, 'month': 1, 'day': 1, 'hour': 1, 'is_raining': False, 'action': array([20.5672 , 28.12763], dtype=float32), 'timestep': 2, 'reward': -43.96143518328036, 'energy_term': -43.67932315835093, 'comfort_term': -0.2821120249294271, 'reward_weight': 0.5, 'abs_energy_penalty': -87.35864631670186, 'abs_comfort_penalty': -0.5642240498588542, 'total_power_demand': 87.35864631670186, 'total_temperature_violation': 0.5642240498588542}
Reward:  -1601995.9857013992 {'time_elapsed(hours)': 745.125, 'month': 2, 'day': 1, 'hour': 0, 'is_raining': False, 'action': array([16.065548, 26.472868], dtype=float32), 'timestep': 745, 'reward': -1142.6355897304295, 'energy_term': -1142.6355897304295, 'comfort_term': 0.0, 'reward_weight': 0.5, 'abs_energy_penalty': -2285.271179460859, 'abs_comfort_penalty': 0, 'total_power_demand': 2285.271179460859, 'total_temperature_violation': 0.0}
Reward:  -2771086.1216104105 {'time_elapsed(hours)': 1417.3333333333333, 'month': 3, 'day': 1, 'hour': 0, 'is_raining': False, 'action': array([19.876032, 24.112223], dtype=float32), 'timestep': 1417, 'reward': -43.67932315835093, 'energy_term': -43.67932315835093, 'comfort_term': 0.0, 'reward_weight': 0.5, 'abs_energy_penalty': -87.35864631670186, 'abs_comfort_penalty': 0, 'total_power_demand': 87.35864631670186, 'total_temperature_violation': 0.0}

Episode  0 Mean reward:  -1935.9258972334976 Cumulative reward:  -2787733.292016237
[WRAPPER CSVLogger] (INFO) : Environment closed, data updated in monitor and progress.csv.
[ENVIRONMENT] (INFO) : Environment closed. [demo-v1]