Skip to content

API reference

Data

openenergy.data.series

Validated time series (prices, capacity factors) on a regular UTC grid.

PriceSeries dataclass

Prices in currency/MWh, each timestamp labelling the start of its interval.

between

between(start=None, end=None)

Return the whole UTC days from start to end, both inclusive.

day

day(day)

Return the prices for one UTC day; the day must be fully covered.

complete_days

complete_days()

UTC days with every interval present and no missing prices.

ProfileSeries dataclass

Capacity factors in [0, 1] for one technology; clipped counts values capped on load.

between

between(start=None, end=None)

Return the whole UTC days from start to end, both inclusive.

day

day(day)

Return the capacity factors for one UTC day; the day must be fully covered.

complete_days

complete_days()

UTC days with every interval present and no missing values.

fill_gaps

fill_gaps(
    series: PriceSeries, max_gap_hours: float
) -> tuple[PriceSeries, int]
fill_gaps(
    series: ProfileSeries, max_gap_hours: float
) -> tuple[ProfileSeries, int]
fill_gaps(
    series: IntensitySeries, max_gap_hours: float
) -> tuple[IntensitySeries, int]
fill_gaps(series, max_gap_hours)

Linearly interpolate interior gaps no longer than max_gap_hours.

Returns the filled series and the number of intervals filled.

openenergy.data.opsd

Open Power System Data (OPSD) time-series CSV source.

OPSDCsvSource

Reads day-ahead prices and capacity-factor profiles from an OPSD singleindex CSV.

zones

zones()

Bidding zones with a day-ahead price column.

technologies

technologies(zone)

Technologies with a capacity-factor profile for zone.

prices

prices(zone, start=None, end=None)

Day-ahead prices for zone over whole UTC days, start and end inclusive.

profile

profile(zone, technology, start=None, end=None)

Capacity factors for technology in zone, capped to [0, 1].

series

series(column, start=None, end=None)

Any column as a validated series over whole UTC days, start and end inclusive.

load

load(zone, start=None, end=None)

Actual load in MW (ENTSO-E Transparency).

generation

generation(zone, technology, start=None, end=None)

Actual generation in MW for technology (e.g. solar, wind).

openenergy.data.ember

GB hourly day-ahead prices from Ember, converted to GBP with ECB reference rates.

EmberPriceSource

Reads gb_day_ahead_ember.csv and ecb_gbp_per_eur.csv written by the fetch script.

rates defaults to ecb_gbp_per_eur.csv next to the price file.

prices

prices(zone='GB_GBN', start=None, end=None)

Hourly GBP/MWh over whole UTC days, start and end inclusive.

gb_prices

gb_prices(paths, start, end)

GB hourly day-ahead prices from OPSD and/or Ember files, earlier files taking priority.

Each file's type is read from its header. A later file only adds hours outside the span already covered, never filling gaps inside it (Ember interpolates its own gaps). Returns the prices and the attribution of every file that supplied at least one value.

openenergy.data.update

Download the latest GB day-ahead prices (Ember) and ECB GBP rates.

cache_dir

cache_dir()

$XDG_CACHE_HOME/openenergy/prices, defaulting to ~/.cache/openenergy/prices.

update_prices

update_prices(directory)

Write Ember hourly UK prices (EUR, unchanged) and ECB daily GBP rates to directory.

Returns the price file, its number of hours and the first and last UTC hour.

openenergy.data.carbon

GB carbon intensity from NESO's Carbon Intensity API extract.

IntensitySeries dataclass

Average grid carbon intensity in gCO2/kWh (= kg/MWh).

rejected counts values outside [0, 1000] that were treated as missing.

CarbonIntensitySource

Reads utc_timestamp, forecast, actual half-hourly CSVs written by the fetch script.

intensity

intensity(kind='actual', start=None, end=None, step=None)

Intensity over whole UTC days, optionally averaged to a coarser step.

An averaged interval is missing unless every half-hour in it is present.

openenergy.data.neso

GB half-hourly generation by fuel from NESO's historic generation mix extract.

