Skip to content

Scenario catalog

Every payload in internal/scenarios/testdata/scenarios/ is a self-contained Job that must validate + emit cleanly for its target; TestScenarios asserts the emitted tree contains the expected framework-native markers, and TestScenariosCapabilityGating asserts each target-specific mode is rejected on the targets that can't honor it. All outputs are shaped to be directly loadable by the pinned framework (see Targets and versions), so "emits cleanly" means "runnable".

PyPSA

Scenario Exercises Key emitted markers
pypsa_generators supply generators, capex/opex p_nom_min, capital_cost, marginal_cost
pypsa_conversion multi-output CHP Link bus2, efficiency2
pypsa_storage StorageUnit detail efficiency_store, standing_loss, cyclic_state_of_charge
pypsa_transmission inter-node Link n1::electricity, n2::electricity
pypsa_trade import/export market gens market_import, market_export
pypsa_committable unit commitment committable, start_up_cost, ramp_limit_up
pypsa_emission CO₂ cap GlobalConstraint primary_energy, co2_emissions
pypsa_constraints linear-constraint IR max_pv
pypsa_extensions must-run dispatch (operation.equals_pu pins both bounds), a system-wide capacity cap (capacity.systemwide_max), an import limited by a series (trade.import.limit) p_min_pu = p_max_pu, p_nom_max, backup_import
pypsa_node_override one tech on [n1,n2] with per-node capacity/cost overrides pv@n1, pv@n2, per-node p_nom_max
pypsa_physics_precomputed physics pv model carried by a precomputed CF series _performance.json (type: physics, precomputed)
pypsa_operate rolling-horizon dispatch over fixed capacities (optimize_with_rolling_horizon) gen, fixed p_nom
pypsa_alternatives MGA near-optimal alternative (optimize_mga; the payload still uses the deprecated solver.options.mga namespace and validates with a warning) p_nom_extendable
pypsa_stochastic two-stage stochastic (set_scenarios + per-scenario overrides) _scenarios.json (fuel_high, translated marginal_cost)
pypsa_constraints_sidecar tech-scoped constraint applied by run.py as a linopy constraint (binding, objective-checked in E2E) force_pv in _constraints.json
pypsa_trade_price_series time-varying import price (objective-checked in E2E) market_import, generators-marginal_cost.csv
pypsa_power_flow mode power_flow: an HV slack feeding two MV nodes through a transformer and a line, PV with a dispatch series, two loads (converges 4/4 in E2E; the linear method and a diverging load are E2E-checked too) v_nom, lines.csv, transformers.csv, control: Slack, generators-p_set.csv
pypsa_lopf plan under the voltage law (network.model: linear_power_flow): a 110 kV ring, an expandable priced line with max_loading; E2E checks Kirchhoff's law around the loop and the security-constrained variant s_nom_extendable, s_max_pu, capital_cost on lines.csv
pypsa_store energy-sized storage as a Store with charger/discharger and the _storage.json constraints; a pumped unit with a pinned soc_profile stores.csv, battery_charge, equal_power, state_of_charge_set
pypsa_modular integer modules on a generator and a store p_nom_mod, e_nom_mod
pypsa_retire_slack continuous retirement of an old unit; priced slack on an island short of capacity ::unmet, retirable old
pypsa_copperplate copperplate: a free hub joins the nodes, the arc is dropped copperplate::electricity
pypsa_uc_extras start/stop ramps, standby cost, prior uptime on a committable unit ramp_limit_start_up, stand_by_cost, up_time_before
pypsa_quadratic quadratic variable cost (QP on HiGHS) marginal_cost_quadratic
pypsa_multi_period two investment periods: a 2020 unit retires before 2040, new capacity is built per period investment_periods.csv, new-2030, new-2040, build_year
pypsa_power_flow_reactive reactive power and voltage control: slack set-point 1.02 pu, a pv generator holding 1.0 pu, loads with a power factor and a reactive series, a shunt, a per-length line impedance, a phase-shifting transformer, a controlled HVDC link into an island PyPSA balances itself (E2E pins the held voltages) v_mag_pu_set, control: PV, q_set, shunt_impedances.csv, phase_shift, links p_set

Calliope 0.7 (calliope)

