citylearn 2.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. citylearn-2.5.0/LICENSE +21 -0
  2. citylearn-2.5.0/MANIFEST.in +2 -0
  3. citylearn-2.5.0/PKG-INFO +48 -0
  4. citylearn-2.5.0/README.md +34 -0
  5. citylearn-2.5.0/citylearn/__init__.py +1 -0
  6. citylearn-2.5.0/citylearn/__main__.py +506 -0
  7. citylearn-2.5.0/citylearn/agents/__init__.py +0 -0
  8. citylearn-2.5.0/citylearn/agents/base.py +285 -0
  9. citylearn-2.5.0/citylearn/agents/marlisa.py +495 -0
  10. citylearn-2.5.0/citylearn/agents/q_learning.py +169 -0
  11. citylearn-2.5.0/citylearn/agents/rbc.py +515 -0
  12. citylearn-2.5.0/citylearn/agents/rlc.py +240 -0
  13. citylearn-2.5.0/citylearn/agents/sac.py +318 -0
  14. citylearn-2.5.0/citylearn/assets/building-0-charge-0.png +0 -0
  15. citylearn-2.5.0/citylearn/assets/building-0-charge-1.png +0 -0
  16. citylearn-2.5.0/citylearn/assets/building-0-charge-2.png +0 -0
  17. citylearn-2.5.0/citylearn/assets/building-0-charge-3.png +0 -0
  18. citylearn-2.5.0/citylearn/assets/building-1-charge-0.png +0 -0
  19. citylearn-2.5.0/citylearn/assets/building-1-charge-1.png +0 -0
  20. citylearn-2.5.0/citylearn/assets/building-1-charge-2.png +0 -0
  21. citylearn-2.5.0/citylearn/assets/building-1-charge-3.png +0 -0
  22. citylearn-2.5.0/citylearn/assets/glow.png +0 -0
  23. citylearn-2.5.0/citylearn/assets/grid.png +0 -0
  24. citylearn-2.5.0/citylearn/base.py +272 -0
  25. citylearn-2.5.0/citylearn/building.py +3353 -0
  26. citylearn-2.5.0/citylearn/citylearn.py +2720 -0
  27. citylearn-2.5.0/citylearn/cost_function.py +388 -0
  28. citylearn-2.5.0/citylearn/data.py +804 -0
  29. citylearn-2.5.0/citylearn/dynamics.py +130 -0
  30. citylearn-2.5.0/citylearn/electric_vehicle.py +162 -0
  31. citylearn-2.5.0/citylearn/electric_vehicle_charger.py +465 -0
  32. citylearn-2.5.0/citylearn/end_use_load_profiles/__init__.py +0 -0
  33. citylearn-2.5.0/citylearn/end_use_load_profiles/clustering.py +191 -0
  34. citylearn-2.5.0/citylearn/end_use_load_profiles/lstm_model/__init__.py +0 -0
  35. citylearn-2.5.0/citylearn/end_use_load_profiles/lstm_model/model.py +44 -0
  36. citylearn-2.5.0/citylearn/end_use_load_profiles/lstm_model/model_generation.py +214 -0
  37. citylearn-2.5.0/citylearn/end_use_load_profiles/lstm_model/model_generation_wrapper.py +40 -0
  38. citylearn-2.5.0/citylearn/end_use_load_profiles/lstm_model/preprocessing.py +142 -0
  39. citylearn-2.5.0/citylearn/end_use_load_profiles/neighborhood.py +830 -0
  40. citylearn-2.5.0/citylearn/end_use_load_profiles/simulate.py +231 -0
  41. citylearn-2.5.0/citylearn/energy_model.py +1398 -0
  42. citylearn-2.5.0/citylearn/misc/queries/create_zone_metadata.sql +26 -0
  43. citylearn-2.5.0/citylearn/misc/queries/select_citylearn_energy_simulation.sql +107 -0
  44. citylearn-2.5.0/citylearn/misc/queries/select_citylearn_weather.sql +12 -0
  45. citylearn-2.5.0/citylearn/misc/queries/select_ideal_loads.sql +24 -0
  46. citylearn-2.5.0/citylearn/misc/queries/select_lstm_training_data.sql +96 -0
  47. citylearn-2.5.0/citylearn/misc/queries/select_zone_conditioning_metadata.sql +47 -0
  48. citylearn-2.5.0/citylearn/misc/settings.yaml +354 -0
  49. citylearn-2.5.0/citylearn/occupant.py +99 -0
  50. citylearn-2.5.0/citylearn/power_outage.py +170 -0
  51. citylearn-2.5.0/citylearn/preprocessing.py +173 -0
  52. citylearn-2.5.0/citylearn/reward_function.py +523 -0
  53. citylearn-2.5.0/citylearn/rl.py +133 -0
  54. citylearn-2.5.0/citylearn/utilities.py +175 -0
  55. citylearn-2.5.0/citylearn/wrappers.py +857 -0
  56. citylearn-2.5.0/citylearn.egg-info/PKG-INFO +48 -0
  57. citylearn-2.5.0/citylearn.egg-info/SOURCES.txt +61 -0
  58. citylearn-2.5.0/citylearn.egg-info/dependency_links.txt +1 -0
  59. citylearn-2.5.0/citylearn.egg-info/entry_points.txt +2 -0
  60. citylearn-2.5.0/citylearn.egg-info/requires.txt +11 -0
  61. citylearn-2.5.0/citylearn.egg-info/top_level.txt +1 -0
  62. citylearn-2.5.0/setup.cfg +4 -0
  63. citylearn-2.5.0/setup.py +41 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 Jose Ramon Vazquez-Canteli, Intelligent Environments Laboratory
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ include citylearn/assets/*
2
+ recursive-include citylearn/misc *
@@ -0,0 +1,48 @@
1
+ Metadata-Version: 2.1
2
+ Name: citylearn
3
+ Version: 2.5.0
4
+ Summary: An open source Farama Foundation Gymnasium environment for benchmarking distributed energy resource control algorithms to provide energy flexibility in a district of buildings.
5
+ Home-page: https://github.com/intelligent-environments-lab/CityLearn
6
+ Author: Jose Ramon Vazquez-Canteli, Kingsley Nweye, Zoltan Nagy
7
+ Author-email: nweye@utexas.edu
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.7.7
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+
15
+ # CityLearn
16
+ CityLearn is an open source Farama Foundation Gymnasium environment for the implementation of Multi-Agent Reinforcement Learning (RL) for building energy coordination and demand response in cities. A major challenge for RL in demand response is the ability to compare algorithm performance. Thus, CityLearn facilitates and standardizes the evaluation of RL agents such that different algorithms can be easily compared with each other.
17
+
18
+ ![Demand-response](https://github.com/intelligent-environments-lab/CityLearn/blob/master/assets/images/dr.jpg)
19
+
20
+ ## Environment Overview
21
+
22
+ CityLearn includes energy models of buildings and distributed energy resources (DER) including air-to-water heat pumps, electric heaters and batteries. A collection of building energy models makes up a virtual district (a.k.a neighborhood or community). In each building, space cooling, space heating and domestic hot water end-use loads may be independently satisfied through air-to-water heat pumps. Alternatively, space heating and domestic hot water loads can be satisfied through electric heaters.
23
+
24
+ ![Citylearn](https://github.com/intelligent-environments-lab/CityLearn/blob/master/assets/images/environment.jpg)
25
+
26
+ ## Installation
27
+ Install latest release in PyPi with `pip`:
28
+ ```console
29
+ pip install CityLearn
30
+ ```
31
+
32
+ ## Documentation
33
+ Refer to the [docs](https://intelligent-environments-lab.github.io/CityLearn/).
34
+
35
+ ## CityLearn UI
36
+
37
+ CityLearn UI is a visual dashboard for exploring simulation data generated by the CityLearn framework. It was developed to simplify the analysis of results from smart energy communities, district energy coordination, demand response (among other applications), allowing users to visually inspect building-level components, compare simulation KPIs, and create simulation schemas with ease.
38
+
39
+ The interface is available in two options:
40
+
41
+ * Web app: https://citylearnui.netlify.app/ (free hosted version — not recommended for sensitive/personal data)
42
+ * Open-source code: https://github.com/Soft-CPS-Research-Group/citylearn-ui
43
+
44
+ You can check a tutorial at the official CityLearn [website](https://intelligent-environments-lab.github.io/CityLearn/ui.html), in the CityLearn UI repository [README](https://github.com/Soft-CPS-Research-Group/citylearn-ui), or at the help [tooltip of the oficial webapp](https://citylearn-ui.netlify.app/admin/help).
45
+
46
+ **Compatibility:** This version of the UI currently supports CityLearn v2.5.0 simulation data.
47
+
48
+ **Developed by:** José, a member of the [SoftCPS](https://www2.isep.ipp.pt/softcps/), Software for Cyber-Physical Systems research group (ISEP, Portugal) in collaboration with the Intelligent Environments Lab, University of Texas at Austin.
@@ -0,0 +1,34 @@
1
+ # CityLearn
2
+ CityLearn is an open source Farama Foundation Gymnasium environment for the implementation of Multi-Agent Reinforcement Learning (RL) for building energy coordination and demand response in cities. A major challenge for RL in demand response is the ability to compare algorithm performance. Thus, CityLearn facilitates and standardizes the evaluation of RL agents such that different algorithms can be easily compared with each other.
3
+
4
+ ![Demand-response](https://github.com/intelligent-environments-lab/CityLearn/blob/master/assets/images/dr.jpg)
5
+
6
+ ## Environment Overview
7
+
8
+ CityLearn includes energy models of buildings and distributed energy resources (DER) including air-to-water heat pumps, electric heaters and batteries. A collection of building energy models makes up a virtual district (a.k.a neighborhood or community). In each building, space cooling, space heating and domestic hot water end-use loads may be independently satisfied through air-to-water heat pumps. Alternatively, space heating and domestic hot water loads can be satisfied through electric heaters.
9
+
10
+ ![Citylearn](https://github.com/intelligent-environments-lab/CityLearn/blob/master/assets/images/environment.jpg)
11
+
12
+ ## Installation
13
+ Install latest release in PyPi with `pip`:
14
+ ```console
15
+ pip install CityLearn
16
+ ```
17
+
18
+ ## Documentation
19
+ Refer to the [docs](https://intelligent-environments-lab.github.io/CityLearn/).
20
+
21
+ ## CityLearn UI
22
+
23
+ CityLearn UI is a visual dashboard for exploring simulation data generated by the CityLearn framework. It was developed to simplify the analysis of results from smart energy communities, district energy coordination, demand response (among other applications), allowing users to visually inspect building-level components, compare simulation KPIs, and create simulation schemas with ease.
24
+
25
+ The interface is available in two options:
26
+
27
+ * Web app: https://citylearnui.netlify.app/ (free hosted version — not recommended for sensitive/personal data)
28
+ * Open-source code: https://github.com/Soft-CPS-Research-Group/citylearn-ui
29
+
30
+ You can check a tutorial at the official CityLearn [website](https://intelligent-environments-lab.github.io/CityLearn/ui.html), in the CityLearn UI repository [README](https://github.com/Soft-CPS-Research-Group/citylearn-ui), or at the help [tooltip of the oficial webapp](https://citylearn-ui.netlify.app/admin/help).
31
+
32
+ **Compatibility:** This version of the UI currently supports CityLearn v2.5.0 simulation data.
33
+
34
+ **Developed by:** José, a member of the [SoftCPS](https://www2.isep.ipp.pt/softcps/), Software for Cyber-Physical Systems research group (ISEP, Portugal) in collaboration with the Intelligent Environments Lab, University of Texas at Austin.
@@ -0,0 +1 @@
1
+ __version__ = '2.5.0'
@@ -0,0 +1,506 @@
1
+ import argparse
2
+ import concurrent.futures
3
+ import datetime
4
+ import getpass
5
+ import importlib
6
+ import inspect
7
+ import logging
8
+ from multiprocessing import cpu_count
9
+ import os
10
+ from pathlib import Path
11
+ import shutil
12
+ import socket
13
+ import subprocess
14
+ import sys
15
+ from typing import Any, List, Mapping, Tuple, Union
16
+ import uuid
17
+ from citylearn.agents.base import Agent as CityLearnAgent
18
+ from citylearn.citylearn import CityLearnEnv
19
+ from citylearn.data import DataSet, get_settings
20
+ from citylearn.__init__ import __version__
21
+ from citylearn.utilities import FileHandler
22
+ import pandas as pd
23
+ import simplejson as json
24
+
25
+ try:
26
+ from stable_baselines3.common.base_class import BaseAlgorithm as StableBaselines3Agent
27
+
28
+ except (ImportError, ModuleNotFoundError):
29
+ pass
30
+
31
+ def run_work_order(work_order_filepath, max_workers=None, start_index=None, end_index=None, virtual_environment_path=None, windows_system=None):
32
+ work_order_filepath = Path(work_order_filepath)
33
+
34
+ if virtual_environment_path is not None:
35
+ if windows_system:
36
+ virtual_environment_command = f'"{os.path.join(virtual_environment_path, "Scripts", "Activate.ps1")}"'
37
+
38
+ else:
39
+ virtual_environment_command = f'source "{os.path.join(virtual_environment_path, "bin", "activate")}"'
40
+
41
+ else:
42
+ virtual_environment_command = 'echo "No virtual environment"'
43
+
44
+ with open(work_order_filepath,mode='r') as f:
45
+ args = f.read()
46
+
47
+ args = args.strip('\n').split('\n')
48
+ start_index = 0 if start_index is None else start_index
49
+ end_index = len(args) - 1 if end_index is None else end_index
50
+ assert start_index <= end_index, 'start_index must be <= end_index'
51
+ assert start_index < len(args), 'start_index must be < number of jobs'
52
+ args = args[start_index:end_index + 1]
53
+ args = [a for a in args if not a.startswith('#')]
54
+ args = [f'{virtual_environment_command} && {a}' for a in args]
55
+ max_workers = cpu_count() if max_workers is None else max_workers
56
+
57
+ with concurrent.futures.ProcessPoolExecutor(max_workers=max_workers) as executor:
58
+ logging.debug(f'Will use {max_workers} workers for job.')
59
+ logging.debug(f'Pooling {len(args)} jobs to run in parallel...')
60
+ results = [executor.submit(subprocess.run,**{'args':a, 'shell':True}) for a in args]
61
+
62
+ for future in concurrent.futures.as_completed(results):
63
+ try:
64
+ logging.debug(future.result())
65
+
66
+ except Exception as e:
67
+ logging.debug(e)
68
+
69
+ class Simulator:
70
+ def __init__(self, schema: str, agent_name: str = None, env_kwargs: Mapping[str, Any] = None, agent_kwargs: Mapping[str, Any] = None, wrappers: List[str] = None,
71
+ time_series_variables: List[str] = None, simulation_id: str = None, output_directory: Union[Path, str] = None, agent_filepath: Union[Path, str] = None,
72
+ random_seed: int = None, overwrite: bool = None
73
+ ) -> None:
74
+ self.schema = schema
75
+ self.agent_name = agent_name
76
+ self.env_kwargs = env_kwargs
77
+ self.agent_kwargs = agent_kwargs
78
+ self.random_seed = random_seed
79
+ self.wrappers = wrappers
80
+ self.time_series_variables = time_series_variables
81
+ self.simulation_id = simulation_id
82
+ self.overwrite = overwrite
83
+ self.output_directory = output_directory
84
+ self.agent_filepath = agent_filepath
85
+ self.__reset()
86
+
87
+ @property
88
+ def schema(self) -> str:
89
+ return self.__schema
90
+
91
+ @property
92
+ def agent_name(self) -> str:
93
+ return self.__agent_name
94
+
95
+ @property
96
+ def env_kwargs(self) -> Mapping[str, Any]:
97
+ return self.__env_kwargs
98
+
99
+ @property
100
+ def agent_kwargs(self) -> Mapping[str, Any]:
101
+ return self.__agent_kwargs
102
+
103
+ @property
104
+ def wrappers(self) -> List[str]:
105
+ return self.__wrappers
106
+
107
+ @property
108
+ def time_series_variables(self) -> List[str]:
109
+ return self.__time_series_variables
110
+
111
+ @property
112
+ def simulation_id(self) -> str:
113
+ return self.__simulation_id
114
+
115
+ @property
116
+ def output_directory(self) -> Path:
117
+ return self.__output_directory
118
+
119
+ @property
120
+ def agent_filepath(self) -> Path:
121
+ return self.__agent_filepath
122
+
123
+ @property
124
+ def random_seed(self) -> int:
125
+ return self.__random_seed
126
+
127
+ @property
128
+ def overwrite(self) -> bool:
129
+ return self.__overwrite
130
+
131
+ @schema.setter
132
+ def schema(self, value: str):
133
+ self.__schema = value
134
+
135
+ @agent_name.setter
136
+ def agent_name(self, value: str):
137
+ self.__agent_name = value
138
+
139
+ @env_kwargs.setter
140
+ def env_kwargs(self, value: Mapping[str, Any]):
141
+ self.__env_kwargs = {} if value is None else value
142
+
143
+ @agent_kwargs.setter
144
+ def agent_kwargs(self, value: Mapping[str, Any]):
145
+ self.__agent_kwargs = {} if value is None else value
146
+
147
+ @wrappers.setter
148
+ def wrappers(self, value:List[str]):
149
+ self.__wrappers = [] if value is None else value
150
+
151
+ @time_series_variables.setter
152
+ def time_series_variables(self, value: List[str]):
153
+ self.__time_series_variables = self.get_default_time_series_variables() if value is None else value
154
+
155
+ @simulation_id.setter
156
+ def simulation_id(self, value: str):
157
+ self.__simulation_id = f'citylearn-simulation-{uuid.uuid4().hex}' if value is None else value
158
+
159
+ @output_directory.setter
160
+ def output_directory(self, value: Path):
161
+ self.__output_directory = Path(os.path.join('citylearn_simulations', self.simulation_id))\
162
+ if value is None else value
163
+
164
+ if os.path.isdir(self.__output_directory) and self.overwrite:
165
+ shutil.rmtree(self.__output_directory)
166
+
167
+ else:
168
+ pass
169
+
170
+ os.makedirs(self.__output_directory, exist_ok=True)
171
+
172
+ @agent_filepath.setter
173
+ def agent_filepath(self, value: Path):
174
+ self.__agent_filepath = value if value is None else Path(value)
175
+
176
+ @random_seed.setter
177
+ def random_seed(self, value: int):
178
+ self.__random_seed = value
179
+
180
+ if self.random_seed is not None:
181
+ self.env_kwargs['random_seed'] = self.random_seed
182
+
183
+ if self.agent_name is not None:
184
+ random_seed_name = 'seed' if 'stable_baselines3' in self.agent_name else 'random_seed'
185
+ self.agent_kwargs[random_seed_name] = self.random_seed
186
+
187
+ else:
188
+ pass
189
+
190
+ else:
191
+ pass
192
+
193
+ @overwrite.setter
194
+ def overwrite(self, value: bool):
195
+ self.__overwrite = True if value is None else value
196
+
197
+ def __get_evaluation_summary(self):
198
+ return {
199
+ 'evaluation_episode_time_steps': [
200
+ self.env.unwrapped.episode_tracker.episode_start_time_step,
201
+ self.env.unwrapped.episode_tracker.episode_end_time_step
202
+ ],
203
+ 'evaluation_start_timestamp': self.__evaluation_start_timestamp,
204
+ 'evaluation_end_timestamp': self.__evaluation_end_timestamp,
205
+ 'evaluation': self.env.unwrapped.evaluate().pivot(index='name', columns='cost_function', values='value').to_dict('index'),
206
+ 'episode_reward_summary': self.env.unwrapped.episode_rewards[-1],
207
+ 'episode_rewards': self.env.unwrapped.rewards,
208
+ 'time_series': self.__get_time_series().to_dict('list'),
209
+ 'actions': self.__actions_list,
210
+ }
211
+
212
+ def __get_time_series(self) -> pd.DataFrame:
213
+ data_list = []
214
+
215
+ for b in self.env.unwrapped.buildings:
216
+ data = {}
217
+
218
+ for variable in self.time_series_variables:
219
+ key = b
220
+ values = variable.split('.')
221
+
222
+ for i in range(len(values)):
223
+ if hasattr(key, values[i]):
224
+ value = getattr(key, values[i])
225
+ key = value
226
+
227
+ else:
228
+ pass
229
+
230
+ data[variable.replace('.', '_')] = value
231
+
232
+ data = pd.DataFrame(data)
233
+ data.insert(0, 'time_step', data.index)
234
+ data.insert(1, 'building_name', b.name)
235
+ data_list.append(data)
236
+
237
+ return pd.concat(data_list, ignore_index=True)
238
+
239
+ def __get_training_summary(self):
240
+ return {
241
+ 'hostname': socket.gethostname(),
242
+ 'username': getpass.getuser(),
243
+ 'simulation_id': self.simulation_id,
244
+ 'agent': self.agent.__class__.__name__,
245
+ 'agent_kwargs': self.agent_kwargs,
246
+ 'wrappers': self.wrappers,
247
+ 'train_episodes': self.env.unwrapped.episode_tracker.episode,
248
+ 'train_episode_time_steps': self.env.unwrapped.episode_time_steps,
249
+ 'train_start_timestamp': self.__train_start_timestamp,
250
+ 'train_end_timestamp': self.__train_end_timestamp,
251
+ 'train_episode_reward_summary': self.env.unwrapped.episode_rewards,
252
+ 'env_metadata': self.env.unwrapped.get_metadata(),
253
+ }
254
+
255
+ def __evaluate(self):
256
+ observations, _ = self.env.reset()
257
+ actions_list = []
258
+ self.__evaluation_start_timestamp = datetime.datetime.now(datetime.UTC)
259
+
260
+ while not self.env.terminated:
261
+ if isinstance(self.agent, CityLearnAgent):
262
+ actions = self.agent.predict(observations, deterministic=True)
263
+ actions_list.append(self.env.unwrapped._parse_actions(actions))
264
+
265
+ elif isinstance(self.agent, StableBaselines3Agent):
266
+ actions, _ = self.agent.predict(observations, deterministic=True)
267
+ actions_list.append(self.env.unwrapped._parse_actions([actions]))
268
+
269
+ else:
270
+ raise Exception(f'Unknown agent type: {type(self.agent)}')
271
+
272
+ observations, _, _, _, _ = self.env.step(actions)
273
+
274
+ self.__evaluation_end_timestamp = datetime.datetime.now(datetime.UTC)
275
+ self.__actions_list = actions_list
276
+
277
+ def __train(self, episodes: int):
278
+ kwargs = {}
279
+ self.__train_start_timestamp = datetime.datetime.now(datetime.UTC)
280
+
281
+ if isinstance(self.agent, CityLearnAgent):
282
+ kwargs = {**kwargs, 'episodes': episodes}
283
+ self.agent.learn(**kwargs)
284
+
285
+ else:
286
+ kwargs = {**kwargs, 'total_timesteps': episodes*self.env.unwrapped.time_steps}
287
+ self.agent = self.agent.learn(**kwargs)
288
+
289
+ self.__train_end_timestamp = datetime.datetime.now(datetime.UTC)
290
+
291
+ def __save_agent(self):
292
+ if isinstance(self.agent, CityLearnAgent):
293
+ filepath = os.path.join(self.output_directory, f'{self.simulation_id}-agent.pkl')
294
+ FileHandler.write_pickle(filepath, self.agent)
295
+
296
+ else:
297
+ filepath = os.path.join(self.output_directory, f'{self.simulation_id}-agent')
298
+ self.agent.save(filepath)
299
+
300
+ def __set_agent(self) -> Union[CityLearnAgent, StableBaselines3Agent]:
301
+ if self.agent_filepath is None:
302
+ agent = self.env.unwrapped.load_agent(
303
+ agent=self.agent_name,
304
+ env=self.env,
305
+ **self.agent_kwargs,
306
+ )
307
+
308
+ else:
309
+ if str(self.agent_filepath).endswith('.pkl'):
310
+ agent = FileHandler.read_pickle(self.agent_filepath)
311
+ agent.env = self.env
312
+
313
+ else:
314
+ agent = StableBaselines3Agent.load(self.agent_filepath, self.env)
315
+
316
+ return agent
317
+
318
+ def __set_env(self) -> CityLearnEnv:
319
+ env = CityLearnEnv(self.schema, **self.env_kwargs)
320
+
321
+ for wrapper in self.wrappers:
322
+ wrapper_module = '.'.join(wrapper.split('.')[0:-1])
323
+ wrapper_name = wrapper.split('.')[-1]
324
+ wrapper_constructor = getattr(importlib.import_module(wrapper_module), wrapper_name)
325
+ env = wrapper_constructor(env)
326
+
327
+ return env
328
+
329
+ def __reset(self):
330
+ self.env = self.__set_env()
331
+ self.agent = self.__set_agent()
332
+ self.__train_start_timestamp = None
333
+ self.__train_end_timestamp = None
334
+ self.__evaluation_start_timestamp = None
335
+ self.__evaluation_end_timestamp = None
336
+ self.__actions_list = None
337
+
338
+ @classmethod
339
+ def evaluate(cls, evaluation_episode_time_steps: Tuple[int, int] = None, **kwargs):
340
+ kwargs['env_kwargs'] = {} if kwargs.get('env_kwargs') is None else kwargs['env_kwargs']
341
+
342
+ if evaluation_episode_time_steps is not None:
343
+ kwargs['env_kwargs']['episode_time_steps'] = [evaluation_episode_time_steps]
344
+
345
+ else:
346
+ pass
347
+
348
+
349
+ simulator = cls(**kwargs)
350
+ simulator.__evaluate()
351
+ filepath = os.path.join(simulator.output_directory, f'{simulator.simulation_id}-evaluation.json')
352
+ FileHandler.write_json(filepath, simulator.__get_evaluation_summary())
353
+
354
+ @classmethod
355
+ def train(cls, episodes: int = None, evaluate: bool = None, evaluation_episode_time_steps: Tuple[int, int] = None, save_agent: bool = None, **kwargs):
356
+ simulator = cls(**kwargs)
357
+ episodes = 1 if episodes is None else episodes
358
+ simulator.__train(episodes)
359
+ filepath = os.path.join(simulator.output_directory, f'{simulator.simulation_id}-train.json')
360
+ FileHandler.write_json(filepath, simulator.__get_training_summary())
361
+
362
+ if save_agent:
363
+ simulator.__save_agent()
364
+
365
+ else:
366
+ pass
367
+
368
+ if evaluate:
369
+ evaluation_episode_time_steps = [[
370
+ simulator.env.unwrapped.episode_tracker.simulation_start_time_step,
371
+ simulator.env.unwrapped.episode_tracker.simulation_end_time_step
372
+ ]] if evaluation_episode_time_steps is None else [evaluation_episode_time_steps]
373
+ kwargs['env_kwargs'] = {} if kwargs.get('env_kwargs') is None else kwargs['env_kwargs']
374
+ kwargs['overwrite'] = False
375
+ kwargs['output_directory'] = simulator.output_directory
376
+ kwargs['env_kwargs']['episode_time_steps'] = evaluation_episode_time_steps
377
+ kwargs['simulation_id'] = f'{simulator.simulation_id}'
378
+ simulator.evaluate(**kwargs)
379
+
380
+ else:
381
+ pass
382
+
383
+ @staticmethod
384
+ def get_default_time_series_variables():
385
+ return get_settings()['default_time_series_variables']
386
+
387
+ def main():
388
+ parser = argparse.ArgumentParser(
389
+ prog='citylearn',
390
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter, description=(
391
+ 'An open source Farama Foundation Gymnasium environment for benchmarking distributed energy resource '
392
+ 'control algorithms to provide energy flexibility in a district of buildings. '
393
+ 'Compatible with training and evaluating internally defined CityLearn agents in `citylearn.agents`, '
394
+ 'user-defined agents that inherit from `citylearn.agents.base.Agent` and use the same interface as it, and agents '
395
+ 'provided by stable-baselines3.'))
396
+ parser.add_argument('--version', action='version', version='%(prog)s' + f' {__version__}')
397
+ subparsers = parser.add_subparsers(title='subcommands', required=True, dest='subcommands')
398
+
399
+ # run many simulations in parallel
400
+ subparser_run_work_order = subparsers.add_parser(
401
+ 'run_work_order',
402
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
403
+ help='Run commands in parallel. Useful for running many `citylearn simulate` commands in parallel.'
404
+ )
405
+ subparser_run_work_order.add_argument('work_order_filepath', type=Path, help=(
406
+ 'Filepath to script containing list of commands to be run in parallel with each command defined on a new line.'))
407
+ subparser_run_work_order.add_argument('-w', '--max_workers', dest='max_workers', type=int, help=(
408
+ 'Maximum number of commands to run at a time. Default is the number of CPUs.'))
409
+ subparser_run_work_order.add_argument('-is', '--start_index', default=0, dest='start_index', type=int, help=(
410
+ 'Line index of first command to execute. Commands above this index are not executed. '
411
+ 'The default is to execute from the first line.'))
412
+ subparser_run_work_order.add_argument('-ie', '--end_index', dest='end_index', type=int, help=(
413
+ 'Line index of last command to execute. Commands below this index are not exectued. '
414
+ 'The default is to execute till the last line.'))
415
+ subparser_run_work_order.set_defaults(func=run_work_order)
416
+
417
+ # get names of datasets
418
+ subparser_datasets = subparsers.add_parser(
419
+ 'list_datasets',
420
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
421
+ help='Lists available dataset names that can be parsed as `schema` in `citylearn simulate schema`.'
422
+ )
423
+ subparser_datasets.set_defaults(func=DataSet().get_dataset_names)
424
+
425
+ # get default time series variables
426
+ subparser_time_series_variables = subparsers.add_parser(
427
+ 'list_default_time_series_variables',
428
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
429
+ help='Lists the default time series variables that will be reported and saved in a `JSON` file post-evaluation.'
430
+ )
431
+ subparser_time_series_variables.set_defaults(func=Simulator.get_default_time_series_variables)
432
+
433
+ # run one simulation
434
+ subparser_simulate = subparsers.add_parser(
435
+ 'simulate',
436
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
437
+ help='Train or evaluate a trained agent against an environment.'
438
+ )
439
+ subparser_simulate.add_argument('schema', type=str, help=(
440
+ 'Name of CityLearn dataset or filepath to a schema. Call `citylearn list_datasets` to get list of valid dataset names.'))
441
+ subparser_simulate.add_argument('-a', '--agent_name', dest='agent_name', default='citylearn.agents.base.BaselineAgent', type=str, help=(
442
+ 'Name path to agent. Currently only compatible with internally defined CityLearn agents in `citylearn.agents`, '
443
+ 'user-defined agents that inherit from `citylearn.agents.base.Agent` and use the same interface as it, and agents '
444
+ 'provided by stable-baselines3. To use stable-baselines3 agents, make sure to run `pip install stable-baselines3` '
445
+ 'before using the `simulate command.`'))
446
+ subparser_simulate.add_argument('-ke', '--env_kwargs', dest='env_kwargs', type=json.loads, help=(
447
+ 'Initialization parameters for`citylearn.citylearn.CityLearnEnv`.'))
448
+ subparser_simulate.add_argument('-ka', '--agent_kwargs', dest='agent_kwargs', type=json.loads, help=(
449
+ 'Initialization parameters for agent class.'))
450
+ subparser_simulate.add_argument('-w', '--wrappers', dest='wrappers', type=str, nargs='+', help=(
451
+ 'Name path to environment wrappers e.g., \'citylearn.wrappers.ClippedObservationWrapper\'.'))
452
+ subparser_simulate.add_argument('-tv', '--time_series_variables', dest='time_series_variables', type=str, nargs='+', help=(
453
+ 'Names of building-level time series properties to be stored in the evaluation `JSON` post-evaluation. '
454
+ 'Call `citylearn list_default_time_series_variables` to see the default variable in use.'))
455
+ subparser_simulate.add_argument('-sid', '--simulation_id', dest='simulation_id', type=str, help=(
456
+ 'SImulation reference ID used in directory and file names.' ))
457
+ subparser_simulate.add_argument('-fa', '--agent_filepath', dest='agent_filepath', type=str, help=(
458
+ 'Filepath to previously saved agent to use for training or evaluation.'))
459
+ subparser_simulate.add_argument('-d', '--output_directory', dest='output_directory', type=str, help=(
460
+ 'Directory to save all simulation output to.'))
461
+ subparser_simulate.add_argument('-te', '--evaluation_episode_time_steps', dest='evaluation_episode_time_steps', type=int, nargs=2,
462
+ action='append', help=('Start and end time steps in data set to evaluate on otherwise, the agent is evaluated on entire dataset.'))
463
+ subparser_simulate.add_argument('-p', '--append', dest='overwrite', action='store_false', help=(
464
+ 'Add to output for existing simulation with `simulation_id` i.e. do not overwrite.'))
465
+ subparser_simulate.add_argument('-rs', '--random_seed', dest='random_seed', type=int, help=(
466
+ 'Random seed used during environment and agent initialization.'))
467
+ simulation_subparsers = subparser_simulate.add_subparsers(title='simulate subcommands', required=True, dest='subcommands')
468
+
469
+ # -> train an agent
470
+ subparser_train = simulation_subparsers.add_parser(
471
+ 'train',
472
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
473
+ help='Train an agent.'
474
+ )
475
+ subparser_train.add_argument('-e', '--episodes', dest='episodes', type=int, help='Number of training episodes/epochs.')
476
+ subparser_train.add_argument('--save_agent', dest='save_agent', action='store_true', help='Whether to save agent to disk at the end of training.')
477
+ subparser_train.add_argument('--evaluate', dest='evaluate', action='store_true', help=(
478
+ 'Whether to run deterministic evaluation for one episode at the end of training.'))
479
+ subparser_train.set_defaults(func=Simulator.train)
480
+
481
+ # -> evaluate a trained agent
482
+ subparser_evaluate = simulation_subparsers.add_parser(
483
+ 'evaluate',
484
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
485
+ help='Deterministically evaluate an agent.'
486
+ )
487
+ subparser_evaluate.set_defaults(func=Simulator.evaluate)
488
+
489
+ argv = sys.argv
490
+ argv = ['sphinx-build' in a for a in argv]
491
+ sphinx = any(argv)
492
+
493
+ if not sphinx:
494
+ args = parser.parse_args()
495
+ arg_spec = inspect.getfullargspec(args.func)
496
+ kwargs = {key:value for (key, value) in args._get_kwargs()
497
+ if (key in arg_spec.args or (arg_spec.varkw is not None and key not in ['func','subcommands']))
498
+ }
499
+
500
+ return args.func(**kwargs)
501
+
502
+ else:
503
+ return parser
504
+
505
+ if __name__ == '__main__':
506
+ sys.exit(main())
File without changes