Skip to content

valuation/2

The outputs as plain data: what Valuation.to_mapping() and LiveValue.to_mapping() give. The library ships the JSON Schema as a data file (microgrid/engine/schemas/valuation-2.schema.json); microgrid schema prints it, and the library's tests check every output against it.

Values are unrounded. Prices are in the input prices' currency per kWh, whatever the field names say (Currency); power in kW; weights and certainties 0-1; times ISO 8601 with their offset; enums their string values. Each field's meaning is defined once in Outputs.

A valuation

Field Type Meaning
model_version string the version of the price rule and plant model that made it (pricing/1.3); it changes when a change to either would change a value
now time the moment valued, to the whole second
kind stored_value_v1, fallback_import, unpriced how the values were made (kinds)
degraded list of causes the inputs that could not be used (causes)
inputs cause → {as_of, usable} each input: when it was known, and whether it could be used
load_forecast string or null the load forecast's source label
slots list of slots the horizon, slot by slot; empty when unpriced
horizon_cut bool a gap in the load forecast cut the horizon short; the slots before it are valued

A slot

Field Type Meaning
start time the slot's start
p_import, p_export number the input prices for the slot
marginal_cost number the price of one more kWh used in the slot, expected over the PV scenarios
marginal_cost_p10 number the same, priced on the pessimistic (p10) PV case
regime export, curtail, battery, import what one more kWh would displace in the median case (Regimes and surplus)
surplus_kw number ≥ 0 PV the projection exports or curtails in the median case: power there is to share
surplus_kw_p10 number ≥ 0 the same in the pessimistic case, never above surplus_kw
certainty_spill 0-1 scenario weight in which the kWh costs only PV that would be exported or curtailed
certainty_covered 0-1 scenario weight in which the battery covers the expensive hours ahead without an import
pv_value object or null a PV kWh diverted to a flexible load: per_kwh, routing (export, curtail, battery: where it would have gone), weight (the scenario weight in which PV had spare)
battery_value object or null a battery kWh drawn for one: per_kwh, displaces (later_import or export: what makes it up, the make-up rule's event), weight
supply list of steps the supply stack in the median case: source (spill, pv, battery, grid), kw (null: the grid, no limit), per_kwh, in the order the plant serves a load
unserved 0-1 scenario weight in which the slot has load a stored kWh would serve but cannot (an import limit); its price is then the import as a floor

A live value

Field Type Meaning
at time the moment
kind kind as above; unpriced without a price for the slot under way
surplus_kw number ≥ 0 PV spilling now, from the measured values
regime regime as above
p_import, p_export, marginal_cost number or null null when unpriced
certainty_spill, certainty_covered 0-1 as above, for the slot from now
degraded list of causes the engine's causes
external list of strings the client's own causes, kept beside the engine's
pv_value, battery_value object or null as above

An example

A valuation of the example dataset at 13:00 local time on 2026-10-22 (11:00 UTC, as the JSON's times show), its first slot only. PV is charging the battery, which holds the week's solar and the night's top-up at 0.18 (the lots from the dataset's meters):

{
  "model_version": "pricing/1.3",
  "now": "2026-10-22T11:00:00+00:00",
  "kind": "stored_value_v1",
  "degraded": [],
  "inputs": {
    "prices": {"as_of": "2026-10-22T10:00:05+00:00", "usable": true},
    "pv_forecast": {"as_of": "2026-10-22T08:00:00+00:00", "usable": true},
    "battery_soc": {"as_of": "2026-10-22T11:00:00+00:00", "usable": true},
    "work_mode": {"as_of": "2026-10-20T22:00:00+00:00", "usable": true},
    "load_forecast": {"as_of": "2026-10-22T10:02:00+00:00", "usable": true},
    "plant_model": {"as_of": "2026-10-22T11:00:00+00:00", "usable": true}
  },
  "load_forecast": "example-load",
  "slots": [
    {
      "start": "2026-10-22T11:00:00+00:00",
      "p_import": 0.3,
      "p_export": 0.07,
      "surplus_kw": 0.0,
      "surplus_kw_p10": 0.0,
      "regime": "battery",
      "marginal_cost": 0.07,
      "marginal_cost_p10": 0.07,
      "certainty_spill": 0.0,
      "certainty_covered": 1.0,
      "pv_value": {"per_kwh": 0.07, "routing": "battery", "weight": 1.0},
      "battery_value": {"per_kwh": 0.13651794998444183, "displaces": "later_import", "weight": 1.0},
      "supply": [
        {"source": "pv", "kw": 1.0833000000000002, "per_kwh": 0.07},
        {"source": "battery", "kw": 6.0, "per_kwh": 0.13651794998444183},
        {"source": "grid", "kw": null, "per_kwh": 0.3}
      ],
      "unserved": 0.0
    }
  ],
  "horizon_cut": false
}

Read it as: one more kWh now costs 0.07, because it would take PV that is charging the battery, and that PV costs the export the site gives up. Up to about 1.08 kW is available at that price; beyond it the battery gives up to 6 kW at about 0.137, what the energy it holds cost through its losses and wear, and beyond that the grid at 0.30.

marginal_cost and pv_value average over the ten PV scenarios; supply is the median scenario's alone, so the two can differ; here every scenario agrees. What a stored kWh is worth has the arithmetic.