This page is the operational reference for installing the simulator, creating environments, running episodes, using the CLI and understanding the main execution parameters.
Portuguese version: pt/running_simulations.md.
| Case | Command | Notes |
|---|---|---|
| Standard install | pip install citylearn |
The Python import path is citylearn. |
| Parquet datasets | pip install pyarrow |
Required only when schemas point to .parquet, .pq or .parq. |
| PV autosizing | pip install "citylearn[pysam]" |
Required only for EPW/PySAM autosizing. |
| Local development | .venv/bin/pip install -e . |
Use the repo .venv when available. |
import numpy as np
from citylearn.citylearn import CityLearnEnv
schema = "data/datasets/citylearn_challenge_2022_phase_all_plus_evs/schema.json"
env = CityLearnEnv(schema, interface="flat", episode_time_steps=24, render_mode="none")
observations, info = env.reset()
terminated = truncated = False
while not (terminated or truncated):
actions = [np.zeros(space.shape, dtype="float32") for space in env.action_space]
observations, reward, terminated, truncated, info = env.step(actions)
kpis_v2 = env.evaluate_v2()from citylearn.citylearn import CityLearnEnv
env = CityLearnEnv(
"data/datasets/citylearn_three_phase_dynamic_topology_demo/schema.json",
interface="entity",
topology_mode="dynamic",
)
obs, info = env.reset()
specs = env.entity_specs
actions = {
"tables": {
"building": env.action_space["tables"]["building"].sample(),
"charger": env.action_space["tables"]["charger"].sample(),
"deferrable_appliance": env.action_space["tables"]["deferrable_appliance"].sample(),
}
}
obs, reward, terminated, truncated, info = env.step(actions)Use MultiCommunityEnv when one training loop should step several independent communities in lockstep. Each child keeps its own physics, KPIs and demand-response files.
from citylearn.multi_community import MultiCommunityEnv
env = MultiCommunityEnv(
communities=[
{
"community_id": "community_a",
"schema": "data/datasets/community_a/schema.json",
"env_kwargs": {"interface": "entity", "episode_time_steps": 48},
"weight": 1.0,
},
{
"community_id": "community_b",
"schema": "data/datasets/community_b/schema.json",
"env_kwargs": {"interface": "entity", "episode_time_steps": 48},
"weight": 1.0,
},
]
)
observations, info = env.reset(seed=0)All communities must share seconds_per_time_step, effective episode length, interface and central_agent mode. evaluate_v2() returns local rows with community_id plus portfolio rows with level="portfolio". See multi_community_reference.md.
| Parameter | Type | Default | Purpose | Notes |
|---|---|---|---|---|
schema |
str, Path, Mapping |
required | Dataset name, schema path or preloaded dict. | Relative paths are resolved from root_directory. |
root_directory |
path | schema | Base folder for dataset files. | Overrides schema value. |
buildings |
list | schema | Subset of buildings to load. | Names or indices. |
electric_vehicles |
list | schema | Subset of EVs to load. | Usually from electric_vehicles_def. |
simulation_start_time_step |
int | schema | First global timestep. | Inclusive. |
simulation_end_time_step |
int | schema | Last global timestep. | Inclusive. |
episode_time_steps |
int/list | schema | Episode size or explicit windows. | Can be used with rolling/random splits. |
rolling_episode_split |
bool | schema | Sequential window episodes. | Useful for training. |
random_episode_split |
bool | schema | Random window episodes. | Uses random_seed. |
seconds_per_time_step |
float | schema | Physical duration of each step. | Examples: 15, 60, 300, 900, 3600. |
time_step_ratio |
int/float | inferred | Ratio between control step and dataset spacing. | Normally dataset and schema should match. |
reward_function |
class/path | schema | Reward used by step. |
Supports reward_function_kwargs. |
reward_function_kwargs |
dict | {} |
Constructor kwargs for the reward. | Pass-through. |
central_agent |
bool | schema | Single controller for all buildings. | False returns one vector per building. |
shared_observations |
list | schema | Shared observations in central mode. | Included once in central vectors. |
active_observations |
list/list[list] | schema | Enable only these observations. | Global or per-building override. |
inactive_observations |
list/list[list] | schema/building | Disable observations. | Applied after active selection. |
active_actions |
list/list[list] | schema | Enable only these actions. | Global or per-building override. |
inactive_actions |
list/list[list] | schema/building | Disable actions. | Applied after active selection. |
simulate_power_outage |
bool | schema/building | Enable outage simulation. | Uses data series or stochastic model. |
solar_generation |
bool | schema | Compatibility switch for solar generation. | Kept for original CityLearn compatibility. |
random_seed |
int | schema | Random seed. | Affects splits and stochastic attributes. |
offline |
bool | False |
Disable network fallbacks. | Requires local datasets. |
interface |
flat/entity |
schema/flat | Observation/action contract. | Entity returns tables and edges. |
topology_mode |
static/dynamic |
schema/static | Enable dynamic topology events. | Dynamic requires interface="entity". |
start_date |
date/string | schema/2024-01-01 | Base date for render/export timestamps. | Does not change physics. |
render_mode |
none/during/end |
none |
CSV export policy. | end is preferred for performance. |
render_session_name |
string | schema/None | Export session subfolder. | Must be relative. |
export_kpis_on_episode_end |
bool | render flag | Export KPIs at episode end. | Can be enabled without full render. |
| Parameter | Type | Default | Purpose |
|---|---|---|---|
render_directory |
path | internal output | Base export folder. |
render_directory_name |
string | render_logs |
Legacy export folder name. |
render |
bool | derived | Legacy render switch. |
debug_timing |
bool | schema/False | Runtime timing logs. |
check_observation_limits |
bool | schema/False | Validate observations against estimated bounds. |
physics_invariant_checks |
bool | schema/False | Run physical invariant checks at runtime. |
metrics_log_interval |
int | schema/0 | Runtime metric log cadence. |
step() builds a smaller reward observation payload when the reward function declares which observation names it needs. Built-in rewards already do this. Custom rewards can opt in with one of these compatible forms:
class MyReward:
required_observation_names = ("net_electricity_consumption",)
def calculate(self, observations):
return [-sum(o["net_electricity_consumption"] for o in observations)]The alias required_observations and method get_required_observation_names() are also supported. If a custom reward does not declare requirements, CityLearn falls back to full include_all observations for backward compatibility.
step_many() repeats one selected action across multiple internal simulator steps and returns one macro transition for RL replay buffers:
obs, rewards, terminated, truncated, info = env.step_many(
action,
repeat_steps=20,
stop_on_done=True,
return_substeps=False,
)The simulator still advances every internal step at seconds_per_time_step resolution. Constraints, EV charging/departures, batteries, deferrables, phases/headroom, rewards, KPIs and render/export time series are updated exactly as they are for repeated step() calls. The returned observation is only the final observation after the executed substeps, and rewards is the per-agent reward sum.
info["executed_steps"] is always present so RL code can discount macro transitions correctly:
gamma_macro = gamma ** info["executed_steps"]When return_substeps=True, info also includes substep_rewards, substep_infos and substep_actions_applied for debugging. Keep it disabled in long training runs.
citylearn --version
citylearn list_datasets
citylearn list_default_time_series_variables
citylearn simulate data/datasets/my_dataset/schema.json train -e 3
citylearn simulate data/datasets/my_dataset/schema.json evaluate| Option | Example | Purpose |
|---|---|---|
schema |
dataset name or schema.json path |
Dataset to run. |
-a, --agent_name |
citylearn.agents.baseline.BusinessAsUsualAgent |
Agent class path. |
-ke, --env_kwargs |
'{"interface":"entity"}' |
JSON kwargs for CityLearnEnv. |
-ka, --agent_kwargs |
'{"x":1}' |
JSON kwargs for the agent. |
-w, --wrappers |
wrapper class paths | Gymnasium wrappers. |
-tv, --time_series_variables |
net_electricity_consumption |
Series stored after evaluation. |
-sid, --simulation_id |
run_001 |
Output naming ID. |
-fa, --agent_filepath |
outputs/agent.pkl |
Load/save agent path. |
-d, --output_directory |
outputs/run_001 |
Output folder. |
-te, --evaluation_episode_time_steps |
0 2879 |
Evaluation window. Can repeat. |
-p, --append |
flag | Do not overwrite existing output. |
-rs, --random_seed |
42 |
Seed. |
--offline |
flag | Require local files. |
train -e |
train -e 10 |
Number of training episodes. |
train --save_agent |
flag | Save agent at the end. |
train --evaluate |
flag | Evaluate after training. |
evaluate |
subcommand | Deterministic evaluation. |
render_mode |
Runtime cost | Output | Use case |
|---|---|---|---|
none |
lowest | No render CSV | Training. |
during |
high | Writes rows each step | Short debugging runs. |
end |
medium | Writes full episode at end | Long episodes with final CSV output. |
Set render_file_format="parquet" to write render, KPI and BAU time-series exports as chunked parquet part files instead of CSV. render_chunk_size controls the number of rows per parquet part; the default is 50000 for parquet and 100000 for CSV.
KPI and BAU exports are episode-scoped, so training loops can keep export disabled and turn it on only for the final episode. If normal time-series outputs are needed on that final episode, create the environment with render_mode="end" and toggle render_enabled; for KPI-only output, leave render disabled and call export_final_kpis() manually after the final episode.
for episode in range(episodes):
last_episode = episode == episodes - 1
env.render_enabled = last_episode
env.export_kpis_on_episode_end = last_episode
observations, info = env.reset()
# run episode...export_final_kpis() controls the BAU cost separately:
| Call | Output | BAU sidecar cost |
|---|---|---|
env.export_final_kpis(include_business_as_usual=False) |
KPI file only | no |
env.export_final_kpis(include_business_as_usual=True, export_business_as_usual_timeseries=False) |
KPI file with BAU rows | yes |
env.export_final_kpis(include_business_as_usual=True, export_business_as_usual_timeseries=True) |
KPI file with BAU rows and BAU time-series file | yes |
Normal episode time series are controlled by render_mode/render_enabled, not by export_final_kpis().
For exact final-episode choices, keep export_kpis_on_episode_end=False and call export_final_kpis() yourself after the episode terminates.
.venv/bin/pytest -q
.venv/bin/python scripts/audit/audit_entity_contract.py --strict
.venv/bin/python scripts/audit/audit_physics.py
.venv/bin/python -m ruff check citylearn tests scripts/manual scripts/ci --select E9,F821For large 15s datasets, always smoke-test a short window first:
.venv/bin/python - <<'PY'
import numpy as np
from citylearn.citylearn import CityLearnEnv
env = CityLearnEnv(
"data/datasets/citylearn_three_phase_electrical_service_demo_15s_parquet/schema.json",
simulation_start_time_step=0,
simulation_end_time_step=120,
episode_time_steps=120,
)
obs, info = env.reset()
for _ in range(100):
if isinstance(env.action_space, list):
actions = [np.zeros(space.shape, dtype="float32") for space in env.action_space]
else:
actions = {
"tables": {
name: np.zeros(space.shape, dtype="float32")
for name, space in env.action_space["tables"].spaces.items()
}
}
obs, reward, terminated, truncated, info = env.step(actions)
if terminated or truncated:
break
print(env.evaluate_v2().head())
PY