Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c8c081f
Major refactoring and speed improvements via custom backend
birnbaum Oct 14, 2026
c91f815
Add Recorder for metrics, time series, and experiment results
birnbaum Oct 16, 2026
f6f76fe
Update performance benchmarks to use Recorder
birnbaum Oct 16, 2026
3e3de1b
Reject invalid battery parameters and nonfinite power values
birnbaum Oct 17, 2026
f1916ed
Integrate battery state through the full simulation duration
birnbaum Oct 17, 2026
dbb5e6d
Respect minimum charge when dispatching CLC batteries
birnbaum Oct 17, 2026
7e44b9f
Validate trace data and reject sampling outside its bounds
birnbaum Oct 17, 2026
39e8053
grid connections are now explicit modeled as dispatchables
birnbaum Oct 17, 2026
30a2f12
simplified interface by introducing Consumer and Producer
birnbaum Oct 17, 2026
7826aa8
Rename sil integration to live
birnbaum Oct 17, 2026
7fbcde0
cleanup
birnbaum Oct 17, 2026
4a880e5
Refine actor signals and add simulation study tooling
birnbaum Oct 17, 2026
51e61f2
improved API refernce doc strings
birnbaum Oct 18, 2026
919f9ad
updated docs
birnbaum Oct 18, 2026
a86beb3
speed and correctness improvements
birnbaum Oct 18, 2026
b9e4df2
supporting up to Python 3.15
birnbaum Oct 18, 2026
3044535
updated examples and tests
birnbaum Oct 18, 2026
2ccd50a
implemented first version of new viewer
birnbaum Oct 18, 2026
091399e
ported viewer to react
birnbaum Oct 19, 2026
a73bd0b
big cleanup
birnbaum Oct 19, 2026
de5dbc1
added "Select Pareto front" button
birnbaum Oct 19, 2026
da06e96
improved docs and use cases
birnbaum Oct 19, 2026
9b936f1
fixed tests
birnbaum Oct 19, 2026
89bdb82
improved timeseries tooltip
birnbaum Oct 19, 2026
b724bea
bugfix
birnbaum Oct 19, 2026
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
27 changes: 27 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ jobs:
run: npm run build
working-directory: viewer

- name: Check viewer
run: npm run lint && npm test
working-directory: viewer

- name: Install uv
uses: astral-sh/setup-uv@v5
with:
Expand All @@ -38,6 +42,29 @@ jobs:
- name: Build package
run: uv build

