Skip to content

Commit

Permalink
Features/#684 distribute pv rooftop buildings (#934)
Browse files Browse the repository at this point in the history
* added pv rooftop distribution and sanity checks
* changed peak load buildings querry to match #826
* updated pre-commit hooks
* made sure that only grid districts with any buildings are assigned pv rooftop capacity
* ensure that no more pv rooftop capacity is allocated to any mv grid district than there is rooftop potential
* match with new table 'boundaries.egon_map_zensus_mvgd_buildings'
* change dataset version

Co-authored-by: Julian.Endres <julian.endres@rl-institut.de>
Co-authored-by: nesnoj <jonathan.amme@rl-institut.de>
  • Loading branch information
3 people committed Nov 24, 2022
1 parent 8c7c9cb commit e8b9a8e
Show file tree
Hide file tree
Showing 7 changed files with 3,023 additions and 15 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,10 @@ Added
`#382 <https://github.com/openego/eGon-data/issues/382>`_
* Add motorized individual travel
`#553 <https://github.com/openego/eGon-data/issues/553>`_
* Allocating MaStR PV rooftop power plants to OSM and synthetic buildings.
Desaggregating PV rooftop scenarios to mv grid districts and OSM and synthetic
buildings.
`#684 <https://github.com/openego/eGon-data/issues/684>`_
* Add mapping zensus - weather cells
`#845 <https://github.com/openego/eGon-data/issues/845>`_
* Add pv rooftop plants per mv grid for eGon100RE
Expand Down Expand Up @@ -609,7 +613,7 @@ Bug Fixes
* Fix model load timeseries in motorized individual travel
`#830 <https://github.com/openego/eGon-data/issues/830>`_
* Fix gas costs
`#830 <https://github.com/openego/eGon-data/issues/847>`_
`#847 <https://github.com/openego/eGon-data/issues/847>`_
* Add imports that have been wrongly deleted
`#849 <https://github.com/openego/eGon-data/issues/849>`_
* Fix final demand of heat demand timeseries
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,9 @@ def read(*names, **kwargs):
"rioxarray",
"rtree",
"saio",
"seaborn",
"shapely",
"snakemake",
"snakemake<7",
"sqlalchemy<1.4", # Airflow<2.0 is not compatible with SQLAlchemy>=1.4
"wtforms<3", # WTForms>=3.0 breaks Airflow<2.0
"xarray",
Expand Down
1 change: 1 addition & 0 deletions src/egon/data/datasets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ hotmaps_current_policy_scenario_heat_demands_buildings:

power_plants:
sources:
mastr_pv: "bnetza_mastr_solar_cleaned.csv"
mastr_biomass: "bnetza_mastr_biomass_cleaned.csv"
mastr_hydro: "bnetza_mastr_hydro_cleaned.csv"
mastr_location: "location_elec_generation_raw.csv"
Expand Down
25 changes: 13 additions & 12 deletions src/egon/data/datasets/power_plants/__init__.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
"""The central module containing all code dealing with power plant data.
"""
from geoalchemy2 import Geometry
from sqlalchemy import (
BigInteger,
Boolean,
Column,
Float,
Integer,
Sequence,
String,
)
from sqlalchemy import BigInteger, Column, Float, Integer, Sequence, String
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
Expand All @@ -25,6 +17,10 @@
select_no_chp_combustion_mastr,
)
from egon.data.datasets.power_plants.pv_rooftop import pv_rooftop_per_mv_grid
from egon.data.datasets.power_plants.pv_rooftop_buildings import (
geocode_mastr_data,
pv_rooftop_to_buildings,
)
import egon.data.config
import egon.data.datasets.power_plants.assign_weather_data as assign_weather_data
import egon.data.datasets.power_plants.pv_ground_mounted as pv_ground_mounted
Expand Down Expand Up @@ -53,7 +49,7 @@ class PowerPlants(Dataset):
def __init__(self, dependencies):
super().__init__(
name="PowerPlants",
version="0.0.10",
version="0.0.14",
dependencies=dependencies,
tasks=(
create_tables,
Expand All @@ -63,7 +59,11 @@ def __init__(self, dependencies):
{
wind_onshore.insert,
pv_ground_mounted.insert,
pv_rooftop_per_mv_grid,
(
pv_rooftop_per_mv_grid,
geocode_mastr_data,
pv_rooftop_to_buildings,
),
},
wind_offshore.insert,
assign_weather_data.weatherId_and_busId,
Expand Down Expand Up @@ -799,7 +799,8 @@ def allocate_other_power_plants():
"""EinheitBetriebsstatus=='InBetrieb'and Energietraeger=='Klaerschlamm'"""
)
mastr_geothermal = pd.read_csv(cfg["sources"]["mastr_gsgk"]).query(
"""EinheitBetriebsstatus=='InBetrieb' and Energietraeger=='Geothermie' and Technologie == 'ORCOrganicRankineCycleAnlage'"""
"EinheitBetriebsstatus=='InBetrieb' and Energietraeger=='Geothermie' "
"and Technologie == 'ORCOrganicRankineCycleAnlage'"
)

mastr_sg = mastr_sludge.append(mastr_geothermal)
Expand Down
80 changes: 80 additions & 0 deletions src/egon/data/datasets/power_plants/pv_rooftop.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
"""The module containing all code dealing with pv rooftop distribution.
"""
from loguru import logger
from numpy import isclose
import geopandas as gpd
import pandas as pd

from egon.data import config, db
from egon.data.datasets.power_plants.pv_rooftop_buildings import (
PV_CAP_PER_SQ_M,
ROOF_FACTOR,
load_building_data,
)
from egon.data.datasets.scenario_parameters import get_sector_parameters

# assumption on theoretical maximum occupancy of rooftops within an mv grid
# district
# TODO: this is a wild guess
MAX_THEORETICAL_PV_OCCUPANCY = 0.5


def pv_rooftop_per_mv_grid():
"""Execute pv rooftop distribution method per scenario
Expand Down Expand Up @@ -88,6 +100,20 @@ def pv_rooftop_per_mv_grid_and_scenario(scenario, level):
"""
)

