State of Charge Is the Real Constraint: Why Dispatch Is a Schedule, Not a Rule

State of Charge Is the Real Constraint: Why Dispatch Is a Schedule, Not a Rule

September 18, 2026 · Dezzmond Team
Storage Sizing & Dispatching a Battery State of Charge

The two previous posts produced clean rules. Charge when the spread clears the cycle cost. Discharge when the price beats the storage value.

Both assume something that is not true: that the battery is in a position to act.

You cannot discharge energy you did not store, and you cannot store energy without having spent hours charging beforehand at a limited rate. State of charge is the variable that links every hour of the day to every other one, and once it enters, the clean rule becomes a schedule.

This post is about that constraint: why the best hours are not freely selectable, how much charging time a peak actually requires, what the warranty does to the usable range, and why a dispatch model that does not track state of charge produces revenue that cannot be earned.

ℹ️ Note: The price shape here is a two-peak day, chosen because single-peak days conceal most of the scheduling difficulty. Figures are labelled assumptions.

State of charge links every hour to every other hour:

flowchart LR
  S0[Opening SoC] -->|charge - losses| S1[SoC after hour 1]
  S1 -->|discharge / reserve| S2[SoC after hour 2]
  S2 -->|charge / discharge| S3[SoC after hour 3]
  S3 --> S4[Closing SoC]
  MIN[Minimum SoC / ancillary reserve] -. floor .-> S1
  MIN -. floor .-> S2
  MAX[Usable-energy ceiling] -. cap .-> S2
  MAX -. cap .-> S3

What State of Charge Actually Does

It makes the hours dependent on each other.

State of charge is the accumulated consequence of earlier charge / discharge decisions and constrains every later one. Other inputs can also be path-dependent — degradation, temperature, availability and ancillary commitments — but SOC is the core physical inventory state.

SoC(h) = SoC(h−1) + η_charge × Charge(h) − Discharge(h) / η_discharge

with:  RTE = η_charge × η_discharge

subject to:   SoC_min  ≤  SoC(h)  ≤  SoC_max
              Charge(h), Discharge(h)  ≤  Power_Rating
              Charge(h) × Discharge(h) = 0        (not both at once)

That recursion is the whole problem. It cannot be solved hour by hour because each hour's feasible set depends on choices made earlier and constrains choices made later.

The Best Hours Are Not Freely Selectable

The most common modelling shortcut is to pick the N highest-priced hours and assume the battery discharges into all of them. It is wrong for two reasons, and the second is the expensive one.

Take a two-peak day:

Hour    00  01  02  03  04  05  06  07  08  09  10  11
Price   22  19  17  16  16  25  55  85  70  40  18  12

Hour    12  13  14  15  16  17  18  19  20  21  22  23
Price   10  12  20  32  50  75  92  85  60  42  30  25

The four highest-priced hours are 18 ($92), 7 ($85), 19 ($85) and 17 ($75).

Reason one: they are split across two peaks. Serving hour 7 and hours 17–19 requires two separate charge–discharge cycles with a recharge in between. That is possible here — the midday trough at hours 11–13 provides it — but it is a scheduling fact, not a price fact, and the top-N selection did not check it.

Reason two: consecutive high hours compete for the same energy. The evening block runs 17, 18, 19, 20 at $75, $92, $85 and $60. A battery cannot discharge into all four unless it has four hours of energy:

Duration Hours served Revenue captured Hours missed
2 hours $92, $85 $177 $75, $60
3 hours $92, $85, $75 $252 $60
4 hours $92, $85, $75, $60 $312 —

That table is the duration question from earlier in this series, arriving again from the operational side. The declining marginal hour is not an abstraction about price distributions — it is literally the fourth-best hour of a consecutive block, and whether you can reach it depends on having stored enough.

Charging Takes Time, and the Time Must Exist Before the Peak

The constraint that catches morning peaks.

Filling a battery is not instantaneous. Do not use round-trip efficiency as though all losses occur on the charging leg. For a worked screening example, assume a 94% charge efficiency and 1 MW charging power; storing E MWh then requires E / 0.94 hours of AC charging:

