Parameter groups and conventions
Parameter groups (metadata)
Spine's parameter_definition table carries an optional parameter_group_name field — a lightweight category label that the Spine DB editor uses to group related parameters visually. FlexTool historically left this field blank. Schema v43 introduced the mechanism with a narrow Outputs foothold, and schema v44 populates it fully: every parameter_definition row in the master template is now tagged with one of 15 groups.
The scheme has no semantic meaning to the solver — it is purely metadata consumed by the Spine DB editor and anything else that walks parameter_group_name. Names are lowercase snake_case (FlexTool convention). Priorities are sparse by design so future groups can slot in without renumbering.
The groups are organised in three informal tiers — asset physics (what the thing is and how it runs), decision overlays (investment, retirement, storage state, advanced dynamics, reserves, emissions, network, flow caps, user-defined constraints), and model plumbing (model-wide settings, solve configuration, timeline definitions, output toggles).
| Group | Priority | Colour | Purpose |
|---|---|---|---|
basics |
10 | b3cde3 |
What the thing is and how it runs at steady state — default home for any parameter not clearly in a decision overlay. |
investment |
20 | fdbf6f |
Capacity expansion: invest costs, methods, cumulative bounds, lifetime, discount rate, annualised fixed costs, capacity margin. |
retirement |
25 | ffb870 |
Retirement/divestment bounds and salvage value. |
storage |
30 | cab2d6 |
Storage state variable, cross-solve and horizon-edge behaviour, reference price/value. |
tech_advanced |
35 | b2df8a |
Unit commitment (startup, min times), ramp rates, time delays — advanced operational dynamics. |
reserve |
40 | fb9a99 |
Spinning / standing / N-1 reserve provision parameters. |
emission |
45 | ccebc5 |
CO2 accounting: fuel content, group caps, prices. |
network |
50 | 80b1d3 |
DC power flow, inertia, non-synchronous share — network-aware modelling. |
flow_limit |
55 | fccde5 |
Group-level aggregated flow caps / floors and loss-of-load share control. |
constraint |
70 | bc80bd |
User-authored linear constraints plus the coefficient parameters on other entities that couple into them. |
model |
80 | d9d9d9 |
Whole-model scope, horizons, numeric framing (version, periods, inflation). |
solve_basics |
85 | bebada |
Per-solve knobs a typical user touches (solver, mode, periods, years). |
solve_advanced |
87 | 9f94c6 |
Per-solve internals: solver options, rolling/nested/stochastic structures, cross-solve storage handoff. |
timeline |
90 | ffed6f |
Time-structure definitions consumed by solves (timestep duration, timeset metadata). |
output |
95 | a6cee3 |
Toggles that shape what the run writes to outputs. |
Convention for new parameters
When you add a new parameter definition, also assign it a parameter_group_name. Use the table above to pick the group — err on the side of matching why the user sets the parameter, not where it is mathematically consumed (e.g. constraint_state_coeff on node belongs to constraint, because the user writes it to participate in a user-defined constraint, not to configure storage).
Both the group definition and the tag are set inside the same db_migration step:
db.add_update_item("parameter_group", name="my_group",
color="a6cee3", priority=60)
db.add_update_item("parameter_definition",
entity_class_name="unit",
name="my_param",
parameter_group_name="my_group")
The colour is a 6-digit hex string with no leading #. Priority controls display order in the Spine DB editor — pick a value that fits the tiered scheme above (asset physics 10–15, decision overlays 20–55, constraints 70, plumbing 80–95).
After editing the migration, run python -m flextool.update_flextool.sync_master_json_template to regenerate schemas/spinedb_schema.json — the group assignment is exported as the 6th slot of the parameter-definition tuple. The coverage pytest (tests/test_parameter_groups_coverage.py) fails loudly if any parameter is left ungrouped.
Behaviour-change notices
When a FlexTool release changes what an existing model does, the database upgrade marks the upgraded database with
a metadata item flextool_behaviour_change:<change> (visible in the database editor's Metadata tab). While the
marker is present, every run prints a notice for each affected entity, once per run: which FlexTool version changed
the behaviour (and when it was published), what changed, the entities whose results change, and how to keep the old
behaviour or silence the notice. Deleting the marker stops the notice. New databases carry no markers. The markers
travel with the database: copies, the Spine Toolbox editor and the Excel export / import keep them.
The markers:
storage_default_fix_nothing— the defaultstorage_start_end_methodbecame fix_nothing. The notice names the storage nodes without their ownstorage_start_end_methodin models where some node has astorage_state_start(before: fixed at the start; now: free). Settingstorage_start_end_methodon those nodes silences it.storage_fix_method_rewritten— the upgrade rewrotestorage_start_end_methodvalues that had no value to fix at, and wrote fix_start on the nodes that used the old default with their ownstorage_state_start(results unchanged); the notice lists node, alternative, old method and what was written. Its value also lists the stored methods the upgrade left because scenarios used them differently.user_constraint_prebuilt_nets_retirements—constraint_cumulative_pre_built_capacity_coeffnow subtracts the retirements of earlier periods, also those decided in the same solve (before: only those of earlier solves of a chain). The notice names the user constraints whose pre-built entity can retire; results change only if the model retires capacity. Deleting the marker silences it.
Input checks
Before it reads the parameters, FlexTool checks the data of the scenario for values it cannot use sensibly. An error stops the run; one message lists every offending value of the period-key, profile-link and value checks (class, entity, parameter, value, alternative and, for a map, the first key that is wrong) with what to do, so one rerun fixes them all. A warning is printed once per scenario read in the run log; the run goes on.
Errors
- An upper_limit or fixed
unit__node__profile,node__profileorconnection__profilelink whoseprofilehas no value in the scenario (it would hold the flow or the storage state at 0). The message names the link and the profile, and the alternatives that hold the profile's value when the scenario does not include them. A lower_limit link without a value is allowed (a floor of 0). efficiencyof a unit or a connection at or below 0.- Unit
min_loadoutside [0, 1];availabilitybelow 0 (unit, connection, node). capacity_max_coeffbelow 0.- A minimum above its maximum, when the
invest_methoduses that pair:invest_min_period/invest_max_period,retire_min_period/retire_max_period,cumulative_min_capacity/cumulative_max_capacity(compared per period; a constant applies to every period),invest_min_total/invest_max_total,retire_min_total/retire_max_total(unit, connection, node, group). No capacity could meet both. lifetimeat or below 0 on an entity that can invest or retire (the investment would be charged for no years).- A map key of a period parameter (
existing,invest_cost,startup_cost,co2_max_period,lifetime, and every other parameter that takes a period map) that is not a period: the readers would drop the value without a word (y2020 for p2020 gave no capacity). Periods are the keys of everyperiod_timeset, the solve period lists,years_represented, the first level ofstochastic_branches, the stochastic branch periods (p2035_high: the period, an underscore and the branch name) andmodel.periods_available, over the whole database, so a key that is a period of another scenario is fine. The message suggests the closest period (p2030x → p2030). When the data covers more years than any solve names (a 2020–2050 trajectory for a 2030 model), add the extra periods tomodel.periods_available. A map without an index name of a parameter that can also vary over time (availability,self_discharge_loss, ...) whose keys are neither periods nor time steps is read as a period map, unless its keys look like time steps (t0099, T0001, a date-time stamp: then the time-series check reports them). Maps keyed by something else (constraint_flow_coeffby constraint,delayby hours) are not period maps and are not checked. - A time-step key, a
timelevel or a time series on a period parameter that cannot vary over time (existing,startup_cost,invest_cost,lifetime, ...; use a constant or a period map), and numeric map keys (2020) on any period parameter (periods are names such as p2020). A name that is both a period and a time step is read as a period. - A timeline used by a solve (through a timeset named in a
period_timeset, or the timeset a solve withoutperiod_timesettakes by default) withouttimestep_duration, or with a step of 0 hours or less (or not a number). Only the timelines a solve uses need durations: a spare timeline in the database is ignored.
Warnings
- A
node__profileorconnection__profilelink without aprofile_method(the default none does not apply the profile). availabilityabove 1.5, and a profile above 1.5 on an upper_limit / fixed unit link: values above 1.5 look like percentages (95 for 0.95). A capacity factor slightly above 1 (rounding, measured data) is not reported. The profile check runs right after the data is read.- Connection
efficiencyabove 1 (creates energy). virtual_unitsizeat or below 0 (ignored:existingis then the capacity itself and the entity is one unit of it).- A negative
existing(unit, connection or node). It is allowed: a unit with a negative capacity is an "anti-energy" unit whose flows run the other way, a node gets a negative storage capacity and a connection a negative transfer capacity. It is more often a typing mistake, and combined with a profile,min_load, storage limits or invest / retire limits it can make the problem infeasible. - A map of a period parameter that can also vary over time (
availability,other_operational_cost, ...) without an index name whose keys are neither periods nor time steps but look like time steps, on a parameter the time-series check does not cover (the value is not used). - A unit flow with
conversion_flow_coeff0 that no user constraint, flowGroup maximum or upper_limit / fixed profile ties: onlymax_flow_for_unconstrained_variablesbounds it. Such an input is a free dump load on its node, such an output a free source. Tie it with a user constraint (constraint_flow_coeff), or give it a non-zeroconversion_flow_coeff. A unit with an input whose outputs all have coefficient 0 gets one warning (it uses no input).capacity_max_coeff/capacity_min_coeffon such an output has no effect. - A node (not commodity) without any unit (
unit__inputNode,unit__outputNode) or connection (connection__node__node) in the scenario: with aninflowonly the penalty slack (penalty_up/penalty_down) can meet it; without one the node does nothing. Also a balance or balance_within_period node whose units and connections only take energy out, without a positiveinflow(a supply), or only bring energy in, without a negativeinflow(a demand) or anenergy_margin_methodinflow_adder: those flows stay at 0, or only the penalty slack can balance them. A node that only has demand (negativeinflow) and units or connections that only take energy out is warned about too. A connection withtransfer_methodunidirectional (its own, or thetransfer_methodof a group that holds both its nodes) only takes from its first node and brings to its second; other connections count both ways. A unit with a negativeexisting(an anti-energy unit) counts on both sides of its nodes. storage nodes are not checked for this (the storage state takes the rest). Aninflowof 0 andinflow_methodno_inflow count as no inflow. The check stays silent when in doubt.
Other checks (time series keys, timesets, input shares, minimum-load curves, ladders, group limits) are described with their parameters.