# CityLearn: a Gymnasium environment for energy communities

> CityLearn is an open source Gymnasium environment for building energy coordination and demand response research. It models buildings with thermal systems, photovoltaics, batteries, electric vehicles and flexible loads, and scores controllers with a common set of KPIs so that results can be compared rather than admired.

**citylearn-project/CityLearn** — Official reinforcement learning environment for demand response and load shaping

- Repository: https://github.com/citylearn-project/CityLearn
- Stars: 655 · Forks: 222
- Language: Python
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/citylearn-project-citylearn

## What kind of environment this is, and for whom

CityLearn describes itself as an open source Gymnasium environment for building energy coordination, demand response and reinforcement learning, and the sentence that follows tells you the workflow it is designed for: configure renewable energy communities, run controllers, and compare energy, cost and service outcomes. The Gymnasium framing is the important part, because it means the environment presents the standard observation, action, reward, done interface that reinforcement learning code expects, so an existing training setup can be pointed at it without a bespoke adapter. The audience is a researcher or engineer working on control of distributed energy resources, and the word in the repository description that captures the niche is demand response: shaping or shifting demand in time to follow a signal, a tariff, or a constraint. The v3 release notes in the repository show that this is a mature rather than a new line, with release notes files for 1.7.0, 1.8.0, 3.0.0, 3.0.1 and 3.0.2, and the current published versions are 3.0.0, 3.0.1 and 3.0.2, all released on 2026-09-09. The licence is MIT, and the setup metadata names the Soft-CPS Research Group and three authors, with the development supported by the project contributors.

## What v3 added to the community model

The README summarises v3 as bringing together buildings and thermal systems, PV, batteries, EVs and flexible loads, with electrical limits, local energy sharing, changing members and assets, and data or equipment failures. Each of those terms hides a specific modelling decision. Thermal systems mean a building has a temperature state with inertia rather than an instantaneous load, which is what makes load shifting physically meaningful. Electrical limits mean the local grid has capacity constraints, so a strategy that looks fine in aggregate can violate a service constraint. Local energy sharing means energy can move between members of the community instead of only being drawn from the grid, which is the part that makes community optimisation different from a single asset problem. Changing members and assets mean a community is not static: a vehicle joins, a battery is replaced, and the controller has to cope. Data or equipment failures mean the environment can inject the kind of disruption that a real deployment sees. The README is careful about provenance here, noting that v3 builds on the CityLearn community's work and on extensions developed by the Soft-CPS Research Group, so the third release is an accumulation rather than a single invention.

## The first episode, and the loop it puts you in

The quickstart is unusually honest about what a first run does, and the example is short enough to read in full. The environment is created with a scenario name, the number of episode time steps, an interface and a render mode.

```python
from citylearn.agents.baseline import BusinessAsUsualAgent
from citylearn.citylearn import CityLearnEnv

env = CityLearnEnv(
    "citylearn_challenge_2022_phase_all_plus_evs",
    episode_time_steps=24,
    interface="flat",
    render_mode="none",
)
agent = BusinessAsUsualAgent(env)
observations, info = env.reset(seed=0)
```

The loop then follows the standard pattern of a Gymnasium environment: the agent predicts actions from the observations, the environment steps, and the loop runs until the episode terminates or is truncated.

```python
while not (terminated or truncated):
    actions = agent.predict(observations)
    observations, rewards, terminated, truncated, info = env.step(actions)
```

Two details are worth noting before you copy anything. The example downloads a public dataset on first use, which means your first run needs a network connection and some disk, and it works outside a repository checkout. The agent used here is the built-in business as usual controller, which is a reference rather than a competitor, and the point of running it is to see what the KPIs look like before you try to beat them. The seed is set on reset, so a run is reproducible. A runnable copy of the same script is in examples/first_simulation.py in the repository, and the first simulation guide explains the loop, the outputs and the next steps.

## KPIs, the scorecard and the interface for comparing runs

Evaluation in CityLearn is a first class part of the API rather than something the researcher assembles afterwards, and the example shows the shape of it. The environment has an evaluate_v2 method that returns the community KPIs, and the example then selects a few cost functions out of that frame and prints them.

```python
kpis = env.evaluate_v2()
selected = [
    "district_cost_total_control_eur",
    "district_energy_grid_total_import_control_kwh",
]
```

The names of those cost functions describe what the scorecard is measuring. One is the total district cost under the controller in euros, one is the total grid import energy in kilowatt hours, and the wider example also selects an electric vehicle performance ratio for departures and the total energy of electrical service phase violations. That last one matters, because it shows the scorecard treats constraint breaking as a first class metric rather than a footnote. The documentation covers KPIs, a scorecard guide for comparisons, and a baselines guide for the reference controllers, and the CityLearn UI is a separate front end for inspecting exported time series and comparing results, with an example file for export in the repository. The scores in the documentation come with published reference controllers, which is what makes a number in a paper comparable to a number in your own run.

## The dependency list is the honest cost of the environment

The requirements file is worth reading before you install, because it explains the size of what you are pulling in.

```
gymnasium<=0.28.1
numpy<2.0.0
scikit-learn<=1.2.2
openstudio<=3.10.0
```