Capacity Hours of charging required
2 MWh 2.13
4 MWh 4.26
6 MWh 6.38

Now check the morning peak. Hour 7 is the first high-priced hour of the day, which leaves hours 0 to 6 — seven hours — in which to charge. At the illustrative 94% charge efficiency, seven full-power charging hours can add 6.58 MWh to SOC, subject to the opening SOC and upper bound.

But those charging hours are priced at $22, $19, $17, $16, $16, $25 and $55. They are not the cheapest hours of the day — the midday trough at $10–12 is much cheaper — and the cheap hours come after the morning peak, when they are useless for serving it.

That is the ordering problem in one example. A rule that charges in the cheapest hours will be empty at hour 7. A rule that serves hour 7 must charge at prices roughly double the daily minimum. Which is right depends on whether the morning spread justifies the more expensive energy, and the top-N approach cannot ask that question because it has no concept of sequence.

The Usable Range Can Be Narrower Than Nameplate

The model needs the usable operating envelope, not a generic 0–100% assumption and not a universal 10–95% rule.

OEM limits and lifetime strategy can restrict minimum / maximum SOC, usable energy, temperature, power and depth of discharge. Those limits can also change with age or operating mode.

Worked example only:

Nameplate capacity                                   400 MWh
Assumed operating range, 10%–95%                    340 MWh usable
Worked effective duration at 100 MW                 3.4 hours

That 3.4-hour figure belongs to the assumed operating band. Another OEM, warranty or operating policy can produce a different usable range.

Two modelling consequences follow.

Use the same energy basis everywhere. Capacity accreditation, tolling guarantees, dispatch, degradation and augmentation should not alternate silently between nameplate, beginning-of-life usable energy and guaranteed usable energy.

SOC bounds and degradation are linked. Tighter operating bands can reduce degradation but also reduce marketable energy. The optimization should value that trade rather than assume the narrowest range is automatically best.

Why This Makes It a Scheduling Problem

Put the pieces together and the character of the problem changes.

The charge rule needs a view of later prices. The discharge rule needs a storage value that depends on remaining time and current state of charge. And the state of charge at any hour is determined by every prior decision, while constraining every subsequent one.

That is an optimization over a horizon, not an independent hour-by-hour rule. Historical studies may use perfect foresight as an upper-bound benchmark, while operational models use forecasts, stochastic scenarios or rolling-horizon updates. Do not present perfect foresight as the standard real-world dispatch construction.

One practical form is a linear or mixed-integer optimization over the horizon, subject to SOC recursion, power limits, efficiency, availability, warranty / throughput and market-product constraints. Whether a binary operating-mode variable is needed depends on the formulation and market economics.

What the threshold rules retain is their value as diagnostics. The minimum viable spread tells you whether a market is worth being in. The storage value tells you what a megawatt-hour is worth right now. They just cannot, on their own, produce a schedule.

Ancillary Services Create Product-Specific SOC Constraints

An ancillary award can reserve energy, charging headroom, power capability or a combination of them. The constraint depends on the product and ISO rules.

A simplified upward-reserve illustration is:

Required stored energy
≈ AS_Award_MW × required delivery duration / η_discharge

while a downward / charging service can require headroom below the maximum SOC.

Do not hard-code every ancillary award as a one-hour SOC floor. ERCOT's current rules after RTC+B explicitly use ESR state of charge in real-time co-optimization, and current / pending protocol changes continue to refine storage-product treatment. ECRS duration references, for example, are aligned to the as-built one-hour requirement in NPRR1339.

The model should therefore carry, by product:

MW award
delivery-duration requirement
upward stored-energy requirement
downward charging-headroom requirement
deployment-energy assumptions
performance / telemetry constraints

Then let those requirements narrow the feasible SOC band interval by interval.

Simultaneous Charge and Discharge Needs a Consistent Physical Boundary

At the battery / DC inventory boundary, the same cell inventory cannot physically charge and discharge at the same instant.

At an AC point of interconnection, however, gross flows can be more complicated: auxiliaries, co-located generation, multiple settlement points or separate market products can create simultaneous-looking import / export quantities even when the battery inventory has one net SOC movement.