- name: Install built package for browser validation
run: |
uv venv .venv
uv pip install dist/*.whl
.venv/bin/python viewer/tests/prepare.py

- name: Generate the published examples from scratch
run: |
uv pip install -r examples/requirements.txt
.venv/bin/python examples/build_gallery.py --output results/gallery
.venv/bin/python viewer/export_docs.py --source results/gallery --output results/viewer-release/gallery

- name: Install browser
run: npx playwright install --with-deps chromium
working-directory: viewer

- name: Verify local viewer and portable report
run: npm run test:browser
working-directory: viewer
env:
PLAYWRIGHT_CHANNEL: chromium
VIEWER_GALLERY_URL: http://127.0.0.1:8722/gallery/index.html

- name: Upload dist
uses: actions/upload-artifact@v4
with:
Expand Down
15 changes: 13 additions & 2 deletions .github/workflows/lint-and-unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.9, 3.13]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.15"]

steps:
- uses: actions/checkout@v4
Expand All @@ -29,6 +29,10 @@ jobs:
run: npm run build
working-directory: viewer

- name: Test viewer data format
run: npm test
working-directory: viewer

# Python setup
- name: Install uv and set python version to ${{ matrix.python-version }}
uses: astral-sh/setup-uv@v5
Expand All @@ -40,7 +44,7 @@ jobs:
run: uv pip install hatch-vcs

- name: Install dependencies
run: uv pip install -e ".[dev,sil]"
run: uv pip install -e ".[dev,live,mosaik]"

# type checking
- name: Run type checker
Expand All @@ -53,3 +57,10 @@ jobs:
# unit-testing
- name: Run tests
run: make test

- name: Check reproducible examples with pinned dependencies
if: matrix.python-version == '3.13'
run: >-
uv run --extra dev --extra live --extra mosaik
--with-requirements examples/requirements.txt
pytest tests/test_examples.py tests/test_infrastructure_study.py tests/test_microgrid_comparison.py
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,8 @@ crash.log
# Sphinx
_build
results.csv

# Browser validation outputs
viewer/test-results/
viewer/playwright-report/
/test-results/
4 changes: 1 addition & 3 deletions .readthedocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@ build:
jobs:
post_create_environment:
- pip install uv
pre_install:
- cd viewer && npm ci && npx vite build
post_install:
- uv pip install -e ".[docs,sil]"
- uv pip install -e ".[docs,live]"
pre_build:
- make docs-viewer

Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Install dependencies (requires [uv](https://docs.astral.sh/uv/)):

```bash
uv pip install -e ".[dev,sil,docs]"
uv pip install -e ".[dev,live,docs]"
```

## Viewer
Expand Down Expand Up @@ -61,4 +61,4 @@ make docs-viewer
## Release

1. Create a GitHub release (tag triggers the `publish.yml` workflow)
2. The workflow builds the viewer, bundles it into the Python package, and publishes to PyPI
2. The workflow builds the viewer, bundles it into the Python package, and publishes to PyPI
23 changes: 10 additions & 13 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,28 +1,25 @@
.PHONY: docs docs-viewer serve-docs typecheck lint test check

CHECK_RUN = uv run --extra dev --extra live --extra mosaik

check: typecheck lint test

typecheck:
uv run mypy vessim
$(CHECK_RUN) mypy vessim

lint:
uv run ruff check vessim
$(CHECK_RUN) ruff check vessim

test:
uv run pytest
$(CHECK_RUN) pytest

docs: docs-viewer
uv run mkdocs build
uv run --extra docs mkdocs build

serve-docs: docs-viewer
uv run mkdocs serve --watch docs
uv run --extra docs mkdocs serve --watch docs

docs-viewer:
cd viewer && [ -d node_modules ] || npm ci
cd viewer && VITE_STATIC_BASE="./" npx vite build --outDir dist
cp -r viewer/dist/. docs/viewer/
uv run python examples/basic_example.py
mkdir -p docs/viewer/results
cp results/basic_example/metadata.yaml docs/viewer/results/
cp results/basic_example/timeseries.csv docs/viewer/results/
echo '{"mode":"single","experiments":[{"name":"","status":"completed"}]}' > docs/viewer/experiments.json
npm --prefix viewer ci
npm --prefix viewer run build
uv run --with-requirements examples/requirements.txt python viewer/export_docs.py
106 changes: 61 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,80 +2,96 @@
<img alt="Vessim Logo" src="docs/assets/logo.png" width="250" />
</p>

Vessim is a **co-simulation testbed for microgrids**\*
Vessim is a Python library for simulating datacenter energy systems.

Vessim lets you model how loads, (renewable) generation, energy storage, and the public grid interact, and test control and dispatch strategies against them, in pure simulation or with **real software and hardware** in the loop.
Vessim models the interaction between datacenter demand and energy infrastructure, helping you understand how design and control decisions affect cost, carbon emissions, and resilience.

*\*By "microgrid" we mean any local energy system that combines loads, generation, and storage behind a grid connection. Grids can scale from small battery-constrained systems to a GW-scale datacenter campus.*
[Documentation](https://vessim.readthedocs.io/en/latest/) · [Getting started](https://vessim.readthedocs.io/en/latest/getting_started/) · [Explore example results](https://vessim.readthedocs.io/en/latest/vessim_viewer/)

Check out the [website and documentation](https://vessim.readthedocs.io/en/latest/)!
- **Simulation studies:** Compare infrastructure and control strategies across demand, weather, and operating scenarios.
Explore design choices through parameter sweeps, optimization, or probabilistic simulations.
- **Live modeling:** Connect real applications to a simulated energy system through REST APIs.
Test how a scheduler or controller responds to changing power availability.
- **Digital twins:** Model an energy system alongside the real installation using live measurements.
Explore possible futures, such as prolonged low renewable generation or a grid outage.

## What can I do with Vessim?
## Getting started

Vessim helps you understand and optimize how loads, energy sources, and storage interact.
It was originally designed for energy-aware and carbon-aware data centers, but has also been used for other use cases such as battery price arbitrage and demand response for cooling warehouses.

- **Control and dispatch strategies**: Test battery charging/discharging, load-shifting, and price- or carbon-driven dispatch.
- **Demand response and power outages**: Simulate demand response signals or power outages to understand your system's flexibility and test mitigation strategies.
- **Microgrid composition**: Experiment with adding solar panels, wind turbines, or batteries to see how they would affect your energy costs and carbon emissions.
- **Energy- and carbon-aware computing**: Develop applications that adapt their energy consumption to the carbon intensity and price of electricity.

Vessim can simulate multiple distributed microgrids in parallel and easily integrates historical datasets and new simulators.
Vessim’s software-in-the-loop capabilities let you run real systems against simulated microgrids. Connect live data sources like Prometheus and interact through REST APIs.
Install Vessim with Python 3.9 to 3.15:

```console
python -m pip install vessim
```

## Simple Example
This example models three days of datacenter demand, solar generation, and battery operation.
Surplus solar power charges the battery, which supplies demand when solar generation falls.
The grid supplies any remaining deficit and receives surplus power once the battery is full.

The scenario below simulates a microgrid with a computing system drawing 700W, a solar panel, and a 1.5 kWh battery.
Download [solar_example.csv](https://raw.githubusercontent.com/dos-group/vessim/main/datasets/solar_example.csv) into your working directory, then run:

```python
import vessim as vs

environment = vs.Environment(sim_start="2022-06-09", step_size=300)
# Scale the solar profile to a 2.4 kW installation.
solar = vs.Trace.from_csv("solar_example.csv", column="Berlin", scale=2400)

environment.add_microgrid(
name="datacenter",
env = vs.Environment(sim_start="2026-01-01", step_size=5 * 60)
env.add_microgrid(
name="site",
actors=[
vs.Actor(name="server", signal=vs.StaticSignal(value=700), consumer=True),
vs.Actor(name="solar_panel", signal=vs.Trace.from_csv(
"datasets/solar_example.csv", column="Berlin", scale=5000
)),
],
dispatchables=[
vs.SimpleBattery(name="battery", capacity=1500, initial_soc=0.8, min_soc=0.3)
vs.Consumer("load", signal=700),
vs.Producer("solar", signal=solar),
],
dispatchables=[vs.SimpleBattery("battery", capacity=1000), vs.GridConnection()],
)
recorder = env.add_controller(vs.Recorder("results/basic_example"))
env.run(until=3 * 24 * 3600)

metrics = recorder.metrics["microgrids"]["site"]
print(f"Grid import: {metrics['grid_import'] / 1000:.2f} kWh")
print(f"Grid export: {metrics['grid_export'] / 1000:.2f} kWh")
```

environment.add_controller(vs.CsvLogger("results/my_experiment"))
environment.run(until=24 * 3600)
```text
Grid import: 26.71 kWh
Grid export: 11.08 kWh
```

Check out the [Getting Started walkthrough](https://vessim.readthedocs.io/en/latest/getting_started/) and [`examples/`](examples/) for software-in-the-loop simulations.
[Explore this example in your browser](https://vessim.readthedocs.io/en/latest/viewer/index.html?study=getting-started#results), or open your saved results locally:

```console
python -m vessim view results/basic_example
```

## Installation
The [Getting Started walkthrough](https://vessim.readthedocs.io/en/latest/getting_started/) explains how the components work together.
To use your own data, see [Actors and Signals](https://vessim.readthedocs.io/en/latest/concepts/signals/).

You can install the [latest release](https://pypi.org/project/vessim/) of Vessim
via [pip](https://pip.pypa.io/en/stable/quickstart/):
## Explore further

```
pip install vessim
```
| Example | What you can explore |
| --- | --- |
| [Exploring microgrid configurations](examples/infrastructure_study.py) | Compare wind, solar, and battery configurations over a year. [View results](https://vessim.readthedocs.io/en/latest/viewer/index.html?study=infrastructure-year#results) |
| [Optimization](https://vessim.readthedocs.io/en/latest/concepts/optimization/) | Search for designs with different cost and emissions trade-offs. [View results](https://vessim.readthedocs.io/en/latest/viewer/index.html?study=optuna-search#results) |
| [Dispatch strategies](examples/strategy_comparison.py) | Compare control policies across demand scenarios. [View results](https://vessim.readthedocs.io/en/latest/viewer/index.html?study=dispatch-comparison#results) |
| [Demand response](examples/demand_response.py) | Adjust workload concurrency under a flexible grid connection agreement |
| [Custom metrics](examples/custom_metrics.py) | Calculate electricity costs, emissions, and your own study objectives |

If you require software-in-the-loop capabilities, install the `sil` extension:
[Run the examples locally](examples/README.md) with a shared setup and reproducible commands.

```
pip install vessim[sil]
```
For [live measurements and REST control](https://vessim.readthedocs.io/en/latest/concepts/live/), install `pip install 'vessim[live]'` and use `vs.Environment.live()`.
To [connect external simulators](https://vessim.readthedocs.io/en/latest/concepts/coupling/) through Mosaik, install `pip install 'vessim[mosaik]'`.

## Publications

If you use Vessim in your research, please cite our paper:
If you use Vessim in your research, please cite:

Philipp Wiesner, Ilja Behnke, Paul Kilian, Marvin Steinke, and Odej Kao.
[**Vessim: A Testbed for Carbon-Aware Applications and Systems.**](https://dl.acm.org/doi/pdf/10.1145/3727200.3727210)
*ACM SIGENERGY Energy Informatics Review 4 (5)*, 2024.

- Philipp Wiesner, Ilja Behnke, Paul Kilian, Marvin Steinke, and Odej Kao. "[Vessim: A Testbed for Carbon-Aware Applications and Systems.](https://dl.acm.org/doi/pdf/10.1145/3727200.3727210)" _ACM SIGENERGY Energy Informatics Review 4 (5)_. 2024.
More papers are listed in the [documentation](https://vessim.readthedocs.io/en/latest/publications/)!

For details in Vessim's software-in-the-loop simulation methodology, refer to:

- Philipp Wiesner, Marvin Steinke, Henrik Nickel, Yazan Kitana, and Odej Kao. "[Software-in-the-Loop Simulation for Developing and Testing Carbon-Aware Applications.](https://doi.org/10.1002/spe.3275)" _Software: Practice and Experience, 53 (12)_. 2023.
## About

For more related papers and concrete use cases, please refer to the [documentation](https://vessim.readthedocs.io/en/latest/publications).
Vessim was originally developed at the research group for [Distributed and Operating Systems](https://distributedsystems.berlin/) at TU Berlin. It is now maintained at the [Machine Learning Systems Lab](https://mlsys.cst.cam.ac.uk/) at the University of Cambridge.
82 changes: 82 additions & 0 deletions benchmarks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Benchmarks

These scripts measure simulation speed, recorder overhead, and viewer indexing.
Run them from a checkout installed with `pip install -e .`.
Install `pip install -e '.[mosaik]'` only when comparing backends.
Write reports under `results/` to keep generated measurements out of the repository.

## Backend workloads

```console
python benchmarks/run.py --backend native --output results/benchmarks/native.json
python benchmarks/run.py --backend mosaik --output results/benchmarks/mosaik.json
```

Six workloads cover static inputs, traces, CSV recording, multiple microgrids, mixed sampling intervals, and controller feedback.
Reports contain median setup and execution times, individual timing samples, peak traced Python memory, and trajectory checksums.
Each workload retains its trajectory in memory with metrics disabled, and the CSV case additionally writes it to disk.
Repeated runs must produce the same checksum.
Imports and trace loading are outside the timed workloads, except for lazy backend initialization on first use.
Memory measurements run separately from timing measurements.

Use `--steps` and `--repeats` to change the workload size.
To profile all six workloads separately:

```console
python benchmarks/run.py --output results/benchmarks/native.json --profile results/benchmarks/profiles
python -m pstats results/benchmarks/profiles/traces.prof
```

The fixed `input.csv` is also used to compare native and Mosaik trajectories in `tests/test_backends.py`.
The tests compare every timestamp and recorded field, allowing floating-point roundoff in numeric values.
Separate behavioral tests check power balance, battery state, sampling, and controller ordering against expected results.
Historical timing reports remain available in Git history.

## Study throughput and memory

```console
python benchmarks/studies.py --runs 1000 --output results/benchmarks/studies-native.json
```

This measures sequential scenarios with a shared trace and metrics-only recording, including model construction and cleanup.
It also compares Python allocation peaks for no recorder, metrics only, and full in-memory trajectories at 1,000 and 10,000 steps.
Use `--backend mosaik` for a backend comparison, or `--runs`, `--steps`, and `--memory-steps` for shorter checks.
Traced allocations are not total process memory.

## Annual workloads and parallel execution

```console
python benchmarks/native_study.py --quiet --output results/benchmarks/annual
```

This uses the infrastructure example's synthetic inputs with three reference configurations.
It measures the cost of observations, built-in metrics, custom metrics, and recording to memory or disk over a simulated year.
It also includes five-minute simulations, profiles, and throughput at different worker counts.
Reports separate input preparation, model setup, and simulation time.
Parallel measurements include process startup and writing experiment records, and check that results agree across worker counts.

For a short throughput check:

```console
python benchmarks/native_study.py --quiet --throughput-only --jobs 6 --workers 1 2 --output results/benchmarks/parallel
```

## Viewer

```console
node viewer/benchmark.mjs 10000
```

This measures indexing synthetic configurations in JavaScript, excluding browser rendering and network requests.
For browser checks with 10,000 recorded summaries, use the existing viewer test fixtures:

```console
npm --prefix viewer ci
npm --prefix viewer run build
python viewer/tests/prepare.py
python -m vessim view results/viewer-release/input/large
```

Both viewer datasets are synthetic fixtures, not simulation results.
Compare timings on the same machine with the same dependencies and workload settings.
Profiling adds overhead, so profile timings should not be compared with ordinary execution times.
Loading
Loading