Size and run a microgrid that survives an outage
Size solar, a battery and generator sets for a winter storm, add the feeder’s limit and the tariff, then run the storm on forecasts
The lesson in one table: Prairie Campus sized for the storm of 20 to 22 January 2025
| Step | Result (2025 dollars, illustrative) |
|---|---|
| Sized for the storm, flat tariff, no export limit | 2,735 kW · 1,923 kWh · 2 sets, $1,239,049 a year |
| Branch and bound on the sets | the relaxation buys 1.312 sets; 1 branching and 3 LPs prove 2 |
| Exports capped at 0 kW | 2,315 kW · 1,979 kWh · 2 sets, $1,252,435 |
| Then time-of-use | 2,564 kW · 3,559 kWh · 1 set, $1,152,533 |
| Time-of-use without the storm | 2,564 kW · 3,559 kWh · 0 sets, $1,139,904: the storm adds $12,629 a year of equipment and grid cost |
| The check: rounded up to the bench's steps, whole sets, 30% minimum | 2,575 kW · 3,600 kWh · 1 set, $1,152,548 a year: 0 kWh of must-stay-on load unserved, 675 gallons ($2,470) |
| Ten drawn winter outages | must-stay-on loads fully served in 10 of 10 |
| Rolling horizon (the stated controller), forecast “perfect” | 0 kWh of must-stay-on load unserved |
| Rolling horizon (the stated controller), forecast “yesterday again” | 2,745 kWh of must-stay-on load unserved |
| Rolling horizon (the stated controller), forecast “too sunny” | 5,586 kWh of must-stay-on load unserved |
| Rolling horizon (the stated controller), forecast “too dull” | 0 kWh of must-stay-on load unserved |
With JavaScript on, you solve each case with a real solver in your browser, grow the branch-and-bound tree, check the plan hour by hour and run the storm on four forecasts.
- Illustrative: Prairie Campus and its flat loads (from the Workbench and F7), equipment costs, fuel use, the export credit and the owner's 5% cost of capital, in 2025 dollars.
- Solar: illustrative campus output per kW of panels: SPP's observed 2025 hourly shape (EIA-930, accessed 2 Nov 2026), scaled to EIA's monthly capacity factors (Table 6.07.B, issue of 23 Oct 2026).
- Tariff: EIA's 2025 U.S. commercial average, 13.37¢/kWh.
- The storm: an assumed grid outage for the 72 hours from 00:00 on 20 January 2025; the battery and the 675-gallon tank start full.
- Solver: HiGHS (open source), loaded on your first solve (about 3.5 MB, once).
Build it in Python · notebook lab, about 60 minutes, then 90 minutes of practice
The engine's models with scipy.optimize.milp and HiGHS (the relaxations with integrality off): the storm, the export limit, the tariff, the ten seeded outages and the rolling horizon.
What it checks: assert cells match the browser's sizes, costs, tree, check and drawn outages. The rolling horizon's totals are printed beside the browser's, with each real hour's physics asserted.
- Download the notebook and the engine's data file; put them in one folder.
- Upload both to Google Colab (File → Upload notebook, then the folder icon), or open the notebook locally with Python 3, NumPy and SciPy 1.9 or later.
- Run all cells, then try the practice questions at the end.
What you just did, as diagrams
- Sizing as a mixed-integer program
- Solar and battery sizes are continuous; generator sets come whole. One model holds a normal year of 12 average days and the storm's 72 hours.
- Branch and bound
- The relaxation may buy part of a set; splitting into fewer and more sets, and pruning, proves the best whole number.
- No prices
- HiGHS returns no duals for a mixed-integer program; the extra equipment-and-grid cost of carrying the hospital is the difference between two solved plans, storm diesel not included.
- Check and test
- The plan, rounded up to the bench's steps, is run again with whole sets and their 30% minimum, then on ten drawn winter outages.
- Rolling horizon
- Re-planning every 6 hours on a forecast, with a stated rule for each real hour, shows what a plan sized with perfect foresight loses when the sun surprises it.
Go deeper · ModelerThe sizing model in symbols
Every hour lasts \(\Delta t = 1\) h, so a kW held for an hour is a kWh; “\(B/4\)” is the battery’s power, a quarter of its kWh.
| Symbol | What it is | Unit | Decided or given |
|---|---|---|---|
| \(S\), \(B\) | solar panels; battery energy (power \(B/4\)) | kW; kWh | decided, \(0 \le S \le 4{,}000\), \(0 \le B \le 8{,}000\) |
| \(N \in \{0, 1, \dots, 4\}\) | generator sets of 200 kW | whole sets | decided |
| \(i, e, s, c, d\) | import, export, solar used, charge, discharge in an hour | kW | decided, all \(\ge 0\) |
| \(q\) | battery stored at the hour’s end | kWh | decided, \(\ge 0\) |
| \(g_t\), \(o_t\) | the sets’ output; other loads served, storm hour \(t\) | kW | decided, \(\ge 0\) |
| \(a_S, a_B, a_N\) | each item’s cost a year: capital \(\times\) the capital recovery factor at the owner’s real rate, plus upkeep | $ per kW, kWh, set | given |
| \(a_F\), \(F\) | the tank’s cost a year per gallon; diesel on site, 675 gallons | $/gal; gal | given: \(F\) is not sized |
| \(w_m\), \(\sigma\) | days in month \(m\); sun per kW of panels | days; kW/kW | given |
| \(r_h\), \(x\) | tariff in hour \(h\); export credit | $/kWh (EIA’s ¢ ÷ 100) | given |
| \(\bar I\), \(\bar E\) | the feeder’s import and export limits | kW | given |
| \(L^{\text{must}}\), \(L^{\text{other}}\) | the must-stay-on load (400 kW here); the other loads (800 kW) | kW | given |
\[\min\; a_S S + a_B B + a_N N + a_F F + \sum_m w_m \sum_h \big(r_h\, i_{m,h} - x\, e_{m,h}\big)\]
Each hour \(h\) of each average day \(m\):
\[s + d + i = c + e + 1{,}200, \quad s \le \sigma_{m,h} S, \quad c, d \le B/4,\; q \le B,\; i \le \bar I,\; e \le \bar E\]
\[q_{m,h} = q_{m,h-1} + 0.9\,c_{m,h} - d_{m,h}, \quad q_{m,-1} = q_{m,23} \;\text{(each day ends where it began)}\]
Each storm hour \(t\), with no grid:
\[s_t + d_t + g_t = c_t + o_t + L^{\text{must}}, \quad s_t \le \sigma_t S, \quad c_t, d_t \le B/4,\; q_t \le B,\; o_t \le L^{\text{other}}\]
\[q_t = q_{t-1} + 0.9\,c_t - d_t,\; q_{-1} = B, \quad g_t \le 200\,N, \quad \textstyle\sum_t 0.07\, g_t \le F\]
The check fixes the sizes, adds \(g_t \ge 60\,u_t\) and \(g_t \le 200\,u_t\) with \(u_t \in \{0, \dots, N\}\) sets on, and lets every load be partly served, with unserved must-stay-on load weighted first.
Go deeper · ModelerWhy the gap and the tie rule matter here
- The sizing stops only within 0.0001% of the best bound (HiGHS options,
mip_rel_gap), so plans a few dollars apart are not passed off as one. - The check's hourly schedule is not unique: the battery can often cover an hour now or later at the same total.
- So its objective adds stated weights, not a strict order: unserved kWh weigh 1,000, 10 and 1 by priority; 0.5% more a step up the list; up to 2% more for earlier hours, which can outweigh the list; then fuel and battery use.
- The page reports only totals those weights fix. A run stopped by its time limit shows its gap and says it is not proven.
Go deeperWhat this model leaves out
- Marginal prices of any kind from the model: a mixed-integer program has no duals.
- Storm diesel and refilling the tank, in the annual cost: an outage's fuel is reported on its own, with no outage frequency assumed.
- The campus's wiring and protection, voltage and frequency inside the island (lesson F7 shows who forms the island).
- Part-load fuel use (a lightly loaded set burns more per kWh), start-up limits and a set that fails to start.
- Battery wear, fuel deliveries during the storm, and how often outages happen; and a full chronological year: the 12 average days can put a large solar-and-battery plan's bill up to 7% low (the Workbench shows the check).
- Loads that change through the day: every load is flat here.
Modeler's note: M7 samples uncertain inputs with Monte Carlo, and M8 decides before the uncertainty resolves.
Check yourself
Myth or fact? “The sizing MILP tells you the price of one more kWh of battery.”
- Myth
- Fact
Answer
Myth. A mixed-integer program returns no duals. To value a change, size the plan twice, with and without it, and subtract.
Myth or fact? “A plan that carries the hospital through the storm in the sizing model will carry it in real operation too.”
- Myth
- Fact
Answer
Myth. The sizing knows the coming sun. In step 7, a controller that plans on yesterday's sun runs the battery down too early and leaves must-stay-on loads short.
Myth or fact? “If the relaxation buys 1.3 generator sets, no plan with whole sets can cost less than the relaxation.”
- Myth
- Fact
Answer
Fact. Whole sets only add a rule to the relaxation, so its cost is a lower bound for every whole-set plan.
How does this lesson put a number on the extra equipment-and-grid cost of carrying the hospital through the storm?
- From the dual of the storm's energy balance
- From the price of diesel
- By sizing the plan with and without the storm, and subtracting
- It cannot be put in numbers
Answer
By sizing twice and subtracting: the difference between two solved plans, which is not a price. Storm diesel and outage frequency are not in it.
The plan rides through all ten drawn winter outages. What can you claim?
- It will survive any winter outage
- It passed these ten tests, with the model's assumptions
- It fails one outage in ten years
- It is the cheapest plan for every outage
Answer
Only that it passed these ten tests. Ten drawn outages are a teaching sample, not outage statistics.
Numbers, sources and what’s next
Numbers to remember
| Number | What it means | Source |
|---|---|---|
| 40 of 72 hours | The storm hours with no sun at all, 20 to 22 January 2025: the battery and the generator sets carry the nights. | This lesson's data (SPP's 2025 sun, EIA-930) |
| 0.0001% | The gap at which this lesson's sizing counts as optimal; HiGHS's default is 0.01%. | HiGHS options · checked 3 Nov 2026 |
| 10.19 kg | CO₂ from burning one gallon of diesel. | EIA coefficients · released 18 Sep 2024 |
Keep going
In the real world
When each facility can install its own wind, solar and storage and trade energy with the grid, a candidate site's renewable resource affects its operating cost.
Three more sentences from the study: its findings and its model
Transactive energy, time-of-use tariffs and climate conditions played a critical role in locating factories, warehouses, wind turbines, solar photovoltaics and energy storage systems.
Contrary to intuition, energy storage systems, which balance intermittent power and uncertain load, had limited benefits for cost saving.
A mixed-integer model opens factories and warehouses, routes products and sizes each open site's wind, solar and storage, with an hourly energy balance and grid trade. Uncertain weekly store demand enters as a chance constraint with a set service level, written as its deterministic equivalent, so the model stays a mixed-integer linear program.
Holds when: stylized networks with synthetic distances and costs, candidate sites in California, Arizona and Nevada, and one year, planned week by week for products and hour by hour for energy. Leaves out: grid outages, which the studies do not model.
- The full case: case file CF1.
- Open code: supply-chain-microgrid-models on GitHub.
- Builds on: M2 (whole numbers and branch and bound), the Microgrid Workbench (the same engine), F7 and A14 (the campus and its value) and lesson 9 (storage).
- Applied in: the capstone C1. Open tools for larger models: PyPSA, Pyomo and JuMP.
Sources and further reading
- Li et al. (2023). Facility and microgrid location-allocation for integrated supply chain and transactive energy operations. Applied Mathematical Modelling 119, 119–136.
- EIA. Hourly Electric Grid Monitor (Form EIA-930): the Southwest Power Pool solar behind the storm's sun.
- HiGHS. Documentation of the open-source solver used in the page and, through SciPy, in the notebook.
Spotted an error, or stuck? Write to hlli@ksu.edu. Corrections are made in place and dated here.
Text licensed CC BY 4.0, code MIT: teachers and students may reuse and adapt it, with credit to Honglin Li.