Skip to content

Commit

Permalink
(v3.7.3) - Weather variability config logging in CSVLogger wrapper (#466
Browse files Browse the repository at this point in the history
)

* Modeling: Delete OU configuration in episode folder, save episode OU parameters as attribute

* CSVLogger: Save OU parameters in CSV for each episode

* Tests fixed

* Documentation: Update Sinergym output graph image and documentation section

* Update Sinergym version from 3.7.2 to 3.7.3
  • Loading branch information
AlejandroCN7 authored Jan 27, 2025
1 parent bc874d3 commit 2902239
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 24 deletions.
Binary file modified docs/source/_static/output_structure.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions docs/source/pages/output.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ The contents of this root output directory include the results of the simulation

- ``progress.csv``. This file contains information about general simulation results. Each row contains episode information registering relevant data such as mean power consumption, rewards or comfort penalties. This file is only available when the environment has been wrapped with a ``LoggerWrapper`` and ``CSVLogger`` (see :ref:`Logger Wrappers` for more information). The structure of this file is defined by the ``LoggerWrapper`` class.

- ``weather_variability_config.json``. This file contains the configuration of the weather variability for each episode. It is only created when the environment has been wrapped with ``LoggerWrapper`` and ``CSVLogger``. It is very useful when you are using ranges in weather variability paramters (more information in :ref:`Weather variability`)

- ``data_available.txt``. It is generated when the *EnergyPlus* API initializes all callbacks and handlers for the simulation. In this file, you can find all the available components of the building model, such as actuators, schedulers, meters, variables, internal variables, etc.

- ``mean.txt`` and ``var.txt``. These files contain the mean and variation values for calibration of normalization in observation space if wrapper ``NormalizeObservation`` is used (see :ref:`NormalizeObservation`).
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
package-mode = true
name = "sinergym"

version = "3.7.2"
version = "3.7.3"
description = "Sinergym provides a Gymnasium-based interface to interact with building simulations. This allows control in simulation time through custom controllers, including reinforcement learning agents"
license = "MIT"

Expand Down
14 changes: 5 additions & 9 deletions sinergym/config/modeling.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ def __init__(
# Weather data (epw.weather object)
self.weather_data = Weather()
self.weather_data.read(self._weather_path)
# Weather variability if exists
self.weather_variability_config = None

# ----------------------------- Other attributes ----------------------------- #

Expand Down Expand Up @@ -340,29 +342,23 @@ def apply_weather_variability(

# Check if there are ranges specified in params and get a random
# value
variability_config = {
self.weather_variability_config = {
weather_var: tuple(
np.random.uniform(param[0], param[1]) if isinstance(param, tuple) else param
for param in params
)
for weather_var, params in weather_variability.items()
}

# Write variability_config to a JSON file for episode
config_path = f"{
self.episode_path}/weather_variability_config.json"
with open(config_path, 'w') as f:
json.dump(variability_config, f)

# Apply Ornstein-Uhlenbeck process to weather data
weather_data_mod.dataframe = ornstein_uhlenbeck_process(
data=self.weather_data.dataframe,
variability_config=variability_config)
variability_config=self.weather_variability_config)

self.logger.info(
'Weather noise applied in columns: {}'.format(
list(
variability_config.keys())))
self.weather_variability_config.keys())))

