diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8b9bf2e00..7f7453028 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -33,7 +33,7 @@ repos: rev: v2.3.0 hooks: - id: codespell - args: ['--ignore-regex="(\b[A-Z]+\b)"', '--ignore-words-list=fom,appartment,bage,ore,setis,tabacco,berfore,vor'] # Ignore capital case words, e.g. country codes + args: ['--ignore-regex="(\b[A-Z]+\b)"', '--ignore-words-list=fom,appartment,bage,ore,setis,tabacco,berfore,vor,pris'] # Ignore capital case words, e.g. country codes types_or: [python, rst, markdown] files: ^(scripts|doc)/ diff --git a/config/config.default.yaml b/config/config.default.yaml index 0fd6e9869..2026c11fa 100644 --- a/config/config.default.yaml +++ b/config/config.default.yaml @@ -42,7 +42,7 @@ scenario: ll: - vopt clusters: - - 38 + - 39 - 128 - 256 opts: @@ -56,7 +56,7 @@ scenario: - 2050 # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#countries -countries: ['AL', 'AT', 'BA', 'BE', 'BG', 'CH', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HU', 'IE', 'IT', 'LT', 'LU', 'LV', 'ME', 'MK', 'NL', 'NO', 'PL', 'PT', 'RO', 'RS', 'SE', 'SI', 'SK'] +countries: ['AL', 'AT', 'BA', 'BE', 'BG', 'CH', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HU', 'IE', 'IT', 'LT', 'LU', 'LV', 'ME', 'MK', 'NL', 'NO', 'PL', 'PT', 'RO', 'RS', 'SE', 'SI', 'SK', 'XK'] # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#snapshots snapshots: @@ -87,7 +87,7 @@ co2_budget: # docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#electricity electricity: voltages: [200., 220., 300., 380., 500., 750.] - base_network: entsoegridkit + base_network: osm-prebuilt gaslimit_enable: false gaslimit: false co2limit_enable: false @@ -449,19 +449,26 @@ sector: 2045: 0.8 2050: 1.0 district_heating_loss: 0.15 - # check these numbers! - forward_temperature: - default: 90 - DK: 70 - SE: 70 - NO: 70 - FI: 70 - return_temperature: - default: 50 - DK: 40 - SE: 40 - NO: 40 - FI: 40 + supply_temperature_approximation: + max_forward_temperature: + default: 90 + DK: 70 + SE: 70 + NO: 70 + min_forward_temperature: + default: 68 + DK: 54 + SE: 54 + NO: 54 + return_temperature: + default: 50 + DK: 40 + SE: 40 + NO: 40 + FI: 40 + lower_threshold_ambient_temperature: 0 + upper_threshold_ambient_temperature: 10 + rolling_window_ambient_temperature: 72 heat_source_cooling: 6 #K heat_pump_cop_approximation: refrigerant: ammonia diff --git a/config/examples/config.entsoe-all.yaml b/config/examples/config.entsoe-all.yaml index 4e7edd036..9f52094d6 100644 --- a/config/examples/config.entsoe-all.yaml +++ b/config/examples/config.entsoe-all.yaml @@ -15,7 +15,7 @@ scenario: ll: - vopt clusters: - - 39 + - 41 - 128 - 256 opts: @@ -26,7 +26,7 @@ scenario: - '' # TODO add Turkey (TR) -countries: ['AL', 'AT', 'BA', 'BE', 'BG', 'CH', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HU', 'IE', 'IT', 'LT', 'LU', 'LV', 'ME', 'MD', 'MK', 'NL', 'NO', 'PL', 'PT', 'RO', 'RS', 'SE', 'SI', 'SK', 'UA'] +countries: ['AL', 'AT', 'BA', 'BE', 'BG', 'CH', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HU', 'IE', 'IT', 'LT', 'LU', 'LV', 'ME', 'MD', 'MK', 'NL', 'NO', 'PL', 'PT', 'RO', 'RS', 'SE', 'SI', 'SK', 'UA', 'XK'] electricity: custom_powerplants: true diff --git a/data/ammonia_plants.csv b/data/ammonia_plants.csv new file mode 100644 index 000000000..1317ceb95 --- /dev/null +++ b/data/ammonia_plants.csv @@ -0,0 +1,37 @@ +"Plant","Ammonia [kt/a]","Urea [kt/a]","AN [kt/a]","UAN [kt/a]","CAN [kt/a]","Nitrogen fertilisers [kt/a]","MAP [kt/a]","Country","Latitude","Longitude","Date","Source","Comment" +"Yara Sluiskil","1700","1300",,,,,,"Netherlands","51.27186","3.84896","13-01-2023","https://www.icis.com/explore/resources/news/2023/01/18/10846094/insight-poor-demand-high-costs-stifle-europe-industry-despite-falling-gas-prices/", +"OCI Geleen","550",,,,,,,"Netherlands","50.99738","5.78497","13-01-2023","https://www.icis.com/explore/resources/news/2023/01/18/10846094/insight-poor-demand-high-costs-stifle-europe-industry-despite-falling-gas-prices/", +"CF Fertilisers Billingham","590",,,,,,,"United Kingdom","53.28149","-2.79744","13-01-2023","https://www.icis.com/explore/resources/news/2023/01/18/10846094/insight-poor-demand-high-costs-stifle-europe-industry-despite-falling-gas-prices/; https://www.cfindustries.com/what-we-do/ammonia-productiont", +"Yara Tertre","420",,,,,,,"Belgium","50.47842","3.80285","13-01-2023","https://www.icis.com/explore/resources/news/2023/01/18/10846094/insight-poor-demand-high-costs-stifle-europe-industry-despite-falling-gas-prices/", +"BASF Antwerp","650",,,,,,,"Belgium","51.35583","4.27811","13-01-2023","https://www.icis.com/explore/resources/news/2023/01/18/10846094/insight-poor-demand-high-costs-stifle-europe-industry-despite-falling-gas-prices/", +"Yara Gonfreville","400",,,,,,,"France","49.47982","0.19587","13-01-2023","https://www.icis.com/explore/resources/news/2023/01/18/10846094/insight-poor-demand-high-costs-stifle-europe-industry-despite-falling-gas-prices/", +"Borealis Rouen",,,,,,,,"France","49.41916","1.02584","13-01-2023","https://www.icis.com/explore/resources/news/2023/01/18/10846094/insight-poor-demand-high-costs-stifle-europe-industry-despite-falling-gas-prices/", +"BASF Ludwigshafen","880",,,,,,,"Germany","49.51171","8.42009","13-01-2023","https://www.icis.com/explore/resources/news/2023/01/18/10846094/insight-poor-demand-high-costs-stifle-europe-industry-despite-falling-gas-prices/", +"SKW","1300",,,,,,,"Germany","51.87746","12.5858","13-01-2023","https://www.icis.com/explore/resources/news/2023/01/18/10846094/insight-poor-demand-high-costs-stifle-europe-industry-despite-falling-gas-prices/", +"Yara Brunsbuettel","750",,,,,,,"Germany","53.91152","9.20982","13-01-2023","https://www.icis.com/explore/resources/news/2023/01/18/10846094/insight-poor-demand-high-costs-stifle-europe-industry-despite-falling-gas-prices/", +"Yara Porsgrunn","500",,,,,,,"Norway","59.12444","9.61922","13-01-2023","https://www.icis.com/explore/resources/news/2023/01/18/10846094/insight-poor-demand-high-costs-stifle-europe-industry-despite-falling-gas-prices/", +"Fertiberia Puertollano","400","250",,"200",,,,"Spain","38.67276","-4.0608","13-01-2023","https://www.icis.com/explore/resources/news/2023/01/18/10846094/insight-poor-demand-high-costs-stifle-europe-industry-despite-falling-gas-prices/; https://www.icis.com/explore/resources/news/2021/10/25/10698264/spain-s-fertiberia-urea-ammonia-plants-in-palos-to-remain-shut-on-gas-prices/", +"Fertiberia Huelva","200","130",,,,,,"Spain","37.22572","-6.94742","13-01-2023","https://www.icis.com/explore/resources/news/2023/01/18/10846094/insight-poor-demand-high-costs-stifle-europe-industry-despite-falling-gas-prices/; https://www.icis.com/explore/resources/news/2021/10/25/10698264/spain-s-fertiberia-urea-ammonia-plants-in-palos-to-remain-shut-on-gas-prices/", +"Yara Ferrara","600","600",,,,,,"Italy","44.86206","11.57922","13-01-2023","https://www.icis.com/explore/resources/news/2023/01/18/10846094/insight-poor-demand-high-costs-stifle-europe-industry-despite-falling-gas-prices/", +"Petrokemija",,"500",,,,,,"Croatia","45.46977","16.78944","13-01-2023","https://www.icis.com/explore/resources/news/2023/01/18/10846094/insight-poor-demand-high-costs-stifle-europe-industry-despite-falling-gas-prices/", +"Neochim","450",,"710",,,,,"Bulgaria","42.20514","25.61547","13-01-2023","https://www.icis.com/explore/resources/news/2023/01/18/10846094/insight-poor-demand-high-costs-stifle-europe-industry-despite-falling-gas-prices/", +"Agopolychim",,,"400","800",,,"300","Bulgaria","43.20652","27.65983","13-01-2023","https://www.icis.com/explore/resources/news/2023/01/18/10846094/insight-poor-demand-high-costs-stifle-europe-industry-despite-falling-gas-prices/; https://ammoniaenergy.org/articles/agropolychim-investing-in-ammonia-distribution-from-bulgaria/","no own ammonia plant" +"Azomures","1600",,,,,,,"Romania","46.76187","24.42361","13-01-2023","https://www.icis.com/explore/resources/news/2023/01/18/10846094/insight-poor-demand-high-costs-stifle-europe-industry-despite-falling-gas-prices/","Total for ammonia, AN, NPK, UAN, Urea" +"Nitrogenmuvek","497",,,,"1300",,,"Hungary","47.16691","18.12844","13-01-2023","https://www.icis.com/explore/resources/news/2023/01/18/10846094/insight-poor-demand-high-costs-stifle-europe-industry-despite-falling-gas-prices/", +"DUSLO","530",,,,,,,"Slovakia","48.18705","17.9301","13-01-2023","https://www.icis.com/explore/resources/news/2023/01/18/10846094/insight-poor-demand-high-costs-stifle-europe-industry-despite-falling-gas-prices/", +"Grupa Azoty","524","375",,,,,,"Poland","51.45179","21.98174","13-01-2023","https://www.icis.com/explore/resources/news/2023/01/18/10846094/insight-poor-demand-high-costs-stifle-europe-industry-despite-falling-gas-prices/", +"Anwil",,,,,,"965",,"Poland","52.70438","18.96606","13-01-2023","https://www.icis.com/explore/resources/news/2023/01/18/10846094/insight-poor-demand-high-costs-stifle-europe-industry-despite-falling-gas-prices/", +"Achema","1100","785","651","1300","540",,,"Lithuania","55.081","24.32377","13-01-2023","https://www.icis.com/explore/resources/news/2023/01/18/10846094/insight-poor-demand-high-costs-stifle-europe-industry-despite-falling-gas-prices/", +"Lifosa",,,,,,"3241",,"Lithuania","55.27567","24.01652","13-01-2023","https://www.icis.com/explore/resources/news/2023/01/18/10846094/insight-poor-demand-high-costs-stifle-europe-industry-despite-falling-gas-prices/", +"Unknown (Linz)",,,,,,,,"Austria","48.28749","14.32369","15-08-2024",,"most likely location" +"Unknown (Kothla-Järve)",,,,,,,,"Estonia","59.39984","27.25401","15-08-2024",,"most likely location" +"Unknown (Oulu)",,,,,,,,"Finland","65.0033","25.43731","15-08-2024",,"most likely location" +"Unknown (Ptolemaida)",,,,,,,,"Greece","40.50701","21.71414","15-08-2024",,"most likely location" +"Unknown (Sabac)",,,,,,,,"Serbia","44.76298","19.68426","15-08-2024",,"most likely location" +"Unknown (Lugano)",,,,,,,,"Switzerland","46.02709","8.9641","15-08-2024",,"most likely location" +"Rivneazot","420",,"540","300","470 ",,,"Ukraine","50.70502","26.1630","28-08-2024","https://interfax.com/newsroom/top-stories/100959/; https://interfax.com/newsroom/top-stories/103386/; https://gmk.center/en/news/ostchem-plants-produced-520-6-thousand-tons-of-fertilizers-in-q1/; http://www.ostchem.com/en/o-kompanii/proizvodstvo/rovno", +"Stirol (Horlivka)","1470","940 ","693",,,,,"Ukraine","48.29697","38.10412","28-08-2024","http://ostchem.com/en/o-kompanii/proizvodstvo/stirol", +"Severodonetsk Azot","1020","390","550",,,,,"Ukraine","48.94185","38.47399","28-08-2024",, +"Odessa Port Plant ","1160","942",,,,,,"Ukraine","46.49460","30.73222","28-08-2024","https://www.spfu.gov.ua/userfiles/files/OPP_Teaser_Eng.pdf", +"Cherkazy Azot","963",,"970","500",,,,"Ukraine","49.38241","32.05719","28-08-2024","https://gmk.center/en/news/ostchem-plants-produced-520-6-thousand-tons-of-fertilizers-in-q1/https://gmk.center/en/news/ostchem-plants-produced-520-6-thousand-tons-of-fertilizers-in-q1/; http://ostchem.com/en/o-kompanii/proizvodstvo/azot", +"DneproAZOT","660",,,,,,,"Ukraine","48.49004","34.66667","28-08-2024","https://www.alfalaval.com/media/stories/fertilizers/increased-plant-capacity-and-reduced-maintenance-at-fertilizer-plant/", diff --git a/data/cement-plants-noneu.csv b/data/cement-plants-noneu.csv new file mode 100644 index 000000000..27c046c03 --- /dev/null +++ b/data/cement-plants-noneu.csv @@ -0,0 +1,26 @@ +"Company","Site","Cement [kt/a]","Country","Latitude","Longitude","Date","Source","Comment" +"Titan Cementarnica USJE AD","Skopje",910,"MK",41.96816,21.45604,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information", +"Colacem Albania Sh.p.k","Balldre plant, Lezhe",500,"AL",41.83689,19.63339,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information", +"Fushe Kruje Cement Factory, Sh.p.k.","Elbasan cement plant",300,"AL",41.12184,20.04347,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information", +"Fushe Kruje Cement Factory, Sh.p.k.","Fushe Kruje plant",1330,"AL",40.46403,19.49948,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information", +"Antea Cement Sh.A","Boka e Kuqe, Burizane",1500,"AL",41.54838,19.72574,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information", +"Beocinska Fabrika Cementa","Beocin",2000,"RS",45.20952,19.71043,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information", +"Holcim (Srbija) a.d.","Novi Popovac",1400,"RS",43.90633,21.50378,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information", +"Titan Cementara Kosjerić Ltd.","Kosjeric",750,"RS",44.0125,19.88831,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information", +"Sharrcem Sh.p.k.","Hani i Elezit,",850,"XK",42.14704,21.29863,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information", +"Fabrika Cementa Lukavac d.d.","Lukavac",800,"BA",44.52596,18.52811,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information", +"Tvornica Cementa Kakanj d.d. ","Kakanj",400,"BA",44.11922,18.10897,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information", +"Lafarge Ciment Moldova SA","Rezina",1400,"MD",47.79094,28.9553,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information", +"ZAO Rybnitsa Cement Comple","Rybnitsa",1100,"MD",47.78115,29.02078,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information", +"ChAO Nikolayevtsemen","Mykolayiv",900,"UA",46.94555,32.06641,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information; https://cemark.ua/en/zavodi/prat-mikolajivcement ", +"OAO KyivCement ","Kyiv",175,"UA",50.38525,30.55205,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information https://www.dyckerhoff.com.ua/en/dyckerhoff-cement-ukraine ", +"OAO YuGCement ","Mykolayiv",1250,"UA",46.98512,31.9317,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information, https://www.dyckerhoff.com.ua/en/yug-cemen", +"OOO Cement","Odessa",550,"UA",46.50818,30.67422,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information https://se.ua/en/projects/llc-cement-odessa-cement-plant/ ", +"Overin Ltd","Amvrosievka",800,"UA",47.83453,38.46961,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information","assumed equal split" +"Overin Ltd","Kamenskoye",800,"UA",48.51783,34.63382,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information","assumed equal split" +"Overin Ltd","Kriviy Rih",800,"UA",47.87454,33.43586,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information","assumed equal split" +"PAO Eurocement Ukraine","Balakleya, Kharkivs'ka",1000,"UA",49.49314,36.74926,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information https://www.globalcement.com/news/item/13733-update-on-ukraine-february-2022 ", +"PAO Ivano-Frankovsktsement","Ivano-Frankivsk ",3600,"UA",48.97709,24.71227,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information", +"PAO Kramatorkiy Tsementnyi Zavod PUSHKA ","Kramatorsk, Donets'ka",138,"UA",48.72802,37.54362,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information https://www.cemnet.com/News/story/144668/pushka-ups-cement-production-56-6p-per-cent-to138-400t-in-q1.html ", +"PAO Podolsk Cement","Khmel'nyts'ka Oblast' ",2050,"UA",48.7461,26.64664,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information https://cemark.ua/en/zavodi/at-podilskiy-cement ", +"PAO Volyn'Cement","Volyn'ska Oblast'",2000,"UA",50.54731,26.25774,"28-08-2024","https://www.usgs.gov/centers/national-minerals-information-center/international-minerals-statistics-and-information https://www.dyckerhoff.com.ua/volyn-cement ", diff --git a/data/district_heat_share.csv b/data/district_heat_share.csv index 07d4f51dd..ad47fb3b5 100644 --- a/data/district_heat_share.csv +++ b/data/district_heat_share.csv @@ -25,7 +25,7 @@ SE,50.4, GB,2, BY,70, EE,52,5406 -KO,3,207 +XK,3,207 RO,23,9962 SK,54,15000 NL,4,9800 diff --git a/data/era5-annual-HDD-per-country.csv b/data/era5-annual-HDD-per-country.csv index 5f6ad45db..4bb02806c 100644 --- a/data/era5-annual-HDD-per-country.csv +++ b/data/era5-annual-HDD-per-country.csv @@ -29,6 +29,7 @@ PL,1615,1584,1275,1356,1364,1378,1517,1290,1230,1296,1249,1438,1294,1478,1446,15 PT,114,114,102,113,86,110,95,79,94,96,107,103,99,109,80,131,106,93,93,97,78,106,104,115,103,98,103,97,112,110,115,116,106,109,109,109,99,139,107,102,89,90,95,104,99,108,85,89,75,87,104,96,104,85,66,88,62,82,96,90,90,78,87,97,103,90,91,91,84,96,80,101,100,81,79,87,79,96,81,72,78,98,75 RO,931,939,801,813,874,851,839,815,799,780,736,811,883,948,822,968,810,789,826,731,744,826,890,867,873,706,791,776,858,792,787,798,838,782,752,855,772,836,780,880,801,811,763,807,917,815,882,827,710,700,846,801,848,701,806,869,842,805,740,710,760,721,844,754,799,780,684,695,695,757,793,781,709,662,681,721,720,690,624,648,740,973,611 RS,292,305,246,274,285,276,265,263,261,239,222,252,273,300,249,313,248,249,254,231,226,270,283,271,274,233,253,243,267,254,251,246,272,242,243,265,233,265,241,273,251,255,244,253,275,256,268,254,231,220,275,243,263,223,250,270,259,250,237,217,234,217,262,238,265,247,217,214,222,229,251,244,214,192,222,223,232,211,197,212,234,306,192 +XK,292,305,246,274,285,276,265,263,261,239,222,252,273,300,249,313,248,249,254,231,226,270,283,271,274,233,253,243,267,254,251,246,272,242,243,265,233,265,241,273,251,255,244,253,275,256,268,254,231,220,275,243,263,223,250,270,259,250,237,217,234,217,262,238,265,247,217,214,222,229,251,244,214,192,222,223,232,211,197,212,234,306,192 SK,221,219,199,207,209,201,212,202,189,189,178,210,195,217,208,228,192,193,185,185,178,208,213,211,219,179,187,192,203,200,192,188,195,178,178,197,188,204,193,215,191,190,180,193,212,198,207,187,172,173,202,186,191,171,186,204,193,184,177,162,184,174,190,184,191,182,163,161,170,188,175,179,174,142,161,170,176,157,152,158,180,231,150 SI,81,77,66,74,72,71,72,69,61,65,61,72,67,73,69,78,65,66,62,61,59,75,75,73,73,62,65,67,71,69,67,68,71,62,61,69,62,71,68,74,67,65,65,69,73,71,71,63,59,58,70,61,63,55,61,71,63,62,62,54,59,55,65,63,67,61,54,57,57,65,58,59,59,46,55,57,58,53,52,53,61,77,51 SE,4509,4537,3713,3939,4134,4059,4374,3918,3633,4015,3891,4219,3560,3919,4426,4488,3950,4223,3662,3988,3814,4451,4260,4021,4358,4613,3929,4280,4255,4254,4043,3806,3975,3634,3625,4238,4132,4314,4246,4287,4301,3913,3840,3819,4588,4139,4376,3931,3476,3446,3785,3695,3893,3991,3916,4073,3757,3950,3781,3446,3898,3778,3755,3769,3632,3561,3606,3590,3806,4397,3474,3935,3675,3452,3421,3635,3693,3705,3689,3247,3807,5084,3769 diff --git a/data/era5-annual-runoff-per-country.csv b/data/era5-annual-runoff-per-country.csv index 862ff743a..f160f0434 100644 --- a/data/era5-annual-runoff-per-country.csv +++ b/data/era5-annual-runoff-per-country.csv @@ -29,6 +29,7 @@ PL,19040.141287526196,13600.003649707409,4232.940272903036,6263.550300160282,117 PT,17389.910347609384,7956.13238827081,8339.728858321132,2075.556471824434,3081.9400195534536,5428.965709165979,13519.093138173732,8266.183051247071,2305.611210847845,4751.414112923152,8372.129135066036,5969.31083523099,2397.6586063764958,3916.9962424030286,9654.699566273128,9603.363148222781,4163.162870337118,9179.465624121007,12131.854156488425,23406.646477036124,10635.077955993413,11029.404714084018,16475.917880350513,12087.55769685171,9804.492049893286,26178.184180984652,5669.820254013018,7559.528320921835,15405.930172416956,11163.387795923936,7088.025310785357,10191.330496756716,7128.069257230898,9335.631563569985,5236.13128329125,4572.251664471752,32605.577182903042,149323.86523586328,34294.81803755196,6061.11721308257,5836.654497517657,4843.7020037722705,7814.782411245904,9395.759000250582,14057.157961109715,8192.288977642056,7655.760207241933,9203.645337709624,7637.7450966202805,7971.812432879156,7421.37678917759,2935.2871895485287,6329.939163930326,8820.796560748415,8944.109217212255,14719.27363438586,11050.779819799696,8379.75073967695,5590.525909815882,10485.131539563748,20476.028147427794,7715.162648805314,11939.81827695821,5116.2596877306005,3047.0903835389504,9273.489011971804,5135.313604526994,3331.418010762715,6338.048693933388,12105.616460835923,7097.468638389797,3348.213951094401,11773.920694867731,13465.586888738457,4379.602415677836,11394.463621230614,3104.8789319036796,7216.150544702967,6653.765864361187,5720.580737490315,6685.493238874142,8486.079863634486,8329.192253601708 RO,46545.50823036229,35517.718755954746,12007.97895282227,19448.903832688356,20440.973051620913,15132.394486734484,21523.767710605425,32075.68337603852,20312.088233477134,14653.654680967496,19346.344374357534,19295.50358149612,27702.617554877004,24644.67023787785,52797.882559506776,42262.7162454671,34689.01143849651,42583.74114324521,28973.147767939397,36882.64868179707,29063.471368254675,39575.626278602576,31898.858414072816,36283.98261870408,42207.50410750205,41714.957670180156,43270.313090823794,40474.54716426176,53565.91349208634,74865.02611452111,48190.11154819992,57257.26135564704,46523.80416861758,44300.83791323246,54901.1701096736,47361.27151969814,49156.09266449866,56461.30009557872,56062.45724028393,58506.511677323855,48499.732173446595,45901.64074770707,33171.12855766741,38310.49272614435,39377.41586450999,28638.803870927226,29424.553978129992,39429.14347226504,39300.73151290347,27525.04968129815,47095.77682827757,30948.90232192865,32939.14262154816,30155.99148400138,35067.04977301419,35592.25066656211,45790.67404045973,42596.77718301701,39223.35618808953,25767.93476452563,30963.38609617001,30034.908201595936,23323.05819720473,28517.276888551194,41560.85308154171,43999.15897330077,21427.19650317167,25084.131072037453,18450.764512377853,38182.39266803458,20422.517191285966,14090.243196787245,21998.55223596247,30809.187151501046,22071.90474198888,31906.454754357772,20763.073093214778,25620.747974742615,19248.368008063957,23289.011422937554,26669.846131314298,27207.74766143675,29386.08741874402 RS,12898.821957973714,11760.375115749226,5762.823681912337,10868.645457667955,6851.647476795101,6489.091511565737,8016.427124526303,11096.059611708006,8784.092785314235,6844.044739629848,5968.673897869134,7450.873145908507,8457.218084424714,10466.134979346216,18700.932768467817,18783.264457747777,9647.157790116991,14650.396388121844,12360.14810912279,11640.673528541236,8882.446980807292,14762.619600006037,18399.930168952076,13646.806400326906,14489.119148714197,14104.804002759012,15462.34970923992,10894.228431882033,15095.088711819322,17853.281396922546,12916.46431873845,14207.379133135786,14418.834370720482,12752.5957180177,15544.588217112372,15820.904121022999,13534.055257847373,15215.146639151637,12996.626380040323,13029.919161637963,10196.668003968522,10284.440766030819,7022.693160251435,8401.249663323453,8435.803252725058,10265.915943082338,8623.546341166757,7782.429841191159,9309.259412625106,4849.32865233776,9480.40891035497,8237.330612702019,6688.9341497267815,7113.796951355988,8945.392918392941,11391.406447517316,8770.78174795334,7797.074404297926,11205.567652241964,7490.638744688466,6890.626375097134,8721.152535625733,6986.899718851523,10014.515393386338,11863.685949406314,12576.12226693591,6930.903954576986,6933.68818306879,8436.351253425808,11680.836931112251,5673.257881932308,4512.930178739657,6094.8735580904795,11616.485592765608,8423.704814584986,9582.260510301834,6280.960312130538,10196.683253538626,5869.428152205924,6637.126459584064,8223.324863109141,9163.49600405833,11744.973584261521 +XK,12898.821957973714,11760.375115749226,5762.823681912337,10868.645457667955,6851.647476795101,6489.091511565737,8016.427124526303,11096.059611708006,8784.092785314235,6844.044739629848,5968.673897869134,7450.873145908507,8457.218084424714,10466.134979346216,18700.932768467817,18783.264457747777,9647.157790116991,14650.396388121844,12360.14810912279,11640.673528541236,8882.446980807292,14762.619600006037,18399.930168952076,13646.806400326906,14489.119148714197,14104.804002759012,15462.34970923992,10894.228431882033,15095.088711819322,17853.281396922546,12916.46431873845,14207.379133135786,14418.834370720482,12752.5957180177,15544.588217112372,15820.904121022999,13534.055257847373,15215.146639151637,12996.626380040323,13029.919161637963,10196.668003968522,10284.440766030819,7022.693160251435,8401.249663323453,8435.803252725058,10265.915943082338,8623.546341166757,7782.429841191159,9309.259412625106,4849.32865233776,9480.40891035497,8237.330612702019,6688.9341497267815,7113.796951355988,8945.392918392941,11391.406447517316,8770.78174795334,7797.074404297926,11205.567652241964,7490.638744688466,6890.626375097134,8721.152535625733,6986.899718851523,10014.515393386338,11863.685949406314,12576.12226693591,6930.903954576986,6933.68818306879,8436.351253425808,11680.836931112251,5673.257881932308,4512.930178739657,6094.8735580904795,11616.485592765608,8423.704814584986,9582.260510301834,6280.960312130538,10196.683253538626,5869.428152205924,6637.126459584064,8223.324863109141,9163.49600405833,11744.973584261521 SK,10526.703178382486,6581.841291454129,2779.6437301689157,6043.293021386589,8068.711762338472,4716.572547513843,4263.788112757618,7436.446538154413,5733.7942336519345,5091.94458281141,6885.47211985414,6956.591212284293,7116.928783228905,4573.269245439668,8169.130903254093,6799.092645889461,7190.199390340817,8819.311158887818,6679.080394471699,8681.76846617681,6714.688996801623,8780.135433744465,9674.052829336924,7510.89080672348,12315.672504913706,11551.288938768861,10658.095118642217,7760.467099195778,7334.701885838525,11743.053621084162,9101.455526104724,11252.254293590808,7089.2297957961755,11692.575497480175,11870.620362608648,8320.411050776946,12664.612697391287,10840.053026355938,9544.145158945727,8285.851845920657,6554.955003499832,6014.385824757616,5119.229272436076,5566.197178944401,8283.751441606055,4576.739002618681,5529.727054286627,4884.285979430572,7030.767074231632,6380.185010419688,6092.06334940043,4789.6919845964085,3988.728835990378,6100.445127127096,6174.336930810738,5839.492530664778,6739.897695678873,6073.547495791424,6239.663814901599,5985.626769812626,5946.910319392802,5350.4706690989715,2897.742723724252,3812.447551889599,5110.734824948982,5466.7571969447445,3822.1224333717755,4189.745228198809,3608.9397299109723,8925.718578619328,5520.297117401385,2338.023634704523,3634.014208205775,5079.021357670912,3789.3939110245783,3599.858750509022,4222.707040620564,3096.1701009538024,2158.8092391216674,4542.203594136759,4220.210061470592,2961.7796555712353,3705.0055429796116 SI,8932.252857660058,6769.672897729603,3971.443731895907,6727.0196008878875,5996.060296331706,4552.96378417224,7787.375795346372,9548.071392837826,4890.204133819321,7094.028640574649,11752.798116380785,9378.602450871665,7169.30972105447,8221.267552376507,10568.142173580174,7705.119699907013,8559.440940655051,9496.2682906945,10641.418485889739,13644.09405840664,8756.865265119286,10877.994032024846,13095.473005855994,10408.222250010325,17110.767148034232,13257.44359305617,10163.420442559087,10743.809603872312,11213.570708968457,12419.028940460184,8518.188189494229,14975.496301661744,10217.449212514946,10521.007791805137,10759.209828867228,9699.803414833696,14530.647692616923,14530.715084460895,12439.143395173185,11990.046930204517,9001.647305603035,10111.423546501868,6824.172499114327,10267.968760960974,10233.709492652128,9562.175707725224,11231.941186997657,8178.692540046128,8568.526412713521,8103.883097000008,9639.895318751553,9157.43571368136,6814.50158997294,8089.913064615838,8170.645053083449,10765.044719853075,6713.170108103339,8936.8362631332,9542.147586182518,8964.110093884974,8653.114721452737,8353.75428643612,4558.589088593759,9432.405903987477,6927.92152111369,7408.760392367348,5834.871486466167,9336.912851251047,8839.494837985663,10766.693939731143,5073.937522450624,6475.598951785893,9482.419717110955,13414.771212079502,5906.5584945059145,9942.151265372298,7696.092255607837,9521.835825354157,8476.124990692872,6361.010018809483,7504.908031281684,6349.444363482254,12494.86342976942 SE,97729.33612275522,104893.13349064675,152164.30605691054,147567.81837152236,172633.569885874,142454.06079467706,109264.12507808255,145966.80578917114,158733.59492028368,161670.6171803483,134822.12256461562,136958.6213484757,175679.6273876132,133962.36617697845,126787.36233921513,130861.49290945902,151133.2982760823,140247.52694114187,130508.52271890236,137421.63579450184,164132.0529341604,142467.57286064525,135886.87305352974,163762.3962091317,161128.38161511483,148196.28633931337,167892.87677861055,132017.696130452,105953.99289036485,119407.22923207263,147139.6039546916,151309.55114963037,164893.61323375857,137608.18237925606,149284.78771947857,108749.15747050248,131859.531763871,117290.48017785426,131751.97960784068,111117.27409251402,110551.615114603,81550.1135393761,114677.940027603,102356.46697333359,128977.80283219383,94483.98267503879,114937.96457142044,93256.91327642264,96751.29026217168,91074.34139788701,87931.96567208324,96560.83822983531,116746.94389793588,64626.25905820224,75156.91767736348,51489.37512699482,74729.26978432719,134673.01513808029,110440.65771030058,149033.9827088587,143576.83069821307,93040.02354987273,64075.160200006496,102493.08906248632,106664.82884715112,90351.43771324471,94801.81222609026,92445.93458012305,88919.20770998915,108111.72872590217,127440.80298482065,124444.06618883039,98961.91573013023,87913.90602563386,115956.48217115598,95526.84265190935,103459.08985036932,86058.90377748368,99040.9103276256,110493.22773914765,94182.7249029169,169752.66741900297,99896.09236620825 diff --git a/data/existing_infrastructure/existing_heating_raw.csv b/data/existing_infrastructure/existing_heating_raw.csv index 18774460c..5e2581c93 100644 --- a/data/existing_infrastructure/existing_heating_raw.csv +++ b/data/existing_infrastructure/existing_heating_raw.csv @@ -28,4 +28,5 @@ United Kingdom,160.49,1.26,7.39,13.81,0.81,0.21 Norway,,,,,2.91,0.334 Switzerland,,,,,1,0.849 Serbia,,,,,, +Kosovo,,,,,, Bosnia Herzegovina,,,,,, diff --git a/data/geth2015_hydro_capacities.csv b/data/geth2015_hydro_capacities.csv deleted file mode 100644 index ddb91baec..000000000 --- a/data/geth2015_hydro_capacities.csv +++ /dev/null @@ -1,32 +0,0 @@ -# Table 25 from F. Geth et al., An overview of large-scale stationary electricity storage plants in Europe (2015) 1212–1227 -country,n,p_nom_discharge,p_nom_charge,e_stor -AT,19,4.051,3.246,132.41 -BE,3,1.301,1.196,5.71 -BG,3,1.399,0.93,11.13 -HR,3,0.281,0.246,2.34 -CY,0,-,-,- -CZ,3,1.119,1.145,5.72 -DK,0,-,-,- -EE,0,-,-,- -FI ,0,-,-,- -FR,10,5.512,4.317,83.37 -DE,34,6.805,6.417,39.12 -GR,2,0.735,-,4.97 -HU,0,-,-,- -IE,1,0.292,-,1.8 -IT,25,7.833,7.64,68.27 -LV,0,-,-,- -LT,1,0.9,0.88,10.8 -LU,1,1.296,1.05,4.92 -MT,0,-,-,- -NL,0,-,-,- -PL,6,1.757,1.647,7.96 -PT,7,1.279,-,40.77 -RO,5,0.285,0.2,10.2 -SK,4,1.016,0.79,3.63 -SI,1,0.185,0.18,0.5 -ES,26,6.358,5.859,70 -SE,2,0.091,-,72.12 -GB,4,2.788,2.65,26.7 -NO,8,1.273,0.892,399.39 -CH,20,2.291,1.512,311.48 diff --git a/data/nuclear_p_max_pu.csv b/data/nuclear_p_max_pu.csv index 0fdb5e5b4..5a8d1d131 100644 --- a/data/nuclear_p_max_pu.csv +++ b/data/nuclear_p_max_pu.csv @@ -1,16 +1,17 @@ country,factor -BE,0.796 -BG,0.894 -CZ,0.827 -FI,0.936 -FR,0.71 -DE,0.871 -HU,0.913 -NL,0.868 -RO,0.909 -SK,0.9 -SI,0.913 -ES,0.897 -SE,0.851 -CH,0.87 -GB,0.656 +BE,0.883 +BG,0.876 +CZ,0.839 +FI,0.924 +FR,0.616 +DE,0.926 +HU,0.891 +NL,0.901 +RO,0.906 +SK,0.908 +SI,0.884 +ES,0.883 +SE,0.817 +CH,0.834 +GB,0.684 +UA,0.701 diff --git a/data/refineries-noneu.csv b/data/refineries-noneu.csv new file mode 100644 index 000000000..e60127971 --- /dev/null +++ b/data/refineries-noneu.csv @@ -0,0 +1,15 @@ +"Site","Capacity [bbl/day]","Country","Latitude","Longitude","Date","Source","Comment" +"Modrica",1,"BA",44.95978,18.31294,"29-08-2024","https://modricaoil.com/Content/Read/onama?lang=en-US","the only one" +"OKTA, Skopje",1,"MK",42.0022,21.65532,"29-08-2024","https://en.wikipedia.org/wiki/OKTA","the only one" +"Ballsh",20000,"AL",40.60468,19.75231,"29-08-2024","https://www.trade.gov/country-commercial-guides/albania-oil-and-gas", +"Fier",10000,"AL",40.70095,19.54698,"29-08-2024","https://www.trade.gov/country-commercial-guides/albania-oil-and-gas", +"Elbasan",5000,"AL",41.08621,20.00762,"29-08-2024","https://www.trade.gov/country-commercial-guides/albania-oil-and-gas", +"Pančevo",96000,"RS",44.83051,20.68127,"29-08-2024","https://en.wikipedia.org/wiki/Naftna_Industrija_Srbije", +"Novi Sad",52000,"RS",45.27559,19.86733,"29-08-2024","https://en.wikipedia.org/wiki/Naftna_Industrija_Srbije", +"Halychyna Refinery (Pryvat), Drohobych",40000,"UA",49.34,23.4851,"29-08-2024","https://en.wikipedia.org/wiki/List_of_oil_refineries", +"Kherson Refinery (Alliance)",36000,"UA",46.65946,32.57479,"29-08-2024","https://en.wikipedia.org/wiki/List_of_oil_refineries", +"Kremenchuk Refinery (Ukrtatnafta)",368500,"UA",49.15299,33.4327,"29-08-2024","https://en.wikipedia.org/wiki/List_of_oil_refineries", +"LINOS Refinery, Lysychansk Oil Refinery (TNK-BP)",320000,"UA",48.84876,38.29979,"29-08-2024","https://en.wikipedia.org/wiki/List_of_oil_refineries", +"Lviv Oil Research & Refinery",18000,"UA",49.80662,24.04376,"29-08-2024","https://en.wikipedia.org/wiki/List_of_oil_refineries","capacity estimated based on number of employees" +"Naftokhimik Prykarpattya (Pryvat) Nadvirna",39000,"UA",48.62873,24.59924,"29-08-2024","https://en.wikipedia.org/wiki/List_of_oil_refineries", +"Odesa Refinery (LUKOIL)",70000,"UA",46.51189,30.68805,"29-08-2024","https://en.wikipedia.org/wiki/List_of_oil_refineries", diff --git a/data/urban_percent.csv b/data/urban_percent.csv deleted file mode 100644 index d57e07281..000000000 --- a/data/urban_percent.csv +++ /dev/null @@ -1,30 +0,0 @@ -AT,66 -BA,40 -BE,98 -BG,74 -CH,74 -CZ,73 -DE,75 -DK,88 -EE,68 -ES,80 -FI,84 -FR,80 -GB,83 -GR,78 -HR,59 -HU,71 -IE,63 -IT,69 -LT,67 -LU,90 -LV,67 -NL,90 -NO,80 -PL,61 -PT,63 -RO,55 -RS,56 -SE,86 -SI,50 -SK,54 diff --git a/doc/configtables/countries.csv b/doc/configtables/countries.csv index 6a386416c..41ddd8e91 100644 --- a/doc/configtables/countries.csv +++ b/doc/configtables/countries.csv @@ -1,2 +1,2 @@ ,Unit,Values,Description -countries,--,"Subset of {'AL', 'AT', 'BA', 'BE', 'BG', 'CH', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HU', 'IE', 'IT', 'LT', 'LU', 'LV', 'ME', 'MK', 'NL', 'NO', 'PL', 'PT', 'RO', 'RS', 'SE', 'SI', 'SK'}","European countries defined by their `Two-letter country codes (ISO 3166-1) `_ which should be included in the energy system model." +countries,--,"Subset of {'AL', 'AT', 'BA', 'BE', 'BG', 'CH', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HU', 'IE', 'IT', 'LT', 'LU', 'LV', 'ME', 'MK', 'NL', 'NO', 'PL', 'PT', 'RO', 'RS', 'SE', 'SI', 'SK', 'XK'}","European countries defined by their `Two-letter country codes (ISO 3166-1) `_ which should be included in the energy system model." diff --git a/doc/configtables/licenses-sector.csv b/doc/configtables/licenses-sector.csv index a44e0a5d5..8c721260d 100644 --- a/doc/configtables/licenses-sector.csv +++ b/doc/configtables/licenses-sector.csv @@ -1,6 +1,5 @@ description,file/folder,licence,source JRC IDEES database,jrc-idees-2015/,CC BY 4.0,https://ec.europa.eu/jrc/en/potencia/jrc-idees -urban/rural fraction,urban_percent.csv,unknown,unknown JRC ENSPRESO biomass potentials,remote,CC BY 4.0,https://data.jrc.ec.europa.eu/dataset/74ed5a04-7d74-4807-9eab-b94774309d9f EEA emission statistics,eea/UNFCCC_v23.csv,EEA standard re-use policy,https://www.eea.europa.eu/data-and-maps/data/national-emissions-reported-to-the-unfccc-and-to-the-eu-greenhouse-gas-monitoring-mechanism-16 Eurostat Energy Balances,eurostat-energy_balances-*/,Eurostat,https://ec.europa.eu/eurostat/web/energy/data/energy-balances diff --git a/doc/configtables/sector.csv b/doc/configtables/sector.csv index 88442a9ee..044c8dc40 100644 --- a/doc/configtables/sector.csv +++ b/doc/configtables/sector.csv @@ -9,8 +9,13 @@ district_heating,--,,`prepare_sector_network.py `__ + of specific countries is used as a spatial distribution key for the cement + demand. The data is stored in ``data/cement-plants-noneu.csv``. + +* Added data on the locations and capacities of refineries in Europe that are + not included in the Hotmaps industrial database. The data is mostly sourced + from the `Wikipedia list of oil refineries + `__. The data is stored + in ``data/refineries-noneu.csv``. + +* Included data from the `Global Steel Plant Tracker + `__ + provided by Global Energy Monitor. The data includes among other attributes + the locations, ages, operating status, relining dates, manufacturing process + and capacities of steel plants in Europe. This data is used as a spatial + distribution key for the steel production, which is now separated by process + type (EAF, DRI + EAF, integrated). + +* Retrieve share of urban population from `World Bank API + `__. The data + originates from the United Nations Population Division. Previously, a file + ``data/urban_percent.csv`` with an undocumented source was used. + +* Updated country-specific Energy Availability Factors (EAFs) for nuclear power + plants based on `IAEA 2021-2023 reported country averages + `__. + +* Update GEM Europe Gas Tracker to May 2024 version. + * Add investment period dependent CO2 sequestration potentials * Add option to produce hydrogen from solid biomass (flag ``solid biomass to hydrogen``), combined with carbon capture @@ -21,6 +61,8 @@ Upcoming Release * Update JRC-IDEES-2015 to `JRC-IDEES-2021 `__. The reference year is changed from 2015 to 2019. +* Made central heating supply temperatures dynamic based on an adaptation of a reference curve from Pieper et al. (2019) (https://www.sciencedirect.com/science/article/pii/S0360544219305857?via%3Dihub). + * Added option to use country-specific district heating forward and return temperatures. Defaults to lower temperatures in Scandinavia. * Added unsustainable biomass potentials for solid, gaseous, and liquid biomass. The potentials can be phased-out and/or @@ -74,7 +116,7 @@ Upcoming Release * Enable parallelism in :mod:`determine_availability_matrix_MD_UA.py` and remove plots. This requires the use of temporary files. -* Added new major feature to create the base_network from OpenStreetMap (OSM) data (PR https://github.com/PyPSA/pypsa-eur/pull/1079). Note that a heuristics based cleaning process is used for lines and links where electrical parameters are incomplete, missing, or ambiguous. Through ``electricity["base_network"]``, the base network can be set to "entsoegridkit" (original default setting, deprecated soon), "osm-prebuilt" (which downloads the latest prebuilt snapshot based on OSM data from Zenodo), or "osm-raw" which retrieves (once) and cleans the raw OSM data and subsequently builds the network. Note that this process may take a few minutes. +* Added new major feature to create the base_network from OpenStreetMap (OSM) data (PR https://github.com/PyPSA/pypsa-eur/pull/1079). Note that a heuristics based cleaning process is used for lines and links where electrical parameters are incomplete, missing, or ambiguous. Through ``electricity["base_network"]``, the base network can be set to "entsoegridkit" (now deprecated), "osm-prebuilt" (default, downloads the latest prebuilt snapshot based on OSM data from Zenodo), or "osm-raw" which retrieves (once) and cleans the raw OSM data and subsequently builds the network. Note that this process may take a few minutes. * Updated pre-built `weather data cutouts `__. These are now merged cutouts with diff --git a/rules/build_electricity.smk b/rules/build_electricity.smk index 1f568e99c..8c3ce32d6 100644 --- a/rules/build_electricity.smk +++ b/rules/build_electricity.smk @@ -373,7 +373,7 @@ rule build_transmission_projects: benchmark: benchmarks("build_transmission_projects") resources: - mem_mb=2000, + mem_mb=4000, threads: 1 conda: "../envs/environment.yaml" @@ -469,7 +469,6 @@ rule add_electricity: regions=resources("regions_onshore.geojson"), powerplants=resources("powerplants.csv"), hydro_capacities=ancient("data/hydro_capacities.csv"), - geth_hydro_capacities="data/geth2015_hydro_capacities.csv", unit_commitment="data/unit_commitment.csv", fuel_price=lambda w: ( resources("monthly_fuel_price.csv") diff --git a/rules/build_sector.smk b/rules/build_sector.smk index 598df96e3..9f94dbbd6 100644 --- a/rules/build_sector.smk +++ b/rules/build_sector.smk @@ -6,7 +6,7 @@ rule build_population_layouts: input: nuts3_shapes=resources("nuts3_shapes.geojson"), - urban_percent="data/urban_percent.csv", + urban_percent="data/worldbank/API_SP.URB.TOTL.IN.ZS_DS2_en_csv_v2_3403768.csv", cutout=lambda w: "cutouts/" + CDIR + config_provider("atlite", "default_cutout")(w) @@ -93,10 +93,7 @@ rule build_gas_network: rule build_gas_input_locations: input: - gem=storage( - "https://globalenergymonitor.org/wp-content/uploads/2023/07/Europe-Gas-Tracker-2023-03-v3.xlsx", - keep_local=True, - ), + gem="data/gem/Europe-Gas-Tracker-2024-05.xlsx", entry="data/gas_network/scigrid-gas/data/IGGIELGN_BorderPoints.geojson", storage="data/gas_network/scigrid-gas/data/IGGIELGN_Storages.geojson", regions_onshore=resources("regions_onshore_elec_s{simpl}_{clusters}.geojson"), @@ -215,17 +212,72 @@ rule build_temperature_profiles: "../scripts/build_temperature_profiles.py" +rule build_central_heating_temperature_profiles: + params: + max_forward_temperature_central_heating=config_provider( + "sector", + "district_heating", + "supply_temperature_approximation", + "max_forward_temperature", + ), + min_forward_temperature_central_heating=config_provider( + "sector", + "district_heating", + "supply_temperature_approximation", + "min_forward_temperature", + ), + return_temperature_central_heating=config_provider( + "sector", + "district_heating", + "supply_temperature_approximation", + "return_temperature", + ), + snapshots=config_provider("snapshots"), + lower_threshold_ambient_temperature=config_provider( + "sector", + "district_heating", + "supply_temperature_approximation", + "lower_threshold_ambient_temperature", + ), + upper_threshold_ambient_temperature=config_provider( + "sector", + "district_heating", + "supply_temperature_approximation", + "upper_threshold_ambient_temperature", + ), + rolling_window_ambient_temperature=config_provider( + "sector", + "district_heating", + "supply_temperature_approximation", + "rolling_window_ambient_temperature", + ), + input: + temp_air_total=resources("temp_air_total_elec_s{simpl}_{clusters}.nc"), + regions_onshore=resources("regions_onshore_elec_s{simpl}_{clusters}.geojson"), + output: + central_heating_forward_temperature_profiles=resources( + "central_heating_forward_temperature_profiles_elec_s{simpl}_{clusters}.nc" + ), + central_heating_return_temperature_profiles=resources( + "central_heating_return_temperature_profiles_elec_s{simpl}_{clusters}.nc" + ), + resources: + mem_mb=20000, + log: + logs("build_central_heating_temperature_profiles_s{simpl}_{clusters}.log"), + benchmark: + benchmarks("build_central_heating_temperature_profiles/s{simpl}_{clusters}") + conda: + "../envs/environment.yaml" + script: + "../scripts/build_central_heating_temperature_profiles/run.py" + + rule build_cop_profiles: params: heat_pump_sink_T_decentral_heating=config_provider( "sector", "heat_pump_sink_T_individual_heating" ), - forward_temperature_central_heating=config_provider( - "sector", "district_heating", "forward_temperature" - ), - return_temperature_central_heating=config_provider( - "sector", "district_heating", "return_temperature" - ), heat_source_cooling_central_heating=config_provider( "sector", "district_heating", "heat_source_cooling" ), @@ -235,6 +287,12 @@ rule build_cop_profiles: heat_pump_sources=config_provider("sector", "heat_pump_sources"), snapshots=config_provider("snapshots"), input: + central_heating_forward_temperature_profiles=resources( + "central_heating_forward_temperature_profiles_elec_s{simpl}_{clusters}.nc" + ), + central_heating_return_temperature_profiles=resources( + "central_heating_return_temperature_profiles_elec_s{simpl}_{clusters}.nc" + ), temp_soil_total=resources("temp_soil_total_elec_s{simpl}_{clusters}.nc"), temp_air_total=resources("temp_air_total_elec_s{simpl}_{clusters}.nc"), regions_onshore=resources("regions_onshore_elec_s{simpl}_{clusters}.geojson"), @@ -578,10 +636,14 @@ rule build_industrial_distribution_key: input: regions_onshore=resources("regions_onshore_elec_s{simpl}_{clusters}.geojson"), clustered_pop_layout=resources("pop_layout_elec_s{simpl}_{clusters}.csv"), - hotmaps_industrial_database=storage( + hotmaps=storage( "https://gitlab.com/hotmaps/industrial_sites/industrial_sites_Industrial_Database/-/raw/master/data/Industrial_Database.csv", keep_local=True, ), + gem_gspt="data/gem/Global-Steel-Plant-Tracker-April-2024-Standard-Copy-V1.xlsx", + ammonia="data/ammonia_plants.csv", + cement_supplement="data/cement-plants-noneu.csv", + refineries_supplement="data/refineries-noneu.csv", output: industrial_distribution_key=resources( "industrial_distribution_key_elec_s{simpl}_{clusters}.csv" diff --git a/rules/retrieve.smk b/rules/retrieve.smk index c30696ccf..c0cd5b6c2 100644 --- a/rules/retrieve.smk +++ b/rules/retrieve.smk @@ -259,6 +259,30 @@ if config["enable"]["retrieve"]: +if config["enable"]["retrieve"]: + + rule retrieve_worldbank_urban_population: + params: + zip="data/worldbank/API_SP.URB.TOTL.IN.ZS_DS2_en_csv_v2_3403768.zip", + output: + gpkg="data/worldbank/API_SP.URB.TOTL.IN.ZS_DS2_en_csv_v2_3403768.csv", + run: + import os + import requests + + response = requests.get( + "https://api.worldbank.org/v2/en/indicator/SP.URB.TOTL.IN.ZS?downloadformat=csv", + params={"name": "API_SP.URB.TOTL.IN.ZS_DS2_en_csv_v2_3403768.zip"}, + ) + + with open(params["zip"], "wb") as f: + f.write(response.content) + output_folder = Path(params["zip"]).parent + unpack_archive(params["zip"], output_folder) + os.remove(params["zip"]) + + + if config["enable"]["retrieve"]: # Download directly from naciscdn.org which is a redirect from naturalearth.com @@ -280,6 +304,40 @@ if config["enable"]["retrieve"]: os.remove(params["zip"]) +if config["enable"]["retrieve"]: + + rule retrieve_gem_europe_gas_tracker: + output: + "data/gem/Europe-Gas-Tracker-2024-05.xlsx", + run: + import requests + + response = requests.get( + "https://globalenergymonitor.org/wp-content/uploads/2024/05/Europe-Gas-Tracker-2024-05.xlsx", + headers={"User-Agent": "Mozilla/5.0"}, + ) + with open(output[0], "wb") as f: + f.write(response.content) + + + +if config["enable"]["retrieve"]: + + rule retrieve_gem_steel_plant_tracker: + output: + "data/gem/Global-Steel-Plant-Tracker-April-2024-Standard-Copy-V1.xlsx", + run: + import requests + + response = requests.get( + "https://globalenergymonitor.org/wp-content/uploads/2024/04/Global-Steel-Plant-Tracker-April-2024-Standard-Copy-V1.xlsx", + headers={"User-Agent": "Mozilla/5.0"}, + ) + with open(output[0], "wb") as f: + f.write(response.content) + + + if config["enable"]["retrieve"]: # Some logic to find the correct file URL # Sometimes files are released delayed or ahead of schedule, check which file is currently available diff --git a/scripts/_helpers.py b/scripts/_helpers.py index a3b77c1c0..f79d92583 100644 --- a/scripts/_helpers.py +++ b/scripts/_helpers.py @@ -507,7 +507,8 @@ def generate_periodic_profiles(dt_index, nodes, weekly_profile, localize=None): week_df = pd.DataFrame(index=dt_index, columns=nodes) for node in nodes: - timezone = pytz.timezone(pytz.country_timezones[node[:2]][0]) + ct = node[:2] if node[:2] != "XK" else "RS" + timezone = pytz.timezone(pytz.country_timezones[ct][0]) tz_dt_index = dt_index.tz_convert(timezone) week_df[node] = [24 * dt.weekday() + dt.hour for dt in tz_dt_index] week_df[node] = week_df[node].map(weekly_profile) diff --git a/scripts/add_electricity.py b/scripts/add_electricity.py index 076eb84ed..6728ea214 100755 --- a/scripts/add_electricity.py +++ b/scripts/add_electricity.py @@ -51,7 +51,6 @@ .. image:: img/hydrocapacities.png :scale: 34 % -- ``data/geth2015_hydro_capacities.csv``: alternative to capacities above; not currently used! - ``resources/electricity_demand.csv`` Hourly per-country electricity demand profiles. - ``resources/regions_onshore.geojson``: confer :ref:`busregions` - ``resources/nuts3_shapes.geojson``: confer :ref:`shapes` diff --git a/scripts/base_network.py b/scripts/base_network.py index afb66387e..0f661c284 100644 --- a/scripts/base_network.py +++ b/scripts/base_network.py @@ -1008,12 +1008,12 @@ def append_bus_shapes(n, shapes, type): ) shapes.to_file(snakemake.output.regions_onshore) - append_bus_shapes(n, shapes, "onshore") + # append_bus_shapes(n, shapes, "onshore") if offshore_regions: shapes = pd.concat(offshore_regions, ignore_index=True) shapes.to_file(snakemake.output.regions_offshore) - append_bus_shapes(n, shapes, "offshore") + # append_bus_shapes(n, shapes, "offshore") else: offshore_shapes.to_frame().to_file(snakemake.output.regions_offshore) diff --git a/scripts/build_biomass_potentials.py b/scripts/build_biomass_potentials.py index b69fcfbe9..4c7752e4b 100644 --- a/scripts/build_biomass_potentials.py +++ b/scripts/build_biomass_potentials.py @@ -88,9 +88,11 @@ def build_nuts_population_data(year=2013): pop = pd.concat([pop, pd.concat(swiss)]).to_frame("total") # add missing manually - pop["AL"] = 2893 - pop["BA"] = 3871 - pop["RS"] = 7210 + pop["AL"] = 2778 + pop["BA"] = 3234 + pop["RS"] = 6664 + pop["ME"] = 617 + pop["XK"] = 1587 pop["ct"] = pop.index.str[:2] @@ -149,10 +151,6 @@ def enspreso_biomass_potentials(year=2020, scenario="ENS_Low"): bio = dff.groupby(["NUTS2", "commodity"]).potential.sum().unstack() - # currently Serbia and Kosovo not split, so aggregate - bio.loc["RS"] += bio.loc["XK"] - bio.drop("XK", inplace=True) - return bio @@ -199,7 +197,7 @@ def build_nuts2_shapes(): ) countries = gpd.read_file(snakemake.input.country_shapes).set_index("name") - missing_iso2 = countries.index.intersection(["AL", "RS", "BA"]) + missing_iso2 = countries.index.intersection(["AL", "RS", "XK", "BA"]) missing = countries.loc[missing_iso2] nuts2.rename(index={"ME00": "ME", "MK00": "MK"}, inplace=True) diff --git a/scripts/build_central_heating_temperature_profiles/central_heating_temperature_approximator.py b/scripts/build_central_heating_temperature_profiles/central_heating_temperature_approximator.py new file mode 100644 index 000000000..5b467824d --- /dev/null +++ b/scripts/build_central_heating_temperature_profiles/central_heating_temperature_approximator.py @@ -0,0 +1,218 @@ +# -*- coding: utf-8 -*- +# SPDX-FileCopyrightText: : 2020-2024 The PyPSA-Eur Authors +# +# SPDX-License-Identifier: MIT + +import pandas as pd +import xarray as xr + + +class CentralHeatingTemperatureApproximator: + """ + A class to approximate central heating temperatures based on ambient + temperature. + + Attributes + ---------- + ambient_temperature : xr.DataArray + The ambient temperature data. + max_forward_temperature : xr.DataArray + The maximum forward temperature. + min_forward_temperature : xr.DataArray + The minimum forward temperature. + fixed_return_temperature : xr.DataArray + The fixed return temperature. + lower_threshold_ambient_temperature : float + Forward temperature is `max_forward_temperature` for ambient temperatures lower-or-equal this threshold. + upper_threshold_ambient_temperature : float + Forward temperature is `min_forward_temperature` for ambient temperatures higher-or-equal this threshold. + """ + + def __init__( + self, + ambient_temperature: xr.DataArray, + max_forward_temperature: float, + min_forward_temperature: float, + fixed_return_temperature: float, + lower_threshold_ambient_temperature: float, + upper_threshold_ambient_temperature: float, + rolling_window_ambient_temperature: int, + ) -> None: + """ + Initialize the CentralHeatingTemperatureApproximator. + + Parameters + ---------- + ambient_temperature : xr.DataArray + The ambient temperature data. + max_forward_temperature : xr.DataArray + The maximum forward temperature. + min_forward_temperature : xr.DataArray + The minimum forward temperature. + fixed_return_temperature : xr.DataArray + The fixed return temperature. + lower_threshold_ambient_temperature : float + Forward temperature is `max_forward_temperature` for ambient temperatures lower-or-equal this threshold. + upper_threshold_ambient_temperature : float + Forward temperature is `min_forward_temperature` for ambient temperatures higher-or-equal this threshold. + rolling_window_ambient_temperature : int + Rolling window size for averaging ambient temperature. + """ + self._ambient_temperature = ambient_temperature + self.max_forward_temperature = max_forward_temperature + self.min_forward_temperature = min_forward_temperature + self.fixed_return_temperature = fixed_return_temperature + self.lower_threshold_ambient_temperature = lower_threshold_ambient_temperature + self.upper_threshold_ambient_temperature = upper_threshold_ambient_temperature + self.rolling_window_ambient_temperature = rolling_window_ambient_temperature + + def ambient_temperature_rolling_mean(self) -> xr.DataArray: + """ + Property to get ambient temperature. + + Returns + ------- + xr.DataArray + Rolling mean of ambient temperature input. + """ + # bfill to avoid NAs in the beginning + return ( + self._ambient_temperature.rolling( + time=self.rolling_window_ambient_temperature + ) + .mean(skip_na=True) + .bfill(dim="time") + ) + + @property + def forward_temperature(self) -> xr.DataArray: + """ + Property to get dynamic forward temperature. + + Returns + ------- + xr.DataArray + Dynamic forward temperatures + """ + return self._approximate_forward_temperature() + + @property + def return_temperature(self) -> float: + """ + Property to get return temperature. + + Returns + ------- + float + Return temperature. + """ + return self._approximate_return_temperature() + + def _approximate_forward_temperature(self) -> xr.DataArray: + """ + Approximate dynamic forward temperature based on reference curve. Adapted from [Pieper et al. (2019)](https://doi.org/10.1016/j.energy.2019.03.165). + + Returns + ------- + xr.DataArray + Dynamic forward temperatures. + """ + + forward_temperature = xr.where( + self.ambient_temperature_rolling_mean() + <= self.lower_threshold_ambient_temperature, + self.max_forward_temperature, + xr.where( + self.ambient_temperature_rolling_mean() + >= self.upper_threshold_ambient_temperature, + self.min_forward_temperature, + self.min_forward_temperature + + (self.max_forward_temperature - self.min_forward_temperature) + * ( + self.upper_threshold_ambient_temperature + - self.ambient_temperature_rolling_mean() + ) + / ( + self.upper_threshold_ambient_temperature + - self.lower_threshold_ambient_temperature + ), + ), + ) + return forward_temperature + + def _approximate_return_temperature(self) -> float: + """ + Approximate return temperature. + + Returns + ------- + float + Return temperature. + """ + return self.fixed_return_temperature + + @property + def forward_temperature(self) -> xr.DataArray: + """ + Property to get dynamic forward temperature. + + Returns + ------- + xr.DataArray + Dynamic forward temperatures. + """ + return self._approximate_forward_temperature() + + @property + def return_temperature(self) -> float: + """ + Property to get return temperature. + + Returns + ------- + float + Return temperature. + """ + return self._approximate_return_temperature() + + def _approximate_forward_temperature(self) -> xr.DataArray: + """ + Approximate dynamic forward temperature. + + Returns + ------- + xr.DataArray + Dynamic forward temperatures. + """ + forward_temperature = xr.where( + self.ambient_temperature_rolling_mean() + <= self.lower_threshold_ambient_temperature, + self.max_forward_temperature, + xr.where( + self.ambient_temperature_rolling_mean() + >= self.upper_threshold_ambient_temperature, + self.min_forward_temperature, + self.min_forward_temperature + + (self.max_forward_temperature - self.min_forward_temperature) + * ( + self.upper_threshold_ambient_temperature + - self.ambient_temperature_rolling_mean() + ) + / ( + self.upper_threshold_ambient_temperature + - self.lower_threshold_ambient_temperature + ), + ), + ) + return forward_temperature + + def _approximate_return_temperature(self) -> float: + """ + Approximate return temperature. + + Returns + ------- + float + Return temperature. + """ + return self.fixed_return_temperature diff --git a/scripts/build_central_heating_temperature_profiles/run.py b/scripts/build_central_heating_temperature_profiles/run.py new file mode 100644 index 000000000..115293e4c --- /dev/null +++ b/scripts/build_central_heating_temperature_profiles/run.py @@ -0,0 +1,147 @@ +# -*- coding: utf-8 -*- +# SPDX-FileCopyrightText: : 2020-2024 The PyPSA-Eur Authors +# +# SPDX-License-Identifier: MIT +""" +Approximate district heating forward and return temperature profiles based on +ambient temperature. The method is based on a reference curve from Pieper et +al. 2019, where for ambient temperatures below 0C, the highest possible forward +temperature is assumed and vice versa for temperatures above 10C. Between these +threshold levels, forward temperatures are linearly interpolated. + +By default, temperature levels are increased for non-Scandinavian countries. +The default ratios between min. and max. forward temperatures is based on AGFW-Hauptbericht 2022. + +Relevant Settings +----------------- + +.. code:: yaml + sector: + district_heating: + max_forward_temperature: + min_forward_temperature: + return_temperature: +Inputs +------ +- `resources//temp_air_total`: Air temperature + +Outputs +------- +- `resources//central_heating_temperature_profiles.nc`: + +References +---------- +- Pieper, et al. (2019): "Assessment of a combination of three heat sources for heat pumps to supply district heating" (https://doi.org/10.1016/j.energy.2019.03.165). +- AGFW (2022): "Hauptbericht 2022" (https://www.agfw.de/zahlen-und-statistiken/agfw-hauptbericht) +""" + +import sys + +import geopandas as gpd +import numpy as np +import pandas as pd +import xarray as xr +from _helpers import set_scenario_config +from central_heating_temperature_approximator import ( + CentralHeatingTemperatureApproximator, +) + + +def get_country_from_node_name(node_name: str) -> str: + return node_name[:2] + + +def map_temperature_dict_to_onshore_regions( + supply_temperature_by_country: dict, + regions_onshore: pd.Index, + snapshots: pd.DatetimeIndex, +) -> xr.DataArray: + """ + Map dictionary of temperatures to onshore regions. + + Parameters: + ---------- + supply_temperature_by_country : dictionary + Dictionary with temperatures as values and country keys as keys. One key must be named "default" + regions_onshore : pd.Index + Names of onshore regions + snapshots : pd.DatetimeIndex + Time stamps + + Returns: + ------- + xr.DataArray + The dictionary values mapped to onshore regions with onshore regions as coordinates. + """ + return xr.DataArray( + [ + [ + ( + supply_temperature_by_country[get_country_from_node_name(node_name)] + if get_country_from_node_name(node_name) + in supply_temperature_by_country.keys() + else supply_temperature_by_country["default"] + ) + for node_name in regions_onshore.values + ] + # pass both nodes and snapshots as dimensions to preserve correct data structure + for _ in snapshots + ], + dims=["time", "name"], + coords={"time": snapshots, "name": regions_onshore}, + ) + + +if __name__ == "__main__": + if "snakemake" not in globals(): + from _helpers import mock_snakemake + + snakemake = mock_snakemake( + "build_cop_profiles", + simpl="", + clusters=48, + ) + + set_scenario_config(snakemake) + + # map forward and return temperatures specified on country-level to onshore regions + regions_onshore = gpd.read_file(snakemake.input.regions_onshore)["name"] + snapshots = pd.date_range(freq="h", **snakemake.params.snapshots) + max_forward_temperature_central_heating_by_node_and_time: xr.DataArray = ( + map_temperature_dict_to_onshore_regions( + supply_temperature_by_country=snakemake.params.max_forward_temperature_central_heating, + regions_onshore=regions_onshore, + snapshots=snapshots, + ) + ) + min_forward_temperature_central_heating_by_node_and_time: xr.DataArray = ( + map_temperature_dict_to_onshore_regions( + supply_temperature_by_country=snakemake.params.min_forward_temperature_central_heating, + regions_onshore=regions_onshore, + snapshots=snapshots, + ) + ) + return_temperature_central_heating_by_node_and_time: xr.DataArray = ( + map_temperature_dict_to_onshore_regions( + supply_temperature_by_country=snakemake.params.return_temperature_central_heating, + regions_onshore=regions_onshore, + snapshots=snapshots, + ) + ) + + central_heating_temperature_approximator = CentralHeatingTemperatureApproximator( + ambient_temperature=xr.open_dataarray(snakemake.input.temp_air_total), + max_forward_temperature=max_forward_temperature_central_heating_by_node_and_time, + min_forward_temperature=min_forward_temperature_central_heating_by_node_and_time, + fixed_return_temperature=return_temperature_central_heating_by_node_and_time, + lower_threshold_ambient_temperature=snakemake.params.lower_threshold_ambient_temperature, + upper_threshold_ambient_temperature=snakemake.params.upper_threshold_ambient_temperature, + rolling_window_ambient_temperature=snakemake.params.rolling_window_ambient_temperature, + ) + + central_heating_temperature_approximator.forward_temperature.to_netcdf( + snakemake.output.central_heating_forward_temperature_profiles + ) + central_heating_temperature_approximator.return_temperature.to_netcdf( + snakemake.output.central_heating_return_temperature_profiles + ) diff --git a/scripts/build_cop_profiles/run.py b/scripts/build_cop_profiles/run.py index b4ec3e43f..d1faf1b12 100644 --- a/scripts/build_cop_profiles/run.py +++ b/scripts/build_cop_profiles/run.py @@ -53,47 +53,6 @@ from scripts.definitions.heat_system_type import HeatSystemType -def map_temperature_dict_to_onshore_regions( - supply_temperature_by_country: dict, - regions_onshore: pd.Index, - snapshots: pd.DatetimeIndex, -) -> xr.DataArray: - """ - Map dictionary of temperatures to onshore regions. - - Parameters: - ---------- - supply_temperature_by_country : dictionary - Dictionary with temperatures as values and country keys as keys. One key must be named "default" - regions_onshore : pd.Index - Names of onshore regions - snapshots : pd.DatetimeIndex - Time stamps - - Returns: - ------- - xr.DataArray - The dictionary values mapped to onshore regions with onshore regions as coordinates. - """ - return xr.DataArray( - [ - [ - ( - supply_temperature_by_country[get_country_from_node_name(node_name)] - if get_country_from_node_name(node_name) - in supply_temperature_by_country.keys() - else supply_temperature_by_country["default"] - ) - for node_name in regions_onshore.values - ] - # pass both nodes and snapshots as dimensions to preserve correct data structure - for _ in snapshots - ], - dims=["time", "name"], - coords={"time": snapshots, "name": regions_onshore}, - ) - - def get_cop( heat_system_type: str, heat_source: str, @@ -154,20 +113,13 @@ def get_country_from_node_name(node_name: str) -> str: # map forward and return temperatures specified on country-level to onshore regions regions_onshore = gpd.read_file(snakemake.input.regions_onshore)["name"] snapshots = pd.date_range(freq="h", **snakemake.params.snapshots) - forward_temperature_central_heating_by_node_and_time: xr.DataArray = ( - map_temperature_dict_to_onshore_regions( - supply_temperature_by_country=snakemake.params.forward_temperature_central_heating, - regions_onshore=regions_onshore, - snapshots=snapshots, - ) + central_heating_forward_temperature: xr.DataArray = xr.open_dataarray( + snakemake.input.central_heating_forward_temperature_profiles ) - return_temperature_central_heating_by_node_and_time: xr.DataArray = ( - map_temperature_dict_to_onshore_regions( - supply_temperature_by_country=snakemake.params.return_temperature_central_heating, - regions_onshore=regions_onshore, - snapshots=snapshots, - ) + central_heating_return_temperature: xr.DataArray = xr.open_dataarray( + snakemake.input.central_heating_return_temperature_profiles ) + cop_all_system_types = [] for heat_system_type, heat_sources in snakemake.params.heat_pump_sources.items(): cop_this_system_type = [] @@ -179,8 +131,8 @@ def get_country_from_node_name(node_name: str) -> str: heat_system_type=heat_system_type, heat_source=heat_source, source_inlet_temperature_celsius=source_inlet_temperature_celsius, - forward_temperature_by_node_and_time=forward_temperature_central_heating_by_node_and_time, - return_temperature_by_node_and_time=return_temperature_central_heating_by_node_and_time, + forward_temperature_by_node_and_time=central_heating_forward_temperature, + return_temperature_by_node_and_time=central_heating_return_temperature, ) cop_this_system_type.append(cop_da) cop_all_system_types.append( diff --git a/scripts/build_electricity_demand.py b/scripts/build_electricity_demand.py index 622fdafa2..d3334ec85 100755 --- a/scripts/build_electricity_demand.py +++ b/scripts/build_electricity_demand.py @@ -192,9 +192,9 @@ def manual_adjustment(load, fn_load, countries): if "ME" in load: load["BA"] = load.HR * (11.0 / 16.2) - if ("KV" not in load or load.KV.isnull().values.all()) and "KV" in countries: + if "XK" not in load and "XK" in countries: if "RS" in load: - load["KV"] = load["RS"] * (4.8 / 27.0) + load["XK"] = load["RS"] * (4.8 / 27.0) copy_timeslice(load, "GR", "2015-08-11 21:00", "2015-08-15 20:00", Delta(weeks=1)) copy_timeslice(load, "AT", "2018-12-31 22:00", "2019-01-01 22:00", Delta(days=2)) @@ -311,8 +311,8 @@ def manual_adjustment(load, fn_load, countries): logger.info("Supplement missing data with synthetic data.") fn = snakemake.input.synthetic synthetic_load = pd.read_csv(fn, index_col=0, parse_dates=True) - # "UA" does not appear in synthetic load data - countries = list(set(countries) - set(["UA", "MD"])) + # UA, MD, XK do not appear in synthetic load data + countries = list(set(countries) - set(["UA", "MD", "XK"])) synthetic_load = synthetic_load.loc[snapshots, countries] load = load.combine_first(synthetic_load) diff --git a/scripts/build_energy_totals.py b/scripts/build_energy_totals.py index 5aefb26b6..2802d8b3b 100644 --- a/scripts/build_energy_totals.py +++ b/scripts/build_energy_totals.py @@ -1065,9 +1065,11 @@ def build_eurostat_co2(eurostat: pd.DataFrame, year: int = 1990) -> pd.Series: specific_emissions = pd.Series(index=eurostat.columns, dtype=float) # emissions in tCO2_equiv per MWh_th - specific_emissions["Solid fuels"] = 0.36 # Approximates coal - specific_emissions["Oil (total)"] = 0.285 # Average of distillate and residue - specific_emissions["Gas"] = 0.2 # For natural gas + specific_emissions["Solid fossil fuels"] = 0.36 # Approximates coal + specific_emissions["Oil and petroleum products"] = ( + 0.285 # Average of distillate and residue + ) + specific_emissions["Natural gas"] = 0.2 # For natural gas return eurostat_year.multiply(specific_emissions).sum(axis=1) @@ -1099,7 +1101,9 @@ def build_co2_totals( co2 = eea_co2.reindex(countries) - for ct in pd.Index(countries).intersection(["BA", "RS", "AL", "ME", "MK"]): + for ct in pd.Index(countries).intersection( + ["BA", "RS", "XK", "AL", "ME", "MK", "UA", "MD"] + ): mappings = { "electricity": (ct, "+", "Electricity & heat generation", np.nan), "residential non-elec": (ct, "+", "+", "Residential"), @@ -1451,10 +1455,10 @@ def update_residential_from_eurostat(energy: pd.DataFrame) -> pd.DataFrame: for nrg_name, (code, siec) in nrg_type.items(): # Select energy balance type, rename columns and countries to match IDEES data, - # convert TJ to TWh, and drop XK data already since included in RS data + # convert TJ to TWh col_to_rename = {"geo": "country", "TIME_PERIOD": "year", "OBS_VALUE": nrg_name} idx_to_rename = {v: k for k, v in idees_rename.items()} - drop_geo = ["EU27_2020", "EA20", "XK"] + drop_geo = ["EU27_2020", "EA20"] nrg_data = eurostat_households.query( "nrg_bal == @code and siec == @siec and geo not in @drop_geo and OBS_VALUE > 0" ).copy() diff --git a/scripts/build_gas_input_locations.py b/scripts/build_gas_input_locations.py index ca43db3c1..0cd114758 100644 --- a/scripts/build_gas_input_locations.py +++ b/scripts/build_gas_input_locations.py @@ -36,15 +36,20 @@ def build_gem_lng_data(fn): "Gran Canaria LNG Terminal", ] + status_list = ["Operating", "Construction"] # noqa: F841 + df = df.query( - "Status != 'Cancelled' \ + "Status in @status_list \ + & FacilityType == 'Import' \ & Country != @remove_country \ & TerminalName != @remove_terminal \ - & CapacityInMtpa != '--'" + & CapacityInMtpa != '--' \ + & CapacityInMtpa != 0" ) geometry = gpd.points_from_xy(df["Longitude"], df["Latitude"]) - return gpd.GeoDataFrame(df, geometry=geometry, crs="EPSG:4326") + gdf = gpd.GeoDataFrame(df, geometry=geometry, crs="EPSG:4326") + return gdf def build_gem_prod_data(fn): @@ -54,8 +59,10 @@ def build_gem_prod_data(fn): remove_country = ["Cyprus", "Türkiye"] # noqa: F841 remove_fuel_type = ["oil"] # noqa: F841 + status_list = ["operating", "in development"] # noqa: F841 + df = df.query( - "Status != 'shut in' \ + "Status in @status_list \ & 'Fuel type' != 'oil' \ & Country != @remove_country \ & ~Latitude.isna() \ @@ -64,7 +71,7 @@ def build_gem_prod_data(fn): p = pd.read_excel(fn, sheet_name="Gas extraction - production") p = p.set_index("GEM Unit ID") - p = p[p["Fuel description"] == "gas"] + p = p[p["Fuel description"].str.contains("gas")] capacities = pd.DataFrame(index=df.index) for key in ["production", "production design capacity", "reserves"]: @@ -85,7 +92,8 @@ def build_gem_prod_data(fn): ) geometry = gpd.points_from_xy(df["Longitude"], df["Latitude"]) - return gpd.GeoDataFrame(df, geometry=geometry, crs="EPSG:4326") + gdf = gpd.GeoDataFrame(df, geometry=geometry, crs="EPSG:4326") + return gdf def build_gas_input_locations(gem_fn, entry_fn, sto_fn, countries): @@ -134,8 +142,7 @@ def build_gas_input_locations(gem_fn, entry_fn, sto_fn, countries): snakemake = mock_snakemake( "build_gas_input_locations", simpl="", - clusters="5", - configfiles="config/test/config.overnight.yaml", + clusters="128", ) configure_logging(snakemake) diff --git a/scripts/build_hydro_profile.py b/scripts/build_hydro_profile.py index 6a0315c73..2d0d2e521 100644 --- a/scripts/build_hydro_profile.py +++ b/scripts/build_hydro_profile.py @@ -82,7 +82,7 @@ def get_eia_annual_hydro_generation(fn, countries, capacities=False): countries=["Czechia", "Slovakia"], start=1980, end=1992 ), "Former Serbia and Montenegro": dict( - countries=["Serbia", "Montenegro"], start=1992, end=2005 + countries=["Serbia", "Montenegro", "Kosovo"], start=1992, end=2005 ), "Former Yugoslavia": dict( countries=[ @@ -90,6 +90,7 @@ def get_eia_annual_hydro_generation(fn, countries, capacities=False): "Croatia", "Bosnia and Herzegovina", "Serbia", + "Kosovo", "Montenegro", "North Macedonia", ], @@ -111,9 +112,8 @@ def get_eia_annual_hydro_generation(fn, countries, capacities=False): ) df.loc["Germany"] = df.filter(like="Germany", axis=0).sum() - df.loc["Serbia"] += df.loc["Kosovo"].fillna(0.0) df = df.loc[~df.index.str.contains("Former")] - df.drop(["Europe", "Germany, West", "Germany, East", "Kosovo"], inplace=True) + df.drop(["Europe", "Germany, West", "Germany, East"], inplace=True) df.index = cc.convert(df.index, to="iso2") df.index.name = "countries" @@ -122,6 +122,8 @@ def get_eia_annual_hydro_generation(fn, countries, capacities=False): factor = 1e3 if capacities else 1e6 df = df.T[countries] * factor + df.ffill(axis=0, inplace=True) + return df @@ -129,7 +131,7 @@ def correct_eia_stats_by_capacity(eia_stats, fn, countries, baseyear=2019): cap = get_eia_annual_hydro_generation(fn, countries, capacities=True) ratio = cap / cap.loc[baseyear] eia_stats_corrected = eia_stats / ratio - to_keep = ["AL", "AT", "CH", "DE", "GB", "NL", "RS", "RO", "SK"] + to_keep = ["AL", "AT", "CH", "DE", "GB", "NL", "RS", "XK", "RO", "SK"] to_correct = eia_stats_corrected.columns.difference(to_keep) eia_stats.loc[:, to_correct] = eia_stats_corrected.loc[:, to_correct] diff --git a/scripts/build_industrial_distribution_key.py b/scripts/build_industrial_distribution_key.py index e66547289..76a0e6d63 100644 --- a/scripts/build_industrial_distribution_key.py +++ b/scripts/build_industrial_distribution_key.py @@ -100,7 +100,7 @@ def prepare_hotmaps_database(regions): """ Load hotmaps database of industrial sites and map onto bus regions. """ - df = pd.read_csv(snakemake.input.hotmaps_industrial_database, sep=";", index_col=0) + df = pd.read_csv(snakemake.input.hotmaps, sep=";", index_col=0) df[["srid", "coordinates"]] = df.geom.str.split(";", expand=True) @@ -133,7 +133,97 @@ def prepare_hotmaps_database(regions): return gdf -def build_nodal_distribution_key(hotmaps, regions, countries): +def prepare_gem_database(regions): + """ + Load GEM database of steel plants and map onto bus regions. + """ + + df = pd.read_excel( + snakemake.input.gem_gspt, + sheet_name="Steel Plants", + na_values=["N/A", "unknown", ">0"], + ).query("Region == 'Europe'") + + df["Retired Date"] = pd.to_numeric( + df["Retired Date"].combine_first(df["Idled Date"]), errors="coerce" + ) + df["Start date"] = pd.to_numeric( + df["Start date"].str.split("-").str[0], errors="coerce" + ) + + latlon = ( + df["Coordinates"] + .str.split(", ", expand=True) + .rename(columns={0: "lat", 1: "lon"}) + ) + geometry = gpd.points_from_xy(latlon["lon"], latlon["lat"]) + gdf = gpd.GeoDataFrame(df, geometry=geometry, crs="EPSG:4326") + + gdf = gpd.sjoin(gdf, regions, how="inner", predicate="within") + + gdf.rename(columns={"name": "bus"}, inplace=True) + gdf["country"] = gdf.bus.str[:2] + + return gdf + + +def prepare_ammonia_database(regions): + """ + Load ammonia database of plants and map onto bus regions. + """ + df = pd.read_csv(snakemake.input.ammonia, index_col=0) + + geometry = gpd.points_from_xy(df.Longitude, df.Latitude) + gdf = gpd.GeoDataFrame(df, geometry=geometry, crs="EPSG:4326") + + gdf = gpd.sjoin(gdf, regions, how="inner", predicate="within") + + gdf.rename(columns={"name": "bus"}, inplace=True) + gdf["country"] = gdf.bus.str[:2] + + return gdf + + +def prepare_cement_supplement(regions): + """ + Load supplementary cement plants from non-EU-(NO-CH) and map onto bus + regions. + """ + + df = pd.read_csv(snakemake.input.cement_supplement, index_col=0) + + geometry = gpd.points_from_xy(df.Longitude, df.Latitude) + gdf = gpd.GeoDataFrame(df, geometry=geometry, crs="EPSG:4326") + + gdf = gpd.sjoin(gdf, regions, how="inner", predicate="within") + + gdf.rename(columns={"name": "bus"}, inplace=True) + gdf["country"] = gdf.bus.str[:2] + + return gdf + + +def prepare_refineries_supplement(regions): + """ + Load supplementary refineries from non-EU-(NO-CH) and map onto bus regions. + """ + + df = pd.read_csv(snakemake.input.refineries_supplement, index_col=0) + + geometry = gpd.points_from_xy(df.Longitude, df.Latitude) + gdf = gpd.GeoDataFrame(df, geometry=geometry, crs="EPSG:4326") + + gdf = gpd.sjoin(gdf, regions, how="inner", predicate="within") + + gdf.rename(columns={"name": "bus"}, inplace=True) + gdf["country"] = gdf.bus.str[:2] + + return gdf + + +def build_nodal_distribution_key( + hotmaps, gem, ammonia, cement, refineries, regions, countries +): """ Build nodal distribution keys for each sector. """ @@ -158,15 +248,137 @@ def build_nodal_distribution_key(hotmaps, regions, countries): if emissions.sum() == 0: key = pd.Series(1 / len(facilities), facilities.index) else: - # BEWARE: this is a strong assumption - emissions = emissions.fillna(emissions.mean()) + # assume 20% quantile for missing values + emissions = emissions.fillna(emissions.quantile(0.2)) key = emissions / emissions.sum() key = key.groupby(facilities.bus).sum().reindex(regions_ct, fill_value=0.0) + elif sector == "Cement" and country in cement.country.unique(): + facilities = cement.query("country == @country") + production = facilities["Cement [kt/a]"] + if production.sum() == 0: + key = pd.Series(1 / len(facilities), facilities.index) + else: + key = production / production.sum() + key = key.groupby(facilities.bus).sum().reindex(regions_ct, fill_value=0.0) + elif sector == "Refineries" and country in refineries.country.unique(): + facilities = refineries.query("country == @country") + production = facilities["Capacity [bbl/day]"] + if production.sum() == 0: + key = pd.Series(1 / len(facilities), facilities.index) + else: + key = production / production.sum() + key = key.groupby(facilities.bus).sum().reindex(regions_ct, fill_value=0.0) else: key = keys.loc[regions_ct, "population"] keys.loc[regions_ct, sector] = key + # add specific steel subsectors + steel_processes = ["EAF", "DRI + EAF", "Integrated steelworks"] + for process, country in product(steel_processes, countries): + regions_ct = regions.index[regions.index.str.contains(country)] + + facilities = gem.query("country == @country") + + if process == "EAF": + status_list = [ + "construction", + "operating", + "operating pre-retirement", + "retired", + ] + capacities = facilities.loc[ + facilities["Capacity operating status"].isin(status_list) + & ( + facilities["Retired Date"].isna() + | facilities["Retired Date"].gt(2025) + ), + "Nominal EAF steel capacity (ttpa)", + ].dropna() + elif process == "DRI + EAF": + status_list = [ + "construction", + "operating", + "operating pre-retirement", + "retired", + "announced", + ] + sel = [ + "Nominal BOF steel capacity (ttpa)", + "Nominal OHF steel capacity (ttpa)", + "Nominal iron capacity (ttpa)", + ] + status_filter = facilities["Capacity operating status"].isin(status_list) + retirement_filter = facilities["Retired Date"].isna() | facilities[ + "Retired Date" + ].gt(2030) + start_filter = ( + facilities["Start date"].isna() + & ~facilities["Capacity operating status"].eq("announced") + ) | facilities["Start date"].le(2030) + capacities = ( + facilities.loc[status_filter & retirement_filter & start_filter, sel] + .sum(axis=1) + .dropna() + ) + elif process == "Integrated steelworks": + status_list = [ + "construction", + "operating", + "operating pre-retirement", + "retired", + ] + sel = [ + "Nominal BOF steel capacity (ttpa)", + "Nominal OHF steel capacity (ttpa)", + ] + capacities = ( + facilities.loc[ + facilities["Capacity operating status"].isin(status_list) + & ( + facilities["Retired Date"].isna() + | facilities["Retired Date"].gt(2025) + ), + sel, + ] + .sum(axis=1) + .dropna() + ) + else: + raise ValueError(f"Unknown process {process}") + + if not capacities.empty: + if capacities.sum() == 0: + key = pd.Series(1 / len(capacities), capacities.index) + else: + key = capacities / capacities.sum() + buses = facilities.loc[capacities.index, "bus"] + key = key.groupby(buses).sum().reindex(regions_ct, fill_value=0.0) + else: + key = keys.loc[regions_ct, "population"] + + keys.loc[regions_ct, process] = key + + # add ammonia + for country in countries: + regions_ct = regions.index[regions.index.str.contains(country)] + + facilities = ammonia.query("country == @country") + + if not facilities.empty: + production = facilities["Ammonia [kt/a]"] + if production.sum() == 0: + key = pd.Series(1 / len(facilities), facilities.index) + else: + # assume 50% of the minimum production for missing values + production = production.fillna(0.5 * facilities["Ammonia [kt/a]"].min()) + key = production / production.sum() + key = key.groupby(facilities.bus).sum().reindex(regions_ct, fill_value=0.0) + else: + key = 0.0 + + keys.loc[regions_ct, "Ammonia"] = key + return keys @@ -188,6 +400,16 @@ def build_nodal_distribution_key(hotmaps, regions, countries): hotmaps = prepare_hotmaps_database(regions) - keys = build_nodal_distribution_key(hotmaps, regions, countries) + gem = prepare_gem_database(regions) + + ammonia = prepare_ammonia_database(regions) + + cement = prepare_cement_supplement(regions) + + refineries = prepare_refineries_supplement(regions) + + keys = build_nodal_distribution_key( + hotmaps, gem, ammonia, cement, refineries, regions, countries + ) keys.to_csv(snakemake.output.industrial_distribution_key) diff --git a/scripts/build_industrial_energy_demand_per_node_today.py b/scripts/build_industrial_energy_demand_per_node_today.py index aa9e9dffa..fd7c281c8 100644 --- a/scripts/build_industrial_energy_demand_per_node_today.py +++ b/scripts/build_industrial_energy_demand_per_node_today.py @@ -33,10 +33,10 @@ # map JRC/our sectors to hotmaps sector, where mapping exist sector_mapping = { - "Electric arc": "Iron and steel", - "Integrated steelworks": "Iron and steel", - "DRI + Electric arc": "Iron and steel", - "Ammonia": "Chemical industry", + "Electric arc": "EAF", + "Integrated steelworks": "Integrated steelworks", + "DRI + Electric arc": "DRI + EAF", + "Ammonia": "Ammonia", "Basic chemicals (without ammonia)": "Chemical industry", "Other chemicals": "Chemical industry", "Pharmaceutical products etc.": "Chemical industry", diff --git a/scripts/build_industrial_production_per_country.py b/scripts/build_industrial_production_per_country.py index ccb4feca6..b7f98d760 100644 --- a/scripts/build_industrial_production_per_country.py +++ b/scripts/build_industrial_production_per_country.py @@ -345,5 +345,7 @@ def separate_basic_chemicals(demand, year): separate_basic_chemicals(demand, year) + demand.fillna(0.0, inplace=True) + fn = snakemake.output.industrial_production_per_country demand.to_csv(fn, float_format="%.2f") diff --git a/scripts/build_industrial_production_per_node.py b/scripts/build_industrial_production_per_node.py index d3edfa45b..862345e57 100644 --- a/scripts/build_industrial_production_per_node.py +++ b/scripts/build_industrial_production_per_node.py @@ -32,10 +32,10 @@ # map JRC/our sectors to hotmaps sector, where mapping exist sector_mapping = { - "Electric arc": "Iron and steel", - "Integrated steelworks": "Iron and steel", - "DRI + Electric arc": "Iron and steel", - "Ammonia": "Chemical industry", + "Electric arc": "EAF", + "Integrated steelworks": "Integrated steelworks", + "DRI + Electric arc": "DRI + EAF", + "Ammonia": "Ammonia", "HVC": "Chemical industry", "HVC (mechanical recycling)": "Chemical industry", "HVC (chemical recycling)": "Chemical industry", diff --git a/scripts/build_population_layouts.py b/scripts/build_population_layouts.py index ca664ed0a..b9af8d850 100644 --- a/scripts/build_population_layouts.py +++ b/scripts/build_population_layouts.py @@ -9,6 +9,7 @@ import logging import atlite +import country_converter as coco import geopandas as gpd import numpy as np import pandas as pd @@ -17,6 +18,8 @@ logger = logging.getLogger(__name__) +cc = coco.CountryConverter() + if __name__ == "__main__": if "snakemake" not in globals(): from _helpers import mock_snakemake @@ -27,6 +30,7 @@ configure_logging(snakemake) set_scenario_config(snakemake) + coco.logging.getLogger().setLevel(coco.logging.CRITICAL) cutout = atlite.Cutout(snakemake.input.cutout) @@ -45,19 +49,14 @@ countries = np.sort(nuts3.country.unique()) + urban_fraction = pd.read_csv(snakemake.input.urban_percent, skiprows=4) + iso3 = urban_fraction["Country Code"] + urban_fraction["iso2"] = cc.convert(names=iso3, src="ISO3", to="ISO2") urban_fraction = ( - pd.read_csv( - snakemake.input.urban_percent, header=None, index_col=0, names=["fraction"] - ).squeeze() - / 100.0 + urban_fraction.query("iso2 in @countries").set_index("iso2")["2019"].div(100) ) - - # fill missing Balkans values - missing = ["AL", "ME", "MK"] - reference = ["RS", "BA"] - average = urban_fraction[reference].mean() - fill_values = pd.Series({ct: average for ct in missing}) - urban_fraction = pd.concat([urban_fraction, fill_values]) + if "XK" in countries: + urban_fraction["XK"] = urban_fraction["RS"] # population in each grid cell pop_cells = pd.Series(I.dot(nuts3["pop"])) diff --git a/scripts/build_powerplants.py b/scripts/build_powerplants.py index bde2bd38c..47cf2e141 100755 --- a/scripts/build_powerplants.py +++ b/scripts/build_powerplants.py @@ -206,12 +206,11 @@ def replace_natural_gas_fueltype(df): # Add "everywhere powerplants" to all bus locations ppl = add_everywhere_powerplants( - ppl, n.buses.query("substation_lv"), snakemake.params.everywhere_powerplants + ppl, n.buses, snakemake.params.everywhere_powerplants ) - substations = n.buses.query("substation_lv") ppl = ppl.dropna(subset=["lat", "lon"]) - ppl = map_country_bus(ppl, substations) + ppl = map_country_bus(ppl, n.buses) bus_null_b = ppl["bus"].isnull() if bus_null_b.any(): diff --git a/scripts/build_retro_cost.py b/scripts/build_retro_cost.py index c6dd31cf9..42144c1aa 100755 --- a/scripts/build_retro_cost.py +++ b/scripts/build_retro_cost.py @@ -198,6 +198,7 @@ def prepare_building_stock_data(): "Iceland": "IS", "Montenegro": "ME", "Serbia": "RS", + "Kosovo": "XK", "Albania": "AL", "United Kingdom": "GB", "Bosnia and Herzegovina": "BA", @@ -1073,6 +1074,7 @@ def sample_dE_costs_area( "AL": ["BG", "RO", "GR"], "BA": ["HR"], "RS": ["BG", "RO", "HR", "HU"], + "KV": ["RS"], "MK": ["BG", "GR"], "ME": ["BA", "AL", "RS", "HR"], "CH": ["SE", "DE"], diff --git a/scripts/build_shapes.py b/scripts/build_shapes.py index 4de5370a7..2370f2aef 100644 --- a/scripts/build_shapes.py +++ b/scripts/build_shapes.py @@ -106,8 +106,6 @@ def _simplify_polys(polys, minarea=0.1, tolerance=None, filterremote=True): def countries(naturalearth, country_list): - if "RS" in country_list: - country_list.append("KV") df = gpd.read_file(naturalearth) @@ -116,15 +114,12 @@ def countries(naturalearth, country_list): df[x].where(lambda s: s != "-99") for x in ("ISO_A2", "WB_A2", "ADM0_A3") ) df["name"] = reduce(lambda x, y: x.fillna(y), fieldnames, next(fieldnames)).str[:2] + df.replace({"name": {"KV": "XK"}}, inplace=True) df = df.loc[ df.name.isin(country_list) & ((df["scalerank"] == 0) | (df["scalerank"] == 5)) ] s = df.set_index("name")["geometry"].map(_simplify_polys).set_crs(df.crs) - if "RS" in country_list: - s["RS"] = s["RS"].union(s.pop("KV")) - # cleanup shape union - s["RS"] = Polygon(s["RS"].exterior.coords) return s @@ -195,7 +190,9 @@ def nuts3(country_shapes, nuts3, nuts3pop, nuts3gdp, ch_cantons, ch_popgdp): df = df.join(pd.DataFrame(dict(pop=pop, gdp=gdp))) - df["country"] = df.index.to_series().str[:2].replace(dict(UK="GB", EL="GR")) + df["country"] = ( + df.index.to_series().str[:2].replace(dict(UK="GB", EL="GR", KV="XK")) + ) excludenuts = pd.Index( ( @@ -217,13 +214,18 @@ def nuts3(country_shapes, nuts3, nuts3pop, nuts3gdp, ch_cantons, ch_popgdp): "FR9", ) ) - excludecountry = pd.Index(("MT", "TR", "LI", "IS", "CY", "KV")) + excludecountry = pd.Index(("MT", "TR", "LI", "IS", "CY")) df = df.loc[df.index.difference(excludenuts)] df = df.loc[~df.country.isin(excludecountry)] manual = gpd.GeoDataFrame( - [["BA1", "BA", 3871.0], ["RS1", "RS", 7210.0], ["AL1", "AL", 2893.0]], + [ + ["BA1", "BA", 3234.0], + ["RS1", "RS", 6664.0], + ["AL1", "AL", 2778.0], + ["XK1", "XK", 1587.0], + ], columns=["NUTS_ID", "country", "pop"], geometry=gpd.GeoSeries(), crs=df.crs, @@ -234,7 +236,7 @@ def nuts3(country_shapes, nuts3, nuts3pop, nuts3gdp, ch_cantons, ch_popgdp): df = pd.concat([df, manual], sort=False) - df.loc["ME000", "pop"] = 650.0 + df.loc["ME000", "pop"] = 617.0 return df diff --git a/scripts/cluster_network.py b/scripts/cluster_network.py index b63747c24..4460b0adb 100644 --- a/scripts/cluster_network.py +++ b/scripts/cluster_network.py @@ -557,12 +557,12 @@ def plot_busmap_for_n_clusters(n, n_clusters, solver_name="scip", fn=None): ): # also available: linemap_positive, linemap_negative getattr(clustering, attr).to_csv(snakemake.output[attr]) - nc.shapes = n.shapes.copy() + # nc.shapes = n.shapes.copy() for which in ["regions_onshore", "regions_offshore"]: regions = gpd.read_file(snakemake.input[which]) clustered_regions = cluster_regions((clustering.busmap,), regions) clustered_regions.to_file(snakemake.output[which]) - append_bus_shapes(nc, clustered_regions, type=which.split("_")[1]) + # append_bus_shapes(nc, clustered_regions, type=which.split("_")[1]) nc.meta = dict(snakemake.config, **dict(wildcards=dict(snakemake.wildcards))) nc.export_to_netcdf(snakemake.output.network) diff --git a/scripts/prepare_sector_network.py b/scripts/prepare_sector_network.py index b6644d6dc..9d099e073 100644 --- a/scripts/prepare_sector_network.py +++ b/scripts/prepare_sector_network.py @@ -281,7 +281,7 @@ def co2_emissions_year( eurostat = build_eurostat(input_eurostat, countries) - # this only affects the estimation of CO2 emissions for BA, RS, AL, ME, MK + # this only affects the estimation of CO2 emissions for BA, RS, AL, ME, MK, XK eurostat_co2 = build_eurostat_co2(eurostat, year) co2_totals = build_co2_totals(countries, eea_co2, eurostat_co2) @@ -2324,8 +2324,7 @@ def add_biomass(n, costs): if ( options["municipal_solid_waste"] and not options["industry"] - and cf_industry["waste_to_energy"] - or cf_industry["waste_to_energy_cc"] + and (cf_industry["waste_to_energy"] or cf_industry["waste_to_energy_cc"]) ): logger.warning( "Flag municipal_solid_waste can be only used with industry " @@ -2345,7 +2344,12 @@ def add_biomass(n, costs): carrier="municipal solid waste", ) - e_max_pu = pd.Series([1] * (len(n.snapshots) - 1) + [0], index=n.snapshots) + e_max_pu = np.array( + len(spatial.msw.nodes) * [[1] * (len(n.snapshots) - 1) + [0]] + ).T + e_max_pu = pd.DataFrame( + e_max_pu, index=n.snapshots, columns=spatial.msw.nodes + ).astype(float) n.madd( "Store", spatial.msw.nodes, @@ -2442,7 +2446,7 @@ def add_biomass(n, costs): ) if biomass_potentials.filter(like="unsustainable").sum().sum() > 0: - + add_carrier_buses(n, "oil") # Create timeseries to force usage of unsustainable potentials e_max_pu = pd.DataFrame(1, index=n.snapshots, columns=spatial.gas.biogas) e_max_pu.iloc[-1] = 0 @@ -2590,13 +2594,15 @@ def add_biomass(n, costs): if options["municipal_solid_waste"]: n.madd( "Link", - biomass_transport.index, - bus0=biomass_transport.bus0 + " municipal solid waste", - bus1=biomass_transport.bus1 + " municipal solid waste", + biomass_transport.index + " municipal solid waste", + bus0=biomass_transport.bus0.values + " municipal solid waste", + bus1=biomass_transport.bus1.values + " municipal solid waste", p_nom_extendable=False, p_nom=5e4, length=biomass_transport.length.values, - marginal_cost=biomass_transport.costs * biomass_transport.length.values, + marginal_cost=( + biomass_transport.costs * biomass_transport.length + ).values, carrier="municipal solid waste transport", ) @@ -2726,6 +2732,7 @@ def add_biomass(n, costs): # Solid biomass to liquid fuel if options["biomass_to_liquid"]: + add_carrier_buses(n, "oil") n.madd( "Link", spatial.biomass.nodes, @@ -2769,7 +2776,7 @@ def add_biomass(n, costs): # Combination of efuels and biomass to liquid, both based on Fischer-Tropsch. # Experimental version - use with caution if options["electrobiofuels"]: - + add_carrier_buses(n, "oil") efuel_scale_factor = costs.at["BtL", "C stored"] name = ( pd.Index(spatial.biomass.nodes) @@ -2881,6 +2888,10 @@ def add_biomass(n, costs): def add_industry(n, costs): logger.info("Add industrial demand") + # add oil buses for shipping, aviation and naptha for industry + add_carrier_buses(n, "oil") + # add methanol buses for industry + add_carrier_buses(n, "methanol") nodes = pop_layout.index nhours = n.snapshot_weightings.generators.sum() @@ -3016,8 +3027,6 @@ def add_industry(n, costs): # methanol for industry - add_carrier_buses(n, "methanol") - n.madd( "Bus", spatial.methanol.industry, @@ -3189,8 +3198,6 @@ def add_industry(n, costs): if shipping_oil_share: - add_carrier_buses(n, "oil") - p_set_oil = shipping_oil_share * p_set.rename(lambda x: x + " shipping oil") if not options["regional_oil_demand"]: diff --git a/scripts/simplify_network.py b/scripts/simplify_network.py index 741fd324c..e95891203 100644 --- a/scripts/simplify_network.py +++ b/scripts/simplify_network.py @@ -124,7 +124,6 @@ def simplify_network_to_380(n, linetype_380): n.buses["v_nom"] = 380.0 - linetype_380 = n.lines["type"].mode()[0] n.lines["type"] = linetype_380 n.lines["v_nom"] = 380 n.lines["i_nom"] = n.line_types.i_nom[linetype_380] @@ -670,7 +669,7 @@ def find_closest_bus(n, x, y, tol=2000): n.lines.drop(remove, axis=1, errors="ignore", inplace=True) if snakemake.wildcards.simpl: - shapes = n.shapes + # shapes = n.shapes n, cluster_map = cluster( n, int(snakemake.wildcards.simpl), @@ -680,7 +679,7 @@ def find_closest_bus(n, x, y, tol=2000): params.simplify_network["feature"], params.aggregation_strategies, ) - n.shapes = shapes + # n.shapes = shapes busmaps.append(cluster_map) update_p_nom_max(n) @@ -692,7 +691,7 @@ def find_closest_bus(n, x, y, tol=2000): regions = gpd.read_file(snakemake.input[which]) clustered_regions = cluster_regions(busmaps, regions) clustered_regions.to_file(snakemake.output[which]) - append_bus_shapes(n, clustered_regions, type=which.split("_")[1]) + # append_bus_shapes(n, clustered_regions, type=which.split("_")[1]) n.meta = dict(snakemake.config, **dict(wildcards=dict(snakemake.wildcards))) n.export_to_netcdf(snakemake.output.network)