diff --git a/config/config.yaml b/config/config.yaml index 2ee9a578..58263b9d 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -4,7 +4,7 @@ # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#run run: - prefix: 202413costupdate + prefix: 20242008_dh_subnodes_off name: # - CurrentPolicies - KN2045_Bal_v4 @@ -279,6 +279,7 @@ sector: 2040: 0.6 2045: 0.8 2050: 1.0 + add_subnodes: false central_heat_vent: true co2_spatial: true biomass_spatial: true diff --git a/data/fernwaermeatlas/cities_geolocations.geojson b/data/fernwaermeatlas/cities_geolocations.geojson new file mode 100755 index 00000000..b67c63b0 --- /dev/null +++ b/data/fernwaermeatlas/cities_geolocations.geojson @@ -0,0 +1,167 @@ +{ +"type": "FeatureCollection", +"name": "cities_geolocation", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, +"features": [ +{ "type": "Feature", "properties": { "Stadt": "Berlin" }, "geometry": { "type": "Point", "coordinates": [ 13.3989421, 52.5108638 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Hamburg" }, "geometry": { "type": "Point", "coordinates": [ 10.000654, 53.550341 ] } }, +{ "type": "Feature", "properties": { "Stadt": "München" }, "geometry": { "type": "Point", "coordinates": [ 11.5753822, 48.1371079 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Köln" }, "geometry": { "type": "Point", "coordinates": [ 6.959974, 50.938361 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Mülheim an der Ruhr" }, "geometry": { "type": "Point", "coordinates": [ 6.8829192, 51.4272925 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Leverkusen" }, "geometry": { "type": "Point", "coordinates": [ 6.9881194, 51.0324743 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Bonn" }, "geometry": { "type": "Point", "coordinates": [ 7.10066, 50.735851 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Bergisch Gladbach" }, "geometry": { "type": "Point", "coordinates": [ 7.1277379, 50.9929303 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Frankfurt am Main" }, "geometry": { "type": "Point", "coordinates": [ 8.6820917, 50.1106444 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Offenbach am Main" }, "geometry": { "type": "Point", "coordinates": [ 8.7610698, 50.1055002 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Stuttgart" }, "geometry": { "type": "Point", "coordinates": [ 9.1800132, 48.7784485 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Düsseldorf" }, "geometry": { "type": "Point", "coordinates": [ 6.7763137, 51.2254018 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Neuss" }, "geometry": { "type": "Point", "coordinates": [ 6.6916476, 51.1981778 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Dortmund" }, "geometry": { "type": "Point", "coordinates": [ 7.4652789, 51.5142273 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Essen" }, "geometry": { "type": "Point", "coordinates": [ 7.0158171, 51.4582235 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Duisburg" }, "geometry": { "type": "Point", "coordinates": [ 6.759562, 51.434999 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Bochum" }, "geometry": { "type": "Point", "coordinates": [ 7.2196635, 51.4818111 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Krefeld" }, "geometry": { "type": "Point", "coordinates": [ 6.5623343, 51.3331205 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Oberhausen" }, "geometry": { "type": "Point", "coordinates": [ 6.8514435, 51.4696137 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Herne" }, "geometry": { "type": "Point", "coordinates": [ 7.219985, 51.5380394 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Gelsenkirchen" }, "geometry": { "type": "Point", "coordinates": [ 7.0960124, 51.5110321 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Bottrop" }, "geometry": { "type": "Point", "coordinates": [ 6.929204, 51.521581 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Recklinghausen" }, "geometry": { "type": "Point", "coordinates": [ 7.1978546, 51.6143815 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Moers" }, "geometry": { "type": "Point", "coordinates": [ 6.62843, 51.451283 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Leipzig" }, "geometry": { "type": "Point", "coordinates": [ 12.3747329, 51.3406321 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Bremen" }, "geometry": { "type": "Point", "coordinates": [ 8.8071646, 53.0758196 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Dresden" }, "geometry": { "type": "Point", "coordinates": [ 13.7381437, 51.0493286 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Hannover" }, "geometry": { "type": "Point", "coordinates": [ 9.7385532, 52.3744779 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Nürnberg" }, "geometry": { "type": "Point", "coordinates": [ 11.077298, 49.453872 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Fürth" }, "geometry": { "type": "Point", "coordinates": [ 10.9893626, 49.4772475 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Wuppertal" }, "geometry": { "type": "Point", "coordinates": [ 7.1780374, 51.264018 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Hagen" }, "geometry": { "type": "Point", "coordinates": [ 7.473296, 51.3582945 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Solingen" }, "geometry": { "type": "Point", "coordinates": [ 7.0845893, 51.1721629 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Remscheid" }, "geometry": { "type": "Point", "coordinates": [ 7.228287474564793, 51.184517 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Bielefeld" }, "geometry": { "type": "Point", "coordinates": [ 8.531007, 52.0191005 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Münster" }, "geometry": { "type": "Point", "coordinates": [ 7.6251879, 51.9625101 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Karlsruhe" }, "geometry": { "type": "Point", "coordinates": [ 8.4034195, 49.0068705 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Mannheim" }, "geometry": { "type": "Point", "coordinates": [ 8.4673098, 49.4892913 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Ludwigshafen am Rhein" }, "geometry": { "type": "Point", "coordinates": [ 8.4381568, 49.4704113 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Augsburg" }, "geometry": { "type": "Point", "coordinates": [ 10.8979522, 48.3690341 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Wiesbaden" }, "geometry": { "type": "Point", "coordinates": [ 8.2416556, 50.0820384 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Mainz" }, "geometry": { "type": "Point", "coordinates": [ 8.2762513, 50.0012314 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Mönchengladbach" }, "geometry": { "type": "Point", "coordinates": [ 6.4353792, 51.1947131 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Braunschweig" }, "geometry": { "type": "Point", "coordinates": [ 10.5236066, 52.2646577 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Kiel" }, "geometry": { "type": "Point", "coordinates": [ 10.135555, 54.3227085 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Chemnitz" }, "geometry": { "type": "Point", "coordinates": [ 12.918914, 50.8323531 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Aachen" }, "geometry": { "type": "Point", "coordinates": [ 6.083862, 50.776351 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Magdeburg" }, "geometry": { "type": "Point", "coordinates": [ 11.6399609, 52.1315889 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Halle" }, "geometry": { "type": "Point", "coordinates": [ 11.9705452, 51.4825041 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Freiburg im Breisgau" }, "geometry": { "type": "Point", "coordinates": [ 7.8494005, 47.9960901 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Lübeck" }, "geometry": { "type": "Point", "coordinates": [ 10.684738, 53.866444 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Erfurt" }, "geometry": { "type": "Point", "coordinates": [ 11.0287364, 50.9777974 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Rostock" }, "geometry": { "type": "Point", "coordinates": [ 12.1400211, 54.0886707 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Kassel" }, "geometry": { "type": "Point", "coordinates": [ 9.4924096, 51.3154546 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Saarbrücken" }, "geometry": { "type": "Point", "coordinates": [ 6.996379, 49.234362 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Hamm" }, "geometry": { "type": "Point", "coordinates": [ 7.815197, 51.6804093 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Potsdam" }, "geometry": { "type": "Point", "coordinates": [ 13.0591397, 52.4009309 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Oldenburg in Holstein" }, "geometry": { "type": "Point", "coordinates": [ 10.8809805, 54.2922574 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Osnabrück" }, "geometry": { "type": "Point", "coordinates": [ 8.047635, 52.2719595 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Heidelberg" }, "geometry": { "type": "Point", "coordinates": [ 8.694724, 49.4093582 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Darmstadt" }, "geometry": { "type": "Point", "coordinates": [ 8.6736295, 49.8851869 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Paderborn" }, "geometry": { "type": "Point", "coordinates": [ 8.764869778177559, 51.71895955 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Regensburg" }, "geometry": { "type": "Point", "coordinates": [ 12.0974869, 49.0195333 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Ingolstadt" }, "geometry": { "type": "Point", "coordinates": [ 11.4250395, 48.7630165 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Würzburg" }, "geometry": { "type": "Point", "coordinates": [ 9.9309779, 49.7933723 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Ulm" }, "geometry": { "type": "Point", "coordinates": [ 9.9912458, 48.3984968 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Wolfsburg" }, "geometry": { "type": "Point", "coordinates": [ 10.7861682, 52.4205588 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Heilbronn" }, "geometry": { "type": "Point", "coordinates": [ 9.218655, 49.142291 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Pforzheim" }, "geometry": { "type": "Point", "coordinates": [ 8.7029532, 48.8908846 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Göttingen" }, "geometry": { "type": "Point", "coordinates": [ 9.9351811, 51.5328328 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Reutlingen" }, "geometry": { "type": "Point", "coordinates": [ 9.2114144, 48.4919508 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Koblenz" }, "geometry": { "type": "Point", "coordinates": [ 7.5943951, 50.3533278 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Bremerhaven" }, "geometry": { "type": "Point", "coordinates": [ 8.5851945, 53.5505392 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Jena" }, "geometry": { "type": "Point", "coordinates": [ 11.5879359, 50.9281717 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Erlangen" }, "geometry": { "type": "Point", "coordinates": [ 11.0056, 49.5928616 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Trier" }, "geometry": { "type": "Point", "coordinates": [ 6.6441878, 49.7596208 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Salzgitter" }, "geometry": { "type": "Point", "coordinates": [ 10.3593147, 52.1503721 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Siegen" }, "geometry": { "type": "Point", "coordinates": [ 8.0256131, 50.8751175 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Hildesheim" }, "geometry": { "type": "Point", "coordinates": [ 9.9513046, 52.1521636 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Cottbus" }, "geometry": { "type": "Point", "coordinates": [ 14.3357307, 51.7567447 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Aschaffenburg" }, "geometry": { "type": "Point", "coordinates": [ 9.1493636, 49.9740542 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Baunatal" }, "geometry": { "type": "Point", "coordinates": [ 9.4119007, 51.2550775 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Bergkamen" }, "geometry": { "type": "Point", "coordinates": [ 7.6362876, 51.6149389 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Brühl" }, "geometry": { "type": "Point", "coordinates": [ 6.9037057, 50.8291313 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Datteln" }, "geometry": { "type": "Point", "coordinates": [ 7.3385906, 51.651468 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Dillingen-Saar" }, "geometry": { "type": "Point", "coordinates": [ 6.7243197, 49.3552721 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Dinslaken" }, "geometry": { "type": "Point", "coordinates": [ 6.7345106, 51.5623618 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Eisenhüttenstadt" }, "geometry": { "type": "Point", "coordinates": [ 14.6294413, 52.1448863 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Ensdorf" }, "geometry": { "type": "Point", "coordinates": [ 11.9353839, 49.3435347 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Esslingen am Neckar" }, "geometry": { "type": "Point", "coordinates": [ 9.3071685, 48.7427584 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Flensburg" }, "geometry": { "type": "Point", "coordinates": [ 9.4333264, 54.7833021 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Grevenbroich" }, "geometry": { "type": "Point", "coordinates": [ 6.584893682540318, 51.0862467 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Gladbeck" }, "geometry": { "type": "Point", "coordinates": [ 6.9877343, 51.5718665 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Glücksburg (Ostsee)" }, "geometry": { "type": "Point", "coordinates": [ 9.562033402693672, 54.84544485 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Hanau" }, "geometry": { "type": "Point", "coordinates": [ 8.9169797, 50.132881 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Harrislee" }, "geometry": { "type": "Point", "coordinates": [ 9.3915374, 54.8047109 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Herten" }, "geometry": { "type": "Point", "coordinates": [ 7.1368071, 51.5942009 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Hohenmölsen" }, "geometry": { "type": "Point", "coordinates": [ 12.0981844, 51.1573976 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Hünxe" }, "geometry": { "type": "Point", "coordinates": [ 6.7660319, 51.6414581 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Hürth" }, "geometry": { "type": "Point", "coordinates": [ 6.876568, 50.8807379 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Ketsch" }, "geometry": { "type": "Point", "coordinates": [ 8.5237178, 49.367538 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Langballig" }, "geometry": { "type": "Point", "coordinates": [ 9.663334714510913, 54.7919719 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Lünen" }, "geometry": { "type": "Point", "coordinates": [ 7.5228088, 51.6142482 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Marl" }, "geometry": { "type": "Point", "coordinates": [ 7.0829054, 51.6485843 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Merseburg" }, "geometry": { "type": "Point", "coordinates": [ 11.996148, 51.3564413 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Neukirchen-Vluyn" }, "geometry": { "type": "Point", "coordinates": [ 6.5467641, 51.4413742 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Neu-Ulm" }, "geometry": { "type": "Point", "coordinates": [ 9.9987169, 48.3943949 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Püttlingen" }, "geometry": { "type": "Point", "coordinates": [ 6.8827786, 49.287307 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Quierschied" }, "geometry": { "type": "Point", "coordinates": [ 7.052847, 49.3260163 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Saarlouis" }, "geometry": { "type": "Point", "coordinates": [ 6.749846, 49.3164661 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Schongau" }, "geometry": { "type": "Point", "coordinates": [ 10.8967857, 47.8134583 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Schwedt-Oder" }, "geometry": { "type": "Point", "coordinates": [ 14.2840858, 53.0586366 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Schwetzingen" }, "geometry": { "type": "Point", "coordinates": [ 8.5735135, 49.3832919 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Sindelfingen" }, "geometry": { "type": "Point", "coordinates": [ 9.0035455, 48.7084162 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Speyer" }, "geometry": { "type": "Point", "coordinates": [ 8.433615, 49.3165553 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Spremberg" }, "geometry": { "type": "Point", "coordinates": [ 14.3804302, 51.5714513 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Süderbrarup" }, "geometry": { "type": "Point", "coordinates": [ 9.775192, 54.6354193 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Tarp" }, "geometry": { "type": "Point", "coordinates": [ 9.4026852, 54.6641816 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Voerde (Niederrhein)" }, "geometry": { "type": "Point", "coordinates": [ 6.6811994, 51.5975224 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Völklingen" }, "geometry": { "type": "Point", "coordinates": [ 6.859519, 49.2522866 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Wadgassen" }, "geometry": { "type": "Point", "coordinates": [ 6.7922183, 49.2634657 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Wallerfangen" }, "geometry": { "type": "Point", "coordinates": [ 6.7183652, 49.3277048 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Wees" }, "geometry": { "type": "Point", "coordinates": [ 9.5186181, 54.8060252 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Zolling" }, "geometry": { "type": "Point", "coordinates": [ 11.7727339, 48.4514051 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Stendal" }, "geometry": { "type": "Point", "coordinates": [ 11.8594279, 52.6050782 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Schwerin" }, "geometry": { "type": "Point", "coordinates": [ 11.4148038, 53.6288297 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Schweinfurt" }, "geometry": { "type": "Point", "coordinates": [ 10.233302, 50.0499945 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Neumünster" }, "geometry": { "type": "Point", "coordinates": [ 9.9815377, 54.0757442 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Weißwasser-O.L." }, "geometry": { "type": "Point", "coordinates": [ 14.6373221, 51.5028807 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Lemgo" }, "geometry": { "type": "Point", "coordinates": [ 8.9012894, 52.0280674 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Bergheim" }, "geometry": { "type": "Point", "coordinates": [ 6.6410004, 50.9540457 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Brandenburg an der Havel" }, "geometry": { "type": "Point", "coordinates": [ 12.5497933, 52.4108261 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Castrop-Rauxel" }, "geometry": { "type": "Point", "coordinates": [ 7.3106175, 51.5646195 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Dessau-Roßlau" }, "geometry": { "type": "Point", "coordinates": [ 12.2312238, 51.8465924 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Frankfurt (Oder)" }, "geometry": { "type": "Point", "coordinates": [ 14.549452, 52.3412273 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Gera" }, "geometry": { "type": "Point", "coordinates": [ 12.0832666, 50.8765537 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Greifswald" }, "geometry": { "type": "Point", "coordinates": [ 13.3815238, 54.095791 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Hameln" }, "geometry": { "type": "Point", "coordinates": [ 9.3561569, 52.1039941 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Iserlohn" }, "geometry": { "type": "Point", "coordinates": [ 7.6999713, 51.3746778 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Kaiserslautern" }, "geometry": { "type": "Point", "coordinates": [ 7.7689951, 49.4432174 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Kamp-Lintfort" }, "geometry": { "type": "Point", "coordinates": [ 6.547923, 51.5017981 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Altenholz" }, "geometry": { "type": "Point", "coordinates": [ 10.1187695, 54.3956762 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Annaberg-Buchholz" }, "geometry": { "type": "Point", "coordinates": [ 13.0106108, 50.5788781 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Bad Homburg v.d. Höhe" }, "geometry": { "type": "Point", "coordinates": [ 8.6169093, 50.2267699 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Bad Mergentheim" }, "geometry": { "type": "Point", "coordinates": [ 9.7730692, 49.490532 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Bernburg (Saale)" }, "geometry": { "type": "Point", "coordinates": [ 11.7391606, 51.7930788 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Coswig" }, "geometry": { "type": "Point", "coordinates": [ 12.458638, 51.8803541 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Dreieich" }, "geometry": { "type": "Point", "coordinates": [ 8.7123912, 50.011974 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Flensburg" }, "geometry": { "type": "Point", "coordinates": [ 9.4333264, 54.7833021 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Gelsenkirchen" }, "geometry": { "type": "Point", "coordinates": [ 7.0960124, 51.5110321 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Görlitz" }, "geometry": { "type": "Point", "coordinates": [ 14.991018, 51.1563185 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Kamen" }, "geometry": { "type": "Point", "coordinates": [ 7.6616804, 51.5918019 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Kiel" }, "geometry": { "type": "Point", "coordinates": [ 10.135555, 54.3227085 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Neustrelitz" }, "geometry": { "type": "Point", "coordinates": [ 13.0630004, 53.3617163 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Pfaffenhofen a.d. Ilm" }, "geometry": { "type": "Point", "coordinates": [ 11.5084954, 48.5296743 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Pullach i. Isartal" }, "geometry": { "type": "Point", "coordinates": [ 11.5217455, 48.0556122 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Rottweil" }, "geometry": { "type": "Point", "coordinates": [ 8.6251283, 48.165531 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Soltau" }, "geometry": { "type": "Point", "coordinates": [ 9.8433909, 52.9859666 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Traunreut" }, "geometry": { "type": "Point", "coordinates": [ 12.5952942, 47.9627599 ] } }, +{ "type": "Feature", "properties": { "Stadt": "Zittau" }, "geometry": { "type": "Point", "coordinates": [ 14.8064807, 50.8960964 ] } } +] +} diff --git a/data/fernwaermeatlas/fernwaermeatlas.xlsx b/data/fernwaermeatlas/fernwaermeatlas.xlsx new file mode 100755 index 00000000..4edd34df Binary files /dev/null and b/data/fernwaermeatlas/fernwaermeatlas.xlsx differ diff --git a/workflow/Snakefile b/workflow/Snakefile index 2274f7ba..66d7f188 100644 --- a/workflow/Snakefile +++ b/workflow/Snakefile @@ -115,9 +115,9 @@ rule retrieve_ariadne_database: def input_profile_offwind(w): return { - f"profile_{tech}": resources(f"profile_{tech}.nc") + f"profile_{tech}": resources("profile_{clusters}_" + tech + ".nc") for tech in ["offwind-ac", "offwind-dc", "offwind-float"] - if (tech in config["electricity"]["renewable_carriers"]) + if (tech in config_provider("electricity", "renewable_carriers")(w)) } @@ -130,7 +130,7 @@ use rule prepare_sector_network from pypsaeur with: if k != "district_heat_share" }, district_heat_share=resources( - "district_heat_share_elec_s{simpl}_{clusters}_{planning_horizons}-modified.csv" + "district_heat_share_base_s_{clusters}_{planning_horizons}-modified.csv" ), @@ -170,13 +170,15 @@ rule build_mobility_demand: leitmodelle=config_provider("iiasa_database", "leitmodelle"), input: ariadne=resources("ariadne_database.csv"), - clustered_pop_layout=resources("pop_layout_elec_s{simpl}_{clusters}.csv"), + clustered_pop_layout=resources("pop_layout_base_s_{clusters}.csv"), output: mobility_demand=resources( - "mobility_demand_aladin_{simpl}_{clusters}_{planning_horizons}.csv" + "mobility_demand_aladin_{clusters}_{planning_horizons}.csv" ), resources: mem_mb=1000, + log: + logs("build_mobility_demand_{clusters}_{planning_horizons}.log"), script: "scripts/build_mobility_demand.py" @@ -196,6 +198,53 @@ rule build_egon_data: "scripts/build_egon_data.py" +baseyear_value = config["scenario"]["planning_horizons"][0] + + +rule add_district_heating_subnodes: + params: + district_heating=config_provider("sector", "district_heating"), + baseyear=config_provider("scenario", "planning_horizons", 0), + input: + network=RESULTS + + "prenetworks/base_s_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc", + heating_technologies_nuts3=resources("heating_technologies_nuts3.geojson"), + nuts3=resources("nuts3_shapes.geojson"), + regions_onshore=resources( + "regions_onshore_base_s_{clusters}.geojson" + ), + fernwaermeatlas="data/fernwaermeatlas/fernwaermeatlas.xlsx", + cities="data/fernwaermeatlas/cities_geolocations.geojson", + cop_profiles=resources("cop_profiles_base_s_{clusters}_{planning_horizons}.nc"), + existing_heating_distribution=resources( + f"existing_heating_distribution_base_s_{{clusters}}_{baseyear_value}.csv" + ), + lau=storage( + "https://gisco-services.ec.europa.eu/distribution/v2/lau/download/ref-lau-2021-01m.geojson.zip", + keep_local=True, + ), + output: + network=RESULTS + + "prenetworks/base-extended_s_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc", + district_heating_subnodes=resources( + "district_heating_subnodes_base_s_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.geojson" + ), + cop_profiles_extended=resources( + "cop_profiles_base-extended_s_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc" + ), + existing_heating_distribution_extended=( + resources( + "existing_heating_distribution_base-extended_s_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.csv" + ) + if baseyear_value != "{planning_horizons}" + else [] + ), + resources: + mem_mb=1000, + script: + "scripts/add_district_heating_subnodes.py" + + ruleorder: modify_district_heat_share > build_district_heat_share @@ -204,13 +253,13 @@ rule modify_district_heat_share: district_heating=config_provider("sector", "district_heating"), input: heating_technologies_nuts3=resources("heating_technologies_nuts3.geojson"), - regions_onshore=resources("regions_onshore_elec_s{simpl}_{clusters}.geojson"), + regions_onshore=resources("regions_onshore_base_s_{clusters}.geojson"), district_heat_share=resources( - "district_heat_share_elec_s{simpl}_{clusters}_{planning_horizons}.csv" + "district_heat_share_base_s_{clusters}_{planning_horizons}.csv" ), output: district_heat_share=resources( - "district_heat_share_elec_s{simpl}_{clusters}_{planning_horizons}-modified.csv" + "district_heat_share_base_s_{clusters}_{planning_horizons}-modified.csv" ), resources: mem_mb=1000, @@ -243,30 +292,49 @@ rule modify_prenetwork: land_transport_electric_share=config_provider( "sector", "land_transport_electric_share" ), + onshore_nep_force=config_provider("onshore_nep_force"), + offshore_nep_force=config_provider("offshore_nep_force"), + shipping_methanol_efficiency=config_provider( + "sector", "shipping_methanol_efficiency" + ), + shipping_oil_efficiency=config_provider("sector", "shipping_oil_efficiency"), + shipping_methanol_share=config_provider("sector", "shipping_methanol_share"), + mwh_meoh_per_tco2=config_provider("sector", "MWh_MeOH_per_tCO2"), input: + costs_modifications="ariadne-data/costs_{planning_horizons}-modifications.csv", network=RESULTS - + "prenetworks-brownfield/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc", + + "prenetworks-brownfield/base_s_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc", wkn=( - resources("wasserstoff_kernnetz_elec_s{simpl}_{clusters}.csv") + resources("wasserstoff_kernnetz_base_s_{clusters}.csv") if config_provider("wasserstoff_kernnetz", "enable") else [] ), costs=resources("costs_{planning_horizons}.csv"), aladin_demand=resources( - "mobility_demand_aladin_{simpl}_{clusters}_{planning_horizons}.csv" + "mobility_demand_aladin_{clusters}_{planning_horizons}.csv" ), - transport_data=resources("transport_data_s{simpl}_{clusters}.csv"), + transport_data=resources("transport_data_s_{clusters}.csv"), biomass_potentials=resources( - "biomass_potentials_s{simpl}_{clusters}_{planning_horizons}.csv" + "biomass_potentials_s_{clusters}_{planning_horizons}.csv" ), industrial_demand=resources( - "industrial_energy_demand_elec_s{simpl}_{clusters}_{planning_horizons}.csv" + "industrial_energy_demand_base_s_{clusters}_{planning_horizons}.csv" + ), + pop_weighted_energy_totals=resources( + "pop_weighted_energy_totals_s_{clusters}.csv" ), + shipping_demand=resources("shipping_demand_s_{clusters}.csv"), + regions_onshore=resources("regions_onshore_base_s_{clusters}.geojson"), + regions_offshore=resources("regions_offshore_base_s_{clusters}.geojson"), + offshore_connection_points="ariadne-data/offshore_connection_points.csv", output: network=RESULTS - + "prenetworks-final/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc", + + "prenetworks-final/base_s_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc", resources: mem_mb=1000, + log: + RESULTS + + "logs/modify_prenetwork_base_s_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.log", script: "scripts/modify_prenetwork.py" @@ -291,7 +359,7 @@ use rule solve_sector_network_myopic from pypsaeur with: if k != "network" }, network=RESULTS - + "prenetworks-final/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc", + + "prenetworks-final/base_s_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc", co2_totals_name=resources("co2_totals.csv"), @@ -328,6 +396,10 @@ rule retrieve_mastr: rule build_existing_chp_de: + params: + add_district_heating_subnodes=config_provider( + "sector", "district_heating", "add_subnodes" + ), input: mastr_biomass="data/mastr/bnetza_open_mastr_2023-08-08_B_biomass.csv", mastr_combustion="data/mastr/bnetza_open_mastr_2023-08-08_B_combustion.csv", @@ -335,17 +407,83 @@ rule build_existing_chp_de: "https://raw.githubusercontent.com/WZBSocialScienceCenter/plz_geocoord/master/plz_geocoord.csv", keep_local=True, ), - busmap=resources("networks/base.nc"), + regions=resources("regions_onshore_base_s_{clusters}.geojson"), + district_heating_subnodes=( + resources( + "district_heating_subnodes_base_s_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.geojson" + ) + if config["sector"]["district_heating"].get("add_subnodes", True) + else [] + ), output: - german_chp=resources("german_chp.csv"), + german_chp=resources( + "german_chp_base_s_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.csv" + ), script: "scripts/build_existing_chp_de.py" use rule add_existing_baseyear from pypsaeur with: + params: + **{k: v for k, v in rules.add_existing_baseyear.params.items()}, + add_district_heating_subnodes=config_provider( + "sector", "district_heating", "add_subnodes" + ), + input: + **{ + k: v + for k, v in rules.add_existing_baseyear.input.items() + if k != "network" + and k != "cop_profiles" + and k != "existing_heating_distribution" + }, + network=( + RESULTS + + "prenetworks/base-extended_s_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc" + if config["sector"]["district_heating"].get("add_subnodes", True) + else RESULTS + + "prenetworks/base_s_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc" + ), + cop_profiles=( + resources( + "cop_profiles_base-extended_s_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc" + ) + if config["sector"]["district_heating"].get("add_subnodes", True) + else resources("cop_profiles_base_s_{clusters}_{planning_horizons}.nc") + ), + custom_powerplants=resources( + "german_chp_base_s_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.csv" + ), + existing_heating_distribution=( + resources( + "existing_heating_distribution_base-extended_s_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.csv" + ) + if config["sector"]["district_heating"].get("add_subnodes", True) + else resources( + "existing_heating_distribution_base_s_{clusters}_{planning_horizons}.csv" + ) + ), + + +def input_profile_tech_brownfield(w): + return { + f"profile_{tech}": resources("profile_{clusters}_" + tech + ".nc") + for tech in config_provider("electricity", "renewable_carriers")(w) + if tech != "hydro" + } + + +use rule add_brownfield from pypsaeur with: input: - **rules.add_existing_baseyear.input, - custom_powerplants=resources("german_chp.csv"), + unpack(input_profile_tech_brownfield), + **{k: v for k, v in rules.add_brownfield.input.items() if k != "network"}, + network=( + RESULTS + + "prenetworks/base-extended_s_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc" + if config["sector"]["district_heating"].get("add_subnodes", True) + else RESULTS + + "prenetworks/base_s_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc" + ), use rule build_existing_heating_distribution from pypsaeur with: @@ -438,11 +576,11 @@ rule cluster_wasserstoff_kernnetz: kernnetz=config_provider("wasserstoff_kernnetz"), input: cleaned_h2_network=resources("wasserstoff_kernnetz.csv"), - regions_onshore=resources("regions_onshore_elec_s{simpl}_{clusters}.geojson"), - regions_offshore=resources("regions_offshore_elec_s{simpl}_{clusters}.geojson"), + regions_onshore=resources("regions_onshore_base_s_{clusters}.geojson"), + regions_offshore=resources("regions_offshore_base_s_{clusters}.geojson"), output: clustered_h2_network=resources( - "wasserstoff_kernnetz_elec_s{simpl}_{clusters}.csv" + "wasserstoff_kernnetz_base_s_{clusters}.csv" ), script: "scripts/cluster_wasserstoff_kernnetz.py" @@ -476,14 +614,14 @@ rule export_ariadne_variables: template=resources("template_ariadne_database.xlsx"), industry_demands=expand( resources( - "industrial_energy_demand_elec_s{simpl}_{clusters}_{planning_horizons}.csv" + "industrial_energy_demand_base_s_{clusters}_{planning_horizons}.csv" ), **config["scenario"], allow_missing=True, ), networks=expand( RESULTS - + "postnetworks/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc", + + "postnetworks/base_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc", **config["scenario"], allow_missing=True, ), @@ -587,7 +725,7 @@ rule build_scenarios: rule check_sector_ratios: input: network=RESULTS - + "postnetworks/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc", + + "postnetworks/base_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}.nc", log: "logs/check_sector_ratios.log", script: diff --git a/workflow/scripts/add_district_heating_subnodes.py b/workflow/scripts/add_district_heating_subnodes.py new file mode 100644 index 00000000..303ad2ee --- /dev/null +++ b/workflow/scripts/add_district_heating_subnodes.py @@ -0,0 +1,452 @@ +# -*- coding: utf-8 -*- + + +import logging + +logger = logging.getLogger(__name__) +from random import randint +from time import sleep + +import geopandas as gpd +import numpy as np +import pandas as pd +import pypsa +import xarray as xr +from typing import Union + + +def prepare_subnodes( + subnodes: pd.DataFrame, + cities: gpd.GeoDataFrame, + regions_onshore: gpd.GeoDataFrame, + lau: gpd.GeoDataFrame, + head: Union[int, bool] = 40, +) -> gpd.GeoDataFrame: + """ + Prepare subnodes by filtering district heating systems data for largest systems and assigning the corresponding LAU and onshore region shapes. + + Parameters + ---------- + subnodes : pd.DataFrame + DataFrame containing information about district heating systems. + cities : gpd.GeoDataFrame + GeoDataFrame containing city coordinates with columns 'Stadt' and 'geometry'. + regions_onshore : gpd.GeoDataFrame + GeoDataFrame containing onshore region geometries of clustered network. + lau : gpd.GeoDataFrame + GeoDataFrame containing LAU (Local Administrative Units) geometries and IDs. + head : Union[int, bool], optional + Number of largest district heating networks to keep. Defaults to 40. If set to True, it will be set to 40. + + Returns + ------- + gpd.GeoDataFrame + GeoDataFrame with processed subnodes, including geometries, clusters, LAU IDs, and NUTS3 shapes. + """ + # If head is boolean set it to 40 for default behavior + if isinstance(head, bool): + head = 40 + + subnodes["Stadt"] = subnodes["Stadt"].str.split("_").str[0] + + # Drop duplicates if Gelsenkirchen, Kiel, or Flensburg is included and keep the one with higher Wärmeeinspeisung in GWh/a + subnodes = subnodes.drop_duplicates(subset="Stadt", keep="first") + + # Keep only n largest district heating networks according to head parameter + subnodes = subnodes.sort_values( + by="Wärmeeinspeisung in GWh/a", ascending=False + ).head(head) + + subnodes["yearly_heat_demand_MWh"] = subnodes["Wärmeeinspeisung in GWh/a"] * 1e3 + + logger.info( + f"The selected district heating networks have an overall yearly heat demand of {subnodes['yearly_heat_demand_MWh'].sum()} MWh/a. " + ) + + subnodes["geometry"] = subnodes["Stadt"].apply( + lambda s: cities.loc[cities["Stadt"] == s, "geometry"].values[0] + ) + + subnodes = subnodes.dropna(subset=["geometry"]) + + # Convert the DataFrame to a GeoDataFrame + subnodes = gpd.GeoDataFrame(subnodes, crs="EPSG:4326") + + # Assign cluster to subnodes according to onshore regions + subnodes["cluster"] = subnodes.apply( + lambda x: regions_onshore.geometry.contains(x.geometry).idxmax(), axis=1 + ) + subnodes["lau"] = subnodes.apply( + lambda x: lau.loc[lau.geometry.contains(x.geometry).idxmax(), "LAU_ID"], axis=1 + ) + subnodes["lau_shape"] = subnodes.apply( + lambda x: lau.loc[lau.geometry.contains(x.geometry).idxmax(), "geometry"].wkt, + axis=1, + ) + + return subnodes + + +def add_subnodes(n: pypsa.Network, subnodes: gpd.GeoDataFrame) -> None: + """ + Add largest district heating systems subnodes to the network. + + They are initialized with: + - the total annual heat demand taken from the mother node, that is assigned to urban central heat and low-temperature heat for industry, + - the heat demand profiles taken from the mother node, + - the district heating investment options (stores, links) from the mother node, + - and heat vents as generator components. + The district heating loads in the mother nodes are reduced accordingly. + + Parameters + ---------- + n : pypsa.Network + The PyPSA network object to which subnodes will be added. + subnodes : gpd.GeoDataFrame + GeoDataFrame containing information about district heating subnodes. + + Returns + ------- + None + """ + + # Add subnodes to network + for _, subnode in subnodes.iterrows(): + name = f'{subnode["cluster"]} {subnode["Stadt"]} urban central heat' + + # Add buses + n.madd( + "Bus", + [name], + y=subnode.geometry.y, + x=subnode.geometry.x, + country="DE", + location=f"{subnode['cluster']} {subnode['Stadt']}", + carrier="urban central heat", + unit="MWh_th", + ) + + # Get heat loads for urban central heat and low-temperature heat for industry + uch_load_cluster = ( + n.snapshot_weightings.generators + @ n.loads_t.p_set[f"{subnode['cluster']} urban central heat"] + ) + lti_load_cluster = ( + n.loads.loc[ + f"{subnode['cluster']} low-temperature heat for industry", "p_set" + ] + * 8760 + ) + + # Calculate share of low-temperature heat for industry in total district heating load of cluster + dh_load_cluster = uch_load_cluster + lti_load_cluster + lti_share = lti_load_cluster / dh_load_cluster + + # Calculate demand ratio between load of subnode according to Fernwärmeatlas and remaining load of assigned cluster + demand_ratio = min( + 1, + (subnode["yearly_heat_demand_MWh"] / dh_load_cluster), + ) + + lost_load = subnode["yearly_heat_demand_MWh"] - dh_load_cluster + + # District heating demand exceeding the original cluster load is disregarded + if demand_ratio == 1: + logger.info( + f"District heating load of {subnode['Stadt']} exceeds load of its assigned cluster {subnode['cluster']}. {lost_load} MWh/a are disregarded." + ) + + # Add load components to subnode preserving the share of low-temperature heat for industry of the cluster + uch_load = ( + demand_ratio + * (1 - lti_share) + * n.loads_t.p_set.filter( + regex=f"{subnode['cluster']} urban central heat" + ).rename( + { + f"{subnode['cluster']} urban central heat": f"{subnode['cluster']} {subnode['Stadt']} urban central heat" + }, + axis=1, + ) + ) + n.madd( + "Load", + [name], + bus=name, + p_set=uch_load, + carrier="urban central heat", + location=f"{subnode['cluster']} {subnode['Stadt']}", + ) + + lti_load = ( + demand_ratio + * lti_share + * n.loads.filter( + regex=f"{subnode['cluster']} low-temperature heat for industry", axis=0 + ).p_set.rename( + { + f"{subnode['cluster']} low-temperature heat for industry": f"{subnode['cluster']} {subnode['Stadt']} low-temperature heat for industry" + }, + axis=0, + ) + ) + n.madd( + "Load", + [ + f"{subnode['cluster']} {subnode['Stadt']} low-temperature heat for industry" + ], + bus=name, + p_set=lti_load, + carrier="low-temperature heat for industry", + location=f"{subnode['cluster']} {subnode['Stadt']}", + ) + + # Adjust loads of cluster buses + n.loads_t.p_set.loc[ + :, f'{subnode["cluster"]} urban central heat' + ] *= 1 - demand_ratio * (1 - lti_share) + n.loads.loc[ + f'{subnode["cluster"]} low-temperature heat for industry', "p_set" + ] *= (1 - demand_ratio * lti_share) + + # Replicate district heating stores and links of mother node for subnodes + n.madd( + "Bus", + [f"{subnode['cluster']} {subnode['Stadt']} urban central water tanks"], + location=f"{subnode['cluster']} {subnode['Stadt']}", + carrier="urban central water tanks", + unit="MWh_th", + ) + + stores = ( + n.stores.filter(like=f"{subnode['cluster']} urban central", axis=0) + .reset_index() + .replace( + { + f"{subnode['cluster']} urban central": f"{subnode['cluster']} {subnode['Stadt']} urban central" + }, + regex=True, + ) + .set_index("Store") + ) + n.madd("Store", stores.index, **stores) + + links = ( + n.links.loc[~n.links.carrier.str.contains("heat pump")] + .filter(like=f"{subnode['cluster']} urban central", axis=0) + .reset_index() + .replace( + { + f"{subnode['cluster']} urban central": f"{subnode['cluster']} {subnode['Stadt']} urban central" + }, + regex=True, + ) + .set_index("Link") + ) + n.madd("Link", links.index, **links) + + # Add heat pumps to subnode + heat_pumps = ( + n.links.filter( + regex=f"{subnode['cluster']} urban central.*heat pump", axis=0 + ) + .reset_index() + .replace( + { + f"{subnode['cluster']} urban central": f"{subnode['cluster']} {subnode['Stadt']} urban central" + }, + regex=True, + ) + .set_index("Link") + ).drop("efficiency", axis=1) + heat_pumps_t = n.links_t.efficiency.filter( + regex=f"{subnode['cluster']} urban central.*heat pump" + ) + heat_pumps_t.columns = heat_pumps_t.columns.str.replace( + f"{subnode['cluster']} urban central", + f"{subnode['cluster']} {subnode['Stadt']} urban central", + ) + n.madd("Link", heat_pumps.index, efficiency=heat_pumps_t, **heat_pumps) + + # Add heat vent to subnode + n.madd( + "Generator", + [f"{name} heat vent"], + bus=name, + location=f"{subnode['cluster']} {subnode['Stadt']}", + carrier="urban central heat vent", + p_nom_extendable=True, + p_min_pu=-1, + p_max_pu=0, + unit="MWh_th", + ) + + return + + +def extend_cops(cops: xr.DataArray, subnodes: gpd.GeoDataFrame) -> xr.DataArray: + """ + Extend COPs (Coefficient of Performance) by subnodes mirroring the timeseries of the corresponding + mother node. + + Parameters + ---------- + cops : xr.DataArray + DataArray containing COP timeseries data. + subnodes : gpd.GeoDataFrame + GeoDataFrame containing information about district heating subnodes. + + Returns + ------- + xr.DataArray + Extended DataArray with COP timeseries for subnodes. + """ + cops_extended = cops.copy() + + # Iterate over the DataFrame rows + for _, subnode in subnodes.iterrows(): + cluster_name = subnode["cluster"] + city_name = subnode["Stadt"] + + # Select the xarray entry where name matches the cluster + selected_entry = cops.sel(name=cluster_name) + + # Rename the selected entry + renamed_entry = selected_entry.assign_coords(name=f"{cluster_name} {city_name}") + + # Combine the renamed entry with the extended dataset + cops_extended = xr.concat([cops_extended, renamed_entry], dim="name") + + # Change dtype of the name dimension to string + cops_extended.coords["name"] = cops_extended.coords["name"].astype(str) + + return cops_extended + + +def extend_heating_distribution( + existing_heating_distribution: pd.DataFrame, subnodes: gpd.GeoDataFrame +) -> pd.DataFrame: + """ + Extend heating distribution by subnodes mirroring the distribution of the + corresponding mother node. + + Parameters + ---------- + existing_heating_distribution : pd.DataFrame + DataFrame containing the existing heating distribution. + subnodes : gpd.GeoDataFrame + GeoDataFrame containing information about district heating subnodes. + + Returns + ------- + pd.DataFrame + Extended DataFrame with heating distribution for subnodes. + """ + # Merge the existing heating distribution with subnodes on the cluster name + mother_nodes = ( + existing_heating_distribution.loc[subnodes.cluster.unique()] + .unstack(-1) + .to_frame() + ) + cities_within_cluster = subnodes.groupby("cluster")["Stadt"].apply(list) + mother_nodes["cities"] = mother_nodes.apply( + lambda i: cities_within_cluster[i.name[2]], axis=1 + ) + # Explode the list of cities + mother_nodes = mother_nodes.explode("cities") + + # Reset index to temporarily flatten it + mother_nodes_reset = mother_nodes.reset_index() + + # Append city name to the third level of the index + mother_nodes_reset["name"] = ( + mother_nodes_reset["name"] + " " + mother_nodes_reset["cities"] + ) + + # Set the index back + mother_nodes = mother_nodes_reset.set_index(["heat name", "technology", "name"]) + + # Drop the temporary 'cities' column + mother_nodes.drop("cities", axis=1, inplace=True) + + # Reformat to match the existing heating distribution + mother_nodes = mother_nodes.squeeze().unstack(-1).T + + # Combine the exploded data with the existing heating distribution + existing_heating_distribution_extended = pd.concat( + [existing_heating_distribution, mother_nodes] + ) + return existing_heating_distribution_extended + + +if __name__ == "__main__": + if "snakemake" not in globals(): + import os + import sys + + os.chdir(os.path.dirname(os.path.abspath(__file__))) + + path = "../submodules/pypsa-eur/scripts" + sys.path.insert(0, os.path.abspath(path)) + from _helpers import mock_snakemake + + snakemake = mock_snakemake( + "add_district_heating_subnodes", + simpl="", + clusters=27, + opts="", + ll="vopt", + sector_opts="none", + planning_horizons="2020", + run="KN2045_Bal_v4", + ) + + logger.info("Adding SysGF-specific functionality") + + n = pypsa.Network(snakemake.input.network) + + lau = gpd.read_file( + f"{snakemake.input.lau}!LAU_RG_01M_2021_3035.geojson", + crs="EPSG:3035", + ).to_crs("EPSG:4326") + + fernwaermeatlas = pd.read_excel( + snakemake.input.fernwaermeatlas, + sheet_name="Fernwärmeatlas_öffentlich", + ) + cities = gpd.read_file(snakemake.input.cities) + regions_onshore = gpd.read_file(snakemake.input.regions_onshore).set_index("name") + + subnodes = prepare_subnodes( + fernwaermeatlas, + cities, + regions_onshore, + lau, + head=snakemake.params.district_heating["add_subnodes"], + ) + subnodes.to_file(snakemake.output.district_heating_subnodes, driver="GeoJSON") + + add_subnodes(n, subnodes) + + if snakemake.config["foresight"] == "myopic": + cops = xr.open_dataarray(snakemake.input.cop_profiles) + cops_extended = extend_cops(cops, subnodes) + cops_extended.to_netcdf(snakemake.output.cop_profiles_extended) + + if snakemake.wildcards.planning_horizons == str(snakemake.params["baseyear"]): + existing_heating_distribution = pd.read_csv( + snakemake.input.existing_heating_distribution, + header=[0, 1], + index_col=0, + ) + existing_heating_distribution_extended = extend_heating_distribution( + existing_heating_distribution, subnodes + ) + existing_heating_distribution_extended.to_csv( + snakemake.output.existing_heating_distribution_extended + ) + else: + # write empty file to output + with open(snakemake.output.existing_heating_distribution_extended, "w") as f: + pass + n.export_to_netcdf(snakemake.output.network) diff --git a/workflow/scripts/build_existing_chp_de.py b/workflow/scripts/build_existing_chp_de.py index 5c0274ad..c14c7e55 100644 --- a/workflow/scripts/build_existing_chp_de.py +++ b/workflow/scripts/build_existing_chp_de.py @@ -15,6 +15,7 @@ import os import sys +import geopandas as gpd import pandas as pd import pypsa from powerplantmatching.export import map_country_bus @@ -203,13 +204,64 @@ def BP(cap, year): return CHP_de +def assign_subnode(CHP_de: pd.DataFrame, subnodes: gpd.GeoDataFrame) -> pd.DataFrame: + """ + Assign subnodes to the CHP plants based on their location. + + Parameters + ---------- + CHP_de : pd.DataFrame + DataFrame containing CHP plant data with latitude and longitude. + subnodes : gpd.GeoDataFrame + GeoDataFrame containing subnode data with geometries. + + Returns + ------- + pd.DataFrame + DataFrame with assigned subnodes. + """ + + # Make a geodataframe from CHP_de using the lat and lon columns + CHP_de = gpd.GeoDataFrame( + CHP_de, geometry=gpd.points_from_xy(CHP_de.lon, CHP_de.lat) + ) + CHP_de.crs = subnodes.crs + # Set nuts_3 shape wkt column as geometry + subnodes["geometry"] = gpd.GeoSeries.from_wkt(subnodes["lau_shape"]) + subnodes.drop("lau_shape", axis=1, inplace=True) + subnodes.index.rename("city", inplace=True) + + # Assign subnode to CHP plants based on the nuts3 region + CHP_de = CHP_de.sjoin(subnodes, how="left", predicate="within") + # Insert leading whitespace for citynames where not nan + CHP_de["city"] = CHP_de["city"].apply(lambda x: " " + x if pd.notna(x) else "") + CHP_de["bus"] = CHP_de["bus"] + CHP_de["city"] + CHP_de.drop("city", axis=1, inplace=True) + + return CHP_de + + if __name__ == "__main__": if "snakemake" not in globals(): + import os + + # Change directory to current script + os.chdir(os.path.dirname(os.path.abspath(__file__))) + path = "../submodules/pypsa-eur/scripts" sys.path.insert(0, os.path.abspath(path)) from _helpers import mock_snakemake - snakemake = mock_snakemake("build_existing_chp_de") + snakemake = mock_snakemake( + "build_existing_chp_de", + simpl="", + clusters=27, + opts="", + ll="vopt", + sector_opts="none", + planning_horizons="2020", + run="KN2045_Bal_v4", + ) logging.basicConfig(level=snakemake.config["logging"]["level"]) @@ -229,8 +281,17 @@ def BP(cap, year): CHP_de = clean_data(combustion, biomass, geodata) CHP_de = calculate_efficiency(CHP_de) - bn = pypsa.Network(snakemake.input.busmap) - substations = bn.buses.query("substation_lv") - CHP_de = map_country_bus(CHP_de, substations) + logger.info("Mapping CHP plants to regions") + regions = gpd.read_file(snakemake.input.regions).set_index("name") + geometry = gpd.points_from_xy(CHP_de["lon"], CHP_de["lat"]) + gdf = gpd.GeoDataFrame(geometry=geometry, crs=4326) + CHP_de["bus"] = gpd.sjoin_nearest(gdf, regions, how="left")["name"] + + if snakemake.params.add_district_heating_subnodes: + subnodes = gpd.read_file( + snakemake.input.district_heating_subnodes, + columns=["Stadt", "lau_shape"], + ).set_index("Stadt") + CHP_de = assign_subnode(CHP_de, subnodes) CHP_de.to_csv(snakemake.output.german_chp, index=False)