Skip to content

Commit

Permalink
Merge pull request #1020 from openego/features/#1014-load-areas
Browse files Browse the repository at this point in the history
Features/#1014 load areas
  • Loading branch information
nesnoj committed Dec 22, 2022
2 parents 88fd2da + 1407d8d commit 8329d3e
Show file tree
Hide file tree
Showing 12 changed files with 1,620 additions and 48 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,8 @@ Added
`#955 <https://github.com/openego/eGon-data/issues/955>`_
* Add desaggregation of pv home batteries onto buildings
`#988 <https://github.com/openego/eGon-data/issues/988>`_
* Add load areas
`#1014 <https://github.com/openego/eGon-data/issues/1014>`_

.. _PR #159: https://github.com/openego/eGon-data/pull/159
.. _PR #703: https://github.com/openego/eGon-data/pull/703
Expand Down
24 changes: 20 additions & 4 deletions src/egon/data/airflow/dags/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
)
from egon.data.datasets.industrial_sites import MergeIndustrialSites
from egon.data.datasets.industry import IndustrialDemandCurves
from egon.data.datasets.loadarea import LoadArea
from egon.data.datasets.loadarea import LoadArea, OsmLanduse
from egon.data.datasets.mastr import mastr_data_setup
from egon.data.datasets.mv_grid_districts import mv_grid_districts_setup
from egon.data.datasets.osm import OpenStreetMap
Expand Down Expand Up @@ -210,7 +210,7 @@
)

# Extract landuse areas from the `osm` dataset
load_area = LoadArea(dependencies=[osm, vg250])
osm_landuse = OsmLanduse(dependencies=[osm, vg250])

# Calculate feedin from renewables
renewable_feedin = RenewableFeedin(
Expand Down Expand Up @@ -300,7 +300,7 @@
dependencies=[
demandregio,
industrial_sites,
load_area,
osm_landuse,
mv_grid_districts,
osm,
]
Expand Down Expand Up @@ -451,7 +451,7 @@
demand_curves_industry,
district_heating_areas,
industrial_sites,
load_area,
osm_landuse,
mastr_data,
mv_grid_districts,
scenario_capacities,
Expand Down Expand Up @@ -591,6 +591,22 @@
]
)

# Create load areas
load_areas = LoadArea(
dependencies=[
osm_landuse,
zensus_vg250,
household_electricity_demand_annual,
tasks[
"electricity_demand_timeseries"
".hh_buildings"
".get-building-peak-loads"
],
cts_demand_buildings,
demand_curves_industry,
]
)