GenerationMixSource

Reads the utc_timestamp, <fuels>, generation CSV written by the fetch script.

mix

mix(start=None, end=None, step=None)

Generation by fuel and in total (MW) over whole UTC days, start/end inclusive.

Averaging to a coarser step leaves an interval missing unless every half-hour in it is present.

openenergy.data.response

GB dynamic frequency response (DC, DM, DR): auction clearing prices and utilisation.

Service dataclass

A response product: low exports when frequency falls, high imports when it rises.

envelope lists (deviation beyond 50 Hz, fraction of contracted MW) points after the deadband, from NESO's Response Services Service Terms; delivery is linear between them.

delivered

delivered(frequency_hz)

Fraction of the contracted MW delivered at each frequency sample.

ResponseMarket dataclass

Clearing prices (GBP/MW/h) per delivery block and monthly utilisation per service.

prices is indexed by block start (UTC) with an end column and one column per service; utilisation is indexed by month start (UTC).

utilisation_at

utilisation_at(starts)

Each block's monthly factors; months without frequency data use the overall mean.

ResponseSource

Reads the block prices and monthly utilisation CSVs written by the fetch script.

utilisation

utilisation(frequency_hz)

Average delivery per contracted MW (= MWh per MW per hour) over the samples.

openenergy.data.fleet

GB fleet capacity, efficiency, fuel prices and carbon prices for the system model.

SystemInputs

Reads the system CSVs written by scripts/fetch_system_data.py from one directory.

unit_parameters

unit_parameters()

Unit size (MW), minimum stable output (share) and start cost (GBP/MW) by technology.

capacity

capacity(index)

MW at each timestamp, linear in time between end-of-year values.

efficiency

efficiency(year)

Fleet-average thermal efficiency (gross calorific value) for year.

A blank value (no station of that type ran) falls back to the latest earlier year.

fuel_prices

fuel_prices(index)

GBP per MWh of fuel at each timestamp, from the quarter it falls in.

Blank (suppressed) prices carry forward from the latest earlier quarter.

carbon_prices

carbon_prices(index)

ETS price (GBP/t) and carbon price support (GBP/MWh of fuel) at each timestamp.

Each row applies from its valid_from date (1 April) to the next. The ETS is the EU ETS to 2020 and the UK ETS from 2021; prices in USD or EUR convert at the stored ECB rates. Files with only eu_ets_usd_per_t (v3.1 and earlier) are still read.

openenergy.data.interconnectors

GB interconnectors: capacity (NESO register), in-service dates and flows (Elexon), and neighbouring markets' day-ahead prices (Ember).

Interconnector dataclass

One GB link: firm import and export capacity (TEC) and the day it entered service.

in_service comes from the bundled Elexon flows (see IN_SERVICE_DAYS), or is None if the link was already in service when the record starts. import_availability and export_availability are the shares of a price taker's trade the link actually made (availability_from_flows); 1 when no fitted file is present.

NeighbourPriceSource

Reads europe_day_ahead_ember.csv (EUR/MWh) and converts with the ECB rate file.

prices

prices(neighbour, index, max_gap_hours=3)

GBP/MWh on index; gaps up to max_gap_hours are interpolated.

interconnectors

interconnectors(system_dir)

Built links from interconnectors.csv with in-service dates from the flow record, and their availability from interconnector_availability.csv when present.

availability_from_flows

availability_from_flows(system_dir, gb_prices, neighbours)

Per link, the share of a price taker's imports and exports that actually flowed.

A price taker on gb_prices (actual hourly GBP/MWh, the fitting years) imports at full capacity whenever GB is dearer than the neighbour and exports whenever it is cheaper, from the day the link entered service. Each share is the actual energy (Elexon) over that trade, at most 1. Modelled prices are never used. Links with no trade in the period get the mean of the others.

interconnector_flows

interconnector_flows(system_dir)

Daily MWh per link, columns <fuel type>_import_mwh and _export_mwh.

openenergy.data.fes

