Template: Energy Grid Planning
Multi-reasoner template: demand forecasting, grid vulnerability analysis, compliance rules, and multi-objective optimization for AI data center interconnection planning on the ERCOT (Texas) grid.
Browse files
Directoryenergy_grid_planning/
What this template is for
ERCOT’s interconnection planning team faces a queue of 10 AI data center requests from hyperscalers (Microsoft, Google, Amazon, Meta, xAI, Oracle, CoreWeave, Lambda Labs, Crusoe Energy, Apple) competing for scarce grid capacity across the Texas grid. They must decide which requests to approve, what substation upgrades to invest in, and how to keep the grid reliable — all under budget constraints and renewable energy mandates.
This template uses RelationalAI’s predictive reasoning, graph analysis, rules-based classification, and prescriptive reasoning (multi-objective optimization) in a chained multi-reasoner workflow:
- Predictive forecasts substation load growth from historical demand data, identifying which substations will exceed capacity as data center demand accelerates. Dallas-Fort Worth is the only substation predicted to breach capacity — at 24 months with 54.6% growth.
- Graph analysis maps the ERCOT grid topology (12 substations + 18 transmission lines), detects community structure (Louvain) across 3 regions (North Texas, West Texas, Gulf Coast), and ranks substations by betweenness centrality to identify structurally critical bottlenecks. 7 of 10 DC requests target structurally critical substations.
- Rules check each data center request against interconnection compliance: capacity limits (using predicted load from Stage 1), low-carbon energy mandates, and structural risk (using centrality-based criticality from Stage 2). Only 2 of 10 requests pass (Crusoe and Oracle); 8 are flagged.
- Prescriptive optimization jointly decides which data centers to approve and which substation upgrades to build across multiple investment levels (
600M). An InvestmentLevel Scenario Concept traces the Pareto frontier in a single solve, with results queryable in the ontology. The knee point at 105M/yr.
Each stage enriches the shared ontology, and downstream stages consume those enrichments — this is the accretive ontology enrichment pattern. No Python dicts or DataFrames carry state between stages; the ontology is the single source of truth:
- Stage 1 writes
Substation.predicted_load— consumed by Stage 3’s capacity rule AND Stage 4’s capacity constraint. Both downstream reasoners see the same forecasted headroom. - Stage 2 writes
Substation.betweenness,Substation.grid_community,Substation.is_structurally_critical— consumed by Stage 3’s structural risk rule (Rule 2). - Stage 3 writes
DataCenterRequest.fails_capacity,.fails_structural,.fails_low_carbon,.is_compliant— queryable compliance flags that document why each request was flagged. - Stage 4 writes
DataCenterRequest.x_approveandSubstationUpgrade.x_upgradeperInvestmentLevel— queried from the ontology viamodel.select(), not parsed from solver output.
The ontology is accretive: each stage enriches it with derived properties, and downstream stages consume those enrichments as first-class attributes. This means changing Stage 1’s demand forecast automatically propagates through the rules engine and optimizer without any code changes.
The optimization is multi-scenario and multi-objective: InvestmentLevel is a Scenario Concept — 5 budget entities (predicted_load from Stage 1 (not raw historical load), the capacity constraints reflect forecasted growth — the same signal the rules engine uses.
Why this problem matters
ERCOT — the Electric Reliability Council of Texas — operates an isolated grid that is not interconnected with the Eastern or Western Interconnections. This isolation means Texas cannot import power from neighboring grids during demand spikes, making capacity planning uniquely consequential. Winter Storm Uri (2021) demonstrated the vulnerability: grid failures cascaded without external relief, causing widespread blackouts.
Texas is now the fastest-growing market for AI data center development. Hyperscalers are requesting multi-hundred-megawatt interconnections at substations designed for decades of steady organic growth. The ERCOT grid must absorb 2,930 MW of new data center load — equivalent to roughly 3 nuclear reactors — while maintaining reliability for 30 million existing customers.
This is not a single-reasoner problem. Approving a data center at a structurally critical substation like Dallas-Fort Worth (graph) without sufficient capacity headroom (predictive) violates reliability rules (rules) and may not be economically justified at lower budget levels (prescriptive). The value of the multi-reasoner approach is that each stage’s output constrains the next — predictions inform rules, rules inform optimization, and the optimizer respects all upstream signals.
Reasoner overview: inputs, outputs, and role
| Stage | Reasoner | Reads from ontology | Writes to ontology | Role |
|---|---|---|---|---|
| 1. Predict | Predictive | DemandForecast table (historical load + DC announcements) | Substation.predicted_load (derived Property) | Forecast which substations hit capacity limits. DFW breaches at 24 months (54.6% growth); Houston, San Antonio, Austin grow 32-44% but remain within capacity. |
| 2. Graph | Graph (WCC, Louvain, centrality) | Substation nodes, TransmissionLine edges | Substation.betweenness, .grid_community (Properties), Substation.is_structurally_critical (Relationship) | Map ERCOT grid topology into 3 regions (North Texas, West Texas, Gulf Coast). DFW and Houston are the top structural bottlenecks. 7 of 10 DC requests target critical substations. |
| 3. Rules | Rules (declarative) | predicted_load (Stage 1), is_structurally_critical (Stage 2), DC request properties | DataCenterRequest.fails_capacity, .fails_structural, .fails_low_carbon, .is_compliant (Relationships) | Check each request against interconnection compliance. 2 compliant (Crusoe, Oracle), 8 flagged. Every flag is a derived Relationship consuming upstream enrichments. |
| 4. Prescriptive | Prescriptive (MIP, Scenario Concept) | predicted_load (Stage 1), InvestmentLevel budget scenarios, upgrade costs/capacities | DataCenterRequest.x_approve, SubstationUpgrade.x_upgrade per InvestmentLevel (Properties) | Jointly optimize approvals + upgrades across budget levels. One solve produces the full Pareto frontier. Knee at model.select(). |
Key design patterns demonstrated:
- Accretive ontology enrichment — each stage writes derived properties that downstream stages consume as first-class ontology attributes. Stage 1’s
predicted_loadflows into both Stage 3 rules and Stage 4 optimization constraints, ensuring consistent capacity signals across the pipeline. - Multi-scenario / multi-objective via Scenario Concept —
InvestmentLevelis a Scenario Concept: 5 budget entities (600M) that parameterize the optimization. One MIP solve produces the entire Pareto frontier simultaneously (not a re-solve loop). Decision variables x_approveandx_upgradeare indexed per InvestmentLevel, and results are queryable ontology properties — not parsed from solver output. - Ontology as shared state — each stage writes derived properties/relationships that downstream stages read; no Python dicts or DataFrames carry state between stages
- Separate graph model — Graph and Prescriptive reasoners use independent
Modelinstances to avoid SDK recursion, with results transferred viamodel.data()+filter_by() - Marginal analysis from ontology queries — the per-level DC approvals, upgrade selections, and net value are all queried from the ontology via
model.select(...).where(x_approve > 0.5)per InvestmentLevel, enabling marginal return analysis across the frontier
Who this is for
- Utility planners and grid operators managing interconnection queues (especially ERCOT)
- Energy infrastructure investors evaluating capacity expansion portfolios
- Operations researchers exploring multi-reasoner pipelines in RelationalAI
- Developers learning how to chain predictive, graph, rules, and optimization in a single model
What you’ll build
- A substation load forecasting pipeline (predictive or pre-baked fallback)
- Grid topology analysis with WCC, Louvain community detection, and multi-metric centrality ranking
- Three declarative compliance rules consuming upstream reasoner outputs
- Binary decision variables for DC approval and substation upgrades, indexed by InvestmentLevel (Scenario Concept)
- Substation capacity, budget, and low-carbon mandate constraints scoped per investment level
- A revenue-maximizing objective producing the Pareto frontier across budget scenarios
- Ontology-native result extraction — no variable parsing, all queryable via
model.select()
What’s included
energy_grid_planning.py— Main script with four chained reasoning stagesdata/substations.csv— 12 Texas substations with capacity, load, and coordinatesdata/generators.csv— 15 generators (2 nuclear: STP + Comanche Peak, plus gas, coal, wind, solar, battery)data/transmission_lines.csv— 18 transmission lines forming a connected ERCOT griddata/load_zones.csv— 5 ERCOT load zonesdata/demand_periods.csv— 24-hour demand profiles per zonedata/renewable_profiles.csv— Solar/wind capacity factors by hourdata/maintenance_windows.csv— Planned generator/line outagesdata/customers.csv— 10 end-use customers with flexibility profilesdata/data_center_requests.csv— 10 hyperscaler interconnection requests (2,930 MW total)data/substation_upgrades.csv— 10 possible substation capacity upgradesdata/demand_forecasts.csv— Pre-computed substation load forecasts (6/12/18/24 month horizons)data/load_history.csv— 4 years of monthly substation load readingsdata/dc_announcements.csv— Hyperscaler announcement eventsdata/train_forecasts.csv,data/val_forecasts.csv,data/test_forecasts.csv— GNN training splits (used by optional GNN training workflow, not by the main script)
Prerequisites
Access
- A Snowflake account that has the RAI Native App installed.
- A Snowflake user with permissions to access the RAI Native App.
Tools
- Python >= 3.10
Quickstart
-
Download the template and extract it:
Terminal window curl -O https://private.relational.ai/templates/zips/v1/energy_grid_planning.zipunzip energy_grid_planning.zipcd energy_grid_planning -
Create a virtual environment and activate it:
Terminal window python -m venv .venvsource .venv/bin/activatepython -m pip install --upgrade pip -
Install dependencies:
Terminal window python -m pip install . -
Configure your RAI connection:
Terminal window rai init -
Run the template:
Terminal window python energy_grid_planning.py -
Expected output:
STAGE 1: PREDICT -- Substation Load ForecastingGNN model not available, falling back to DEMAND_FORECASTS tableSubstations at risk of capacity breach:Dallas-Fort Worth: 1100 MW current → 1700 MW predicted vs 1600 MW capacity (breach at 24mo, 54.6% growth)All substation forecasts:Houston Ship Channel pred=1797.1 MW growth=43.8% breach=safeDallas-Fort Worth pred=1700.2 MW growth=54.6% breach=24moSan Antonio Metro pred=1069.1 MW growth=37.1% breach=safeAustin Energy pred=818.8 MW growth=32.1% breach=safe...STAGE 2: GRAPH -- Grid Topology & Structural VulnerabilityGrid connectivity: 12 substations, 1 component(s) -- CONNECTEDGrid community structure (Louvain): 3 region(s)Region 1 (North Texas): Dallas-Fort Worth, Austin Energy, Waco GatewayRegion 2 (West Texas): Midland-Permian, Lubbock, El Paso, Amarillo, AbileneRegion 3 (Gulf Coast): Houston, San Antonio, Corpus Christi, BrownsvilleTop 3 structurally critical substations:#1: Dallas-Fort Worth (betw=31.67) [CRITICAL]#2: Houston Ship Channel (betw=15.83) [CRITICAL]#3: San Antonio Metro (betw=4.33) [CRITICAL]KEY INSIGHT: 7 of 10 DC requests target structurally critical substationsSTAGE 3: RULES -- Interconnection Queue ComplianceDC Request Hyper Q# MW Cap LowC Crit OK?Microsoft Horizon Campus Microsoft 1 350 FAIL PASS FAIL NMeta Bayou DC Meta 2 300 FAIL PASS FAIL NGoogle Metroplex DC Google 3 400 FAIL PASS FAIL NxAI Colossus Texas xAI 4 500 FAIL PASS FAIL NLambda Labs DFW Lambda Labs 5 200 FAIL PASS FAIL NAmazon SA Cloud Amazon 6 280 FAIL PASS FAIL NApple iCloud Texas Apple 7 250 FAIL PASS FAIL NCoreWeave Austin GPU CoreWeave 8 320 FAIL PASS PASS NCrusoe Permian DC Crusoe Energy 9 180 PASS PASS PASS YOracle Coastal DC Oracle 10 150 PASS PASS PASS YSummary: 2 compliant, 8 flaggedSTAGE 4: OPTIMIZE -- Joint Interconnection + UpgradeStatus: OPTIMAL | Objective: 1,579,200,000Pareto frontier:# Level DCs DC MW Revenue $/yr Upg $M Net Value1 $200M 4 1,000 174,350,000 190.0 164,850,0002 $300M 5 1,500 279,350,000 300.0 264,350,0003 $400M 6 1,800 328,850,000 385.0 309,600,0004 $500M 7 2,080 376,450,000 430.0 354,950,0005 $600M 8 2,330 420,200,000 505.0 394,950,000KNEE POINT: $300M -- 5 DCs, 1,500 MW, $264M net valuexAI Colossus ($105M/yr) unlocks at $300M -- highest-revenue single requestPer-level (abbreviated):$200M: Microsoft, CoreWeave, Crusoe, Oracle (1,000 MW)$300M: + xAI (500 MW)$400M: + Meta (300 MW)$500M: + Amazon (280 MW)$600M: + Apple (250 MW)Never approved: Google (400 MW), Lambda Labs (200 MW) -- DFW is fullMarginal: $200->$300M = $995K/$M | $300->$400M = $453K/$M | $400->$500M = $454K/$M | $500->$600M = $400K/$MPIPELINE COMPLETE: 4 stages executed on shared Energy Grid ontology
Template structure
energy_grid_planning/ energy_grid_planning.py # Main script (4 chained reasoning stages) data/ substations.csv # 12 Texas grid nodes generators.csv # 15 generators (nuclear, gas, coal, wind, solar, battery) transmission_lines.csv # 18 ERCOT grid edges load_zones.csv # 5 ERCOT load zones demand_periods.csv # 24-hour demand profiles renewable_profiles.csv # Solar/wind capacity factors maintenance_windows.csv # Planned outages customers.csv # End-use customers data_center_requests.csv # 10 hyperscaler interconnection requests (2,930 MW) substation_upgrades.csv # 10 upgrade options demand_forecasts.csv # Pre-computed load forecasts load_history.csv # Historical load readings dc_announcements.csv # Hyperscaler announcements train_forecasts.csv # GNN training split (optional, not used by main script) val_forecasts.csv # GNN validation split (optional, not used by main script) test_forecasts.csv # GNN test split (optional, not used by main script) README.md # This file pyproject.toml # DependenciesHow it works
Stage 1: Predict — Substation Load Forecasting
Derives Substation.predicted_load as an ontology property from the DemandForecast table using aggs.max().per(Substation). Substations near announced data center projects show 32-55% growth depending on location and announced capacity. Dallas-Fort Worth is the only substation predicted to breach capacity (1,700 MW predicted vs 1,600 MW capacity at 24 months, 54.6% growth). Houston Ship Channel shows the highest absolute load (1,797 MW) but remains within its larger capacity. This predicted load feeds the capacity constraint in Stage 4.
The predicted_load derived property aggregates the max forecasted load per substation:
Substation.predicted_load = model.Property(f"{Substation} has {Float:predicted_load}")model.define( Substation.predicted_load( aggs.max(DemandForecast.predicted_load_mw) .where(DemandForecast.substation(Substation)) .per(Substation) ))Stage 2: Graph — Grid Topology & Structural Vulnerability
Uses a separate graph_model (to avoid SDK 1.0.12 recursion with prescriptive) to build a substation-transmission line graph across the ERCOT grid. Computes:
- Weakly connected components (grid connectivity — confirms all 12 substations are reachable)
- Louvain community detection (3 ERCOT regions: North Texas, West Texas, Gulf Coast)
- Betweenness centrality (DFW=31.67, Houston=15.83, San Antonio=4.33 are the top structural bottlenecks)
- 7 of 10 DC requests target structurally critical substations — a key input to the rules engine
Results are loaded back into the main model via model.data() + filter_by().
The graph is constructed from substations and active transmission lines, then analyzed with Louvain and betweenness centrality:
grid_graph = Graph( graph_model, directed=False, weighted=False, node_concept=GSub, aggregator="sum")
gline = GLine.ref()gs1, gs2 = GSub.ref(), GSub.ref()graph_model.where( gline.from_substation(gs1), gline.to_substation(gs2), gline.is_active == True,).define(grid_graph.Edge.new(src=gs1, dst=gs2))
community = grid_graph.louvain()betweenness = grid_graph.betweenness_centrality()Stage 3: Rules — Interconnection Queue Compliance
Three declarative rules (RAI Relationships) consume upstream outputs:
- Capacity check:
requested_mw + predicted_load > max_capacity_mw(uses Stage 1). Most requests fail because the existing grid lacks headroom for new AI load without upgrades. - Structural risk: DC request targets a structurally critical substation (uses Stage 2 centrality). 7 of 10 requests fail this check.
- Low-carbon mandate: substation’s low-carbon generation fraction < DC’s requirement (nuclear + renewable). All requests pass — ERCOT’s nuclear plants (STP, Comanche Peak) and extensive wind/solar fleet provide sufficient low-carbon generation.
- Composite:
is_compliant= passes all checks. Only Crusoe Permian DC and Oracle Coastal DC are fully compliant.
The capacity rule demonstrates the accretive pattern, consuming predicted_load from Stage 1 with a fallback to current_load_mw:
DataCenterRequest.fails_capacity = model.Relationship(f"{DataCenterRequest} fails capacity check")SubRef_rule = Substation.ref()effective_load_rule = SubRef_rule.predicted_load | SubRef_rule.current_load_mwmodel.where( DataCenterRequest.substation(SubRef_rule), DataCenterRequest.requested_mw + effective_load_rule > SubRef_rule.max_capacity_mw,).define(DataCenterRequest.fails_capacity())Stage 4: Prescriptive — Multi-Objective Optimization
Uses the InvestmentLevel Scenario Concept pattern:
- 5 budget levels (
600M) as Scenario entities - Binary variables
x_approveandx_upgradeindexed by InvestmentLevel - Substation capacity constraints use
predicted_loadfrom Stage 1 (not raw historical load), ensuring the optimizer sees the same forecasted headroom as the rules engine - Budget constraints scoped
.per(InvestmentLevel) - Revenue values reflect annual interconnection capacity revenue (
210K per MW/yr), not energy revenue - One solve produces the entire Pareto frontier — the knee point at
264M net value) shows the highest marginal return, unlocking xAI Colossus ($105M/yr) as the single highest-revenue request - At
100M budget increment adds 1 more DC with diminishing marginal returns ( M at the knee vs M at $600M) - Google Metroplex DC (400 MW) and Lambda Labs DFW (200 MW) are never approved at any budget level — DFW substation capacity is fully consumed
- Per-level details (which DCs approved, which upgrades selected, upgrade MW) are all queried from the ontology via
model.select(), not parsed from solver output - All decision variables (
x_approve,x_upgrade) are ontology properties indexed by InvestmentLevel, making the full Pareto frontier queryable without any output parsing
The Problem setup defines the InvestmentLevel Scenario Concept with binary decision variables, and the capacity constraint uses predicted_load from Stage 1:
p = Problem(model, Float)
p.solve_for(DataCenterRequest.x_approve(InvestmentLevel, x_a), type="bin", name=["approve", InvestmentLevel.name, DataCenterRequest.id])p.solve_for(SubstationUpgrade.x_upgrade(InvestmentLevel, x_u), type="bin", name=["upgrade", InvestmentLevel.name, SubstationUpgrade.id])
# C1: Substation capacity per investment level# Uses predicted_load from Stage 1 (with current_load fallback) — the accretive chain.x_a_c = Float.ref("xa_c")x_u_c = Float.ref("xu_c")effective_load = Substation.predicted_load | Substation.current_load_mw
p.satisfy(model.where( DataCenterRequest.x_approve(InvestmentLevel, x_a_c), SubstationUpgrade.x_upgrade(InvestmentLevel, x_u_c), DataCenterRequest.substation(Substation), SubstationUpgrade.substation(Substation),).require( Substation.max_capacity_mw - effective_load + sum(x_u_c * UpgRef.capacity_increase_mw).where( UpgRef.substation == Substation).per(Substation, InvestmentLevel) >= sum(x_a_c * DCRef.requested_mw).where( DCRef.substation == Substation).per(Substation, InvestmentLevel)))Customize this template
- Add investment levels: Add rows to the InvestmentLevel DataFrame for finer Pareto resolution
- Add demand scenarios: Create a DemandScenario concept as a second Scenario axis
- Add generation dispatch: Extend with GeneratorPeriod cross-product and dispatch variables (increases problem size significantly)
- Use real GNN predictions: Install the predictive reasoner and train on
load_history.csvviaenergy_demand_forecast.py - Adjust low-carbon target: Modify the
fails_low_carbonrule (e.g., exclude nuclear to use renewable-only) - Add your grid data: Replace CSVs with your substation/transmission line topology
Troubleshooting
Stage 2 graph queries work but Stage 4 fails with UnsupportedRecursionError
- The Graph reasoner’s recursive definitions conflict with prescriptive
variable_values()in SDK 1.0.12. - The template uses a separate
graph_modelfor Stage 2 to avoid this.
Most DCs fail the capacity check
- This is expected — the existing ERCOT grid doesn’t have headroom for 2,930 MW of new AI load without upgrades.
- Only Crusoe (Midland-Permian) and Oracle (Corpus Christi) pass all checks because they target substations with sufficient spare capacity and low structural criticality.
- The optimizer selects which upgrades to build to unlock the remaining requests.
Knee point shifts depending on predicted load
- The optimizer uses
predicted_loadfrom Stage 1 as the capacity baseline. - Higher predicted load means less headroom, so fewer DCs fit at each budget level.
- The knee point at $300M reflects forecasted growth (up to 54.6% for DFW), not historical load.
- If you change the demand forecasts, the Pareto frontier and knee point will shift accordingly.
Google and Lambda Labs are never approved at any budget level
- Both target the Dallas-Fort Worth substation, which is already the only substation predicted to breach capacity (54.6% growth).
- Even with upgrades, DFW capacity is fully consumed by higher-revenue requests (xAI Colossus at 500 MW, $105M/yr).
- The optimizer correctly prioritizes revenue-maximizing allocations at the constrained substation.