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:

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 seriesadd_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 limitsadd_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_stats

  • grid.OPF_obj — discounted operational cost components

  • element 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 in examples/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.