NESO Future Energy Scenarios: capacity, storage and demand by pathway and year.

FesSource

Reads the ES1 and ED1 tables written by scripts/fetch_fes.py (unchanged).

capacity_mw

capacity_mw(pathway, year)

Installed capacity (MW) by FES technology type, all connections summed.

storage_gwh

storage_gwh(pathway, year)

Storage energy capacity (GWh) by FES technology type.

demand_gwh

demand_gwh(pathway, year)

Annual GB system demand (GWh, fiscal year).

years

years(pathway)

Years with capacity data for the pathway.

supply

supply(pathway, year, variable)

Any ES1 variable (e.g. Export (TWh)) summed by technology type.

openenergy.data.quality

Transparent filters for telemetry glitches in metered time series.

flag_glitches

flag_glitches(
    values, spike_fraction=0.25, dropout_fraction=0.5
)

Flag isolated spikes and dropouts in a smooth series such as demand.

A spike deviates from its centred 5-interval median by more than spike_fraction of the series' 99th percentile; a dropout falls below dropout_fraction of its centred 7-day median. Genuine ramps sit on their rolling median, so are not flagged.

Assets

openenergy.assets.battery

Battery specification, state and physics.

BatteryState dataclass

Physical state carried between intervals.

BatterySpec dataclass

Battery ratings. SOC limits are fractions of usable (degraded) energy.

DispatchOutcome dataclass

State after a dispatch and the stored energy at each interval boundary.

apply_dispatch

apply_dispatch(
    spec,
    state,
    charge_mw,
    discharge_mw,
    step_hours,
    response_charge_mw=None,
    response_discharge_mw=None,
)

Apply a charge/discharge schedule (MW per interval) and return the new state.

response_charge_mw/response_discharge_mw are average power moved by frequency response in each interval; they may overlap (low and high services in one interval).

age

age(spec, state, hours)

Advance calendar age and recompute state of health from lifetime totals.

openenergy.assets.renewable

Renewable plants driven by capacity-factor profiles.

RenewableSpec dataclass

A plant of capacity_mw whose output declines by degradation_per_year.

generation_mw

generation_mw(profile, start_age_years=0.0)

Available output in MW: capacity x capacity factor x (1 - degradation)^age.

Age is start_age_years at the first interval of profile and grows with time.

Dispatch

openenergy.dispatch.arbitrage

Single-battery energy arbitrage as a mixed-integer linear programme (HiGHS).

DispatchConfig dataclass

Optimisation settings.

end_soc is a fraction of usable energy; None means return to the starting energy. max_cycles caps equivalent full cycles over the horizon (the backtest scales it by the days planned, so it stays a per-day cap). lookahead_days makes the backtest plan each day together with that many following days of forecasts, settling only the first; the end-energy target then applies at the end of the last day.

DispatchPlan dataclass

Optimal schedule in MW per interval and stored energy at each boundary (T+1).

With frequency response, reserve_mw is MW held per block and service, and response_charge_mw/response_discharge_mw the expected average power it moves. expected_revenue covers energy; expected_response the availability payments.

Reserve dataclass

Frequency-response options over a horizon.

block maps each interval to a row of price (-1 where nothing can be reserved). price (GBP/MW/h) and utilisation (MWh delivered per MW per hour) have one row per block and one column per service. fixed holds MW already committed (NaN where free). beyond_hours is how long each block runs past the horizon end; the plan keeps enough energy to stay idle through it. With energy_paid, delivered energy is bought or sold at the interval's price; otherwise it is free, as under NESO's ABSVD adjustment.

signs

signs()

+1 for services that export (low), -1 for those that import (high).

flows

flows(reserve_mw)

Average charge and discharge MW from responding, per interval.

payment

payment(reserve_mw, price, step_hours)

Availability payments for the intervals in the horizon at price per block.

optimise_dispatch

optimise_dispatch(
    spec,
    state,
    prices,
    step_hours,
    config=None,
    reserve=None,
)

Maximise arbitrage (and response) revenue less degradation cost over the horizon.

energy_window

