Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
f9d3dd6
feat(stochastic): Slice A scenario-lineage frames (dd_same_scenario, …
jkiviluo Sep 22, 2026
19407fd
feat(stochastic): walker lineage capability, inert
jkiviluo Sep 22, 2026
dd20b5e
test(stochastic): walker lineage capability + NPV obligation suite
jkiviluo Sep 22, 2026
91db38b
fix(stochastic): keep obligation checker I/O out of the cascade
jkiviluo Sep 22, 2026
d15e31e
feat(stochastic): schema opt-in stochastic_invest_method (v70)
jkiviluo Sep 22, 2026
af6ebee
feat(stochastic): recourse guards
jkiviluo Sep 22, 2026
d934ef0
test(stochastic): v70 migration + recourse guard tests
jkiviluo Sep 22, 2026
abc188c
feat(stochastic): D0 recourse-active plumbing (inert)
jkiviluo Sep 22, 2026
95d1411
feat(stochastic): D1 beta narrowing — LineageFilterError
jkiviluo Sep 22, 2026
c318bb3
feat(stochastic): D2 invest axis + per-period anchor-mapping
jkiviluo Sep 22, 2026
8aef021
feat(stochastic): D3 alpha year/factor provider threading
jkiviluo Sep 22, 2026
4e39d2c
feat(stochastic): D4 lineage activation + NPV preconditions
jkiviluo Sep 22, 2026
51bf2d4
feat(stochastic): D5 objective weighting + Guard-1 lift + hand-calc gate
jkiviluo Sep 23, 2026
b2d0039
feat(stochastic): D6 per-path caps
jkiviluo Sep 23, 2026
03f19c0
feat(stochastic): D7 handoff explicit non-realized drop
jkiviluo Sep 23, 2026
f7b724d
feat(stochastic): D8 outputs/costs + fixture variant B
jkiviluo Sep 23, 2026
edb158f
docs(stochastic): D9 per-scenario invest recipe + changelog
jkiviluo Sep 23, 2026
28e35b0
test(stochastic): mark recourse cost reconciliation as solver
jkiviluo Sep 23, 2026
f0ea27d
fix(stochastic): read recourse flag via provider, not disk (cascade i…
jkiviluo Sep 23, 2026
c691612
fix(migration): create solve_advanced group if absent in v70 migration
jkiviluo Sep 23, 2026
d2837a1
merge: bring v70 migration fix + B/C into recourse-invest
jkiviluo Sep 23, 2026
4e86d64
fix(stochastic): F1 defensive per-solve reset of recourse globals
jkiviluo Sep 23, 2026
3455dce
fix(stochastic): F2 hard-raise on nulled token in _anchor_expand_expl…
jkiviluo Sep 23, 2026
289c462
feat(stochastic): E0 mid-horizon fan-out + storage linkage
jkiviluo Sep 23, 2026
daa5d93
feat(stochastic): E1-E3 shared-trunk lineage, per-path caps, annuity …
jkiviluo Sep 23, 2026
1f6f26b
test(stochastic): E4 hedging gate (173250) + storage scenario + docs
jkiviluo Sep 23, 2026
81ca8cf
feat(stochastic): export dd_same_scenario_annuity in __all__
jkiviluo Sep 23, 2026
55ad9cf
test(stochastic): annuity-lineage dedup frame-content tests + docstri…
jkiviluo Sep 23, 2026
523617c
fix(schema): migrate test fixtures + canonical DBs to v70
jkiviluo Sep 23, 2026
9322405
fix(test): skip v70 master-template test on Windows (sqlite handle lock)
jkiviluo Sep 23, 2026
fd76f5e
fix(test): decouple v70 idempotency test from auto-migrated corpus
jkiviluo Sep 23, 2026
05c7222
test(infra): isolate examples.sqlite readers from the shared checked-…
jkiviluo Sep 25, 2026
f029ae0
docs(stochastics): reorder how-to — basics/modes first, investment la…
jkiviluo Sep 29, 2026
28ca2a0
merge: refresh base — bring restructured stochastics docs into read-i…
jkiviluo Sep 29, 2026
90a0004
Update stochastics in how_to.md
jkiviluo Sep 29, 2026
1c67312
feat(stochastic): G0 non_anticipativity_periods schema (v71)
jkiviluo Sep 29, 2026
546e92f
feat(stochastic): G1 configurable NA window
jkiviluo Sep 29, 2026
b0c466c
test(stochastic): G Slice-G configurable NA window gate
jkiviluo Sep 29, 2026
d381ce1
docs(how-to): G3 non_anticipativity_periods window guidance
jkiviluo Sep 29, 2026
ef4f298
fix(export): whitelist non_anticipativity_periods in split_params
jkiviluo Sep 29, 2026
2c1aa76
fix(stochastic): G tri-state reader — malformed value -> legacy, doc …
jkiviluo Sep 29, 2026
69f53ea
fix(schema): stochastic_branches leaf is numeric, not Utf8 (axis cont…
jkiviluo Sep 29, 2026
cb927ba
docs(stochastics): explain default dispatch vs investment NA assumpti…
jkiviluo Sep 29, 2026
75afb58
docs(stochastics): reframe investment modes — standard two-stage is t…
jkiviluo Sep 29, 2026
db97614
feat(stochastic): invest-NA window schema (v72)
jkiviluo Sep 29, 2026
8a09883
feat(stochastic): non_anticipativity_invest constraint families
jkiviluo Sep 29, 2026
81df8dc
test(stochastic): invest-NA window hand-calc + families + v72 migration
jkiviluo Sep 29, 2026
49c7938
docs(stochastics): deliver the invest-NA window recipe + changelog
jkiviluo Sep 29, 2026
4202ac9
refactor(stochastic): drop dead ctx branch in invest-NA window reader
jkiviluo Sep 29, 2026
407ce09
feat(invest): construction lead time schema (v73)
jkiviluo Sep 29, 2026
08952d8
feat(invest): commissioning-year helper for construction lead time
jkiviluo Sep 29, 2026
19a858b
feat(invest): thread commissioning lag into the availability walk
jkiviluo Sep 29, 2026
1dbeca7
feat(invest): forbid forced out-of-horizon commissioning (F5)
jkiviluo Sep 29, 2026
a69abb9
test(invest): staging composition + calendar identity (Slice H P3)
jkiviluo Sep 29, 2026
95c900f
docs(invest): construction lead time — how-to + CHANGELOG (Slice H P4)
jkiviluo Sep 29, 2026
ea3795a
fix(invest): surface commissioning-lag guard/helper failures (don't s…
jkiviluo Sep 29, 2026
a47c218
fix(invest): commissioning helper must not read stale recourse-walk g…
jkiviluo Sep 29, 2026
5112e38
feat(reserve): load reserve_duration param (#322)
jkiviluo Sep 23, 2026
5303f09
feat(reserve): storage energy-adequacy constraints for reserve_durati…
jkiviluo Sep 23, 2026
5b65cc4
test(reserve): cover invest/divest down-headroom + fix e2e comment (#…
jkiviluo Sep 23, 2026
6c2a24f
feat(schema): v74 folded bump — rename NA knobs to shared_* + reserve…
jkiviluo Oct 1, 2026
eb16acb
fix(param-shapes): failed-cast tokens no longer broadcast as missing …
jkiviluo Oct 2, 2026
fb28c9a
fix(param-shapes): per-row period/time classification for dual-axis p…
jkiviluo Oct 2, 2026
411358f
fix(min_load): route p_min_load through the (p,d,t) shape resolver
jkiviluo Oct 2, 2026
43635f1
test(stochastic): coverage-first rolling stochastic NA e2e (W2)
jkiviluo Oct 5, 2026
403f4b5
fix(export): follow v74 rename in export_settings split_params
jkiviluo Oct 5, 2026
6a2d219
fix(stochastic): triggering-cohort branch-reveal validation (W1)
jkiviluo Oct 5, 2026
79dce7e
fix(stochastic): populate per-roll non-anticipativity frames (W3)
jkiviluo Oct 5, 2026
9921c6a
feat(stochastic): storage branch-point continuity across branches (§D.6)
jkiviluo Oct 6, 2026
d00bb6a
feat(rolling): warn on investment intent in a flat rolling_window solve
jkiviluo Oct 6, 2026
e841828
test(stochastic): realized-dispatch handoff e2e (W8.1)
jkiviluo Oct 6, 2026
e164399
feat(schema): add solve.dispatch_realization_method (v75) — W4 slice …
jkiviluo Oct 6, 2026
09cbc8f
feat(stochastic): auto-spawn realized-dispatch solve on separate_disp…
jkiviluo Oct 7, 2026
9ad7b86
feat(stochastic): realized-branch projection for the dispatch sibling…
jkiviluo Oct 7, 2026
c503f7d
docs(stochastic): consolidate the realized-dispatch feature (coherenc…
jkiviluo Oct 7, 2026
674fea7
feat(stochastic): de-scenario realized operational weight for separat…
jkiviluo Oct 7, 2026
14d3c7a
feat(stochastic): invert W1 branch-reveal validation to forecasts-onl…
jkiviluo Oct 7, 2026
91d1a6e
feat(stochastic): forecast-to-forecast non-anticipativity (R-NA/R-CONT)
jkiviluo Oct 7, 2026
42cad0b
feat(stochastic): reference forecast owns real period names at n_real…
jkiviluo Oct 8, 2026
02d53eb
feat(stochastic): forecasts-only branch weighting + genuine fans (R2+…
jkiviluo Oct 8, 2026
c4653af
refactor(stochastic): rename __realized lineage leaf to __anchor (R5-B)
jkiviluo Oct 8, 2026
f65768f
fix(rolling): carry committed jump-end trunk storage state in handoff…
jkiviluo Oct 12, 2026
04e5b78
Merge origin/main (4.0.5, #346) into the stochastics re-grounding branch
jkiviluo Oct 12, 2026
a1981b9
fix(polars2): raise CommissioningLagError on unsupported i64->enum ke…
jkiviluo Oct 12, 2026
9d31083
docs(stochastic): re-ground user docs to the forecasts-only model (4.…
jkiviluo Oct 12, 2026
828bdea
refactor(stochastic): quality cleanup — hoist, helpers, comment trim …
jkiviluo Oct 13, 2026
e168e33
docs(schema): correct stochastics param descriptions to the forecasts…
jkiviluo Oct 13, 2026
d639e15
fix(rolling): carry committed unit commitment across roll boundaries …
jkiviluo Oct 14, 2026
e8ef1ab
fix(rolling): build min_uptime/min_downtime lookback on the synthetic…
jkiviluo Oct 14, 2026
326840d
fix(rolling): carry min_uptime/min_downtime across within-period roll…
jkiviluo Oct 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,75 @@
## Unreleased

Schema change (adds `solve.stochastic_invest_method`,
`solve.shared_invest_periods`, `solve.shared_operation_periods`,
`reserve__upDown__group.reserve_duration`, and per-entity
`construction_lead_time` / `construction_lead_time_method` on
`unit`/`connection`/`node`; migrated automatically). The stochastic
shared-decision window parameters use positive names:
`shared_invest_periods` / `shared_operation_periods` name the periods
whose decisions are *shared* (identical) across stochastic branches.

- **Mixed period/time Map shapes within one parameter.** A parameter can
now be authored with different Map shapes for different entities (for
example a `flowGroup.min_instant_flow` that is a `Map(period)` on one
group and a `Map(time)` on another). Each row keeps its own index
semantics regardless of the order of rows in the database, instead of
being silently misplaced or dropped. As a consequence of classifying
every row, a Map shape that a parameter does not accept — for example a
time Map (or any other non-accepted shape) on a period-only parameter —
now **raises** an error that names the offending entity, instead of
being silently ignored. Databases that relied on such input being
dropped will stop with a clear message identifying the entity and
parameter to fix.

- **Construction lead time.** New per-entity parameters
`construction_lead_time` ([years]) and `construction_lead_time_method`
(`immediate` / `closest_seam` / `previous_seam` / `next_seam`) model
physical build time on `unit`, `connection` and `node` investments.
Capacity *ordered* in a period becomes *available* only after the lead
time, snapped to a period boundary — the investment decision and its
overnight annuity stay in the order period, while balances, flows and
reserves see the plant (and its full technical lifetime) only from its
commissioning year. It applies to deterministic and stochastic models
alike, and composes with the invest non-anticipativity window to stage
long- versus short-lead technologies across a stochastic reveal
automatically (a long-lead asset serving a post-reveal period must be
ordered in a pre-reveal, tied period). Left at the default 0 (or
`immediate`) every existing result is byte-identical.

- **Standard two-stage stochastic investment (the main mode).** The new
`solve.shared_invest_periods` parameter names the periods over
which investment is a single shared *here-and-now* decision: `v_invest` and
`v_divest` are tied across the stochastic branches over those periods, while
later periods invest per branch (recourse). Compose it with
`stochastic_invest_method = recourse` and `shared_operation_periods = []`
to get the classic capacity-expansion structure — shared first-period
investment, per-scenario recourse investment thereafter, operations free from
the first timestep. Both invest and divest are tied so the *net* first-stage
capacity is genuinely shared. Left unset (or empty) it adds no tie and every
existing result is byte-identical; on a non-`recourse` solve it is a harmless
no-op.

- **Per-scenario stochastic investment** (opt-in
`solve.stochastic_invest_method = recourse`): each stochastic branch can now
make its own investment decisions at and after the branching period, with
probability-weighted investment costs and per-scenario investment limits — a
wait-and-see analysis giving per-scenario optimal plans and their expected
cost. Committed results report the realized scenario; non-realized branch
investments are available under the horizon-output debug flag
(`model.output_horizon`). Default behaviour (`none`) is unchanged, and the
option is rejected in combination with Benders decomposition.
- **Mid-horizon reveal / hedged two-stage stochastics.** A stochastic branch
may now start at any period boundary, not only the solve's first step, so the
pre-reveal periods stay a single shared *here-and-now* investment (a genuine
first stage) while the branches fan out into per-scenario recourse from the
reveal onward. Under `recourse` this makes the objective a real two-stage
hedge — strictly between the wait-and-see bound and the deterministic
mean-value plan (positive EVPI/VSS). Storage state is carried across the
reveal for stochastic-group storage nodes. Mid-horizon reveal is a general
stochastics capability; the previous multi-period restriction is lifted. A
malformed reveal (analysis time off a period boundary, or a branching period
without exactly one realized branch) is now rejected with a clear error.
- **Minimum load now applies once per unit, on the sum of its outputs.**
`min_load` × online capacity is a floor on the sum of the outputs that
define the unit's capacity (outputs with `conversion_flow_coeff ≠ 0`; the
Expand Down
17 changes: 17 additions & 0 deletions docs/dev/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -789,11 +789,28 @@ names as before for debug inspection:
| `realized_existing` | `p_entity_period_existing_capacity.csv` (existing column) | Cumulative existing capacity history per `(entity, period)`. |
| `divest_cumulative` | `p_entity_divested.csv` | Cumulative divest per entity carried forward. |
| `roll_end_state` / `upward_roll_end_state` | `p_roll_continue_state.csv` | `v_state` at the end of the realized window; pins the next roll's first timestep. |
| `roll_end_online` | *(in-memory only)* | Committed jump-end `v_online` per online process (OC1 Carrier 1); the next roll's `online__startup`/`online__shutdown` first step sees the prior commitment instead of the within-roll cyclic wrap, so a unit that stays committed is not re-charged a startup across the boundary. |
| `online_history` | *(in-memory only)* | Trailing realized committed `v_startup`/`v_shutdown` window (OC1 Carrier 2); rebuilt into `uptime_carry`/`downtime_carry` at the next roll and injected on the `minimum_uptime` RHS / `minimum_downtime` LHS so an obligation opened in roll N constrains roll N+1's early steps. |
| `fix_storage_quantity / price / usage` | `fix_storage_*.csv` | Parent-imposed storage quota at the boundary (any subset of the three metrics — each carries its own `(node, period, step)` index, so a separate timesteps carrier is not needed). |
| `cumulative_co2` | `co2_cum_realized_tonnes.csv` | Running CO2 totals across rolls. |
| `cumulative_commodity` | `commodity_ladder_cumulative.csv` | Per-tier commodity consumption (ladder pricing). |
| `cum_sim_hours` | `ladder_cum_sim_hours.csv` | Running simulated-hour total per period. |

Cross-boundary unit-commitment continuity (OC1 Carriers 1 & 2) is
**hard-enforced** and **period-aware**: a `min_uptime` / `min_downtime`
obligation opened near a roll's jump-end carries into the next roll's early
steps *only within the same period*. FlexTool's online dynamics are
period-cyclic (a period's first step wraps within that period, never to the
prior period), so a commitment obligation must not propagate across a period
boundary — the non-contiguous investment years (e.g. 2020 → 2025) do not
connect, and forcing one across would be a unit-commitment infeasibility no
node penalty slack can absorb. A node with the default `penalty_method`
(`regular`) absorbs any forced-on generation through its downward penalty
(`vq_state_down`, a feasible spill); a hard-balance node (`penalty_method =
off`) can instead become infeasible when a myopically-committed unit's
forced generation cannot be absorbed — that is the model correctly refusing
an un-absorbable commitment, resolved by allowing slack.

`fix_storage_timesteps`, `ed_history_realized_first`, and `edd_history`
used to be `SolveHandoff` fields too, populated only by the now-removed
`capture_post_solve()` disk-read constructor. They were retired when the
Expand Down
Loading
Loading