Changelog¶
All notable changes to MEME are documented here. The format follows
Keep a Changelog; versions follow
Semantic Versioning, and git tags carry the v prefix
Go modules require (v0.1.0-alpha).
Unreleased¶
Added¶
docs/roadmap.md: the coverage roadmap. It records the 2026-10-25 measurement of what each framework accepts natively against what the emitters write for the whole scenario and example corpus, lists the emitter code paths no payload exercises, names two defects the capability reference hides (PyPSA storage units losecapacity.maxandoperation.min_pu/max_pu; AdOpT batteries ignorestorage.charge_eff/discharge_eff), and plans the additive work for0.2.xand the breaking bundle for0.3.0, each with its deprecation. Technology catalogs become selectable: alibrarypin on a technology or arc names an upstream entry (AdOpT database, PyPSA standard types) or asks for a MEME-synthesized definition, andexperiment.catalogsets the default.
0.2.0-alpha - 2026-10-25¶
Added¶
- Power flow as a PyPSA run mode (ADR-0008):
experiment.mode: power_flowsolves the AC network for a fixed dispatch withlpf()andpf()instead of optimizing. The canonical model describes the network itself —node.electrical.v_nom,transmission.electrical(kindline or transformer,r,x,s_nom,num_parallel,tap_ratio) andtechnology.dispatch(overridable per node) — andexperiment.power_flownames theslack, themethod(newton_raphsonorlinear),distribute_slackand thetolerance. The run writesoutput/power_flow/:convergence.csv, per-bus voltage magnitude, angle and power, per-line and per-transformer flows and loading, generator dispatch and asummary.json; a snapshot that does not converge fails the run. The API consumer chains a plan and a power flow as two calls; MEME holds no state between them. - Gates around the new inputs: the mode routes through the
power_flowcapability (PyPSA only); power-flow inputs in an optimization mode (adispatch, theelectricalblocks) are kept and named in one warning while the arcs stay transport links (see Changed); in modepower_flowexpandable capacities, trade, constraints, emission limits and scenarios are rejected, anddistribute_slackrequires a dispatch on the slack technology (PyPSA shares the balance in proportion to each generator's set-point, so a slack without one would receive none of it). -
Scenario
pypsa_power_flow, the example payloadsexamples/pypsa_power_flow*.json(the reference network, the smallest valid power flow, a meshed ring with per-node dispatch, and the options: linear method, distributed slack, storage sign, a second carrier) and E2E checks for convergence, the linear method and a diverging load. The examples walkthrough documents the options, the result files and the rejected requests. -
Reactive power, voltage control, shunts and controlled transfers for the power flow (ADR-0009):
technology.electricalwithreactive_dispatchorpower_factor(+leading),control: pvandvoltage_setpoint;node.electrical.shunt;transmission.dispatchas a controlled HVDC transfer on an arc without impedance;b,g,phase_shiftandr_per_length/x_per_lengthontransmission.electrical. The result folder gains the reactive tables, the load and link tables and the shunt tables. Gates: one way to give Q, voltage set-points forpvgenerators and the slack only and agreeing per bus, an electricity arc needs an impedance or a transfer, every AC island needs a generator (an island without the named slack is reported as balanced by PyPSA), and the linear method says what it ignores. Scenario and example payloadpypsa_power_flow_reactive, E2E-pinned on the held voltages. -
The electrical network inside the optimization (ADR-0010):
experiment.network.model: linear_power_flowmakes PyPSA solve under Kirchhoff's voltage law with Lines and Transformers (expansion from the arc'scapacity,electrical.max_loadingass_max_pu), andnetwork.security.outagesruns the security-constrained solve; other targets reject the model like a mode. Transmission gainslifetime,interest_rateandcost_basis, and PyPSA prices the arc's investment and fixed O&M on Links and Lines. Every optimization run writesoutput/results/(dispatch, flows, storage states, marginal prices,capacities.csv,statistics.csv,summary.json). - PyPSA optimization completeness (ADR-0011): an energy-sized storage
(
storage.energy_capacity) is a Store with charger and discharger Links, which honors independent rates, depth of discharge and the energy-capacity cost (_storage.jsoncarries the equal-power and rate constraints);storage.soc_profilepins a StorageUnit's state of charge;capacity.per_unitbuilds integer modules;decommission.mode: continuousretires existing capacity at its fixed O&M and builds new capacity above it;allow_unmet_demandadds priced slack generators;copperplatejoins every node through a free hub;operation.ramp_start_up,ramp_shut_down,standby_cost,initial_uptime,initial_downtimeandcosts.<class>.variable_om_quadraticmap onto PyPSA's unit-commitment and cost attributes;model.periodswith years are investment periods withbuild_year,lifetimeand new builds per period. Scenarios and goldens for every claim, E2E on the real framework, and the exampleexamples/pypsa_planning.json.
Changed¶
- PyPSA no longer rejects independent storage rates, depth of discharge,
continuous retirement, unmet demand or copperplate; the energy-capacity
cost is priced instead of dropped. Calliope and AdOpT-NET0 keep every
mapping they had; the new fields they have no slot for (
soc_profile, the unit-commitment extras, the quadratic cost, investment periods) are ignored there with a warning. The validation matrix records every verdict that moved. time.subsetandcosts.<class>.purchaseare rejected on PyPSA and AdOpT-NET0 instead of being dropped without a word: neither target has a slot (narrowtime.start/end; fold the purchase cost intoinvestment_per_capacity). Calliope 0.7 warns thatarea.per_capacityis not emitted (0.6 emitsresource_area_per_energy_cap). The validation matrix records the moved verdicts.- Power-flow inputs —
dispatch, theelectricalblocks on technologies, arcs and nodes — are ignored with one warning wherever no power flow runs: on Calliope and AdOpT-NET0 and in PyPSA's optimization modes alike. Adispatchin an optimization mode was rejected before; now one payload serves the planning call and the power-flow call (ADR-0009). - The PyPSA driver parses its run configuration as JSON (
json.loads) instead of pasting the JSON in as a Python literal, which broke on the first boolean setting.
Fixed¶
- PyPSA
state_of_charge_initialis absolute (MWh), but the canonical fractionstorage.initial_socwas written into it unchanged, so a half-full 24 MWh unit started at 0.5 MWh. The fraction is now applied to the installed energy (p_nom×max_hours); an expandable storage is told it starts from its existing capacity.
0.1.0-alpha - 2026-10-24¶
The first pre-release. It carries the service as scaffolded in July 2026 — the canonical model and its validation, the emission toolkit, the target registry with capability-driven validation, the PyPSA, Calliope and AdOpT-NET0 targets, the job execution engine with bounded concurrency, the HTTP API with multi-target resolution and API-key authentication, the embedded scenario corpus with golden snapshots and the validation matrix, the containerized solver environment, the real-solver E2E suite, the published JSON Schemas and the example payloads — together with everything that landed on it since, listed below. Before 1.0 the API and the canonical model may still change between releases.
Added¶
meme -versionand theversionfield ofGET /healthzreport the build version, whichmake buildstamps from the git tag.- Calliope 0.6.8 as the
calliope-0.6target: its own emitter for the 0.6 model definition (model/run/techs/locations/links,file=timeseries,group_constraints,conversion_pluscarrier tiers), its own capability profile (k-means typical days and an emissions-minimizing objective gained; indexed parameters, piecewise performance and custom math absent) and native keynative["calliope-0.6"](#4). - Framework versions as separate targets: a default per framework, version
variants selected by name, version-explicit aliases (
calliope-0.7), andGET /capabilitiesreportingdefault_targets,aliasesandversions. - Environment image with one virtual environment per Calliope release,
provisioned by
uv(Calliope 0.6.8 runs on a managed Python 3.8). - Scenarios and cross-framework E2E checks for multi-node technologies
(
calliope_multi_node,calliope_multi_node_supply) and operate-mode limits (calliope_operate_limits); the same payload is pinned to the same objective on PyPSA, Calliope 0.7 and Calliope 0.6. - A CI workflow (format, vet, race tests, build, schema check, vulnerability scan) and a strict documentation build deployed to GitHub Pages.
- Validation of
timeseries.source(inlinewith values,filewith a path) and rejection of a node listed twice on one technology.
Changed¶
- Calliope 0.7 target migrated from 0.7.0.dev7 to the stable 0.7.0
release: data tables are spelled
table/add_dims.inputs, a supply technology's fuel cost iscost_source_use, ratio-pinning math no longer uses the removeddefined()helper, and node coordinates are emitted on all nodes or none (#3). - Calliope data tables are named after technology and parameter
(
<tech>__<param>.csv) instead of the series id two technologies could share. - The documentation moved from one long README into a MkDocs site; the
capability reference lives at
docs/reference/capability-reference.md.
Removed¶
- The organisation-template workflows
sync-legal.ymlandsync-branch-rules.ymlwith their scripts: run from this repository they acted on every repository of two GitHub organisations and needed the template's tokens, so every run here failed. The template-only pages on legal sync and branch rules left the documentation site; the branch-naming and commit-convention pages stay, reworded for MEME, and the lint scripts point at them.
Fixed¶
- Calliope: a technology placed at several nodes wrote its demand profile, availability or source series for the first node only; every placed node now gets its own data-table column, and per-node profile overrides land in their node's column (#7).
- Calliope: in operate mode a trade import ran unbounded and a transmission
line ignored its capacity; both are installed at their fixed size, as are
source_capandarea_use, with a warning wherecapacity.maxstands in for the installed size, and a model with no fixed capacity is rejected before the solve instead of aborting after it (#8). The same gap was closed on the 0.6 target, where the import ran above its limit. - Calliope: a transmission line's
capacity.existingis honored in plan mode (it used to be ignored, leaving a fixed line unbounded). - Calliope: an already-annualized cost basis pins
cost_depreciation_rate: 1instead of being divided by the lifetime again. - PyPSA: a payload without inline series is solved over the whole
model.timerange instead of a single default snapshot. - A file-backed time series, which no target can materialize, is rejected at validation instead of failing at emit time.
- Container: the API service restarts unless stopped; development tooling
corrected and
tsampinned below 4 for AdOpT-NET0. - Earlier fixes: CORS validation for credentialed wildcards and reading the
CORS setting from the environment; solver process-group cleanup, output
capping and bundle symlinks; capability gates on node overrides; numeric
edge cases, resolution parsing and YAML floats in the emitters; a node
with no technologies emitted as
techs: {}.