The list also includes doe_xstock at 2.0.0 or newer, pandas, pyyaml, simplejson, torch and torchvision. Two things follow. First, the upper bounds are real constraints: gymnasium is pinned to 0.28.1 or lower, numpy to below 2.0.0, scikit-learn to 1.2.2 or lower, and openstudio to 3.10.0 or lower, so this is a package that expects a specific and rather old dependency neighbourhood, and combining it with a modern scientific stack in the same environment is the first likely source of friction. Second, the presence of torch and torchvision means the environment ships with the heavy learning stack rather than leaving it to you, which is convenient for the common case and wasteful for the case where you only want the simulator. The setup metadata also defines two optional extras, parquet for pyarrow support and pysam for nrel-pysam, and the installation guide documents the optional dependencies including Parquet support. The package also ships a console entry point, a citylearn command, and the documentation covers running simulations from the CLI as well as from Python.

## Interfaces, time resolution and the shape of a community run

Two configuration choices shape every run and are easy to miss. The first is the interface, and the example passes flat, which selects how the observation and action spaces are presented to the agent. The second is time resolution, and the README is specific: individual scenarios can use different time resolutions, while synchronized multi-community runs use a common timestep. That distinction is what lets one community run at five minute intervals and another at hourly intervals in the same project, and it also means that when you compare two communities inside a single run, the comparison is made on a shared clock. The features guide is where assets, services, interfaces and scenario conditions are chosen, and the running simulations guide covers datasets, exports and advanced examples, including notebooks for loading environments and a tutorial notebook, and a quickstart notebook in the repository. The examples directory also carries a custom controller and a custom reward function, which is the pair of files that tells you where the real extension points are: a controller is the thing that decides what to do, and the reward function is the thing that defines what good means.

## Three releases on one day, and the notes to read

The release history deserves a mention because of its shape. Versions 3.0.0, 3.0.1 and 3.0.2 were all published on 2026-09-09, within about an hour of each other, and the last push to the repository was on 2026-09-22. The repository also keeps separate release notes files for 1.7.0, 1.8.0, 3.0.0, 3.0.1 and 3.0.2, and there is a PYPI_RELEASE.md and a README_PYPI.md alongside the main readme, which is the pattern of a project that takes its packaging metadata seriously. For a user, the practical consequence is that the 3.0.x line is where you are, and that the differences between the three tags are likely small fixes rather than feature changes, but the notes files are there to tell you which. The version number itself is not written in setup.py, where it is parsed out of the citylearn package init file with a regular expression, so the version follows the package source rather than a separate constant. The tests live in a tests directory with a pytest configuration, the test requirements are listed separately, and there is a sample_observations.txt at the top level, which is a small but useful artefact for anyone building a controller against a recorded observation space.

## Conclusion

Adopt CityLearn if you are doing reinforcement learning for demand response or load shaping and you want a published, comparable environment with a scorecard rather than a bespoke simulator you would have to defend yourself. It does not fit a project that needs a high fidelity building physics model, since the environment is a research testbed, and it does not fit an environment with a tight dependency budget, because the requirements pull in torch, torchvision, openstudio and doe_xstock alongside gymnasium. Before you start, read the release notes for 3.0.2 and 3.0.1 in the repository, because three releases landed on 2026-09-09 and the 3.x line changed the feature set substantially.

## FAQ

### What is CityLearn and who uses it?

CityLearn is an open source Gymnasium environment for building energy coordination, demand response and reinforcement learning, used to configure renewable energy communities, run controllers and compare energy, cost and service outcomes. It is distributed under the MIT licence and is developed by the Soft-CPS Research Group with project contributors.

### How do I install CityLearn?

The README gives a single command, python -m pip install citylearn, and asks for Python 3.9 or newer in a virtual environment. The installation guide covers setup and optional dependencies, including Parquet support, and the package defines parquet and pysam extras.

### How do I evaluate a controller in CityLearn?

The environment exposes an evaluate_v2 method that returns the community KPIs, which you can filter to the cost functions you care about, such as district cost and grid import energy. The documentation also covers the KPI reference, the scorecard for comparisons and the baselines guide for the reference controllers, and the CityLearn UI can inspect exported time series.

### What does CityLearn v3 add to the earlier releases?

The README says v3 brings together buildings and thermal systems, PV, batteries, EVs and flexible loads, with electrical limits, local energy sharing, changing members and assets, and data or equipment failures. The repository carries release notes files for 1.7.0, 1.8.0, 3.0.0, 3.0.1 and 3.0.2.

### Can I use CityLearn with a different Gymnasium version?

The requirements file pins gymnasium to 0.28.1 or lower, numpy below 2.0.0, scikit-learn to 1.2.2 or lower and openstudio to 3.10.0 or lower, and the package also requires torch, torchvision, doe_xstock, pandas, pyyaml and simplejson. The pinned upper bounds are the constraint to plan for if you want to share an environment with other work.

## Sources

- [citylearn-project/CityLearn on GitHub](https://github.com/citylearn-project/CityLearn)
- [Issues](https://github.com/citylearn-project/CityLearn/issues)
- [License: MIT](https://github.com/citylearn-project/CityLearn/blob/master/LICENSE)
- [README](https://github.com/citylearn-project/CityLearn/blob/master/README.md)
- [Releases](https://github.com/citylearn-project/CityLearn/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/citylearn-project-citylearn