energy_window(spec, state, config)

Stored-energy bounds and the end-of-horizon target, in MWh.

openenergy.dispatch.colocated

A renewable plant and a battery behind one grid connection.

GridConnection dataclass

Connection limits in MW; premium_per_mwh is paid on renewable output delivered.

import_limit_mw defaults to the export limit; set it to 0 to forbid grid charging.

ColocatedPlan dataclass

Flows in MW per interval; export_mw is net (negative when importing).

optimise_colocated

optimise_colocated(
    spec,
    state,
    available_mw,
    prices,
    step_hours,
    grid,
    config=None,
)

Maximise market revenue plus premium less degradation cost for plant and battery.

dispatch_plant

dispatch_plant(available_mw, prices, step_hours, grid)

A plant on its own: export up to the limit unless price plus premium is negative.

Forecasting

openenergy.forecast.baseline

Baseline price forecasters.

Forecaster

Bases: Protocol

Predicts prices for index from history observed before the decision time.

PerfectForesight

Returns the actual prices; the upper-bound benchmark.

NaiveLastWeek

Same interval one week earlier, or two weeks earlier where that is missing.

NoisyForesight

Actual prices plus Gaussian error, seeded per forecast window for reproducibility.

openenergy.forecast.ml

Gradient-boosted price forecaster (requires the openenergy[forecast] extra).

GradientBoostingForecaster

Histogram gradient boosting on calendar and lagged prices, trained chronologically.

fit

fit(prices)

Train on every interval of prices; forecasts must start after its last timestamp.

make_features

make_features(index, prices, lead_hours)

Calendar and lagged-price features; lags at or after the decision time are NaN.

The decision time for an interval is its UTC day start minus lead_hours, matching the backtest, so training rows see exactly what a live forecast would.

Backtesting and metrics

openenergy.backtest.engine

Rolling day-ahead backtest: plan on forecasts, settle at actual prices.

BacktestConfig dataclass

lead_hours: how long before each UTC day the plan is fixed (12 = noon the day before).

Site dataclass

Renewable output (MW, on the price grid) sharing grid with the battery.

Output is known when planning (perfect generation foresight).

Carbon dataclass

Actual grid carbon intensity (gCO2/kWh) for accounting and, with a price, for dispatch.

planning sets what dispatch sees: last_week uses the same interval a week earlier (two weeks if missing), cut off at the decision time like prices; actual uses the real values and is an upper bound. NESO's archived "forecast" is not used: it is the last forecast before each half-hour, so planning on it the day before would look ahead. Reported money stays market revenue.

BacktestResult dataclass

Interval and daily results; skipped maps unsimulated days to the reason.

PlantResult dataclass

A renewable plant operated on its own connection.

run_backtest

run_backtest(
    spec,
    prices,
    forecaster,
    config=None,
    days=None,
    site=None,
    carbon=None,
    response=None,
)

Simulate each UTC day in order; the battery ages through every calendar day in range.

With a site, the battery shares a grid connection with renewable output. With carbon, emissions are reported and a carbon price steers dispatch. With response, frequency-response availability is sold alongside trading.

run_plant_backtest

run_plant_backtest(
    site, prices, forecaster, config=None, days=None
)

The plant alone: curtail when forecast price plus premium is negative, settle at actual.

openenergy.backtest.response

Frequency response stacked with trading in the rolling backtest.

Response dataclass

Frequency-response services the battery may sell alongside trading.

MW for each block are fixed in the plan for the day the block starts in and paid the actual clearing price. forecast sets the price planned on: previous_day uses the latest earlier block at the same time of day whose result was public at the decision time; actual uses the clearing price itself (an upper bound). energy_paid buys and sells the energy delivered at the trading price instead of treating it as free.

DayReserve dataclass

A day's reserve options, the actual clearing prices of its blocks and their market rows.

Stacking

Builds each day's reserve options and carries blocks committed across midnight.

positions

positions(index)

Market row of the block covering each timestamp, or -1.

missing

missing(day)

Why the day cannot be settled, if a block covering it has no clearing price.

