diff --git a/doc/img/workflow.png b/doc/img/workflow.png index 96ce7f378..0b274af8e 100644 Binary files a/doc/img/workflow.png and b/doc/img/workflow.png differ diff --git a/doc/release_notes.rst b/doc/release_notes.rst index 6605f9a45..192987201 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -260,6 +260,8 @@ Upcoming Release * Fix gas network retrofitting in `add_brownfield`. +* Time aggregation for sector-coupled networks have been split into its own rule. When using time step segmentation, time aggregation is constant over planning horizons of the same network. + PyPSA-Eur 0.10.0 (19th February 2024) ===================================== diff --git a/doc/sector.rst b/doc/sector.rst index bdfc5386f..e186ccf75 100644 --- a/doc/sector.rst +++ b/doc/sector.rst @@ -183,6 +183,11 @@ Rule ``cluster_gas_network`` .. automodule:: cluster_gas_network +Rule ``time_aggregation`` +============================================================================== + +.. automodule:: time_aggregation + Rule ``prepare_sector_network`` ============================================================================== diff --git a/doc/tutorial_sector.rst b/doc/tutorial_sector.rst index 67b65a3ec..70943ab0b 100644 --- a/doc/tutorial_sector.rst +++ b/doc/tutorial_sector.rst @@ -71,12 +71,14 @@ which were already included in the electricity-only tutorial: add_electricity 1 add_extra_components 1 all 1 + base_network 1 build_ammonia_production 1 build_biomass_potentials 1 build_clustered_population_layouts 1 build_cop_profiles 1 build_daily_heat_demand 1 build_district_heat_share 1 + build_electricity_demand 1 build_energy_totals 1 build_gas_input_locations 1 build_gas_network 1 @@ -93,8 +95,10 @@ which were already included in the electricity-only tutorial: build_industry_sector_ratios_intermediate 1 build_population_layouts 1 build_population_weighted_energy_totals 2 - build_renewable_profiles 4 + build_powerplants 1 + build_renewable_profiles 5 build_salt_cavern_potentials 1 + build_shapes 1 build_ship_raster 1 build_shipping_demand 1 build_simplified_population_layouts 1 @@ -117,9 +121,12 @@ which were already included in the electricity-only tutorial: retrieve_electricity_demand 1 retrieve_eurostat_data 1 retrieve_gas_infrastructure_data 1 + retrieve_ship_raster 1 + retrieve_synthetic_electricity_demand 1 simplify_network 1 solve_sector_network 1 - total 63 + time_aggregation 1 + total 67 This covers the retrieval of additional raw data from online resources and preprocessing data about the transport, industry, and heating sectors as well as @@ -138,243 +145,203 @@ successfully. graph[bgcolor=white, margin=0]; node[shape=box, style=rounded, fontname=sans, fontsize=10, penwidth=2]; edge[penwidth=2, color=grey]; - 0[label = "all", color = "0.44 0.6 0.85", style="rounded"]; - 1[label = "plot_summary", color = "0.63 0.6 0.85", style="rounded"]; - 2[label = "make_summary", color = "0.59 0.6 0.85", style="rounded"]; - 3[label = "solve_sector_network", color = "0.47 0.6 0.85", style="rounded"]; - 4[label = "prepare_sector_network\nsector_opts: CO2L0-24h-T-H-B-I-A-dist1", color = "0.47 0.6 0.85", style="rounded"]; - 5[label = "build_renewable_profiles\ntechnology: offwind-ac", color = "0.62 0.6 0.85", style="rounded"]; - 6[label = "base_network", color = "0.27 0.6 0.85", style="rounded"]; - 7[label = "build_shapes", color = "0.43 0.6 0.85", style="rounded"]; - 8[label = "retrieve_databundle", color = "0.55 0.6 0.85", style="rounded"]; - 9[label = "build_ship_raster", color = "0.29 0.6 0.85", style="rounded"]; - 10[label = "retrieve_ship_raster", color = "0.33 0.6 0.85", style="rounded"]; - 11[label = "retrieve_cutout\ncutout: be-03-2013-era5", color = "0.06 0.6 0.85", style="rounded"]; - 12[label = "build_renewable_profiles\ntechnology: offwind-dc", color = "0.62 0.6 0.85", style="rounded"]; - 13[label = "cluster_gas_network", color = "0.60 0.6 0.85", style="rounded"]; - 14[label = "build_gas_network", color = "0.34 0.6 0.85", style="rounded"]; - 15[label = "retrieve_gas_infrastructure_data", color = "0.44 0.6 0.85", style="rounded"]; - 16[label = "cluster_network\nclusters: 5", color = "0.03 0.6 0.85", style="rounded"]; - 17[label = "simplify_network\nsimpl: ", color = "0.64 0.6 0.85", style="rounded"]; - 18[label = "add_electricity", color = "0.51 0.6 0.85", style="rounded"]; - 19[label = "build_renewable_profiles\ntechnology: solar", color = "0.62 0.6 0.85", style="rounded"]; - 20[label = "build_renewable_profiles\ntechnology: onwind", color = "0.62 0.6 0.85", style="rounded"]; - 21[label = "retrieve_cost_data\nyear: 2030", color = "0.57 0.6 0.85", style="rounded"]; - 22[label = "build_powerplants", color = "0.39 0.6 0.85", style="rounded"]; - 23[label = "build_electricity_demand", color = "0.22 0.6 0.85", style="rounded"]; - 24[label = "retrieve_electricity_demand", color = "0.09 0.6 0.85", style="rounded"]; - 25[label = "retrieve_synthetic_electricity_demand", color = "0.66 0.6 0.85", style="rounded"]; - 26[label = "build_gas_input_locations", color = "0.50 0.6 0.85", style="rounded"]; - 27[label = "prepare_network\nll: v1.5\nopts: ", color = "0.52 0.6 0.85", style="rounded"]; - 28[label = "add_extra_components", color = "0.02 0.6 0.85", style="rounded"]; - 29[label = "retrieve_eurostat_data", color = "0.04 0.6 0.85", style="rounded,dashed"]; - 30[label = "build_population_weighted_energy_totals\nkind: energy", color = "0.07 0.6 0.85", style="rounded"]; - 31[label = "build_energy_totals", color = "0.24 0.6 0.85", style="rounded"]; - 32[label = "build_clustered_population_layouts", color = "0.38 0.6 0.85", style="rounded"]; - 33[label = "build_population_layouts", color = "0.61 0.6 0.85", style="rounded"]; - 34[label = "build_population_weighted_energy_totals\nkind: heat", color = "0.07 0.6 0.85", style="rounded"]; - 35[label = "build_heat_totals", color = "0.22 0.6 0.85", style="rounded"]; - 36[label = "build_shipping_demand", color = "0.19 0.6 0.85", style="rounded"]; - 37[label = "build_transport_demand", color = "0.42 0.6 0.85", style="rounded"]; - 38[label = "build_temperature_profiles\nscope: total", color = "0.58 0.6 0.85", style="rounded"]; - 39[label = "build_biomass_potentials\nplanning_horizons: 2030", color = "0.18 0.6 0.85", style="rounded"]; - 40[label = "build_salt_cavern_potentials", color = "0.19 0.6 0.85", style="rounded"]; - 41[label = "build_simplified_population_layouts", color = "0.37 0.6 0.85", style="rounded"]; - 42[label = "build_industrial_energy_demand_per_node", color = "0.57 0.6 0.85", style="rounded"]; - 43[label = "build_industry_sector_ratios_intermediate\nplanning_horizons: 2030", color = "0.06 0.6 0.85", style="rounded"]; - 44[label = "build_industry_sector_ratios", color = "0.45 0.6 0.85", style="rounded"]; - 45[label = "build_ammonia_production", color = "0.42 0.6 0.85", style="rounded"]; - 46[label = "build_industrial_energy_demand_per_country_today", color = "0.12 0.6 0.85", style="rounded"]; - 47[label = "build_industrial_production_per_country", color = "0.01 0.6 0.85", style="rounded"]; - 48[label = "build_industrial_production_per_node", color = "0.30 0.6 0.85", style="rounded"]; - 49[label = "build_industrial_distribution_key", color = "0.62 0.6 0.85", style="rounded"]; - 50[label = "build_industrial_production_per_country_tomorrow\nplanning_horizons: 2030", color = "0.13 0.6 0.85", style="rounded"]; - 51[label = "build_industrial_energy_demand_per_node_today", color = "0.29 0.6 0.85", style="rounded"]; - 52[label = "build_hourly_heat_demand", color = "0.24 0.6 0.85", style="rounded"]; - 53[label = "build_daily_heat_demand\nscope: total", color = "0.27 0.6 0.85", style="rounded"]; - 54[label = "build_district_heat_share\nplanning_horizons: 2030", color = "0.46 0.6 0.85", style="rounded"]; - 55[label = "build_temperature_profiles\nscope: rural", color = "0.58 0.6 0.85", style="rounded"]; - 56[label = "build_temperature_profiles\nscope: urban", color = "0.58 0.6 0.85", style="rounded"]; - 57[label = "build_cop_profiles", color = "0.65 0.6 0.85", style="rounded"]; - 58[label = "build_solar_thermal_profiles\nscope: total", color = "0.23 0.6 0.85", style="rounded"]; - 59[label = "build_solar_thermal_profiles\nscope: urban", color = "0.23 0.6 0.85", style="rounded"]; - 60[label = "build_solar_thermal_profiles\nscope: rural", color = "0.23 0.6 0.85", style="rounded"]; - 61[label = "plot_power_network_clustered", color = "0.49 0.6 0.85", style="rounded"]; - 62[label = "plot_power_network", color = "0.05 0.6 0.85", style="rounded"]; - 63[label = "plot_hydrogen_network", color = "0.56 0.6 0.85", style="rounded"]; - 64[label = "plot_gas_network", color = "0.59 0.6 0.85", style="rounded"]; + 0[label = "all", color = "0.06 0.6 0.85", style="rounded"]; + 1[label = "plot_summary", color = "0.16 0.6 0.85", style="rounded"]; + 2[label = "make_summary", color = "0.57 0.6 0.85", style="rounded"]; + 3[label = "solve_sector_network", color = "0.35 0.6 0.85", style="rounded"]; + 4[label = "prepare_sector_network", color = "0.36 0.6 0.85", style="rounded"]; + 5[label = "build_renewable_profiles", color = "0.12 0.6 0.85", style="rounded"]; + 6[label = "base_network", color = "0.45 0.6 0.85", style="rounded"]; + 7[label = "build_shapes", color = "0.66 0.6 0.85", style="rounded"]; + 8[label = "retrieve_databundle", color = "0.28 0.6 0.85", style="rounded"]; + 9[label = "build_ship_raster", color = "0.58 0.6 0.85", style="rounded"]; + 10[label = "retrieve_ship_raster", color = "0.50 0.6 0.85", style="rounded"]; + 11[label = "retrieve_cutout", color = "0.34 0.6 0.85", style="rounded"]; + 12[label = "cluster_gas_network", color = "0.09 0.6 0.85", style="rounded"]; + 13[label = "build_gas_network", color = "0.19 0.6 0.85", style="rounded"]; + 14[label = "retrieve_gas_infrastructure_data", color = "0.52 0.6 0.85", style="rounded"]; + 15[label = "cluster_network", color = "0.05 0.6 0.85", style="rounded"]; + 16[label = "simplify_network", color = "0.50 0.6 0.85", style="rounded"]; + 17[label = "add_electricity", color = "0.32 0.6 0.85", style="rounded"]; + 18[label = "retrieve_cost_data", color = "0.39 0.6 0.85", style="rounded"]; + 19[label = "build_powerplants", color = "0.33 0.6 0.85", style="rounded"]; + 20[label = "build_electricity_demand", color = "0.31 0.6 0.85", style="rounded"]; + 21[label = "retrieve_electricity_demand", color = "0.23 0.6 0.85", style="rounded"]; + 22[label = "retrieve_synthetic_electricity_demand", color = "0.16 0.6 0.85", style="rounded"]; + 23[label = "build_gas_input_locations", color = "0.62 0.6 0.85", style="rounded"]; + 24[label = "time_aggregation", color = "0.28 0.6 0.85", style="rounded"]; + 25[label = "prepare_network", color = "0.23 0.6 0.85", style="rounded"]; + 26[label = "add_extra_components", color = "0.21 0.6 0.85", style="rounded"]; + 27[label = "build_hourly_heat_demand", color = "0.21 0.6 0.85", style="rounded"]; + 28[label = "build_daily_heat_demand", color = "0.24 0.6 0.85", style="rounded"]; + 29[label = "build_population_layouts", color = "0.33 0.6 0.85", style="rounded"]; + 30[label = "build_solar_thermal_profiles", color = "0.60 0.6 0.85", style="rounded"]; + 31[label = "retrieve_eurostat_data", color = "0.03 0.6 0.85", style="rounded"]; + 32[label = "build_population_weighted_energy_totals", color = "0.11 0.6 0.85", style="rounded"]; + 33[label = "build_energy_totals", color = "0.62 0.6 0.85", style="rounded"]; + 34[label = "build_clustered_population_layouts", color = "0.55 0.6 0.85", style="rounded"]; + 35[label = "build_heat_totals", color = "0.38 0.6 0.85", style="rounded"]; + 36[label = "build_shipping_demand", color = "0.37 0.6 0.85", style="rounded"]; + 37[label = "build_transport_demand", color = "0.44 0.6 0.85", style="rounded"]; + 38[label = "build_temperature_profiles", color = "0.54 0.6 0.85", style="rounded"]; + 39[label = "build_biomass_potentials", color = "0.00 0.6 0.85", style="rounded"]; + 40[label = "build_salt_cavern_potentials", color = "0.61 0.6 0.85", style="rounded"]; + 41[label = "build_simplified_population_layouts", color = "0.40 0.6 0.85", style="rounded"]; + 42[label = "build_industrial_energy_demand_per_node", color = "0.22 0.6 0.85", style="rounded"]; + 43[label = "build_industry_sector_ratios_intermediate", color = "0.65 0.6 0.85", style="rounded"]; + 44[label = "build_industry_sector_ratios", color = "0.57 0.6 0.85", style="rounded"]; + 45[label = "build_ammonia_production", color = "0.01 0.6 0.85", style="rounded"]; + 46[label = "build_industrial_energy_demand_per_country_today", color = "0.01 0.6 0.85", style="rounded"]; + 47[label = "build_industrial_production_per_country", color = "0.59 0.6 0.85", style="rounded"]; + 48[label = "build_industrial_production_per_node", color = "0.47 0.6 0.85", style="rounded"]; + 49[label = "build_industrial_distribution_key", color = "0.09 0.6 0.85", style="rounded"]; + 50[label = "build_industrial_production_per_country_tomorrow", color = "0.43 0.6 0.85", style="rounded"]; + 51[label = "build_industrial_energy_demand_per_node_today", color = "0.64 0.6 0.85", style="rounded"]; + 52[label = "build_district_heat_share", color = "0.38 0.6 0.85", style="rounded"]; + 53[label = "build_cop_profiles", color = "0.14 0.6 0.85", style="rounded"]; + 54[label = "plot_power_network_clustered", color = "0.25 0.6 0.85", style="rounded"]; + 55[label = "plot_power_network", color = "0.65 0.6 0.85", style="rounded"]; + 56[label = "plot_hydrogen_network", color = "0.30 0.6 0.85", style="rounded"]; + 57[label = "plot_gas_network", color = "0.56 0.6 0.85", style="rounded"]; 1 -> 0 2 -> 1 - 29 -> 1 + 31 -> 1 8 -> 1 + 57 -> 2 + 18 -> 2 + 55 -> 2 3 -> 2 - 21 -> 2 - 61 -> 2 - 62 -> 2 - 63 -> 2 - 64 -> 2 + 56 -> 2 + 54 -> 2 4 -> 3 - 5 -> 4 - 12 -> 4 - 13 -> 4 - 26 -> 4 - 27 -> 4 - 29 -> 4 - 30 -> 4 - 34 -> 4 - 36 -> 4 - 37 -> 4 - 31 -> 4 - 8 -> 4 39 -> 4 - 21 -> 4 - 40 -> 4 - 17 -> 4 16 -> 4 - 32 -> 4 - 41 -> 4 42 -> 4 - 52 -> 4 - 54 -> 4 + 31 -> 4 + 15 -> 4 + 25 -> 4 38 -> 4 - 55 -> 4 - 56 -> 4 - 57 -> 4 - 58 -> 4 - 59 -> 4 - 60 -> 4 + 34 -> 4 + 36 -> 4 + 12 -> 4 + 52 -> 4 + 18 -> 4 + 32 -> 4 + 5 -> 4 + 30 -> 4 + 40 -> 4 + 24 -> 4 + 33 -> 4 + 53 -> 4 + 41 -> 4 + 23 -> 4 + 8 -> 4 + 37 -> 4 + 27 -> 4 + 11 -> 5 6 -> 5 - 8 -> 5 9 -> 5 + 8 -> 5 7 -> 5 - 11 -> 5 7 -> 6 8 -> 7 - 10 -> 9 11 -> 9 - 6 -> 12 - 8 -> 12 - 9 -> 12 - 7 -> 12 - 11 -> 12 + 10 -> 9 + 13 -> 12 + 15 -> 12 14 -> 13 - 16 -> 13 - 15 -> 14 + 16 -> 15 + 18 -> 15 + 6 -> 16 17 -> 16 - 21 -> 16 - 18 -> 17 - 21 -> 17 + 18 -> 16 + 20 -> 17 6 -> 17 - 19 -> 18 - 20 -> 18 - 5 -> 18 - 12 -> 18 - 6 -> 18 - 21 -> 18 - 22 -> 18 - 23 -> 18 - 7 -> 18 + 18 -> 17 + 5 -> 17 + 19 -> 17 + 7 -> 17 6 -> 19 - 8 -> 19 - 7 -> 19 - 11 -> 19 - 6 -> 20 - 8 -> 20 - 7 -> 20 - 11 -> 20 - 6 -> 22 - 24 -> 23 - 25 -> 23 + 21 -> 20 + 22 -> 20 + 14 -> 23 + 15 -> 23 + 30 -> 24 + 27 -> 24 + 25 -> 24 + 26 -> 25 + 18 -> 25 15 -> 26 - 16 -> 26 + 18 -> 26 28 -> 27 - 21 -> 27 - 16 -> 28 - 21 -> 28 - 31 -> 30 - 32 -> 30 - 7 -> 31 - 8 -> 31 - 29 -> 31 + 11 -> 28 + 15 -> 28 + 29 -> 28 + 11 -> 29 + 7 -> 29 + 11 -> 30 + 15 -> 30 + 29 -> 30 33 -> 32 - 16 -> 32 - 11 -> 32 + 34 -> 32 + 35 -> 32 + 31 -> 33 7 -> 33 - 11 -> 33 - 35 -> 34 - 32 -> 34 - 31 -> 35 + 8 -> 33 + 11 -> 34 + 15 -> 34 + 29 -> 34 + 33 -> 35 + 33 -> 36 7 -> 36 - 16 -> 36 - 31 -> 36 + 15 -> 36 + 34 -> 37 + 33 -> 37 32 -> 37 - 30 -> 37 - 31 -> 37 8 -> 37 38 -> 37 - 33 -> 38 - 16 -> 38 11 -> 38 - 8 -> 39 - 16 -> 39 + 15 -> 38 + 29 -> 38 + 15 -> 39 7 -> 39 + 8 -> 39 + 15 -> 40 8 -> 40 - 16 -> 40 - 33 -> 41 - 17 -> 41 11 -> 41 - 43 -> 42 + 16 -> 41 + 29 -> 41 48 -> 42 51 -> 42 + 43 -> 42 44 -> 43 - 46 -> 43 47 -> 43 + 46 -> 43 45 -> 44 8 -> 44 8 -> 45 - 8 -> 46 47 -> 46 + 8 -> 46 45 -> 47 + 31 -> 47 8 -> 47 - 29 -> 47 - 49 -> 48 50 -> 48 - 16 -> 49 - 32 -> 49 + 49 -> 48 + 34 -> 49 + 15 -> 49 47 -> 50 49 -> 51 46 -> 51 - 53 -> 52 - 33 -> 53 - 16 -> 53 - 11 -> 53 - 31 -> 54 - 32 -> 54 - 33 -> 55 - 16 -> 55 - 11 -> 55 - 33 -> 56 - 16 -> 56 - 11 -> 56 - 38 -> 57 - 55 -> 57 - 56 -> 57 - 33 -> 58 - 16 -> 58 - 11 -> 58 - 33 -> 59 - 16 -> 59 - 11 -> 59 - 33 -> 60 - 16 -> 60 - 11 -> 60 - 16 -> 61 - 3 -> 62 - 16 -> 62 - 3 -> 63 - 16 -> 63 - 3 -> 64 - 16 -> 64 + 33 -> 52 + 34 -> 52 + 38 -> 53 + 15 -> 54 + 3 -> 55 + 15 -> 55 + 3 -> 56 + 15 -> 56 + 3 -> 57 + 15 -> 57 } | @@ -434,401 +401,222 @@ workflow: graph[bgcolor=white, margin=0]; node[shape=box, style=rounded, fontname=sans, fontsize=10, penwidth=2]; edge[penwidth=2, color=grey]; - 0[label = "all", color = "0.30 0.6 0.85", style="rounded"]; - 1[label = "plot_summary", color = "0.58 0.6 0.85", style="rounded"]; - 2[label = "make_summary", color = "0.56 0.6 0.85", style="rounded"]; - 3[label = "solve_sector_network_myopic", color = "0.40 0.6 0.85", style="rounded"]; - 4[label = "add_existing_baseyear", color = "0.22 0.6 0.85", style="rounded"]; - 5[label = "prepare_sector_network\nsector_opts: 24h-T-H-B-I-A-dist1", color = "0.05 0.6 0.85", style="rounded"]; - 6[label = "build_renewable_profiles\ntechnology: offwind-ac", color = "0.58 0.6 0.85", style="rounded"]; - 7[label = "base_network", color = "0.50 0.6 0.85", style="rounded"]; - 8[label = "build_shapes", color = "0.11 0.6 0.85", style="rounded"]; - 9[label = "retrieve_databundle", color = "0.15 0.6 0.85", style="rounded"]; - 10[label = "build_ship_raster", color = "0.07 0.6 0.85", style="rounded"]; - 11[label = "retrieve_ship_raster", color = "0.26 0.6 0.85", style="rounded"]; - 12[label = "retrieve_cutout\ncutout: be-03-2013-era5", color = "0.31 0.6 0.85", style="rounded"]; - 13[label = "build_renewable_profiles\ntechnology: offwind-dc", color = "0.58 0.6 0.85", style="rounded"]; - 14[label = "cluster_gas_network", color = "0.13 0.6 0.85", style="rounded"]; - 15[label = "build_gas_network", color = "0.22 0.6 0.85", style="rounded"]; - 16[label = "retrieve_gas_infrastructure_data", color = "0.08 0.6 0.85", style="rounded"]; - 17[label = "cluster_network\nclusters: 5", color = "0.13 0.6 0.85", style="rounded"]; - 18[label = "simplify_network\nsimpl: ", color = "0.60 0.6 0.85", style="rounded"]; - 19[label = "add_electricity", color = "0.61 0.6 0.85", style="rounded"]; - 20[label = "build_renewable_profiles\ntechnology: solar", color = "0.58 0.6 0.85", style="rounded"]; - 21[label = "build_renewable_profiles\ntechnology: onwind", color = "0.58 0.6 0.85", style="rounded"]; - 22[label = "retrieve_cost_data\nyear: 2030", color = "0.09 0.6 0.85", style="rounded"]; - 23[label = "build_powerplants", color = "0.19 0.6 0.85", style="rounded"]; - 24[label = "build_electricity_demand", color = "0.54 0.6 0.85", style="rounded"]; - 25[label = "retrieve_electricity_demand", color = "0.18 0.6 0.85", style="rounded"]; - 26[label = "retrieve_synthetic_electricity_demand", color = "0.39 0.6 0.85", style="rounded"]; - 27[label = "build_gas_input_locations", color = "0.37 0.6 0.85", style="rounded"]; - 28[label = "prepare_network\nll: v1.5\nopts: ", color = "0.53 0.6 0.85", style="rounded"]; - 29[label = "add_extra_components", color = "0.65 0.6 0.85", style="rounded"]; - 30[label = "retrieve_eurostat_data", color = "0.35 0.6 0.85", style="rounded,dashed"]; - 31[label = "build_population_weighted_energy_totals\nkind: energy", color = "0.60 0.6 0.85", style="rounded"]; - 32[label = "build_energy_totals", color = "0.04 0.6 0.85", style="rounded"]; - 33[label = "build_clustered_population_layouts", color = "0.43 0.6 0.85", style="rounded"]; - 34[label = "build_population_layouts", color = "0.66 0.6 0.85", style="rounded"]; - 35[label = "build_population_weighted_energy_totals\nkind: heat", color = "0.60 0.6 0.85", style="rounded"]; - 36[label = "build_heat_totals", color = "0.62 0.6 0.85", style="rounded"]; - 37[label = "build_shipping_demand", color = "0.36 0.6 0.85", style="rounded"]; - 38[label = "build_transport_demand", color = "0.21 0.6 0.85", style="rounded"]; - 39[label = "build_temperature_profiles\nscope: total", color = "0.14 0.6 0.85", style="rounded"]; - 40[label = "build_biomass_potentials\nplanning_horizons: 2030", color = "0.46 0.6 0.85", style="rounded"]; - 41[label = "build_salt_cavern_potentials", color = "0.46 0.6 0.85", style="rounded"]; - 42[label = "build_simplified_population_layouts", color = "0.55 0.6 0.85", style="rounded"]; - 43[label = "build_industrial_energy_demand_per_node", color = "0.32 0.6 0.85", style="rounded"]; - 44[label = "build_industry_sector_ratios_intermediate\nplanning_horizons: 2030", color = "0.27 0.6 0.85", style="rounded"]; - 45[label = "build_industry_sector_ratios", color = "0.08 0.6 0.85", style="rounded"]; - 46[label = "build_ammonia_production", color = "0.49 0.6 0.85", style="rounded"]; - 47[label = "build_industrial_energy_demand_per_country_today", color = "0.29 0.6 0.85", style="rounded"]; - 48[label = "build_industrial_production_per_country", color = "0.33 0.6 0.85", style="rounded"]; - 49[label = "build_industrial_production_per_node", color = "0.34 0.6 0.85", style="rounded"]; - 50[label = "build_industrial_distribution_key", color = "0.34 0.6 0.85", style="rounded"]; - 51[label = "build_industrial_production_per_country_tomorrow\nplanning_horizons: 2030", color = "0.62 0.6 0.85", style="rounded"]; - 52[label = "build_industrial_energy_demand_per_node_today", color = "0.11 0.6 0.85", style="rounded"]; - 53[label = "build_hourly_heat_demand", color = "0.16 0.6 0.85", style="rounded"]; - 54[label = "build_daily_heat_demand\nscope: total", color = "0.28 0.6 0.85", style="rounded"]; - 55[label = "build_district_heat_share\nplanning_horizons: 2030", color = "0.51 0.6 0.85", style="rounded"]; - 56[label = "build_temperature_profiles\nscope: rural", color = "0.14 0.6 0.85", style="rounded"]; - 57[label = "build_temperature_profiles\nscope: urban", color = "0.14 0.6 0.85", style="rounded"]; - 58[label = "build_cop_profiles", color = "0.51 0.6 0.85", style="rounded"]; - 59[label = "build_solar_thermal_profiles\nscope: total", color = "0.52 0.6 0.85", style="rounded"]; - 60[label = "build_solar_thermal_profiles\nscope: urban", color = "0.52 0.6 0.85", style="rounded"]; - 61[label = "build_solar_thermal_profiles\nscope: rural", color = "0.52 0.6 0.85", style="rounded"]; - 62[label = "build_existing_heating_distribution", color = "0.17 0.6 0.85", style="rounded"]; - 63[label = "solve_sector_network_myopic", color = "0.40 0.6 0.85", style="rounded"]; - 64[label = "add_brownfield", color = "0.00 0.6 0.85", style="rounded"]; - 65[label = "prepare_sector_network\nsector_opts: 24h-T-H-B-I-A-dist1", color = "0.05 0.6 0.85", style="rounded"]; - 66[label = "build_biomass_potentials\nplanning_horizons: 2040", color = "0.46 0.6 0.85", style="rounded"]; - 67[label = "retrieve_cost_data\nyear: 2040", color = "0.09 0.6 0.85", style="rounded"]; - 68[label = "build_industrial_energy_demand_per_node", color = "0.32 0.6 0.85", style="rounded"]; - 69[label = "build_industry_sector_ratios_intermediate\nplanning_horizons: 2040", color = "0.27 0.6 0.85", style="rounded"]; - 70[label = "build_industrial_production_per_node", color = "0.34 0.6 0.85", style="rounded"]; - 71[label = "build_industrial_production_per_country_tomorrow\nplanning_horizons: 2040", color = "0.62 0.6 0.85", style="rounded"]; - 72[label = "build_district_heat_share\nplanning_horizons: 2040", color = "0.51 0.6 0.85", style="rounded"]; - 73[label = "solve_sector_network_myopic", color = "0.40 0.6 0.85", style="rounded"]; - 74[label = "add_brownfield", color = "0.00 0.6 0.85", style="rounded"]; - 75[label = "prepare_sector_network\nsector_opts: 24h-T-H-B-I-A-dist1", color = "0.05 0.6 0.85", style="rounded"]; - 76[label = "build_biomass_potentials\nplanning_horizons: 2050", color = "0.46 0.6 0.85", style="rounded"]; - 77[label = "retrieve_cost_data\nyear: 2050", color = "0.09 0.6 0.85", style="rounded"]; - 78[label = "build_industrial_energy_demand_per_node", color = "0.32 0.6 0.85", style="rounded"]; - 79[label = "build_industry_sector_ratios_intermediate\nplanning_horizons: 2050", color = "0.27 0.6 0.85", style="rounded"]; - 80[label = "build_industrial_production_per_node", color = "0.34 0.6 0.85", style="rounded"]; - 81[label = "build_industrial_production_per_country_tomorrow\nplanning_horizons: 2050", color = "0.62 0.6 0.85", style="rounded"]; - 82[label = "build_district_heat_share\nplanning_horizons: 2050", color = "0.51 0.6 0.85", style="rounded"]; - 83[label = "plot_power_network_clustered", color = "0.02 0.6 0.85", style="rounded"]; - 84[label = "plot_power_network", color = "0.45 0.6 0.85", style="rounded"]; - 85[label = "plot_power_network", color = "0.45 0.6 0.85", style="rounded"]; - 86[label = "plot_power_network", color = "0.45 0.6 0.85", style="rounded"]; - 87[label = "plot_hydrogen_network", color = "0.38 0.6 0.85", style="rounded"]; - 88[label = "plot_hydrogen_network", color = "0.38 0.6 0.85", style="rounded"]; - 89[label = "plot_hydrogen_network", color = "0.38 0.6 0.85", style="rounded"]; + 0[label = "all", color = "0.10 0.6 0.85", style="rounded"]; + 1[label = "plot_summary", color = "0.56 0.6 0.85", style="rounded"]; + 2[label = "make_summary", color = "0.44 0.6 0.85", style="rounded"]; + 3[label = "solve_sector_network_myopic", color = "0.11 0.6 0.85", style="rounded"]; + 4[label = "add_existing_baseyear", color = "0.24 0.6 0.85", style="rounded"]; + 5[label = "prepare_sector_network", color = "0.26 0.6 0.85", style="rounded"]; + 6[label = "build_renewable_profiles", color = "0.62 0.6 0.85", style="rounded"]; + 7[label = "base_network", color = "0.13 0.6 0.85", style="rounded"]; + 8[label = "build_shapes", color = "0.31 0.6 0.85", style="rounded"]; + 9[label = "retrieve_databundle", color = "0.49 0.6 0.85", style="rounded"]; + 10[label = "build_ship_raster", color = "0.39 0.6 0.85", style="rounded"]; + 11[label = "retrieve_ship_raster", color = "0.61 0.6 0.85", style="rounded"]; + 12[label = "retrieve_cutout", color = "0.65 0.6 0.85", style="rounded"]; + 13[label = "cluster_gas_network", color = "0.05 0.6 0.85", style="rounded"]; + 14[label = "build_gas_network", color = "0.18 0.6 0.85", style="rounded"]; + 15[label = "retrieve_gas_infrastructure_data", color = "0.49 0.6 0.85", style="rounded"]; + 16[label = "cluster_network", color = "0.45 0.6 0.85", style="rounded"]; + 17[label = "simplify_network", color = "0.28 0.6 0.85", style="rounded"]; + 18[label = "add_electricity", color = "0.40 0.6 0.85", style="rounded"]; + 19[label = "retrieve_cost_data", color = "0.66 0.6 0.85", style="rounded"]; + 20[label = "build_powerplants", color = "0.60 0.6 0.85", style="rounded"]; + 21[label = "build_electricity_demand", color = "0.48 0.6 0.85", style="rounded"]; + 22[label = "retrieve_electricity_demand", color = "0.60 0.6 0.85", style="rounded"]; + 23[label = "retrieve_synthetic_electricity_demand", color = "0.09 0.6 0.85", style="rounded"]; + 24[label = "build_gas_input_locations", color = "0.08 0.6 0.85", style="rounded"]; + 25[label = "time_aggregation", color = "0.51 0.6 0.85", style="rounded"]; + 26[label = "prepare_network", color = "0.58 0.6 0.85", style="rounded"]; + 27[label = "add_extra_components", color = "0.25 0.6 0.85", style="rounded"]; + 28[label = "build_hourly_heat_demand", color = "0.06 0.6 0.85", style="rounded"]; + 29[label = "build_daily_heat_demand", color = "0.23 0.6 0.85", style="rounded"]; + 30[label = "build_population_layouts", color = "0.33 0.6 0.85", style="rounded"]; + 31[label = "build_solar_thermal_profiles", color = "0.62 0.6 0.85", style="rounded"]; + 32[label = "retrieve_eurostat_data", color = "0.43 0.6 0.85", style="rounded"]; + 33[label = "build_population_weighted_energy_totals", color = "0.12 0.6 0.85", style="rounded"]; + 34[label = "build_energy_totals", color = "0.17 0.6 0.85", style="rounded"]; + 35[label = "build_clustered_population_layouts", color = "0.59 0.6 0.85", style="rounded"]; + 36[label = "build_heat_totals", color = "0.01 0.6 0.85", style="rounded"]; + 37[label = "build_shipping_demand", color = "0.15 0.6 0.85", style="rounded"]; + 38[label = "build_transport_demand", color = "0.16 0.6 0.85", style="rounded"]; + 39[label = "build_temperature_profiles", color = "0.41 0.6 0.85", style="rounded"]; + 40[label = "build_biomass_potentials", color = "0.53 0.6 0.85", style="rounded"]; + 41[label = "build_salt_cavern_potentials", color = "0.54 0.6 0.85", style="rounded"]; + 42[label = "build_simplified_population_layouts", color = "0.42 0.6 0.85", style="rounded"]; + 43[label = "build_industrial_energy_demand_per_node", color = "0.28 0.6 0.85", style="rounded"]; + 44[label = "build_industry_sector_ratios_intermediate", color = "0.35 0.6 0.85", style="rounded"]; + 45[label = "build_industry_sector_ratios", color = "0.58 0.6 0.85", style="rounded"]; + 46[label = "build_ammonia_production", color = "0.02 0.6 0.85", style="rounded"]; + 47[label = "build_industrial_energy_demand_per_country_today", color = "0.07 0.6 0.85", style="rounded"]; + 48[label = "build_industrial_production_per_country", color = "0.40 0.6 0.85", style="rounded"]; + 49[label = "build_industrial_production_per_node", color = "0.46 0.6 0.85", style="rounded"]; + 50[label = "build_industrial_distribution_key", color = "0.20 0.6 0.85", style="rounded"]; + 51[label = "build_industrial_production_per_country_tomorrow", color = "0.47 0.6 0.85", style="rounded"]; + 52[label = "build_industrial_energy_demand_per_node_today", color = "0.30 0.6 0.85", style="rounded"]; + 53[label = "build_district_heat_share", color = "0.17 0.6 0.85", style="rounded"]; + 54[label = "build_cop_profiles", color = "0.14 0.6 0.85", style="rounded"]; + 55[label = "build_existing_heating_distribution", color = "0.19 0.6 0.85", style="rounded"]; + 56[label = "add_brownfield", color = "0.33 0.6 0.85", style="rounded"]; + 57[label = "plot_power_network_clustered", color = "0.56 0.6 0.85", style="rounded"]; + 58[label = "plot_power_network", color = "0.51 0.6 0.85", style="rounded"]; + 59[label = "plot_hydrogen_network", color = "0.47 0.6 0.85", style="rounded"]; 1 -> 0 - 2 -> 1 - 30 -> 1 9 -> 1 + 32 -> 1 + 2 -> 1 + 58 -> 2 3 -> 2 - 63 -> 2 - 73 -> 2 - 22 -> 2 - 83 -> 2 - 84 -> 2 - 85 -> 2 - 86 -> 2 - 87 -> 2 - 88 -> 2 - 89 -> 2 + 59 -> 2 + 19 -> 2 + 57 -> 2 + 19 -> 3 4 -> 3 - 22 -> 3 - 5 -> 4 - 23 -> 4 - 18 -> 4 + 56 -> 3 17 -> 4 - 33 -> 4 - 22 -> 4 - 58 -> 4 - 62 -> 4 - 6 -> 5 - 13 -> 5 - 14 -> 5 - 27 -> 5 - 28 -> 5 - 30 -> 5 - 31 -> 5 - 35 -> 5 + 35 -> 4 + 16 -> 4 + 20 -> 4 + 54 -> 4 + 55 -> 4 + 19 -> 4 + 5 -> 4 + 25 -> 5 + 33 -> 5 + 54 -> 5 + 40 -> 5 37 -> 5 + 41 -> 5 38 -> 5 + 53 -> 5 + 34 -> 5 + 39 -> 5 + 13 -> 5 + 42 -> 5 + 26 -> 5 32 -> 5 - 9 -> 5 - 40 -> 5 - 22 -> 5 - 41 -> 5 - 18 -> 5 + 28 -> 5 17 -> 5 - 33 -> 5 - 42 -> 5 + 16 -> 5 + 35 -> 5 + 24 -> 5 43 -> 5 - 53 -> 5 - 55 -> 5 - 39 -> 5 - 56 -> 5 - 57 -> 5 - 58 -> 5 - 59 -> 5 - 60 -> 5 - 61 -> 5 + 6 -> 5 + 31 -> 5 + 9 -> 5 + 19 -> 5 + 8 -> 6 7 -> 6 9 -> 6 - 10 -> 6 - 8 -> 6 12 -> 6 + 10 -> 6 8 -> 7 9 -> 8 - 11 -> 10 12 -> 10 - 7 -> 13 - 9 -> 13 - 10 -> 13 - 8 -> 13 - 12 -> 13 + 11 -> 10 + 14 -> 13 + 16 -> 13 15 -> 14 - 17 -> 14 - 16 -> 15 + 17 -> 16 + 19 -> 16 18 -> 17 - 22 -> 17 - 19 -> 18 - 22 -> 18 + 7 -> 17 + 19 -> 17 + 8 -> 18 + 21 -> 18 7 -> 18 - 20 -> 19 - 21 -> 19 - 6 -> 19 - 13 -> 19 - 7 -> 19 - 22 -> 19 - 23 -> 19 - 24 -> 19 - 8 -> 19 + 20 -> 18 + 19 -> 18 + 6 -> 18 7 -> 20 - 9 -> 20 - 8 -> 20 - 12 -> 20 - 7 -> 21 - 9 -> 21 - 8 -> 21 - 12 -> 21 - 7 -> 23 - 25 -> 24 - 26 -> 24 + 22 -> 21 + 23 -> 21 + 15 -> 24 + 16 -> 24 + 31 -> 25 + 26 -> 25 + 28 -> 25 + 19 -> 26 + 27 -> 26 + 19 -> 27 16 -> 27 - 17 -> 27 29 -> 28 - 22 -> 28 - 17 -> 29 - 22 -> 29 - 32 -> 31 - 33 -> 31 - 8 -> 32 - 9 -> 32 - 30 -> 32 + 12 -> 29 + 16 -> 29 + 30 -> 29 + 12 -> 30 + 8 -> 30 + 12 -> 31 + 16 -> 31 + 30 -> 31 34 -> 33 - 17 -> 33 - 12 -> 33 + 36 -> 33 + 35 -> 33 + 9 -> 34 + 32 -> 34 8 -> 34 - 12 -> 34 - 36 -> 35 - 33 -> 35 - 32 -> 36 + 12 -> 35 + 16 -> 35 + 30 -> 35 + 34 -> 36 + 34 -> 37 8 -> 37 - 17 -> 37 - 32 -> 37 + 16 -> 37 + 39 -> 38 33 -> 38 - 31 -> 38 - 32 -> 38 + 35 -> 38 9 -> 38 - 39 -> 38 - 34 -> 39 - 17 -> 39 + 34 -> 38 12 -> 39 + 16 -> 39 + 30 -> 39 9 -> 40 - 17 -> 40 8 -> 40 + 16 -> 40 9 -> 41 - 17 -> 41 - 34 -> 42 - 18 -> 42 + 16 -> 41 12 -> 42 - 44 -> 43 + 17 -> 42 + 30 -> 42 49 -> 43 52 -> 43 + 44 -> 43 45 -> 44 - 47 -> 44 48 -> 44 - 46 -> 45 + 47 -> 44 9 -> 45 + 46 -> 45 9 -> 46 9 -> 47 48 -> 47 - 46 -> 48 9 -> 48 - 30 -> 48 + 32 -> 48 + 46 -> 48 50 -> 49 51 -> 49 - 17 -> 50 - 33 -> 50 + 35 -> 50 + 16 -> 50 48 -> 51 - 50 -> 52 47 -> 52 - 54 -> 53 - 34 -> 54 - 17 -> 54 - 12 -> 54 - 32 -> 55 + 50 -> 52 + 34 -> 53 + 35 -> 53 + 39 -> 54 + 53 -> 55 + 35 -> 55 33 -> 55 - 34 -> 56 17 -> 56 - 12 -> 56 - 34 -> 57 - 17 -> 57 - 12 -> 57 - 39 -> 58 - 56 -> 58 - 57 -> 58 - 34 -> 59 - 17 -> 59 - 12 -> 59 - 34 -> 60 - 17 -> 60 - 12 -> 60 - 34 -> 61 - 17 -> 61 - 12 -> 61 - 33 -> 62 - 31 -> 62 - 55 -> 62 - 64 -> 63 - 67 -> 63 - 20 -> 64 - 21 -> 64 - 6 -> 64 - 13 -> 64 - 18 -> 64 - 17 -> 64 - 65 -> 64 - 3 -> 64 - 67 -> 64 - 58 -> 64 - 6 -> 65 - 13 -> 65 - 14 -> 65 - 27 -> 65 - 28 -> 65 - 30 -> 65 - 31 -> 65 - 35 -> 65 - 37 -> 65 - 38 -> 65 - 32 -> 65 - 9 -> 65 - 66 -> 65 - 67 -> 65 - 41 -> 65 - 18 -> 65 - 17 -> 65 - 33 -> 65 - 42 -> 65 - 68 -> 65 - 53 -> 65 - 72 -> 65 - 39 -> 65 - 56 -> 65 - 57 -> 65 - 58 -> 65 - 59 -> 65 - 60 -> 65 - 61 -> 65 - 9 -> 66 - 17 -> 66 - 8 -> 66 - 69 -> 68 - 70 -> 68 - 52 -> 68 - 45 -> 69 - 47 -> 69 - 48 -> 69 - 50 -> 70 - 71 -> 70 - 48 -> 71 - 32 -> 72 - 33 -> 72 - 74 -> 73 - 77 -> 73 - 20 -> 74 - 21 -> 74 - 6 -> 74 - 13 -> 74 - 18 -> 74 - 17 -> 74 - 75 -> 74 - 63 -> 74 - 77 -> 74 - 58 -> 74 - 6 -> 75 - 13 -> 75 - 14 -> 75 - 27 -> 75 - 28 -> 75 - 30 -> 75 - 31 -> 75 - 35 -> 75 - 37 -> 75 - 38 -> 75 - 32 -> 75 - 9 -> 75 - 76 -> 75 - 77 -> 75 - 41 -> 75 - 18 -> 75 - 17 -> 75 - 33 -> 75 - 42 -> 75 - 78 -> 75 - 53 -> 75 - 82 -> 75 - 39 -> 75 - 56 -> 75 - 57 -> 75 - 58 -> 75 - 59 -> 75 - 60 -> 75 - 61 -> 75 - 9 -> 76 - 17 -> 76 - 8 -> 76 - 79 -> 78 - 80 -> 78 - 52 -> 78 - 45 -> 79 - 47 -> 79 - 48 -> 79 - 50 -> 80 - 81 -> 80 - 48 -> 81 - 32 -> 82 - 33 -> 82 - 17 -> 83 - 3 -> 84 - 17 -> 84 - 63 -> 85 - 17 -> 85 - 73 -> 86 - 17 -> 86 - 3 -> 87 - 17 -> 87 - 63 -> 88 - 17 -> 88 - 73 -> 89 - 17 -> 89 + 3 -> 56 + 16 -> 56 + 54 -> 56 + 19 -> 56 + 5 -> 56 + 6 -> 56 + 16 -> 57 + 16 -> 58 + 3 -> 58 + 16 -> 59 + 3 -> 59 } | diff --git a/rules/build_sector.smk b/rules/build_sector.smk index c2ca80c36..86784fe87 100644 --- a/rules/build_sector.smk +++ b/rules/build_sector.smk @@ -859,6 +859,40 @@ rule build_existing_heating_distribution: "../scripts/build_existing_heating_distribution.py" +rule time_aggregation: + params: + time_resolution=config_provider("clustering", "temporal", "resolution_sector"), + drop_leap_day=config_provider("enable", "drop_leap_day"), + solver_name=config_provider("solving", "solver", "name"), + input: + network=resources("networks/elec_s{simpl}_{clusters}_ec_l{ll}_{opts}.nc"), + hourly_heat_demand_total=lambda w: ( + resources("hourly_heat_demand_total_elec_s{simpl}_{clusters}.nc") + if config_provider("sector", "heating")(w) + else None + ), + solar_thermal_total=lambda w: ( + resources("solar_thermal_total_elec_s{simpl}_{clusters}.nc") + if config_provider("sector", "solar_thermal")(w) + else None + ), + output: + snapshot_weightings=resources( + "snapshot_weightings_elec_s{simpl}_{clusters}_ec_l{ll}_{opts}.csv" + ), + threads: 1 + resources: + mem_mb=5000, + log: + logs("time_aggregation_elec_s{simpl}_{clusters}_ec_l{ll}_{opts}.log"), + benchmark: + benchmarks("time_aggregation_elec_s{simpl}_{clusters}_ec_l{ll}_{opts}") + conda: + "../envs/environment.yaml" + script: + "../scripts/time_aggregation.py" + + def input_profile_offwind(w): return { f"profile_{tech}": resources(f"profile_{tech}.nc") @@ -870,7 +904,6 @@ def input_profile_offwind(w): rule prepare_sector_network: params: time_resolution=config_provider("clustering", "temporal", "resolution_sector"), - drop_leap_day=config_provider("enable", "drop_leap_day"), co2_budget=config_provider("co2_budget"), conventional_carriers=config_provider( "existing_capacities", "conventional_carriers" @@ -891,6 +924,9 @@ rule prepare_sector_network: unpack(input_profile_offwind), **rules.cluster_gas_network.output, **rules.build_gas_input_locations.output, + snapshot_weightings=resources( + "snapshot_weightings_elec_s{simpl}_{clusters}_ec_l{ll}_{opts}.csv" + ), retro_cost=lambda w: ( resources("retro_cost_elec_s{simpl}_{clusters}.csv") if config_provider("sector", "retrofitting", "retro_endogen")(w) diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index d35af1c90..fb6af9763 100755 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -809,33 +809,6 @@ def add_co2limit(n, options, nyears=1.0, limit=0.0): ) -# TODO PyPSA-Eur merge issue -def average_every_nhours(n, offset): - logger.info(f"Resampling the network to {offset}") - m = n.copy(with_time=False) - - snapshot_weightings = n.snapshot_weightings.resample(offset).sum() - sns = snapshot_weightings.index - if snakemake.params.drop_leap_day: - sns = sns[~((sns.month == 2) & (sns.day == 29))] - snapshot_weightings = snapshot_weightings.loc[sns] - m.set_snapshots(snapshot_weightings.index) - m.snapshot_weightings = snapshot_weightings - - for c in n.iterate_components(): - pnl = getattr(m, c.list_name + "_t") - for k, df in c.pnl.items(): - if not df.empty: - if c.list_name == "stores" and k == "e_max_pu": - pnl[k] = df.resample(offset).min() - elif c.list_name == "stores" and k == "e_min_pu": - pnl[k] = df.resample(offset).max() - else: - pnl[k] = df.resample(offset).mean() - - return m - - def cycling_shift(df, steps=1): """ Cyclic shift on index of pd.Series|pd.DataFrame by number of steps. @@ -3513,100 +3486,48 @@ def renamer(s): import_components_from_dataframe(n, df.loc[to_add], c.name) -def apply_time_segmentation( - n, segments, solver_name="cbc", overwrite_time_dependent=True -): +def set_temporal_aggregation(n, resolution, snapshot_weightings): """ - Aggregating time series to segments with different lengths. - - Input: - n: pypsa Network - segments: (int) number of segments in which the typical period should be - subdivided - solver_name: (str) name of solver - overwrite_time_dependent: (bool) overwrite time dependent data of pypsa network - with typical time series created by tsam + Aggregate time-varying data to the given snapshots. """ - try: - import tsam.timeseriesaggregation as tsam - except ImportError: - raise ModuleNotFoundError( - "Optional dependency 'tsam' not found." "Install via 'pip install tsam'" - ) - - # get all time-dependent data - columns = pd.MultiIndex.from_tuples([], names=["component", "key", "asset"]) - raw = pd.DataFrame(index=n.snapshots, columns=columns) - for c in n.iterate_components(): - for attr, pnl in c.pnl.items(): - # exclude e_min_pu which is used for SOC of EVs in the morning - if not pnl.empty and attr != "e_min_pu": - df = pnl.copy() - df.columns = pd.MultiIndex.from_product([[c.name], [attr], df.columns]) - raw = pd.concat([raw, df], axis=1) - - # normalise all time-dependent data - annual_max = raw.max().replace(0, 1) - raw = raw.div(annual_max, level=0) - - # get representative segments - agg = tsam.TimeSeriesAggregation( - raw, - hoursPerPeriod=len(raw), - noTypicalPeriods=1, - noSegments=int(segments), - segmentation=True, - solver=solver_name, - ) - segmented = agg.createTypicalPeriods() - - weightings = segmented.index.get_level_values("Segment Duration") - offsets = np.insert(np.cumsum(weightings[:-1]), 0, 0) - timesteps = [raw.index[0] + pd.Timedelta(f"{offset}h") for offset in offsets] - snapshots = pd.DatetimeIndex(timesteps) - sn_weightings = pd.Series( - weightings, index=snapshots, name="weightings", dtype="float64" - ) - logger.info(f"Distribution of snapshot durations:\n{weightings.value_counts()}") - - n.set_snapshots(sn_weightings.index) - n.snapshot_weightings = n.snapshot_weightings.mul(sn_weightings, axis=0) - - # overwrite time-dependent data with timeseries created by tsam - if overwrite_time_dependent: - values_t = segmented.mul(annual_max).set_index(snapshots) - for component, key in values_t.columns.droplevel(2).unique(): - n.pnl(component)[key] = values_t[component, key] - - return n - - -def set_temporal_aggregation(n, resolution, solver_name): - """ - Aggregate network temporally. - """ - if not resolution: - return n - - # representative snapshots if "sn" in resolution.lower(): + # Representative snapshots are dealt with directly sn = int(resolution[:-2]) logger.info("Use every %s snapshot as representative", sn) n.set_snapshots(n.snapshots[::sn]) n.snapshot_weightings *= sn + else: + # Otherwise, use the provided snapshots + snapshot_weightings = pd.read_csv( + snapshot_weightings, index_col=0, parse_dates=True + ) - # segments with package tsam - elif "seg" in resolution.lower(): - segments = int(resolution[:-3]) - logger.info("Use temporal segmentation with %s segments", segments) - n = apply_time_segmentation(n, segments, solver_name=solver_name) + n.set_snapshots(snapshot_weightings.index) + n.snapshot_weightings = snapshot_weightings - # temporal averaging - elif "h" in resolution.lower(): - logger.info("Aggregate to frequency %s", resolution) - n = average_every_nhours(n, resolution) + # Define a series used for aggregation, mapping each hour in + # n.snapshots to the closest previous timestep in + # snapshot_weightings.index + aggregation_map = ( + pd.Series( + snapshot_weightings.index.get_indexer(n.snapshots), index=n.snapshots + ) + .replace(-1, np.nan) + .ffill() + .astype(int) + .map(lambda i: snapshot_weightings.index[i]) + ) - return n + # Aggregation all time-varying data. + for c in n.iterate_components(): + for k, df in c.pnl.items(): + if not df.empty: + if c.list_name == "stores" and k == "e_max_pu": + c.pnl[k] = df.groupby(aggregation_map).min() + elif c.list_name == "stores" and k == "e_min_pu": + c.pnl[k] = df.groupby(aggregation_map).max() + else: + c.pnl[k] = df.groupby(aggregation_map).mean() def lossy_bidirectional_links(n, carrier, efficiencies={}): @@ -3758,9 +3679,9 @@ def lossy_bidirectional_links(n, carrier, efficiencies={}): if options["allam_cycle"]: add_allam(n, costs) - solver_name = snakemake.config["solving"]["solver"]["name"] - resolution = snakemake.params.time_resolution - n = set_temporal_aggregation(n, resolution, solver_name) + set_temporal_aggregation( + n, snakemake.params.time_resolution, snakemake.input.snapshot_weightings + ) co2_budget = snakemake.params.co2_budget if isinstance(co2_budget, str) and co2_budget.startswith("cb"): diff --git a/scripts/time_aggregation.py b/scripts/time_aggregation.py new file mode 100644 index 000000000..93b41f3af --- /dev/null +++ b/scripts/time_aggregation.py @@ -0,0 +1,150 @@ +# -*- coding: utf-8 -*- +# SPDX-FileCopyrightText: : 2017-2024 The PyPSA-Eur Authors +# +# SPDX-License-Identifier: MIT +""" +Defines the time aggregation to be used for sector-coupled network. + +Relevant Settings +----------------- + +.. code:: yaml + + clustering: + temporal: + resolution_sector: + + enable: + drop_leap_day: + +Inputs +------ + +- ``networks/elec_s{simpl}_{clusters}_ec_l{ll}_{opts}.nc``: the network whose snapshots are to be aggregated +- ``resources/hourly_heat_demand_total_elec_s{simpl}_{clusters}.nc``: the total hourly heat demand +- ``resources/solar_thermal_total_elec_s{simpl}_{clusters}.nc``: the total hourly solar thermal generation + +Outputs +------- + +- ``snapshot_weightings_elec_s{simpl}_{clusters}_ec_l{ll}_{opts}.csv`` + +Description +----------- +Computes a time aggregation scheme for the given network, in the form of a CSV file with the snapshot weightings, indexed by the new subset of snapshots. This rule only computes said aggregation scheme; aggregation of time-varying network data is done in ``prepare_sector_network.py``. +""" + + +import logging + +import numpy as np +import pandas as pd +import pypsa +import xarray as xr +from _helpers import ( + configure_logging, + set_scenario_config, + update_config_from_wildcards, +) + +if __name__ == "__main__": + if "snakemake" not in globals(): + from _helpers import mock_snakemake + + snakemake = mock_snakemake( + "time_aggregation", + configfiles="test/config.overnight.yaml", + simpl="", + opts="", + clusters="37", + ll="v1.0", + sector_opts="CO2L0-24h-T-H-B-I-A-dist1", + planning_horizons="2030", + ) + + configure_logging(snakemake) + set_scenario_config(snakemake) + update_config_from_wildcards(snakemake.config, snakemake.wildcards) + + n = pypsa.Network(snakemake.input.network) + resolution = snakemake.params.time_resolution + + # Representative snapshots + if "sn" in resolution.lower(): + logger.info("Use representative snapshots") + # Output an empty csv; this is taken care of in prepare_sector_network.py + pd.DataFrame().to_csv(snakemake.output.snapshot_weightings) + + # Plain resampling + elif "h" in resolution.lower(): + offset = resolution.lower() + logger.info(f"Averaging every {offset} hours") + snapshot_weightings = n.snapshot_weightings.resample(offset).sum() + sns = snapshot_weightings.index + if snakemake.params.drop_leap_day: + sns = sns[~((sns.month == 2) & (sns.day == 29))] + snapshot_weightings = snapshot_weightings.loc[sns] + snapshot_weightings.to_csv(snakemake.output.snapshot_weightings) + + # Temporal segmentation + elif "seg" in resolution.lower(): + segments = int(resolution[:-3]) + logger.info(f"Use temporal segmentation with {segments} segments") + + try: + import tsam.timeseriesaggregation as tsam + except ImportError: + raise ModuleNotFoundError( + "Optional dependency 'tsam' not found." "Install via 'pip install tsam'" + ) + + # Get all time-dependent data + dfs = [ + pnl + for c in n.iterate_components() + for attr, pnl in c.pnl.items() + if not pnl.empty and attr != "e_min_pu" + ] + if snakemake.input.hourly_heat_demand_total: + dfs.append( + xr.open_dataset(snakemake.input.hourly_heat_demand_total) + .to_dataframe() + .unstack(level=1) + ) + if snakemake.input.solar_thermal_total: + dfs.append( + xr.open_dataset(snakemake.input.solar_thermal_total) + .to_dataframe() + .unstack(level=1) + ) + df = pd.concat(dfs, axis=1) + + # Reset columns to flat index + df = df.T.reset_index(drop=True).T + + # Normalise all time-dependent data + annual_max = df.max().replace(0, 1) + df = df.div(annual_max, level=0) + + # Get representative segments + agg = tsam.TimeSeriesAggregation( + df, + hoursPerPeriod=len(df), + noTypicalPeriods=1, + noSegments=segments, + segmentation=True, + solver=snakemake.params.solver_name, + ) + agg = agg.createTypicalPeriods() + + weightings = agg.index.get_level_values("Segment Duration") + offsets = np.insert(np.cumsum(weightings[:-1]), 0, 0) + snapshot_weightings = n.snapshot_weightings.loc[n.snapshots[offsets]].mul( + weightings, axis=0 + ) + + logger.info( + f"Distribution of snapshot durations:\n{snapshot_weightings.objective.value_counts()}" + ) + + snapshot_weightings.to_csv(snakemake.output.snapshot_weightings)