From 0c8ad8b356a46334b3f7ac982449b34f51056055 Mon Sep 17 00:00:00 2001 From: Fabian Neumann Date: Wed, 18 Feb 2026 09:13:05 +0100 Subject: [PATCH 01/16] Add option to specify fixed DC-AC ratio for solar PV --- config/config.default.yaml | 2 ++ config/schema.default.json | 30 ++++++++++++++++++++++ scripts/build_renewable_profiles.py | 6 +++++ scripts/lib/validation/config/renewable.py | 5 ++++ 4 files changed, 43 insertions(+) diff --git a/config/config.default.yaml b/config/config.default.yaml index 9e8e27d8d8..4eb0a95480 100644 --- a/config/config.default.yaml +++ b/config/config.default.yaml @@ -368,6 +368,7 @@ renewable: natura: true excluder_resolution: 100 clip_p_max_pu: 0.01 + dc_ac_ratio: 1.3 "solar-hsat": cutout: default resource: @@ -408,6 +409,7 @@ renewable: natura: true excluder_resolution: 100 clip_p_max_pu: 0.01 + dc_ac_ratio: 1.3 hydro: cutout: default carriers: diff --git a/config/schema.default.json b/config/schema.default.json index 078a459c2b..81b2851446 100644 --- a/config/schema.default.json +++ b/config/schema.default.json @@ -3690,6 +3690,12 @@ "default": 0.01, "description": "To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", "type": "number" + }, + "dc_ac_ratio": { + "default": 1.3, + "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", + "exclusiveMinimum": 0, + "type": "number" } } }, @@ -3814,6 +3820,12 @@ "default": 0.01, "description": "To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", "type": "number" + }, + "dc_ac_ratio": { + "default": 1.3, + "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", + "exclusiveMinimum": 0, + "type": "number" } } }, @@ -7529,6 +7541,12 @@ "default": 0.01, "description": "To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", "type": "number" + }, + "dc_ac_ratio": { + "default": 1.3, + "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", + "exclusiveMinimum": 0, + "type": "number" } } }, @@ -9552,6 +9570,12 @@ "default": 0.01, "description": "To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", "type": "number" + }, + "dc_ac_ratio": { + "default": 1.3, + "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", + "exclusiveMinimum": 0, + "type": "number" } } }, @@ -9676,6 +9700,12 @@ "default": 0.01, "description": "To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", "type": "number" + }, + "dc_ac_ratio": { + "default": 1.3, + "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", + "exclusiveMinimum": 0, + "type": "number" } } }, diff --git a/scripts/build_renewable_profiles.py b/scripts/build_renewable_profiles.py index 724d002e16..d17d3041c9 100644 --- a/scripts/build_renewable_profiles.py +++ b/scripts/build_renewable_profiles.py @@ -321,6 +321,12 @@ ) ) + dc_ac_ratio = params.get("dc_ac_ratio", 1.0) + if dc_ac_ratio != 1.0: + logger.info(f"Applying DC/AC ratio of {dc_ac_ratio} to solar profiles") + ds["profile"] = ds["profile"].clip(max=1 / dc_ac_ratio) * dc_ac_ratio + ds["p_nom_max"] = ds["p_nom_max"] / dc_ac_ratio + if "clip_p_max_pu" in params: min_p_max_pu = params["clip_p_max_pu"] ds["profile"] = ds["profile"].where(ds["profile"] >= min_p_max_pu, 0) diff --git a/scripts/lib/validation/config/renewable.py b/scripts/lib/validation/config/renewable.py index 7d549419f3..a4f0134b52 100644 --- a/scripts/lib/validation/config/renewable.py +++ b/scripts/lib/validation/config/renewable.py @@ -256,6 +256,11 @@ class _SolarConfig(BaseModel): 0.01, description="To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", ) + dc_ac_ratio: float = Field( + 1.3, + description="DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", + gt=0, + ) class _HydroConfig(BaseModel): From dc24ebae75b2bdec15d98523326fe2f1d1861f70 Mon Sep 17 00:00:00 2001 From: Michael Lindner Date: Fri, 27 Feb 2026 16:26:34 +0100 Subject: [PATCH 02/16] keep dc profiles but clip them correctly --- scripts/build_renewable_profiles.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/build_renewable_profiles.py b/scripts/build_renewable_profiles.py index d17d3041c9..59f64b41d8 100644 --- a/scripts/build_renewable_profiles.py +++ b/scripts/build_renewable_profiles.py @@ -324,8 +324,8 @@ dc_ac_ratio = params.get("dc_ac_ratio", 1.0) if dc_ac_ratio != 1.0: logger.info(f"Applying DC/AC ratio of {dc_ac_ratio} to solar profiles") - ds["profile"] = ds["profile"].clip(max=1 / dc_ac_ratio) * dc_ac_ratio - ds["p_nom_max"] = ds["p_nom_max"] / dc_ac_ratio + ds["profile"] = ds["profile"].clip(max=1 / dc_ac_ratio) + ds["p_nom_max"] = ds["p_nom_max"] if "clip_p_max_pu" in params: min_p_max_pu = params["clip_p_max_pu"] From ca23b65b7b1ec8a8f94c763e000b09be2999482a Mon Sep 17 00:00:00 2001 From: Michael Lindner Date: Fri, 27 Feb 2026 17:30:21 +0100 Subject: [PATCH 03/16] add switch if costs are given in DC --- scripts/add_electricity.py | 4 ++++ scripts/prepare_sector_network.py | 12 +++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/scripts/add_electricity.py b/scripts/add_electricity.py index 0df64d148f..e89a68dc50 100755 --- a/scripts/add_electricity.py +++ b/scripts/add_electricity.py @@ -460,6 +460,7 @@ def attach_wind_and_solar( extendable_carriers: list | set, line_length_factor: float = 1.0, landfall_lengths: dict = None, + params_renewable: dict = None, ) -> None: """ Attach wind and solar generators to the network. @@ -531,6 +532,8 @@ def attach_wind_and_solar( logger.info( f"Added connection cost of {connection_cost.min():0.0f}-{connection_cost.max():0.0f} Eur/MW/a to {car}" ) + elif params_renewable[car].get("costs_given_for_ac") and params_renewable[car].get("dc_ac_ratio", 1.0) != 1.0: + capital_cost = costs.at[car, "capital_cost"] / params_renewable[car]["dc_ac_ratio"] else: capital_cost = costs.at[car, "capital_cost"] @@ -1211,6 +1214,7 @@ def attach_stores( extendable_carriers, params.line_length_factor, landfall_lengths, + params.renewable, ) if "hydro" in renewable_carriers: diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index d9434ef76e..7824dc9bc8 100755 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -427,6 +427,7 @@ def update_wind_solar_costs( profiles: dict[str, str], landfall_lengths: dict = None, line_length_factor: int | float = 1, + params_renewable: dict = None, ) -> None: """ Update costs for wind and solar generators added with pypsa-eur to those @@ -445,6 +446,8 @@ def update_wind_solar_costs( profiles : dict[str, str] Dictionary mapping technology names to profile file paths e.g. {'offwind-dc': 'path/to/profile.nc'} + params_renewable : dict, optional + Dictionary of renewable parameters, by default None """ if landfall_lengths is None: @@ -464,6 +467,9 @@ def update_wind_solar_costs( n.generators.loc[n.generators.carrier == carrier, "capital_cost"] = costs.at[ cost_key, "capital_cost" ] + if params_renewable[carrier].get("costs_given_for_ac") and params_renewable[carrier].get("dc_ac_ratio", 1.0) != 1.0: + n.generators.loc[n.generators.carrier == carrier, "capital_cost"] /= params_renewable[carrier]["dc_ac_ratio"] + # for offshore wind, need to calculated connection costs for key, fn in profiles.items(): @@ -689,11 +695,11 @@ def remove_non_electric_buses(n): n.buses = n.buses[n.buses.carrier.isin(["AC", "DC"])] -def patch_electricity_network(n, costs, carriers_to_keep, profiles, landfall_lengths): +def patch_electricity_network(n, costs, carriers_to_keep, profiles, landfall_lengths, params_renewable=None): remove_elec_base_techs(n, carriers_to_keep) remove_non_electric_buses(n) update_wind_solar_costs( - n, costs, landfall_lengths=landfall_lengths, profiles=profiles + n, costs, landfall_lengths=landfall_lengths, profiles=profiles, params_renewable=params_renewable ) n.loads["carrier"] = "electricity" n.buses["location"] = n.buses.index @@ -6287,7 +6293,7 @@ def add_import_options( for tech, settings in snakemake.params.renewable.items() if "landfall_length" in settings.keys() } - patch_electricity_network(n, costs, carriers_to_keep, profiles, landfall_lengths) + patch_electricity_network(n, costs, carriers_to_keep, profiles, landfall_lengths, snakemake.params.renewable) fn = snakemake.input.heating_efficiencies year = int(snakemake.params["energy_totals_year"]) From c6fdfa7fcc82f2f64b4199cd12592190b43fb373 Mon Sep 17 00:00:00 2001 From: Michael Lindner Date: Fri, 27 Feb 2026 17:30:32 +0100 Subject: [PATCH 04/16] scale grid expansion cost to dc --- scripts/prepare_sector_network.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index 7824dc9bc8..7338f48902 100755 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -1743,14 +1743,15 @@ def insert_gas_distribution_costs( n.links.loc[mchp, "capital_cost"] += capital_cost -def add_electricity_grid_connection(n, costs): +def add_electricity_grid_connection(n, costs, params_renewables): carriers = ["onwind", "solar", "solar-hsat"] - - gens = n.generators.index[n.generators.carrier.isin(carriers)] - - n.generators.loc[gens, "capital_cost"] += costs.at[ - "electricity grid connection", "capital_cost" - ] + for car in carriers: + + gens = n.generators.index[n.generators.carrier == car] + dc_ac_ratio = params_renewables[car].get("dc_ac_ratio", 1.0) + n.generators.loc[gens, "capital_cost"] += costs.at[ + "electricity grid connection", "capital_cost" + ] / dc_ac_ratio def add_h2_gas_infrastructure( @@ -6576,7 +6577,7 @@ def add_import_options( insert_gas_distribution_costs(n, costs, options=options) if options["electricity_grid_connection"]: - add_electricity_grid_connection(n, costs) + add_electricity_grid_connection(n, costs, snakemake.param["renewable"]) for k, v in options["transmission_efficiency"].items(): if k in options["transmission_efficiency"]["enable"]: From 6dab084f6c98a2fe2f17cecb4b5a8019c00c986e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 16:30:51 +0000 Subject: [PATCH 05/16] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- scripts/add_electricity.py | 9 ++++++-- scripts/build_renewable_profiles.py | 2 +- scripts/prepare_sector_network.py | 36 +++++++++++++++++++++-------- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/scripts/add_electricity.py b/scripts/add_electricity.py index e89a68dc50..9a7ac9ba6f 100755 --- a/scripts/add_electricity.py +++ b/scripts/add_electricity.py @@ -532,8 +532,13 @@ def attach_wind_and_solar( logger.info( f"Added connection cost of {connection_cost.min():0.0f}-{connection_cost.max():0.0f} Eur/MW/a to {car}" ) - elif params_renewable[car].get("costs_given_for_ac") and params_renewable[car].get("dc_ac_ratio", 1.0) != 1.0: - capital_cost = costs.at[car, "capital_cost"] / params_renewable[car]["dc_ac_ratio"] + elif ( + params_renewable[car].get("costs_given_for_ac") + and params_renewable[car].get("dc_ac_ratio", 1.0) != 1.0 + ): + capital_cost = ( + costs.at[car, "capital_cost"] / params_renewable[car]["dc_ac_ratio"] + ) else: capital_cost = costs.at[car, "capital_cost"] diff --git a/scripts/build_renewable_profiles.py b/scripts/build_renewable_profiles.py index 59f64b41d8..186c382e1b 100644 --- a/scripts/build_renewable_profiles.py +++ b/scripts/build_renewable_profiles.py @@ -324,7 +324,7 @@ dc_ac_ratio = params.get("dc_ac_ratio", 1.0) if dc_ac_ratio != 1.0: logger.info(f"Applying DC/AC ratio of {dc_ac_ratio} to solar profiles") - ds["profile"] = ds["profile"].clip(max=1 / dc_ac_ratio) + ds["profile"] = ds["profile"].clip(max=1 / dc_ac_ratio) ds["p_nom_max"] = ds["p_nom_max"] if "clip_p_max_pu" in params: diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index 7338f48902..e7e1c1cdab 100755 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -467,9 +467,13 @@ def update_wind_solar_costs( n.generators.loc[n.generators.carrier == carrier, "capital_cost"] = costs.at[ cost_key, "capital_cost" ] - if params_renewable[carrier].get("costs_given_for_ac") and params_renewable[carrier].get("dc_ac_ratio", 1.0) != 1.0: - n.generators.loc[n.generators.carrier == carrier, "capital_cost"] /= params_renewable[carrier]["dc_ac_ratio"] - + if ( + params_renewable[carrier].get("costs_given_for_ac") + and params_renewable[carrier].get("dc_ac_ratio", 1.0) != 1.0 + ): + n.generators.loc[n.generators.carrier == carrier, "capital_cost"] /= ( + params_renewable[carrier]["dc_ac_ratio"] + ) # for offshore wind, need to calculated connection costs for key, fn in profiles.items(): @@ -695,11 +699,17 @@ def remove_non_electric_buses(n): n.buses = n.buses[n.buses.carrier.isin(["AC", "DC"])] -def patch_electricity_network(n, costs, carriers_to_keep, profiles, landfall_lengths, params_renewable=None): +def patch_electricity_network( + n, costs, carriers_to_keep, profiles, landfall_lengths, params_renewable=None +): remove_elec_base_techs(n, carriers_to_keep) remove_non_electric_buses(n) update_wind_solar_costs( - n, costs, landfall_lengths=landfall_lengths, profiles=profiles, params_renewable=params_renewable + n, + costs, + landfall_lengths=landfall_lengths, + profiles=profiles, + params_renewable=params_renewable, ) n.loads["carrier"] = "electricity" n.buses["location"] = n.buses.index @@ -1746,12 +1756,11 @@ def insert_gas_distribution_costs( def add_electricity_grid_connection(n, costs, params_renewables): carriers = ["onwind", "solar", "solar-hsat"] for car in carriers: - gens = n.generators.index[n.generators.carrier == car] dc_ac_ratio = params_renewables[car].get("dc_ac_ratio", 1.0) - n.generators.loc[gens, "capital_cost"] += costs.at[ - "electricity grid connection", "capital_cost" - ] / dc_ac_ratio + n.generators.loc[gens, "capital_cost"] += ( + costs.at["electricity grid connection", "capital_cost"] / dc_ac_ratio + ) def add_h2_gas_infrastructure( @@ -6294,7 +6303,14 @@ def add_import_options( for tech, settings in snakemake.params.renewable.items() if "landfall_length" in settings.keys() } - patch_electricity_network(n, costs, carriers_to_keep, profiles, landfall_lengths, snakemake.params.renewable) + patch_electricity_network( + n, + costs, + carriers_to_keep, + profiles, + landfall_lengths, + snakemake.params.renewable, + ) fn = snakemake.input.heating_efficiencies year = int(snakemake.params["energy_totals_year"]) From 6ab77fb5cce51d580938d5b60a98d54d192876f8 Mon Sep 17 00:00:00 2001 From: Michael Lindner Date: Mon, 2 Mar 2026 10:20:16 +0100 Subject: [PATCH 06/16] add new config to indicate if solar costs are given in ac or in dc --- config/config.default.yaml | 2 ++ config/schema.default.json | 26 +++++++++++++++++++++- scripts/lib/validation/config/renewable.py | 4 ++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/config/config.default.yaml b/config/config.default.yaml index 4eb0a95480..ab2f51bdd1 100644 --- a/config/config.default.yaml +++ b/config/config.default.yaml @@ -369,6 +369,7 @@ renewable: excluder_resolution: 100 clip_p_max_pu: 0.01 dc_ac_ratio: 1.3 + costs_given_for_ac: true "solar-hsat": cutout: default resource: @@ -410,6 +411,7 @@ renewable: excluder_resolution: 100 clip_p_max_pu: 0.01 dc_ac_ratio: 1.3 + costs_given_for_ac: true hydro: cutout: default carriers: diff --git a/config/schema.default.json b/config/schema.default.json index 81b2851446..5de89902c9 100644 --- a/config/schema.default.json +++ b/config/schema.default.json @@ -3696,7 +3696,11 @@ "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", "exclusiveMinimum": 0, "type": "number" - } + }, + "costs_given_for_ac": { + "default": true, + "description": "Switch to indicate that the capital_costs for solar are given for AC capacity. If `false`, costs are given for DC peak capacity.", + "type": "boolean" } }, "solar-hsat": { @@ -3826,6 +3830,11 @@ "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", "exclusiveMinimum": 0, "type": "number" + }, + "costs_given_for_ac": { + "default": true, + "description": "Switch to indicate that the capital_costs for solar are given for AC capacity. If `false`, costs are given for DC peak capacity.", + "type": "boolean" } } }, @@ -7547,6 +7556,11 @@ "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", "exclusiveMinimum": 0, "type": "number" + }, + "costs_given_for_ac": { + "default": true, + "description": "Switch to indicate that the capital_costs for solar are given for AC capacity. If `false`, costs are given for DC peak capacity.", + "type": "boolean" } } }, @@ -9576,6 +9590,11 @@ "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", "exclusiveMinimum": 0, "type": "number" + }, + "costs_given_for_ac": { + "default": true, + "description": "Switch to indicate that the capital_costs for solar are given for AC capacity. If `false`, costs are given for DC peak capacity.", + "type": "boolean" } } }, @@ -9706,6 +9725,11 @@ "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", "exclusiveMinimum": 0, "type": "number" + }, + "costs_given_for_ac": { + "default": true, + "description": "Switch to indicate that the capital_costs for solar are given for AC capacity. If `false`, costs are given for DC peak capacity.", + "type": "boolean" } } }, diff --git a/scripts/lib/validation/config/renewable.py b/scripts/lib/validation/config/renewable.py index a4f0134b52..46d6fbd546 100644 --- a/scripts/lib/validation/config/renewable.py +++ b/scripts/lib/validation/config/renewable.py @@ -261,6 +261,10 @@ class _SolarConfig(BaseModel): description="DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", gt=0, ) + costs_given_for_ac: bool = Field( + True, + description="Switch to indicate that the capital_costs for solar are given for AC capacity. If `false`, costs are given for DC peak capacity.", + ) class _HydroConfig(BaseModel): From 7581116336b7db9eadfbcc34714449f738b019c8 Mon Sep 17 00:00:00 2001 From: Michael Lindner Date: Mon, 2 Mar 2026 10:23:14 +0100 Subject: [PATCH 07/16] more reasonable bound for dc/ac ratio --- config/schema.default.json | 10 +++++----- scripts/lib/validation/config/renewable.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/config/schema.default.json b/config/schema.default.json index 5de89902c9..950ad07d54 100644 --- a/config/schema.default.json +++ b/config/schema.default.json @@ -3694,7 +3694,7 @@ "dc_ac_ratio": { "default": 1.3, "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", - "exclusiveMinimum": 0, + "minimum": 1, "type": "number" }, "costs_given_for_ac": { @@ -3828,7 +3828,7 @@ "dc_ac_ratio": { "default": 1.3, "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", - "exclusiveMinimum": 0, + "minimum": 1, "type": "number" }, "costs_given_for_ac": { @@ -7554,7 +7554,7 @@ "dc_ac_ratio": { "default": 1.3, "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", - "exclusiveMinimum": 0, + "minimum": 1, "type": "number" }, "costs_given_for_ac": { @@ -9588,7 +9588,7 @@ "dc_ac_ratio": { "default": 1.3, "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", - "exclusiveMinimum": 0, + "minimum": 1, "type": "number" }, "costs_given_for_ac": { @@ -9723,7 +9723,7 @@ "dc_ac_ratio": { "default": 1.3, "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", - "exclusiveMinimum": 0, + "minimum": 1, "type": "number" }, "costs_given_for_ac": { diff --git a/scripts/lib/validation/config/renewable.py b/scripts/lib/validation/config/renewable.py index 46d6fbd546..cfb222fb63 100644 --- a/scripts/lib/validation/config/renewable.py +++ b/scripts/lib/validation/config/renewable.py @@ -259,7 +259,7 @@ class _SolarConfig(BaseModel): dc_ac_ratio: float = Field( 1.3, description="DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", - gt=0, + ge=1, ) costs_given_for_ac: bool = Field( True, From d47c739054df106cf41c175f84e624fa09205a0b Mon Sep 17 00:00:00 2001 From: Michael Lindner Date: Mon, 2 Mar 2026 10:27:08 +0100 Subject: [PATCH 08/16] pixi run generate-config --- config/schema.default.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/config/schema.default.json b/config/schema.default.json index 950ad07d54..d2eed43b77 100644 --- a/config/schema.default.json +++ b/config/schema.default.json @@ -3701,6 +3701,7 @@ "default": true, "description": "Switch to indicate that the capital_costs for solar are given for AC capacity. If `false`, costs are given for DC peak capacity.", "type": "boolean" + } } }, "solar-hsat": { @@ -7558,9 +7559,9 @@ "type": "number" }, "costs_given_for_ac": { - "default": true, - "description": "Switch to indicate that the capital_costs for solar are given for AC capacity. If `false`, costs are given for DC peak capacity.", - "type": "boolean" + "default": true, + "description": "Switch to indicate that the capital_costs for solar are given for AC capacity. If `false`, costs are given for DC peak capacity.", + "type": "boolean" } } }, From 0e2fdbeb8bbc9aa4af208eee430a1ce594712586 Mon Sep 17 00:00:00 2001 From: Michael Lindner Date: Mon, 2 Mar 2026 11:36:56 +0100 Subject: [PATCH 09/16] fix typo --- scripts/prepare_sector_network.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index e7e1c1cdab..e47ef35c27 100755 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -6593,7 +6593,7 @@ def add_import_options( insert_gas_distribution_costs(n, costs, options=options) if options["electricity_grid_connection"]: - add_electricity_grid_connection(n, costs, snakemake.param["renewable"]) + add_electricity_grid_connection(n, costs, snakemake.params["renewable"]) for k, v in options["transmission_efficiency"].items(): if k in options["transmission_efficiency"]["enable"]: From 30598769f145b5adfac2af838f59b2d9a9b1b403 Mon Sep 17 00:00:00 2001 From: Michael Lindner Date: Mon, 2 Mar 2026 12:45:57 +0100 Subject: [PATCH 10/16] improve logger message --- scripts/build_renewable_profiles.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/build_renewable_profiles.py b/scripts/build_renewable_profiles.py index 186c382e1b..552ff4b0d5 100644 --- a/scripts/build_renewable_profiles.py +++ b/scripts/build_renewable_profiles.py @@ -323,9 +323,8 @@ dc_ac_ratio = params.get("dc_ac_ratio", 1.0) if dc_ac_ratio != 1.0: - logger.info(f"Applying DC/AC ratio of {dc_ac_ratio} to solar profiles") + logger.info(f"Applying DC/AC ratio of {dc_ac_ratio} by clipping solar profiles at {1/dc_ac_ratio:.2f}.") ds["profile"] = ds["profile"].clip(max=1 / dc_ac_ratio) - ds["p_nom_max"] = ds["p_nom_max"] if "clip_p_max_pu" in params: min_p_max_pu = params["clip_p_max_pu"] From 78080cfa6261524447b3764d7dfb09075b4b808a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 11:46:15 +0000 Subject: [PATCH 11/16] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- scripts/build_renewable_profiles.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/build_renewable_profiles.py b/scripts/build_renewable_profiles.py index 552ff4b0d5..f0d3975457 100644 --- a/scripts/build_renewable_profiles.py +++ b/scripts/build_renewable_profiles.py @@ -323,7 +323,9 @@ dc_ac_ratio = params.get("dc_ac_ratio", 1.0) if dc_ac_ratio != 1.0: - logger.info(f"Applying DC/AC ratio of {dc_ac_ratio} by clipping solar profiles at {1/dc_ac_ratio:.2f}.") + logger.info( + f"Applying DC/AC ratio of {dc_ac_ratio} by clipping solar profiles at {1 / dc_ac_ratio:.2f}." + ) ds["profile"] = ds["profile"].clip(max=1 / dc_ac_ratio) if "clip_p_max_pu" in params: From 3647bac5ec1782c61c5cd92af9418ee14935bc06 Mon Sep 17 00:00:00 2001 From: Michael Lindner Date: Tue, 3 Mar 2026 09:22:14 +0100 Subject: [PATCH 12/16] apply cost transformation to solar rooftop as well --- scripts/prepare_sector_network.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index e47ef35c27..9181855cc0 100755 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -1521,6 +1521,7 @@ def insert_electricity_distribution_grid( options: dict, pop_layout: pd.DataFrame, solar_rooftop_potentials_fn: str, + params_renewable: dict, ) -> None: """ Insert electricity distribution grid components into the network. @@ -1545,6 +1546,8 @@ def insert_electricity_distribution_grid( Population data per node with at least: - 'total' column containing population in thousands Index should match network nodes + params_renewable : dict + Renewable energy parameters Returns ------- @@ -1627,11 +1630,19 @@ def insert_electricity_distribution_grid( solar = n.generators.index[n.generators.carrier == "solar"] n.generators.loc[solar, "capital_cost"] = costs.at["solar-utility", "capital_cost"] + dc_ac_ratio = params_renewable["solar"].get("dc_ac_ratio", 1.0) + if params_renewable["solar"].get("costs_given_for_ac") and dc_ac_ratio != 1.0: + n.generators.loc[solar, "capital_cost"] /= dc_ac_ratio + + fn = solar_rooftop_potentials_fn if len(fn) > 0: potential = pd.read_csv(fn, index_col=["bus", "bin"]).squeeze(axis=1) potential.index = potential.index.map(flatten) + " solar" + capital_cost = costs.at["solar rooftop", "capital_cost"] + if params_renewable["solar"].get("costs_given_for_ac") and dc_ac_ratio != 1.0: + capital_cost /= dc_ac_ratio n.add( "Generator", solar, @@ -1641,7 +1652,7 @@ def insert_electricity_distribution_grid( p_nom_extendable=True, p_nom_max=potential.loc[solar], marginal_cost=n.generators.loc[solar, "marginal_cost"], - capital_cost=costs.at["solar-rooftop", "capital_cost"], + capital_cost=capital_cost, efficiency=n.generators.loc[solar, "efficiency"], p_max_pu=n.generators_t.p_max_pu[solar], lifetime=costs.at["solar-rooftop", "lifetime"], @@ -6571,7 +6582,7 @@ def add_import_options( if options["electricity_distribution_grid"]: insert_electricity_distribution_grid( - n, costs, options, pop_layout, snakemake.input.solar_rooftop_potentials + n, costs, options, pop_layout, snakemake.input.solar_rooftop_potentials, snakemake.params.renewable ) if options["enhanced_geothermal"].get("enable", False): From 7e2ac5cb0010e9f656816356af65fa5c53ddb008 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2026 08:22:34 +0000 Subject: [PATCH 13/16] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- scripts/prepare_sector_network.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index 9181855cc0..3b184cf7e5 100755 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -1634,7 +1634,6 @@ def insert_electricity_distribution_grid( if params_renewable["solar"].get("costs_given_for_ac") and dc_ac_ratio != 1.0: n.generators.loc[solar, "capital_cost"] /= dc_ac_ratio - fn = solar_rooftop_potentials_fn if len(fn) > 0: potential = pd.read_csv(fn, index_col=["bus", "bin"]).squeeze(axis=1) @@ -6582,7 +6581,12 @@ def add_import_options( if options["electricity_distribution_grid"]: insert_electricity_distribution_grid( - n, costs, options, pop_layout, snakemake.input.solar_rooftop_potentials, snakemake.params.renewable + n, + costs, + options, + pop_layout, + snakemake.input.solar_rooftop_potentials, + snakemake.params.renewable, ) if options["enhanced_geothermal"].get("enable", False): From eb228041f6d4eb87756e3f48b44c5e4d2a9e2246 Mon Sep 17 00:00:00 2001 From: Michael Lindner Date: Wed, 4 Mar 2026 13:26:17 +0100 Subject: [PATCH 14/16] set default dc_ac_ratio to DEA value --- config/config.default.yaml | 4 ++-- config/schema.default.json | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/config/config.default.yaml b/config/config.default.yaml index a99c90f450..f006b9283a 100644 --- a/config/config.default.yaml +++ b/config/config.default.yaml @@ -364,7 +364,7 @@ renewable: natura: true excluder_resolution: 100 clip_p_max_pu: 0.01 - dc_ac_ratio: 1.3 + dc_ac_ratio: 1.25 costs_given_for_ac: true "solar-hsat": cutout: default @@ -406,7 +406,7 @@ renewable: natura: true excluder_resolution: 100 clip_p_max_pu: 0.01 - dc_ac_ratio: 1.3 + dc_ac_ratio: 1.25 costs_given_for_ac: true hydro: cutout: default diff --git a/config/schema.default.json b/config/schema.default.json index cc4ba544a9..354652d5b7 100644 --- a/config/schema.default.json +++ b/config/schema.default.json @@ -3773,7 +3773,7 @@ "type": "number" }, "dc_ac_ratio": { - "default": 1.3, + "default": 1.25, "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", "minimum": 1, "type": "number" @@ -3908,7 +3908,7 @@ "type": "number" }, "dc_ac_ratio": { - "default": 1.3, + "default": 1.25, "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", "minimum": 1, "type": "number" @@ -5020,7 +5020,7 @@ "type": "boolean" }, "factor": { - "default": 1.3, + "default": 1.25, "description": "The maximum growth factor of a carrier (e.g. 1.3 allows 30% larger than max historic growth).", "type": "number" }, @@ -6724,7 +6724,7 @@ "type": "boolean" }, "factor": { - "default": 1.3, + "default": 1.25, "description": "The maximum growth factor of a carrier (e.g. 1.3 allows 30% larger than max historic growth).", "type": "number" }, @@ -7633,7 +7633,7 @@ "type": "number" }, "dc_ac_ratio": { - "default": 1.3, + "default": 1.25, "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", "minimum": 1, "type": "number" @@ -9685,7 +9685,7 @@ "type": "number" }, "dc_ac_ratio": { - "default": 1.3, + "default": 1.25, "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", "minimum": 1, "type": "number" @@ -9820,7 +9820,7 @@ "type": "number" }, "dc_ac_ratio": { - "default": 1.3, + "default": 1.25, "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", "minimum": 1, "type": "number" @@ -11341,7 +11341,7 @@ "type": "boolean" }, "factor": { - "default": 1.3, + "default": 1.25, "description": "The maximum growth factor of a carrier (e.g. 1.3 allows 30% larger than max historic growth).", "type": "number" }, From ce8863c86002b14178cd389a1623fc2e4fa45487 Mon Sep 17 00:00:00 2001 From: "pypsa[bot]" <181215446+pypsa[bot]@users.noreply.github.com> Date: Wed, 4 Mar 2026 12:26:35 +0000 Subject: [PATCH 15/16] [pypsa-bot] run `sync-locks` & `generate-config` - `pixi run sync-locks` - `pixi run generate-config` --- config/config.default.yaml | 4 ++-- config/schema.default.json | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/config/config.default.yaml b/config/config.default.yaml index f006b9283a..a99c90f450 100644 --- a/config/config.default.yaml +++ b/config/config.default.yaml @@ -364,7 +364,7 @@ renewable: natura: true excluder_resolution: 100 clip_p_max_pu: 0.01 - dc_ac_ratio: 1.25 + dc_ac_ratio: 1.3 costs_given_for_ac: true "solar-hsat": cutout: default @@ -406,7 +406,7 @@ renewable: natura: true excluder_resolution: 100 clip_p_max_pu: 0.01 - dc_ac_ratio: 1.25 + dc_ac_ratio: 1.3 costs_given_for_ac: true hydro: cutout: default diff --git a/config/schema.default.json b/config/schema.default.json index 354652d5b7..cc4ba544a9 100644 --- a/config/schema.default.json +++ b/config/schema.default.json @@ -3773,7 +3773,7 @@ "type": "number" }, "dc_ac_ratio": { - "default": 1.25, + "default": 1.3, "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", "minimum": 1, "type": "number" @@ -3908,7 +3908,7 @@ "type": "number" }, "dc_ac_ratio": { - "default": 1.25, + "default": 1.3, "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", "minimum": 1, "type": "number" @@ -5020,7 +5020,7 @@ "type": "boolean" }, "factor": { - "default": 1.25, + "default": 1.3, "description": "The maximum growth factor of a carrier (e.g. 1.3 allows 30% larger than max historic growth).", "type": "number" }, @@ -6724,7 +6724,7 @@ "type": "boolean" }, "factor": { - "default": 1.25, + "default": 1.3, "description": "The maximum growth factor of a carrier (e.g. 1.3 allows 30% larger than max historic growth).", "type": "number" }, @@ -7633,7 +7633,7 @@ "type": "number" }, "dc_ac_ratio": { - "default": 1.25, + "default": 1.3, "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", "minimum": 1, "type": "number" @@ -9685,7 +9685,7 @@ "type": "number" }, "dc_ac_ratio": { - "default": 1.25, + "default": 1.3, "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", "minimum": 1, "type": "number" @@ -9820,7 +9820,7 @@ "type": "number" }, "dc_ac_ratio": { - "default": 1.25, + "default": 1.3, "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", "minimum": 1, "type": "number" @@ -11341,7 +11341,7 @@ "type": "boolean" }, "factor": { - "default": 1.25, + "default": 1.3, "description": "The maximum growth factor of a carrier (e.g. 1.3 allows 30% larger than max historic growth).", "type": "number" }, From c50201520ac950e975d90a0b2fd6418378ac74c6 Mon Sep 17 00:00:00 2001 From: Michael Lindner Date: Wed, 4 Mar 2026 13:51:24 +0100 Subject: [PATCH 16/16] set default dc_ac_ratio to DEA value (second try) --- config/config.default.yaml | 4 ++-- config/schema.default.json | 10 +++++----- scripts/lib/validation/config/renewable.py | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/config/config.default.yaml b/config/config.default.yaml index a99c90f450..f006b9283a 100644 --- a/config/config.default.yaml +++ b/config/config.default.yaml @@ -364,7 +364,7 @@ renewable: natura: true excluder_resolution: 100 clip_p_max_pu: 0.01 - dc_ac_ratio: 1.3 + dc_ac_ratio: 1.25 costs_given_for_ac: true "solar-hsat": cutout: default @@ -406,7 +406,7 @@ renewable: natura: true excluder_resolution: 100 clip_p_max_pu: 0.01 - dc_ac_ratio: 1.3 + dc_ac_ratio: 1.25 costs_given_for_ac: true hydro: cutout: default diff --git a/config/schema.default.json b/config/schema.default.json index cc4ba544a9..adc8a872d6 100644 --- a/config/schema.default.json +++ b/config/schema.default.json @@ -3773,7 +3773,7 @@ "type": "number" }, "dc_ac_ratio": { - "default": 1.3, + "default": 1.25, "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", "minimum": 1, "type": "number" @@ -3908,7 +3908,7 @@ "type": "number" }, "dc_ac_ratio": { - "default": 1.3, + "default": 1.25, "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", "minimum": 1, "type": "number" @@ -7633,7 +7633,7 @@ "type": "number" }, "dc_ac_ratio": { - "default": 1.3, + "default": 1.25, "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", "minimum": 1, "type": "number" @@ -9685,7 +9685,7 @@ "type": "number" }, "dc_ac_ratio": { - "default": 1.3, + "default": 1.25, "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", "minimum": 1, "type": "number" @@ -9820,7 +9820,7 @@ "type": "number" }, "dc_ac_ratio": { - "default": 1.3, + "default": 1.25, "description": "DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", "minimum": 1, "type": "number" diff --git a/scripts/lib/validation/config/renewable.py b/scripts/lib/validation/config/renewable.py index 7202646abc..0a6a4707d0 100644 --- a/scripts/lib/validation/config/renewable.py +++ b/scripts/lib/validation/config/renewable.py @@ -257,7 +257,7 @@ class _SolarConfig(BaseModel): description="To avoid too small values in the renewables` per-unit availability time series values below this threshold are set to zero.", ) dc_ac_ratio: float = Field( - 1.3, + 1.25, description="DC/AC ratio for solar PV. Capacity factors are clipped at 1/dc_ac_ratio and rescaled to express output as a fraction of AC capacity.", ge=1, )