From bb8471e29402dab583b5c9eb2ec2f2851cbc3b54 Mon Sep 17 00:00:00 2001 From: Thomas Gilon Date: Thu, 18 Jun 2026 13:52:01 +0200 Subject: [PATCH 01/12] doc: improve Open-TYNDP doc formatting --- scripts/_helpers.py | 67 ++++++- scripts/add_brownfield.py | 16 +- scripts/build_snapshot_weightings.py | 2 +- scripts/build_tyndp_network.py | 42 +++-- scripts/cba/_helpers.py | 15 +- scripts/cba/average_indicators.py | 19 +- scripts/cba/build_msv_snapshot_weightings.py | 14 +- scripts/cba/clean_projects.py | 30 +-- scripts/cba/collect_indicators.py | 17 +- scripts/cba/fix_reference_sb_to_cba.py | 2 +- scripts/cba/make_indicators.py | 36 ++-- scripts/cba/prepare_rolling_horizon.py | 17 +- scripts/cba/simplify_sb_network.py | 4 +- scripts/cba/solve_cba_msv_extraction.py | 6 +- scripts/cba/solve_cba_network.py | 63 +++---- scripts/cba/summarize_indicators.py | 17 +- scripts/prepare_sector_network.py | 176 +++++++++--------- scripts/sb/build_pemmdb_data.py | 14 +- scripts/sb/build_renewable_profiles_pecd.py | 4 +- scripts/sb/build_statistics.py | 10 +- scripts/sb/build_tyndp_gas_demand.py | 4 +- scripts/sb/build_tyndp_h2_demand.py | 10 +- scripts/sb/build_tyndp_h2_network.py | 54 ++++-- scripts/sb/build_tyndp_hydro_profile.py | 2 +- scripts/sb/build_tyndp_offshore_hubs.py | 6 +- scripts/sb/build_tyndp_trajectories.py | 2 +- .../sb/build_tyndp_transmission_projects.py | 10 +- scripts/sb/clean_tyndp_h2_imports.py | 15 +- scripts/sb/clean_tyndp_h2_storages.py | 2 +- scripts/sb/clean_tyndp_output_benchmark.py | 4 +- scripts/sb/clean_tyndp_report_benchmark.py | 10 +- scripts/sb/clean_tyndp_smr.py | 4 +- scripts/sb/group_tyndp_conventionals.py | 12 +- scripts/sb/make_benchmark.py | 24 ++- scripts/sb/plot_benchmark.py | 6 +- scripts/sb/plot_offshore_network.py | 4 +- scripts/solve_network.py | 14 +- scripts/temporal_aggregation.py | 4 +- 38 files changed, 437 insertions(+), 321 deletions(-) diff --git a/scripts/_helpers.py b/scripts/_helpers.py index 441a9a839d..b9f6a1297a 100644 --- a/scripts/_helpers.py +++ b/scripts/_helpers.py @@ -1143,11 +1143,11 @@ def extract_grid_data_tyndp( Parameters ---------- links : pd.DataFrame - DataFrame with raw links to extract grid information from + DataFrame with raw links to extract grid information from. replace_dict : dict - Dictionary with region names to replace + Dictionary with region names to replace. expand_from_index : bool - Whether to expand the bus0 and bus1 from index or directly use the columns + Whether to expand the bus0 and bus1 from index or directly use the columns. idx_prefix : str, optional Prefix to prepend to generated indices. idx_connector : str, optional @@ -1160,7 +1160,7 @@ def extract_grid_data_tyndp( Returns ------- pd.DataFrame - DataFrame with extracted grid data information with nominal capacity in input unit, bus0 and bus1 + DataFrame with extracted grid data information with nominal capacity in input unit, bus0 and bus1. """ if expand_from_index: @@ -1231,7 +1231,7 @@ def safe_pyear( Returns ------- year_new : int - Safe pyear adjusted for available years + Safe pyear adjusted for available years. """ if not available_years: @@ -1341,6 +1341,16 @@ def get_version(hash_len: int = 9) -> str: - If HEAD is exactly at a tag: returns the tag name (e.g., "v1.2.3") - If HEAD is beyond a tag: returns "tag+g{hash}" (e.g., "v1.2.3+g1a2b3c4d") - If no tags found: returns just the commit hash (e.g., "1a2b3c4d5") + + Parameters + ---------- + hash_len : int, optional + Number of characters to use from the commit hash. Defaults to 9. + + Returns + ------- + str + Version string derived from the git repository state. """ try: repo = git.Repo(search_parent_directories=True) @@ -1434,7 +1444,21 @@ def convert_units( def check_cyear(cyear: int, scenario: str) -> int: - """Check if the climatic year is valid for the given scenario.""" + """ + Check if the climatic year is valid for the given scenario. + + Parameters + ---------- + cyear : int + Climatic year to validate. + scenario : str + TYNDP scenario name. + + Returns + ------- + int + Valid climatic year, falling back to 2009 if the input is not available. + """ valid_years = { "NT": [1995, 2008, 2009], @@ -1604,9 +1628,21 @@ def interpolate_demand( return result -def find_free_port(start_port=8050, max_attempts=50): +def find_free_port(start_port: int = 8050, max_attempts: int = 50) -> int: """ Find the first available port starting from start_port. + + Parameters + ---------- + start_port : int, optional + Port number to begin scanning from. Defaults to 8050. + max_attempts : int, optional + Maximum number of ports to check before raising an error. Defaults to 50. + + Returns + ------- + int + First available port number in the scanned range. """ for port in range(start_port, start_port + max_attempts): try: @@ -1703,8 +1739,21 @@ def align_demand_to_snapshots( demand: pd.DataFrame, snapshots: pd.DatetimeIndex, format: str = None ) -> pd.DataFrame: """ - Convert demand index to DatetimeIndex, adjust year to match snapshots, - and reindex to snapshots. + Convert demand index to DatetimeIndex, adjust year to match snapshots, and reindex to snapshots. + + Parameters + ---------- + demand : pd.DataFrame + Demand time series with a datetime-compatible index. + snapshots : pd.DatetimeIndex + Target snapshot index to align demand to. + format : str, optional + Datetime format string for parsing the demand index. Defaults to None. + + Returns + ------- + pd.DataFrame + Demand data reindexed to the provided snapshots. """ demand.index = pd.to_datetime(demand.index, format=format) diff --git a/scripts/add_brownfield.py b/scripts/add_brownfield.py index 4c6468ec4c..59daca3508 100644 --- a/scripts/add_brownfield.py +++ b/scripts/add_brownfield.py @@ -45,21 +45,21 @@ def add_brownfield( Parameters ---------- n : pypsa.Network - Network to add brownfield to + Network to add brownfield to. n_p : pypsa.Network - Previous network to get brownfield from + Previous network to get brownfield from. year : int - Planning year + Planning year. h2_retrofit : bool - Whether to allow hydrogen pipeline retrofitting + Whether to allow hydrogen pipeline retrofitting. h2_retrofit_capacity_per_ch4 : float - Ratio of hydrogen to methane capacity for pipeline retrofitting + Ratio of hydrogen to methane capacity for pipeline retrofitting. capacity_threshold : float - Threshold for removing assets with low capacity + Threshold for removing assets with low capacity. offshore_hubs_tyndp : bool - Whether to enable offshore hubs + Whether to enable offshore hubs. h2_topology_tyndp : bool - Whether to enable TYNDP Hydrogen topology + Whether to enable TYNDP Hydrogen topology. carriers_tyndp : list[str] List of TYNDP carriers included in the model. """ diff --git a/scripts/build_snapshot_weightings.py b/scripts/build_snapshot_weightings.py index f27612d85c..028df45298 100644 --- a/scripts/build_snapshot_weightings.py +++ b/scripts/build_snapshot_weightings.py @@ -3,7 +3,7 @@ # # SPDX-License-Identifier: MIT """ -Defines the time aggregation, known as ``snapshot_weightings``, to be used for sector-coupled network. +Defines the time aggregation, known as `snapshot_weightings`, to be used for sector-coupled network. Description ----------- diff --git a/scripts/build_tyndp_network.py b/scripts/build_tyndp_network.py index 8256fa004c..0a2901cd3e 100644 --- a/scripts/build_tyndp_network.py +++ b/scripts/build_tyndp_network.py @@ -81,7 +81,20 @@ IBFI_COORD = (63.0, 25.0) -def format_bz_names(s: str): +def format_bz_names(s: str) -> str: + """ + Standardize bidding zone name formats to Open-TYNDP conventions. + + Parameters + ---------- + s : str + Raw bidding zone name string to format. + + Returns + ------- + str + Formatted bidding zone name with standardized region codes. + """ s = s.replace("FR-C", "FR15").replace("UK-N", "UKNI").replace("UK", "GB") return s @@ -100,7 +113,7 @@ def extract_shape_by_bbox( Parameters ---------- - gdf : GeoDataFrame + gdf : gpd.GeoDataFrame GeoDataFrame containing country geometries. country : str The country code or name to filter. @@ -117,7 +130,7 @@ def extract_shape_by_bbox( Returns ------- - GeoDataFrame + gpd.GeoDataFrame Updated GeoDataFrame with the extracted shape separated. """ country_gdf = gdf.explode().query(f"country == '{country}'").reset_index(drop=True) @@ -226,9 +239,9 @@ def build_buses( Path to bidding zone shape file. countries : list[str] List of countries to consider. - bidding_shapes : GeoDataFrame + bidding_shapes : gpd.GeoDataFrame A GeoDataFrame including bidding zone geometry, representative point and id. - country_shapes : GeoDataFrame + country_shapes : gpd.GeoDataFrame A GeoDataFrame including country geometry and representative point. geo_crs : CRS, optional Coordinate reference system for geographic calculations. Defaults to GEO_CRS. @@ -329,14 +342,19 @@ def add_links_missing_attributes( Parameters ---------- - - links (pd.DataFrame): DataFrame of links with 'bus0' and 'bus1' columns. - - buses (gpd.GeoDataFrame): GeoDataFrame of electrical buses including country and coordinates. - - geo_crs (CRS, optional): Coordinate reference system for geographic calculations. Defaults to GEO_CRS. - - distance_crs (CRS, optional): Coordinate reference system for distance calculations. Defaults to DISTANCE_CRS. + links : pd.DataFrame + DataFrame of links with bus0 and bus1 columns. + buses : gpd.GeoDataFrame + GeoDataFrame of electrical buses including country and coordinates. + geo_crs : str, optional + Coordinate reference system for geographic calculations. Defaults to GEO_CRS. + distance_crs : str, optional + Coordinate reference system for distance calculations. Defaults to DISTANCE_CRS. Returns ------- - - links: DataFrame with added geometry columns. + gpd.GeoDataFrame + GeoDataFrame of links with added geometry columns. """ links = links.merge( buses["geometry"], how="left", left_on="bus0", right_index=True @@ -418,7 +436,7 @@ def build_links( ---------- grid_fn : str | Path Path to bidding zone shape file. - buses : GeoDataFrame + buses : gpd.GeoDataFrame A GeoDataFrame of electrical buses including country and coordinates. geo_crs : CRS, optional Coordinate reference system for geographic calculations. Defaults to GEO_CRS. @@ -427,7 +445,7 @@ def build_links( Returns ------- - GeoDataFrame + gpd.GeoDataFrame A GeoDataFrame including NTC from the reference grid. """ links = pd.read_excel(grid_fn) diff --git a/scripts/cba/_helpers.py b/scripts/cba/_helpers.py index 497390d74f..fc241a256b 100644 --- a/scripts/cba/_helpers.py +++ b/scripts/cba/_helpers.py @@ -12,10 +12,10 @@ def get_link_attrs(project: pd.Series, costs: pd.DataFrame) -> dict: Return length, underwater_fraction, and capital_cost for a new DC link. The capital_cost is computed using the same per-km formula as - ``add_electricity.py`` to ensure consistency with existing network + `add_electricity.py` to ensure consistency with existing network links: - ``capital_cost = length * ((1 - uf) * overhead + uf * submarine) + inverter`` + `capital_cost = length * ((1 - uf) * overhead + uf * submarine) + inverter` Parameters ---------- @@ -24,8 +24,13 @@ def get_link_attrs(project: pd.Series, costs: pd.DataFrame) -> dict: underwater_fraction. costs : pd.DataFrame Technology costs table (indexed by technology name) with a - ``capital_cost`` column containing annualized EUR/MW or EUR/MW/km + `capital_cost` column containing annualized EUR/MW or EUR/MW/km values. + + Returns + ------- + dict + Dictionary with keys length, underwater_fraction, and capital_cost. """ length = float(project.get("length_km", 0)) uf = float(project.get("underwater_fraction", 0)) @@ -57,9 +62,9 @@ def filter_projects_by_specs( Parameters ---------- project_list : list[str] - List of all available project names to filter from + List of all available project names to filter from. spec_list : list[str], str, or None - List of specifications, a single specification string, or None to return all projects + List of specifications, a single specification string, or None to return all projects. Returns ------- diff --git a/scripts/cba/average_indicators.py b/scripts/cba/average_indicators.py index e7ca81fe84..63f7e29f5e 100644 --- a/scripts/cba/average_indicators.py +++ b/scripts/cba/average_indicators.py @@ -46,14 +46,17 @@ def average_indicators_csv(input_files, output_file, planning_horizon): """ Concatenate multiple CSV files into one using the csv module. - Args: - input_files: List of paths to input CSV files - output_file: Path to output CSV file - - The function: - 1. Reads the header from the first file - 2. Writes all rows from all files to the output - 3. Ensures all files have the same header structure + Reads the header from the first file, writes all rows from all files to the + output, and ensures all files have the same header structure. + + Parameters + ---------- + input_files : list[str] + List of paths to input CSV files. + output_file : str + Path to output CSV file. + planning_horizon : int or str + Planning horizon year used for climatic year weighting. """ if not input_files: logger.warning("No input files provided") diff --git a/scripts/cba/build_msv_snapshot_weightings.py b/scripts/cba/build_msv_snapshot_weightings.py index e4119e650c..9d7c4f62a0 100644 --- a/scripts/cba/build_msv_snapshot_weightings.py +++ b/scripts/cba/build_msv_snapshot_weightings.py @@ -5,21 +5,21 @@ Generate snapshot weightings for MSV extraction temporal aggregation. Produces a CSV with resampled snapshot weightings at the configured -MSV extraction resolution. Follows the same logic as ``time_aggregation.py`` +MSV extraction resolution. Follows the same logic as `time_aggregation.py` for the supported resolution formats: -- ``false``: No aggregation, outputs empty CSV -- ``"Nsn"``: Representative snapshots (e.g., "2sn"), outputs empty CSV - (handled directly by ``set_temporal_aggregation``) -- ``"Nh"``: Hourly resampling (e.g., "24H", "48H"), outputs resampled weightings +- `false`: No aggregation, outputs empty CSV +- `"Nsn"`: Representative snapshots (e.g., "2sn"), outputs empty CSV + (handled directly by `set_temporal_aggregation`) +- `"Nh"`: Hourly resampling (e.g., "24H", "48H"), outputs resampled weightings **Inputs** -- ``resources/cba/networks/reference_{planning_horizons}.nc``: Reference network +- `resources/cba/networks/reference_{planning_horizons}.nc`: Reference network **Outputs** -- ``resources/cba/msv_snapshot_weightings_{planning_horizons}.csv``: Snapshot weightings +- `resources/cba/msv_snapshot_weightings_{planning_horizons}.csv`: Snapshot weightings """ import logging diff --git a/scripts/cba/clean_projects.py b/scripts/cba/clean_projects.py index 62cff8d599..ba1ef5a297 100644 --- a/scripts/cba/clean_projects.py +++ b/scripts/cba/clean_projects.py @@ -14,24 +14,24 @@ **Inputs** -- ``data/tyndp_2024_bundle/cba_projects/20250312_export_transmission.xlsx``: Excel file containing CBA transmission projects -- ``data/tyndp_2024_bundle/cba_projects/20250312_export_storage.xlsx``: Excel file containing CBA storage projects (not yet processed) +- `data/tyndp_2024_bundle/cba_projects/20250312_export_transmission.xlsx`: Excel file containing CBA transmission projects +- `data/tyndp_2024_bundle/cba_projects/20250312_export_storage.xlsx`: Excel file containing CBA storage projects (not yet processed) **Outputs** -- ``resources/cba/transmission_projects.csv``: Cleaned CSV with columns: - - ``project_id``: Integer project identifier - - ``project_name``: Project name - - ``border``: Border string in format "BUS0-BUS1" - - ``p_nom 0->1``: Transfer capacity increase from bus0 to bus1 (MW) - - ``p_nom 1->0``: Transfer capacity increase from bus1 to bus0 (MW) - - ``bus0``: Source bus code (4 alphanumeric characters) - - ``bus1``: Destination bus code (4 alphanumeric characters) - - ``length_km``: Total route length in km (from Trans.Investments) - - ``capex_meur``: Total estimated CAPEX in MEUR (from Trans.Investments) - - ``underwater_fraction``: Fraction of route that is offshore cable - -- ``resources/cba/storage_projects.csv``: Empty CSV with columns project_id and project_name (stub implementation) +- `resources/cba/transmission_projects.csv`: Cleaned CSV with columns: + - `project_id`: Integer project identifier + - `project_name`: Project name + - `border`: Border string in format "BUS0-BUS1" + - `p_nom 0->1`: Transfer capacity increase from bus0 to bus1 (MW) + - `p_nom 1->0`: Transfer capacity increase from bus1 to bus0 (MW) + - `bus0`: Source bus code (4 alphanumeric characters) + - `bus1`: Destination bus code (4 alphanumeric characters) + - `length_km`: Total route length in km (from Trans.Investments) + - `capex_meur`: Total estimated CAPEX in MEUR (from Trans.Investments) + - `underwater_fraction`: Fraction of route that is offshore cable + +- `resources/cba/storage_projects.csv`: Empty CSV with columns project_id and project_name (stub implementation) """ diff --git a/scripts/cba/collect_indicators.py b/scripts/cba/collect_indicators.py index 688892753c..585de07c0e 100644 --- a/scripts/cba/collect_indicators.py +++ b/scripts/cba/collect_indicators.py @@ -21,14 +21,15 @@ def collect_indicators_csv(input_files, output_file): """ Concatenate multiple CSV files into one using the csv module. - Args: - input_files: List of paths to input CSV files - output_file: Path to output CSV file - - The function: - 1. Reads the header from the first file - 2. Writes all rows from all files to the output - 3. Ensures all files have the same header structure + Reads the header from the first file, writes all rows from all files to the + output, and ensures all files have the same header structure. + + Parameters + ---------- + input_files : list[str] + List of paths to input CSV files. + output_file : str + Path to output CSV file. """ if not input_files: logger.warning("No input files provided") diff --git a/scripts/cba/fix_reference_sb_to_cba.py b/scripts/cba/fix_reference_sb_to_cba.py index be206f5eee..d921980ab4 100644 --- a/scripts/cba/fix_reference_sb_to_cba.py +++ b/scripts/cba/fix_reference_sb_to_cba.py @@ -3,7 +3,7 @@ # SPDX-License-Identifier: MIT """ -Build a ``Link`` dataframe of capacity corrections to align SB transmission projects with the CBA reference grid. A positive ``p_nom`` indicates capacity to add (potentially as a new link), while a negative ``p_nom`` indicates capacity to reduce on an existing link. +Build a `Link` dataframe of capacity corrections to align SB transmission projects with the CBA reference grid. A positive `p_nom` indicates capacity to add (potentially as a new link), while a negative `p_nom` indicates capacity to reduce on an existing link. This only works for 2040, as we are assuming the 2030 reference grid does not need corrections. """ diff --git a/scripts/cba/make_indicators.py b/scripts/cba/make_indicators.py index 828a81d811..b895415119 100644 --- a/scripts/cba/make_indicators.py +++ b/scripts/cba/make_indicators.py @@ -99,11 +99,17 @@ def calculate_total_system_cost(n, remove_noisy_costs: bool = False): - Time-aggregated operational costs - All component types (generators, links, storage, etc.) - Args: - n: PyPSA Network (must be solved) + Parameters + ---------- + n : pypsa.Network + PyPSA network (must be solved). + remove_noisy_costs : bool, optional + Whether to remove noisy costs before calculation. - Returns: - float: Total system cost in currency units (Euros) + Returns + ------- + dict + Dictionary with keys total, capex, and opex (all in MEur). """ if not n.is_solved: raise ValueError("Network must be solved before calculating costs") @@ -252,7 +258,7 @@ def calculate_power_sector_co2_emissions( """ Calculate annual power-sector CO2 emissions for assets producing on AC buses. - Generators use carrier-specific ``co2_emissions`` intensities. Links use the + Generators use carrier-specific `co2_emissions` intensities. Links use the explicit CO2 port flows of the electricity-producing asset. Parameters @@ -418,13 +424,21 @@ def calculate_b1_indicator( - PINT: positive B1 means beneficial (project reduces costs) - TOOT: positive B1 means beneficial (removing project increases costs) - Args: - n_reference: Reference network - n_project: Project network - method: Either "pint" or "toot" (case-insensitive) + Parameters + ---------- + n_reference : pypsa.Network + Reference network (solved). + n_project : pypsa.Network + Project network. + method : str, optional + Either "pint" or "toot". + remove_noisy_costs : bool, optional + Whether to remove noisy costs before calculation. - Returns: - dict: Dictionary with B1 and component costs + Returns + ------- + dict + Dictionary with B1 and component costs. """ # Calculate full cost breakdowns for reporting cost_reference = calculate_total_system_cost(n_reference, remove_noisy_costs) diff --git a/scripts/cba/prepare_rolling_horizon.py b/scripts/cba/prepare_rolling_horizon.py index c750390323..62f488923d 100644 --- a/scripts/cba/prepare_rolling_horizon.py +++ b/scripts/cba/prepare_rolling_horizon.py @@ -33,7 +33,7 @@ def disable_global_constraints(n: pypsa.Network): Parameters ---------- n : pypsa.Network - Network to modify + Network to modify. """ if "co2_sequestration_limit" in n.global_constraints.index: n.remove("GlobalConstraint", "co2_sequestration_limit") @@ -89,13 +89,18 @@ def resample_msv_to_target( Parameters ---------- msv : pd.DataFrame - MSV data from extraction (e.g., 24H resolution) + MSV data from extraction (e.g., 24H resolution). target_snapshots : pd.DatetimeIndex - Target snapshots (e.g., 3H resolution) + Target snapshots (e.g., 3H resolution). method : str, optional Resampling method: - - "ffill": Forward fill - each MSV value applies until the next one - - "interpolate": Linear interpolation between marginal storage values + - "ffill": Forward fill - each MSV value applies until the next one. + - "interpolate": Linear interpolation between marginal storage values. + + Returns + ------- + pd.DataFrame + Resampled MSV data aligned to target_snapshots. """ if method == "interpolate": # Combine indices and interpolate @@ -125,7 +130,7 @@ def disable_volume_limits(n: pypsa.Network): Parameters ---------- n : pypsa.Network - Network to modify + Network to modify. """ for c in n.components[{"Generator", "Link"}]: has_e_sum_min = isfinite(c.static.get("e_sum_min", [])) diff --git a/scripts/cba/simplify_sb_network.py b/scripts/cba/simplify_sb_network.py index a1a32a9672..8084a3abbf 100644 --- a/scripts/cba/simplify_sb_network.py +++ b/scripts/cba/simplify_sb_network.py @@ -15,7 +15,7 @@ **Outputs** -- ``resources/cba/networks/simple_{planning_horizons}.nc``: Simplified network for CBA +- `resources/cba/networks/simple_{planning_horizons}.nc`: Simplified network for CBA """ import logging @@ -40,7 +40,7 @@ def extend_primary_fuel_sources(n: pypsa.Network, tyndp_conventional_carriers: l Parameters ---------- n : pypsa.Network - Network to modify + Network to modify. tyndp_conventional_carriers : list List of conventional carrier names from TYNDP data, which may include fuel sub-types (e.g., 'oil-light', 'oil-heavy'). These are grouped by diff --git a/scripts/cba/solve_cba_msv_extraction.py b/scripts/cba/solve_cba_msv_extraction.py index e494409887..9ea9998c08 100644 --- a/scripts/cba/solve_cba_msv_extraction.py +++ b/scripts/cba/solve_cba_msv_extraction.py @@ -11,12 +11,12 @@ **Inputs** -- ``resources/cba/networks/reference_{planning_horizons}.nc``: Reference network -- ``resources/cba/msv_snapshot_weightings_{planning_horizons}.csv``: Snapshot weightings (optional) +- `resources/cba/networks/reference_{planning_horizons}.nc`: Reference network +- `resources/cba/msv_snapshot_weightings_{planning_horizons}.csv`: Snapshot weightings (optional) **Outputs** -- ``resources/cba/networks/msv_{planning_horizons}.nc``: Network with marginal storage values in stores_t.mu_energy_balance +- `resources/cba/networks/msv_{planning_horizons}.nc`: Network with marginal storage values in stores_t.mu_energy_balance """ import copy diff --git a/scripts/cba/solve_cba_network.py b/scripts/cba/solve_cba_network.py index eb21fb57d0..cd0c6cc07f 100644 --- a/scripts/cba/solve_cba_network.py +++ b/scripts/cba/solve_cba_network.py @@ -11,8 +11,8 @@ Description ----------- -The optimization is based on the :func:`network.optimize_with_rolling_horizon` method. -Additionally, some extra constraints specified in :mod:`solve_network` are added, if +The optimization is based on the `network.optimize_with_rolling_horizon` method. +Additionally, some extra constraints specified in `solve_network` are added, if they apply to the dispatch. """ @@ -57,23 +57,23 @@ def extra_functionality( planning_horizons: str | None = None, ) -> None: """ - Add custom constraints and functionality for operations network + Add custom constraints and functionality for operations network. + + Collects supplementary constraints which will be passed to + `pypsa.optimization.optimize`. + + If you want to enforce additional custom constraints, this is a good + location to add them. The arguments `opts` and + `snakemake.config` are expected to be attached to the network. Parameters ---------- n : pypsa.Network - The PyPSA network instance with config and params attributes + The PyPSA network instance with config and params attributes. snapshots : pd.DatetimeIndex - Simulation timesteps + Simulation timesteps. planning_horizons : str, optional - The current planning horizon year or None in perfect foresight - - Collects supplementary constraints which will be passed to - ``pypsa.optimization.optimize``. - - If you want to enforce additional custom constraints, this is a good - location to add them. The arguments ``opts`` and - ``snakemake.config`` are expected to be attached to the network. + The current planning horizon year or None in perfect foresight. """ config = n.config @@ -114,18 +114,20 @@ def optimize_with_rolling_horizon( Parameters ---------- n : pypsa.Network - snapshots : list-like + The PyPSA network instance to optimize. + snapshots : Sequence, optional Set of snapshots to consider in the optimization. The default is None. horizon : int Number of snapshots to consider in each iteration. Defaults to 100. overlap : int Number of snapshots to overlap between two iterations. Defaults to 0. - **kwargs: - Keyword argument used by `linopy.Model.solve`, such as `solver_name`, + **kwargs + Keyword arguments used by `linopy.Model.solve`, such as `solver_name`. Returns ------- tuple[str, str] + Tuple of (status, condition) from the final optimization window. """ if snapshots is None: snapshots: Sequence = n.snapshots @@ -212,28 +214,17 @@ def solve_network( Parameters ---------- n : pypsa.Network - The PyPSA network instance - config : Dict - Configuration dictionary containing solver settings - params : Dict - Dictionary of solving parameters - solving : Dict - Dictionary of solving options and configuration - rule_name : str, optional - Name of the snakemake rule being executed + The PyPSA network instance. + config : dict + Configuration dictionary containing solver settings. + params : dict + Dictionary of solving parameters. + solving : dict + Dictionary of solving options and configuration. planning_horizons : str, optional - The current planning horizon year or None in perfect foresight + The current planning horizon year or None in perfect foresight. **kwargs - Additional keyword arguments passed to the solver - - Returns - ------- - n : pypsa.Network - Solved network instance - status : str - Solution status - condition : str - Termination condition + Additional keyword arguments passed to the solver. Raises ------ diff --git a/scripts/cba/summarize_indicators.py b/scripts/cba/summarize_indicators.py index 976c75cc51..1b6f235486 100644 --- a/scripts/cba/summarize_indicators.py +++ b/scripts/cba/summarize_indicators.py @@ -300,14 +300,15 @@ def summarize_indicators(input_files, output_file): """ Concatenate multiple CSV files into one using the csv module. - Args: - input_files: List of paths to input CSV files - output_file: Path to output CSV file - - The function: - 1. Reads the header from the first file - 2. Writes all rows from all files to the output - 3. Ensures all files have the same header structure + Reads the header from the first file, writes all rows from all files to the + output, and ensures all files have the same header structure. + + Parameters + ---------- + input_files : list[str] + List of paths to input CSV files. + output_file : str + Path to output CSV file. """ if not input_files: logger.warning("No input files provided") diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index a4bebf66fd..0ed88c82fc 100755 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -120,7 +120,7 @@ def define_spatial( Parameters ---------- nodes : list-like - Nodes to define spatial data for + Nodes to define spatial data for. options : dict Configuration options containing at least: - biomass_spatial : bool @@ -572,9 +572,9 @@ def create_h2_topology_tyndp(n, fn_h2_network, options): Parameters ---------- n : pypsa.Network - Network to create H2 topology for + Network to create H2 topology for. fn_h2_network : str - Pointing to the input TYNDP H2 reference grid csv file + Pointing to the input TYNDP H2 reference grid csv file. options : dict Dictionary of configuration options. Key options include: - h2_zones_tyndp : bool @@ -1507,22 +1507,22 @@ def _add_other_non_res_tyndp( Parameters ---------- n : pypsa.Network - The PyPSA network container object + The PyPSA network container object. generator : str - Name of the Other Non-RES generator + Name of the Other Non-RES generator. carrier : str - Name of the Other Non-RES fuel carrier + Name of the Other Non-RES fuel carrier. carrier_nodes : list - Nodes of the fuel carrier + Nodes of the fuel carrier. spatial : SimpleNamespace Namespace containing spatial information for different carriers, - including nodes and locations + including nodes and locations. costs : pd.DataFrame - DataFrame containing cost and technical parameters for different technologies + DataFrame containing cost and technical parameters for different technologies. pemmdb_capacities : pd.DataFrame - Dataframe containing PEMMDB capacities including information on the different Other Non-RES price bands + Dataframe containing PEMMDB capacities including information on the different Other Non-RES price bands. co2_price: float - Emission price for the given planning year + Emission price for the given planning year. Returns ------- @@ -1610,24 +1610,24 @@ def add_thermal_generation_tyndp( Parameters ---------- n : pypsa.Network - The PyPSA network container object + The PyPSA network container object. costs : pd.DataFrame - DataFrame containing cost and technical parameters for different technologies + DataFrame containing cost and technical parameters for different technologies. nodes : pd.Index - pd.Index with demand nodes + pd.Index with demand nodes. tyndp_conventionals : dict[str, str] - Dictionary mapping TYNDP conventional generation technologies to their energy carriers + Dictionary mapping TYNDP conventional generation technologies to their energy carriers. spatial : SimpleNamespace Namespace containing spatial information for different carriers, - including nodes and locations + including nodes and locations. options : dict - Configuration dictionary containing settings for the model + Configuration dictionary containing settings for the model. cf_industry : dict - Dictionary of industrial conversion factors, needed for carrier buses + Dictionary of industrial conversion factors, needed for carrier buses. pemmdb_capacities: pd.DataFrame - Dataframe containing PEMMDB capacities including Other Non-RES price band information + Dataframe containing PEMMDB capacities including Other Non-RES price band information. co2_price: float - Emission price for the given planning year + Emission price for the given planning year. Returns ------- @@ -1721,7 +1721,7 @@ def add_other_res_tyndp( n : pypsa.Network The PyPSA network container object. costs : pd.DataFrame - DataFrame containing cost and technical parameters for different technologies + DataFrame containing cost and technical parameters for different technologies. pop_layout : SimpleNamespace. Namespace containing spatial information for different carriers, including nodes and locations. @@ -1965,7 +1965,7 @@ def _add_other_non_res_capacities( tech : str Other Non-RES price band to be added to the network. group_conventionals : bool - Whether TYNDP conventional carriers are aggregated into higher level groups + Whether TYNDP conventional carriers are aggregated into higher level groups. Returns ------- @@ -2114,7 +2114,7 @@ def _add_conventional_thermal_capacities( nuclear_profiles : pd.DataFrame DataFrame containing the availability profiles of nuclear power plants. group_conventionals : bool - Whether TYNDP conventional carriers are aggregated into higher level groups + Whether TYNDP conventional carriers are aggregated into higher level groups. Returns ------- @@ -2314,7 +2314,7 @@ def _extract_inflows( hydro_tech_i : pd.Index Index of network components associated with the hydro technology. name_sfx : str, optional - String suffix added to the column name of the returned inflow Dataframe + String suffix added to the column name of the returned inflow Dataframe. Returns ------- @@ -3490,13 +3490,13 @@ def add_h2_production_tyndp(n, nodes, buses_h2, costs, options={}): Parameters ---------- n : pypsa.Network - The PyPSA network container object + The PyPSA network container object. nodes : pd.Index - Pandas Index of electricity node locations/nodes + Pandas Index of electricity node locations/nodes. buses_h2 : pd.Index - Pandas Index of hydrogen nodes to which H2 production technologies will connect + Pandas Index of hydrogen nodes to which H2 production technologies will connect. costs : pd.DataFrame - Technology cost assumptions + Technology cost assumptions. options : dict, optional Dictionary of configuration options. Defaults to empty dict if not provided. Key options include: @@ -3614,13 +3614,13 @@ def add_h2_dres_tyndp(n, spatial, buses_h2_z2, costs): Parameters ---------- n : pypsa.Network - The PyPSA network container object + The PyPSA network container object. spatial : object - Namespace object with spatial nodes for different carriers such as `h2_tyndp` + Namespace object with spatial nodes for different carriers such as `h2_tyndp`. buses_h2_z2 : SimpleNamespace - Namespace object with spatial nodes of H2 Z2 buses + Namespace object with spatial nodes of H2 Z2 buses. costs : pd.DataFrame - Technology cost assumptions + Technology cost assumptions. Returns ------- @@ -3660,15 +3660,15 @@ def add_h2_reconversion_tyndp(n, spatial, nodes, buses_h2, costs, options=None): Parameters ---------- n : pypsa.Network - The PyPSA network container object + The PyPSA network container object. spatial : object - Namespace object with spatial nodes for different carriers such as `h2_tyndp` + Namespace object with spatial nodes for different carriers such as `h2_tyndp`. nodes : pd.Index - Pandas Index of electricity node locations/nodes + Pandas Index of electricity node locations/nodes. buses_h2 : pd.Index - Pandas Index of hydrogen nodes to which H2 reconversion technologies will connect + Pandas Index of hydrogen nodes to which H2 reconversion technologies will connect. costs : pd.DataFrame - Technology cost assumptions + Technology cost assumptions. options : dict, optional Dictionary of configuration options. Defaults to empty dict if not provided. Key options include: @@ -3747,15 +3747,15 @@ def add_h2_grid_tyndp(n, nodes, h2_pipes_file, interzonal_file, costs, options): Parameters ---------- n : pypsa.Network - The PyPSA network container object + The PyPSA network container object. nodes : pd.Index - Pandas Index of electricity node locations/nodes + Pandas Index of electricity node locations/nodes. h2_pipes_file : str - Path to CSV file containing prepped H2 reference grid data + Path to CSV file containing prepped H2 reference grid data. interzonal_file : str - Path to CSV file containing prepped H2 interzonal connection data + Path to CSV file containing prepped H2 interzonal connection data. costs : pd.DataFrame - Technology cost assumptions + Technology cost assumptions. options : dict Dictionary of configuration options. Key options include: - h2_zones_tyndp : bool @@ -3823,13 +3823,13 @@ def _add_h2_stores_and_links_tyndp( Parameters ---------- n : pypsa.Network - The PyPSA network container object + The PyPSA network container object. storage_tech: str Storage technology to add. Can be either 'cavern-storage' or 'tank-storage' buses : pd.Index - nodes of H2 buses to add the storages to + nodes of H2 buses to add the storages to. costs : pd.DataFrame - Technology cost assumptions + Technology cost assumptions. extendable : bool Whether the added storage components shall be extendable in the optimization or not. @@ -3900,13 +3900,13 @@ def add_h2_storage_tyndp( Parameters ---------- n : pypsa.Network - The PyPSA network container object + The PyPSA network container object. buses_h2_z1 : pd.Index - Nnodes of H2 Z1 buses + Nnodes of H2 Z1 buses. buses_h2_z2 : pd.Index - Nodes of H2 Z2 buses + Nodes of H2 Z2 buses. costs : pd.DataFrame - Technology cost assumptions + Technology cost assumptions. options : dict, optional Dictionary of configuration options. Defaults to empty dict if not provided. - h2_zones_tyndp : bool @@ -3965,21 +3965,21 @@ def add_h2_topology_tyndp( Parameters ---------- n : pypsa.Network - The PyPSA network container object + The PyPSA network container object. pop_layout : pd.DataFrame - Population layout with index of locations/nodes + Population layout with index of locations/nodes. spatial : object - Namespace object with spatial nodes for different carriers such as `h2_tyndp` + Namespace object with spatial nodes for different carriers such as `h2_tyndp`. h2_pipes_file : str - Path to CSV file containing prepped H2 reference grid data + Path to CSV file containing prepped H2 reference grid data. interzonal_file : str - Path to CSV file containing prepped H2 interzonal connection data + Path to CSV file containing prepped H2 interzonal connection data. costs : pd.DataFrame - Technology cost assumptions + Technology cost assumptions. options : dict, optional Dictionary of configuration options. Defaults to empty dict if not provided. h2_demand_file : str - Path to CSV file containing exogenous hydrogen demand time series + Path to CSV file containing exogenous hydrogen demand time series. Returns @@ -4068,9 +4068,9 @@ def add_h2_demand_tyndp(n, h2_demand_file): Parameters ---------- n : pypsa.Network - The PyPSA network container object + The PyPSA network container object. h2_demand_file : str - Path to CSV file containing exogenous hydrogen demand time series + Path to CSV file containing exogenous hydrogen demand time series. """ logger.info("Add exogenous hydrogen demand to network") @@ -4105,9 +4105,9 @@ def add_h2_production(n, nodes, options, spatial, costs): Parameters ---------- n : pypsa.Network - The PyPSA network container object + The PyPSA network container object. nodes : pd.Index - Pandas Index of locations/nodes + Pandas Index of locations/nodes. options : dict, optional Dictionary of configuration options. Defaults to empty dict if not provided. Key options include: @@ -4115,9 +4115,9 @@ def add_h2_production(n, nodes, options, spatial, costs): - SMR : bool - cc_fraction : float spatial : object, optional - Object containing spatial information about nodes and their locations + Object containing spatial information about nodes and their locations. costs : pd.DataFrame - Technology cost assumptions + Technology cost assumptions. Returns ------- @@ -4182,9 +4182,9 @@ def add_h2_reconversion(n, nodes, options, spatial, costs): Parameters ---------- n : pypsa.Network - The PyPSA network container object + The PyPSA network container object. nodes : pd.Index - Pandas Index of locations/nodes + Pandas Index of locations/nodes. options : dict, optional Dictionary of configuration options. Defaults to empty dict if not provided. Key options include: @@ -4192,9 +4192,9 @@ def add_h2_reconversion(n, nodes, options, spatial, costs): - hydrogen_turbine : bool - methanation : bool spatial : object, optional - Object containing spatial information about nodes and their locations + Object containing spatial information about nodes and their locations. costs : pd.DataFrame - Technology cost assumptions + Technology cost assumptions. Returns ------- @@ -4264,19 +4264,19 @@ def add_h2_storage(n, nodes, options, cavern_types, h2_cavern_file, costs): Parameters ---------- n : pypsa.Network - The PyPSA network container object + The PyPSA network container object. nodes : pd.Index - Pandas Index of locations/nodes + Pandas Index of locations/nodes. options : dict, optional Dictionary of configuration options. Defaults to empty dict if not provided. Key options include: - hydrogen_underground_storage : bool cavern_types : list - List of underground storage types to consider + List of underground storage types to consider. h2_cavern_file : str - Path to CSV file containing hydrogen cavern storage potentials + Path to CSV file containing hydrogen cavern storage potentials. costs : pd.DataFrame - Technology cost assumptions + Technology cost assumptions. Returns ------- @@ -4343,16 +4343,16 @@ def add_gas_network(n, gas_pipes, options, costs, gas_input_nodes): Parameters ---------- n : pypsa.Network - The PyPSA network container object + The PyPSA network container object. gas_pipes : pd.DataFrame - Dataframe containing gas network data + Dataframe containing gas network data. options : dict, optional Dictionary of configuration options. Defaults to empty dict if not provided. Key options include: - H2_retrofit : bool - gas_network_connectivity_upgrade : int costs : pd.DataFrame - Technology cost assumptions + Technology cost assumptions. gas_input_nodes : pd.DataFrame, optional DataFrame containing gas input node information (LNG, pipeline, etc.) @@ -4489,15 +4489,15 @@ def add_h2_pipeline_retrofit(n, gas_pipes, options, costs): Parameters ---------- n : pypsa.Network - The PyPSA network container object + The PyPSA network container object. gas_pipes : pd.DataFrame - Dataframe containing gas network data + Dataframe containing gas network data. options : dict, optional Dictionary of configuration options. Defaults to empty dict if not provided. Key options include: - H2_retrofit_capacity_per_CH4 : float costs : pd.DataFrame - Technology cost assumptions + Technology cost assumptions. Returns ------- @@ -4535,9 +4535,9 @@ def add_h2_pipeline_new(n, costs): Parameters ---------- n : pypsa.Network - The PyPSA network container object + The PyPSA network container object. costs : pd.DataFrame - Technology cost assumptions + Technology cost assumptions. Returns ------- @@ -4587,25 +4587,25 @@ def add_h2_gas_infrastructure( Parameters ---------- n : pypsa.Network - The PyPSA network container object + The PyPSA network container object. costs : pd.DataFrame Cost assumptions for different technologies. Must include gas and hydrogen assumptions. pop_layout : pd.DataFrame - Population layout with index of locations/nodes + Population layout with index of locations/nodes. h2_cavern_file : str - Path to CSV file containing hydrogen cavern storage potentials + Path to CSV file containing hydrogen cavern storage potentials. h2_pipes_file : str - Path to CSV file containing prepped H2 reference grid data + Path to CSV file containing prepped H2 reference grid data. interzonal_file : str - Path to CSV file containing prepped H2 interzonal connection data + Path to CSV file containing prepped H2 interzonal connection data. cavern_types : list - List of underground storage types to consider + List of underground storage types to consider. clustered_gas_network_file : str, optional - Path to CSV file containing gas network data + Path to CSV file containing gas network data. gas_input_nodes : pd.DataFrame, optional DataFrame containing gas input node information (LNG, pipeline, etc.) spatial : object, optional - Object containing spatial information about nodes and their locations + Object containing spatial information about nodes and their locations. options : dict, optional Dictionary of configuration options. Defaults to empty dict if not provided. Key options include: @@ -4620,7 +4620,7 @@ def add_h2_gas_infrastructure( - cc_fraction : float - methanation : bool h2_demand_file : str - Path to CSV file containing exogenous hydrogen demand data + Path to CSV file containing exogenous hydrogen demand data. Returns ------- diff --git a/scripts/sb/build_pemmdb_data.py b/scripts/sb/build_pemmdb_data.py index d0de6b515e..a8bae81517 100644 --- a/scripts/sb/build_pemmdb_data.py +++ b/scripts/sb/build_pemmdb_data.py @@ -8,8 +8,8 @@ ------- Cleaned CSV file with all NT capacities (p_nom) in long format and NetCDF file containing the must run obligations (p_min_pu) and availability (p_max_pu) for each of the different PEMMDB technologies. -- ``resources/pemmdb_capacities_{planning_horizon}.csv`` in long format -- ``resources/pemmdb_profiles_{planning_horizon}.nc`` with the following structure: +- `resources/pemmdb_capacities_{planning_horizon}.csv` in long format +- `resources/pemmdb_profiles_{planning_horizon}.nc` with the following structure: =================== ==================== ========================================================= Field Coordinates Description @@ -155,18 +155,18 @@ def _drop_duplicate_price_bands( Dataframe to check for duplicate prices bands. groupby : str|list[str] Columns to group by. - pemmdb_tech: str + pemmdb_tech : str PEMMDB technology name. - node: str + node : str Node name. - cyear: int + cyear : int Climate year. **kwargs : dict Keyword arguments passed to pd.DataFrame.groupby(). Returns ------- - df : pd.DataFrame + pd.DataFrame Dataframe without duplicate prices bands. """ if (groupby in df.columns and df[groupby].duplicated().any()) or ( @@ -1083,7 +1083,7 @@ def process_pemmdb_profiles( sns_year_h : pd.DatetimeIndex Hourly Datetime index for a full given cyear. carrier_mapping_fn : str - Path to file with mapping from external carriers to available tyndp_carrier names + Path to file with mapping from external carriers to available tyndp_carrier names. Returns ------- diff --git a/scripts/sb/build_renewable_profiles_pecd.py b/scripts/sb/build_renewable_profiles_pecd.py index 522455c7f6..307b601d18 100644 --- a/scripts/sb/build_renewable_profiles_pecd.py +++ b/scripts/sb/build_renewable_profiles_pecd.py @@ -6,12 +6,12 @@ generation time series are read for each node across all renewable technologies, including onshore wind, AC-connected offshore wind, DC-connected offshore wind, and solar PV generators. -.. note:: Hydroelectric profiles will be built in script :mod:`build_hydro_profiles_PECD`. Not yet implemented. +.. note:: Hydroelectric profiles will be built in script `build_hydro_profiles_PECD`. Not yet implemented. Outputs ------- -- ``resources/profile_pecd_{clusters}_{technology}.nc`` with the following structure +- `resources/profile_pecd_{clusters}_{technology}.nc` with the following structure =================== ==================== ========================================================= Field Dimensions Description diff --git a/scripts/sb/build_statistics.py b/scripts/sb/build_statistics.py index c6d75f67a3..ca8aab44a2 100644 --- a/scripts/sb/build_statistics.py +++ b/scripts/sb/build_statistics.py @@ -43,19 +43,19 @@ def add_benchmarking_mappings( ) -> None: """ Load benchmarking mappings from the carrier mapping file and apply them - to the ``mapping`` configuration dictionary of each table. + to the `mapping` configuration dictionary of each table. Parameters ---------- carrier_mapping_fn : str Path to csv file with carrier mapping. tables : dict - Dictionary defining the benchmarking tables. When the ``mapping_col`` key is + Dictionary defining the benchmarking tables. When the `mapping_col` key is defined in the configuration, the loaded carrier mapping will be added to - the dictionary with the ``mapping`` key. + the dictionary with the `mapping` key. group_tyndp_conventionals : bool, default False - Whether TYNDP technologies are grouped to their ``open_tyndp_type``. - These group names then take precedence over the names in ``open_tyndp_index`` and ``open_tyndp_carrier``. + Whether TYNDP technologies are grouped to their `open_tyndp_type`. + These group names then take precedence over the names in `open_tyndp_index` and `open_tyndp_carrier`. Returns ------- diff --git a/scripts/sb/build_tyndp_gas_demand.py b/scripts/sb/build_tyndp_gas_demand.py index 7cbd3456fe..a47a9eb014 100644 --- a/scripts/sb/build_tyndp_gas_demand.py +++ b/scripts/sb/build_tyndp_gas_demand.py @@ -38,7 +38,7 @@ Inputs ------ -- ``data/tyndp_2024_bundle/Supply Tool/20240518-Supply-Tool.xlsm``: TYNDP 2024 Supply Tool Excel file containing: +- `data/tyndp_2024_bundle/Supply Tool/20240518-Supply-Tool.xlsm`: TYNDP 2024 Supply Tool Excel file containing: - NT+ data sheet: Final demand by country - Other data and Conversions sheet: Heat distribution and efficiency factors - IT sheet: Italian gas production data @@ -46,7 +46,7 @@ Outputs ------- -- ``gas_demand_tyndp_{planning_horizons}.csv``: Processed gas demand data (MWh) by country/bus +- `gas_demand_tyndp_{planning_horizons}.csv`: Processed gas demand data (MWh) by country/bus for the specified planning horizon """ diff --git a/scripts/sb/build_tyndp_h2_demand.py b/scripts/sb/build_tyndp_h2_demand.py index d697c42332..d2f8459734 100644 --- a/scripts/sb/build_tyndp_h2_demand.py +++ b/scripts/sb/build_tyndp_h2_demand.py @@ -5,16 +5,16 @@ Builds TYNDP Scenario Building hydrogen demand profiles for Open-TYNDP. This script processes hydrogen demand data from TYNDP 2024, using the -``snapshots`` year as the climatic year (``cyear``) for demand profiles. +`snapshots` year as the climatic year (`cyear`) for demand profiles. The data is filtered and interpolated based on the selected scenario (Distributed Energy, Global Ambition, or National Trends) and planning horizon. Climatic Year Selection ----------------------- -The ``snapshots`` year determines the climatic year for demand profiles: +The `snapshots` year determines the climatic year for demand profiles: -- **DE and GA scenarios**: Must use 1995, 2008, or 2009. If ``snapshots`` +- **DE and GA scenarios**: Must use 1995, 2008, or 2009. If `snapshots` is not one of these years, 2009 is used as the default (considered most representative). - **NT scenario**: Must be between 1982 and 2019. @@ -40,12 +40,12 @@ Inputs ------ -- ``data/tyndp_2024_bundle/Demand Profiles``: TYNDP 2024 hydrogen demand profiles +- `data/tyndp_2024_bundle/Demand Profiles`: TYNDP 2024 hydrogen demand profiles Outputs ------- -- ``resources/h2_demand_tyndp_{planning_horizons}.csv``: Processed hydrogen +- `resources/h2_demand_tyndp_{planning_horizons}.csv`: Processed hydrogen demand time series for the specified planning horizon """ diff --git a/scripts/sb/build_tyndp_h2_network.py b/scripts/sb/build_tyndp_h2_network.py index fe992983be..d3269099b2 100644 --- a/scripts/sb/build_tyndp_h2_network.py +++ b/scripts/sb/build_tyndp_h2_network.py @@ -29,14 +29,26 @@ def normalize_starting_grid_h2_nodes(df: pd.DataFrame) -> pd.DataFrame: Normalize node IDs from the newer H2 starting grid workbook to the country-level H2 node IDs currently used in the TYNDP H2 topology. - This is needed because the newer H2 starting grid workbook contains node IDs with trailing zeros (e.g. ``DE00``) - and some exceptions (e.g. ``UK00`` instead of ``GB00``) that need to be normalized to match the country-level node IDs - used in the TYNDP H2 topology (e.g. ``DE``, ``GB``). - - Examples: - - ``AT00`` -> ``AT`` - - ``IBIT00`` -> ``IBIT`` - - ``UK00`` -> ``GB`` + This is needed because the newer H2 starting grid workbook contains node IDs with + trailing zeros (e.g. `DE00`) and some exceptions (e.g. `UK00` instead of `GB00`) + that need to be normalized to match the country-level node IDs used in the TYNDP + H2 topology (e.g. `DE`, `GB`). + + Parameters + ---------- + df : pd.DataFrame + DataFrame with bus0 and bus1 columns containing raw H2 node IDs. + + Returns + ------- + pd.DataFrame + DataFrame with normalized node IDs in bus0 and bus1. + + Notes + ----- + - `AT00` -> `AT` + - `IBIT00` -> `IBIT` + - `UK00` -> `GB` """ df = df.copy() @@ -77,7 +89,7 @@ def load_h2_interzonal_connections(fn, scenario="GA", pyear=2030): Returns ------- pd.DataFrame - The function returns cleaned TYNDP H2 interzonal connections. + Cleaned TYNDP H2 interzonal connections. """ if scenario in ["DE", "GA"]: @@ -130,7 +142,7 @@ def load_h2_grid_entsoe(fn_grid: str, pyear: int) -> pd.DataFrame: Returns ------- pd.DataFrame - The function returns the cleaned TYNDP H2 reference grid. + Cleaned TYNDP H2 reference grid. """ available_years = [2030, 2040, 2050] @@ -173,13 +185,13 @@ def load_h2_grid_entsos(fn_grid: str, fn_projects: str | None) -> pd.DataFrame: ---------- fn_grid : str Path to Excel file containing the ENTSO-E H2 reference grid data. - fn_projects : str + fn_projects : str or None Path to CSV file containing H2 projects data. Returns ------- pd.DataFrame - The function returns the cleaned TYNDP H2 reference grid. + Cleaned TYNDP H2 reference grid. """ h2_grid_raw = pd.read_excel(fn_grid) @@ -210,6 +222,24 @@ def load_h2_grid( ) -> pd.DataFrame: """ Load the corresponding H2 grid based on the source. + + Parameters + ---------- + source : str + Source identifier, either 'entsoe' or 'entsos'. + fn_grid_entsoe : str + Path to the ENTSO-E H2 reference grid file. + fn_grid_entsos : str + Path to the ENTSO-E/ENTSOG joint scenarios H2 reference grid file. + fn_projects : str or None + Path to CSV file containing H2 projects data. + pyear : int + Planning horizon year. + + Returns + ------- + pd.DataFrame + Cleaned H2 grid data for the specified source. """ if source == "entsoe": diff --git a/scripts/sb/build_tyndp_hydro_profile.py b/scripts/sb/build_tyndp_hydro_profile.py index a7e998fe4d..2b9acee2a0 100644 --- a/scripts/sb/build_tyndp_hydro_profile.py +++ b/scripts/sb/build_tyndp_hydro_profile.py @@ -7,7 +7,7 @@ Outputs ------- -- ``resources/profile_pemmdb_hydro.nc``: +- `resources/profile_pemmdb_hydro.nc`: =================== ================ ========================================================= Field Dimensions Description diff --git a/scripts/sb/build_tyndp_offshore_hubs.py b/scripts/sb/build_tyndp_offshore_hubs.py index bec28c101c..9bf214c247 100644 --- a/scripts/sb/build_tyndp_offshore_hubs.py +++ b/scripts/sb/build_tyndp_offshore_hubs.py @@ -96,7 +96,7 @@ def load_offshore_grid( countries : list[str] List of country codes used to clean data. max_capacity : dict[str, int] - Maximum transmission capacity between two offshore hubs per carrier + Maximum transmission capacity between two offshore hubs per carrier. h2_zones_tyndp : bool Whether to use TYNDP hydrogen zones splitting. @@ -430,10 +430,10 @@ def load_offshore_generators( Returns ------- generators : pd.DataFrame - DataFrame containing the formatted offshore generators data + DataFrame containing the formatted offshore generators data. zone_trajectories : pd.DataFrame - DataFrame containing the zone potentials trajectories + DataFrame containing the zone potentials trajectories. """ column_names = { "NODE": "bus", diff --git a/scripts/sb/build_tyndp_trajectories.py b/scripts/sb/build_tyndp_trajectories.py index 457e607c7c..c41e8c3f42 100644 --- a/scripts/sb/build_tyndp_trajectories.py +++ b/scripts/sb/build_tyndp_trajectories.py @@ -8,7 +8,7 @@ ------- Cleaned CSV file with all TYNDP trajectories (`p_nom_min`, `p_nom_max`) in long format. -- ``resources/tyndp_trajectories.csv`` in long format. +- `resources/tyndp_trajectories.csv` in long format. """ import logging diff --git a/scripts/sb/build_tyndp_transmission_projects.py b/scripts/sb/build_tyndp_transmission_projects.py index d37ebbc5a0..53cef57132 100644 --- a/scripts/sb/build_tyndp_transmission_projects.py +++ b/scripts/sb/build_tyndp_transmission_projects.py @@ -15,18 +15,18 @@ Inputs ------ -- ``data/tyndp_2024_bundle/Investment Datasets/GRID.xlsx``: Grid investment dataset +- `data/tyndp_2024_bundle/Investment Datasets/GRID.xlsx`: Grid investment dataset containing electricity and hydrogen transmission projects with capacities and commissioning years. -- ``resources/tyndp/build/geojson/buses.geojson``: Electrical buses with geometry. -- ``resources/tyndp/build/geojson/buses_h2.geojson``: Hydrogen buses with geometry. +- `resources/tyndp/build/geojson/buses.geojson`: Electrical buses with geometry. +- `resources/tyndp/build/geojson/buses_h2.geojson`: Hydrogen buses with geometry. Outputs ------- -- ``resources/tyndp/new_links_{planning_horizons}.csv``: Processed electricity transmission projects with bus +- `resources/tyndp/new_links_{planning_horizons}.csv`: Processed electricity transmission projects with bus connections, capacities (p_nom), lengths, and geometry attributes ready for network integration. -- ``resources/tyndp/new_links_h2_{planning_horizons}.csv``: Processed hydrogen transmission projects with bus +- `resources/tyndp/new_links_h2_{planning_horizons}.csv`: Processed hydrogen transmission projects with bus connections and capacities (p_nom) attributes ready for network integration. """ diff --git a/scripts/sb/clean_tyndp_h2_imports.py b/scripts/sb/clean_tyndp_h2_imports.py index 66c9c30843..85c2484175 100644 --- a/scripts/sb/clean_tyndp_h2_imports.py +++ b/scripts/sb/clean_tyndp_h2_imports.py @@ -28,15 +28,15 @@ def match_centroids(df, countries_centroids): Parameters ---------- - df : pd:DataFrame - Dataframe containing import data with bus0 as import nodes + df : pd.DataFrame + DataFrame containing import data with bus0 as import nodes. countries_centroids : gpd.GeoDataFrame - GeoDataFrame containing country centroid information as geometry + GeoDataFrame containing country centroid information as geometry. Returns ------- pd.DataFrame - The function returns the input Dataframe df with matched coordinates inside new columns bus0_x and bus0_y + Input DataFrame with matched coordinates in new columns bus0_x and bus0_y. """ import_nodes = df.bus0.unique() @@ -69,18 +69,19 @@ def match_centroids(df, countries_centroids): def load_import_data(fn, countries_centroids): """ - Load and clean TYNDP H2 import potentials, maximum capacity, offer quantity and marginal cost for pipeline and shipping - Returns the cleaned data as dataframe. + Load and clean TYNDP H2 import potentials, maximum capacity, offer quantity and marginal cost for pipeline and shipping. Parameters ---------- fn : str Path to Excel file containing TYNDP H2 imports data. + countries_centroids : gpd.GeoDataFrame + GeoDataFrame containing country centroid information as geometry. Returns ------- pd.DataFrame - The function returns cleaned TYNDP H2 import potentials, maximum capacity, offer quantity and marginal cost. + Cleaned TYNDP H2 import potentials, maximum capacity, offer quantity and marginal cost. """ column_dict = { diff --git a/scripts/sb/clean_tyndp_h2_storages.py b/scripts/sb/clean_tyndp_h2_storages.py index e821d3ad79..a96e28ffa1 100644 --- a/scripts/sb/clean_tyndp_h2_storages.py +++ b/scripts/sb/clean_tyndp_h2_storages.py @@ -39,7 +39,7 @@ def load_h2_storage_data( Returns ------- pd.DataFrame - The function returns cleaned TYNDP H2 storage data. + Cleaned TYNDP H2 storage data. """ column_dict = { diff --git a/scripts/sb/clean_tyndp_output_benchmark.py b/scripts/sb/clean_tyndp_output_benchmark.py index 1eb2e03d48..d16d35bffb 100644 --- a/scripts/sb/clean_tyndp_output_benchmark.py +++ b/scripts/sb/clean_tyndp_output_benchmark.py @@ -204,7 +204,7 @@ def load_MM_sheet( table_name : str Name of the table from LOOKUP_TABLES (e.g., "power_capacity"). countries : list[str] - List of modelled countries + List of modelled countries. eu27 : list List of EU27 country codes. mapping : dict[str, dict[str, str]] @@ -505,7 +505,7 @@ def clean_h2_imports_for_benchmarking( Returns ------- pd.DataFrame - dataFrame with columns [carrier, bus, unit, table, value] for each importing country and an EU27 aggregated row. + DataFrame with columns [carrier, bus, unit, table, value] for each importing country and an EU27 aggregated row. """ df = ( crossborder_h2.loc[["bus0", "bus1", "sum"]] diff --git a/scripts/sb/clean_tyndp_report_benchmark.py b/scripts/sb/clean_tyndp_report_benchmark.py index 09653d07da..45c56fcbad 100644 --- a/scripts/sb/clean_tyndp_report_benchmark.py +++ b/scripts/sb/clean_tyndp_report_benchmark.py @@ -137,16 +137,16 @@ def _add_identifier(s: str) -> str: def add_report_carrier_mappings(carrier_mapping_fn: str, tables: dict) -> None: """ Load report carrier mappings from the carrier mapping file and apply them - to the ``mapping`` configuration dictionary of each table. + to the `mapping` configuration dictionary of each table. Parameters ---------- carrier_mapping_fn : str Path to csv file with carrier mapping. tables : dict - Dictionary defining the benchmarking tables. When the ``mapping_col`` key is + Dictionary defining the benchmarking tables. When the `mapping_col` key is defined in the configuration, the loaded carrier mapping will be added to - the dictionary with the ``mapping`` key. + the dictionary with the `mapping` key. Returns ------- @@ -193,9 +193,9 @@ def clean_data_for_benchmarking( Parameters ---------- table : str - Benchmarking table name + Benchmarking table name. df : pd.DataFrame - Dataframe containing report values for benchmarking + DataFrame containing report values for benchmarking. mapping : dict Carrier mapping from report carrier names to benchmarking carrier names. diff --git a/scripts/sb/clean_tyndp_smr.py b/scripts/sb/clean_tyndp_smr.py index d168810629..44002f8fdf 100644 --- a/scripts/sb/clean_tyndp_smr.py +++ b/scripts/sb/clean_tyndp_smr.py @@ -33,14 +33,14 @@ def load_smr_data( pyear : int Planning horizon to read SMR data for. h2_zones_tyndp : bool - Whether TYNDP H2 nodes are split into two zones (Z1, Z2) + Whether TYNDP H2 nodes are split into two zones (Z1, Z2). scenario : str TYNDP scenario to filter for. Returns ------- pd.DataFrame - The function returns cleaned TYNDP SMR data with capacity, must run and CCS information. + Cleaned TYNDP SMR data with capacity, must run and CCS information. """ column_dict = { diff --git a/scripts/sb/group_tyndp_conventionals.py b/scripts/sb/group_tyndp_conventionals.py index 589757037a..d07e23102d 100644 --- a/scripts/sb/group_tyndp_conventionals.py +++ b/scripts/sb/group_tyndp_conventionals.py @@ -12,16 +12,16 @@ Inputs ------ -- ``pemmdb_capacities_{planning_horizon}.csv``: Processed PEMMDB capacities for the given planning_horizon. -- ``pemmdb_profiles_{planning_horizon}.nc``: Processed PEMMDB must-run and availability profiles for the given +- `pemmdb_capacities_{planning_horizon}.csv`: Processed PEMMDB capacities for the given planning_horizon. +- `pemmdb_profiles_{planning_horizon}.nc`: Processed PEMMDB must-run and availability profiles for the given planning_horizon. -- ``data/tyndp_technology_map.csv``: TYNDP technology mapping used for the grouping. +- `data/tyndp_technology_map.csv`: TYNDP technology mapping used for the grouping. Outputs ------- -- ``pemmdb_capacities_{planning_horizon}_grouped.csv``: Grouped PEMMDB capacities for the given planning_horizon. -- ``pemmdb_profiles_{planning_horizon}_grouped.nc``: Grouped PEMMDB must-run and availability profiles for the given +- `pemmdb_capacities_{planning_horizon}_grouped.csv`: Grouped PEMMDB capacities for the given planning_horizon. +- `pemmdb_profiles_{planning_horizon}_grouped.nc`: Grouped PEMMDB must-run and availability profiles for the given planning_horizon. """ @@ -171,7 +171,7 @@ def group_tyndp_conventionals( pemmdb_profiles : pd.DataFrame All PEMMDB must-run and availability profiles. tyndp_conventional_carriers : list[str] - List of TYNDP conventional carriers to group + List of TYNDP conventional carriers to group. Returns ------- diff --git a/scripts/sb/make_benchmark.py b/scripts/sb/make_benchmark.py index 1f533712fc..17b8f01b65 100644 --- a/scripts/sb/make_benchmark.py +++ b/scripts/sb/make_benchmark.py @@ -50,14 +50,14 @@ def load_data( Path to the Open-TYNDP results data file. scenario : str Name of scenario to compare. - vp_data_fn : str (optional) + vp_data_fn : str, optional Path to the Visualisation data file. - mm_data_fn : str (optional) + mm_data_fn : str, optional Path to the Market Model Output data file. Returns ------- - benchmarks_raw : pd.DataFrame + pd.DataFrame Combined DataFrame containing both Open-TYNDP and TYNDP 2024 data. """ @@ -341,9 +341,9 @@ def compute_all_indicators( Column name for model/projected values (ŷᵢ). rfc_col : str, default "TYNDP 2024 Scenarios Report" Column name for reference/actual values (yᵢ). - eps: float, default 1e-6 + eps : float, default 1e-6 Small value used when the denominator is zero. - carrier: str, default None + carrier : str, default None Name of the carrier for indicator calculation. If None, calculates overall table indicator. df_na : pd.DataFrame, default pd.DataFrame() DataFrame with missing values for missing carrier calculation. @@ -451,9 +451,9 @@ def compute_indicators( Returns ------- pd.DataFrame - DataFrame with per carriers accuracy indicators. + DataFrame with per carriers accuracy indicators. pd.Series - Series containing overall accuracy indicators. + Series containing overall accuracy indicators. """ opt = options["tables"][table] missing_name = "Missing countries" if bus_col_name != "bus" else "Missing buses" @@ -543,8 +543,6 @@ def compare_sources( ---------- table : str Benchmark metric to compute. - bus : str - Bus of the current figure. benchmarks_raw : pd.DataFrame Combined DataFrame containing both Open-TYNDP and TYNDP 2024 data. scenario : str @@ -561,9 +559,9 @@ def compare_sources( Returns ------- pd.DataFrame - DataFrame containing original data with appended multi-value accuracy metric columns. + DataFrame containing original data with appended multi-value accuracy metric columns. pd.Series - Series containing single-value accuracy metrics. + Series containing single-value accuracy metrics. """ # Parameters opt = options["tables"][table] @@ -658,7 +656,7 @@ def compute_overall_accuracy( Parameters ---------- - benchmarks_raw: pd.DataFrame + benchmarks_raw : pd.DataFrame Combined DataFrame containing both Open-TYNDP and TYNDP 2024 data. options : dict Full benchmarking configuration. @@ -668,7 +666,7 @@ def compute_overall_accuracy( Returns ------- pd.Series - Series containing overall accuracy metrics. + Series containing overall accuracy metrics. """ logger.info("Making global benchmark using TYNDP 2024 and Open-TYNDP") tables_series = [ # noqa: F841 diff --git a/scripts/sb/plot_benchmark.py b/scripts/sb/plot_benchmark.py index 978058c646..90c2e160af 100644 --- a/scripts/sb/plot_benchmark.py +++ b/scripts/sb/plot_benchmark.py @@ -503,11 +503,11 @@ def plot_benchmark( Benchmark table to plot. bus : str Bus of the current figure. - benchmarks: pd.DataFrame + benchmarks : pd.DataFrame Combined DataFrame containing both model and reference data. - output_dir: str + output_dir : str Output directory. - scenario: str + scenario : str Scenario name. snapshots : dict[str, str] Dictionary defining the temporal range with 'start' and 'end' keys. diff --git a/scripts/sb/plot_offshore_network.py b/scripts/sb/plot_offshore_network.py index fedbc55269..3f70bed60a 100644 --- a/scripts/sb/plot_offshore_network.py +++ b/scripts/sb/plot_offshore_network.py @@ -53,9 +53,9 @@ def plot_offshore_map( map_fn : str Path to save the final map plot to. planning_horizons : int - The planning horizon year + The planning horizon year. carrier : str, optional - Carrier to plot + Carrier to plot. p_nom : str | float, optional Nominal power parameter for determining link thickness. If str, must be "p_nom" or "p_nom_opt". If float, uses fixed value for all links. Defaults to plotting only base network (p_nom). diff --git a/scripts/solve_network.py b/scripts/solve_network.py index 41c9b186b6..3ecd415e51 100644 --- a/scripts/solve_network.py +++ b/scripts/solve_network.py @@ -1256,13 +1256,13 @@ def add_offshore_hubs_constraint( Parameters ---------- n : pypsa.Network - The PyPSA network instance - planning_horizons : int, optional - The current planning horizon year or None in perfect foresight - offshore_zone_trajectories_fn: str - Path to the file containing the offshore zone potentials trajectories - renewable_carriers_tyndp : list[str], optional - List of TYNDP renewable carriers + The PyPSA network instance. + planning_horizons : int + The current planning horizon year. + offshore_zone_trajectories_fn : str + Path to the file containing the offshore zone potentials trajectories. + renewable_carriers_tyndp : list[str] + List of TYNDP renewable carriers. """ ext_i = n.generators.p_nom_extendable gens = n.generators.assign( diff --git a/scripts/temporal_aggregation.py b/scripts/temporal_aggregation.py index 3ba25b453e..a21ab70180 100644 --- a/scripts/temporal_aggregation.py +++ b/scripts/temporal_aggregation.py @@ -7,8 +7,8 @@ Description ----------- -Reads the snapshot weightings from the CSV file prepared in ``build_snapshot_weightings`` -and applies it on the time-varying network data prepared in ``prepare_sector_network.py``. +Reads the snapshot weightings from the CSV file prepared in `build_snapshot_weightings` +and applies it on the time-varying network data prepared in `prepare_sector_network.py`. """ import logging From 5b926580b076a99df155d2cd45be93c1c586b161 Mon Sep 17 00:00:00 2001 From: Thomas Gilon Date: Fri, 19 Jun 2026 14:10:52 +0200 Subject: [PATCH 02/12] doc: add missing defaults --- scripts/add_brownfield.py | 20 ++++---- scripts/cba/clean_projects.py | 2 +- scripts/cba/clean_tyndp_indicators.py | 18 ++++++- scripts/cba/make_indicators.py | 17 +++++-- scripts/cba/plot_benchmark_indicators.py | 48 ++++++++++++++++-- scripts/cba/plot_indicators.py | 58 +++++++++++++++++++++- scripts/cba/summarize_all.py | 35 ++++++++++++- scripts/cba/summarize_indicators.py | 35 ++++++++++++- scripts/prepare_sector_network.py | 16 +++--- scripts/sb/build_pemmdb_data.py | 3 +- scripts/sb/build_tyndp_h2_demand.py | 20 +++++++- scripts/sb/clean_tyndp_output_benchmark.py | 17 +++++++ scripts/sb/clean_tyndp_report_benchmark.py | 3 +- scripts/sb/make_benchmark.py | 21 ++++++-- scripts/sb/plot_base_hydrogen_network.py | 2 +- scripts/solve_network.py | 13 +++-- 16 files changed, 283 insertions(+), 45 deletions(-) diff --git a/scripts/add_brownfield.py b/scripts/add_brownfield.py index 59daca3508..e39fa2a314 100644 --- a/scripts/add_brownfield.py +++ b/scripts/add_brownfield.py @@ -50,16 +50,16 @@ def add_brownfield( Previous network to get brownfield from. year : int Planning year. - h2_retrofit : bool - Whether to allow hydrogen pipeline retrofitting. - h2_retrofit_capacity_per_ch4 : float - Ratio of hydrogen to methane capacity for pipeline retrofitting. - capacity_threshold : float - Threshold for removing assets with low capacity. - offshore_hubs_tyndp : bool - Whether to enable offshore hubs. - h2_topology_tyndp : bool - Whether to enable TYNDP Hydrogen topology. + h2_retrofit : bool, optional + Whether to allow hydrogen pipeline retrofitting. Default is False. + h2_retrofit_capacity_per_ch4 : float, optional + Ratio of hydrogen to methane capacity for pipeline retrofitting. Default is None. + capacity_threshold : float, optional + Threshold for removing assets with low capacity. Default is None. + offshore_hubs_tyndp : bool, optional + Whether to enable offshore hubs. Default is False. + h2_topology_tyndp : bool, optional + Whether to enable TYNDP Hydrogen topology. Default is False. carriers_tyndp : list[str] List of TYNDP carriers included in the model. """ diff --git a/scripts/cba/clean_projects.py b/scripts/cba/clean_projects.py index ba1ef5a297..4c9330290d 100644 --- a/scripts/cba/clean_projects.py +++ b/scripts/cba/clean_projects.py @@ -260,7 +260,7 @@ def read_tyndp_electricity_buses(buses_fn: str): Returns ------- - - buses: Index of electricity buses as used in open tyndp + - buses: Index of electricity buses as used in Open-TYNDP See Also -------- diff --git a/scripts/cba/clean_tyndp_indicators.py b/scripts/cba/clean_tyndp_indicators.py index 6b00088aa3..bdee8c96ae 100644 --- a/scripts/cba/clean_tyndp_indicators.py +++ b/scripts/cba/clean_tyndp_indicators.py @@ -47,8 +47,22 @@ def normalize_text( """ Normalize text for parsing the TYNDP Excel data. - Strips whitespace, remove Delta symbol, replace Euro symbols with 'euro', - standardizes spelling. + Strips whitespace, removes Delta symbol, replaces Euro symbols with 'euro', + and standardizes spelling. + + Parameters + ---------- + value : str + Input text to normalize. + drop_spaces : bool, optional + If True, remove all spaces from the result. Default is False. + monetised : bool, optional + If True, replace "monetized" with "monetised". Default is False. + + Returns + ------- + str + Normalized text string. """ text = ( str(value) diff --git a/scripts/cba/make_indicators.py b/scripts/cba/make_indicators.py index b895415119..fac19c207c 100644 --- a/scripts/cba/make_indicators.py +++ b/scripts/cba/make_indicators.py @@ -104,7 +104,7 @@ def calculate_total_system_cost(n, remove_noisy_costs: bool = False): n : pypsa.Network PyPSA network (must be solved). remove_noisy_costs : bool, optional - Whether to remove noisy costs before calculation. + Whether to remove noisy costs before calculation. Default is False. Returns ------- @@ -240,7 +240,7 @@ def get_ac_energy_balance( assets : pandas.Series Assets for which to calculate energy balance. bus_carrier : str, optional - If set, filter energy balance to this bus carrier (e.g. "co2"). + If set, filter energy balance to this bus carrier (e.g. "co2"). Default is None. """ balance = n.statistics.energy_balance( groupby_time=False, @@ -269,6 +269,7 @@ def calculate_power_sector_co2_emissions( Pre-filtered Series of electricity-producing assets on AC buses. If not provided, it will be computed within the function. However, calculating it before calling this function can improve performance. + Default is None. Returns ------- @@ -431,9 +432,9 @@ def calculate_b1_indicator( n_project : pypsa.Network Project network. method : str, optional - Either "pint" or "toot". + Either "pint" or "toot". Default is "pint". remove_noisy_costs : bool, optional - Whether to remove noisy costs before calculation. + Whether to remove noisy costs before calculation. Default is False. Returns ------- @@ -535,10 +536,12 @@ def calculate_b2_indicator( Pre-filtered Series of electricity-producing assets on AC buses for the reference network. If not provided, it will be computed within the function. Providing these can improve performance by avoiding redundant calculations. + Default is None. ac_assets_project : pandas.Series, optional Pre-filtered Series of electricity-producing assets on AC buses for the project network. If not provided, it will be computed within the function. Providing these can improve performance by avoiding redundant calculations. + Default is None. Returns ------- @@ -666,6 +669,12 @@ def calculate_b4_indicator( emission_factors : pd.DataFrame DataFrame with non-CO2 emission factors (kg/MWh) indexed by carrier and with columns for different pollutants and statistics (min, mean, max). + ac_assets_reference : pandas.Series, optional + Pre-filtered Series of electricity-producing assets on AC buses for the reference network. + Default is None. + ac_assets_project : pandas.Series, optional + Pre-filtered Series of electricity-producing assets on AC buses for the project network. + Default is None. Returns ------- diff --git a/scripts/cba/plot_benchmark_indicators.py b/scripts/cba/plot_benchmark_indicators.py index b99fe60c82..7ab39aab71 100644 --- a/scripts/cba/plot_benchmark_indicators.py +++ b/scripts/cba/plot_benchmark_indicators.py @@ -53,7 +53,23 @@ def select_value_by_subindex( def benchmark_range( df: pd.DataFrame, indicator: str, source: str = "TYNDP 2024" ) -> tuple[float, float, float] | None: - """Return (min, mean, max) range for a benchmark indicator.""" + """ + Return (min, mean, max) range for a benchmark indicator. + + Parameters + ---------- + df : pd.DataFrame + DataFrame containing benchmark indicator data. + indicator : str + Indicator key. + source : str, optional + Data source label to filter on. Default is "TYNDP 2024". + + Returns + ------- + tuple[float, float, float] or None + (min, mean, max) values for the indicator, or None if no data. + """ benchmark = df[(df["source"] == source) & (df["indicator"] == indicator)].copy() benchmark.subindex = benchmark.subindex.fillna("explicit") if benchmark.empty: @@ -115,7 +131,20 @@ def plot_project_benchmarks( project_label: str | None = None, area_subtitle: str | None = None, ) -> None: - """Plot one subplot per indicator with its own y-axis and legend.""" + """ + Plot one subplot per indicator with its own y-axis and legend. + + Parameters + ---------- + df : pd.DataFrame + DataFrame containing indicator data for one project. + output_path : Path + File path where the plot is saved. + project_label : str, optional + Label shown in the plot title. Default is None (no label). + area_subtitle : str, optional + Subtitle describing the spatial scope of the assessment. Default is None. + """ indicators = sorted(df["indicator"].dropna().unique()) if not indicators: logger.info("No benchmark indicators available to plot") @@ -324,7 +353,20 @@ def plot_project_benchmarks( def create_plots(indicators_file, output_path, planning_horizon=None, area=None): - """Create benchmark plots from a per-project or collected indicators file.""" + """ + Create benchmark plots from a per-project or collected indicators file. + + Parameters + ---------- + indicators_file : str or Path + Path to the CSV file containing indicator data. + output_path : str or Path + Output file path or directory for the generated plots. + planning_horizon : int or str, optional + Planning horizon year used to label project. Default is None. + area : str, optional + Spatial scope identifier passed to format_area_subtitle. Default is None. + """ output_path = Path(output_path) output_dir = output_path if output_path.suffix == "" else output_path.parent output_dir.mkdir(parents=True, exist_ok=True) diff --git a/scripts/cba/plot_indicators.py b/scripts/cba/plot_indicators.py index d26de94eea..1ee1d54349 100644 --- a/scripts/cba/plot_indicators.py +++ b/scripts/cba/plot_indicators.py @@ -111,6 +111,23 @@ def plot_b1_top_projects( - CAPEX change extending left from zero (negative = cost saved) - OPEX change extending right from zero (positive = additional cost) - Diamond marker showing the net B1 value + + Parameters + ---------- + df : pd.DataFrame + DataFrame with B1 results per project. + output_dir : str or Path + Directory where the plot file is saved. + method : str + CBA method ("pint" or "toot"). + colors : dict + Color mapping. + output_formats : list[str] + File formats to save. + n_top : int, optional + Number of top projects to display. Default is 20. + filename_suffix : str, optional + Suffix appended to the output filename. Default is "". """ df_top = df.nlargest(n_top, "B1_billion_EUR", keep="first") df_sorted = df_top.sort_values("B1_billion_EUR", ascending=True).reset_index( @@ -251,7 +268,26 @@ def plot_b1_top_projects( def plot_b1_summary( df, output_dir, method, colors, output_formats, total_projects, filename_suffix="" ): - """Summary plot with B1 histogram.""" + """ + Summary histogram of B1 values across all projects. + + Parameters + ---------- + df : pd.DataFrame + DataFrame with B1 results per project. + output_dir : str or Path + Directory where the plot file is saved. + method : str + CBA method ("pint" or "toot"). + colors : dict + Color mapping. + output_formats : list[str] + File formats to save. + total_projects : int + Total number of projects. + filename_suffix : str, optional + Suffix appended to the output filename. Default is "". + """ beneficial = df[df["is_beneficial"] == True] not_beneficial = df[df["is_beneficial"] == False] @@ -299,7 +335,25 @@ def plot_b1_summary( def plot_b1_capex_vs_opex( df, output_dir, method, colors, output_formats, filename_suffix="" ): - """Scatter plot of B1 CAPEX vs OPEX changes.""" + """ + Scatter plot of B1 CAPEX vs OPEX changes per project. + + Parameters + ---------- + df : pd.DataFrame + DataFrame with B1 results per project including capex_change_billion + and opex_change_billion columns. + output_dir : str or Path + Directory where the plot file is saved. + method : str + CBA method ("pint" or "toot"). + colors : dict + Color mapping. + output_formats : list[str] + File formats to save. + filename_suffix : str, optional + Suffix appended to the output filename. Default is "". + """ fig, ax = plt.subplots(figsize=(10, 8)) beneficial = df[df["is_beneficial"]] diff --git a/scripts/cba/summarize_all.py b/scripts/cba/summarize_all.py index e445154821..952df0744f 100644 --- a/scripts/cba/summarize_all.py +++ b/scripts/cba/summarize_all.py @@ -70,7 +70,25 @@ def benchmark_range( source: str = "TYNDP 2024", planning_horizon: int = 0, ) -> tuple[float, float, float] | None: - """Return (min, mean, max) range for a benchmark indicator.""" + """ + Return (min, mean, max) range for a benchmark indicator. + + Parameters + ---------- + df : pd.DataFrame + DataFrame containing benchmark indicator data. + indicator : str + Indicator key to look up. + source : str, optional + Data source label to filter on. Default is "TYNDP 2024". + planning_horizon : int, optional + Planning horizon year to filter on. Default is 0. + + Returns + ------- + tuple[float, float, float] or None + (min, mean, max) values for the indicator, or None if no data. + """ benchmark = df[ (df["source"] == source) & (df["indicator"] == indicator) @@ -128,7 +146,20 @@ def create_plots( planning_horizons=None, area: str = None, ): - """Create benchmark plot from all collected indicator files.""" + """ + Create benchmark plot from all collected indicator files. + + Parameters + ---------- + df : pd.DataFrame + Combined DataFrame with all indicator data. + output_file : str + Output file path for the generated plot. + planning_horizons : list, optional + Planning horizons to include. Default is None (derived from df). + area : str, optional + Spatial scope identifier passed to format_area_subtitle. Default is None. + """ # if df.empty: diff --git a/scripts/cba/summarize_indicators.py b/scripts/cba/summarize_indicators.py index 1b6f235486..ea8a491881 100644 --- a/scripts/cba/summarize_indicators.py +++ b/scripts/cba/summarize_indicators.py @@ -79,7 +79,25 @@ def benchmark_range( source: str = "TYNDP 2024", planning_horizon: int = 0, ) -> tuple[float, float, float] | None: - """Return (min, mean, max) range for a benchmark indicator.""" + """ + Return (min, mean, max) range for a benchmark indicator. + + Parameters + ---------- + df : pd.DataFrame + DataFrame containing benchmark indicator data. + indicator : str + Indicator key to look up. + source : str, optional + Data source label to filter on. Default is "TYNDP 2024". + planning_horizon : int, optional + Planning horizons to filter on. Default is 0. + + Returns + ------- + tuple[float, float, float] or None + (min, mean, max) values for the indicator, or None if no data. + """ benchmark = df[ (df["source"] == source) & (df["indicator"] == indicator) @@ -137,7 +155,20 @@ def plot_project_benchmarks( project_label: str | None = None, area_subtitle: str | None = None, ) -> None: - """Plot one subplot per indicator with its own y-axis and legend.""" + """ + Plot one subplot per indicator with its own y-axis and legend. + + Parameters + ---------- + df : pd.DataFrame + DataFrame containing indicator data for one project. + output_path : Path + File path where the plot is saved. + project_label : str, optional + Label shown in the plot title. Default is None (no label). + area_subtitle : str, optional + Subtitle describing the spatial scope of the assessment. Default is None. + """ indicators = sorted(df["indicator"].dropna().unique()) if not indicators: logger.info("No benchmark indicators available to plot") diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index 0ed88c82fc..7d46229e32 100755 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -131,10 +131,12 @@ def define_spatial( - methanol : dict - regional_oil_demand : bool - regional_coal_demand : bool - buses_h2_file : str - Path to the file containing TYNDP H2 buses information. - offshore_buses_fn : str - Path to the file containing offshore bus data. + offshore_buses_fn : str, optional + Path to the file containing offshore bus data. Default is None. + buses_h2_file : str, optional + Path to the file containing TYNDP H2 buses information. Default is None. + tyndp_scenario : str, optional + TYNDP scenario name. Default is None. """ spatial.nodes = nodes @@ -5345,10 +5347,10 @@ def attach_gas_load( - gas_demand_exogenously costs : pd.DataFrame Technology costs assumptions. - spatial : object, optional + spatial : object Object containing spatial information about nodes and their locations. - nhours : int - Number of hours over which the annual gas demand is divided. + nhours : int, optional + Number of hours over which the annual gas demand is divided. Default is 8760. """ gas_demand = pd.read_csv(gas_demand_fn, index_col=0) / nhours diff --git a/scripts/sb/build_pemmdb_data.py b/scripts/sb/build_pemmdb_data.py index a8bae81517..0e99395045 100644 --- a/scripts/sb/build_pemmdb_data.py +++ b/scripts/sb/build_pemmdb_data.py @@ -106,7 +106,8 @@ def read_pemmdb_data( pyear : int Planning year used for data retrieval (fallback year if pyear_i not available). required_sheets : list[str], optional - List of required technology sheets to read PEMMDB data for. + List of required technology sheets to read PEMMDB data for. Default is None, + which reads all available sheets. Returns ------- diff --git a/scripts/sb/build_tyndp_h2_demand.py b/scripts/sb/build_tyndp_h2_demand.py index d2f8459734..7368ae7563 100644 --- a/scripts/sb/build_tyndp_h2_demand.py +++ b/scripts/sb/build_tyndp_h2_demand.py @@ -139,7 +139,25 @@ def read_h2_excel( def get_file_path(fn: str, scenario: str, pyear: int, h2_zone: int = None) -> Path: - """Construct file path for given planning year and zone.""" + """ + Construct file path for given planning year and zone. + + Parameters + ---------- + fn : str + Base directory path containing scenario subdirectories. + scenario : str + Scenario name. + pyear : int + Planning year. + h2_zone : int, optional + H2 zone identifier required for "DE" and "GA" scenarios. Default is None. + + Returns + ------- + Path + Path to the H2 demand Excel file for the given scenario and year. + """ if scenario == "NT": return Path( diff --git a/scripts/sb/clean_tyndp_output_benchmark.py b/scripts/sb/clean_tyndp_output_benchmark.py index d16d35bffb..53ecff89b7 100644 --- a/scripts/sb/clean_tyndp_output_benchmark.py +++ b/scripts/sb/clean_tyndp_output_benchmark.py @@ -123,6 +123,23 @@ def load_crossborder_sheet( filepath: str | Path, skiprows: int = 5, ) -> pd.DataFrame: + """ + Load the cross-border flow sheet from a TYNDP Market Model output file. + + Parameters + ---------- + sheet_name : str + Name of the Excel sheet to read. + filepath : str or Path + Path to the Excel file. + skiprows : int, optional + Number of header rows to skip. Default is 5. + + Returns + ------- + pd.DataFrame + DataFrame with normalized cross-border flow data. + """ df = pd.read_excel( filepath, sheet_name=sheet_name, diff --git a/scripts/sb/clean_tyndp_report_benchmark.py b/scripts/sb/clean_tyndp_report_benchmark.py index 45c56fcbad..b57384e573 100644 --- a/scripts/sb/clean_tyndp_report_benchmark.py +++ b/scripts/sb/clean_tyndp_report_benchmark.py @@ -196,8 +196,9 @@ def clean_data_for_benchmarking( Benchmarking table name. df : pd.DataFrame DataFrame containing report values for benchmarking. - mapping : dict + mapping : dict, optional Carrier mapping from report carrier names to benchmarking carrier names. + Default is {} (no renaming applied). Returns ------- diff --git a/scripts/sb/make_benchmark.py b/scripts/sb/make_benchmark.py index 17b8f01b65..53fd8b89a1 100644 --- a/scripts/sb/make_benchmark.py +++ b/scripts/sb/make_benchmark.py @@ -310,7 +310,20 @@ def _compute_growth_rate(values: pd.Series) -> float: def _compute_missing(df_na: pd.DataFrame, cols: str | list[str] = "carrier") -> int: """ - Calculate missing count, by default, using carriers. + Calculate missing count by unique values of the specified column(s). + + Parameters + ---------- + df_na : pd.DataFrame + DataFrame containing rows with missing values. + cols : str or list[str], optional + Column(s) to use for deduplication when counting missing items. + Default is "carrier". + + Returns + ------- + int + Number of unique missing entries. """ if isinstance(cols, str): cols = [cols] @@ -660,8 +673,10 @@ def compute_overall_accuracy( Combined DataFrame containing both Open-TYNDP and TYNDP 2024 data. options : dict Full benchmarking configuration. - bus_col_name : str, default "bus" - Bus column name. + bus_col_name : str, optional + Bus column name. Default is "bus". + model_col : str, optional + Column name identifying Open-TYNDP model results. Default is "Open-TYNDP". Returns ------- diff --git a/scripts/sb/plot_base_hydrogen_network.py b/scripts/sb/plot_base_hydrogen_network.py index 3fc4d92602..a1962b4970 100644 --- a/scripts/sb/plot_base_hydrogen_network.py +++ b/scripts/sb/plot_base_hydrogen_network.py @@ -62,7 +62,7 @@ def plot_h2_map_base( Whether to plot expanded capacities. Defaults to plotting only base network (p_nom). regions_for_storage : gpd.GeoDataframe, optional Geodataframe of regions to use for plotting hydrogen storage capacities. Index needs to match storage locations. - If none is given, no hydrogen storage capacities are plotted. + Default is None (no hydrogen storage capacities are plotted). Returns ------- diff --git a/scripts/solve_network.py b/scripts/solve_network.py index 3ecd415e51..5f3eae19f6 100644 --- a/scripts/solve_network.py +++ b/scripts/solve_network.py @@ -519,6 +519,7 @@ def prepare_network( config : dict, default None A dictionary containing configuration information, specifically the "plotting" key with "nice_names" and "tech_colors" keys for carriers. + Default is None. Returns ------- @@ -1429,10 +1430,11 @@ def extra_functionality( Simulation timesteps planning_horizons : str, optional The current planning horizon year or None in perfect foresight. - offshore_zone_trajectories_fn: str, optional - Path to the file containing the offshore zone potentials trajectories + offshore_zone_trajectories_fn : str, optional + Path to the file containing the offshore zone potentials trajectories. + Default is None. renewable_carriers_tyndp : list[str], optional - List of TYNDP renewable carriers + List of TYNDP renewable carriers. Default is []. Notes ----- @@ -1674,9 +1676,10 @@ def create_optimization_model( planning_horizons : str, optional The current planning horizon year or None in perfect foresight offshore_zone_trajectories_fn : str, optional - Path to DataFrame containing the offshore zone potentials trajectories + Path to DataFrame containing the offshore zone potentials trajectories. + Default is None. renewable_carriers_tyndp : list[str], optional - List of TYNDP renewable carriers + List of TYNDP renewable carriers. Default is []. """ # Add config and params to network for extra_functionality n.config = config From 0353021725695a4675c670cd7e2f37c3c5fcc398 Mon Sep 17 00:00:00 2001 From: Thomas Gilon Date: Fri, 19 Jun 2026 15:13:42 +0200 Subject: [PATCH 03/12] doc: update typehints --- scripts/cba/average_indicators.py | 8 ++- scripts/cba/collect_indicators.py | 2 +- scripts/cba/make_indicators.py | 35 +++++++------ scripts/cba/plot_benchmark_indicators.py | 15 ++++-- scripts/cba/plot_indicators.py | 29 +++++++--- scripts/cba/prepare_rolling_horizon.py | 8 +-- scripts/cba/simplify_sb_network.py | 4 +- scripts/cba/solve_cba_network.py | 6 +-- scripts/cba/summarize_all.py | 8 +-- scripts/cba/summarize_indicators.py | 6 +-- scripts/prepare_sector_network.py | 67 ++++++++++++++++-------- scripts/sb/build_statistics.py | 6 +-- scripts/sb/build_tyndp_h2_network.py | 4 +- scripts/sb/build_tyndp_offshore_hubs.py | 12 +++-- scripts/sb/clean_tyndp_h2_imports.py | 6 ++- scripts/sb/plot_offshore_network.py | 23 ++++---- scripts/solve_network.py | 10 ++-- 17 files changed, 158 insertions(+), 91 deletions(-) diff --git a/scripts/cba/average_indicators.py b/scripts/cba/average_indicators.py index 63f7e29f5e..07f2ae3c06 100644 --- a/scripts/cba/average_indicators.py +++ b/scripts/cba/average_indicators.py @@ -42,7 +42,9 @@ } -def average_indicators_csv(input_files, output_file, planning_horizon): +def average_indicators_csv( + input_files: list[str], output_file: str, planning_horizon: int | str +) -> None: """ Concatenate multiple CSV files into one using the csv module. @@ -57,6 +59,10 @@ def average_indicators_csv(input_files, output_file, planning_horizon): Path to output CSV file. planning_horizon : int or str Planning horizon year used for climatic year weighting. + + Returns + ------- + None """ if not input_files: logger.warning("No input files provided") diff --git a/scripts/cba/collect_indicators.py b/scripts/cba/collect_indicators.py index 585de07c0e..ed64d830a0 100644 --- a/scripts/cba/collect_indicators.py +++ b/scripts/cba/collect_indicators.py @@ -17,7 +17,7 @@ logger = logging.getLogger(__name__) -def collect_indicators_csv(input_files, output_file): +def collect_indicators_csv(input_files: list[str], output_file: str) -> None: """ Concatenate multiple CSV files into one using the csv module. diff --git a/scripts/cba/make_indicators.py b/scripts/cba/make_indicators.py index fac19c207c..9ee06026fd 100644 --- a/scripts/cba/make_indicators.py +++ b/scripts/cba/make_indicators.py @@ -90,7 +90,9 @@ def _apply_original_costs(n, remove_noisy_costs: bool) -> None: ].astype(t.static["capital_cost"].dtype) -def calculate_total_system_cost(n, remove_noisy_costs: bool = False): +def calculate_total_system_cost( + n: pypsa.Network, remove_noisy_costs: bool = False +) -> dict: """ Calculate total annualized system cost using PyPSA built-in statistics. @@ -239,7 +241,7 @@ def get_ac_energy_balance( PyPSA network object. assets : pandas.Series Assets for which to calculate energy balance. - bus_carrier : str, optional + bus_carrier : str or None, optional If set, filter energy balance to this bus carrier (e.g. "co2"). Default is None. """ balance = n.statistics.energy_balance( @@ -265,11 +267,11 @@ def calculate_power_sector_co2_emissions( ---------- n : pypsa.Network PyPSA network object. - ac_assets : pandas.Series, optional + ac_assets : pandas.Series or None, optional Pre-filtered Series of electricity-producing assets on AC buses. If not provided, it will be computed within the function. However, calculating it before calling this function can improve performance. - Default is None. + Default is None. Returns ------- @@ -388,7 +390,7 @@ def calculate_res_dump_per_carrier( return res_dump -def get_co2_ets_price(config, planning_horizon) -> float: +def get_co2_ets_price(config: dict, planning_horizon: int | str) -> float: """ Retrieve the CO2 ETS price for a given planning horizon from the configuration. @@ -416,8 +418,11 @@ def get_co2_ets_price(config, planning_horizon) -> float: def calculate_b1_indicator( - n_reference, n_project, method="pint", remove_noisy_costs: bool = False -): + n_reference: pypsa.Network, + n_project: pypsa.Network, + method: str = "pint", + remove_noisy_costs: bool = False, +) -> tuple[dict, dict]: """ Calculate B1 indicator. @@ -438,8 +443,8 @@ def calculate_b1_indicator( Returns ------- - dict - Dictionary with B1 and component costs. + tuple[dict, dict] + Tuple of (results, units) dictionaries with B1 and component costs. """ # Calculate full cost breakdowns for reporting cost_reference = calculate_total_system_cost(n_reference, remove_noisy_costs) @@ -532,12 +537,12 @@ def calculate_b2_indicator( Dictionary with keys "low", "central", "high" for societal cost of CO2 in EUR/t. co2_ets_price : float The CO2 ETS price in EUR/t for the relevant planning horizon. - ac_assets_reference : pandas.Series, optional + ac_assets_reference : pandas.Series or None, optional Pre-filtered Series of electricity-producing assets on AC buses for the reference network. If not provided, it will be computed within the function. Providing these can improve performance by avoiding redundant calculations. Default is None. - ac_assets_project : pandas.Series, optional + ac_assets_project : pandas.Series or None, optional Pre-filtered Series of electricity-producing assets on AC buses for the project network. If not provided, it will be computed within the function. Providing these can improve performance by avoiding redundant calculations. @@ -669,17 +674,17 @@ def calculate_b4_indicator( emission_factors : pd.DataFrame DataFrame with non-CO2 emission factors (kg/MWh) indexed by carrier and with columns for different pollutants and statistics (min, mean, max). - ac_assets_reference : pandas.Series, optional + ac_assets_reference : pandas.Series or None, optional Pre-filtered Series of electricity-producing assets on AC buses for the reference network. Default is None. - ac_assets_project : pandas.Series, optional + ac_assets_project : pandas.Series or None, optional Pre-filtered Series of electricity-producing assets on AC buses for the project network. Default is None. Returns ------- - dict - Dictionary with B4 indicators for each pollutant: + tuple[dict, dict] + Tuple of (results, units) dictionaries with B4 indicators for each pollutant: - B4{sub}_{pollutant} """ diff --git a/scripts/cba/plot_benchmark_indicators.py b/scripts/cba/plot_benchmark_indicators.py index 7ab39aab71..d7af3c5e50 100644 --- a/scripts/cba/plot_benchmark_indicators.py +++ b/scripts/cba/plot_benchmark_indicators.py @@ -140,9 +140,9 @@ def plot_project_benchmarks( DataFrame containing indicator data for one project. output_path : Path File path where the plot is saved. - project_label : str, optional + project_label : str or None, optional Label shown in the plot title. Default is None (no label). - area_subtitle : str, optional + area_subtitle : str or None, optional Subtitle describing the spatial scope of the assessment. Default is None. """ indicators = sorted(df["indicator"].dropna().unique()) @@ -352,7 +352,12 @@ def plot_project_benchmarks( plt.close(fig) -def create_plots(indicators_file, output_path, planning_horizon=None, area=None): +def create_plots( + indicators_file: str | Path, + output_path: str | Path, + planning_horizon: int | str | None = None, + area: str | None = None, +) -> None: """ Create benchmark plots from a per-project or collected indicators file. @@ -363,8 +368,8 @@ def create_plots(indicators_file, output_path, planning_horizon=None, area=None) output_path : str or Path Output file path or directory for the generated plots. planning_horizon : int or str, optional - Planning horizon year used to label project. Default is None. - area : str, optional + Planning horizon used to label project. Default is None. + area : str or None, optional Spatial scope identifier passed to format_area_subtitle. Default is None. """ output_path = Path(output_path) diff --git a/scripts/cba/plot_indicators.py b/scripts/cba/plot_indicators.py index 1ee1d54349..0a92b810f5 100644 --- a/scripts/cba/plot_indicators.py +++ b/scripts/cba/plot_indicators.py @@ -102,8 +102,14 @@ def load_and_merge_data(indicators_path, projects_path): def plot_b1_top_projects( - df, output_dir, method, colors, output_formats, n_top=20, filename_suffix="" -): + df: pd.DataFrame, + output_dir: str | Path, + method: str, + colors: dict, + output_formats: list[str], + n_top: int = 20, + filename_suffix: str = "", +) -> None: """ Diverging bar chart showing B1 with CAPEX/OPEX breakdown for top N projects. @@ -266,8 +272,14 @@ def plot_b1_top_projects( def plot_b1_summary( - df, output_dir, method, colors, output_formats, total_projects, filename_suffix="" -): + df: pd.DataFrame, + output_dir: str | Path, + method: str, + colors: dict, + output_formats: list[str], + total_projects: int, + filename_suffix: str = "", +) -> None: """ Summary histogram of B1 values across all projects. @@ -333,8 +345,13 @@ def plot_b1_summary( def plot_b1_capex_vs_opex( - df, output_dir, method, colors, output_formats, filename_suffix="" -): + df: pd.DataFrame, + output_dir: str | Path, + method: str, + colors: dict, + output_formats: list[str], + filename_suffix: str = "", +) -> None: """ Scatter plot of B1 CAPEX vs OPEX changes per project. diff --git a/scripts/cba/prepare_rolling_horizon.py b/scripts/cba/prepare_rolling_horizon.py index 62f488923d..df97d00373 100644 --- a/scripts/cba/prepare_rolling_horizon.py +++ b/scripts/cba/prepare_rolling_horizon.py @@ -55,8 +55,8 @@ def disable_store_cyclicity( ---------- n : pypsa.Network Network to modify in place. - cyclic_carriers : list[str], optional - Carriers that remain cyclic. Defaults to empty list. + cyclic_carriers : list[str] or None, optional + Carriers that remain cyclic. Default is None (treated as empty list). """ if cyclic_carriers is None: cyclic_carriers = [] @@ -265,8 +265,8 @@ def fix_reservoir_soc_at_boundaries( Target network for rolling horizon (will be modified in place). n_msv : pypsa.Network Network with perfect foresight solution. - carriers : list[str], optional - Carriers to fix. Defaults to ["hydro-reservoir"]. + carriers : list[str] or None, optional + Carriers to fix. Default is None (treated as ["hydro-reservoir"]). horizon : int Number of snapshots per rolling horizon window. Default 168 (one week at 1H). overlap : int diff --git a/scripts/cba/simplify_sb_network.py b/scripts/cba/simplify_sb_network.py index 8084a3abbf..390acd80d7 100644 --- a/scripts/cba/simplify_sb_network.py +++ b/scripts/cba/simplify_sb_network.py @@ -29,7 +29,9 @@ logger = logging.getLogger(__name__) -def extend_primary_fuel_sources(n: pypsa.Network, tyndp_conventional_carriers: list): +def extend_primary_fuel_sources( + n: pypsa.Network, tyndp_conventional_carriers: list +) -> None: """ Set infinite capacity for primary fuel source generators. diff --git a/scripts/cba/solve_cba_network.py b/scripts/cba/solve_cba_network.py index cd0c6cc07f..dafa6b0c04 100644 --- a/scripts/cba/solve_cba_network.py +++ b/scripts/cba/solve_cba_network.py @@ -72,7 +72,7 @@ def extra_functionality( The PyPSA network instance with config and params attributes. snapshots : pd.DatetimeIndex Simulation timesteps. - planning_horizons : str, optional + planning_horizons : str or None, optional The current planning horizon year or None in perfect foresight. """ config = n.config @@ -115,7 +115,7 @@ def optimize_with_rolling_horizon( ---------- n : pypsa.Network The PyPSA network instance to optimize. - snapshots : Sequence, optional + snapshots : Sequence or None, optional Set of snapshots to consider in the optimization. The default is None. horizon : int Number of snapshots to consider in each iteration. Defaults to 100. @@ -221,7 +221,7 @@ def solve_network( Dictionary of solving parameters. solving : dict Dictionary of solving options and configuration. - planning_horizons : str, optional + planning_horizons : str or None, optional The current planning horizon year or None in perfect foresight. **kwargs Additional keyword arguments passed to the solver. diff --git a/scripts/cba/summarize_all.py b/scripts/cba/summarize_all.py index 952df0744f..6cbca2c559 100644 --- a/scripts/cba/summarize_all.py +++ b/scripts/cba/summarize_all.py @@ -143,9 +143,9 @@ def format_area_subtitle(area: str | None) -> str | None: def create_plots( df: pd.DataFrame, output_file: str, - planning_horizons=None, - area: str = None, -): + planning_horizons: list | None = None, + area: str | None = None, +) -> None: """ Create benchmark plot from all collected indicator files. @@ -157,7 +157,7 @@ def create_plots( Output file path for the generated plot. planning_horizons : list, optional Planning horizons to include. Default is None (derived from df). - area : str, optional + area : str or None, optional Spatial scope identifier passed to format_area_subtitle. Default is None. """ # diff --git a/scripts/cba/summarize_indicators.py b/scripts/cba/summarize_indicators.py index ea8a491881..f55f293e54 100644 --- a/scripts/cba/summarize_indicators.py +++ b/scripts/cba/summarize_indicators.py @@ -164,9 +164,9 @@ def plot_project_benchmarks( DataFrame containing indicator data for one project. output_path : Path File path where the plot is saved. - project_label : str, optional + project_label : str or None, optional Label shown in the plot title. Default is None (no label). - area_subtitle : str, optional + area_subtitle : str or None, optional Subtitle describing the spatial scope of the assessment. Default is None. """ indicators = sorted(df["indicator"].dropna().unique()) @@ -327,7 +327,7 @@ def create_plots(df, output_file, area): logger.info("Benchmark plots saved to %s", output_file) -def summarize_indicators(input_files, output_file): +def summarize_indicators(input_files: list[str], output_file: str) -> None: """ Concatenate multiple CSV files into one using the csv module. diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index 7d46229e32..333d449d60 100755 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -3140,7 +3140,7 @@ def add_ammonia( 'fixed', 'VOM', 'efficiency', 'lifetime', etc. pop_layout : pd.DataFrame Population layout data with index of location nodes - spatial : Namespace + spatial : SimpleNamespace Configuration object containing ammonia-specific spatial information with attributes: - nodes: list of ammonia bus nodes @@ -3485,7 +3485,13 @@ def add_electricity_grid_connection(n, costs): ] -def add_h2_production_tyndp(n, nodes, buses_h2, costs, options={}): +def add_h2_production_tyndp( + n: pypsa.Network, + nodes: pd.Index, + buses_h2: pd.Index, + costs: pd.DataFrame, + options: dict = {}, +) -> None: """ Add TYNDP electrolyzers for Z1 and Z2, and optionally add SMR, SMR CC and ATR. @@ -3609,7 +3615,12 @@ def add_h2_production_tyndp(n, nodes, buses_h2, costs, options={}): ) -def add_h2_dres_tyndp(n, spatial, buses_h2_z2, costs): +def add_h2_dres_tyndp( + n: pypsa.Network, + spatial: SimpleNamespace, + buses_h2_z2: SimpleNamespace, + costs: pd.DataFrame, +) -> None: """ Adds TYNDP Z2 DRES electricity buses and electrolyzers. @@ -3617,7 +3628,7 @@ def add_h2_dres_tyndp(n, spatial, buses_h2_z2, costs): ---------- n : pypsa.Network The PyPSA network container object. - spatial : object + spatial : SimpleNamespace Namespace object with spatial nodes for different carriers such as `h2_tyndp`. buses_h2_z2 : SimpleNamespace Namespace object with spatial nodes of H2 Z2 buses. @@ -3655,7 +3666,14 @@ def add_h2_dres_tyndp(n, spatial, buses_h2_z2, costs): ) -def add_h2_reconversion_tyndp(n, spatial, nodes, buses_h2, costs, options=None): +def add_h2_reconversion_tyndp( + n: pypsa.Network, + spatial: SimpleNamespace, + nodes: pd.Index, + buses_h2: pd.Index, + costs: pd.DataFrame, + options: dict | None = None, +) -> None: """ Adds TYNDP H2 reconversion with options for Fuel cells, H2 turbines and methanation. @@ -3663,7 +3681,7 @@ def add_h2_reconversion_tyndp(n, spatial, nodes, buses_h2, costs, options=None): ---------- n : pypsa.Network The PyPSA network container object. - spatial : object + spatial : SimpleNamespace Namespace object with spatial nodes for different carriers such as `h2_tyndp`. nodes : pd.Index Pandas Index of electricity node locations/nodes. @@ -3671,7 +3689,7 @@ def add_h2_reconversion_tyndp(n, spatial, nodes, buses_h2, costs, options=None): Pandas Index of hydrogen nodes to which H2 reconversion technologies will connect. costs : pd.DataFrame Technology cost assumptions. - options : dict, optional + options : dict or None, optional Dictionary of configuration options. Defaults to empty dict if not provided. Key options include: - methanation : bool @@ -3742,7 +3760,14 @@ def add_h2_reconversion_tyndp(n, spatial, nodes, buses_h2, costs, options=None): ) -def add_h2_grid_tyndp(n, nodes, h2_pipes_file, interzonal_file, costs, options): +def add_h2_grid_tyndp( + n: pypsa.Network, + nodes: pd.Index, + h2_pipes_file: str, + interzonal_file: str, + costs: pd.DataFrame, + options: dict, +) -> None: """ Adds TYNDP hydrogen pipelines and interzonal (Z1 <-> Z2) connections. @@ -3943,15 +3968,15 @@ def add_h2_storage_tyndp( def add_h2_topology_tyndp( - n, - pop_layout, - spatial, - h2_pipes_file, - interzonal_file, - costs, - options, - h2_demand_file, -): + n: pypsa.Network, + pop_layout: pd.DataFrame, + spatial: SimpleNamespace, + h2_pipes_file: str, + interzonal_file: str, + costs: pd.DataFrame, + options: dict, + h2_demand_file: str, +) -> None: """ Add TYNDP H2 topology to the network. This adds new single country H2 buses (Z1 + Z2 nodes) and pipeline connections @@ -3970,7 +3995,7 @@ def add_h2_topology_tyndp( The PyPSA network container object. pop_layout : pd.DataFrame Population layout with index of locations/nodes. - spatial : object + spatial : SimpleNamespace Namespace object with spatial nodes for different carriers such as `h2_tyndp`. h2_pipes_file : str Path to CSV file containing prepped H2 reference grid data. @@ -4063,7 +4088,7 @@ def add_h2_topology_tyndp( add_h2_demand_tyndp(n=n, h2_demand_file=h2_demand_file) -def add_h2_demand_tyndp(n, h2_demand_file): +def add_h2_demand_tyndp(n: pypsa.Network, h2_demand_file: str) -> None: """ Add exogenous TYNDP hydrogen demand to the network. @@ -5254,7 +5279,7 @@ def add_offshore_hubs_tyndp( Series mapping technology names (indexes) to PECD profile file paths (values). costs : pd.DataFrame Technology costs assumptions. - spatial : object, optional + spatial : SimpleNamespace Object containing spatial information about nodes and their locations. options : dict Configuration options containing at least: @@ -5347,7 +5372,7 @@ def attach_gas_load( - gas_demand_exogenously costs : pd.DataFrame Technology costs assumptions. - spatial : object + spatial : SimpleNamespace Object containing spatial information about nodes and their locations. nhours : int, optional Number of hours over which the annual gas demand is divided. Default is 8760. diff --git a/scripts/sb/build_statistics.py b/scripts/sb/build_statistics.py index ca8aab44a2..406f1cfa79 100644 --- a/scripts/sb/build_statistics.py +++ b/scripts/sb/build_statistics.py @@ -85,7 +85,7 @@ def add_benchmarking_mappings( ) -def remove_last_day(sws: pd.Series, nhours: int = 24): +def remove_last_day(sws: pd.Series, nhours: int = 24) -> pd.Series: """ Remove the last day from snapshots to ensure exactly 52 weeks of data. @@ -98,8 +98,8 @@ def remove_last_day(sws: pd.Series, nhours: int = 24): Returns ------- - tuple[pd.DatetimeIndex, pd.Series] - Modified snapshots and snapshot weightings with the last day removed. + pd.Series + Snapshot weightings with the last day zeroed out. """ sws = sws.copy() diff --git a/scripts/sb/build_tyndp_h2_network.py b/scripts/sb/build_tyndp_h2_network.py index d3269099b2..69c131c940 100644 --- a/scripts/sb/build_tyndp_h2_network.py +++ b/scripts/sb/build_tyndp_h2_network.py @@ -62,7 +62,9 @@ def normalize_starting_grid_h2_nodes(df: pd.DataFrame) -> pd.DataFrame: return df -def load_h2_interzonal_connections(fn, scenario="GA", pyear=2030): +def load_h2_interzonal_connections( + fn: str, scenario: str = "GA", pyear: int = 2030 +) -> pd.DataFrame: """ Load and clean H2 interzonal connections. Returns the cleaned interzonal connections as dataframe. diff --git a/scripts/sb/build_tyndp_offshore_hubs.py b/scripts/sb/build_tyndp_offshore_hubs.py index 9bf214c247..14b7c31366 100644 --- a/scripts/sb/build_tyndp_offshore_hubs.py +++ b/scripts/sb/build_tyndp_offshore_hubs.py @@ -18,7 +18,7 @@ GEO_CRS = "EPSG:4326" -def load_offshore_hubs(fn: str): +def load_offshore_hubs(fn: str) -> gpd.GeoDataFrame: """ Load and process offshore hub coordinates from Excel file. @@ -79,7 +79,7 @@ def load_offshore_grid( countries: list[str], max_capacity: dict[str, int], h2_zones_tyndp: bool, -): +) -> pd.DataFrame: """ Load offshore grid (electricity and hydrogen) and format data. @@ -219,7 +219,7 @@ def load_offshore_electrolysers( planning_horizons: list[int], countries: list[str], h2_zones_tyndp: bool, -): +) -> pd.DataFrame: """ Load offshore electrolysers data and format data. @@ -292,7 +292,9 @@ def load_offshore_electrolysers( return electrolysers -def collect_from_layer(generators_e, generators_l, nodes): +def collect_from_layer( + generators_e: pd.DataFrame, generators_l: pd.DataFrame, nodes: pd.DataFrame +) -> pd.DataFrame: """ Combine existing capacities with potentials and resolve bus allocations. @@ -396,7 +398,7 @@ def load_offshore_generators( planning_horizons: list[int], countries: list[str], extendable_carriers: dict[str, list[str]], -): +) -> tuple[pd.DataFrame, pd.DataFrame]: """ Load offshore generators data and format data. diff --git a/scripts/sb/clean_tyndp_h2_imports.py b/scripts/sb/clean_tyndp_h2_imports.py index 85c2484175..ded8941625 100644 --- a/scripts/sb/clean_tyndp_h2_imports.py +++ b/scripts/sb/clean_tyndp_h2_imports.py @@ -21,7 +21,9 @@ logger = logging.getLogger(__name__) -def match_centroids(df, countries_centroids): +def match_centroids( + df: pd.DataFrame, countries_centroids: gpd.GeoDataFrame +) -> pd.DataFrame: """ Matches coordinates of country centroids to bus0 countries. Manually matches coordinates next to Faroe Island ("FO") to Ammonia import node. @@ -67,7 +69,7 @@ def match_centroids(df, countries_centroids): ) -def load_import_data(fn, countries_centroids): +def load_import_data(fn: str, countries_centroids: gpd.GeoDataFrame) -> pd.DataFrame: """ Load and clean TYNDP H2 import potentials, maximum capacity, offer quantity and marginal cost for pipeline and shipping. diff --git a/scripts/sb/plot_offshore_network.py b/scripts/sb/plot_offshore_network.py index 3f70bed60a..1545cd3e9e 100644 --- a/scripts/sb/plot_offshore_network.py +++ b/scripts/sb/plot_offshore_network.py @@ -8,6 +8,7 @@ import logging import re +import cartopy import geopandas as gpd import matplotlib.pyplot as plt import numpy as np @@ -28,16 +29,16 @@ def plot_offshore_map( - network, - map_opts, - proj, - map_fn, - planning_horizons, - carrier="DC_OH", - p_nom="p_nom", - legend=True, - hubs_only=False, -): + network: pypsa.Network, + map_opts: dict, + proj: cartopy.crs.Projection, + map_fn: str, + planning_horizons: int, + carrier: str = "DC_OH", + p_nom: str | float = "p_nom", + legend: bool = True, + hubs_only: bool = False, +) -> None: """ Plots the offshore network hydrogen or electricity capacities and offshore-hubs buses. If `p_nom` parameter is set as `p_nom_opt`, optimal capacities are plotted instead. @@ -49,7 +50,7 @@ def plot_offshore_map( map_opts : dict Map options for plotting. proj : cartopy.crs.Projection - Projection to use for plotting. + Cartopy CRS projection to use for plotting. map_fn : str Path to save the final map plot to. planning_horizons : int diff --git a/scripts/solve_network.py b/scripts/solve_network.py index 5f3eae19f6..09773aecaf 100644 --- a/scripts/solve_network.py +++ b/scripts/solve_network.py @@ -1243,11 +1243,11 @@ def add_import_limit_constraint(n: pypsa.Network, sns: pd.DatetimeIndex): def add_offshore_hubs_constraint( - n, + n: pypsa.Network, planning_horizons: int, - offshore_zone_trajectories_fn, + offshore_zone_trajectories_fn: str, renewable_carriers_tyndp: list[str], -): +) -> None: """ Add two constraints on offshore hubs. @@ -1430,7 +1430,7 @@ def extra_functionality( Simulation timesteps planning_horizons : str, optional The current planning horizon year or None in perfect foresight. - offshore_zone_trajectories_fn : str, optional + offshore_zone_trajectories_fn : str or None, optional Path to the file containing the offshore zone potentials trajectories. Default is None. renewable_carriers_tyndp : list[str], optional @@ -1675,7 +1675,7 @@ def create_optimization_model( Arguments for n.optimize.solve_model() planning_horizons : str, optional The current planning horizon year or None in perfect foresight - offshore_zone_trajectories_fn : str, optional + offshore_zone_trajectories_fn : str or None, optional Path to DataFrame containing the offshore zone potentials trajectories. Default is None. renewable_carriers_tyndp : list[str], optional From 6cd9d9976ce39a9bd1a90377553b964130b71a39 Mon Sep 17 00:00:00 2001 From: Thomas Gilon Date: Fri, 19 Jun 2026 16:14:34 +0200 Subject: [PATCH 04/12] doc: add release note --- doc/release_notes.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/release_notes.md b/doc/release_notes.md index 6e44a86fa1..f36e6a18fa 100644 --- a/doc/release_notes.md +++ b/doc/release_notes.md @@ -35,6 +35,8 @@ * Update benchmarking documentation tables and figures for v0.7.1 ([#711](https://github.com/open-energy-transition/open-tyndp/pull/711)). +* Improve docstring formatting and add missing type hints (https://github.com/open-energy-transition/open-tyndp/pull/759). + **Developers Note** * Change GitHub issue templates to comply with ISO security checks ([#714](https://github.com/open-energy-transition/open-tyndp/pull/714), [#730](https://github.com/open-energy-transition/open-tyndp/pull/730)). From cddca82586449d84e71a696bfef256e63d087cf0 Mon Sep 17 00:00:00 2001 From: Thomas Gilon Date: Mon, 22 Jun 2026 10:10:58 +0200 Subject: [PATCH 05/12] doc: create open-tyndp rule documentation --- doc/cba.md | 2 +- doc/cba_rules.md | 140 +++++++++++++++++++++++++++ doc/plotting.md | 47 --------- doc/preparation.md | 52 ---------- doc/retrieve.md | 31 ------ doc/sb.md | 2 +- doc/sb_rules.md | 234 +++++++++++++++++++++++++++++++++++++++++++++ doc/sector.md | 28 ------ mkdocs.yml | 3 + rules/cba.smk | 23 +++++ rules/sb.smk | 4 + 11 files changed, 406 insertions(+), 160 deletions(-) create mode 100644 doc/cba_rules.md create mode 100644 doc/sb_rules.md diff --git a/doc/cba.md b/doc/cba.md index a96ed0a1e5..7ca7ff5d06 100644 --- a/doc/cba.md +++ b/doc/cba.md @@ -9,7 +9,7 @@ The Cost-Benefit Analysis (CBA) evaluates transmission and storage projects by c ## CBA Workflow Methodology -The workflow evaluates projects using a **rolling horizon** approach where the full year is divided into sequential weekly windows (168 hourly snapshots each, with an overlap of 1 snapshot). +The workflow evaluates projects using a **rolling horizon** approach where the full year is divided into sequential weekly windows (168 hourly snapshots each, with an overlap of 1 snapshot). An overview of the CBA rules is documented at [CBA rules](cba_rules.md). To resolve **myopia**—where the optimizer cannot see beyond the current week and makes suboptimal decisions for seasonal storage (H2, gas, large hydro)—the workflow uses Marginal Storage Values (MSV) derived from a full-year optimization. diff --git a/doc/cba_rules.md b/doc/cba_rules.md new file mode 100644 index 0000000000..08ee625b39 --- /dev/null +++ b/doc/cba_rules.md @@ -0,0 +1,140 @@ + + + +# Cost-Benefit Analysis (CBA) + +The Cost-Benefit Analysis (CBA) workflow is implemented in `rules/cba.smk`. Rules are organised into stages: `Retrieve`, `Build MSV`, `Build rolling`, `Postprocess`, `Benchmarking` and `Collect`. + +## Retrieve + +### Rule `retrieve_tyndp_cba_projects` + +Downloads the CBA project explorer dataset, containing project definitions. + +### Rule `retrieve_tyndp_cba_non_co2_emissions` + +Downloads non-CO₂ emission factors (NOx, SO₂, PM, etc.) used to compute B4 indicators. + +### Rule `retreive_cba_guidelines_reference_projects` + +Downloads the CBA Implementation Guidelines reference project table +(`table_B1_CBA_Implementations_Guidelines_TYNDP2024.csv`) used to reconcile SB investments +with CBA project definitions. + +### Rule `retrieve_presolved_sb_networks` + +Downloads pre-solved SB networks from a previous Open-TYNDP release for use as CBA inputs +when `cba.cba_scenario_input.use_presolved` is `true`. + +## Build MSV + +### Rule `clean_projects` *(checkpoint)* + +::: clean_projects + +### Rule `clean_tyndp_indicators` + +::: clean_tyndp_indicators + +### Rule `simplify_sb_network` + +::: simplify_sb_network + +### Rule `fix_reference_sb_to_cba` + +::: fix_reference_sb_to_cba + +### Rule `prepare_reference` + +::: prepare_reference + +### Rule `build_msv_snapshot_weightings` + +::: build_msv_snapshot_weightings + +### Rule `solve_cba_msv_extraction` + +::: solve_cba_msv_extraction + +## Build rolling + +### Rule `prepare_rolling_horizon` + +::: prepare_rolling_horizon + +### Rule `prepare_project` + +::: prepare_project + +### Rule `solve_cba_reference_network` + +Solves the reference network using the rolling horizon approach. Shares the script with +[`solve_cba_network`](#rule-solve_cba_network). + +::: solve_cba_network + +### Rule `solve_cba_network` + +Solves the CBA network using the rolling horizon approach. Shares the script with [`solve_cba_reference_network`](#rule-solve_cba_reference_network). + +::: solve_cba_network + +## Postprocess + +### Rule `make_indicators` + +::: make_indicators + +### Rule `collect_indicators` + +::: collect_indicators + +### Rule `plot_indicators` + +::: plot_indicators + +## Benchmarking + +### Rule `plot_cba_benchmark` + +Plots per-project indicator benchmarking charts comparing computed indicators against +TYNDP 2024 reference values. Shares the script with [`plot_weather_benchmark`](#rule-plot_weather_benchmark). + +::: plot_benchmark_indicators + +### Rule `plot_weather_benchmark` + +Plots weather ensemble benchmarking charts from indicators aggregated across climate years. +Shares the script with [`plot_cba_benchmark`](#rule-plot_cba_benchmark). + +::: plot_benchmark_indicators + +### Rule `average_indicators_per_project_and_planning_horizon` + +::: average_indicators + +### Rule `summarize_indicators_per_project` + +::: summarize_indicators + +### Rule `summarize_all_indicators` + +::: summarize_all + +## Collect + +Aggregate rules that run the corresponding base rule across all configured wildcards. They do not have dedicated scripts. + +### Rule `prepare_references` + +Aggregate [`prepare_reference`](#rule-prepare_reference) outputs. + +### Rule `collect_cba_scenario` + +Collects all per-scenario outputs (indicator plots, benchmark charts) into a single target +for a single climate year run (e.g. `NT-cy2009`). + +### Rule `cba` + +Top-level target rule. Collects ensemble outputs from all climate year runs in a collection +scenario (e.g. `NT-cyears`) and the per-scenario results from nested runs. diff --git a/doc/plotting.md b/doc/plotting.md index 09379f3329..904c673766 100644 --- a/doc/plotting.md +++ b/doc/plotting.md @@ -27,14 +27,6 @@ ::: plot_base_network -## Rule `plot_base_offshore_network` - -::: plot_offshore_network - -## Rule `plot_offshore_network` - -::: plot_offshore_network - ## Rule `plot_power_network_clustered` ::: plot_power_network_clustered @@ -49,10 +41,6 @@ ::: plot_power_network_perfect -## Rule `plot_base_hydrogen_network` - -::: plot_base_hydrogen_network - ## Rule `plot_hydrogen_network` ::: plot_hydrogen_network @@ -93,38 +81,3 @@ ::: plot_interactive_bus_balance -## Rule `clean_tyndp_output_benchmark` - -::: clean_tyndp_output_benchmark - -## Rule `clean_tyndp_report_benchmark` - -::: clean_tyndp_report_benchmark - -## Rule `clean_tyndp_vp_data` - -::: clean_tyndp_vp_data - -## Rule `build_statistics` - -::: build_statistics - -## Rule `make_benchmark` - -::: make_benchmark - -## Rule `plot_benchmark` - -::: plot_benchmark - -## Rule `launch_explorer` - -::: launch_explorer - -## Rule `launch_presolved_explorer` - -Mirrors the `launch_explorer` rule to launch the `PyPSA-Explorer` web interface with pre-solved SB networks from previous Open-TYNDP release runs. - -## Rule `close_explorers` - -Closes all open local instances of launched PyPSA-Explorers and frees up used ports again. diff --git a/doc/preparation.md b/doc/preparation.md index 96889a685b..8225e7265d 100644 --- a/doc/preparation.md +++ b/doc/preparation.md @@ -92,18 +92,10 @@ together into a detailed PyPSA network stored in `networks/base_s_{clusters}_ele ::: build_electricity_demand_base -## Rule `build_electricity_demand_base_tyndp` - -::: build_electricity_demand_base - ## Rule `build_electricity_demand` {#electricity_demand} ::: build_electricity_demand -## Rule `build_electricity_demand_tyndp` - -::: build_electricity_demand - ## Rule `build_hac_features` ::: build_hac_features @@ -155,47 +147,3 @@ together into a detailed PyPSA network stored in `networks/base_s_{clusters}_ele ## Rule `prepare_network` {#prepare} ::: prepare_network - -## Rule `prepare_pecd_release` - -::: prepare_pecd_release - -## Rule `clean_pecd_data` - -::: clean_pecd_data - -## Rule `build_renewable_profiles_pecd` - -::: build_renewable_profiles_pecd - -## Rule `clean_tyndp_hydro_inflows` - -::: clean_tyndp_hydro_inflows - -## Rule `build_tyndp_hydro_profile` - -::: build_tyndp_hydro_profile - -## Rule `build_pemmdb_data` - -::: build_pemmdb_data - -## Rule `build_tyndp_transmission_projects` - -::: build_tyndp_transmission_projects - -## Rule `build_tyndp_trajectories` - -::: build_tyndp_trajectories - -## Rule `clean_tyndp_electricity_demand` - -::: clean_tyndp_electricity_demand - -## Rule `clean_tyndp_smr` - -::: clean_tyndp_smr - -## Rule `clean_tyndp_h2_storages` - -::: clean_tyndp_h2_storages diff --git a/doc/retrieve.md b/doc/retrieve.md index 5b605ce3d0..33dbc2eea0 100644 --- a/doc/retrieve.md +++ b/doc/retrieve.md @@ -95,34 +95,3 @@ costs: **Outputs** - `data/costs/primary/{version}/costs_{year}.csv` - - -## Rule `retrieve_countries_centroids` - -This rule downloads country centroid geometry data by Copyright (c) 2021 Gavin Rehkemper from https://cdn.jsdelivr.net/gh/gavinr/world-countries-centroids@v1.0.0/dist/countries.geojson. - -**Relevant Settings** - -None. - -**Outputs** - -- `data/countries_centroids.geojson` - - -## Rule `retrieve_presolved_networks` - -This rule downloads pre-solved networks from a previous Open-TYNDP release (*preliminary outcomes* published on [Zenodo](https://zenodo.org/records/18608105)) and extracts the solved network for each planning horizon. These can be investigated with PyPSA-Explorer's web interface using the `launch_presolved_explorer` rule without having to re-run the workflow. - -**Relevant Settings** - -```yaml -data: - open_tyndp_prelim: - source: - version: -``` - -**Outputs** - -- `data/open_tyndp_prelim/{source}/{version}/base_s_all___{planning_horizons}.nc` diff --git a/doc/sb.md b/doc/sb.md index ada4fa52fb..e3898c4986 100644 --- a/doc/sb.md +++ b/doc/sb.md @@ -79,7 +79,7 @@ a scenario output file from the TYNDP 2024 process used as a fixed input. The SB workflow transforms raw ENTSO-E input datasets into a solved, sector-coupled PyPSA network. The key stages are: integrating public input data, constructing the sector-coupled -network, applying TYNDP-specific constraints, solving the capacity expansion optimisation, visualising results, and running the Open-TYNDP [benchmarking framework](benchmarking.md). +network, applying TYNDP-specific constraints, solving the capacity expansion optimisation, visualising results, and running the Open-TYNDP [benchmarking framework](benchmarking.md). An overview of the SB rules is documented at [SB rules](sb_rules.md). ### Network Construction diff --git a/doc/sb_rules.md b/doc/sb_rules.md new file mode 100644 index 0000000000..6ae8c38733 --- /dev/null +++ b/doc/sb_rules.md @@ -0,0 +1,234 @@ + + + +# Scenario Building (SB) + +The Scenario Building (SB) workflow is implemented in `rules/sb.smk`. Rules are organised into stages: `Retrieve`, `Development`, `Build electricity`, `Build sector`, `Postprocess`, `Benchmarking`, `Collect` and `Visualize`. + +## Retrieve + +### Rule `retrieve_tyndp_pecd` + +Downloads the PECD dataset. + +### Rule `retrieve_tyndp_vp_data` + +Downloads the TYNDP Visualisation Platform data used by the benchmarking framework. + +### Rule `retrieve_tyndp_nuclear_profiles` + +Downloads per-country nuclear availability profiles. + +### Rule `retrieve_presolved_networks` + +Downloads pre-solved networks from a previous Open-TYNDP release (*preliminary outcomes* +published on [Zenodo](https://doi.org/10.5281/zenodo.18608105)) and extracts the solved network +for each planning horizon. These can be investigated with PyPSA-Explorer's web interface +using the [`launch_presolved_explorer`](#rule-launch_presolved_explorer) rule without having to re-run the workflow. + +**Relevant Settings** + +```yaml +data: + open_tyndp_prelim: + source: + version: +``` + +**Outputs** + +- `data/open_tyndp_prelim/{source}/{version}/base_s_all___{planning_horizons}.nc` + +### Rule `retrieve_countries_centroids` + +Downloads country centroid geometry data by Copyright (c) 2021 Gavin Rehkemper from +. + +**Relevant Settings** + +None. + +**Outputs** + +- `data/countries_centroids.geojson` + +## Development + +### Rule `prepare_pecd_release` + +::: prepare_pecd_release + +## Build electricity + +### Rule `clean_tyndp_electricity_demand` + +::: clean_tyndp_electricity_demand + +### Rule `build_electricity_demand_tyndp` + +Extends the upstream [`build_electricity_demand`](preparation.md#electricity_demand) rule with TYNDP-specific load data. Builds +per-country load time series from the TYNDP electricity demand prepared by +[`clean_tyndp_electricity_demand`](#rule-clean_tyndp_electricity_demand). + +### Rule `clean_pecd_data` + +::: clean_pecd_data + +### Rule `build_renewable_profiles_pecd` + +::: build_renewable_profiles_pecd + +### Rule `build_pemmdb_data` + +::: build_pemmdb_data + +### Rule `build_tyndp_transmission_projects` + +::: build_tyndp_transmission_projects + +### Rule `build_tyndp_trajectories` + +::: build_tyndp_trajectories + +### Rule `clean_tyndp_hydro_inflows` + +::: clean_tyndp_hydro_inflows + +### Rule `build_tyndp_hydro_profile` + +::: build_tyndp_hydro_profile + +### Rule `build_electricity_demand_base_tyndp` + +Extends the upstream [`build_electricity_demand_base`](preparation.md#rule-build_electricity_demand_base) rule with TYNDP-specific load data. Builds +the electricity demand for base regions from the TYNDP electricity demand prepared by +[`build_electricity_demand_tyndp`](#rule-build_electricity_demand_tyndp). + +## Build sector + +### Rule `build_tyndp_gas_demand` + +::: build_tyndp_gas_demand + +### Rule `build_tyndp_h2_demand` + +::: build_tyndp_h2_demand + +### Rule `build_tyndp_h2_network` + +::: build_tyndp_h2_network + +### Rule `clean_tyndp_h2_imports` + +::: clean_tyndp_h2_imports + +### Rule `build_tyndp_h2_imports` + +::: build_tyndp_h2_imports + +### Rule `clean_tyndp_smr` + +::: clean_tyndp_smr + +### Rule `clean_tyndp_h2_storages` + +::: clean_tyndp_h2_storages + +### Rule `build_tyndp_offshore_hubs` + +::: build_tyndp_offshore_hubs + +### Rule `group_tyndp_conventionals` + +::: group_tyndp_conventionals + +## Postprocess + +### Rule `plot_base_hydrogen_network` + +::: plot_base_hydrogen_network + +### Rule `plot_base_offshore_network` + +::: plot_offshore_network + +### Rule `plot_offshore_network` + +::: plot_offshore_network + +## Benchmarking + +### Rule `clean_tyndp_output_benchmark` + +::: clean_tyndp_output_benchmark + +### Rule `clean_tyndp_report_benchmark` + +::: clean_tyndp_report_benchmark + +### Rule `clean_tyndp_vp_data` + +::: clean_tyndp_vp_data + +### Rule `build_statistics` + +::: build_statistics + +### Rule `make_benchmark` + +::: make_benchmark + +### Rule `plot_benchmark` + +::: plot_benchmark + +## Collect + +Aggregate rules that run the corresponding base rule across all configured wildcards. They do not have dedicated scripts. + +### Rule `clean_pecd_datas` + +Aggregate [`clean_pecd_data`](#rule-clean_pecd_data) outputs. + +### Rule `build_renewable_profiles_pecds` + +Aggregate [`build_renewable_profiles_pecd`](#rule-build_renewable_profiles_pecd) outputs. + +### Rule `prepare_benchmarks` + +Aggregate benchmark inputs before the benchmarking stage. + +### Rule `make_benchmarks` + +Aggregate [`make_benchmark`](#rule-make_benchmark) outputs. + +### Rule `plot_benchmarks` + +Aggregate [`plot_benchmark`](#rule-plot_benchmark) outputs. + +### Rule `build_pemmdb_and_trajectories` + +Aggregate [`build_pemmdb_data`](#rule-build_pemmdb_data) and [`build_tyndp_trajectories`](#rule-build_tyndp_trajectories) outputs. + +### Rule `build_tyndp_h2_demands` + +Aggregate [`build_tyndp_h2_demand`](#rule-build_tyndp_h2_demand) outputs. + +### Rule `build_tyndp_gas_demands` + +Aggregate [`build_tyndp_gas_demand`](#rule-build_tyndp_gas_demand) outputs. + +## Visualize + +### Rule `launch_explorer` + +::: launch_explorer + +### Rule `launch_presolved_explorer` + +Mirrors the [`launch_explorer`](#rule-launch_explorer) rule to launch the PyPSA-Explorer web interface with +pre-solved SB networks from previous Open-TYNDP release runs (see [`retrieve_presolved_networks`](#rule-retrieve_presolved_networks)). + +### Rule `close_explorers` + +Closes all open local instances of launched PyPSA-Explorers and frees up used ports again. diff --git a/doc/sector.md b/doc/sector.md index 1038a0fbad..96ac02b2ba 100644 --- a/doc/sector.md +++ b/doc/sector.md @@ -216,34 +216,6 @@ Having downloaded the necessary data, ::: build_snapshot_weightings -## Rule `build_tyndp_h2_network` - -::: build_tyndp_h2_network - -## Rule `clean_tyndp_h2_imports` - -::: clean_tyndp_h2_imports - -## Rule `build_tyndp_h2_imports` - -::: build_tyndp_h2_imports - -## Rule `build_tyndp_offshore_hubs` - -::: build_tyndp_offshore_hubs - -## Rule `build_tyndp_h2_demand` - -::: build_tyndp_h2_demand - -## Rule `group_tyndp_conventionals` - -::: group_tyndp_conventionals - -## Rule `build_tyndp_gas_demand` - -::: build_tyndp_gas_demand - ## Rule `prepare_sector_network` ::: prepare_sector_network diff --git a/mkdocs.yml b/mkdocs.yml index 355ca64579..d9adc342d5 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -77,6 +77,8 @@ nav: - Data Repositories: data-repos.md - Rules Overview: + - Scenario Building: sb_rules.md + - Cost-Benefit Analysis: cba_rules.md - Retrieving Data: retrieve.md - Building Electricity Networks: preparation.md - Building Sector-Coupled Networks: sector.md @@ -107,6 +109,7 @@ plugins: show_source: false show_root_heading: false show_root_full_path: false + show_root_toc_entry: false members: false allow_inspection: true inventories: diff --git a/rules/cba.smk b/rules/cba.smk index c90db6d1f6..a8176405d8 100644 --- a/rules/cba.smk +++ b/rules/cba.smk @@ -23,6 +23,9 @@ wildcard_constraints: run="(?!None)[-a-zA-Z0-9]+", # Disallow None as a run wildcard +# Retrieve +########## + if (CBA_PROJECTS_DATASET := dataset_version("tyndp_cba_projects"))[ "source" ] in ARCHIVE_SOURCES: @@ -140,6 +143,10 @@ if config.get("cba", {}).get("cba_scenario_input", {}).get("use_presolved", Fals +# Build MSV +############ + + # read in transmission and storage projects from excel sheets # def input_clustered_network(w): @@ -346,6 +353,10 @@ rule solve_cba_msv_extraction: "../scripts/cba/solve_cba_msv_extraction.py" +# Build rolling +################ + + # Prepare network for rolling horizon: disable seasonal cyclicity, apply marginal storage value rule prepare_rolling_horizon: input: @@ -427,6 +438,10 @@ rule solve_cba_network: scripts("cba/solve_cba_network.py") +# Postprocess +############## + + # Compute CBA indicators comparing reference and project networks rule make_indicators: input: @@ -497,6 +512,10 @@ rule plot_indicators: scripts("cba/plot_indicators.py") +# Benchmarking +############### + + rule plot_cba_benchmark: input: indicators=RESULTS + "cba/project_{cba_project}_{planning_horizons}.csv", @@ -706,6 +725,10 @@ def collect_cba_scenario_inputs(w): return inputs +# Collect +########## + + # collect files to be stored in the scenario directory, e.g., NT-cy1995 rule collect_cba_scenario: input: diff --git a/rules/sb.smk b/rules/sb.smk index 7afb03da22..9b491e5859 100644 --- a/rules/sb.smk +++ b/rules/sb.smk @@ -1099,6 +1099,10 @@ rule build_tyndp_gas_demands: ), +# Visualize +########### + + rule launch_explorer: input: expand( From f601b3eaf72829eb20e937ace64f23094f3a0e60 Mon Sep 17 00:00:00 2001 From: Thomas Gilon Date: Tue, 23 Jun 2026 11:38:48 +0200 Subject: [PATCH 06/12] chore: use scripts path provider --- rules/cba.smk | 12 ++++++------ rules/sb.smk | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rules/cba.smk b/rules/cba.smk index a8176405d8..67a155df73 100644 --- a/rules/cba.smk +++ b/rules/cba.smk @@ -321,7 +321,7 @@ rule build_msv_snapshot_weightings: msv_resolution=config_provider("cba", "msv_extraction", "resolution"), drop_leap_day=config_provider("enable", "drop_leap_day"), script: - "../scripts/cba/build_msv_snapshot_weightings.py" + scripts("cba/build_msv_snapshot_weightings.py") def input_msv_snapshot_weightings(w): @@ -350,7 +350,7 @@ rule solve_cba_msv_extraction: msv_resolution=config_provider("cba", "msv_extraction", "resolution"), cyclic_carriers=config_provider("cba", "storage", "cyclic_carriers"), script: - "../scripts/cba/solve_cba_msv_extraction.py" + scripts("cba/solve_cba_msv_extraction.py") # Build rolling @@ -543,7 +543,7 @@ rule plot_weather_benchmark: plot_file=RESULTS + "cba/ensemble_plots/ensemble_{cba_project}_{planning_horizons}.png", script: - "../scripts/cba/plot_benchmark_indicators.py" + scripts("cba/plot_benchmark_indicators.py") rule average_indicators_per_project_and_planning_horizon: @@ -558,7 +558,7 @@ rule average_indicators_per_project_and_planning_horizon: indicators=RESULTS + "cba/ensemble_indicators/ensemble_indicators_{cba_project}_{planning_horizons}.csv", script: - "../scripts/cba/average_indicators.py" + scripts("cba/average_indicators.py") rule summarize_indicators_per_project: @@ -572,7 +572,7 @@ rule summarize_indicators_per_project: output: plot_file=RESULTS + "cba/ensemble_plots/ensemble_{cba_project}_all_horizons.png", script: - "../scripts/cba/summarize_indicators.py" + scripts("cba/summarize_indicators.py") rule summarize_all_indicators: @@ -586,7 +586,7 @@ rule summarize_all_indicators: output: plot_file=RESULTS + "cba/ensemble_plots/ensemble_all.png", script: - "../scripts/cba/summarize_all.py" + scripts("cba/summarize_all.py") def cba_target_runs(w): diff --git a/rules/sb.smk b/rules/sb.smk index 9b491e5859..311794d8fe 100644 --- a/rules/sb.smk +++ b/rules/sb.smk @@ -588,7 +588,7 @@ if config["sector"]["h2_topology_tyndp"]: tyndp_scenario=config_provider("tyndp_scenario"), h2_zones_tyndp=config_provider("sector", "h2_zones_tyndp"), script: - "../scripts/sb/clean_tyndp_smr.py" + scripts("sb/clean_tyndp_smr.py") rule clean_tyndp_h2_storages: input: @@ -608,7 +608,7 @@ if config["sector"]["h2_topology_tyndp"]: tyndp_scenario=config_provider("tyndp_scenario"), h2_zones_tyndp=config_provider("sector", "h2_zones_tyndp"), script: - "../scripts/sb/clean_tyndp_h2_storages.py" + scripts("sb/clean_tyndp_h2_storages.py") if config["sector"]["offshore_hubs_tyndp"]["enable"]: From bc08555b1065e8dda6b073cac2004b1f1002445b Mon Sep 17 00:00:00 2001 From: Thomas Gilon Date: Tue, 23 Jun 2026 11:42:58 +0200 Subject: [PATCH 07/12] fix: fix typo in retrieve_cba_guidelines_reference_projects --- doc/cba_rules.md | 2 +- rules/cba.smk | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/cba_rules.md b/doc/cba_rules.md index 08ee625b39..250c1bd8fd 100644 --- a/doc/cba_rules.md +++ b/doc/cba_rules.md @@ -15,7 +15,7 @@ Downloads the CBA project explorer dataset, containing project definitions. Downloads non-CO₂ emission factors (NOx, SO₂, PM, etc.) used to compute B4 indicators. -### Rule `retreive_cba_guidelines_reference_projects` +### Rule `retrieve_cba_guidelines_reference_projects` Downloads the CBA Implementation Guidelines reference project table (`table_B1_CBA_Implementations_Guidelines_TYNDP2024.csv`) used to reconcile SB investments diff --git a/rules/cba.smk b/rules/cba.smk index 67a155df73..00a0b77f47 100644 --- a/rules/cba.smk +++ b/rules/cba.smk @@ -64,13 +64,13 @@ if (CBA_GUIDELINES_DATASET := dataset_version("cba_guidelines_reference_projects "source" ] in ARCHIVE_SOURCES: - rule retreive_cba_guidelines_reference_projects: + rule retrieve_cba_guidelines_reference_projects: input: file=storage(CBA_GUIDELINES_DATASET["url"]), output: file=f"{CBA_GUIDELINES_DATASET['folder']}/table_B1_CBA_Implementations_Guidelines_TYNDP2024.csv", log: - "logs/retreive_cba_guidelines_reference_projects.log", + "logs/retrieve_cba_guidelines_reference_projects.log", run: copy2(input["file"], output["file"]) @@ -159,7 +159,7 @@ checkpoint clean_projects: input: dir=rules.retrieve_tyndp_cba_projects.output.dir, buses=rules.retrieve_tyndp.output.nodes, - guidelines=rules.retreive_cba_guidelines_reference_projects.output.file, + guidelines=rules.retrieve_cba_guidelines_reference_projects.output.file, output: # TODO: The toot_projects and pint_projects outputs are likely only # transmission projects (no storage). In order to confirm, we should check @@ -274,7 +274,7 @@ def get_elec_project_build_years(w): rule fix_reference_sb_to_cba: input: invest_grid=rules.retrieve_tyndp.output.invest_grid, - guidelines=rules.retreive_cba_guidelines_reference_projects.output.file, + guidelines=rules.retrieve_cba_guidelines_reference_projects.output.file, transmission_projects=rules.clean_projects.output.transmission_projects, buses=rules.build_tyndp_network.output.substations_geojson, output: From 58632070f34c34988cdf58ded50cddb7e935101d Mon Sep 17 00:00:00 2001 From: Thomas Gilon Date: Tue, 23 Jun 2026 11:55:26 +0200 Subject: [PATCH 08/12] fix: change table format to markdown --- scripts/sb/build_pemmdb_data.py | 10 +++------- scripts/sb/build_renewable_profiles_pecd.py | 8 +++----- scripts/sb/build_tyndp_hydro_profile.py | 9 +++------ 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/scripts/sb/build_pemmdb_data.py b/scripts/sb/build_pemmdb_data.py index 0e99395045..860f09d94c 100644 --- a/scripts/sb/build_pemmdb_data.py +++ b/scripts/sb/build_pemmdb_data.py @@ -11,13 +11,9 @@ - `resources/pemmdb_capacities_{planning_horizon}.csv` in long format - `resources/pemmdb_profiles_{planning_horizon}.nc` with the following structure: - =================== ==================== ========================================================= - Field Coordinates Description - =================== ==================== ========================================================= - p_min_pu, time, bus, carrier, the per unit hourly availability and must-run obligations - p_max_pu index_carrier, for each bus and PEMMDB technology - open_tyndp_type - =================== ==================== ========================================================= + | Field | Coordinates | Description | + | ------------------ | -------------------------------------------------- | -------------------------------------------------------------------- | + | p_min_pu, p_max_pu | time, bus, carrier, index_carrier, open_tyndp_type | the per unit hourly availability and must-run obligations for each bus and PEMMDB technology | """ import logging diff --git a/scripts/sb/build_renewable_profiles_pecd.py b/scripts/sb/build_renewable_profiles_pecd.py index 307b601d18..b80ea145f9 100644 --- a/scripts/sb/build_renewable_profiles_pecd.py +++ b/scripts/sb/build_renewable_profiles_pecd.py @@ -13,11 +13,9 @@ - `resources/profile_pecd_{clusters}_{technology}.nc` with the following structure - =================== ==================== ========================================================= - Field Dimensions Description - =================== ==================== ========================================================= - profile year, bus, bin, time the per unit hourly availability factors for each bus - =================== ==================== ========================================================= + | Field | Dimensions | Description | + | ------- | -------------------- | -------------------------------------------------- | + | profile | year, bus, bin, time | the per unit hourly availability factors for each bus | """ import logging diff --git a/scripts/sb/build_tyndp_hydro_profile.py b/scripts/sb/build_tyndp_hydro_profile.py index 2b9acee2a0..6b7b59c36d 100644 --- a/scripts/sb/build_tyndp_hydro_profile.py +++ b/scripts/sb/build_tyndp_hydro_profile.py @@ -9,12 +9,9 @@ - `resources/profile_pemmdb_hydro.nc`: - =================== ================ ========================================================= - Field Dimensions Description - =================== ================ ========================================================= - inflow bus, time, Inflow to the state of charge (in MW), - year, hydro_tech e.g. due to river inflow in hydro reservoir. - =================== ================ ========================================================= + | Field | Dimensions | Description | + | ------ | ------------------------------- | ------------------------------------------------------------------------ | + | inflow | bus, time, year, hydro_tech | Inflow to the state of charge (in MW), e.g. due to river inflow in hydro reservoir. | """ import logging From 9237d7184fed1275044e13eac860f28fe28e31b2 Mon Sep 17 00:00:00 2001 From: Thomas Gilon Date: Tue, 23 Jun 2026 12:04:11 +0200 Subject: [PATCH 09/12] fix: use proper lists and links in make_indicators --- scripts/cba/make_indicators.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/scripts/cba/make_indicators.py b/scripts/cba/make_indicators.py index 9ee06026fd..db2dc67db7 100644 --- a/scripts/cba/make_indicators.py +++ b/scripts/cba/make_indicators.py @@ -9,23 +9,24 @@ cases. PINT (Put In at a Time): - - Reference: Network WITHOUT any projects - - Project: Network WITH the specific project added - - B1 = OPEX(reference) - OPEX(with project) + +- Reference: Network WITHOUT any projects +- Project: Network WITH the specific project added +- B1 = OPEX(reference) - OPEX(with project) TOOT (Take Out One at a Time): - - Reference: Network WITH all projects (current plan) - - Project: Network WITHOUT the specific project (removed) - - B1 = OPEX(without project) - OPEX(reference) + +- Reference: Network WITH all projects (current plan) +- Project: Network WITHOUT the specific project (removed) +- B1 = OPEX(without project) - OPEX(reference) References: -- CBA guidelines: https://eepublicdownloads.blob.core.windows.net/public-cdn-container/clean-documents/news/2024/entso-e_4th_CBA_Guideline_240409.pdf - - section 3.2.2: TOOT and PINT, page 23-24 -- CBA implementation guidelines: https://eepublicdownloads.blob.core.windows.net/public-cdn-container/tyndp-documents/TYNDP2024/foropinion/CBA_Implementation_Guidelines.pdf - - section 5.1: B1 - SEW, page 58-59 -""" +- [CBA guidelines](https://eepublicdownloads.blob.core.windows.net/public-cdn-container/clean-documents/news/2024/entso-e_4th_CBA_Guideline_240409.pdf), section 3.2.2: TOOT and PINT, page 23-24 +- [CBA implementation guidelines](https://eepublicdownloads.blob.core.windows.net/public-cdn-container/tyndp-documents/TYNDP2024/foropinion/CBA_Implementation_Guidelines.pdf), section 5.1: B1 - SEW, page 58-59 + +""" # noqa: D412 import logging from pathlib import Path From f4e1207b7d02138711eabdd9f2c0c3bb734b37df Mon Sep 17 00:00:00 2001 From: Thomas Gilon Date: Tue, 23 Jun 2026 12:16:56 +0200 Subject: [PATCH 10/12] doc: add release note --- doc/release_notes.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/release_notes.md b/doc/release_notes.md index 0a20775256..6345926745 100644 --- a/doc/release_notes.md +++ b/doc/release_notes.md @@ -37,7 +37,9 @@ * Migrate the Sphinx/RST-based documentation to MkDocs/Markdown, as a follow-up to the [upstream migration](https://github.com/PyPSA/pypsa-eur/pull/2162) ([754](https://github.com/open-energy-transition/open-tyndp/pull/754)). -* Improve docstring formatting and add missing type hints (https://github.com/open-energy-transition/open-tyndp/pull/759). +* Improve docstring formatting and add missing type hints ([759](https://github.com/open-energy-transition/open-tyndp/pull/759)). + +* Create rules overview for SB and CBA rules ([761](https://github.com/open-energy-transition/open-tyndp/pull/761)) **Developers Note** From de73f5417d205b3b3402057736b52d3c12addc9d Mon Sep 17 00:00:00 2001 From: Thomas Gilon Date: Fri, 10 Jul 2026 11:36:23 +0200 Subject: [PATCH 11/12] Apply suggestions from code review --- doc/cba_rules.md | 16 +++++++++------- doc/sb_rules.md | 6 +++--- rules/cba.smk | 4 ++-- rules/sb.smk | 6 +++--- scripts/prepare_sector_network.py | 2 +- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/doc/cba_rules.md b/doc/cba_rules.md index 250c1bd8fd..ec95193b1d 100644 --- a/doc/cba_rules.md +++ b/doc/cba_rules.md @@ -3,7 +3,7 @@ # Cost-Benefit Analysis (CBA) -The Cost-Benefit Analysis (CBA) workflow is implemented in `rules/cba.smk`. Rules are organised into stages: `Retrieve`, `Build MSV`, `Build rolling`, `Postprocess`, `Benchmarking` and `Collect`. +The Cost-Benefit Analysis (CBA) workflow is implemented in `rules/cba.smk`. Rules are organised into stages: `Retrieve`, `Build MSV`, `Build rolling horizon`, `Postprocess` and `Benchmark`. In addition, a `Collect` stage provides convenient target rules that run the workflow up to a specific stage, aggregating each stage's output across all configured wildcards. ## Retrieve @@ -52,11 +52,7 @@ when `cba.cba_scenario_input.use_presolved` is `true`. ::: build_msv_snapshot_weightings -### Rule `solve_cba_msv_extraction` - -::: solve_cba_msv_extraction - -## Build rolling +## Build rolling horizon ### Rule `prepare_rolling_horizon` @@ -66,6 +62,12 @@ when `cba.cba_scenario_input.use_presolved` is `true`. ::: prepare_project +## Solve + +### Rule `solve_cba_msv_extraction` + +::: solve_cba_msv_extraction + ### Rule `solve_cba_reference_network` Solves the reference network using the rolling horizon approach. Shares the script with @@ -93,7 +95,7 @@ Solves the CBA network using the rolling horizon approach. Shares the script wit ::: plot_indicators -## Benchmarking +## Benchmark ### Rule `plot_cba_benchmark` diff --git a/doc/sb_rules.md b/doc/sb_rules.md index 6ae8c38733..8e9335c74b 100644 --- a/doc/sb_rules.md +++ b/doc/sb_rules.md @@ -3,7 +3,7 @@ # Scenario Building (SB) -The Scenario Building (SB) workflow is implemented in `rules/sb.smk`. Rules are organised into stages: `Retrieve`, `Development`, `Build electricity`, `Build sector`, `Postprocess`, `Benchmarking`, `Collect` and `Visualize`. +The Scenario Building (SB) workflow is implemented in `rules/sb.smk`. Rules are organised into stages: `Retrieve`, `Development`, `Build electricity`, `Build sector`, `Postprocess`, `Benchmark` and `Explore`. In addition, a `Collect` stage provides convenient target rules that run the workflow up to a specific stage, aggregating each stage's output across all configured wildcards. ## Retrieve @@ -156,7 +156,7 @@ the electricity demand for base regions from the TYNDP electricity demand prepar ::: plot_offshore_network -## Benchmarking +## Benchmark ### Rule `clean_tyndp_output_benchmark` @@ -218,7 +218,7 @@ Aggregate [`build_tyndp_h2_demand`](#rule-build_tyndp_h2_demand) outputs. Aggregate [`build_tyndp_gas_demand`](#rule-build_tyndp_gas_demand) outputs. -## Visualize +## Explore ### Rule `launch_explorer` diff --git a/rules/cba.smk b/rules/cba.smk index 00a0b77f47..13b687d527 100644 --- a/rules/cba.smk +++ b/rules/cba.smk @@ -353,8 +353,8 @@ rule solve_cba_msv_extraction: scripts("cba/solve_cba_msv_extraction.py") -# Build rolling -################ +# Build rolling horizon +####################### # Prepare network for rolling horizon: disable seasonal cyclicity, apply marginal storage value diff --git a/rules/sb.smk b/rules/sb.smk index 96a4b5cd38..8b9b7c6cae 100644 --- a/rules/sb.smk +++ b/rules/sb.smk @@ -756,8 +756,8 @@ if config["foresight"] != "perfect": expanded=True, -# Benchmarking -############## +# Benchmark +########### if config["benchmarking"]["enable"]: @@ -1103,7 +1103,7 @@ rule build_tyndp_gas_demands: ), -# Visualize +# Explore ########### diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index e06dc22dc1..fd5ceb92e7 100755 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -3929,7 +3929,7 @@ def add_h2_storage_tyndp( n : pypsa.Network The PyPSA network container object. buses_h2_z1 : pd.Index - Nnodes of H2 Z1 buses. + Nodes of H2 Z1 buses. buses_h2_z2 : pd.Index Nodes of H2 Z2 buses. costs : pd.DataFrame From 0447ec586b3061383b5c7bb55065040bde66c60f Mon Sep 17 00:00:00 2001 From: Thomas Gilon Date: Fri, 10 Jul 2026 11:37:35 +0200 Subject: [PATCH 12/12] refactor: rename collect_indicators to combine_indicators --- doc/cba_rules.md | 4 ++-- rules/cba.smk | 12 ++++++------ scripts/cba/average_indicators.py | 4 +++- .../{collect_indicators.py => combine_indicators.py} | 12 ++++++------ scripts/cba/plot_indicators.py | 2 +- scripts/cba/summarize_all.py | 2 +- scripts/cba/summarize_indicators.py | 2 +- 7 files changed, 20 insertions(+), 18 deletions(-) rename scripts/cba/{collect_indicators.py => combine_indicators.py} (85%) diff --git a/doc/cba_rules.md b/doc/cba_rules.md index ec95193b1d..6887c4f517 100644 --- a/doc/cba_rules.md +++ b/doc/cba_rules.md @@ -87,9 +87,9 @@ Solves the CBA network using the rolling horizon approach. Shares the script wit ::: make_indicators -### Rule `collect_indicators` +### Rule `combine_indicators` -::: collect_indicators +::: combine_indicators ### Rule `plot_indicators` diff --git a/rules/cba.smk b/rules/cba.smk index 13b687d527..fc76cc9926 100644 --- a/rules/cba.smk +++ b/rules/cba.smk @@ -490,19 +490,19 @@ def input_indicators(w): ) -# Collect indicators for all projects into overview CSV -rule collect_indicators: +# Combine indicators for all projects into overview CSV +rule combine_indicators: input: indicators=input_indicators, output: indicators=RESULTS + "cba/indicators_{planning_horizons}.csv", script: - scripts("cba/collect_indicators.py") + scripts("cba/combine_indicators.py") rule plot_indicators: input: - indicators=rules.collect_indicators.output.indicators, + indicators=rules.combine_indicators.output.indicators, transmission_projects=rules.clean_projects.output.transmission_projects, output: plot_dir=directory(RESULTS + "cba/plots_{planning_horizons}"), @@ -528,7 +528,7 @@ rule plot_cba_benchmark: # rule plot_all_cba_benchmark: # input: -# indicators=rules.collect_indicators.output.indicators, +# indicators=rules.combine_indicators.output.indicators, # output: # plot_dir=directory(RESULTS + "cba/validation_{planning_horizons}"), # script: @@ -537,7 +537,7 @@ rule plot_cba_benchmark: rule plot_weather_benchmark: input: - # indicators=rules.collect_indicators.output.indicators, + # indicators=rules.combine_indicators.output.indicators, indicators=rules.make_indicators.output.indicators, output: plot_file=RESULTS diff --git a/scripts/cba/average_indicators.py b/scripts/cba/average_indicators.py index 07f2ae3c06..5868f4ac0f 100644 --- a/scripts/cba/average_indicators.py +++ b/scripts/cba/average_indicators.py @@ -182,7 +182,9 @@ def average_indicators_csv( if "snakemake" not in globals(): from scripts._helpers import mock_snakemake - snakemake = mock_snakemake("collect_indicators") + snakemake = mock_snakemake( + "average_indicators_per_project_and_planning_horizon" + ) configure_logging(snakemake) set_scenario_config(snakemake) diff --git a/scripts/cba/collect_indicators.py b/scripts/cba/combine_indicators.py similarity index 85% rename from scripts/cba/collect_indicators.py rename to scripts/cba/combine_indicators.py index ed64d830a0..4d5c9e30aa 100644 --- a/scripts/cba/collect_indicators.py +++ b/scripts/cba/combine_indicators.py @@ -17,7 +17,7 @@ logger = logging.getLogger(__name__) -def collect_indicators_csv(input_files: list[str], output_file: str) -> None: +def combine_indicators_csv(input_files: list[str], output_file: str) -> None: """ Concatenate multiple CSV files into one using the csv module. @@ -38,7 +38,7 @@ def collect_indicators_csv(input_files: list[str], output_file: str) -> None: pass return - logger.info(f"Collecting {len(input_files)} indicator files") + logger.info(f"Combining {len(input_files)} indicator files") # Read header from first file with open(input_files[0], newline="") as f: @@ -68,17 +68,17 @@ def collect_indicators_csv(input_files: list[str], output_file: str) -> None: writer.writerow(row) row_count += 1 - logger.info(f"Collected {row_count} rows from {len(input_files)} files") + logger.info(f"Combined {row_count} rows from {len(input_files)} files") if __name__ == "__main__": if "snakemake" not in globals(): from scripts._helpers import mock_snakemake - snakemake = mock_snakemake("collect_indicators") + snakemake = mock_snakemake("combine_indicators") configure_logging(snakemake) set_scenario_config(snakemake) - # Collect all indicators into a single CSV - collect_indicators_csv(snakemake.input.indicators, snakemake.output.indicators) + # Combine all indicators into a single CSV + combine_indicators_csv(snakemake.input.indicators, snakemake.output.indicators) diff --git a/scripts/cba/plot_indicators.py b/scripts/cba/plot_indicators.py index 0a92b810f5..4e069e24ce 100644 --- a/scripts/cba/plot_indicators.py +++ b/scripts/cba/plot_indicators.py @@ -5,7 +5,7 @@ """ Create plots for CBA indicators. -This script reads the collected indicators CSV file and generates various +This script reads the combined indicators CSV file and generates various plots to visualize the cost-benefit analysis results, including the B1 indicator (Total System Cost difference). """ diff --git a/scripts/cba/summarize_all.py b/scripts/cba/summarize_all.py index 6cbca2c559..f884fd0ba4 100644 --- a/scripts/cba/summarize_all.py +++ b/scripts/cba/summarize_all.py @@ -229,7 +229,7 @@ def create_plots( if "snakemake" not in globals(): from scripts._helpers import mock_snakemake - snakemake = mock_snakemake("collect_indicators") + snakemake = mock_snakemake("summarize_all_indicators") configure_logging(snakemake) set_scenario_config(snakemake) diff --git a/scripts/cba/summarize_indicators.py b/scripts/cba/summarize_indicators.py index f55f293e54..62219d2285 100644 --- a/scripts/cba/summarize_indicators.py +++ b/scripts/cba/summarize_indicators.py @@ -374,7 +374,7 @@ def summarize_indicators(input_files: list[str], output_file: str) -> None: if "snakemake" not in globals(): from scripts._helpers import mock_snakemake - snakemake = mock_snakemake("collect_indicators") + snakemake = mock_snakemake("summarize_indicators") configure_logging(snakemake) set_scenario_config(snakemake)