From 1adaa7d273c48dd3ada26548fe8e2e27a354becf Mon Sep 17 00:00:00 2001 From: yerbol-akhmetov Date: Wed, 4 Dec 2024 22:31:48 +0500 Subject: [PATCH 1/5] add electricity_distribution_grid to config --- config.default.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/config.default.yaml b/config.default.yaml index 156454543..be8c1225f 100644 --- a/config.default.yaml +++ b/config.default.yaml @@ -663,6 +663,11 @@ sector: NZ_2050: 0.36 DF_2050: 0.12 + electricity_distribution_grid: true + transmission_efficiency: + electricity distribution grid: + efficiency_static: 0.97 + gadm_level: 1 h2_cavern: true marginal_cost_storage: 0 From 418f0357b4729feeaecb93bd08e83b495dfa6696 Mon Sep 17 00:00:00 2001 From: yerbol-akhmetov Date: Wed, 4 Dec 2024 22:35:56 +0500 Subject: [PATCH 2/5] add_electricity_distribution_grid function to prepare_sector_network --- scripts/prepare_sector_network.py | 134 ++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index 01ab70a7b..5d15c8f28 100644 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -2629,6 +2629,137 @@ def add_residential(n, costs): ) +def add_electricity_distribution_grid(n, costs): + + nodes = pop_layout.index + + n.madd( + "Bus", + nodes + " low voltage", + location=nodes, + carrier="low voltage", + unit="MWh_el", + ) + + n.madd( + "Link", + nodes + " electricity distribution grid", + bus0=nodes, + bus1=nodes + " low voltage", + p_nom_extendable=True, + p_min_pu=-1, + carrier="electricity distribution grid", + efficiency=1, + lifetime=costs.at["electricity distribution grid", "lifetime"], + capital_cost=costs.at["electricity distribution grid", "fixed"], + ) + + # deduct distribution losses from electricity demand as these are included in total load + # https://nbviewer.org/github/Open-Power-System-Data/datapackage_timeseries/blob/2020-10-06/main.ipynb + if ( + efficiency := options["transmission_efficiency"] + .get("electricity distribution grid", {}) + .get("efficiency_static") + ): + logger.info( + f"Deducting distribution losses from electricity demand: {np.around(100*(1-efficiency), decimals=2)}%" + ) + n.loads_t.p_set.loc[:, n.loads.carrier == "AC"] *= efficiency + + # move AC loads to low voltage buses + ac_loads = n.loads.index[n.loads.carrier == "AC"] + n.loads.loc[ac_loads, "bus"] += " low voltage" + + # move industry, rail transport, agriculture and services electricity to low voltage + loads = n.loads.index[n.loads.carrier.str.contains("electricity")] + n.loads.loc[loads, "bus"] += " low voltage" + + bevs = n.links.index[n.links.carrier == "BEV charger"] + n.links.loc[bevs, "bus0"] += " low voltage" + + v2gs = n.links.index[n.links.carrier == "V2G"] + n.links.loc[v2gs, "bus1"] += " low voltage" + + hps = n.links.index[n.links.carrier.str.contains("heat pump")] + n.links.loc[hps, "bus0"] += " low voltage" + + rh = n.links.index[n.links.carrier.str.contains("resistive heater")] + n.links.loc[rh, "bus0"] += " low voltage" + + mchp = n.links.index[n.links.carrier.str.contains("micro gas")] + n.links.loc[mchp, "bus1"] += " low voltage" + + # set existing solar to cost of utility cost rather the 50-50 rooftop-utility + solar = n.generators.index[n.generators.carrier == "solar"] + n.generators.loc[solar, "capital_cost"] = costs.at["solar-utility", "fixed"] + pop_solar = pop_layout.total.rename(index=lambda x: x + " solar") + + # add max solar rooftop potential assuming 0.1 kW/m2 and 20 m2/person, + # i.e. 2 kW/person (population data is in thousands of people) so we get MW + potential = 0.1 * 20 * pop_solar + + n.madd( + "Generator", + solar, + suffix=" rooftop", + bus=n.generators.loc[solar, "bus"] + " low voltage", + carrier="solar rooftop", + p_nom_extendable=True, + p_nom_max=potential.loc[solar], + marginal_cost=n.generators.loc[solar, "marginal_cost"], + capital_cost=costs.at["solar-rooftop", "fixed"], + efficiency=n.generators.loc[solar, "efficiency"], + p_max_pu=n.generators_t.p_max_pu[solar], + lifetime=costs.at["solar-rooftop", "lifetime"], + ) + + n.add("Carrier", "home battery") + + n.madd( + "Bus", + nodes + " home battery", + location=nodes, + carrier="home battery", + unit="MWh_el", + ) + + n.madd( + "Store", + nodes + " home battery", + bus=nodes + " home battery", + location=nodes, + e_cyclic=True, + e_nom_extendable=True, + carrier="home battery", + capital_cost=costs.at["home battery storage", "fixed"], + lifetime=costs.at["battery storage", "lifetime"], + ) + + n.madd( + "Link", + nodes + " home battery charger", + bus0=nodes + " low voltage", + bus1=nodes + " home battery", + carrier="home battery charger", + efficiency=costs.at["battery inverter", "efficiency"] ** 0.5, + capital_cost=costs.at["home battery inverter", "fixed"], + p_nom_extendable=True, + lifetime=costs.at["battery inverter", "lifetime"], + ) + + n.madd( + "Link", + nodes + " home battery discharger", + bus0=nodes + " home battery", + bus1=nodes + " low voltage", + carrier="home battery discharger", + efficiency=costs.at["battery inverter", "efficiency"] ** 0.5, + marginal_cost=options["marginal_cost_storage"], + p_nom_extendable=True, + lifetime=costs.at["battery inverter", "lifetime"], + ) + + # def add_co2limit(n, Nyears=1.0, limit=0.0): # print("Adding CO2 budget limit as per unit of 1990 levels of", limit) @@ -2950,6 +3081,9 @@ def remove_carrier_related_components(n, carriers_to_drop): add_residential(n, costs) add_services(n, costs) + if options.get("electricity_distribution_grid", False): + add_electricity_distribution_grid(n, costs) + sopts = snakemake.wildcards.sopts.split("-") for o in sopts: From 07a99f66acff9dd30a50164bc99d54ecb3967c54 Mon Sep 17 00:00:00 2001 From: yerbol-akhmetov Date: Thu, 5 Dec 2024 11:47:11 +0500 Subject: [PATCH 3/5] add option to choose solar_rooftop and home_battery --- config.default.yaml | 12 +-- scripts/prepare_sector_network.py | 130 +++++++++++++++--------------- 2 files changed, 74 insertions(+), 68 deletions(-) diff --git a/config.default.yaml b/config.default.yaml index be8c1225f..b68509302 100644 --- a/config.default.yaml +++ b/config.default.yaml @@ -594,6 +594,13 @@ sector: efficiency_heat_biomass_to_elec: 0.9 efficiency_heat_gas_to_elec: 0.9 + electricity_distribution_grid: true + solar_rooftop: true + home_battery: true + transmission_efficiency: + electricity distribution grid: + efficiency_static: 0.97 + dynamic_transport: enable: false # If "True", then the BEV and FCEV shares are obtained depending on the "Co2L"-wildcard (e.g. "Co2L0.70: 0.10"). If "False", then the shares are obtained depending on the "demand" wildcard and "planning_horizons" wildcard as listed below (e.g. "DF_2050: 0.08") land_transport_electric_share: @@ -663,11 +670,6 @@ sector: NZ_2050: 0.36 DF_2050: 0.12 - electricity_distribution_grid: true - transmission_efficiency: - electricity distribution grid: - efficiency_static: 0.97 - gadm_level: 1 h2_cavern: true marginal_cost_storage: 0 diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index 5d15c8f28..df8e7b48b 100644 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -2630,7 +2630,7 @@ def add_residential(n, costs): def add_electricity_distribution_grid(n, costs): - + logger.info("Adding electricity distribution network") nodes = pop_layout.index n.madd( @@ -2689,75 +2689,79 @@ def add_electricity_distribution_grid(n, costs): mchp = n.links.index[n.links.carrier.str.contains("micro gas")] n.links.loc[mchp, "bus1"] += " low voltage" - # set existing solar to cost of utility cost rather the 50-50 rooftop-utility - solar = n.generators.index[n.generators.carrier == "solar"] - n.generators.loc[solar, "capital_cost"] = costs.at["solar-utility", "fixed"] - pop_solar = pop_layout.total.rename(index=lambda x: x + " solar") + if options.get("solar_rooftop", False): + logger.info("Adding solar rooftop technology") + # set existing solar to cost of utility cost rather the 50-50 rooftop-utility + solar = n.generators.index[n.generators.carrier == "solar"] + n.generators.loc[solar, "capital_cost"] = costs.at["solar-utility", "fixed"] + pop_solar = pop_layout.total.rename(index=lambda x: x + " solar") - # add max solar rooftop potential assuming 0.1 kW/m2 and 20 m2/person, - # i.e. 2 kW/person (population data is in thousands of people) so we get MW - potential = 0.1 * 20 * pop_solar + # add max solar rooftop potential assuming 0.1 kW/m2 and 20 m2/person, + # i.e. 2 kW/person (population data is in thousands of people) so we get MW + potential = 0.1 * 20 * pop_solar - n.madd( - "Generator", - solar, - suffix=" rooftop", - bus=n.generators.loc[solar, "bus"] + " low voltage", - carrier="solar rooftop", - p_nom_extendable=True, - p_nom_max=potential.loc[solar], - marginal_cost=n.generators.loc[solar, "marginal_cost"], - capital_cost=costs.at["solar-rooftop", "fixed"], - efficiency=n.generators.loc[solar, "efficiency"], - p_max_pu=n.generators_t.p_max_pu[solar], - lifetime=costs.at["solar-rooftop", "lifetime"], - ) + n.madd( + "Generator", + solar, + suffix=" rooftop", + bus=n.generators.loc[solar, "bus"] + " low voltage", + carrier="solar rooftop", + p_nom_extendable=True, + p_nom_max=potential.loc[solar], + marginal_cost=n.generators.loc[solar, "marginal_cost"], + capital_cost=costs.at["solar-rooftop", "fixed"], + efficiency=n.generators.loc[solar, "efficiency"], + p_max_pu=n.generators_t.p_max_pu[solar], + lifetime=costs.at["solar-rooftop", "lifetime"], + ) - n.add("Carrier", "home battery") + if options.get("home_battery", False): + logger.info("Adding home battery technology") + n.add("Carrier", "home battery") - n.madd( - "Bus", - nodes + " home battery", - location=nodes, - carrier="home battery", - unit="MWh_el", - ) + n.madd( + "Bus", + nodes + " home battery", + location=nodes, + carrier="home battery", + unit="MWh_el", + ) - n.madd( - "Store", - nodes + " home battery", - bus=nodes + " home battery", - location=nodes, - e_cyclic=True, - e_nom_extendable=True, - carrier="home battery", - capital_cost=costs.at["home battery storage", "fixed"], - lifetime=costs.at["battery storage", "lifetime"], - ) + n.madd( + "Store", + nodes + " home battery", + bus=nodes + " home battery", + location=nodes, + e_cyclic=True, + e_nom_extendable=True, + carrier="home battery", + capital_cost=costs.at["home battery storage", "fixed"], + lifetime=costs.at["battery storage", "lifetime"], + ) - n.madd( - "Link", - nodes + " home battery charger", - bus0=nodes + " low voltage", - bus1=nodes + " home battery", - carrier="home battery charger", - efficiency=costs.at["battery inverter", "efficiency"] ** 0.5, - capital_cost=costs.at["home battery inverter", "fixed"], - p_nom_extendable=True, - lifetime=costs.at["battery inverter", "lifetime"], - ) + n.madd( + "Link", + nodes + " home battery charger", + bus0=nodes + " low voltage", + bus1=nodes + " home battery", + carrier="home battery charger", + efficiency=costs.at["battery inverter", "efficiency"] ** 0.5, + capital_cost=costs.at["home battery inverter", "fixed"], + p_nom_extendable=True, + lifetime=costs.at["battery inverter", "lifetime"], + ) - n.madd( - "Link", - nodes + " home battery discharger", - bus0=nodes + " home battery", - bus1=nodes + " low voltage", - carrier="home battery discharger", - efficiency=costs.at["battery inverter", "efficiency"] ** 0.5, - marginal_cost=options["marginal_cost_storage"], - p_nom_extendable=True, - lifetime=costs.at["battery inverter", "lifetime"], - ) + n.madd( + "Link", + nodes + " home battery discharger", + bus0=nodes + " home battery", + bus1=nodes + " low voltage", + carrier="home battery discharger", + efficiency=costs.at["battery inverter", "efficiency"] ** 0.5, + marginal_cost=options["marginal_cost_storage"], + p_nom_extendable=True, + lifetime=costs.at["battery inverter", "lifetime"], + ) # def add_co2limit(n, Nyears=1.0, limit=0.0): From 90011f82e1ea5b172df8b5a534da8e52e524d807 Mon Sep 17 00:00:00 2001 From: yerbol-akhmetov Date: Thu, 5 Dec 2024 12:15:38 +0500 Subject: [PATCH 4/5] document the dsitribution network technologies --- config.default.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config.default.yaml b/config.default.yaml index b68509302..cd8850723 100644 --- a/config.default.yaml +++ b/config.default.yaml @@ -594,12 +594,12 @@ sector: efficiency_heat_biomass_to_elec: 0.9 efficiency_heat_gas_to_elec: 0.9 - electricity_distribution_grid: true - solar_rooftop: true - home_battery: true + electricity_distribution_grid: true # adds low voltage buses and shifts AC loads, BEVs, heat pumps, and resistive heaters, micro CHPs to low voltage buses if technologies are present + solar_rooftop: true # adds distribution side customer rooftop PV (only work if electricity_distribution_grid: true) + home_battery: true # adds home batteries to low voltage buses ((only work if electricity_distribution_grid: true) transmission_efficiency: electricity distribution grid: - efficiency_static: 0.97 + efficiency_static: 0.97 # efficiency of distribution grid (i.e. 3% loses) dynamic_transport: enable: false # If "True", then the BEV and FCEV shares are obtained depending on the "Co2L"-wildcard (e.g. "Co2L0.70: 0.10"). If "False", then the shares are obtained depending on the "demand" wildcard and "planning_horizons" wildcard as listed below (e.g. "DF_2050: 0.08") From fb5396a6fa34615662679606a0563ac97a52fcb9 Mon Sep 17 00:00:00 2001 From: yerbol-akhmetov Date: Thu, 5 Dec 2024 12:21:20 +0500 Subject: [PATCH 5/5] add release notes --- doc/release_notes.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/release_notes.rst b/doc/release_notes.rst index d8777d57f..ede8ab312 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -37,6 +37,8 @@ E.g. if a new rule becomes available describe how to use it `make test` and in o * Remove elec-based H2 and battery technologies before addition in `prepare_sector_network.py` script and fix bus names for links that models H2 repuspose network `PR #1198 `__ +* Add electricity distribution grid with solar rooftop and home battery technologies `PR #1221 `__ + **Minor Changes and bug-fixing** * The default configuration for `electricity:estimate_renewable_capacities:year` was updated from 2020 to 2023. `PR #1106 `__