Scenario Exercises Key emitted markers
calliope_plan base plan run mode: base, base_tech: supply, solver: cbc
calliope_spores SPORES / MGA mode: spores, number:
calliope_operate receding-horizon dispatch mode: operate
calliope_custommath constraint IR + emission limit → additional_math.yaml extra_math, max_pv, cost_flow_in
calliope_storage storage tech base_tech: storage, storage_cap_max, cost_storage_cap
calliope_source_native first-class technology.source + area (source_use_max, source_unit, area_use_max) source_use_max, area_use_max
calliope_conversion multi-output CHP → carrier lists + ratio-pinning extra math (rigid ratios, single-counted fuel) meme_ratio_chp_out_heat, masked balance_conversion
calliope_transmission transmission tech across two nodes base_tech: transmission, link_from, link_to
calliope_node_override per-node cost_flow_cap override under nodes.<n>.techs cost_flow_cap, overridden value
calliope_trade import/export market techs (import = supply at price + co2 factor; export = flexible demand paid the price) market_import, market_export, cost_flow_in: -30
calliope_committable MILP unit commitment (integer units, min stable load) cap_method: integer, flow_cap_per_unit, extra_math: [milp]
calliope_resample time_aggregation: resample → config.init.resample.timesteps resample:, timesteps: 3h
calliope_piecewise piecewise part-load curve → piecewise_constraints math on a fixed-capacity conversion piecewise_x, breakpoints
calliope_multi_node one demand tech at two nodes → one data table with a column per node (#7); solves to the same objective on PyPSA and both Calliope releases (TestE2EMultiNodeDemandConsistency) techs,load,load, nodes,n1,n2
calliope_multi_node_supply one PV tech with an availability series at two nodes → source_use_max table with a column per node (#7, supply side); same-objective check on all three targets (TestE2EMultiNodeAvailabilityConsistency) techs,pv,pv, source_unit: per_cap
calliope_operate_limits operate mode with a limited import and a capacity-bound line (#8): both installed as fixed flow_cap; same-objective check on PyPSA and both Calliope releases (TestE2EOperateLimitsConsistency) flow_cap: 50, flow_cap: 40
calliope_extensions annualized costs under a model-wide discount_rate, a system-wide capacity cap, a battery with energy_capacity, depth of discharge and charge rate, a curtailable demand (demand_curtailable), reporting.save_logs flow_cap_max_systemwide, storage_discharge_depth, flow_cap_per_storage_cap_max, sink_use_max, save_logs
calliope_hub_node a hub node without technologies joined by two arcs (0.7 and 0.6 dialects) link_to: hub, link_from: hub, hub: with techs: {}

Calliope 0.6 (calliope-0.6)

The 0.6 target runs every calliope_* scenario its gates accept (all of the above except calliope_piecewise, whose piecewise performance has no 0.6 representation) through its own dialect — TestScenarios pins the 0.6 markers, the goldens under testdata/golden/calliope-0.6/ freeze the emitted documents, and TestE2ECalliope06Corpus solves them on the real calliope-0.6 CLI — plus four 0.6-specific scenarios:

Scenario Exercises Key emitted markers
calliope06_typical_days time_aggregation: typical_days → k-means representative days (0.7 dropped built-in clustering) apply_clustering, clustering_func: kmeans, k: 2, random_seed
calliope06_spores first-class experiment.alternatives → run.spores_options + systemwide_cost_max group + spores_score costs mode: spores, spores_number: 2, slack: 0.1
calliope06_group_constraints constraint IR + emission limit → group_constraints (capacity cap, minimum production, CO₂ cap) energy_cap_max: 800, carrier_prod_min, cost_max: {emissions: …}
calliope06_min_emissions objective: min_emissions → objective_options.cost_class weights monetary: 0, emissions: 1

AdOpT-NET0

Scenario Exercises Key emitted markers
adopt_demand_trade demand + import/export carrier CSVs ;Demand;, glpk
adopt_storage Storage_Battery database tech + overrides Storage_Battery, OPEX_fixed
adopt_pv_climate physics pv → Photovoltaic + ClimateData.csv Photovoltaic, ;ghi;
adopt_emission emission-limit objective costs_emissionlimit
adopt_wind physics wind → WindTurbine_Onshore_1500 + wind climate columns WindTurbine_Onshore_1500, ;ws10;
adopt_pareto mode: pareto → objective: pareto (+ points via the deprecated solver.options.pareto, warned) pareto
adopt_monte_carlo mode: monte_carlo → monte_carlo.N > 0 (via the deprecated solver.options.monte_carlo.N, warned) monte_carlo (N asserted in TestAdoptRunModeConfig)
adopt_typicaldays time_aggregation: typical_days → typicaldays.N = periods typicaldays (N asserted in TestAdoptRunModeConfig)
adopt_node_override per-node storage overrides in the node-keyed _meme_overrides.json Storage_Battery, per-node size_max
adopt_extensions a battery with independent charge/discharge rates and no_simultaneous_charge_discharge, retirement (capacity.decommission), import/export with emission factors, discount_rate, reporting.case_name charge_rate, discharge_rate, allow_only_one_direction, decommission, case_name
adopt_min_emissions objective: min_emissions on a PV system emissions_net, Photovoltaic

The three AdOpT run-mode scenarios are backed by TestAdoptRunModeConfig, which parses the emitted ConfigModel.json and checks the mode actually reached AdOpT's optimization block (objective / monte_carlo.N / typicaldays.N) — the translation described on the targets page that makes those modes genuinely runnable rather than silently downgraded to a cost-optimal plan.