A practical guide to understanding and using the Peaky Flexers electricity market model
← Back to the modelPeaky Flexers is a welfare-maximising linear program (LP) for an electricity market. It answers a deceptively simple question: if we had to design an electricity system from scratch, what mix of generation and storage capacity would maximise the total value that consumers get from electricity, minus the cost of providing it?
The model works with five supply-side elements—renewables (zero variable cost), nuclear (firm baseload), gas peakers, battery storage, and transmission & distribution—and a demand side that can flex: consumers can shift demand to cheaper periods, expand consumption when prices are low, or curtail when the cost of supply exceeds the value of the electricity to them.
The key output is a set of market-clearing prices—one for each time period. These prices emerge naturally from the optimisation: they are the shadow prices on the energy-balance constraints, representing the cost to the system of serving one more megawatt-hour of demand in that period. In a well-functioning market, these are the prices that would prevail.
The model page has four input sections. Each represents a distinct part of the electricity system. Hover over the ⓘ button next to any section heading on the model page to see a condensed version of the guide for that section. Below is the full detail for each input.
Real electricity demand varies hour by hour across the year. Rather than modelling all 8,760 hours individually, the model uses a Load Duration Curve (LDC)—a standard tool in energy economics that sorts hours by demand level.
The year is divided into three blocks:
| Block | What it represents | Default hours |
|---|---|---|
| Winter Peak | The coldest, darkest hours when demand is highest | 300 |
| Shoulder | A large middle band—spring, autumn, and milder winter days | 3,000 |
| Low Demand | Summer and overnight hours when demand is lowest and (often) renewables are abundant | 5,460 |
You only set the hours for each block. The total should sum to 8,760 (one year). The demand levels come from the tier definitions below.
The wind does not blow constantly during winter, nor does the sun always shine in summer. To capture this intermittency, each LDC block is split into two sub-blocks:
For each sub-block you set two parameters:
| Parameter | Meaning |
|---|---|
| % Hours | What fraction of the block's hours fall in this sub-block (must sum to 100%) |
| % Renewables Availability | What fraction of installed renewables capacity is available during these hours |
For example, the default Winter Peak is split 10/90: for just 10% of winter peak hours (30 hours), only 15% of installed renewables capacity is available. For the remaining 90% (270 hours), 80% is available. This creates a large price differential between the two sub-blocks, and it is precisely this differential that makes storage valuable within a block—storage can charge when renewables are plentiful and discharge when they are scarce, even within the same season.
Not all electricity demand is equally valuable. A hospital's life-support system has a very different willingness to pay than a dishwasher that could run at 3am instead of 6pm. The model captures this by dividing demand in each block into three tiers:
For each tier you set three parameters:
| Parameter | Meaning |
|---|---|
| Quantity (GW) | How many gigawatts of this tier's demand are present during the block |
| VoLL (£/MWh) | Value of Lost Load: the maximum price this demand is willing to pay. If the clearing price exceeds VoLL, this demand goes unserved ("curtailed"). |
| Shift Cost (£/MWh) | The cost of moving this demand to a later, cheaper block. Demand can only shift downward: Winter Peak → Shoulder → Low Demand. It cannot shift upward. |
Expandable demand represents new consumption that would switch on if prices fell low enough—think of energy-intensive industries that would locate near cheap power, or electric heating that becomes attractive when electricity prices drop. You specify a quantity (GW) and an activation value (£/MWh). If the clearing price drops below this value, the expandable demand activates.
The model does not pre-set any generation capacities. Instead, it decides how much of each technology to build, balancing capital costs against the value of the electricity they produce. There are five supply-side elements:
Wind and solar. Once built, they produce electricity at zero fuel cost, but their output depends on weather conditions (captured by the availability profile above). The only input is the annualised capital cost (£/kW/year).
Firm baseload capacity with zero carbon emissions. You set:
Unlike renewables, nuclear is not affected by the renewables availability profile. Its effective capacity in non-peak periods is capacity × (1 − planned outage); during winter peak sub-blocks it is further derated to capacity × (1 − planned outage) × (1 − peak unavailability).
A flexible thermal plant that can run whenever needed but burns gas. You set:
Storage is specified in terms of physical CapEx parameters that are familiar to anyone looking at battery project costs:
The model converts these internally using the capital recovery factor (CRF):
When the discount rate is zero, CRF = 1/n (straight-line). For r > 0, CRF > 1/n, so annual costs reflect the time value of money.
A duration constraint ensures that energy capacity (GWh) = power capacity (GW) × duration (h), so the optimiser determines how much storage to build as a single decision, with the duration as a design parameter.
Because the LDC representation can only track one storage cycle through the sub-block sequence, the model scales the storage energy level constraints by cycles per year. This gives the LP a realistic annual throughput budget (physical energy capacity × cycles) while keeping full annual capital costs in the objective. The result is that the LP makes correct investment decisions: it compares the full annual CapEx against the full annual arbitrage value from repeated cycling.
T&D capacity represents the cost of the wires, transformers, and infrastructure needed to deliver electricity from generators to consumers. You set a single parameter:
T&D capacity is determined endogenously by the model. The constraint binds at peak total generation dispatched (renewables + gas + storage discharge) across all sub-blocks, so the system must build enough T&D to handle the highest simultaneous power flow.
The model asks: what system design and operating pattern makes society best off?
It simultaneously decides:
The objective is to maximise net welfare: the total value that consumers place on the electricity they receive, minus the total cost of providing it (both capital and operating costs).
Think of it as a social planner trying to make the best possible use of available resources. The planner values each MWh of served demand at its VoLL, credits shifted demand at VoLL minus the shift cost, and charges for nuclear and gas generation at their variable costs and all technologies at their capital costs. Internally, the model converts GW × hours into MWh (×1000) and GW/GWh into kW/kWh (×1,000,000), so all welfare and cost outputs are reported in full annual currency units rather than “thousands”.
The optimisation is subject to physical and economic constraints:
The market-clearing prices are not directly chosen. They emerge as shadow prices on the energy-balance constraints—they tell you the marginal value to society of one additional MWh of supply in each sub-block. In an efficient market, these would be the wholesale electricity prices.
For those who want the mathematics, here is the LP formulation. The model maximises:
Where b indexes blocks, i indexes sub-blocks within each block, t indexes demand tiers, h are hours, K are capacity variables, and C are capital costs. Csto_pw and Csto_en are derived from the connection and cell CapEx inputs using the CRF. The factor 1000 converts GWh into MWh in the flow terms; the factor 106 converts GW/GWh into kW/kWh in the capital terms. The variable cost of gas includes any carbon adder: cgasvar = variable_cost + carbon_price × emission_factor.
Subject to (for all sub-blocks b,i):
Shadow prices on the energy-balance constraints, divided by the sub-block's hours, give clearing prices in £/MWh.
After you click Optimise the System, the results page shows several sections. Here is what each one tells you.
A row of metric cards showing the headline results at a glance:
Two charts side by side:
Together, these tell you how the system spends its investment budget. A system dominated by renewables will have high renewables capital cost but zero variable cost. A system with more gas will have lower capital cost but higher variable cost. T&D costs appear as a separate wedge.
This shows the raw demand profile before any optimisation. Sub-blocks are sorted from highest to lowest GW, and the three demand tiers are stacked within each sub-block (low-value at the bottom, high-value at the top). This is what the demand side "looks like" before the model decides what to do with it.
Use this chart as a reference point. After you look at the optimised results, come back here to see how much has changed—how much demand was shifted, curtailed, or served by different sources.
This is the post-optimisation counterpart of the input LDC. It shows total generation in each sub-block, broken down by source (renewables in green, gas in red, storage discharge in blue). Sub-blocks are sorted by total generation from left to right.
Hover over any block in the chart to see a pop-out with the sub-block name, total generation, supply stack, and clearing price. Hatched areas indicate curtailed demand.
This chart sorts sub-blocks from highest to lowest clearing price, with price on the Y-axis and cumulative hours on the X-axis. Hover over any block to see a pop-out summarising the key activity in that sub-block:
A 2×3 grid of stacked bar charts (one per sub-block). Each chart has two bars:
All six charts share the same Y-axis so you can visually compare the scale of demand and supply across sub-blocks. The title of each chart shows the clearing price.
Storage charging appears as demand (it consumes electricity) while storage discharging appears as supply (it provides electricity). This is physically correct—charging is a load on the system.
The same structure as the GW charts, but multiplied by hours to show energy (GWh). Each sub-block has its own Y-axis scale because the blocks have very different durations (300 hours vs. 5,460 hours), which would make the shorter blocks invisible if they shared an axis.
These charts are important for understanding the total energy balance. A sub-block might have low GW but high GWh if it lasts many hours. Storage is particularly visible here: you can see how much energy in GWh is being moved around the system.
Per-block Sankey diagrams showing what happens to demand in each block. Left-side nodes are demand sources (tiers + shifted-in demand + storage charging). Right-side nodes are destinations:
The width of each flow band is proportional to the GW. This chart only appears when there is flexibility activity (shifting, curtailment, or storage) in the system.
A system-level Sankey diagram showing energy transfers between sub-blocks. This is fundamentally different from the flexibility Sankeys above: while those show what happens within a block, this shows what moves between blocks.
Left nodes are energy exporters (sub-blocks that charge storage or shed demand). Right nodes are energy importers (sub-blocks that discharge storage or receive shifted demand), plus efficiency losses.
The diagram visually distinguishes two types of flows:
This is perhaps the most informative single chart. It reveals the full pattern of energy arbitrage: where is energy coming from, where is it going, and how much is lost to efficiency?
Two charts plus summary metrics:
The final section gives you the full numerical detail for each of the 6 sub-blocks: every demand tier (available, served, lost, shifted out), every supply technology (dispatched vs available), expandable demand activation, storage level, and the clearing price.
This is where you go to verify specific numbers or understand exactly what is happening in a particular sub-block. If a Sankey diagram shows an unexpected flow, the detail tables will tell you exactly which tiers and technologies are involved.
This optional section lets you model a single household as a price-taker in the wholesale market. After the system optimisation determines clearing prices, the household minimises its annual electricity bill given those prices.
Enable it by expanding the “Household Bill Optimisation” section in the inputs and ticking the checkbox. You specify:
The output shows total annual spending (energy cost and battery cost separately), average unit cost (p/kWh), and a per-sub-block breakdown of consumption, curtailment, battery activity, and spend. This is useful for understanding how retail consumers would respond to the wholesale price signals the model produces.
Peaky Flexers includes a built-in AI assistant powered by Anthropic's Claude. Click the AI Chat button in the bottom-right corner to open the chat panel.
The AI has access to the complete model source code, your current input values, and your latest optimisation results. This means it can reason about your specific scenario—not just generic electricity economics. Good questions include:
Here is an example of the kind of conversation you might have:
The price in Winter Peak Low Renewables reflects extreme scarcity. During these 30 hours, only 15% of installed renewables capacity is available. The marginal consumer willing to pay up to their VoLL sets the price.
Looking at the results: all High-value demand (VoLL £15,000/MWh) is served, all Mid-value demand (VoLL £800/MWh) is served, but some Low-value demand (VoLL £100/MWh) may be curtailed or shifted. The clearing price settles between the VoLL of the last tier being served and the marginal cost of the next unit of supply.
Storage is discharging at full power here (if built), and gas peakers are running flat out. The high price is what is needed to justify the capital investment in gas peakers that only run for a few hundred hours per year.
To use the AI chat, you need an Anthropic API key. The model defaults to Claude Opus 4.6, which provides excellent analysis of the model's economics. To get a key:
Your API key is stored in your browser's local storage and is only sent to the Peaky Flexers server to proxy requests to Anthropic. It is never stored on the server.
The Clear Chat button resets the conversation history, which is useful when you change input parameters and run a new optimisation—the old context will no longer match the new results.