# Modify filename to reflect noise addition
filename = f"{filename.split('.epw')[0]}_OU_Noise.epw"
Expand Down
28 changes: 28 additions & 0 deletions sinergym/utils/wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1478,6 +1478,9 @@ def __init__(
self.progress_file_path = self.get_wrapper_attr(
'workspace_path') + '/progress.csv'

self.weather_variability_config_path = self.get_wrapper_attr(
'workspace_path') + '/weather_variability_config.csv'

self.logger.info('Wrapper initialized.')

def reset(self,
Expand Down Expand Up @@ -1602,6 +1605,31 @@ def dump_log_files(self) -> None:
writer.writerow(list(episode_summary.keys()))
writer.writerow(list(episode_summary.values()))

# Update weather_variability_config if exists
modeling = self.get_wrapper_attr('model')
config_path = self.get_wrapper_attr(
'weather_variability_config_path')

if modeling.weather_variability_config is not None:
with open(config_path, 'a+') as f:
writer = csv.writer(f)

# If first episode, write header
if self.get_wrapper_attr('episode') == 1:
header = ['episode_num'] + [
f"{var_name}_{var_param}"
for var_name in list(modeling.weather_variability_config.keys())
for var_param in ['sigma', 'mu', 'tau']
]
writer.writerow(header)

# Write OU params for each weather variable
var_values = list()
var_values = [
self.get_wrapper_attr('episode')] + [
value for params in modeling.weather_variability_config.values() for value in params]
writer.writerow(var_values)


# ---------------------------------------------------------------------------- #

Expand Down
2 changes: 1 addition & 1 deletion sinergym/version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.7.2
3.7.3
10 changes: 0 additions & 10 deletions tests/test_modeling.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,23 +200,13 @@ def test_apply_weather_variability(model_5zone):
original_filename = model_5zone._weather_path.split('/')[-1]
path_filename = path_result.split('/')[-1]
assert original_filename == path_filename
# It shouldn't generate variability config
# It should generate a json file
assert not os.path.exists(
model_5zone.episode_path +
'/weather_variability_config.json')

# Check with a variation
weather_variability = {
'Dry Bulb Temperature': (1.0, 0.0, 24.0),
'Wind Speed': (3.0, 0.0, 35.0)
}
path_result = model_5zone.apply_weather_variability(
weather_variability=weather_variability)
# It should generate weather variability config file
assert os.path.exists(
model_5zone.episode_path +
'/weather_variability_config.json')
filename = model_5zone._weather_path.split('/')[-1]
filename = filename.split('.epw')[0]
filename += '_OU_Noise.epw'
Expand Down
22 changes: 19 additions & 3 deletions tests/test_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -850,9 +850,14 @@ def test_custom_loggers(env_demo, custom_logger_wrapper):
assert logger.interactions == 0


def test_CSVlogger_wrapper(env_demo):

env = CSVLogger(env=LoggerWrapper(env=NormalizeObservation(env=env_demo)))
@pytest.mark.parametrize('env_name',
[('env_demo'),
('env_5zone_stochastic')
])
def test_CSVlogger_wrapper(env_name, request):
env = request.getfixturevalue(env_name)

env = CSVLogger(env=LoggerWrapper(env=NormalizeObservation(env=env)))
# Check progress CSV path
assert env.get_wrapper_attr('progress_file_path') == env.get_wrapper_attr(
'workspace_path') + '/progress.csv'
Expand All @@ -862,6 +867,8 @@ def test_CSVlogger_wrapper(env_demo):

# Assert logger files are not created
assert not os.path.isfile(env.get_wrapper_attr('progress_file_path'))
assert not os.path.isfile(env.get_wrapper_attr(
'weather_variability_config_path'))
assert not os.path.isdir(env.get_wrapper_attr('episode_path') + '/monitor')

# simulating short episode
Expand All @@ -881,6 +888,15 @@ def test_CSVlogger_wrapper(env_demo):
reader = csv.reader(csvfile, delimiter=',')
# Header row and episode summary
assert len(list(reader)) == 2
if env_name == 'env_demo':
# File not exists
assert not os.path.isfile(env.get_wrapper_attr(
'weather_variability_config_path'))
else:
with open(env.get_wrapper_attr('weather_variability_config_path'), mode='r', newline='') as csvfile:
reader = csv.reader(csvfile, delimiter=',')
# Header row and episode config
assert len(list(reader)) == 2
# Check csv in monitor is created correctly (only check with observations)
with open(episode_path + '/monitor/observations.csv', mode='r', newline='') as csvfile:
reader = csv.reader(csvfile, delimiter=',')
Expand Down

0 comments on commit 2902239

Please sign in to comment.