Putting carbon in the model: caps, prices, frontiers and procurement rules
Cap the Plains Grid’s CO₂ in a planning LP, trace the frontier, compare a carbon price, then add an hourly-matching rule and see what it costs and what it changes
The Plains Grid's planning LP: caps, their duals, and four purchases for a data center
| Cap, share of 4.78 Mt | CO₂ | Cost | Dual, $ per t |
|---|---|---|---|
| No carbon rule | 4.78 Mt | $388.9M | – |
| 90% | 4.30 Mt | $399.4M | $21.98 |
| 80% | 3.82 Mt | $410.1M | $22.59 |
| 70% | 3.34 Mt | $421.0M | $22.81 |
| 60% | 2.87 Mt | $433.2M | $32.36 |
| 50% | 2.39 Mt | $450.0M | $40.35 |
| 40% | 1.91 Mt | $476.5M | $83.50 |
| 30% | 1.43 Mt | $525.8M | $130.61 |
At the 70% cap's dual, $22.81 per t rounded (the tie needs its full precision), every plan from 3.15 Mt to 3.64 Mt scores the same: a price there picks no single plan.
| The 300 MW data center buys | May claim | The plants emit | Cost | Built for it |
|---|---|---|---|---|
| No rule on what it buys | its share at average factors | +1.14 Mt | $0.0M | nothing |
| Certificates from existing wind | 0 t | +1.14 Mt | $0.0M | nothing |
| New wind and sun, matched over the year | 0 t | +0.04 Mt | +$23.0M | 844 MW wind |
| New wind, sun and batteries, matched hour by hour | 0 t | −2.48 Mt | +$127.1M | 2,382 MW wind, 615 MW solar, 199 MW battery |
The plants' change is against the grid without the data center; the cost against no rule. A year in 2025 dollars.
With JavaScript on, you set each cap, price and rule yourself and the solver re-plans the year in your browser.
Illustrative grid. The Plains Grid of A10 and A12, fictional, with EIA's 2025 gas heat rates, gas price and CO₂ coefficient.
Costs. New wind and solar at EIA's AEO2026 values; a 4-hour battery at NREL's 2025 update, moved to 2025 dollars; 4% real.
Six days for a year. Each season's most typical day of 2025, plus the calmest day and the day of the highest net load, weighted by the days they stand for (EIA-930 shapes).
About 40 guided minutes in seven steps. Solver: HiGHS (open source), loaded on your first solve (about 3.5 MB, once).
Build it in Python · notebook lab, about 45 minutes, then 90 minutes of practice
The same planning LP in scipy.optimize.linprog(method="highs"): caps and their duals, the frontier, prices and their ties, the two matching rules, and the full-year checks. assert cells check your numbers against the lesson's reference block.
- Download the notebook and the four data files into one folder (in Colab, upload them with the folder icon).
- Open it in Google Colab (File → Upload notebook) or locally with Python 3, NumPy and SciPy 1.9 or later.
- Run all cells (about a minute), then try the practice tasks at the end.
Notebook (.ipynb) Lesson data The fleet Year 2025 Capacity factors
What you just did, as diagrams
- A cap
- A rule on the year's CO₂. At 70% of 4.78 Mt the plan costs $32.1M more, and the cap's dual is $22.81 per t.
- The frontier
- The dual is the extra cost of cutting one more tonne, the magnitude of the frontier's downward slope: from $21.98 at 90% to $130.61 at 30%.
- A price and its ties
- A price at the 70% cap's unrounded dual ties every plan from 3.15 Mt to 3.64 Mt; the cap picks one CO₂ level.
- A procurement rule
- A rule on one buyer's purchases. Hourly matching costs $104.1M a year more than annual matching here.
- Claim and cause
- The three certificate or matching purchases may claim 0 t; with no rule, the load's share counts at average factors. The plants emit from −2.48 Mt to +1.14 Mt against the grid without the data center.
Go deeper · ModelerThe planning LP in algebra
For each hour \(t\) of the six days, with weight \(w_t\) (the days it stands for); each step lasts one hour, so \(x\) MW for an hour is \(x\) MWh:
\[\min\; \sum_{k} C_k X_k + \sum_t w_t \big( c_{cc}\,\mathit{cc}_t + c_{ct}\,\mathit{ct}_t + V\,u_t \big)\]
\[ \begin{aligned} \mathit{we}_t + \mathit{se}_t + \mathit{wn}_t + \mathit{sn}_t + \mathit{cc}_t + \mathit{ct}_t + d_t - c_t + u_t &= D_t && \text{(balance)}\\ \mathit{wn}_t \le f^w_t\, W, \quad \mathit{sn}_t \le f^s_t\, S, \quad c_t, d_t \le B, \quad e_t &\le 4B && \text{(sizes)}\\ \mathit{we}_t \le \bar w_t, \quad \mathit{se}_t \le \bar s_t, \quad \mathit{cc}_t \le 2{,}000, \quad \mathit{ct}_t &\le 2{,}300 && \text{(what exists)}\\ W, S \le 8{,}000, \quad B &\le 4{,}000 && \text{(build limits)}\\ e_t = e_{t-1} + 0.85\, c_t - d_t &&& \text{(battery; each day ends with the energy it began with)}\\ \sum_t w_t \big( r_{cc}\,\mathit{cc}_t + r_{ct}\,\mathit{ct}_t \big) &\le \varepsilon && \text{(the cap)} \end{aligned} \]
Every variable is zero or more.
| Symbol | Meaning | Unit |
|---|---|---|
| \(X_k\), \(C_k\) | new wind \(W\), solar \(S\), battery \(B\); each one’s yearly capital and upkeep | MW; $ per MW a year |
| \(\mathit{we}, \mathit{se}, \mathit{wn}, \mathit{sn}\) | existing and new wind and solar used | MW |
| \(\bar w_t\), \(\bar s_t\) | what the existing wind and solar could make that hour | MW |
| \(\mathit{cc}\), \(\mathit{ct}\) | output of the gas combined-cycle plants and of the gas peakers (combustion turbines) | MW |
| \(c_{cc}\), \(c_{ct}\); \(r_{cc}\), \(r_{ct}\) | their running cost and CO₂ per MWh | $/MWh; t/MWh |
| \(c_t, d_t, e_t\) | battery charge, discharge and energy | MW; MW; MWh |
| \(u_t\) | unserved demand | MW |
| \(V\) | the value of lost load, $10,000 | $/MWh |
| \(f^w_t\), \(f^s_t\), \(D_t\) | capacity factors per MW, and demand | share; MW |
| \(\varepsilon\) | the cap | t a year |
- A price \(\tau\) drops the cap and adds \(\tau \sum_t w_t (r_{cc}\,\mathit{cc}_t + r_{ct}\,\mathit{ct}_t)\) to the goal.
- Hourly matching adds the buyer’s own \(W_b, S_b, B_b\) and, every hour, \(\mathit{wb}_t + \mathit{sb}_t + d^b_t - c^b_t \ge 300\), with \(c^b_t \le \mathit{wb}_t + \mathit{sb}_t\).
- Annual matching keeps one row: \(\sum_t w_t (\mathit{wb}_t + \mathit{sb}_t) \ge 300 \times 8{,}760\).
Go deeper · ModelerCaps, prices and ties
- One dual, checked by finite steps. The test loosens and tightens each cap by 1,000 t; the cost changes at the dual’s rate both ways. That is a sensitivity check, not a proof.
- A positive dual means the cap binds in every best plan. This is complementary slackness: a rule with a nonzero dual holds exactly. So the cap’s CO₂ is the same for all of them, though their schedules may differ.
- A price ties a stretch. Where the frontier is straight, a price equal to the magnitude of its downward slope makes every plan on that stretch score the same; the solver returns one of them.
- So report the range. The page solves twice more at each price, for the least and the most CO₂ among the best plans, and shows the range when they differ.
Go deeper · ModelerSix days against the full year
- Annual matching. On all 8,760 hours of 2025 the 844 MW of wind make 95% of the data center's load over the year: the six weighted days were windier than the year.
- Hourly matching. Run hour by hour through 2025, the portfolio leaves at least 290 MWh unmatched, 0.01% of the load's year, in hours of low wind and sun. Which hours carry it can differ between equally good schedules.
- So: a rule met on representative days is a representative-day result. Check it on the full year, as the notebook does.
Go deeperWhat this model leaves out
- Demand that answers to prices, and the other years' weather.
- Lines inside the grid: every plant serves every load.
- Retiring the gas plants, and their fixed costs.
- Uncertainty: the plan knows the six days in advance (M7 samples what could happen).
Check yourself
Myth or fact? “A carbon price equal to a cap's dual gives back exactly the cap's plan.”
- Myth
- Fact
Answer
Myth. At the 70% cap's dual ($22.81 per t, rounded; the tie needs its full precision), every plan from 3.15 Mt to 3.64 Mt scores the same. The cap's plan is one of them.
Myth or fact? “If a buyer's purchases let it claim zero emissions, the plants emit no more than without it.”
- Myth
- Fact
Answer
Myth. With certificates from existing wind the data center claims 0 t, and the plants emit +1.14 Mt a year more.
Myth or fact? “In an LP, the cost of cutting one more tonne never falls as the cap tightens.”
- Myth
- Fact
Answer
Fact. The LP's frontier is convex: here the dual rises from $21.98 at 90% to $130.61 at 30%.
A cap's dual is $22.81 per t. Loosening the cap by 1,000 t changes the best cost by about how much?
- It rises by about $22,810
- It falls by about $22,810
- It falls by $22.81
- Nothing: the dual is only a price
Answer
It falls by about $22,810: $22.81 × 1,000 t, the dual rounded to the cent, while the cap stays in the dual's range.
Numbers, sources and what’s next
Next
In the real world
Reductions are computed under average-emission-intensity accounting with perfect foresight; they are not estimates of the consequential (marginal) system response.
Case file CF6: how much shifting load to cleaner hours can cut emissions, and when.
A two-stage stochastic linear program sized electrolyzers, hydrogen tanks and fuel cells once in each of ERCOT's eight weather zones, with hydrogen pipelines between neighbouring zones, then scheduled the system hour by hour over a full year in nine scenarios: three completion rates of planned renewable capacity by three electricity-demand levels.
When gas-fired backup power became much more expensive, the model built fuel cells and turned stored green hydrogen back into electricity.
Industry then drew more hydrogen from gas reformers, and the round trip from power to hydrogen and back lost energy, so total emissions rose although gas-plant emissions fell.
Inputs reflect 2022–2023 conditions; post-2023 federal hydrogen credit rules are not modeled.
Case file CF3: where hydrogen should go when nobody knows how much wind will be built.
Try it yourself
Optional, about 30 minutes; needs Python on your computer (lesson B2).
- Write the synthetic regions with
ghg-framework synthetic my_inputs/, then runghg-framework run my_inputs/ --region Mixed --limit 0.08 --lam 5 --out-dir outputs/mixed. - Its goal counts each hour at its average intensity. Write down what you would change to count the change at the plants instead, as question 2 of A10 does.
ghg-load-regulation: public example repository · MIT licence · synthetic data.
Sources and further reading
- EIA. Assumptions to the Annual Energy Outlook 2026: Electricity Market Module, Table 3, April 2026.
- Cole, W., Ramasamy, V. and Turan, M. (2025). Cost Projections for Utility-Scale Battery Storage: 2025 Update. NREL/TP-6A40-93281, June 2025.
- NLR. 2025 Electricity Annual Technology Baseline: Financial Cases and Methods.
- EIA. Electric Power Annual, Table 8.2, 2025 data; Carbon Dioxide Emissions Coefficients; Hourly Electric Grid Monitor (EIA-930), 2025.
- Ricks, W., Xu, Q. and Jenkins, J. (2023). Minimizing emissions from grid-based hydrogen production in the United States. Environmental Research Letters 18(1), 014025.
- Gagnon, P. and Cole, W. (2022). Planning for the evolution of the electric grid with a long-run marginal emission rate. iScience 25(3), 103915.
- Li et al. (2025). Real-time greenhouse gas emission intensity informed demand-side load regulation for power grid decarbonization. Cell Reports Sustainability 2, 100367.
- Li et al. (2025). Hydrogen’s role in expanding renewable energy and reducing carbon emissions in Texas. International Journal of Hydrogen Energy 113, 77–89.
- ghg-load-regulation on GitHub, version 1.0.0 (30 October 2026).
- Earlier lessons: M1 (duals as marginal values), lesson 12 (the frontier), A10 (four emissions questions), A12 (the same LP under four instruments).
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.