# ########## Keep this dataset at the end
# Sanity Checks
sanity_checks = SanityChecks(
Expand Down
121 changes: 117 additions & 4 deletions src/egon/data/datasets/loadarea/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
"""The central module containing all code to create tables for osm landuse
extraction.
"""
OSM landuse extraction and load areas creation.
**Landuse**
* Landuse data is extracted from OpenStreetMap: residential, retail,
industrial, Agricultural
* Data is cut with German borders (VG 250), data outside is dropped
* Invalid geometries are fixed
* Results are stored in table `openstreetmap.osm_landuse`
**Load Areas**
TBD
Note: industrial demand contains:
* voltage levels 4-7
* only demand from ind. sites+osm located in LA!
"""

import os

from airflow.operators.postgres_operator import PostgresOperator
from geoalchemy2.types import Geometry
Expand Down Expand Up @@ -31,10 +49,10 @@ class OsmPolygonUrban(Base):
geom = Column(Geometry("MultiPolygon", 3035))


class LoadArea(Dataset):
class OsmLanduse(Dataset):
def __init__(self, dependencies):
super().__init__(
name="LoadArea",
name="OsmLanduse",
version="0.0.0",
dependencies=dependencies,
tasks=(
Expand All @@ -51,6 +69,27 @@ def __init__(self, dependencies):
)


class LoadArea(Dataset):
def __init__(self, dependencies):
super().__init__(
name="LoadArea",
version="0.0.1",
dependencies=dependencies,
tasks=(
osm_landuse_melt,
census_cells_melt,
osm_landuse_census_cells_melt,
loadareas_create,
{
loadareas_add_demand_hh,
loadareas_add_demand_cts,
loadareas_add_demand_ind,
},
drop_temp_tables,
),
)


def create_landuse_table():
"""Create tables for landuse data
Returns
Expand All @@ -70,3 +109,77 @@ def create_landuse_table():

engine = db.engine()
OsmPolygonUrban.__table__.create(bind=engine, checkfirst=True)


def execute_sql_script(script):
"""Execute SQL script
Parameters
----------
script : str
Filename of script
"""
db.execute_sql_script(os.path.join(os.path.dirname(__file__), script))


def osm_landuse_melt():
"""Melt all OSM landuse areas by: buffer, union, unbuffer"""
print("Melting OSM landuse areas from openstreetmap.osm_landuse...")
execute_sql_script("osm_landuse_melt.sql")


def census_cells_melt():
"""Melt all census cells: buffer, union, unbuffer"""
print(
"Melting census cells from "
"society.destatis_zensus_population_per_ha_inside_germany..."
)
execute_sql_script("census_cells_melt.sql")


def osm_landuse_census_cells_melt():
"""Melt OSM landuse areas and census cells"""
print(
"Melting OSM landuse areas from openstreetmap.osm_landuse_melted and "
"census cells from "
"society.egon_destatis_zensus_cells_melted_cluster..."
)
execute_sql_script("osm_landuse_census_cells_melt.sql")


def loadareas_create():
"""Create load areas from merged OSM landuse and census cells:
* Cut Loadarea with MV Griddistrict
* Identify and exclude Loadarea smaller than 100m².
* Generate Centre of Loadareas with Centroid and PointOnSurface.
* Calculate population from Census 2011.
* Cut all 4 OSM sectors with MV Griddistricts.
* Calculate statistics like NUTS and AGS code.
* Check for Loadareas without AGS code.
"""
print("Create initial load areas and add some sector stats...")
execute_sql_script("loadareas_create.sql")


def loadareas_add_demand_hh():
"""Adds consumption and peak load to load areas for households"""
print("Add consumption and peak loads to load areas for households...")
execute_sql_script("loadareas_add_demand_hh.sql")


def loadareas_add_demand_cts():
"""Adds consumption and peak load to load areas for CTS"""
print("Add consumption and peak loads to load areas for CTS...")
execute_sql_script("loadareas_add_demand_cts.sql")


def loadareas_add_demand_ind():
"""Adds consumption and peak load to load areas for industry"""
print("Add consumption and peak loads to load areas for industry...")
execute_sql_script("loadareas_add_demand_ind.sql")


def drop_temp_tables():
print("Dropping temp tables, views and sequences...")
execute_sql_script("drop_temp_tables.sql")
159 changes: 159 additions & 0 deletions src/egon/data/datasets/loadarea/census_cells_melt.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/*
Loads from Census 2011
Include Census 2011 population per ha.
Identify population in OSM loads.
Include Census cells with CTS demand.
__copyright__ = "Reiner Lemoine Institut"
__license__ = "GNU Affero General Public License Version 3 (AGPL-3.0)"
__url__ = "https://github.com/openego/eGon-data/blob/main/LICENSE"
__author__ = "Ludee, nesnoj"
*/


-- zensus load
DROP TABLE IF EXISTS society.egon_destatis_zensus_cells_melted CASCADE;
CREATE TABLE society.egon_destatis_zensus_cells_melted (
id SERIAL NOT NULL,
gid integer,
population integer,
inside_la boolean,
geom_point geometry(Point,3035),
geom geometry(Polygon,3035),
CONSTRAINT egon_destatis_zensus_cells_melted_pkey PRIMARY KEY (id));

-- insert zensus loads
INSERT INTO society.egon_destatis_zensus_cells_melted (gid,population,inside_la,geom_point,geom)
SELECT id ::integer AS gid,
population ::integer,
'FALSE' ::boolean AS inside_la,
geom_point ::geometry(Point,3035),
geom ::geometry(Polygon,3035)
--FROM model_draft.destatis_zensus_population_per_ha_invg_mview
FROM society.destatis_zensus_population_per_ha_inside_germany
ORDER BY gid;

-- zensus cells with CTS loads
INSERT INTO society.egon_destatis_zensus_cells_melted (gid,population,inside_la,geom_point,geom)
SELECT
id ::integer AS gid,
0 AS population,
'FALSE' ::boolean AS inside_la,
geom_point ::geometry(Point,3035),
geom ::geometry(Polygon,3035)
FROM society.destatis_zensus_population_per_ha
WHERE id in (
SELECT DISTINCT zensus_population_id
FROM demand.egon_demandregio_zensus_electricity
WHERE scenario = 'eGon2035' AND sector = 'service'
)
ORDER BY gid;

-- index gist (geom_point)
CREATE INDEX egon_destatis_zensus_cells_melted_geom_point_idx
ON society.egon_destatis_zensus_cells_melted USING GIST (geom_point);

-- index gist (geom)
CREATE INDEX egon_destatis_zensus_cells_melted_geom_idx
ON society.egon_destatis_zensus_cells_melted USING GIST (geom);

-- population in osm loads
UPDATE society.egon_destatis_zensus_cells_melted AS t1
SET inside_la = t2.inside_la
FROM (
SELECT zensus.id AS id,
'TRUE' ::boolean AS inside_la
FROM society.egon_destatis_zensus_cells_melted AS zensus,
openstreetmap.osm_landuse_melted AS osm
WHERE osm.geom && zensus.geom_point AND
ST_CONTAINS(osm.geom,zensus.geom_point)
) AS t2
WHERE t1.id = t2.id;

-- remove identified population
DELETE FROM society.egon_destatis_zensus_cells_melted AS lp
WHERE lp.inside_la IS TRUE;



-- cluster from zensus load lattice
DROP TABLE IF EXISTS society.egon_destatis_zensus_cells_melted_cluster CASCADE;
CREATE TABLE society.egon_destatis_zensus_cells_melted_cluster (
cid serial,
zensus_sum INT,
area_ha INT,
geom geometry(Polygon,3035),
geom_buffer geometry(Polygon,3035),
geom_centroid geometry(Point,3035),
geom_surfacepoint geometry(Point,3035),
CONSTRAINT egon_destatis_zensus_cells_melted_cluster_pkey PRIMARY KEY (cid));

-- insert cluster
INSERT INTO society.egon_destatis_zensus_cells_melted_cluster(geom)
SELECT (ST_DUMP(ST_MULTI(ST_UNION(geom)))).geom ::geometry(Polygon,3035)
FROM society.egon_destatis_zensus_cells_melted;
-- ORDER BY gid;

-- index gist (geom)
CREATE INDEX egon_destatis_zensus_cells_melted_cluster_geom_idx
ON society.egon_destatis_zensus_cells_melted_cluster USING GIST (geom);

-- index gist (geom_centroid)
CREATE INDEX egon_destatis_zensus_cells_melted_cluster_geom_centroid_idx
ON society.egon_destatis_zensus_cells_melted_cluster USING GIST (geom_centroid);

-- index gist (geom_surfacepoint)
CREATE INDEX egon_destatis_zensus_cells_melted_cluster_geom_surfacepoint_idx
ON society.egon_destatis_zensus_cells_melted_cluster USING GIST (geom_surfacepoint);

-- insert cluster
INSERT INTO society.egon_destatis_zensus_cells_melted_cluster(geom)
SELECT (ST_DUMP(ST_MULTI(ST_UNION(grid.geom)))).geom ::geometry(Polygon,3035) AS geom
FROM society.egon_destatis_zensus_cells_melted AS grid;

-- cluster data
UPDATE society.egon_destatis_zensus_cells_melted_cluster AS t1
SET zensus_sum = t2.zensus_sum,
area_ha = t2.area_ha,
geom_buffer = t2.geom_buffer,
geom_centroid = t2.geom_centroid,
geom_surfacepoint = t2.geom_surfacepoint
FROM (
SELECT cl.cid AS cid,
SUM(lp.population) AS zensus_sum,
COUNT(lp.geom) AS area_ha,
ST_BUFFER(cl.geom, 100) AS geom_buffer,
ST_Centroid(cl.geom) AS geom_centroid,
ST_PointOnSurface(cl.geom) AS geom_surfacepoint
FROM society.egon_destatis_zensus_cells_melted AS lp,
society.egon_destatis_zensus_cells_melted_cluster AS cl
WHERE cl.geom && lp.geom AND
ST_CONTAINS(cl.geom,lp.geom)
GROUP BY cl.cid
ORDER BY cl.cid
) AS t2
WHERE t1.cid = t2.cid;


-- zensus stats
DROP MATERIALIZED VIEW IF EXISTS openstreetmap.egon_society_zensus_per_la_mview CASCADE;
CREATE MATERIALIZED VIEW openstreetmap.egon_society_zensus_per_la_mview AS
-- SELECT 'destatis_zensus_population_per_ha_mview' AS name,
-- sum(population),
-- count(geom) AS census_count
-- FROM openstreetmap.destatis_zensus_population_per_ha_mview
-- UNION ALL
SELECT 'destatis_zensus_population_per_ha_inside_germany' AS name,
sum(population),
count(geom) AS census_count
FROM society.destatis_zensus_population_per_ha_inside_germany
UNION ALL
SELECT 'egon_destatis_zensus_cells_melted' AS name,
sum(population),
count(geom) AS census_count
FROM society.egon_destatis_zensus_cells_melted
UNION ALL
SELECT 'egon_destatis_zensus_cells_melted_cluster' AS name,
sum(zensus_sum),
count(geom) AS census_count
FROM society.egon_destatis_zensus_cells_melted_cluster;

0 comments on commit 8329d3e

Please sign in to comment.