diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 93fe5b49a..fe04e03ef 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -177,6 +177,10 @@ Added `#262 `_ * Assign household profiles to OSM buildings `#435 `_ +* Add link to meta creator to docs + `#599 `_ +* Add extendable batteries and heat stores + `#566 `_ .. _PR #159: https://github.com/openego/eGon-data/pull/159 @@ -359,5 +363,7 @@ Bug fixes `#535 `_ * Filter target values by scenario name `#570 `_ +* Reduce number of timesteps of hh electricity demand profiles to 8760 + `#593 `_ * Fix assignemnt of heat demand profiles at German borders `#585 `_ diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index a20a0f443..3b4434455 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -236,7 +236,8 @@ Things that definitely should be checked during a review of a PR: * *Was* ``CHANGELOG.rst`` *updated accordingly?* Should be the case, please verify. * *Is metadata complete and correct (in case of data integration)?* Please - verify. In case of a pending metadata creation make sure an appropriate issue is filed. + verify. In case of a pending metadata creation make sure an appropriate + issue is filed. Extending the data workflow @@ -322,6 +323,11 @@ For previous discussions on metadata, you may want to check Helpers ^^^^^^^ +You can use the `Metadata creator `_ **GUI**. +Fill the fields and hit `Edit JSON` to get the metadata string. Vice versa, +you can paste a metadata string into this box and the fields will be filled +automatically which may be helpful if you want to amend existing strings. + There are some **licence templates** provided in :py:mod:`egon.data.metadata` you can make use of for fields 11.4 and 12 of the `Open Energy Metadata Description`_. Also, there's a template for the diff --git a/src/egon/data/airflow/dags/pipeline.py b/src/egon/data/airflow/dags/pipeline.py index 9dee48aaa..be1df8cac 100755 --- a/src/egon/data/airflow/dags/pipeline.py +++ b/src/egon/data/airflow/dags/pipeline.py @@ -185,7 +185,12 @@ # osmTGmod ehv/hv grid model generation osmtgmod = Osmtgmod( - dependencies=[osm_download, substation_extraction, setup_etrago] + dependencies=[ + osm_download, + substation_extraction, + setup_etrago, + scenario_parameters, + ] ) osmtgmod.insert_into(pipeline) osmtgmod_pypsa = tasks["osmtgmod.to-pypsa"] @@ -220,6 +225,7 @@ hd_abroad.insert_into(pipeline) heat_demands_abroad_download = tasks["heat_demand_europe.download"] + # Extract landuse areas from osm data set load_area = LoadArea(dependencies=[osm, vg250]) diff --git a/src/egon/data/datasets.yml b/src/egon/data/datasets.yml index ae242dbf6..baf128462 100644 --- a/src/egon/data/datasets.yml +++ b/src/egon/data/datasets.yml @@ -611,6 +611,9 @@ etrago_heat: heat_link_timeseries: schema: 'grid' table: 'egon_etrago_link_timeseries' + heat_stores: + schema: 'grid' + table: 'egon_etrago_store' etrago_hydrogen: sources: diff --git a/src/egon/data/datasets/electricity_demand_timeseries/hh_profiles.py b/src/egon/data/datasets/electricity_demand_timeseries/hh_profiles.py index 69d4a2ce7..c62211269 100644 --- a/src/egon/data/datasets/electricity_demand_timeseries/hh_profiles.py +++ b/src/egon/data/datasets/electricity_demand_timeseries/hh_profiles.py @@ -10,10 +10,10 @@ The resulting data is stored in two separate tables * `demand.household_electricity_profiles_in_census_cells`: - Lists references and scaling parameters to time series data for each household - in a cell by identifiers. This table is fundamental for creating subsequent - data like demand profiles on MV grid level or for determining the peak load - at load area level. + Lists references and scaling parameters to time series data for each + household in a cell by identifiers. This table is fundamental for creating + subsequent data like demand profiles on MV grid level or for determining + the peak load at load area level. The table is created by:func:`houseprofiles_in_census_cells`. * `demand.household_electricity_profiles_hvmv_substation`: Household electricity demand profiles aggregated at MV grid district level @@ -52,17 +52,24 @@ **How are these datasets combined?** -* Spatial information about people living in households by zensus (2011) at federal state NUTS1 level - :var:`df_zensus` is aggregated to be compatible to IEE household profile specifications. +* Spatial information about people living in households by zensus (2011) at + federal state NUTS1 level :var:`df_zensus` is aggregated to be compatible + to IEE household profile specifications. * exclude kids and reduce to adults and seniors * group as defined in :var:`HH_TYPES` - * convert data from people living in households to number of households by :var:`mapping_people_in_households` - * calculate fraction of fine household types (10) within subgroup of rough household types (5) :var:`df_dist_households` -* Spatial information about number of households per ha :var:`df_households_typ` is mapped to NUTS1 and NUTS3 level. - Data is enriched with refined household subgroups via :var:`df_dist_households` in :var:`df_zensus_cells`. -* Enriched 100 x 100 m household dataset is used to sample and aggregate household profiles. A table including - individual profile id's for each cell and scaling factor to match Demand-Regio annual sum projections for 2035 and 2050 - at NUTS3 level is created in the database as `demand.household_electricity_profiles_in_census_cells`. + * convert data from people living in households to number of households + by :var:`mapping_people_in_households` + * calculate fraction of fine household types (10) within subgroup of rough + household types (5) :var:`df_dist_households` +* Spatial information about number of households per ha + :var:`df_households_typ` is mapped to NUTS1 and NUTS3 level. + Data is enriched with refined household subgroups via + :var:`df_dist_households` in :var:`df_zensus_cells`. +* Enriched 100 x 100 m household dataset is used to sample and aggregate + household profiles. A table including individual profile id's for each cell + and scaling factor to match Demand-Regio annual sum projections for 2035 + and 2050 at NUTS3 level is created in the database as + `demand.household_electricity_profiles_in_census_cells`. **What are central assumptions during the data processing?** @@ -130,7 +137,8 @@ # - Adults living in households type # - number of kids are not included even if mentioned in household type name # **! The Eurostat data only counts adults/seniors, excluding kids <15** -# Eurostat household types are used for demand-profile-generator @iee-fraunhofer +# Eurostat household types are used for demand-profile-generator +# @iee-fraunhofer HH_TYPES = { "SR": [ ("Einpersonenhaushalte (Singlehaushalte)", "Insgesamt", "Seniors"), @@ -177,10 +185,10 @@ ("Paare ohne Kind(er)", "6 und mehr Personen", "Seniors"), ], # no info about share of kids - # OO, O1, O2 have the same amount, as no information about the share of kids - # within census data set. If needed the total amount can be estimated in the - # hh_tools.get_hh_dist function using multi_adjust=True option. The Eurostat - # share is then applied. + # OO, O1, O2 have the same amount, as no information about the share of + # kids within census data set. If needed the total amount can be estimated + # in the :func:`get_hh_dist` function using multi_adjust=True option. + # The Eurostat share is then applied. "OO": [ ("Mehrpersonenhaushalte ohne Kernfamilie", "3 Personen", "Adults"), ("Mehrpersonenhaushalte ohne Kernfamilie", "4 Personen", "Adults"), @@ -242,10 +250,12 @@ class EgonEtragoElectricityHouseholds(Base): setup = partial( Dataset, name="HH Demand", - version="0.0.3", + version="0.0.4", dependencies=[], - # Tasks are declared in pipeline as function is used multiple times with different args - # To differentiate these tasks PythonOperator with specific id-names are used + # Tasks are declared in pipeline as function is used multiple times with + # different args. + # To differentiate these tasks PythonOperator with specific id-names are + # used. # PythonOperator needs to be declared in pipeline to be mapped to DAG # tasks=[], ) @@ -321,7 +331,8 @@ def write_hh_profiles_to_db(hh_profiles): def get_iee_hh_demand_profiles_raw(): - """Gets and returns household electricity demand profiles from the egon-data-bundle. + """Gets and returns household electricity demand profiles from the + egon-data-bundle. Household electricity demand profiles generated by Fraunhofer IEE. Methodology is described in @@ -331,12 +342,22 @@ def get_iee_hh_demand_profiles_raw(): It is used and further described in the following theses by: * Jonas Haack: - "Auswirkungen verschiedener Haushaltslastprofile auf PV-Batterie-Systeme" (confidential) + "Auswirkungen verschiedener Haushaltslastprofile auf PV-Batterie-Systeme" + (confidential) * Simon Ruben Drauz - "Synthesis of a heat and electrical load profile for single and multi-family houses used for subsequent - performance tests of a multi-component energy system", + "Synthesis of a heat and electrical load profile for single and + multi-family houses used for subsequent performance tests of a + multi-component energy system", http://dx.doi.org/10.13140/RG.2.2.13959.14248 + Notes + ----- + The household electricity demand profiles have been generated for a leap + year (8784 hours) starting on a Friday. The weather year is 2011 and the + heat timeseries 2011 are generated for 2011 too (cf. dataset + :mod:`egon.data.datasets.heat_demand_timeseries.HTS`), having 8760h and + starting on a Saturday. To align the profiles, the first day of the IEE + profiles are deleted, resulting in 8760h starting on Saturday. Returns ------- @@ -375,6 +396,13 @@ def ve(s): df_hh_profiles = pd.read_hdf(hh_profiles_file) + # Use only last 8760 timesteps of profiles (for details see notes) + timesteps_target = 8760 + if len(df_hh_profiles) > timesteps_target: + df_hh_profiles = df_hh_profiles[ + len(df_hh_profiles) - timesteps_target : + ].reset_index(drop=True) + return df_hh_profiles @@ -421,8 +449,9 @@ def get_zensus_households_raw(): * Search for: "1000A-3016" * or choose topic: "Bevölkerung kompakt" - * Choose table code: "1000A-3016" with title "Personen: Alter (11 Altersklassen) - Größe des - privaten Haushalts - Typ des privaten Haushalts (nach Familien/Lebensform)" + * Choose table code: "1000A-3016" with title "Personen: Alter + (11 Altersklassen) - Größe des privaten Haushalts - Typ des privaten + Haushalts (nach Familien/Lebensform)" - Change setting "GEOLK1" to "Bundesländer (16)" Data would be available in higher resolution @@ -657,8 +686,9 @@ def inhabitants_to_households( def process_nuts1_census_data(df_census_households_raw): """Make data compatible with household demand profile categories - Groups, removes and reorders categories which are not needed to fit data to household types of - IEE electricity demand time series generated by demand-profile-generator (DPG). + Groups, removes and reorders categories which are not needed to fit data to + household types of IEE electricity demand time series generated by + demand-profile-generator (DPG). * Kids (<15) are excluded as they are also excluded in DPG origin dataset * Adults (15<65) @@ -758,7 +788,7 @@ def refine_census_data_at_cell_level(df_zensus): Number of hh types per census cell and scaling factors """ - # hh_tools.get_hh_dist without eurostat adjustment for O1-03 Groups in + # :func:`get_hh_dist` without eurostat adjustment for O1-03 Groups in # absolute values df_hh_types_nad_abs = get_hh_dist(df_zensus, HH_TYPES) @@ -767,10 +797,10 @@ def refine_census_data_at_cell_level(df_zensus): # The hh types 1 P and 2 P households are dropped df_hh_size = db.select_dataframe( sql=""" - SELECT characteristics_text, SUM(quantity) as summe - FROM society.egon_destatis_zensus_household_per_ha as egon_d - WHERE attribute = 'HHGROESS_KLASS' AND quantity_q < 2 - GROUP BY characteristics_text """, + SELECT characteristics_text, SUM(quantity) as summe + FROM society.egon_destatis_zensus_household_per_ha as egon_d + WHERE attribute = 'HHGROESS_KLASS' AND quantity_q < 2 + GROUP BY characteristics_text """, index_col="characteristics_text", ) df_hh_size = df_hh_size.drop(index=["1 Person", "2 Personen"]) @@ -809,9 +839,9 @@ def refine_census_data_at_cell_level(df_zensus): # Only use cell-data which quality (quantity_q<2) is acceptable df_households_typ = db.select_dataframe( sql=""" - SELECT grid_id, attribute, characteristics_code, characteristics_text, quantity - FROM society.egon_destatis_zensus_household_per_ha - WHERE attribute = 'HHTYP_FAM' AND quantity_q <2""" + SELECT grid_id, attribute, characteristics_code, characteristics_text, quantity + FROM society.egon_destatis_zensus_household_per_ha + WHERE attribute = 'HHTYP_FAM' AND quantity_q <2""" ) df_households_typ = df_households_typ.drop( columns=["attribute", "characteristics_text"] @@ -841,21 +871,21 @@ def refine_census_data_at_cell_level(df_zensus): ) missing_cells = db.select_dataframe( sql=""" - SELECT t12.grid_id, t12.quantity - FROM ( - SELECT t2.grid_id, (case when quantity_sum_fam isnull then quantity_gesamt end) as quantity - FROM ( - SELECT grid_id, SUM(quantity) as quantity_sum_fam - FROM society.egon_destatis_zensus_household_per_ha - WHERE attribute = 'HHTYP_FAM' - GROUP BY grid_id) as t1 - Full JOIN ( - SELECT grid_id, sum(quantity) as quantity_gesamt - FROM society.egon_destatis_zensus_household_per_ha - WHERE attribute = 'INSGESAMT' - GROUP BY grid_id) as t2 ON t1.grid_id = t2.grid_id - ) as t12 - WHERE quantity is not null""" + SELECT t12.grid_id, t12.quantity + FROM ( + SELECT t2.grid_id, (case when quantity_sum_fam isnull then quantity_gesamt end) as quantity + FROM ( + SELECT grid_id, SUM(quantity) as quantity_sum_fam + FROM society.egon_destatis_zensus_household_per_ha + WHERE attribute = 'HHTYP_FAM' + GROUP BY grid_id) as t1 + Full JOIN ( + SELECT grid_id, sum(quantity) as quantity_gesamt + FROM society.egon_destatis_zensus_household_per_ha + WHERE attribute = 'INSGESAMT' + GROUP BY grid_id) as t2 ON t1.grid_id = t2.grid_id + ) as t12 + WHERE quantity is not null""" ) # Missing cells are substituted by average share of cells with same amount @@ -875,13 +905,13 @@ def refine_census_data_at_cell_level(df_zensus): # Census cells with nuts3 and nuts1 information df_grid_id = db.select_dataframe( sql=""" - SELECT pop.grid_id, pop.id as cell_id, vg250.vg250_nuts3 as nuts3, lan.nuts as nuts1, lan.gen - FROM society.destatis_zensus_population_per_ha_inside_germany as pop - LEFT JOIN boundaries.egon_map_zensus_vg250 as vg250 - ON (pop.id=vg250.zensus_population_id) - LEFT JOIN boundaries.vg250_lan as lan - ON (LEFT(vg250.vg250_nuts3, 3)=lan.nuts) - WHERE lan.gf = 4 """ + SELECT pop.grid_id, pop.id as cell_id, vg250.vg250_nuts3 as nuts3, lan.nuts as nuts1, lan.gen + FROM society.destatis_zensus_population_per_ha_inside_germany as pop + LEFT JOIN boundaries.egon_map_zensus_vg250 as vg250 + ON (pop.id=vg250.zensus_population_id) + LEFT JOIN boundaries.vg250_lan as lan + ON (LEFT(vg250.vg250_nuts3, 3)=lan.nuts) + WHERE lan.gf = 4 """ ) df_grid_id = df_grid_id.drop_duplicates() df_grid_id = df_grid_id.reset_index(drop=True) @@ -1073,8 +1103,8 @@ def adjust_to_demand_regio_nuts3_annual( Returns ------- pd.DataFrame - Returns the same data as :func:`allocate_hh_demand_profiles_to_cells`, but with - filled columns `factor_2035` and `factor_2050`. + Returns the same data as :func:`allocate_hh_demand_profiles_to_cells`, + but with filled columns `factor_2035` and `factor_2050`. """ for nuts3_id, df_nuts3 in df_hh_profiles_in_census_cells.groupby( by="nuts3" @@ -1154,7 +1184,8 @@ def get_load_timeseries( load_area_meta = df_hh_profiles_in_census_cells.loc[ cell_ids, ["cell_profile_ids", "nuts3", f"factor_{year}"] ] - # loop over nuts3 (part_load) and sum (full_load) as the scaling factor applies at nuts3 level + # loop over nuts3 (part_load) and sum (full_load) as the scaling factor + # applies at nuts3 level for (nuts3, factor), df in load_area_meta.groupby( by=["nuts3", f"factor_{year}"] ): diff --git a/src/egon/data/datasets/heat_etrago/__init__.py b/src/egon/data/datasets/heat_etrago/__init__.py index cc1c00c7b..abdd7b3cf 100644 --- a/src/egon/data/datasets/heat_etrago/__init__.py +++ b/src/egon/data/datasets/heat_etrago/__init__.py @@ -88,6 +88,142 @@ def insert_buses(carrier, scenario="eGon2035"): ) +def insert_store(scenario, carrier): + + sources = config.datasets()["etrago_heat"]["sources"] + targets = config.datasets()["etrago_heat"]["targets"] + + db.execute_sql( + f""" + DELETE FROM {targets['heat_buses']['schema']}. + {targets['heat_buses']['table']} + WHERE carrier = '{carrier}_store' + AND scn_name = '{scenario}' + """ + ) + db.execute_sql( + f""" + DELETE FROM {targets['heat_links']['schema']}. + {targets['heat_links']['table']} + WHERE carrier LIKE '{carrier}_store%' + AND scn_name = '{scenario}' + """ + ) + db.execute_sql( + f""" + DELETE FROM {targets['heat_stores']['schema']}. + {targets['heat_stores']['table']} + WHERE carrier = '{carrier}_store' + AND scn_name = '{scenario}' + """ + ) + + dh_bus = db.select_geodataframe( + f""" + SELECT * FROM + {targets['heat_buses']['schema']}. + {targets['heat_buses']['table']} + WHERE carrier = '{carrier}' + AND scn_name = '{scenario}' + """, + epsg=4326, + ) + + water_tank_bus = dh_bus.copy() + water_tank_bus.carrier = carrier + "_store" + water_tank_bus.bus_id = range( + db.next_etrago_id("bus"), + db.next_etrago_id("bus") + len(water_tank_bus), + ) + + water_tank_bus.to_postgis( + targets['heat_buses']['table'], + schema=targets['heat_buses']['schema'], + con=db.engine(), + if_exists="append", + index=False, + ) + + water_tank_charger = pd.DataFrame( + data={ + "scn_name": scenario, + "bus0": dh_bus.bus_id, + "bus1": water_tank_bus.bus_id, + "carrier": carrier + "_store_charger", + "efficiency_fixed": get_sector_parameters("heat", "eGon2035")[ + "efficiency" + ]["water_tank_charger"], + "p_nom_extendable": True, + "link_id": range( + db.next_etrago_id("link"), + db.next_etrago_id("link") + len(water_tank_bus), + ), + } + ) + + water_tank_charger.to_sql( + targets['heat_links']['table'], + schema=targets['heat_links']['schema'], + con=db.engine(), + if_exists="append", + index=False, + ) + + water_tank_discharger = pd.DataFrame( + data={ + "scn_name": scenario, + "bus0": water_tank_bus.bus_id, + "bus1": dh_bus.bus_id, + "carrier": carrier + "_store_discharger", + "efficiency_fixed": get_sector_parameters("heat", "eGon2035")[ + "efficiency" + ]["water_tank_discharger"], + "p_nom_extendable": True, + "link_id": range( + db.next_etrago_id("link"), + db.next_etrago_id("link") + len(water_tank_bus), + ), + } + ) + + water_tank_discharger.to_sql( + targets['heat_links']['table'], + schema=targets['heat_links']['schema'], + con=db.engine(), + if_exists="append", + index=False, + ) + + water_tank_store = pd.DataFrame( + data={ + "scn_name": scenario, + "bus": water_tank_bus.bus_id, + "carrier": carrier + "_store", + "capital_cost": get_sector_parameters("heat", "eGon2035")[ + "capital_cost" + ][f"{carrier.split('_')[0]}_water_tank"], + "e_nom_extendable": True, + "store_id": range( + db.next_etrago_id("store"), + db.next_etrago_id("store") + len(water_tank_bus), + ), + } + ) + + water_tank_store.to_sql( + targets['heat_stores']['table'], + schema=targets['heat_stores']['schema'], + con=db.engine(), + if_exists="append", + index=False, + ) + + +def store(): + insert_store("eGon2035", "central_heat") + insert_store("eGon2035", "rural_heat") + + def insert_central_direct_heat(scenario="eGon2035"): """Insert renewable heating technologies (solar and geo thermal) @@ -414,5 +550,5 @@ def __init__(self, dependencies): name="HeatEtrago", version="0.0.6", dependencies=dependencies, - tasks=(buses, supply), + tasks=(buses, supply, store), ) diff --git a/src/egon/data/datasets/osmtgmod/__init__.py b/src/egon/data/datasets/osmtgmod/__init__.py index df538d21c..198fbf69c 100644 --- a/src/egon/data/datasets/osmtgmod/__init__.py +++ b/src/egon/data/datasets/osmtgmod/__init__.py @@ -15,6 +15,7 @@ from egon.data import db from egon.data.config import settings from egon.data.datasets import Dataset +from egon.data.datasets.scenario_parameters import get_sector_parameters import egon.data.config import egon.data.subprocess as subproc @@ -535,6 +536,10 @@ def to_pypsa(): for scenario_name in ["'eGon2035'", "'eGon100RE'"]: + capital_cost = get_sector_parameters("electricity", "eGon2035")[ + "capital_cost" + ] + db.execute_sql( f""" -- BUS DATA @@ -555,7 +560,8 @@ def to_pypsa(): -- BRANCH DATA INSERT INTO grid.egon_etrago_line (scn_name, line_id, bus0, - bus1, x, r, b, s_nom, cables, v_nom, + bus1, x, r, b, s_nom, s_nom_min, s_nom_extendable, + cables, v_nom, geom, topo) SELECT {scenario_name}, @@ -566,6 +572,8 @@ def to_pypsa(): br_r AS r, br_b as b, rate_a as s_nom, + rate_a as s_nom_min, + TRUE, cables, branch_voltage/1000 as v_nom, geom, @@ -578,7 +586,7 @@ def to_pypsa(): -- TRANSFORMER DATA INSERT INTO grid.egon_etrago_transformer (scn_name, trafo_id, bus0, bus1, x, - s_nom, tap_ratio, + s_nom, s_nom_min, s_nom_extendable, tap_ratio, phase_shift, geom, topo) SELECT {scenario_name}, @@ -587,6 +595,8 @@ def to_pypsa(): t_bus AS bus1, br_x/100 AS x, rate_a as s_nom, + rate_a as s_nom_min, + TRUE, tap AS tap_ratio, shift AS phase_shift, geom, @@ -626,7 +636,62 @@ def to_pypsa(): as result WHERE a.line_id = result.line_id; - + -- set capital costs for eHV-lines + UPDATE grid.egon_etrago_line + SET capital_cost = {capital_cost['ac_ehv_overhead_line']} * length + WHERE v_nom > 110; + + -- set capital costs for HV-lines + UPDATE grid.egon_etrago_line + SET capital_cost = {capital_cost['ac_hv_overhead_line']} * length + WHERE v_nom = 110; + + -- set capital costs for transformers + UPDATE grid.egon_etrago_transformer a + SET capital_cost = {capital_cost['transformer_380_220']} + WHERE (a.bus0 IN ( + SELECT bus_id FROM grid.egon_etrago_bus + WHERE v_nom = 380) + AND a.bus1 IN ( + SELECT bus_id FROM grid.egon_etrago_bus + WHERE v_nom = 220)) + OR (a.bus0 IN ( + SELECT bus_id FROM grid.egon_etrago_bus + WHERE v_nom = 220) + AND a.bus1 IN ( + SELECT bus_id FROM grid.egon_etrago_bus + WHERE v_nom = 380)); + + UPDATE grid.egon_etrago_transformer a + SET capital_cost = {capital_cost['transformer_380_110']} + WHERE (a.bus0 IN ( + SELECT bus_id FROM grid.egon_etrago_bus + WHERE v_nom = 380) + AND a.bus1 IN ( + SELECT bus_id FROM grid.egon_etrago_bus + WHERE v_nom = 110)) + OR (a.bus0 IN ( + SELECT bus_id FROM grid.egon_etrago_bus + WHERE v_nom = 110) + AND a.bus1 IN ( + SELECT bus_id FROM grid.egon_etrago_bus + WHERE v_nom = 380)); + + UPDATE grid.egon_etrago_transformer a + SET capital_cost = {capital_cost['transformer_220_110']} + WHERE (a.bus0 IN ( + SELECT bus_id FROM grid.egon_etrago_bus + WHERE v_nom = 220) + AND a.bus1 IN ( + SELECT bus_id FROM grid.egon_etrago_bus + WHERE v_nom = 110)) + OR (a.bus0 IN ( + SELECT bus_id FROM grid.egon_etrago_bus + WHERE v_nom = 110) + AND a.bus1 IN ( + SELECT bus_id FROM grid.egon_etrago_bus + WHERE v_nom = 220)); + -- delete buses without connection to AC grid and generation or -- load assigned @@ -653,7 +718,7 @@ class Osmtgmod(Dataset): def __init__(self, dependencies): super().__init__( name="Osmtgmod", - version="0.0.0", + version="0.0.1", dependencies=dependencies, tasks=( import_osm_data, diff --git a/src/egon/data/datasets/scenario_parameters/parameters.py b/src/egon/data/datasets/scenario_parameters/parameters.py index 63f88529b..412527e3e 100644 --- a/src/egon/data/datasets/scenario_parameters/parameters.py +++ b/src/egon/data/datasets/scenario_parameters/parameters.py @@ -350,7 +350,7 @@ def heat(scenario): "central_water_tank": read_costs( costs, "central water tank storage", "investment" ), - "rual_water_tank": read_costs( + "rural_water_tank": read_costs( costs, "decentral water tank storage", "investment" ), } diff --git a/src/egon/data/datasets/storages_etrago/__init__.py b/src/egon/data/datasets/storages_etrago/__init__.py index ed6f339da..097ef23b3 100644 --- a/src/egon/data/datasets/storages_etrago/__init__.py +++ b/src/egon/data/datasets/storages_etrago/__init__.py @@ -17,9 +17,9 @@ class StorageEtrago(Dataset): def __init__(self, dependencies): super().__init__( name="StorageEtrago", - version="0.0.3", + version="0.0.4", dependencies=dependencies, - tasks=(insert_PHES), + tasks=(insert_PHES, extendable_batteries), ) @@ -73,3 +73,82 @@ def insert_PHES(): if_exists="append", index=phes.index, ) + + +def extendable_batteries_per_scenario(scenario): + + # Get datasets configuration + sources = config.datasets()["storage_etrago"]["sources"] + targets = config.datasets()["storage_etrago"]["targets"] + + engine = db.engine() + + # Delete outdated data on extendable battetries inside Germany from database + db.execute_sql( + f""" + DELETE FROM {targets['storage']['schema']}.{targets['storage']['table']} + WHERE carrier = 'battery' + AND scn_name = '{scenario}' + AND bus NOT IN (SELECT bus_id FROM {sources['bus']['schema']}.{sources['bus']['table']} + WHERE scn_name = '{scenario}' + AND country = 'DE'); + """ + ) + + extendable_batteries = db.select_dataframe( + f""" + SELECT bus_id as bus, scn_name FROM + {sources['bus']['schema']}. + {sources['bus']['table']} + WHERE carrier = 'AC' + AND scn_name = '{scenario}' + AND bus_id IN (SELECT bus_id + FROM {sources['bus']['schema']}.{sources['bus']['table']} + WHERE scn_name = '{scenario}' + AND country = 'DE') + """, + ) + + # Update index + extendable_batteries[ + "storage_id" + ] = extendable_batteries.index + db.next_etrago_id("storage") + + # Set parameters + extendable_batteries["p_nom_extendable"] = True + + extendable_batteries["capital_cost"] = get_sector_parameters( + "electricity", scenario + )["capital_cost"]["battery"] + + extendable_batteries["max_hours"] = get_sector_parameters( + "electricity", scenario + )["efficiency"]["battery"]["max_hours"] + + extendable_batteries["efficiency_store"] = get_sector_parameters( + "electricity", scenario + )["efficiency"]["battery"]["store"] + + extendable_batteries["efficiency_dispatch"] = get_sector_parameters( + "electricity", scenario + )["efficiency"]["battery"]["dispatch"] + + extendable_batteries["standing_loss"] = get_sector_parameters( + "electricity", scenario + )["efficiency"]["battery"]["standing_loss"] + + extendable_batteries["carrier"] = "battery" + + # Write data to db + extendable_batteries.to_sql( + targets["storage"]["table"], + engine, + schema=targets["storage"]["schema"], + if_exists="append", + index=False, + ) + + +def extendable_batteries(): + + extendable_batteries_per_scenario("eGon2035")