# make sure only grid districts with any buildings are used
valid_buildings_gdf = load_building_data()

valid_buildings_gdf = valid_buildings_gdf.assign(
bus_id=valid_buildings_gdf.bus_id.astype(int),
overlay_id=valid_buildings_gdf.overlay_id.astype(int),
max_cap=valid_buildings_gdf.building_area.multiply(
ROOF_FACTOR * PV_CAP_PER_SQ_M
),
)

bus_ids = valid_buildings_gdf.bus_id.unique()
demand = demand.loc[demand.bus_id.isin(bus_ids)]

# Distribute to mv grids per federal state or Germany
if level == "federal_state":
targets_per_federal_state = db.select_dataframe(
Expand Down Expand Up @@ -129,6 +155,18 @@ def pv_rooftop_per_mv_grid_and_scenario(scenario, level):
"""
).capacity[0]

dataset = config.settings()["egon-data"]["--dataset-boundary"]

if dataset == "Schleswig-Holstein":
# since the required data is missing for a SH run, it is implemented
# manually here
total_2035 = 84070
sh_2035 = 2700

share = sh_2035 / total_2035

target *= share

demand["share_country"] = demand.demand / demand.demand.sum()

demand.set_index("bus_id", inplace=True)
Expand All @@ -149,6 +187,48 @@ def pv_rooftop_per_mv_grid_and_scenario(scenario, level):
}
)

# ensure that no more pv rooftop capacity is allocated to any mv grid district than
# there is rooftop potential
max_cap_per_bus_df = (
valid_buildings_gdf[["max_cap", "bus_id"]].groupby("bus_id").sum()
/ 1000
* MAX_THEORETICAL_PV_OCCUPANCY
)

pv_rooftop = pv_rooftop.merge(
max_cap_per_bus_df, how="left", left_on="bus", right_index=True
)

assert ~pv_rooftop.max_cap.isna().any(), (
"There are bus IDs within 'pv_rooftop' which are not included within "
" 'max_cap_per_bus_df'."
)

pv_rooftop = pv_rooftop.assign(delta=pv_rooftop.max_cap - pv_rooftop.p_nom)
loss = pv_rooftop.delta.clip(upper=0).sum()
total = pv_rooftop.p_nom.sum()

pv_rooftop = pv_rooftop.assign(
p_nom=pv_rooftop[["p_nom", "max_cap"]].min(axis=1)
)

pos_delta = pv_rooftop.loc[pv_rooftop.delta > 0].delta
rel_delta = pos_delta / pos_delta.sum()
add_pv_cap = rel_delta * abs(loss)

pv_rooftop.loc[add_pv_cap.index, "p_nom"] += add_pv_cap
pv_rooftop = pv_rooftop.drop(columns=["max_cap", "delta"])

assert isclose(
total, pv_rooftop.p_nom.sum()
), f"{total} != {pv_rooftop.p_nom.sum()}"

if loss < 0:
logger.debug(
f"{loss:g} MW got redistributed from MV grids with too little rooftop "
f"potential towards other MV grids."
)

# Select feedin timeseries
weather_cells = db.select_geodataframe(
f"""
Expand Down

0 comments on commit e8b9a8e

Please sign in to comment.