About Storage Modelling#

Storage modeling is complex because storage systems behave differently than generation or demand. Storages move energy across time, not just produce or consume it. This means:

1. Storage shifts energy in time

Unlike solar panels (generate) or homes (consume), storage stores energy when it’s abundant and releases it when needed. That means:

  • One hour cannot be looked at in isolation — what happened before matters.

  • The state of charge (SOC) today depends on all previous hours.

    Analogy: It’s like a savings account — what can be spent today depends on how much was saved earlier.

2. Time resolution matters

Should a model cover every single hour? Every day? A few representative days? Each choice impacts:

  • Accuracy (high resolution = realistic, but slow to compute)

  • Computational effort (simplified models lose detail)

    Modelling storage with only a few sample days risks missing important patterns, like a long wind lull or weekend charging habits.

3. Storage needs continuity

Many simplification techniques (e.g. clustering days) break the continuity of storage:

  • The model might forget how full the battery was yesterday.

  • Artificial resets can distort when charging/discharging actually happens.

This makes it tricky to model seasonal storage or multi-day balancing.

4. Storage interacts with everything Storage isn’t just an isolated battery:

  • It interacts with renewables (absorbing variability)

  • It supports grid reliability (fast response)

  • It responds to prices, constraints, and emissions goals

So modeling it well requires integrating with the rest of the energy system.

The four storage formulations#

Every formulation below works on the same representative days (k-means on daily wind/solar/hydro and demand profiles, see Workflow Guide). They differ in which sequence the state of charge (SoC) follows and where its bounds are checked. The model files are in models/Model_<name>/osemosys_fast_<name>.txt; Novo is implemented in Model_Cluster.

A small example makes the differences concrete. Take four days A–B–A–B, where every A day charges the battery by 6 MWh and every B day discharges it by 6 MWh, starting at 6 MWh. The hand-calculated SoC of each formulation (checked against the models in validation/soc/) is given with each description.

Welsch (fixed pattern) — “Photocopied day”#

  • How it works: the standard OSeMOSYS storage equations. The year is described by seasons, day types and daily time brackets; each day type is repeated as a block (A-A-B-B), with a fixed intra-day pattern.

  • SoC bounds: checked only at a few points: the first and last instance of each day type in the first and last week.

  • Example: 6 → 12 → 18 → 12 → 6 MWh (grouped days A-A-B-B).

  • Consequence: compact, but the day order is lost and limits can be violated between the checked points.

Kotzur (day-boundary verified) — “Daily ledger”#

  • How it works: the 365 chronological days are linked in calendar order. Each day adds the net change of its representative day.

  • SoC bounds: checked at the start of each chronological day only; within a day the level is not constrained.

  • Example: 6 → 12 → 6 → 12 → 6 MWh (chronological days A-B-A-B).

  • Consequence: keeps the seasonal sequence at low cost, but intra-day peaks and troughs can exceed the capacity (visible in the binding-capacity stress test).

Novo (time-step verified) — “Hourly ledger”#

  • How it works: every chronological time block of the year (365 × 24/h) is mapped to its representative block, and the SoC is carried from block to block in calendar order.

  • SoC bounds: checked at every chronological time block.

  • Example: 6 → 12 → 6 → 12 → 6 MWh, with the bounds respected at every block.

  • Consequence: the most faithful of the four, at the cost of the largest model (it has a SoC variable for every block of the year).

Summary#

Niet

Welsch

Kotzur

Novo

SoC follows

Representative slices in date order, each stretched by its cluster size

Grouped day types (A-A-B-B)

Chronological days

Chronological time blocks

Actual order of the 365 days

❌

❌

✅

✅

Intra-day SoC

Per representative slice

Per day type

Reconstructed after the solve

Native

Bounds checked at

Each representative slice

A few day-type instances

Day boundaries

Every time block

SoC variables (k days, b blocks/day)

k·b

per season, day type, bracket

365 + k·b

365·b

Code

Model_Niet

Model_Welsch

Model_Kotzur

Model_Cluster

At full resolution (k = 365, h = 1) the four formulations give nearly the same cost; their differences grow as the number of representative days falls. The quantitative comparison is in the Reproducibility guide guide and the reproduction notebook.