Context. Nodes with storage_solve_horizon_method = use_reference_price
get a credit in the LP objective at the last timestep of the last period
(flextool.mod ~line 2390-2394):
− Σ_{n, last t, last d}
p_storage_state_reference_price[n, d]
× v_state[n, d, t] × p_entity_unitsize[n]
× p_rp_cost_weight[d, t] × p_inflation_factor_operations_yearly[d]
/ complete_period_share_of_year[d] × pdt_branch_weight[d, t]
This term is not aggregated by process_outputs/calc_costs.py — so
costs_discounted_d_p / costs_discounted_p_ understate the solver's
objective by this credit whenever use_reference_price is active.
Why it's been left out. End-of-horizon storage valuation has several
valid interpretations depending on what the analyst wants to express:
- Reference price × end state — the method currently in the mod.
Attractive because it's a marginal value the user can set. But the
resulting dollar figure is sensitive to the reference price choice
and is often treated as "shadow accounting" rather than a real cost.
- Difference between initial and final state, priced at a
representative price — e.g. average commodity price over the
horizon, or the marginal storage use-cost at the last timestep.
- Opportunity cost under a rolling assumption — project forward
using the average trajectory and credit the expected value of having
stored energy at horizon boundary.
- Book value — zero if the horizon is long enough that the credit
is immaterial.
Because the choice is model-scoped (different users want different
behavior) and because it's usually computed after the optimization
rather than baked into cost reports, FlexTool currently omits the term
from costs_discounted_* entirely. Users needing it today can compute
it in a post-processing notebook using the solver objective plus the
reference-price term separately.
What would be required to add it.
- Mod: emit
p_storage_state_reference_price[n, d] and the set of
nodes with use_reference_price method to CSV (not currently
written). Also emit set_period_last and set_period__time_last
if not already.
- Python: new helper in
calc_costs.py / out_costs.py that computes
the credit per the formula above, adds it to costOper_and_penalty_d
(or a new dedicated bucket).
- Tests: fixture with non-trivial storage +
use_reference_price;
hand-derived expected credit; assert solver ↔ parquet match.
- Docs: briefly note where the term now shows up in
summary_solve.csv
and the parquet categories.
Existing xfail marker:
tests/test_cost_aggregation_semantics.py::test_storage_state_reference_price_credit
documents the gap. Ref: flextool/flextool.mod:2390-2394.
Decision pending on whether FlexTool should implement method 1
(currently in the mod objective) or leave end-of-horizon valuation as
post-analysis only.
Status (2026-04-21): The documentation side has been addressed —
docs/how_to.md ("How to add a storage unit") and docs/reference.md
(node storage_solve_horizon_method section) now state explicitly
that the use_reference_price credit appears in the solver objective
but not in the reported cost totals (commit 720212d). The
implementation decision (methods 1–4 above) is still pending.
Context. Nodes with
storage_solve_horizon_method = use_reference_priceget a credit in the LP objective at the last timestep of the last period
(flextool.mod ~line 2390-2394):
This term is not aggregated by
process_outputs/calc_costs.py— socosts_discounted_d_p/costs_discounted_p_understate the solver'sobjective by this credit whenever
use_reference_priceis active.Why it's been left out. End-of-horizon storage valuation has several
valid interpretations depending on what the analyst wants to express:
Attractive because it's a marginal value the user can set. But the
resulting dollar figure is sensitive to the reference price choice
and is often treated as "shadow accounting" rather than a real cost.
representative price — e.g. average commodity price over the
horizon, or the marginal storage use-cost at the last timestep.
using the average trajectory and credit the expected value of having
stored energy at horizon boundary.
is immaterial.
Because the choice is model-scoped (different users want different
behavior) and because it's usually computed after the optimization
rather than baked into cost reports, FlexTool currently omits the term
from
costs_discounted_*entirely. Users needing it today can computeit in a post-processing notebook using the solver objective plus the
reference-price term separately.
What would be required to add it.
p_storage_state_reference_price[n, d]and the set ofnodes with
use_reference_pricemethod to CSV (not currentlywritten). Also emit
set_period_lastandset_period__time_lastif not already.
calc_costs.py/out_costs.pythat computesthe credit per the formula above, adds it to
costOper_and_penalty_d(or a new dedicated bucket).
use_reference_price;hand-derived expected credit; assert solver ↔ parquet match.
summary_solve.csvand the parquet categories.
Existing xfail marker:
tests/test_cost_aggregation_semantics.py::test_storage_state_reference_price_creditdocuments the gap. Ref:
flextool/flextool.mod:2390-2394.Decision pending on whether FlexTool should implement method 1
(currently in the mod objective) or leave end-of-horizon valuation as
post-analysis only.
Status (2026-04-21): The documentation side has been addressed —
docs/how_to.md("How to add a storage unit") anddocs/reference.md(node
storage_solve_horizon_methodsection) now state explicitlythat the
use_reference_pricecredit appears in the solver objectivebut not in the reported cost totals (commit 720212d). The
implementation decision (methods 1–4 above) is still pending.