Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(v3.7.3) - Weather variability config logging in CSVLogger wrapper #466

Merged
merged 5 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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