So the optimizer should define the boundary carefully.

For a standalone BESS inventory model, enforce or check:

Charge_to_battery(h) > 0  ⇒ Discharge_from_battery(h) = 0

A binary formulation is one way to impose that condition, but not always the only one. In some convex models, losses and prices make simultaneous cycling uneconomic without a binary. If negative prices, subsidies, separate settlements or hybrid flows can create artificial profit, enforce complementarity explicitly.

The practical QA test remains valuable: flag intervals with both gross battery charge and discharge and explain each one.

The Cost of Getting the Starting Point Wrong

A practical consequence of the recursion that is easy to overlook: the schedule depends on where the battery starts.

A model that forcibly resets SOC at each daily boundary can distort revenue. Resetting to full injects free stored energy; resetting to empty can destroy legitimate carry value. For multi-day analysis, carry SOC chronologically unless the operating contract genuinely resets the position.

Day-boundary treatment              Effect on modelled revenue
Start empty each day                Understates — misses overnight carry
Start full each day                 Overstates — free energy every day
Carry SoC across the boundary       Correct

The boundary error can be especially material for long-duration and multi-day strategies. A ten-hour battery can complete one full charge/discharge cycle inside 24 hours under some power and efficiency assumptions; the issue is not physical impossibility, but that a daily reset destroys the option to carry energy between days.

The fix is conceptually simple: carry closing SOC into the next interval / day and preserve a terminal-value assumption at the end of the modeled horizon. A daily model should reset SOC only if an actual operating requirement forces that reset.

There is a related question at the end of the horizon. A schedule optimised to the last hour will empty the battery, because energy has no value beyond the modelled window. That produces an artefact: the final day of any modelled period shows unusually high revenue and leaves the asset empty. Either terminate the analysis before the boundary or attach a terminal value to the remaining state of charge.

How Do You Model State of Charge in Excel?

As a running balance with hard feasibility checks, before any revenue is recognised.

The recursion

SoC(0)   = Starting_SoC
SoC(h)   = SoC(h−1) + η_charge × Charge(h) − Discharge(h) / η_discharge

Feasibility tests, every hour:
   SoC(h) ≥ SoC_min
   SoC(h) ≤ SoC_max
   Charge(h) ≤ Power_MW
   Discharge(h) ≤ MIN(Power_MW, (SoC(h−1) − SoC_min) × η_discharge)
   Charge(h) × Discharge(h) = 0

The fourth line is the one that matters most and is most often omitted: you cannot discharge more than you are holding.

The check that catches the common error

PF_InfeasibleHours = COUNT of hours where a scheduled discharge
                     exceeds available state of charge

If PF_InfeasibleHours > 0, the revenue in this model cannot be earned.

A model built by selecting the best hours and summing their prices will frequently fail this test, and the failure is silent unless the check exists.

The usable energy correction

Illustrative usable MWh = 400 × (0.95 − 0.10)       = 340 MWh
Illustrative duration   = 340 / 100                  = 3.4 hours

For the actual project, replace this with the contract / OEM definition
of guaranteed usable AC or DC energy at the relevant measurement point.

Run every duration calculation on usable energy, not nameplate.

The outputs to publish

PF_UsableDuration               based on guaranteed / modeled usable energy
PF_InfeasibleHours              must be zero
PF_EndOfDaySoC                  does the schedule leave the asset positioned?
PF_ChargingHoursRequired        E / η_charge / Charge_MW, checked chronologically

The third is worth reporting for a reason that is easy to miss: a schedule that empties the battery at midnight has optimised the day and may have left the asset unable to serve an early peak tomorrow.

ℹ️ Note: Never compute storage revenue by selecting the highest-priced hours. The selection is a price calculation and the answer is a scheduling problem — and the gap between them is revenue that cannot be delivered.

To build the state-of-charge recursion, the feasibility checks and the usable-duration correction, prompt Dezzmond with your price series, power rating and operating bounds.