day_reserve

day_reserve(horizon)

Options for the horizon; blocks starting before it keep their committed MW.

commit

commit(day, held, day_end)

Remember MW for blocks that start before day_end and run past it.

openenergy.metrics.summary

Summary metrics for backtest results.

Summary dataclass

Headline results. Money is in currency; energy in MWh.

For a co-located site, revenue covers the whole site and the plant fields are set; per-MW and captured-spread figures apply to battery-only runs. With frequency response, revenue includes response_revenue (availability payments).

total property

total

Market revenue plus any premium.

summarise

summarise(result, benchmark=None)

Summarise result; capture_ratio compares with benchmark on common days.

openenergy.metrics.capture

Captured price: what the market pays a generator for the hours it actually produces.

CaptureMetrics dataclass

Market value of a generation profile. Prices in currency/MWh, energy in MWh.

capture_metrics

capture_metrics(
    prices, generation_mw, capacity_mw, label="all"
)

Captured price, capture rate and related metrics over intervals with both values.

Capture rate is captured price divided by the time-weighted (baseload) price.

capture_by_year

capture_by_year(prices, profile)

Capture metrics per calendar year (UTC) for 1 MW of profile.

System analysis

openenergy.system.netload

Net load: demand left for dispatchable plant once wind and solar are subtracted.

NetLoadStats dataclass

One calendar year of net load. Power in MW, energy in MWh, price slope per GW.

Ramps are 99th percentiles of absolute changes, so a single bad reading cannot set them.

SystemData dataclass

Load, wind and solar (MW) and price on one UTC index.

load_flagged counts load readings removed as telemetry glitches.

load_system

load_system(
    source, zone, start=None, end=None, clean_load=True
)

Load, wind and solar generation and day-ahead price, with load glitches removed.

net_load

net_load(system, scale_wind=1.0, scale_solar=1.0)

Load minus wind and solar, each scaled to model more (or less) installed capacity.

surplus

surplus(net, must_run_mw=0.0)

Renewable output the system cannot use: how far net load falls below the must-run floor.

duration_curve

duration_curve(net)

Values sorted high to low, indexed by the share of time at or above each value.

netload_by_year

netload_by_year(
    system, scale_wind=1.0, scale_solar=1.0, must_run_mw=0.0
)

Net-load statistics per UTC calendar year.

The price slope is only meaningful for the system as it was, so it is omitted when renewables are scaled.

openenergy.system.storage

How much system surplus a storage fleet can absorb.

FleetResult dataclass

Surplus absorbed by a fleet of power_mw and hours duration; energy in MWh.

residual is net load after storage, relative to the must-run floor's surplus rule.

absorb_surplus

absorb_surplus(
    net,
    power_mw,
    hours,
    eta_charge=0.95,
    eta_discharge=0.95,
    must_run_mw=0.0,
)

Charge from surplus (net load below must_run_mw), release when net load is above it.

Greedy in time order: release only as much as keeps net load at or above the floor, so storage never creates new surplus. Missing hours are idle.

sizing_grid

sizing_grid(
    net,
    powers_mw,
    hours,
    eta_charge=0.95,
    eta_discharge=0.95,
    must_run_mw=0.0,
)

absorb_surplus for every power and duration combination.

System model

openenergy.system.model

System dispatch: what runs each hour, at what cost, and the price it implies.

Generator dataclass

One generator or tranche: available MW and marginal cost (GBP/MWh) per snapshot.

must_run units produce exactly their available output (historic profiles). emissions_t_per_mwh is tCO2 per MWh of electricity.

Storage dataclass

Storage optimised in the dispatch; efficiency is round trip.

A price-taking interconnector to a neighbouring market.

It imports when GB's price is above price (the neighbour's, GBP/MWh) and exports when below, up to import_mw and export_mw in each snapshot. hurdle (GBP/MWh) is added to imports and taken off exports (losses and trading costs).

SystemSpec dataclass

Demand (MW), generators, storage and links on one bus, all on the demand's UTC index.

