Solved national example · Part 4 of 6
BESS siting in India: where a 5 GW four-hour fleet actually earns
A worked national siting study: rank Indian states by nodal marginal-price spread and evening ramp, place 5 GW / 4 h, re-solve, and test the modelled revenue against real IEX day-ahead prices.
- For
- Grid planners, IPP and utility strategy teams, storage investors and policy analysts
- Time
- 60 minutes plus paired solves
- Updated
- 4 Aug 2026
Direct answer
Rank every bus on the price spread and ramp severity the nodal solve actually produced, place capacity on the top-ranked buses in proportion to score, re-solve the clone on identical settings, and then check the resulting revenue against a market benchmark before calling it revenue — on this network the model puts 98.4% of a 5 GW fleet into the northern load pocket for delivery reasons, not arbitrage.
Solved result first
What the completed analysis must expose
This is the national counterpart to the 30 MW single-project screen in ‘Small BESS project tutorial’: same workflow, but the question is which nodes get capacity rather than what one site does. The production demo below is a 168-hour India state-resolution case solved on 2 August 2026 with PyPSA + HiGHS (build b87889a6, base result 927c1e87, 7.73 s). It is a method benchmark, not a forecast or a market quote. The decisive move is running the same week twice — once on merit-order and once nodally — because the difference between them is the constraint the siting answer is built on.
| Result | Merit-order | PyPSA nodal | Decision reading |
|---|---|---|---|
| Energy served | 36,258.6 GWh | 35,827.4 GWh | 431.2 GWh the network could not deliver |
| System cost | ₹14.7 billion | ₹26.1 billion | Objective value only; compare across matched horizons |
| Emissions | 10.6 MtCO₂ | 19.5 MtCO₂ | Constrained delivery forces dirtier local generation |
| Curtailment | 0 GWh | 355.3 GWh | Renewable output spilled where it cannot be moved |
| Unserved energy | 0 GWh | 492.7 GWh | Demand the network physically failed to reach |
| Average price | ₹404.3/MWh | ₹24,830.9/MWh | Nodal average carries scarcity pricing; not a tariff |
Analytical method
- 01State the decision as a siting question with a capacity, a duration and a number of sites, and name the metric that would falsify it.
- 02Solve the base case on both engines: merit-order to see the fleet, nodal to see the network, and treat the difference as the thing you are actually siting against.
- 03Rank buses on the price spread and ramp severity the solve produced, and read the ranking's shape before trusting its order.
- 04Place capacity in a copy-on-write clone, re-solve on identical settings, and compare physical and commercial metrics separately.
- 05Reconcile modelled revenue against a market benchmark for the same window, and attribute the gap before quoting either number.
- 06Re-run at a second duration and a second fleet size, because duration and scale move system value and owner revenue in opposite directions.
Who should interrogate what
Transmission planner
Is this a storage problem or a wires problem?
Compare curtailment and unserved energy between the merit-order and nodal solves; if the gap is large, storage is competing with a line, and the ranking is telling you where the line is missing.
Storage investor
What would this asset actually be paid?
Ignore the workflow's revenue tile until you have priced the same dispatch at a real market series; the modelled figure is a system value derived from LP duals, not a merchant revenue.
Procurement or policy lead
Two hours or four, and how many tranches?
Read the duration and fleet-size sensitivities together: the system barely notices the extra two hours, the owner earns a third more from them, and the fourth tranche does not earn what the first did.
DISCOM or SLDC planner
Does this help my evening peak?
Check the p95 net-load delta, not the price-spread delta; a cost-optimised battery with no peak term can flatten prices while raising the coincident peak.
Reviewer or agent
Can I reproduce and challenge this?
Take the base and study result IDs, the solve settings and the run manifest hash, re-run the pair, and confirm each configuration returns a different result key before accepting any sensitivity.
Before you start
- • A network whose buses are the unit you are willing to site at — states here, substations if you need a yard-level answer.
- • An untouched base scenario that solves on the nodal engine, not merit-order.
- • A market price series for the same calendar window, used to sanity-check modelled revenue rather than to drive the solve.
What you will have
You will produce a ranked site list with capacities, paired base and study result IDs, a before/after KPI comparison, a duration and fleet-size sensitivity, and an explicit reconciliation between modelled system value and market-priced merchant revenue.
Step-by-step
Complete the workflow
Frame the siting decision before you model
Decide whether you are siting for arbitrage or for delivery, because the two answers sit in different places and are paid by different mechanisms.
Do this
- 1.Write the question as one sentence with a capacity, a duration and a site count — here, 5,000 MW at 4 h across 5 sites.
- 2.List the arbitrage hypothesis (buy cheap midday, sell dear at the evening peak) and the delivery hypothesis (serve load the network cannot reach) separately.
- 3.Name the metric that would falsify each: a flat price spread kills arbitrage, zero unserved energy kills delivery.
Expected evidence
- ✓ A one-sentence decision with an explicit counterfactual and two competing hypotheses to test rather than one to confirm.
Worked calculation
Energy capacity = 5,000 MW × 4 h = 20,000 MWh
Trust check
A siting study answers where, not whether. It excludes capex, fixed O&M, degradation, augmentation, financing, taxes and contracted revenue.
Load a network whose buses are the unit you can site at
Load the catalog dataset whose bus granularity matches the decision; a state-resolution model answers which state, never which yard.
Do this
- 1.Open the data catalog and read each dataset's bus, generator, load and line counts before loading.
- 2.Load ‘India National — demo week (states)’ into a named project, which deep-copies it as a private, editable model.
- 3.Confirm the horizon is 168 contiguous hourly snapshots.
Expected evidence
- ✓ A private model with 33 state buses and an untouched base scenario, and a catalog original that is unchanged.
Trust check
Catalog datasets are read-only sources. Loading copies them into your project, so nothing you do downstream can alter the shared original.
Confirm the geography and fleet you actually loaded
Look at the network on the map before solving, because a siting result only means something relative to the topology that produced it.
Do this
- 1.Open the Map tab and colour generators by carrier.
- 2.Check the inter-state corridors are present — 33 lines here, one per bus pair modelled as a transfer limit.
- 3.Note which regions carry dense generation and which carry dense load.
Expected evidence
- ✓ A recognisable India network with generation clusters and inter-state corridors visible before any scenario edit.
Trust check
Corridor limits are transfer-capacity assumptions, not a circuit-by-circuit seasonal rating register. Treat congestion results as planning-grade.
Audit the component counts and find the gaps
Read the tables before the first solve; the counts are the model, whatever the catalog card promised.
Do this
- 1.Open Tables and record buses, generators, storage units, lines and loads.
- 2.List the carriers present and, more importantly, the ones absent.
- 3.Confirm no batteries exist in the base case so the later comparison is meaningful.
Expected evidence
- ✓ 346 components: 33 buses, 201 generators, 46 storage units, 33 lines, 33 loads, 8 carriers.
- ✓ The 46 storage units are existing hydro and pumped storage; there are no batteries in the base.
| Carrier | Capacity | Note |
|---|---|---|
| solar | 230,678 | Drives the midday price collapse |
| coal | 223,498 | Committable; sets the marginal price most hours |
| hydro | 51,876 | Includes reservoir units modelled as storage |
| gas | 20,122 | Peaking and balancing |
| nuclear | 8,780 | Must-run baseload |
| lignite | 6,620 | Mine-mouth thermal |
| diesel | 470 | Emergency only; dispatched 0 GWh this week |
Trust check
This dataset carries no wind carrier. India has roughly 50 GW of wind, much of it in Tamil Nadu, Gujarat and Karnataka, and it is the resource that most shapes the evening ramp — so this run understates the case for storage in the windy southern and western states.
Solve once with no network — the copper-plate read
Merit-order stacks the fleet by marginal cost and meets demand with no transmission in the model at all.
Do this
- 1.Open Analytics and select the merit-order engine.
- 2.Record energy served, system cost, emissions, curtailment, unserved energy and average price.
- 3.Resist concluding anything from this run.
Expected evidence
- ✓ 36,258.6 GWh served, ₹14.7 bn cost, 10.6 MtCO₂, 0 GWh curtailed, 0 GWh unserved, ₹404.3/MWh average.
Trust check
Zero curtailment and zero unserved energy here are a property of the formulation, not a finding about India. A model with no network cannot locate a constraint.
Solve the same week with the network enforced
Re-solve as a nodal linear program with inter-state transfer limits binding; same fleet, same demand, same hours.
Do this
- 1.Switch the engine to PyPSA (nodal) and re-solve.
- 2.Record the same six KPIs and the base result ID.
- 3.Difference the two runs line by line.
Expected evidence
- ✓ 35,827.4 GWh served, ₹26.1 bn cost, 19.5 MtCO₂, 355.3 GWh curtailed, 492.7 GWh unserved, ₹24,830.9/MWh average, solved in 7.73 s.
- ✓ Cost up 78%, emissions up 84%, and two constraint signals that were invisible a moment ago.
How to read the result
- • 355.3 GWh of renewable output is spilled in one place while 492.7 GWh of demand goes unserved in another.
- • Storage is valuable here precisely because those two places are different — which is a siting statement, not a technology statement.
- • The 61× jump in average price is scarcity pricing entering the duals, and it will contaminate every revenue figure downstream.
Trust check
The 492.7 GWh of unserved energy is what this model does with these transfer limits over one week. Real operators redispatch, invoke reserves and import across ties in ways a weekly dispatch LP does not represent.
Freeze the solve settings for the paired comparison
A before-and-after is only valid when both runs share horizon, formulation, stride and unit-commitment treatment.
Do this
- 1.Open the run options and record engine, horizon, network model, unit commitment and stride.
- 2.Reuse the base result rather than re-solving it, so the workflow runs only the one new solve it needs.
- 3.Note the solver build so the pair can be reproduced later.
Expected evidence
- ✓ A recorded settings set and a base result ID to pass into the workflow, so the comparison differs by storage alone.
Trust check
Comparing runs at different strides or formulations produces a difference you cannot attribute. Change one thing.
Rank the buses and read the shape of the ranking
The workflow scores each bus on normalised price spread plus normalised ramp severity, then takes the top K with a deterministic tie-break.
Do this
- 1.Open Workflows and select the BESS siting study.
- 2.Set total capacity 5,000 MW, duration 4 h, ranked sites 5, and pass the base result ID.
- 3.Before running, read the per-bus spread from the base solve and look at its distribution, not just its order.
Expected evidence
- ✓ A ranking dominated by three northern states, with a long flat tail — the median state's spread is ₹169/MWh.
| State | Mean | Min | Max | Spread |
|---|---|---|---|---|
| punjab | 414,766 | 1,163 | 500,000 | 498,837 |
| haryana | 197,294 | 1,075 | 500,000 | 498,925 |
| delhi | 174,185 | 1,075 | 500,000 | 498,925 |
| rajasthan | 1,347 | 0 | 21,382 | 21,382 |
| chhattisgarh | 651 | −20,000 | 1,244 | 21,244 |
| gujarat | 1,154 | 50 | 4,328 | 4,278 |
| the other 26 | 1,153 | 1,075 | 1,244 | 169 |
How to read the result
- • Delhi, Haryana and Punjab hit ₹500,000/MWh — the value-of-lost-load penalty, not a price. These are delivery failures.
- • Rajasthan bottoms at ₹0 and Chhattisgarh at −₹20,000/MWh: the system paying to shed energy. That is the arbitrage story, worth roughly a twentieth per MWh.
- • Only 3 of 33 buses reach the penalty. For 26 states this week offers storage almost nothing to trade against.
Trust check
Ranking uses each bus's LP marginal price, which is the economic signature of local stress. Where a bus prices at the value-of-lost-load, the score is measuring scarcity, not tradeable spread.
Place the fleet and re-solve the clone
The workflow clones the scenario copy-on-write, splits capacity across the ranked buses in proportion to score, and re-solves at the same resolution.
Do this
- 1.Run the workflow and watch it in the Jobs panel rather than holding the request open.
- 2.Confirm the run completed and note its duration.
- 3.Record the study result ID and result key.
Expected evidence
- ✓ A completed run in 11.8 s reusing the base result, with a new persisted study result and its own result key.
Trust check
Every solve is an addressable job with a recorded duration and status. If a job is missing, the number you are about to quote does not exist.
Read the study result critically, not gratefully
Three things in this result deserve scrutiny, and only one of them is good news.
Do this
- 1.Record the siting table: which buses, how much each.
- 2.Difference the system KPIs — price spread, p95 net load and average price — between base and study.
- 3.Check the sign on every delta before deciding whether it is an improvement.
Expected evidence
- ✓ 98.4% of the fleet in delhi, haryana and punjab (1,640 MW each); rajasthan and chhattisgarh take 40 MW each.
- ✓ System daily price spread ₹45,420 → ₹32,603/MWh, a 28% flattening.
- ✓ P95 net load 236,441 → 238,604 MW — the fleet raises the coincident peak by 2,163 MW.
- ✓ Average price ₹24,831 → ₹25,192/MWh, up slightly.
How to read the result
- • The price-spread reduction is the genuine system benefit and the one a regulator would care about.
- • The rising peak is not a bug in the solver; it is what cost-optimal storage does when nothing prices the peak.
- • Average price rises because round-trip losses are real: at 92% each way you discard about 15% of everything you cycle, and in a week with unserved energy that lost energy is expensive.
Trust check
A battery optimised against energy cost with a cyclic state-of-charge constraint and no explicit peak term will charge during hours that are already tight if the arithmetic pays. If your business case is a capacity contract, the p95 delta is the number that kills it.
Check the scenario lineage before trusting a sensitivity
Re-runs must land in distinct scenarios, or a later run can silently report results for an earlier one.
Do this
- 1.Open the scenario tree and confirm the study clone exists alongside an untouched base.
- 2.Before each new configuration, remove or rename the previous ‘BESS …’ clone.
- 3.After each run, confirm the returned result key differs from the last one.
Expected evidence
- ✓ One untouched base, one clone per configuration, and a different result key for every run.
How to read the result
- • This was caught on the production run: a 2-hour and a 4-hour configuration returned byte-identical results and the same content-addressed result key.
- • The persisted battery state of charge peaked at 16,783.7 MWh, which is infeasible for a 2-hour build at 5,000 MW (10,000 MWh) — proof the 2-hour figures came from the 4-hour scenario.
- • Isolating the clones and re-running produced four distinct result keys and the sensitivities below.
Trust check
The workflow currently names its clone from capacity alone, and the re-solve resolves scenarios by name. Two runs at the same MW can therefore collide: changing only duration or site count may return the earlier run's numbers. Verify by result key, not by trust.
Reconcile modelled value against the market, then hand it over
Convert the workflow's revenue to a per-kilowatt-year figure, compare it to a market benchmark, and attribute the gap before quoting either number.
Do this
- 1.Annualise the workflow revenue and divide by installed kW.
- 2.Compute a perfect-foresight arbitrage benchmark on a real day-ahead series for the same calendar window and the same round-trip efficiency.
- 3.Export the run manifest, source-input lineage and tolerances from the evidence workbench with both result IDs.
Expected evidence
- ✓ Model: ₹16.41 bn per 168 h ≈ ₹171,000 per kW-year — several times the capital cost of the asset, every year.
- ✓ Market: ₹1.01 bn per week ≈ ₹10,543 per kW-year, on IEX day-ahead over 6–12 July 2026 (mean ₹3,443/MWh, floor ₹200, ceiling ₹10,000).
- ✓ A 16.2× gap, and a run manifest hash that lets a reviewer reproduce both halves.
Worked calculation
₹16,413,352,093 per 168 h × 52.14 weeks ÷ 5,000,000 kW ≈ ₹171,000/kW-year
How to read the result
- • Perfect foresight and a single daily cycle make ₹10,543/kW-year an optimistic merchant estimate, and it is still sixteen times smaller than the modelled system value.
- • That difference is the wedge capacity contracts, ancillary payments and viability-gap funding exist to close.
- • Quoting only the model oversells the asset; quoting only the market understates what it is worth to the grid. The useful question — who pays the difference, under what contract — lives in the gap.
| Basis | Week | Annualised | Per kW-year |
|---|---|---|---|
| Model, LP marginal prices | ₹16.41 bn | ₹856 bn | ₹171,000 |
| Real IEX day-ahead, perfect foresight | ₹1.01 bn | ₹53 bn | ₹10,543 |
| Ratio | 16.2× | 16.2× | 16.2× |
Trust check
The workflow's revenue is derived from the LP's marginal prices. In a week where the model fails to serve 492.7 GWh, those prices sit at the ₹500,000/MWh value-of-lost-load, so the figure is the system's value of avoided unserved energy — not revenue anyone would pay into a market. The result card is additionally captioned ₹/yr while rendering the horizon total, which is one week for every demo-week dataset.
Before you share
Completion checklist
- The decision names a capacity, a duration, a site count and a metric that could falsify it.
- The same week was solved on both engines, and the difference between them was read before siting anything.
- The per-bus spread distribution was inspected, not just its ordering, and buses pricing at the value-of-lost-load were identified as scarcity rather than tradeable spread.
- Base and study runs share horizon, formulation and stride, and both result IDs are recorded.
- Every sensitivity landed in its own scenario and returned a distinct result key.
- The p95 net-load delta was checked for sign, not assumed to improve.
- Modelled revenue was reconciled against a market series for the same window, and the gap was attributed rather than averaged away.
- Dataset gaps — here, the absent wind carrier — are stated next to the conclusion they affect.
Answer desk
Frequently asked questions
Why does the model put almost everything in Delhi, Haryana and Punjab?
Because on this network those three states cannot be reached at peak, so their marginal price hits the ₹500,000/MWh value-of-lost-load and dominates a ranking built on price spread. That is a delivery constraint, not an arbitrage opportunity — storage there is competing with a transmission line, and the correct comparison is against building one.
Should I build two-hour or four-hour storage?
On this run the system barely notices: halving duration changes the system-wide price-spread reduction by 0.2%, from ₹12,817 to ₹12,793/MWh. The owner notices a great deal — revenue falls 35%, from ₹16.41 bn to ₹10.66 bn for the week. If you are paid for system value, the cheaper two-hour asset wins; if you are paid on energy, the extra two hours are a third of your revenue.
Does storage revenue scale with fleet size?
No, it falls. Going from 5 GW to 20 GW quadruples the fleet and halves total revenue, ₹16.41 bn to ₹7.10 bn — an 89% collapse per MW — while system benefit roughly doubles, with the spread reduction rising to ₹27,917/MWh and average system price finally falling to ₹21,043/MWh. Storage destroys the spread it earns on, so a pipeline business case built by multiplying the first tranche is wrong by roughly a factor of eight.
Can this tell me which substation to build at?
No. Buses here are whole states, so the answer is Haryana, not a particular 400 kV yard in Haryana. For a yard-level answer use a nodal EHV network whose buses are individual substations, and expect longer solves. For a single-site developer screen at one bus, the 30 MW walkthrough in ‘Small BESS project tutorial’ is the closer starting point.
Is the ₹171,000 per kW-year figure wrong?
It is not wrong, it is mislabelled. It is the system's value of avoided unserved energy, computed from LP duals that sit at the value-of-lost-load in a week with 492.7 GWh unserved. It is not a merchant revenue. Price the same dispatch at a real day-ahead series and you get ₹10,543 per kW-year, and the ratio between the two is the policy question.