What Do Operators and Lenders Actually Check?

  • Does the model track state of charge hour by hour, with the discharge limited by it?
  • Is the count of infeasible hours reported, and is it zero?
  • Is duration computed on usable energy or on nameplate?
  • What operating bounds are assumed, and do they match the warranty?
  • Are the best hours consecutive? If so, duration determines how many are reachable.
  • Is there enough charging time before each peak, at the power rating?
  • Is state of charge carried across the day boundary, and is there a terminal value at the end of the horizon?

Frequently Asked Questions

Why can't you just pick the highest-priced hours?

Because the selection ignores sequence. The best hours may be split across separate peaks requiring a recharge in between, or consecutive and competing for the same stored energy — and a battery cannot discharge into more consecutive hours than it has hours of duration.

How long does it take to charge a battery?

Stored-energy requirement divided by charging power and charge efficiency. In the worked 94% charge-efficiency example, adding 4 MWh to SOC at 1 MW takes about 4.26 hours. The real model should use the OEM charge-efficiency curve.

Why can usable capacity be less than nameplate?

Because OEM limits, warranty conditions and the operating strategy can reserve energy at the top or bottom of the SOC range. The 10%–95% band in the worked example is illustrative; use the actual guaranteed / permitted operating envelope.

Does state of charge make threshold rules useless?

No, it makes them insufficient. The minimum viable spread and the storage value remain the right diagnostics for whether a market and a moment are worth acting on. They just cannot produce a feasible schedule on their own.

Does an ancillary award restrict state of charge?

It can impose a stored-energy floor, charging-headroom ceiling or other product-specific constraint. Use the ISO product duration, deployment and SOC rules rather than a universal one-hour reserve formula.

Why does the model constrain simultaneous charging and discharging?

To prevent the optimizer from creating artificial cycling through one battery inventory. Define the physical boundary first: co-located generation and gross AC flows can coexist while the battery SOC still has one net direction. A binary is one implementation, not a universal requirement.

What does a dispatch optimiser actually solve?

Revenue or margin maximisation over a horizon subject to SOC recursion, charge / discharge power, efficiency, operating bounds, degradation / throughput constraints, ancillary commitments and terminal SOC. A perfect-foresight backtest is a benchmark, not a live forecast.

Closing: The Constraint That Turns Rules Into Schedules

Each of the last two posts produced something clean. A minimum spread that tells you whether a cycle is worth running. A storage value that tells you what the energy in the battery is worth right now. Both are correct and both are useful.

Neither can run a battery, because both assume a freedom the asset does not have. The energy has to be there. Putting it there takes hours at a limited rate, in a window that has to occur before the price you are targeting. And every megawatt-hour spent now is one not available for the next opportunity.

That is what makes storage distinctly intertemporal. Solar and wind output are serially correlated through weather and can face multi-hour curtailment, but generating one MWh does not normally deplete a finite stored-energy inventory. A battery’s dispatch intervals are linked directly by SOC.

It is worth noticing how much of this material has converged on that single variable. Duration is the length of the chain. Throughput / warranty limits constrain how aggressively it can be traversed. OEM operating limits define the permitted SOC and power envelope. Ancillary awards can reserve stored energy or charging headroom. And the storage value from the previous post is precisely the marginal worth of one link. Five apparently separate decisions, all expressed in the same state variable — which is the clearest sign available that it belongs at the centre of any storage model rather than as a constraint bolted on at the end.

The practical consequence for anyone reading a storage revenue forecast is a single question: does the model track state of charge, and does it check that every scheduled discharge was actually available? A forecast built by summing the best hours of the year is not conservative or aggressive — it is describing an asset with infinite energy and instantaneous charging, and the gap between that and a real battery is not small.

The last post in this series takes the remaining gap: between a schedule optimised against known prices and a revenue forecast a lender will lend against.

Sources: NREL SAM — battery dispatch, efficiency and lifetime modeling · CAISO — Ancillary Service State-of-Charge Constraint initiative · CAISO DMM — 2023 Special Report on Battery Storage; SOC effects of ancillary awards · ERCOT current Nodal Protocols — current real-time / ESR rules · ERCOT RTC+B go-live — battery SOC explicitly modeled in real-time co-optimization · ERCOT NPRR1339 — ECRS duration alignment under RTC+B · Dezzmond analysis; SOC bands and reserve examples are illustrative.