SystemResult dataclass

Dispatch outcome. Power in MW per snapshot; storage is net discharge.

Unit commitment adds start_price (marginal price including start costs), units_online and starts per committed tranche. With links, links holds each link's net flow (MW, positive into GB).

by_technology

by_technology()

Generation summed by technology, plus storage, net imports and unserved energy.

price_of

price_of(kind)

The marginal price, or (unit commitment only) the price including start costs.

solve

solve(spec, backend='auto', commitment=None)

Dispatch spec at least cost.

Without storage every hour is independent and the merit order is the exact optimum, so auto uses it; with storage auto uses PyPSA (pip install openenergy[system]). With commitment, thermal units have start-up costs and minimum stable output and days are solved in turn with a lookahead (storage included). The merit order and unit commitment dispatch links as two generators each (see _flatten_links), which gives the same prices as PyPSA's links.

result_frames

result_frames(spec, dispatch)

Generation frame, renewable curtailment and emissions from a T x G dispatch array.

openenergy.system.fleet

Build a GB system from the generation mix, fleet data and stated assumptions.

FleetAssumptions dataclass

Stated, configurable modelling assumptions; none is fitted to prices.

Each thermal group is split into tranches of equal size whose efficiencies spread evenly over the fleet average plus or minus the spread (fractions, e.g. 0.04 = 4 pp). Peaking plant (gas turbines and oil engines) is one tranche costed as gas.

Adjustments dataclass

Scenario changes to the historic system.

scale multiplies historic profiles (wind, solar, nuclear, imports, ...) and demand; capacity_mw replaces installed thermal capacity (ccgt, coal, peaking); fuel_price_scale multiplies gas or coal prices; ets_gbp_per_t replaces the ETS price (EU ETS to 2020, UK ETS from 2021); carbon_price_support keeps or removes the GB top-up.

thermal_generators

thermal_generators(
    index, inputs, assumptions=None, adjustments=None
)

CCGT, coal and peaking tranches with short-run marginal cost (GBP/MWh) per period.

build_system

build_system(
    mix,
    inputs,
    assumptions=None,
    adjustments=None,
    storage=(),
    voll=DEFAULT_VOLL,
    links=(),
)

GB on one bus for one calendar year of the generation mix (MW per period).

Demand is NESO's total generation (scaled by scale['demand']). Fixed profiles keep their (scaled) historic output; wind and solar can be curtailed; thermal plant follows the merit order; links trade with neighbours.

openenergy.system.merit

Merit-order dispatch: each period independently, cheapest available plant first.

merit_order

merit_order(spec)

Exact least-cost dispatch for a system without storage.

Must-run units run at their profile. The rest are stacked by marginal cost; the price is the cost of the last unit needed, or the value of lost load when capacity runs out.

openenergy.system.validate

Backcast: rebuild history with the system model and measure how far it is from reality.

BackcastYear dataclass

Model vs reality for one year. Prices in GBP/MWh, energy in TWh, emissions in MtCO2.

emissions_mt_actual_mix applies the model's emission factors and fleet-average efficiencies to the gas and coal that actually ran, so the two emission figures differ only because of dispatch.

backcast

backcast(
    mix,
    inputs,
    prices,
    assumptions=None,
    adjustments=None,
    storage=(),
    backend="auto",
    commitment=None,
    price="marginal",
)

Solve each calendar year of mix and compare with actual prices and fuel use.

Nothing is fitted: assumptions are applied as given and the gap is the finding. price chooses which modelled price is compared (start needs commitment).

write_backcast

write_backcast(
    result,
    directory,
    assumptions=None,
    commitment=None,
    price="marginal",
    price_attribution=(OPSD_ATTRIBUTION,),
)

Write backcast.csv (per year), hourly.csv and summary.json with sources.

openenergy.system.scenario

System scenarios: one GB year with changes, and optionally a battery valued on its prices.

StorageSection

Bases: _Strict

Storage added to the system; power_mw: 0 leaves it out (handy in sweeps).

InterconnectorSection

