Power & Energy Engineering · battery storage
The extra containers
Two simulated battery parks run through ten real years of German power prices. One chases every spread and the other prices its own wear, and the crane comes for both.
Overview
Two identical simulated parks, each 50 MW / 100 MWh of lithium-iron-phosphate containers, trade the real German day-ahead market from January 2016 to October 2026 (German–Austrian–Luxembourg prices until September 2018, German–Luxembourg after that; hourly until September 2025, then 15-minute). Each day is dispatched with a linear program that knows that day's day-ahead prices. The left park takes every spread that beats its conversion losses. The right park solves the same problem, but every megawatt-hour it discharges pays a wear price, which we derive from the ageing model. Spreads too shallow to pay for that wear are left alone.
A published cell-ageing model for LFP cells (Naumann et al., calendar ageing 2018, cycle ageing 2020) tracks every container. Calendar loss depends on time, temperature and state of charge. Cycle loss depends on depth and rate, and every half-cycle in the state-of-charge trace is counted. Whenever a park's usable energy falls below its 100 MWh target, a fresh container is added and starts ageing from new. In the film, a crane sets each one down on the day the simulation adds it.
The result is less dramatic than the usual story. Pricing wear made the right park discharge 11% less energy, and each of its containers arrived weeks to months later. Yet both yards needed the same six. About half the fade is calendar ageing, which continues while a battery stands still. Both parks trade day-ahead only, a price-taking simulation with perfect foresight of each day's prices. Real storage revenue also comes from balancing and intraday markets.
What we built
- A day-by-day dispatch of two 50 MW / 100 MWh parks over 3,951 days of real SMARD day-ahead prices, as a linear program solved with HiGHS.
- An ageing-priced strategy: the wear price per MWh discharged is derived from the same cycle-ageing model and a stated replacement cost of capacity.
- The Naumann et al. LFP calendar and cycle ageing model, applied per container cohort, with half-cycle counting on the state-of-charge trace.
- An augmentation rule that adds a 5 MWh container whenever usable energy drops below 100 MWh, with each new container ageing from its own install date.
- A deterministic 3D film where white light is the energy flowing through the racks, emerald shows the share of energy the right park declined to trade, and every container lands on its simulated date.
Tech stack
Python, NumPy, pandas, SciPy (HiGHS linear programming), three.js (WebGL), Playwright headless capture, FFmpeg.
Numbers
| Number | How we measured it |
|---|---|
| 6 and 6 extra containers; the wear-pricing park's arrived 44 to 196 days later each. | Measured in the simulation: the dates on which each park's usable energy first fell below 100 MWh. Assumptions: 5 MWh per container, a 110 MWh start (22 containers), cells at 25 °C, a 1 January 2016 to 25 October 2026 price history. |
| 11% less energy discharged (670 GWh vs 595 GWh) for less than 1% of day-ahead revenue (€31.1 M vs €30.9 M). | Summed over all 3,951 simulated days. Assumptions: perfect foresight of each day's day-ahead prices, 87% round-trip efficiency, a €3.8/MWh wear price, day-ahead revenue only. The bound includes the gap left by our no-MILP charge/discharge fix. |
| 27.9% vs 27.2% capacity lost by an original container after 10.8 years: 14.3 points from calendar ageing in both parks, 13.6 vs 12.9 from cycling. | This is the model output for the first 22 containers on the last simulated day. |
Prices: Bundesnetzagentur | SMARD.de, licensed CC BY 4.0. Ageing model: Naumann et al., Journal of Energy Storage 17 (2018) and Journal of Power Sources 451 (2020).