Multi-period Transmission Expansion Planning (MP TEP and MP+MS TEP)¶
Requires pip install pyflow-acdc[OPF] plus Bonmin/Ipopt (see Installation).
This page covers multi-period TEP (MP TEP) and multi-period multi-scenario TEP (MP+MS TEP) over an investment horizon. Static TEP and single-snapshot MS TEP are in Transmission Expansion Planning (TEP and MS TEP). Grid fundamentals are in Usage Guide.
After grid setup (step 1) and planning CSVs (step 2), pick one driver for step 3:
multi_period_transmission_expansion() (MP TEP),
sequential_STEP(), or MP+MS via
multi_period_MS_TEP() / sequential_MS_STEP().
Then export results (step 4).
Workflow¶
1. Create the grid¶
Grid creation for multi-period TEP includes topology, generators, renewables, and marking which elements are expandable — in this order:
nodes and branches
add_gen()(withinstallation_cost)add_RenSource()(withbase_cost)expandable table (line rows, then one row per generator and renewable) →
expand_elements_from_pd()
For the bundled example:
import pyflow_acdc as pyf
grid, res = pyf.cases["case24_MP"]()
When building a custom case, follow the same order as in Transmission Expansion Planning (TEP and MS TEP) step 1 (or Usage Guide for topology and generators only).
2. Attach planning CSVs¶
Load CSVs in this order (same as case24_MP_TEP / case24_seq_STEP):
a) Investment and decommission series — add_inv_series()
first (load multipliers, planned installations/decommissions, import expansion):
import importlib.util
from pathlib import Path
case_path = (
Path(pyf.__file__).resolve().parent
/ "example_grids"
/ "TEP"
/ "case24_MP.py"
)
spec = importlib.util.spec_from_file_location("case24_MP", case_path)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
inv_csv = mod._resolve_example_path("case24_MP_TEP_inv_series_10.csv")
pyf.add_inv_series(grid, inv_csv)
Each column maps to investment_decisions on linked elements (Load,
planned_decomision, planned_installation, etc.). CSVs ship under
examples/Case24_MP/ in a repo checkout.
b) Generation-type mix limits — add_gen_mix_limits()
second:
mix_csv = mod._resolve_example_path("case24_MP_TEP_gen_mix_limits.csv")
pyf.add_gen_mix_limits(grid, mix_csv)
Each column is one period; row 0 is the generation type, row 1 the limit kind, rows 2+ the numeric bounds.
3. Run¶
Pick one of the following (same CSV setup from step 2 unless noted).
MP TEP — one-shot multi-period MINLP, all periods in one model:
import pyflow_acdc as pyf
from pyflow_tests.test_constants import (
CASE24_MP_GEN_MIX_LIMITS_URL,
CASE24_MP_INV_SERIES_URL,
)
build_only = not pyf.is_pyomo_solver_available("ipopt")
grid, res = pyf.cases["case24_MP"]()
pyf.add_inv_series(grid, CASE24_MP_INV_SERIES_URL)
pyf.add_gen_mix_limits(grid, CASE24_MP_GEN_MIX_LIMITS_URL)
model, model_results, timing_info, solver_stats = pyf.multi_period_transmission_expansion(
grid,
n_years=10,
Hy=8760,
discount_rate=0.02,
ObjRule={"Energy_cost": 1},
solver="ipopt",
tee=False,
obj_scaling=1e9,
build_only=build_only,
)
res.all()
For production solves use solver="bonmin" and
solver_options={"bonmin.algorithm": "B-Hyb", "bonmin.nlp_failure_behavior": "fathom"}.
ObjRule is {"Energy_cost": 1}; n_years is 10. Check
solver_stats["solution_found"] before exporting.
Sequential STEP — one static transmission_expansion()
per period (grid state carried forward):
run_results = pyf.sequential_STEP(
grid=grid,
inv_data=inv_csv,
mix_data=mix_csv,
n_years=mod.DEFAULT_N_YEARS,
Hy=8760,
discount_rate=0.02,
ObjRule=mod.DEFAULT_OBJ_RULE,
solver="bonmin",
tee=False,
obj_scaling=1e9,
solver_options={
"bonmin.nlp_failure_behavior": "fathom",
"bonmin.algorithm": "B-BB", # recommended for sequential STEP
},
)
MP+MS TEP — multi-period planning with clustered operating scenarios
(cases with time series). For the North Sea bundled case use
pyf.cases["NS_MTDC_2025"](years_data="23,24", expandable="mp") — CSVs and
precomputed clusters are under examples/North_Sea_grid_data/. Use
expandable="step" for single-period MS TEP without MP investment series (see
Transmission Expansion Planning (TEP and MS TEP)). Then call multi_period_MS_TEP() or
sequential_MS_STEP():
Cluster time series — run cluster_TS() (or
run_clustering_analysis_and_plot() for exploratory work), or
reload a saved result via precomputed_clusters_path in clustering_options
(see load_precomputed_clusters_to_grid()):
from pyflow_tests.test_constants import north_sea_ms_clustering_options
grid, _ = pyf.cases["NS_MTDC_2025"](years_data="23,24", expandable="mp")
clustering_options = north_sea_ms_clustering_options()
# points to examples/North_Sea_grid_data/clusters_kmeans_medoids_k4.json
Pass clustering_options to multi_period_MS_TEP() or
sequential_MS_STEP`(). For price-zone OPEX use
ObjRule={"PZ_cost_of_generation": 1} (see Optimal Power Flow Module). See
Time-Series Clustering.
import pyflow_acdc as pyf
from pyflow_tests.test_constants import north_sea_ms_clustering_options
build_only = True
grid, res = pyf.cases["NS_MTDC_2025"](years_data="23,24", expandable="mp", online=True)
mp_load_series = list(grid.Price_Zones[0].investment_decisions["Load"])
model, model_results, timing_info, solver_stats, ts_results = pyf.multi_period_MS_TEP(
grid,
inv_periods=mp_load_series,
ObjRule={"PZ_cost_of_generation": 1},
clustering_options=north_sea_ms_clustering_options(),
solver="ipopt",
tee=True,
build_only=build_only,
)
4. Results¶
After a feasible solve, the grid is updated in place — line multiplicities, generator counts, and decommission flags reflect the expansion plan.
Solver return values (MP TEP):
model,model_results,timing_info,solver_statsgrid.OPF_obj— discounted operational cost componentselement
np_gen/np_line— chosen build-out
Export and post-processing (typical after MP TEP):
if not solver_stats.get("solution_found", False):
raise RuntimeError(solver_stats.get("solver_message", "no solution"))
pyf.export_and_save_inv_period_svgs(grid, folder_name="case24_MP_TEP")
res.pyomo_model_results(model, solver_stats=solver_stats, model_results=model_results)
res.all(export_type="excel", file_name="case24_MP_TEP", export_location="case24_MP_TEP")
pyf.run_opf_for_all_investment_periods(
grid,
ObjRule=mod.DEFAULT_OBJ_RULE,
obj_scaling=1e9,
export_excel=True,
export_location="case24_MP_TEP",
)
run_opf_for_all_investment_periods re-solves OPF for each investment period
on the expanded grid and exports per-period operating results.
For time-series OPF on one built-out period (clustered or hourly), use
run_ts_opf_for_investment_period() — it applies the period
state, calls ts_acdc_opf(), and writes Excel via
results_ts_opf().
After MP+MS TEP, export scenario tables with
export_TEP_multiScenario_results_to_excel() (deprecated alias
export_TEP_TS_results_to_excel):
pyf.export_TEP_multiScenario_results_to_excel(grid, "NS_MP_MS_results.xlsx")
Sequential STEP returns a run_results dict keyed by period. Set
export_steps=True and export_dir=... for CSV summaries per step.
Example cases¶
pyf.cases['case24_MP']()pyf.cases['NS_MTDC_2025'](years_data="23,24", expandable="mp")(MP+MS; data inexamples/North_Sea_grid_data/)
See Usage Guide for the full catalogue.
See Multi period Transmission Expansion Planning Module and Sequential STEP for MP+MS signatures.