Bases: _Strict

fixed keeps historic imports; links trades at neighbours' actual prices.

prices defaults to prices/europe_day_ahead_ember.csv next to the system data. availability: flows uses each link's availability fitted from its flows; a number from 0 to 1 applies to every link instead.

FesSection

Bases: _Strict

A NESO FES pathway and year applied to the weather year (system.year).

Storage efficiencies are stated assumptions; FES does not publish them. data defaults to fes/ next to the system data.

SystemSection

Bases: _Strict

window

window()

Days modelled: the year, narrowed by start/end and the data window.

SystemSummary dataclass

One scenario year. Prices GBP/MWh, energy TWh, emissions MtCO2, cost GBP million.

run_system_scenario

run_system_scenario(scenario)

Solve the scenario year and, if a battery is given, backtest it on the modelled prices.

The battery plans on forecasts from modelled history only, exactly as on market prices.

write_system_outputs

write_system_outputs(run, directory)

Write dispatch.csv (hourly price and output by technology) and summary.json.

system_sweep

system_sweep(path, grid)

Run a system scenario for every combination of grid values, in grid order.

openenergy.system.forward

Forward scenarios: NESO FES pathways and interconnector links on a historic weather year.

FesChanges dataclass

What a FES pathway and year change on the weather year's system.

fes_changes

fes_changes(
    source,
    pathway,
    year,
    base_year,
    thermal_mw,
    battery_efficiency,
    ldes_efficiency,
)

Ratios of FES year to FES base_year applied to the weather year's system.

thermal_mw is the weather year's installed CCGT and peaking capacity (DUKES); both scale with FES gas. Coal is zero (FES has none). FES batteries are added in full, and long-duration storage beyond the base year's (pumped storage keeps its historic output).

links(
    index,
    system_dir,
    prices_path,
    scale=1.0,
    hurdle=0.0,
    availability=None,
)

One price-taking link per GB interconnector in service on each snapshot.

availability None uses each link's import and export availability fitted from flows (interconnector_availability.csv); a number applies to every link.

price_column

price_column(pathway, year)

Column name for a pathway and year in data/scenarios/fes_prices.csv.

Scenarios

openenergy.scenario

YAML scenarios: load, validate, run and write results.

AssetsSection

Bases: _Strict

Renewable plants sharing the battery's grid connection.

GridSection

Bases: _Strict

Shared connection; export defaults to total plant capacity, import to export.

CarbonSection

Bases: _Strict

Carbon intensity file, an optional carbon price, and what dispatch plans on.

ResponseSection

Bases: _Strict

Frequency-response services sold alongside trading (GB dynamic response).

Scenario

Bases: _Strict

connection

connection()

The shared grid connection, or None for a battery-only scenario.

Colocation dataclass

Site total against the same plant and battery on separate connections (common days).

load_scenario

load_scenario(path)

Read and validate a scenario; relative data paths resolve against the file.

parse_assignment

parse_assignment(text)

Parse dotted.key=v1,v2 into the key and YAML-typed values.

apply_overrides

apply_overrides(raw, overrides)

A copy of raw with each dotted key set, creating nested mappings as needed.

sweep

sweep(path, grid)

Run a scenario for every combination of grid values, best total first.

run_scenario

run_scenario(scenario)

Backtest the scenario and, unless it is already perfect foresight, its benchmark.

write_outputs

write_outputs(run, directory)

Write intervals.csv, daily.csv and summary.json; returns the directory.

With a perfect-foresight benchmark, its daily results go to benchmark_daily.csv.

Reports

openenergy.report.load

Read the outputs written by run, system run, system validate and sweep.

BatteryOutput dataclass

A battery or co-located site backtest (openenergy run).

SystemOutput dataclass

One GB system scenario year (openenergy system run).

BackcastOutput dataclass

A system backcast (openenergy system validate).

SweepOutput dataclass

A sweep results table; keys are the swept settings, in column order.

load_output

load_output(path)

Read one output directory (or a sweep CSV), detecting its kind from the files.

