Core Modules#
Documentation for core PyPSA-GB modules.
solve_network#
Network optimization using PyPSA’s LOPF.
Main Functions#
solve_network()#
Solve the optimal power flow for a network.
def solve_network(
network: pypsa.Network,
solver_name: str = "gurobi",
solver_options: dict = None,
solve_mode: str = "LP"
) -> pypsa.Network:
"""
Solve network optimal dispatch.
Parameters
----------
network : pypsa.Network
Complete network with generators, loads, etc.
solver_name : str
Solver to use: 'gurobi', 'highs', 'cplex'
solver_options : dict
Solver-specific options
solve_mode : str
'LP' for linear, 'MILP' for integer
Returns
-------
pypsa.Network
Solved network with dispatch results
"""
Usage Example#
import pypsa
from scripts.solve_network import solve_network
# Load prepared network
n = pypsa.Network("resources/network/HT35_finalized.nc")
# Solve
n = solve_network(
n,
solver_name="gurobi",
solver_options={"method": 2, "threads": 4}
)
# Check result
print(f"Objective: £{n.objective/1e9:.2f}B")
print(f"Status: {n.optimization_status}")
build_network#
Base network construction from topology files.
Note
For the ETYS network model, the build logic is split across the scripts/network_build/ package:
process_ETYS_data.py— Raw Excel parsing (stage 1)ETYS_network.py— Network assembly (stage 2)ETYS_upgrades.py— Network upgrade applicationetys_file_registry.py— ETYS file/sheet name mapping and constantsbuild_network.py— Reduced/Zonal builders (ETYS delegates toETYS_network.py)
ETYS Network Assembly (scripts/network_build/ETYS_network.py)#
create_network()#
def create_network(
df: pd.DataFrame,
df_buses_with_locs: pd.DataFrame,
logger: logging.Logger = None
) -> pypsa.Network:
"""
Build a PyPSA network from preprocessed ETYS component and bus DataFrames.
Parameters
----------
df : pd.DataFrame
Components (circuits, transformers, HVDC) from process_ETYS_data
df_buses_with_locs : pd.DataFrame
Bus data with resolved coordinates
logger : logging.Logger, optional
Logger for output
Returns
-------
pypsa.Network
Assembled network with buses, lines, transformers, links
"""
validate_network_topology()#
def validate_network_topology(
network: pypsa.Network,
logger: logging.Logger = None
) -> None:
"""
Validate network connectivity, parameter ranges, and coordinate coverage.
"""
ensure_buses_on_land()#
def ensure_buses_on_land(
network: pypsa.Network,
land_boundary: gpd.GeoDataFrame,
logger: logging.Logger = None
) -> pypsa.Network:
"""
Validate bus coordinates against GB land boundaries and move
any that fall in the sea to the nearest land point.
"""
ETYS Upgrades (scripts/network_build/ETYS_upgrades.py)#
apply_etys_network_upgrades()#
def apply_etys_network_upgrades(
network: pypsa.Network,
modelled_year: int,
etys_file: str = None,
logger: logging.Logger = None
) -> pypsa.Network:
"""
Apply all ETYS network upgrades through a target year.
Applies circuit additions/removals/modifications, transformer
additions/removals/modifications, and HVDC additions.
Parameters
----------
network : pypsa.Network
Base network to modify
modelled_year : int
Apply upgrades through this year
etys_file : str, optional
Path to ETYS Appendix B Excel file
logger : logging.Logger, optional
Logger for output
Returns
-------
pypsa.Network
Modified network with upgrades applied
"""
add_missing_buses_from_upgrades()#
def add_missing_buses_from_upgrades(
network: pypsa.Network,
upgrades_data: dict,
logger: logging.Logger = None
) -> pypsa.Network:
"""
Add buses referenced by upgrades but missing from the base network.
Uses multi-pass strategy (substation coords → same-site → already-added → connected bus offset).
"""
remove_orphan_buses()#
def remove_orphan_buses(
network: pypsa.Network,
logger: logging.Logger = None
) -> int:
"""
Remove buses with no connected lines, transformers, or links.
Returns the number of buses removed.
"""
ETYS Data Processing (scripts/network_build/process_ETYS_data.py)#
sort_raw_ETYS_data()#
def sort_raw_ETYS_data(
etys_file: str,
gb_network_file: str,
logger: logging.Logger = None
) -> tuple:
"""
Parse raw ETYS Appendix B Excel into standardized DataFrames.
Returns
-------
tuple of (pd.DataFrame, pd.DataFrame)
(components_df, buses_df)
"""
ETYS File Registry (scripts/network_build/etys_file_registry.py)#
Central registry mapping ETYS publication years to filenames and sheet names.
Key constants:
Constant |
Description |
|---|---|
|
Dict mapping years (2022, 2023, 2024) to filenames |
|
Dict of base network sheet names (circuits, transformers, HVDC) |
|
Dict of upgrade sheet names by operator |
|
Voltage code → kV mapping |
|
Default per-unit values for lines, transformers, cables |
|
Default MVA ratings by component type |
scenario_detection#
Automatic routing based on modelled year.
Main Functions#
is_historical_scenario()#
def is_historical_scenario(scenario: dict) -> bool:
"""
Check if scenario models a historical year (≤2024).
Parameters
----------
scenario : dict
Scenario configuration dictionary
Returns
-------
bool
True if modelled_year ≤ 2024
"""
auto_configure_scenario()#
def auto_configure_scenario(scenario: dict) -> dict:
"""
Add automatic configuration based on scenario type.
Adds metadata for data source routing:
- is_historical: bool
- thermal_source: 'DUKES' or 'FES'
- renewable_source: 'REPD' or 'FES'
Parameters
----------
scenario : dict
Scenario configuration
Returns
-------
dict
Enhanced scenario with routing metadata
"""
Usage Example#
from scripts.scenario_detection import is_historical_scenario, auto_configure_scenario
scenario = {
'modelled_year': 2035,
'FES_scenario': 'Holistic Transition'
}
# Check type
if is_historical_scenario(scenario):
print("Using DUKES/REPD data")
else:
print("Using FES projections")
# Add routing metadata
scenario = auto_configure_scenario(scenario)
print(f"Thermal source: {scenario['thermal_source']}")
carrier_definitions#
Technology/carrier definitions and attributes.
Main Functions#
get_carrier_definitions()#
def get_carrier_definitions() -> pd.DataFrame:
"""
Get carrier definitions with colors and emissions.
Returns
-------
pd.DataFrame
Carrier definitions with columns:
- nice_name: Display name
- color: Hex color for plotting
- co2_emissions: tCO2/MWh
- renewable: bool
"""
Defined Carriers#
Carrier |
Nice Name |
Color |
Renewable |
|---|---|---|---|
|
Onshore Wind |
|
Yes |
|
Offshore Wind |
|
Yes |
|
Solar PV |
|
Yes |
|
Nuclear |
|
No |
|
CCGT |
|
No |
|
Battery |
|
No |
|
Pumped Hydro |
|
No |
Usage Example#
from scripts.carrier_definitions import get_carrier_definitions
carriers = get_carrier_definitions()
# Get color for plotting
wind_color = carriers.loc['wind_offshore', 'color']
# Get all renewable carriers
renewable_carriers = carriers[carriers.renewable].index.tolist()
logging_config#
Centralized logging configuration.
Main Functions#
setup_logging()#
def setup_logging(
log_path: str = None,
log_level: str = "INFO",
log_to_console: bool = True
) -> logging.Logger:
"""
Set up logging for a script.
Parameters
----------
log_path : str
Path to log file (from snakemake.log[0])
log_level : str
Level: DEBUG, INFO, WARNING, ERROR
log_to_console : bool
Also print to console
Returns
-------
logging.Logger
Configured logger
"""
get_log_path()#
def get_log_path(fallback: str = None) -> str:
"""
Get log path from Snakemake context.
Parameters
----------
fallback : str
Fallback name if not in Snakemake
Returns
-------
str
Log file path
"""
Usage Pattern#
from scripts.logging_config import setup_logging
# In Snakemake script
if __name__ == "__main__":
snk = globals().get('snakemake')
log_path = snk.log[0] if snk and snk.log else "my_script"
logger = setup_logging(log_path)
logger.info("Starting processing...")
time_utils#
Time series manipulation utilities.
Main Functions#
align_time_series()#
def align_time_series(
series: pd.Series,
target_index: pd.DatetimeIndex,
method: str = 'ffill'
) -> pd.Series:
"""
Align a time series to a target index.
Parameters
----------
series : pd.Series
Input time series
target_index : pd.DatetimeIndex
Target time index
method : str
Interpolation method
Returns
-------
pd.Series
Aligned time series
"""
resample_to_hourly()#
def resample_to_hourly(
df: pd.DataFrame,
method: str = 'mean'
) -> pd.DataFrame:
"""
Resample half-hourly data to hourly.
"""
validation_utils#
Data validation functions.
Main Functions#
validate_network_connectivity()#
def validate_network_connectivity(
network: pypsa.Network
) -> tuple[bool, list]:
"""
Check that all buses are connected.
Returns
-------
tuple[bool, list]
(is_connected, isolated_buses)
"""
validate_generator_data()#
def validate_generator_data(
generators: pd.DataFrame
) -> pd.DataFrame:
"""
Validate and clean generator data.
Checks:
- Valid coordinates
- Positive capacity
- Known carrier types
Returns
-------
pd.DataFrame
Validated generators (invalid rows removed)
"""