Skip to content

Phased portfolio commissioning

Generated: 2026-05-08. Model: 10-site JS-SEZ stylised composite portfolio, identical site sizes to aggregator_portfolio.md, but each site has a commissioning_year offset (0 = project t=0). Two anchors come online each of years 0–4 plus one in year 5, mirroring the public-announcement wave pattern (Sedenak phases, AirTrunk JHB1 staging, YTL Green DC tranches). Per-unit economics auto-derived from btm_economics_dc100.json (same as the instant-deployment aggregator).

Phasing capex over 6 years improves portfolio NPV by +371 M RM (+17%) vs instant deployment — but the absolute headline is still −1,843 M, because the BESS gap is structural, not timing-related.

MetricInstant deploymentPhased (default)Δ phased
Portfolio NPV−2,213 M−1,843 M+371 M
PV capex (undiscounted)2,5722,5720
PV capex @ PV t=02,5722,106−466
BESS capex (undiscounted)3,6753,6750
BESS capex @ PV t=03,6753,009−666
Final aggregate tierTier 2: DRC + capTier 2: DRC + capsame
Project end yearyear 20year 25+5 yr

The +371 M lift is essentially a capital-cost-of-money refund: discounting the late-coming sites’ capex saves ~1.1 B RM total (PV+BESS combined), but those sites also accrue savings later, so the net benefit is the timing spread between capex (early in the site’s life) and savings (across the 20-yr horizon, more of which sits beyond t=0).

What phasing actually does (and doesn’t do)

Section titled “What phasing actually does (and doesn’t do)”
  • Discount distant capex. Late-stage BESS capex at year 5 is worth only 68% of its nominal value in t=0 terms. On 184 MW of BESS at 5 M RM/MWh, that’s meaningful (~660 M present-value savings on capex alone).
  • Defer commercial risk. A phased build lets each tranche learn from the prior one’s interconnection / O&M lessons before the next tranche commits.
  • Match anchor-tenant ramp. Hyperscaler MW commissions in waves; phased PV+BESS matches the load curve more closely than a one-shot install would.
  • Close the BESS gap. At 5 M RM/MWh capex with current BTM-only economics (100 kRM/MWh/yr incremental), the per-MWh NPV is still structurally negative. Phasing rearranges the cashflow — it doesn’t make the gap go away.
  • Earn higher VPP-tier revenue. The portfolio still tops out at Tier 2 at full deployment (~184 MW BESS); phasing doesn’t push us into Tier 3 (>200 MW) without changing the underlying portfolio.
  • Move the breakeven aggregator rate. The 162 kRM/MW/mo breakeven from aggregator_portfolio.md is invariant — phasing doesn’t change per-MW economics.
YearOnline PV (MW)Online BESS (MW)Aggregate tier
000.0Sub-market (capex year only)
17518.8Tier 1: minor DRC
219548.8Tier 1: minor DRC
331578.8Tier 2: DRC + capacity
4495123.8Tier 2: DRC + capacity
5615153.8Tier 2: DRC + capacity
6735183.8Tier 2: DRC + capacity (full)
7-20735183.8Tier 2: DRC + capacity
21-25tail-off (sites decommissioning at site_horizon=20)

The portfolio crosses into Tier 2 (60 kRM/MW/mo, DRC + capacity) at year 3, two years earlier than the median site’s mid-life — meaning 3+ years of sub-tier VPP revenue lost vs the (counterfactual) world where Tier 2 clears immediately at full deployment.

  1. Deal-team realism check. Headline NPVs in aggregator_portfolio.md assume instant build-out, which overstates the BESS gap by ~17%. Use this report’s −1,843 M for boardroom pricing instead.
  2. Phasing-strategy A/B testing. Pass a custom tuple[TimedSite, ...] into evaluate_phased_portfolio() to compare alternative deployment sequences (e.g., “frontload BESS into year 0” vs “BESS-only-after-year-3”).
  3. Tier-progression diagnostics. The schedule field in the JSON output shows which years the portfolio sits in which tier — useful for negotiating tier-conditional VPP service contracts where revenue switches on at a known threshold.

For each year t in [0, end_year]:

  • Capex(t) = sum of (PV + BESS) capex for sites whose commissioning_year == t.
  • Augmentation(t) = sum of (0.5 × BESS capex) for sites whose commissioning_year + 10 == t.
  • Online sites at t = sites with commissioning_year < t ≤ commissioning_year + 20.
  • Savings(t) = online_pv_mw × pv_per_mw + online_bess_mwh × bess_per_mwh.
  • VPP revenue(t) = online_bess_mw × tier(online_bess_mw) × 12 months.
  • OpEx(t) = online_pv_mw × pv_opex + online_bess_mwh × bess_opex.
  • Net(t) = −Capex(t) − Augmentation(t) + Savings(t) + VPP rev(t) − OpEx(t).

NPV = Σ_t Net(t) / (1 + r)^t with r = 8%.

The model converges to aggregator.evaluate_portfolio exactly when every site’s commissioning_year = 0 — pinned by tests/test_portfolio_timeline.py::test_phased_with_all_zero_commissioning_matches_aggregator_exactly.

  • Commissioning timing is hard-coded in DEFAULT_TIMELINE from a stylised wave pattern, not from real dc_tracker dates (which are private and not yet populated; see CLAUDE.md). When dc_tracker.sites becomes populated, swap the default in.
  • No revenue ramp during the commissioning year. A site commissioned in year c produces zero savings in year c and full savings from year c+1. In reality there’s a partial-year ramp; the bias is small (~½ year × per-site savings) and fits within the existing capex-vs-opex precision band.
  • Per-MW economics are still linear (validated invariant). Phasing does not change the per-MWh BESS gap — it only shifts the cashflow.
  • aggregator_portfolio.md — instant-deployment baseline (−2,213 M).
  • vpp_service_revenue_required.md — single-site 209 kRM/MW/mo breakeven.
  • bess_trigger_curve.md — capex-fall × VPP-revenue trigger matrix.
  • risk_adjusted_npv.md — Monte Carlo on capex / discount / load growth.

reports/portfolio_timeline.json has the full schedule (per-year cashflow trace), all aggregate present-value capex / augmentation, and both phased and instant NPVs for one-shot delta comparison.