openenergy.report.figures

Chart functions: pandas in, plotly figure out (no file I/O, reusable in an app).

cumulative_revenue

cumulative_revenue(
    runs, currency="GBP", title="Cumulative revenue"
)

One line per named daily revenue series, summed over time.

revenue_by_market

revenue_by_market(daily, currency='GBP')

Monthly trading and frequency-response revenue, stacked.

typical_day

typical_day(intervals)

Mean charge and discharge (MW) by hour of day.

state_of_charge

state_of_charge(intervals)

Stored energy at the end of each interval, as a day by hour heat map.

site_by_month

site_by_month(daily)

Plant energy available and curtailed per month (co-located sites).

generation_by_month

generation_by_month(dispatch, step_hours=1.0)

Stacked monthly generation (TWh) by technology.

price_duration

price_duration(series, currency='GBP')

Prices sorted from highest to lowest, one line per series, on a 0-100% axis.

mean_by

mean_by(series, by='hour', currency='GBP')

Mean of each series by hour of day or by month.

system_by_month

system_by_month(dispatch, step_hours=1.0)

Curtailment (TWh) and fossil emissions (MtCO2) by month.

model_vs_actual_by_year

model_vs_actual_by_year(years, pairs)

Grouped bars of model and actual per year; pairs maps a label to a column stem.

sweep_effect

sweep_effect(table, key)

Mean revenue per MW-year at each value of one swept setting.

openenergy.report.document

Assemble output directories into one HTML report.

build_report

build_report(outputs, title='OpenEnergy report', cdn=False)

One self-contained HTML document (plotly.js inlined unless cdn).

Web app

openenergy.app.runs

What the app runs: one battery request, checked and run on historic or pathway prices.

Request dataclass

A battery of hours duration valued per MW (revenue scales with power).

year is the historic year for history and the FES year for scenario.

prices property

prices

Which prices and services, e.g. gb-2024, gb-2023-dc, falling_behind_2030.

problem

problem()

A fixed message if the request cannot run, so no exception text reaches a page.

from_prices

from_prices(prices, hours, forecast)

The request for a Request.prices token, or None if it names nothing known.

run

run(request, data, power_mw=1.0)

Backtest the request and its perfect-foresight benchmark on the bundled data.

openenergy.app.results

Results for the app: a compact per-MW summary and daily revenue, precomputed or live.

Result dataclass

One request's per-MW results; daily has revenue and benchmark_revenue.

Store

The precomputed results, read once.

requests

requests(hours, forecast)

Precomputed requests for one duration and forecast, in grid order.

grid

grid()

Every request the app precomputes.

from_output

from_output(request, output, precomputed)

Per-MW summary and daily revenue from a 1 MW run.

write

write(results, data)

Write the precomputed summary and daily files under data.

result

result(request, data)

The precomputed result if there is one, else a live run.

openenergy.app.server

FastAPI routes: HTML pages and a JSON API on the same requests.

api_result

api_result(request)

Per-MW results for one battery: precomputed when available, else run now.

result_page

result_page(request, power_mw=1.0)

The answer page: at once when precomputed, else a progress message while it runs.

explore_page

explore_page(
    hours="2",
    forecast="naive_last_week",
    services="none",
    power_mw=1.0,
)

Value by year and by pathway for one battery, from the precomputed results.

compare_page

compare_page(
    item=[],
    hours="2",
    forecast="naive_last_week",
    power_mw=1.0,
)

Up to three precomputed results side by side; the URL is the shareable comparison.

Errors

openenergy.errors

Exceptions raised by OpenEnergy.

OpenEnergyError

Bases: Exception

Base class for all OpenEnergy errors.

ConfigError

Bases: OpenEnergyError

A scenario or component is configured with invalid values.

DataError

Bases: OpenEnergyError

Input data is missing, malformed or inconsistent.

DispatchError

Bases: OpenEnergyError

A dispatch plan cannot be produced or applied.

InfeasibleDispatchError

Bases: DispatchError

The dispatch optimisation has no feasible solution.