├── input ├── .DS_Store ├── cop │ └── cop_parameters.csv ├── heating_thresholds │ └── heating_thresholds.csv ├── bgw_bdew │ ├── daily_demand.csv │ ├── hourly_factors_MFH.csv │ ├── hourly_factors_SFH.csv │ └── hourly_factors_COM.csv └── notation.csv ├── checksums.txt ├── environment.yml ├── README.md ├── LICENSE.md ├── scripts ├── misc.py ├── write.py ├── download.py ├── read.py ├── preprocess.py ├── cop.py ├── demand.py └── metadata.py ├── preprocessing └── preprocessing_JRC_IDEES.ipynb └── main.ipynb /input/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oruhnau/when2heat/HEAD/input/.DS_Store -------------------------------------------------------------------------------- /input/cop/cop_parameters.csv: -------------------------------------------------------------------------------- 1 | Coefficient;air;ground;water 2 | 0;6,08;10,29;9,99 3 | 1;-0,0941;-0,2084;-0,2049 4 | 2;0,000464;0,001322;0,001249 5 | -------------------------------------------------------------------------------- /checksums.txt: -------------------------------------------------------------------------------- 1 | when2heat.csv,28feacd386db46959527eae534e79338 2 | when2heat.sqlite,8dbd2b2e699fb9dfadbe610663c58a7a 3 | when2heat.xlsx,befe64ab9006704827b4b23e81b49569 4 | when2heat_multiindex.csv,3516c32f0b029f9ef684a7a689606c19 5 | when2heat_stacked.csv,e30b38420e6a7e8d8e01952bdf611987 6 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: when2heat 2 | channels: 3 | - defaults 4 | - conda-forge 5 | dependencies: 6 | - geopandas=0.9 7 | - jupyter=1.0 8 | - netcdf4=1.5 9 | - pandas=1.3 10 | - pip 11 | - python=3.7 12 | - pyyaml 13 | - pip: 14 | - openpyxl 15 | - cdsapi==0.5.1 16 | 17 | -------------------------------------------------------------------------------- /input/heating_thresholds/heating_thresholds.csv: -------------------------------------------------------------------------------- 1 | ;Heating threshold 2 | AT;14,59 3 | BE;15,2 4 | BG;16,02 5 | CZ;14,8 6 | CH;12 7 | DE;13,98 8 | DK;15,2 9 | EE;11,12 10 | ES;18,47 11 | FI;13,16 12 | FR;15,61 13 | GB;14,18 14 | GR;16,84 15 | HR;18,67 16 | HU;16,84 17 | IE;12,76 18 | IT;15,61 19 | LT;15,2 20 | LU;13,98 21 | LV;12,96 22 | NL;13,98 23 | NO;11,53 24 | PL;15,2 25 | PT;18,47 26 | RO;15,41 27 | SE;13,16 28 | SI;15,41 29 | SK;14,18 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # When2Heat data package 2 | 3 | This repository contains scripts that compile heat demand and COP time series for European countries. 4 | 5 | See the [main Jupter notebook](main.ipynb) for further details. 6 | 7 | ## Preparation 8 | 9 | To work on the Notebooks locally see the installation instructions in the 10 | [wiki](https://github.com/Open-Power-System-Data/common/wiki/Tutorial-to-run-OPSD-scripts). 11 | Note that the requirements can be found in the environment.yml file. 12 | 13 | ## License 14 | 15 | This repository is published under the [MIT License](LICENSE.md). 16 | -------------------------------------------------------------------------------- /input/bgw_bdew/daily_demand.csv: -------------------------------------------------------------------------------- 1 | building_type;SFH;MFH;COM;SFH;MFH;COM;SFH_water 2 | windiness;normal;normal;normal;windy;windy;windy;any 3 | A;1,6209544;1,2328655;1,3010623;1,3819663;1,0443538;1,25696; 4 | B;-37,1833141;-34,7213605;-35,6816144;-37,4124155;-35,0333754;-36,6078453; 5 | C;5,6727847;5,8164304;6,6857976;6,1723179;6,2240634;7,321187; 6 | D;0,0716431;0,0873352;0,1409267;0,0396284;0,0502917;0,077696; 7 | m_s;-0,04957;-0,0409284;-0,0473428;-0,0672159;-0,053583;-0,0696826; 8 | b_s;0,8401015;0,767292;0,8141691;1,1167138;0,9995901;1,1379702; 9 | m_w;-0,002209;-0,002232;-0,0010601;-0,0019982;-0,0021758;-0,0008522;-0,002209 10 | b_w;0,1074468;0,1199207;0,1325092;0,135507;0,1633299;0,1921068;0,1790899 11 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Oliver Ruhnau 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /input/bgw_bdew/hourly_factors_MFH.csv: -------------------------------------------------------------------------------- 1 | ;-15;-10;-5;0;5;10;15;20;25;30 2 | 00:00;0,0299;0,0298;0,0291;0,0258;0,0235;0,0225;0,0221;0,0222;0,0246;0,0246 3 | 01:00;0,0296;0,0294;0,0288;0,0266;0,025;0,0247;0,0215;0,022;0,0255;0,0255 4 | 02:00;0,0294;0,0292;0,0285;0,027;0,0254;0,0235;0,0208;0,0207;0,0247;0,0247 5 | 03:00;0,0304;0,0302;0,0296;0,0291;0,0274;0,0258;0,0231;0,0223;0,0237;0,0237 6 | 04:00;0,0355;0,0355;0,0352;0,0351;0,0339;0,0346;0,0343;0,037;0,038;0,038 7 | 05:00;0,0543;0,0543;0,055;0,051;0,0506;0,0513;0,0526;0,0548;0,0519;0,0519 8 | 06:00;0,0549;0,0525;0,0527;0,0505;0,0504;0,0524;0,0605;0,0602;0,044;0,044 9 | 07:00;0,0459;0,0462;0,0465;0,0478;0,049;0,0507;0,055;0,057;0,0555;0,0555 10 | 08:00;0,0459;0,0461;0,0463;0,0487;0,0485;0,0494;0,0521;0,0517;0,0496;0,0496 11 | 09:00;0,0447;0,0449;0,0451;0,0479;0,0475;0,0467;0,0487;0,0507;0,0504;0,0504 12 | 10:00;0,0423;0,0425;0,0426;0,0464;0,0462;0,045;0,0463;0,0474;0,0478;0,0478 13 | 11:00;0,0436;0,0438;0,0439;0,0452;0,0446;0,0434;0,0439;0,0452;0,0464;0,0464 14 | 12:00;0,0427;0,0429;0,043;0,0448;0,0442;0,0426;0,0429;0,0449;0,0466;0,0466 15 | 13:00;0,0423;0,0424;0,0424;0,0438;0,0441;0,042;0,0416;0,0409;0,0428;0,0428 16 | 14:00;0,0419;0,0421;0,0421;0,0437;0,0443;0,042;0,0401;0,0404;0,0439;0,0439 17 | 15:00;0,043;0,0432;0,0432;0,0441;0,0447;0,0432;0,0404;0,0385;0,0398;0,0398 18 | 16:00;0,0436;0,0438;0,0439;0,0459;0,0466;0,0455;0,0415;0,0391;0,0392;0,0392 19 | 17:00;0,0458;0,046;0,0462;0,0473;0,0482;0,0478;0,0436;0,041;0,042;0,042 20 | 18:00;0,0453;0,0457;0,0458;0,0479;0,0492;0,0499;0,047;0,0444;0,0442;0,0442 21 | 19:00;0,0463;0,0466;0,0469;0,0476;0,0491;0,0513;0,0505;0,0474;0,0455;0,0455 22 | 20:00;0,0455;0,0458;0,0461;0,0461;0,048;0,0509;0,0518;0,05;0,0496;0,0496 23 | 21:00;0,0432;0,0434;0,0435;0,0423;0,0439;0,0465;0,0496;0,0494;0,0481;0,0481 24 | 22:00;0,0417;0,0418;0,0419;0,0377;0,0379;0,0393;0,0409;0,0419;0,0437;0,0437 25 | 23:00;0,0321;0,0319;0,0315;0,0277;0,0278;0,0289;0,0292;0,0307;0,0326;0,0326 26 | -------------------------------------------------------------------------------- /input/bgw_bdew/hourly_factors_SFH.csv: -------------------------------------------------------------------------------- 1 | ;-15;-10;-5;0;5;10;15;20;25;30 2 | 00:00;0,0296;0,0292;0,0281;0,0262;0,023;0,0196;0,0142;0,0096;0,0045;0,0045 3 | 01:00;0,0294;0,0289;0,0279;0,0266;0,0237;0,0208;0,0155;0,0105;0,0054;0,0054 4 | 02:00;0,03;0,0296;0,0286;0,0274;0,0243;0,0217;0,0167;0,0112;0,0044;0,0044 5 | 03:00;0,0307;0,0303;0,0294;0,0289;0,0262;0,0249;0,02;0,0162;0,0081;0,0081 6 | 04:00;0,0321;0,0318;0,031;0,0333;0,0312;0,0326;0,0298;0,0289;0,0191;0,0191 7 | 05:00;0,0381;0,038;0,0377;0,043;0,0448;0,0483;0,0541;0,0662;0,0601;0,0601 8 | 06:00;0,0577;0,0573;0,0602;0,0551;0,0577;0,0619;0,0685;0,0821;0,0939;0,0939 9 | 07:00;0,0525;0,053;0,0537;0,051;0,0537;0,0578;0,0679;0,0757;0,0809;0,0809 10 | 08:00;0,0498;0,0501;0,0507;0,0497;0,051;0,0527;0,0578;0,0609;0,0614;0,0614 11 | 09:00;0,0476;0,0479;0,0483;0,0482;0,0488;0,0486;0,0527;0,0601;0,0587;0,0587 12 | 10:00;0,0466;0,0469;0,0472;0,0467;0,0465;0,0452;0,0478;0,0513;0,0533;0,0533 13 | 11:00;0,0437;0,0438;0,0438;0,0446;0,0448;0,043;0,0435;0,0483;0,0541;0,0541 14 | 12:00;0,0423;0,0424;0,0424;0,0439;0,0438;0,0424;0,0415;0,0442;0,0521;0,0521 15 | 13:00;0,0422;0,0422;0,0422;0,0435;0,0437;0,0422;0,0402;0,0397;0,042;0,042 16 | 14:00;0,0418;0,0418;0,0418;0,0448;0,0446;0,0433;0,0399;0,0378;0,0403;0,0403 17 | 15:00;0,0438;0,0439;0,0439;0,0456;0,0458;0,0452;0,0414;0,0359;0,036;0,036 18 | 16:00;0,0472;0,0475;0,0478;0,0481;0,0481;0,0478;0,0441;0,04;0,0423;0,0423 19 | 17:00;0,0482;0,0485;0,0489;0,0489;0,0501;0,0509;0,0477;0,0428;0,0498;0,0498 20 | 18:00;0,0472;0,0475;0,0478;0,0491;0,0506;0,0523;0,0515;0,0456;0,0482;0,0482 21 | 19:00;0,0469;0,0471;0,0475;0,0484;0,0505;0,0527;0,0552;0,0513;0,0508;0,0508 22 | 20:00;0,0461;0,0463;0,0465;0,0466;0,0485;0,0504;0,0535;0,0519;0,0526;0,0526 23 | 21:00;0,0423;0,0424;0,0424;0,0414;0,043;0,044;0,0474;0,0442;0,045;0,045 24 | 22:00;0,0345;0,0343;0,0337;0,0318;0,0309;0,0301;0,0315;0,0309;0,0262;0,0262 25 | 23:00;0,0298;0,0294;0,0284;0,0272;0,0246;0,0218;0,0177;0,0147;0,0108;0,0108 26 | -------------------------------------------------------------------------------- /scripts/misc.py: -------------------------------------------------------------------------------- 1 | 2 | import pytz 3 | import pandas as pd 4 | 5 | 6 | def localize(df, country, ambiguous=None): 7 | 8 | # The exceptions below correct for daylight saving time 9 | try: 10 | df.index = df.index.tz_localize(pytz.country_timezones[country][0], ambiguous=ambiguous) 11 | return df 12 | 13 | # Delete values that do not exist because of daylight saving time 14 | except pytz.NonExistentTimeError as err: 15 | return localize(df.loc[df.index != err.args[0], ], country) 16 | 17 | # Duplicate values that exist twice because of daylight saving time 18 | except pytz.AmbiguousTimeError as err: 19 | idx = pd.Timestamp(err.args[0].split("from ")[1].split(",")[0]) 20 | unambiguous_df = localize(df.loc[df.index != idx, ], country) 21 | ambiguous_df = localize(df.loc[[idx, idx], ], country, ambiguous=[True, False]) 22 | return unambiguous_df.append(ambiguous_df).sort_index() 23 | 24 | 25 | def upsample_df(df, resolution): 26 | 27 | # The low-resolution values are applied to all high-resolution values up to the next low-resolution value 28 | # In particular, the last low-resolution value is extended up to where the next low-resolution value would be 29 | 30 | df = df.copy() 31 | 32 | # Determine the original frequency 33 | freq = df.index[-1] - df.index[-2] 34 | 35 | # Temporally append the DataFrame by one low-resolution value 36 | df.loc[df.index[-1] + freq, :] = df.iloc[-1, :] 37 | 38 | # Up-sample 39 | df = df.resample(resolution).pad() 40 | 41 | # Drop the temporal low-resolution value 42 | df.drop(df.index[-1], inplace=True) 43 | 44 | return df 45 | 46 | 47 | def group_df_by_multiple_column_levels(df, column_levels): 48 | 49 | df = df.groupby(df.columns.droplevel(list(set(df.columns.names) - set(column_levels))), axis=1).sum() 50 | df.columns = pd.MultiIndex.from_tuples(df.columns, names=column_levels) 51 | 52 | return df 53 | -------------------------------------------------------------------------------- /input/notation.csv: -------------------------------------------------------------------------------- 1 | country,variable,attribute,description 2 | ISO-2 digit country code,heat_demand,total,Heat demand for space and water heating 3 | ISO-2 digit country code,heat_demand,space,Heat demand for space heating 4 | ISO-2 digit country code,heat_demand,water,Heat demand for water heating 5 | ISO-2 digit country code,heat_demand,space_SFH,Heat demand for space heating in single-family houses 6 | ISO-2 digit country code,heat_demand,space_MFH,Heat demand for space heating in multi-family houses 7 | ISO-2 digit country code,heat_demand,space_COM,Heat demand for space heating in commercial buildings 8 | ISO-2 digit country code,heat_demand,water_SFH,Heat demand for water heating in single-family houses 9 | ISO-2 digit country code,heat_demand,water_MFH,Heat demand for water heating in multi-family houses 10 | ISO-2 digit country code,heat_demand,water_COM,Heat demand for water heating in commercial buildings 11 | ISO-2 digit country code,heat_profile,space_SFH,Normalized heat demand for space heating in single-family houses 12 | ISO-2 digit country code,heat_profile,space_MFH,Normalized heat demand for space heating in multi-family houses 13 | ISO-2 digit country code,heat_profile,space_COM,Normalized heat demand for space heating in commercial buildings 14 | ISO-2 digit country code,heat_profile,water_SFH,Normalized heat demand for water heating in single-family houses 15 | ISO-2 digit country code,heat_profile,water_MFH,Normalized heat demand for water heating in multi-family houses 16 | ISO-2 digit country code,heat_profile,water_COM,Normalized heat demand for water heating in commercial buildings 17 | ISO-2 digit country code,COP,ASHP_floor,COP of air-source heat pumps with floor heating 18 | ISO-2 digit country code,COP,ASHP_radiator,COP of air-source heat pumps with radiator heating 19 | ISO-2 digit country code,COP,ASHP_water,COP of air-source heat pumps with water heating 20 | ISO-2 digit country code,COP,GSHP_floor,COP of ground-source heat pumps with floor heating 21 | ISO-2 digit country code,COP,GSHP_radiator,COP of ground-source heat pumps with radiator heating 22 | ISO-2 digit country code,COP,GSHP_water,COP of ground-source heat pumps with water heating 23 | ISO-2 digit country code,COP,WSHP_floor,COP of groundwater-source heat pumps with floor heating 24 | ISO-2 digit country code,COP,WSHP_radiator,COP of groundwater-source heat pumps with radiator heating 25 | ISO-2 digit country code,COP,WSHP_water,COP of groundwater-source heat pumps with water heating -------------------------------------------------------------------------------- /scripts/write.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import sqlite3 4 | import pandas as pd 5 | 6 | 7 | def shaping(demand, cop): 8 | print("index:") 9 | 10 | # Merge demand and cop 11 | df = pd.concat([demand, cop], axis=1) 12 | 13 | df = df.sort_index(level=0, axis=1) 14 | 15 | # Timestamp 16 | index = pd.DatetimeIndex(df.index) 17 | df.index = pd.MultiIndex.from_tuples(zip( 18 | index.strftime('%Y-%m-%dT%H:%M:%SZ'), 19 | index.tz_convert('Europe/Brussels').strftime('%Y-%m-%dT%H:%M:%S%z') 20 | )) 21 | df.index.names = ['utc_timestamp', 'cet_cest_timestamp'] 22 | 23 | # SingleIndex 24 | single = df.copy() 25 | single.columns = ['_'.join([level for level in col_name[0:3]]) for col_name in df.columns.values] 26 | single.insert(0, 'cet_cest_timestamp', single.index.get_level_values(1)) 27 | single.index = single.index.droplevel(['cet_cest_timestamp']) 28 | 29 | # Stacked 30 | stacked = df.copy() 31 | stacked.index = stacked.index.droplevel(['cet_cest_timestamp']) 32 | stacked.columns = stacked.columns.droplevel(['unit']) 33 | stacked = stacked.transpose().stack(dropna=True).to_frame(name='data') 34 | 35 | # Excel 36 | df_excel = df.copy() 37 | df_excel.index = pd.MultiIndex.from_tuples(zip( 38 | index.strftime('%Y-%m-%dT%H:%M:%SZ'), 39 | index.tz_convert('Europe/Brussels').strftime('%Y-%m-%dT%H:%M:%S') 40 | )) 41 | 42 | return { 43 | 'multiindex': df, 44 | 'singleindex': single, 45 | 'stacked': stacked, 46 | 'excel': df_excel 47 | } 48 | 49 | 50 | def to_sql(shaped_dfs, output_path, home_path): 51 | 52 | os.chdir(output_path) 53 | table = 'when2heat' 54 | shaped_dfs['singleindex'].to_sql(table, sqlite3.connect('when2heat.sqlite'), 55 | if_exists='replace', index_label='utc_timestamp') 56 | os.chdir(home_path) 57 | 58 | 59 | def to_csv(shaped_dfs, output_path): 60 | 61 | for shape, df in shaped_dfs.items(): 62 | 63 | if shape == 'excel': 64 | file = os.path.join(output_path, 'when2heat.xlsx.csv') 65 | df.to_csv(file, sep=';', decimal=',', float_format='%g') 66 | 67 | elif shape == 'singleindex': 68 | file = os.path.join(output_path, 'when2heat.csv') 69 | df.to_csv(file, sep=';', decimal=',', float_format='%g') 70 | 71 | else: 72 | file = os.path.join(output_path, 'when2heat_{}.csv'.format(shape)) 73 | df.to_csv(file, float_format='%g') 74 | -------------------------------------------------------------------------------- /scripts/download.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import ssl 4 | import datetime 5 | import urllib 6 | import zipfile 7 | import cdsapi 8 | from IPython.display import clear_output 9 | 10 | 11 | 12 | def wind(input_path): 13 | filename = 'ERA_wind.nc' 14 | weather_path = os.path.join(input_path, 'weather') 15 | os.makedirs(weather_path, exist_ok=True) 16 | file = os.path.join(weather_path, filename) 17 | 18 | if not os.path.isfile(file): 19 | 20 | # Select all months from 1979 to 2021 by the date of the first day of the month 21 | data_package = 'reanalysis-era5-single-levels-monthly-means' 22 | variable = "10m_wind_speed" 23 | product_type = 'monthly_averaged_reanalysis' 24 | dates = { 25 | 'year': [str(year) for year in range(1979, 2022)], 26 | 'month': ["%.2d" % month for month in range(1, 13)], 27 | 'time': [datetime.time(i).strftime('%H:%M') for i in range(1)] 28 | } 29 | 30 | # Call the general weather download function with wind specific parameters 31 | weather(data_package, variable, dates, product_type, file) 32 | 33 | else: 34 | print('{} already exists. Download is skipped.'.format(file)) 35 | 36 | clear_output(wait=False) 37 | print("Download successful") 38 | 39 | 40 | def temperatures(input_path, year_start, year_end): 41 | 42 | for year in ["%.2d" % y for y in range(year_start, year_end+1)]: 43 | for variable in ['2m_temperature', 'soil_temperature_level_1']: 44 | 45 | filename = 'ERA_temperature_{}_{}.nc'.format(variable, year) 46 | weather_path = os.path.join(input_path, 'weather') 47 | os.makedirs(weather_path, exist_ok=True) 48 | file = os.path.join(weather_path, filename) 49 | 50 | if not os.path.isfile(file): 51 | #Select period 52 | data_package = 'reanalysis-era5-single-levels' 53 | variable = variable 54 | product_type = 'reanalysis' 55 | dates = { 56 | 'year': year, 57 | 'month': ["%.2d" % month for month in range(1, 13)], 58 | 'day': ["%.2d" % day for day in range(1, 32)], 59 | 'time': [datetime.time(i).strftime('%H:%M') for i in range(24)] 60 | } 61 | 62 | # Call the general weather download function with temperature specific parameters 63 | weather(data_package, variable, dates, product_type, file) 64 | 65 | else: 66 | print('{} already exists. Download is skipped.'.format(file)) 67 | 68 | clear_output(wait=False) 69 | print("Download successful") 70 | 71 | def weather(data_package, variable, dates, product_type, file): 72 | 73 | # if not os.environ.get('PYTHONHTTPSVERIFY', '') and getattr(ssl, '_create_unverified_context', None): 74 | # ssl._create_default_https_context = ssl._create_unverified_context 75 | 76 | c = cdsapi.Client() 77 | 78 | params = { 79 | 'format': 'netcdf', 80 | 'variable': variable, 81 | "year": dates["year"], 82 | "month": dates["month"], 83 | "time": dates["time"], 84 | 'product_type': product_type, 85 | 'area': [72, -10.5, 36.75, 25.5] 86 | } 87 | 88 | if (variable == '2m_temperature') | (variable == 'soil_temperature_level_1'): 89 | params["day"] = ["%.2d" % day for day in range(1, 32)] 90 | c.retrieve(data_package, params, file) 91 | 92 | 93 | def population(input_path): 94 | 95 | # Set URL and directories 96 | url = 'https://ec.europa.eu/eurostat/cache/GISCO/geodatafiles/GEOSTAT-grid-POP-1K-2011-V2-0-1.zip' 97 | population_path = os.path.join(input_path, 'population') 98 | os.makedirs(population_path, exist_ok=True) 99 | destination = os.path.join(population_path, 'GEOSTAT-grid-POP-1K-2011-V2-0-1.zip') 100 | unzip_dir = os.path.join(population_path, 'Version 2_0_1') 101 | 102 | # Download file 103 | if not os.path.isfile(destination): 104 | urllib.request.urlretrieve(url, destination) 105 | else: 106 | print('{} already exists. Download is skipped.'.format(destination)) 107 | # Unzip file 108 | if not os.path.isdir(unzip_dir): 109 | with zipfile.ZipFile(destination, 'r') as f: 110 | f.extractall(population_path) 111 | else: 112 | print('{} already exists. Unzipping is skipped.'.format(unzip_dir)) 113 | 114 | clear_output(wait=False) 115 | print("Download successful") 116 | -------------------------------------------------------------------------------- /scripts/read.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import pandas as pd 4 | import geopandas as gpd 5 | import datetime as dt 6 | from netCDF4 import Dataset, num2date 7 | from shapely.geometry import Point 8 | 9 | 10 | def temperature(input_path, year_start, year_end, parameter): 11 | 12 | 13 | df_list = [] 14 | for year in range(year_start, year_end + 1): 15 | if parameter == "t2m": 16 | df_int = pd.concat( 17 | [weather(input_path, 'ERA_temperature_{}_{}.nc'.format('2m_temperature', year), parameter)], 18 | axis=0 19 | ) 20 | 21 | if parameter == "stl1": 22 | df_int = pd.concat( 23 | [weather(input_path, 'ERA_temperature_{}_{}.nc'.format('soil_temperature_level_1', year), parameter)], 24 | axis=0 25 | ) 26 | df_list.append(df_int) 27 | df = pd.concat(df_list, axis = 0) 28 | 29 | return df 30 | 31 | 32 | def wind(input_path): 33 | 34 | return weather(input_path, 'ERA_wind.nc', 'si10') 35 | 36 | 37 | def weather(input_path, filename, variable_name): 38 | 39 | file = os.path.join(input_path, 'weather', filename) 40 | # Read the netCDF file 41 | nc = Dataset(file) 42 | time = nc.variables['time'][:] 43 | time_units = nc.variables['time'].units 44 | latitude = nc.variables['latitude'][:] 45 | longitude = nc.variables['longitude'][:] 46 | variable = nc.variables[variable_name][:] 47 | 48 | # Transform to pd.DataFrame 49 | index = pd.Index(num2date(time, time_units, only_use_python_datetimes=True), name='time') 50 | 51 | index = index.map(lambda x: dt.datetime(x.year, x.month, x.day, x.hour, x.minute, x.second)) 52 | 53 | df = pd.DataFrame(data=variable.reshape(len(time), len(latitude) * len(longitude)), 54 | index=index, 55 | columns=pd.MultiIndex.from_product([latitude, longitude], names=('latitude', 'longitude'))) 56 | 57 | return df 58 | 59 | def population(input_path): 60 | 61 | directory = 'population/Version 2_0_1/' 62 | filename = 'GEOSTAT_grid_POP_1K_2011_V2_0_1.csv' 63 | 64 | # Read population data 65 | df = pd.read_csv(os.path.join(input_path, directory, filename), 66 | usecols=['GRD_ID', 'TOT_P', 'CNTR_CODE'], 67 | index_col='GRD_ID') 68 | 69 | # Make GeoDataFrame from the the coordinates in the index 70 | gdf = gpd.GeoDataFrame(df) 71 | gdf['geometry'] = df.index.map(lambda i: Point( 72 | [1000 * float(x) + 500 for x in reversed(i.split('N')[1].split('E'))] 73 | )) 74 | 75 | # Transform coordinate reference system to 'latitude/longitude' 76 | gdf.crs = {'init': 'epsg:3035'} 77 | 78 | return gdf 79 | 80 | 81 | def daily_parameters(input_path): 82 | 83 | file = os.path.join(input_path, 'bgw_bdew', 'daily_demand.csv') 84 | return pd.read_csv(file, sep=';', decimal=',', header=[0, 1], index_col=0) 85 | 86 | 87 | def heating_thresholds(input_path): 88 | 89 | file = os.path.join(input_path, 'heating_thresholds', 'heating_thresholds.csv') 90 | return pd.read_csv(file, sep=';', decimal=',', index_col=0)['Heating threshold'] 91 | 92 | 93 | def hourly_parameters(input_path): 94 | 95 | def read(): 96 | file = os.path.join(input_path, 'bgw_bdew', filename) 97 | return pd.read_csv(file, sep=';', decimal=',', index_col=index_col).apply(pd.to_numeric, downcast='float') 98 | 99 | parameters = {} 100 | for building_type in ['SFH', 'MFH', 'COM']: 101 | 102 | filename = 'hourly_factors_{}.csv'.format(building_type) 103 | 104 | # MultiIndex for commercial heat because of weekday dependency 105 | index_col = [0, 1] if building_type == 'COM' else 0 106 | 107 | parameters[building_type] = read() 108 | 109 | return parameters 110 | 111 | 112 | def building_database(input_path): 113 | 114 | return { 115 | heat_type: { 116 | building_type: pd.read_csv( 117 | os.path.join(input_path, 118 | 'JRC_IDEES', 119 | '{}_{}.csv'.format(building_type, heat_type)), 120 | decimal=',', index_col=0 121 | ).apply(pd.to_numeric, downcast='float') 122 | for building_type in ['Residential', 'Tertiary'] 123 | } 124 | for heat_type in ['space', 'water'] 125 | } 126 | 127 | def cop_parameters(input_path): 128 | 129 | file = os.path.join(input_path, 'cop', 'cop_parameters.csv') 130 | return pd.read_csv(file, sep=';', decimal=',', header=0, index_col=0).apply(pd.to_numeric, downcast='float') 131 | -------------------------------------------------------------------------------- /scripts/preprocess.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import pandas as pd 4 | import geopandas as gpd 5 | from shapely.geometry import Point 6 | 7 | import scripts.read as read 8 | from scripts.misc import upsample_df 9 | 10 | 11 | def map_population(input_path, countries, interim_path, plot=True): 12 | 13 | population = None 14 | weather_grid = None 15 | mapped_population = {} 16 | 17 | for country in countries: 18 | 19 | file = os.path.join(interim_path, 'population_{}'.format(country)) 20 | 21 | if not os.path.isfile(file): 22 | 23 | if population is None: 24 | 25 | population = read.population(input_path) 26 | weather_data = read.wind(input_path) # For the weather grid 27 | 28 | # Make GeoDataFrame from the weather data coordinates 29 | weather_grid = gpd.GeoDataFrame(index=weather_data.columns) 30 | weather_grid['geometry'] = weather_grid.index.map(lambda i: Point(reversed(i))) 31 | 32 | # Set coordinate reference system to 'latitude/longitude' 33 | weather_grid.crs = {'init': 'epsg:4326'} 34 | 35 | # Make polygons around the weather points 36 | weather_grid['geometry'] = weather_grid.geometry.apply(lambda point: point.buffer(.75 / 2, cap_style=3)) 37 | 38 | # Make list from MultiIndex (this is necessary for the spatial join) 39 | weather_grid.index = weather_grid.index.tolist() 40 | 41 | 42 | # For Luxembourg, a single weather grid point is manually added for lack of population geodata 43 | if country == 'LU': 44 | s = pd.Series({(49.5, 6): 1}) 45 | 46 | else: 47 | 48 | # Filter population data by country to cut processing time 49 | if country == 'GB': 50 | gdf = population[population['CNTR_CODE'] == 'UK'].copy() 51 | elif country == 'GR': 52 | gdf = population[population['CNTR_CODE'] == 'EL'].copy() 53 | else: 54 | gdf = population[population['CNTR_CODE'] == country].copy() 55 | 56 | # Align coordinate reference systems 57 | gdf = gdf.to_crs({'init': 'epsg:4326'}) 58 | 59 | # Spatial join 60 | gdf = gpd.sjoin(gdf, weather_grid, how="left", op='within') 61 | 62 | # Sum up population 63 | s = gdf.groupby('index_right')['TOT_P'].sum() 64 | 65 | # Write results to interim path 66 | s.to_pickle(file) 67 | 68 | else: 69 | 70 | s = pd.read_pickle(file) 71 | print('{} already exists and is read from disk.'.format(file)) 72 | 73 | mapped_population[country] = s 74 | 75 | if plot: 76 | print('Plot of the re-mapped population data of {} (first selected country) ' 77 | 'for visual inspection:'.format(countries[0])) 78 | gdf = gpd.GeoDataFrame(mapped_population[countries[0]], columns=['TOT_P']) 79 | gdf['geometry'] = gdf.index.map(lambda i: Point(reversed(i))) 80 | gdf.plot(column='TOT_P') 81 | 82 | return mapped_population 83 | 84 | 85 | def wind(input_path, mapped_population, plot=True): 86 | 87 | df = read.wind(input_path) 88 | 89 | # Temporal average 90 | s = df.mean(0) 91 | if plot: 92 | print('Plot of the wind averages for visual inspection:') 93 | gdf = gpd.GeoDataFrame(s, columns=['wind']) 94 | gdf['geometry'] = gdf.index.map(lambda i: Point(reversed(i))) 95 | gdf.plot(column='wind') 96 | 97 | # Wind data is filtered by country 98 | return pd.concat( 99 | [s[population.index] for population in mapped_population.values()], 100 | keys=mapped_population.keys(), names=['country', 'latitude', 'longitude'], axis=0 101 | ).apply(pd.to_numeric, downcast='float') 102 | 103 | 104 | def temperature(input_path, year_start, year_end, mapped_population): 105 | 106 | parameters = { 107 | 'air': 't2m', 108 | 'soil': 'stl1' 109 | } 110 | 111 | ts_parameters = [] 112 | for parameter in parameters.values(): 113 | 114 | ts_years = [] 115 | for year in list(range(year_start, year_end + 1)): 116 | ts = read.temperature(input_path, year, year, parameter) 117 | ts_years.append(ts) 118 | 119 | df_years = pd.concat(ts_years, axis=0) 120 | 121 | # Temperature data is filtered by country 122 | df_countries = pd.concat( 123 | [df_years[population.index] for population in mapped_population.values()], 124 | keys=mapped_population.keys(), axis=1, names=['country', 'latitude', 'longitude'] 125 | ).apply(pd.to_numeric, downcast='float') 126 | 127 | ts_parameters.append(df_countries) 128 | 129 | return pd.concat( 130 | ts_parameters, keys=parameters.keys(), names=['parameter', 'country', 'latitude', 'longitude'], axis=1 131 | ) -------------------------------------------------------------------------------- /preprocessing/preprocessing_JRC_IDEES.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "636d200f", 6 | "metadata": {}, 7 | "source": [ 8 | "## Manual data download" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "id": "d3033e30", 14 | "metadata": {}, 15 | "source": [ 16 | "Download files from [JRC-IDEES](https://data.jrc.ec.europa.eu/dataset/jrc-10110-10001) and follow the Excel url. Than, download the country-specific annual demands \"JRC-IDEES-2015_All_xlsx_COUNTRY.zip\" and pose the sector specific file in your repository or adjust your filename with your download path. " 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "id": "ed6d60d6", 22 | "metadata": {}, 23 | "source": [ 24 | "## Import Python libraries" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": 1, 30 | "id": "c6404e3a", 31 | "metadata": {}, 32 | "outputs": [], 33 | "source": [ 34 | "import pandas as pd\n", 35 | "from openpyxl import load_workbook\n", 36 | "import os" 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "id": "1b5bcba0", 42 | "metadata": {}, 43 | "source": [ 44 | "## Select geographical, sectoral, and temporal scope" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": 2, 50 | "id": "1b192e12", 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [ 54 | "all_countries = ['AT', 'BE', 'BG', 'CZ', 'DE', 'DK', \n", 55 | " 'EE', 'ES', 'FI', 'FR', 'GB', 'HR', \n", 56 | " 'HU', 'IE', 'IT', 'LT', 'LU', 'LV', 'GR',\n", 57 | " 'NL', 'PL', 'PT', 'RO', 'SE', 'SI', 'SK']\n", 58 | "selected_countries = all_countries\n", 59 | "\n", 60 | "# GB is named UK in JCR\n", 61 | "# GR is named EL in JCR\n", 62 | "# missing in JCR: CH, NO\n", 63 | "name_clearification = {\"GB\" : \"UK\",\n", 64 | " \"GR\": \"EL\"}" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": 3, 70 | "id": "852ecc55", 71 | "metadata": {}, 72 | "outputs": [], 73 | "source": [ 74 | "sectors = [\"Residential\", \"Tertiary\"]\n", 75 | "applications = [\"water\", \"space\"]\n", 76 | "\n", 77 | "start_year = \"2008\"\n", 78 | "end_year = \"2015\"" 79 | ] 80 | }, 81 | { 82 | "cell_type": "markdown", 83 | "id": "10173279", 84 | "metadata": {}, 85 | "source": [ 86 | "## Preprocessing of JRC-IDEES data" 87 | ] 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "id": "2ae9398a", 92 | "metadata": {}, 93 | "source": [ 94 | "Read the excel country- and sector-specific excel and make a sector-specific dataframe which is placed in the interim folder" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": 4, 100 | "id": "fe220566", 101 | "metadata": {}, 102 | "outputs": [], 103 | "source": [ 104 | "def read (sector, application, country_code):\n", 105 | " \n", 106 | " if country_code == \"GB\":\n", 107 | " filename = f\"JRC-IDEES-2015_{sector}_{name_clearification[country_code]}.xlsx\"\n", 108 | " elif country_code ==\"GR\":\n", 109 | " filename = f\"JRC-IDEES-2015_{sector}_{name_clearification[country_code]}.xlsx\"\n", 110 | " else:\n", 111 | " filename = f\"JRC-IDEES-2015_{sector}_{country_code}.xlsx\"\n", 112 | " sheet_name = \"RES_hh_tes\" if sector == \"Residential\" else \"SER_hh_tes\"\n", 113 | " \n", 114 | " raw = pd.read_excel(filename, header = 0, sheet_name = sheet_name, index_col = 0)\n", 115 | " \n", 116 | " if application == \"water\":\n", 117 | " row_selection = 'Water heating' if sector == \"Residential\" else \"Hot water\"\n", 118 | " else: \n", 119 | " row_selection = \"Space heating\"\n", 120 | " \n", 121 | " df = raw.loc[row_selection, start_year:end_year].to_frame().rename(columns = {row_selection: country_code})\n", 122 | " df = df.transpose() * 1.163e-2\n", 123 | " return df" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": 7, 129 | "id": "c20c8af6", 130 | "metadata": {}, 131 | "outputs": [], 132 | "source": [ 133 | "input_path = os.path.realpath('../input')" 134 | ] 135 | }, 136 | { 137 | "cell_type": "code", 138 | "execution_count": 8, 139 | "id": "2d27793a", 140 | "metadata": {}, 141 | "outputs": [], 142 | "source": [ 143 | "for sector in sectors:\n", 144 | " for application in applications:\n", 145 | " pd.concat([\n", 146 | " read(sector, application, country) for country in selected_countries\n", 147 | " ], axis = 0).to_csv(f\"{input_path}/JRC_IDEES/{sector}_{application}.csv\", decimal = \",\")" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": null, 153 | "id": "2cdf39dc", 154 | "metadata": {}, 155 | "outputs": [], 156 | "source": [] 157 | } 158 | ], 159 | "metadata": { 160 | "kernelspec": { 161 | "display_name": "Python 3", 162 | "language": "python", 163 | "name": "python3" 164 | }, 165 | "language_info": { 166 | "codemirror_mode": { 167 | "name": "ipython", 168 | "version": 3 169 | }, 170 | "file_extension": ".py", 171 | "mimetype": "text/x-python", 172 | "name": "python", 173 | "nbconvert_exporter": "python", 174 | "pygments_lexer": "ipython3", 175 | "version": "3.7.10" 176 | } 177 | }, 178 | "nbformat": 4, 179 | "nbformat_minor": 5 180 | } 181 | -------------------------------------------------------------------------------- /scripts/cop.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import pandas as pd 4 | 5 | from scripts.misc import localize 6 | from scripts.misc import group_df_by_multiple_column_levels 7 | 8 | 9 | def source_temperature(temperature): 10 | 11 | celsius = temperature - 273.15 12 | 13 | return pd.concat( 14 | [celsius['air'], celsius['soil'] - 5, 0 * celsius['air'] + 10 - 5], 15 | keys=['air', 'ground', 'water'], 16 | names=['source', 'country', 'latitude', 'longitude'], 17 | axis=1 18 | ) 19 | 20 | 21 | def sink_temperature(temperature): 22 | 23 | celsius = temperature['air'] - 273.15 24 | 25 | return pd.concat( 26 | [-1 * celsius + 40, -.5 * celsius + 30, 0 * celsius + 50], 27 | keys=['radiator', 'floor', 'water'], 28 | names=['sink', 'country', 'latitude', 'longitude'], 29 | axis=1 30 | ) 31 | 32 | 33 | def spatial_cop(source, sink, cop_parameters): 34 | 35 | def cop_curve(delta_t, source_type): 36 | delta_t = delta_t.clip(lower=15) 37 | return sum(cop_parameters.loc[i, source_type] * delta_t ** i for i in range(3)) 38 | 39 | source_types = source.columns.get_level_values('source').unique() 40 | sink_types = sink.columns.get_level_values('sink').unique() 41 | 42 | return pd.concat( 43 | [pd.concat( 44 | [cop_curve(sink[sink_type] - source[source_type], source_type) 45 | for sink_type in sink_types], 46 | keys=sink_types, 47 | axis=1 48 | ) for source_type in source_types], 49 | keys=source_types, 50 | axis=1, 51 | names=['source', 'sink', 'country', 'latitude', 'longitude'] 52 | ).round(4).swaplevel(0, 2, axis=1) 53 | 54 | 55 | def finishing(cop, demand_space, demand_water, correction=.85): 56 | 57 | # Localize Timestamps (including daylight saving time correction) and convert to UTC 58 | countries = cop.columns.get_level_values('country').unique() 59 | sinks = cop.columns.get_level_values('sink').unique() 60 | cop = pd.concat( 61 | [pd.concat( 62 | [pd.concat( 63 | [localize(cop[country][sink], country).tz_convert('utc') for sink in sinks], 64 | keys=sinks, axis=1 65 | )], keys=[country], axis=1 66 | ).swaplevel(0, 2, axis=1) for country in countries], 67 | axis=1, names=['source', 'sink', 'country', 'latitude', 'longitude'] 68 | ).sort_index(axis=1) 69 | 70 | 71 | 72 | # Prepare demand values 73 | demand_space = demand_space.loc[:, demand_space.columns.get_level_values('unit') == 'MW/TWh'] 74 | demand_space = group_df_by_multiple_column_levels(demand_space, ['country', 'latitude', 'longitude']) 75 | 76 | demand_water = demand_water.loc[:, demand_water.columns.get_level_values('unit') == 'MW/TWh'] 77 | demand_water = group_df_by_multiple_column_levels(demand_water, ['country', 'latitude', 'longitude']) 78 | 79 | # Spatial aggregation 80 | sources = cop.columns.get_level_values('source').unique() 81 | sinks = cop.columns.get_level_values('sink').unique() 82 | power = pd.concat( 83 | [pd.concat( 84 | [(demand_water / cop[source][sink]).groupby(level=0, axis=1).sum() 85 | if sink == 'water' else 86 | (demand_space / cop[source][sink]).groupby(level=0, axis=1).sum() 87 | for sink in sinks], 88 | keys=sinks, axis=1 89 | ) for source in sources], 90 | keys=sources, axis=1, names=['source', 'sink', 'country'] 91 | ) 92 | heat = pd.concat( 93 | [pd.concat( 94 | [demand_water.groupby(level=0, axis=1).sum() 95 | if sink == 'water' else 96 | demand_space.groupby(level=0, axis=1).sum() 97 | for sink in sinks], ## 98 | keys=sinks, axis=1 99 | ) for source in sources], 100 | keys=sources, axis=1, names=['source', 'sink', 'country'] 101 | ) 102 | cop = heat / power 103 | 104 | # Correction and round 105 | cop = (cop * correction).round(2) 106 | 107 | # Fill NA at the end and the beginning of the dataset arising from different local times 108 | cop = cop.fillna(method='bfill').fillna(method='ffill') 109 | 110 | # Rename columns 111 | cop.columns = cop.columns.set_levels(['ASHP', 'GSHP', 'WSHP'], level=0) 112 | cop.columns = cop.columns.set_levels(['floor', 'radiator', 'water'], level=1) 113 | cop.columns = pd.MultiIndex.from_tuples([('_'.join([level for level in col_name[0:2]]), col_name[2]) for col_name in cop.columns.values]) 114 | cop = pd.concat([cop], keys=['COP'], axis=1) 115 | cop = pd.concat([cop], keys=['coefficient'], axis=1) 116 | cop = cop.swaplevel(i=0, j=3, axis=1) 117 | cop = cop.sort_index(level=0, axis=1) 118 | cop.columns.names = ['country', 'variable', 'attribute', 'unit'] 119 | 120 | return cop 121 | 122 | 123 | def validation(cop, heat, output_path, corrected): 124 | 125 | def averages(df): 126 | return pd.concat( 127 | [df.loc[(df.index >= pd.Timestamp(year=2011, month=7, day=1, tz='utc')) 128 | & (df.index < pd.Timestamp(year=2012, month=7, day=1, tz='utc')), ].sum(), 129 | df.loc[(df.index >= pd.Timestamp(year=2012, month=7, day=1, tz='utc')) 130 | & (df.index < pd.Timestamp(year=2013, month=7, day=1, tz='utc')), ].sum()], 131 | keys=['2011/2012', '2012/2013'], axis=1 132 | ) 133 | 134 | # Data preparation 135 | cop = cop['DE']['COP'] 136 | cop.columns = cop.columns.droplevel(1) 137 | 138 | heat = heat['DE']['heat_profile'] 139 | heat.columns = heat.columns.droplevel(1) 140 | 141 | # Power calculation 142 | power = pd.DataFrame() 143 | for heat_pump in ['ASHP', 'GSHP', 'WSHP']: 144 | power[heat_pump] = ( 145 | .8 * .85 * heat['space_SFH'] / cop['{}_{}'.format(heat_pump, 'floor')] + 146 | .8 * .15 * heat['space_SFH'] / cop['{}_{}'.format(heat_pump, 'radiator')] + 147 | .2 * heat['water_SFH'] / cop['{}_water'.format(heat_pump)] 148 | ) 149 | heat = pd.concat([.8 * heat['space_SFH'] + .2 * heat['water_SFH']]*3, axis=1, keys=power.columns) 150 | 151 | # Monthly aggregation 152 | heat = averages(heat) 153 | power = averages(power) 154 | 155 | cop = heat/power 156 | 157 | cop.round(2).to_csv(os.path.join(output_path, 'cop_{}.csv'.format(corrected)), sep=';', decimal=',') 158 | -------------------------------------------------------------------------------- /scripts/demand.py: -------------------------------------------------------------------------------- 1 | 2 | import numpy as np 3 | import pandas as pd 4 | 5 | from scripts.misc import localize, upsample_df, group_df_by_multiple_column_levels 6 | 7 | 8 | def reference_temperature(temperature): 9 | 10 | # Daily average 11 | daily_average = temperature.groupby(pd.Grouper(freq='D')).mean().copy() 12 | 13 | # Weighted mean 14 | return sum([.5 ** i * daily_average.shift(i).fillna(method='bfill') for i in range(4)]) / \ 15 | sum([.5 ** i for i in range(4)]) 16 | 17 | 18 | def adjust_temperature(temperature, heating_thresholds): 19 | 20 | # Difference as compared to Germany 21 | diff = heating_thresholds - heating_thresholds['DE'] 22 | 23 | # Shift reference temperature by this difference 24 | adjusted = temperature.copy() 25 | 26 | for country in temperature.columns.get_level_values(0).unique(): 27 | adjusted[country] = temperature[country] - diff[country] 28 | 29 | return adjusted 30 | 31 | 32 | def daily_heat(temperature, wind, all_parameters): 33 | 34 | # BDEW et al. 2015 describes the function for the daily heat demand 35 | # This is implemented in the following and passed to the general daily function 36 | 37 | def heat_function(t, parameters): 38 | 39 | celsius = t - 273.15 # The temperature input is in Kelvin 40 | 41 | sigmoid = parameters['A'] / ( 42 | 1 + (parameters['B'] / (celsius - 40)) ** parameters['C'] 43 | ) + parameters['D'] 44 | 45 | linear = pd.DataFrame( 46 | [parameters['m_{}'.format(i)] * celsius + parameters['b_{}'.format(i)] for i in ['s', 'w']] 47 | ).max() 48 | 49 | return sigmoid + linear 50 | 51 | return daily(temperature, wind, all_parameters, heat_function) 52 | 53 | 54 | def daily_water(temperature, wind, all_parameters): 55 | 56 | # A function for the daily water heating demand is derived from BDEW et al. 2015 57 | # This is implemented in the following and passed to the general daily function 58 | 59 | def water_function(t, parameters): 60 | 61 | celsius = t - 273.15 # The temperature input is in Kelvin 62 | 63 | # Below 15 °C, the water heating demand is not defined and assumed to stay constant 64 | celsius.clip(15, inplace=True) 65 | 66 | return parameters['m_w'] * celsius + parameters['b_w'] + parameters['D'] 67 | 68 | return daily(temperature, wind, all_parameters, water_function) 69 | 70 | 71 | def daily(temperature, wind, all_parameters, func): 72 | 73 | # All locations are separated by the average wind speed with the threshold 4.4 m/s 74 | windy_locations = { 75 | 'normal': wind[wind <= 4.4].index, 76 | 'windy': wind[wind > 4.4].index 77 | } 78 | 79 | buildings = ['SFH', 'MFH', 'COM'] 80 | 81 | return pd.concat( 82 | [pd.concat( 83 | [temperature[locations].apply(func, parameters=all_parameters[(building, windiness)]) 84 | for windiness, locations in windy_locations.items()], 85 | axis=1 86 | ) for building in buildings], 87 | keys=buildings, names=['building', 'country', 'latitude', 'longitude'], axis=1 88 | ) 89 | 90 | 91 | def hourly_heat(daily_df, temperature, parameters): 92 | 93 | # According to BGW 2006, temperature classes are derived from the temperature data 94 | # This is re-sampled to a 60-min-resolution and passed to the general hourly function 95 | 96 | classes = upsample_df( 97 | (np.ceil(((temperature - 273.15) / 5).astype('float64')) * 5).clip(lower=-15, upper=30), 98 | '60min' 99 | ).astype(int).astype(str) 100 | 101 | return hourly(daily_df, classes, parameters) 102 | 103 | 104 | def hourly_water(daily_df, temperature, parameters): 105 | 106 | # For water heating, the highest temperature classes '30' is chosen 107 | # This is re-sampled to a 60-min-resolution and passed to the general hourly function 108 | 109 | classes = upsample_df( 110 | pd.DataFrame(30, index=temperature.index, columns=temperature.columns), 111 | '60min' 112 | ).astype(int).astype(str) 113 | 114 | return hourly(daily_df, classes, parameters) 115 | 116 | 117 | def hourly(daily_df, classes, parameters): 118 | 119 | def hourly_factors(building, country_classes): 120 | 121 | # This function selects hourly factors from BGW 2006 by time and temperature class 122 | slp = pd.DataFrame(index=country_classes.index, columns=country_classes.columns) 123 | 124 | # Time includes the hour of the day 125 | times = country_classes.index.map(lambda x: x.strftime('%H:%M')) 126 | 127 | # For commercial buildings, time additionally includes the weekday 128 | if building == 'COM': 129 | weekdays = country_classes.index.map(lambda x: int(x.strftime('%w'))) 130 | times = list(zip(weekdays, times)) 131 | 132 | for column in country_classes.columns: 133 | slp[column] = parameters[building].lookup(times, country_classes.loc[:, column]) 134 | 135 | return slp 136 | 137 | country_results = {} 138 | # Upsample daily_df to 60 minutes 139 | upsampled = upsample_df(daily_df, '60min') 140 | 141 | countries = daily_df.columns.get_level_values('country').unique() 142 | buildings = daily_df.columns.get_level_values('building').unique() 143 | 144 | 145 | for country in countries: 146 | print(country) 147 | country_results[country] = pd.concat( 148 | [upsampled[building][country] * hourly_factors(building, classes[country]) 149 | for building in buildings], 150 | keys=buildings, names=['building', 'latitude', 'longitude'], axis=1 151 | ) 152 | 153 | results = pd.concat( 154 | country_results.values(), keys=countries, names=['country', 'building', 'latitude', 'longitude'], axis=1 155 | ) 156 | 157 | return results 158 | 159 | 160 | def finishing(df, mapped_population, building_database): 161 | 162 | # Single- and multi-family houses are aggregated assuming a ratio of 70:30 163 | # Transforming to heat demand assuming an average conversion efficiency of 0.9 164 | building_database = { 165 | 'SFH': .7 * building_database['Residential'], 166 | 'MFH': .3 * building_database['Residential'], 167 | 'COM': building_database['Tertiary'] 168 | } 169 | 170 | results = [] 171 | for country, population in mapped_population.items(): 172 | 173 | # Localize Timestamps (including daylight saving time correction) 174 | df_country = localize(df[country], country) 175 | 176 | normalized = [] 177 | absolute = [] 178 | for building_type, building_data in building_database.items(): 179 | 180 | # Weighting 181 | df_cb = df_country[building_type] * population 182 | 183 | # Scaling to 1 TWh/a 184 | years = df_cb.index.year.unique() 185 | factor = 1000000 / df_cb.sum().sum() * len(years) 186 | normalized.append(df_cb.multiply(factor)) 187 | 188 | # Scaling to building database 189 | if country not in ['CH', 'NO']: 190 | database_years = building_data.columns 191 | factors = pd.Series([ 192 | building_data.loc[country, str(year)] * 1000000 / df_cb.loc[df_cb.index.year == year, ].sum().sum() 193 | if str(year) in database_years else float('nan') 194 | for year in years 195 | ], index=years) 196 | absolute.append(df_cb.multiply( 197 | pd.Series(factors.loc[df_cb.index.year].values, index=df_cb.index), axis=0, fill_value=None 198 | )) 199 | 200 | 201 | if country not in ['CH', 'NO']: 202 | country_results = pd.concat( 203 | [pd.concat(x, axis=1, keys=building_database.keys()) for x in [normalized, absolute]], 204 | axis=1, keys=['MW/TWh', 'MW'] 205 | ).apply(pd.to_numeric, downcast='float') 206 | else: 207 | country_results = pd.concat( 208 | [pd.concat(x, axis=1, keys=building_database.keys()) for x in [normalized]], 209 | axis=1, keys=['MW/TWh'] 210 | ).apply(pd.to_numeric, downcast='float') 211 | 212 | # Change index to UCT 213 | results.append(country_results.tz_convert('utc')) 214 | 215 | return pd.concat(results, keys=mapped_population.keys(), axis=1, 216 | names=['country', 'unit', 'building_type', 'latitude', 'longitude']) 217 | 218 | 219 | def combine(space, water): 220 | 221 | # Spatial aggregation 222 | space = group_df_by_multiple_column_levels(space, ['country', 'unit', 'building_type']) 223 | water = group_df_by_multiple_column_levels(water, ['country', 'unit', 'building_type']) 224 | 225 | # Merge space and water 226 | df = pd.concat([space, water], axis=1, keys=['space', 'water'], 227 | names=['attribute', 'country', 'unit', 'building_type']) 228 | 229 | # Aggregation of building types for absolute values 230 | dfx = df.loc[:, df.columns.get_level_values('unit') == 'MW'] 231 | dfx = dfx.groupby(dfx.columns.droplevel('building_type'), axis=1).sum() 232 | dfx.columns = pd.MultiIndex.from_tuples(dfx.columns) 233 | dfx = pd.concat([dfx['space'], dfx['water'], dfx['space'] + dfx['water']], axis=1, 234 | keys=['space', 'water', 'total'], names=['attribute', 'country', 'unit']) 235 | 236 | # Rename columns 237 | df.columns = pd.MultiIndex.from_tuples( 238 | [('_'.join([level for level in [col_name[0], col_name[3]]]), col_name[1], col_name[2]) 239 | for col_name in df.columns.values] 240 | ) 241 | 242 | # Combine building-specific and aggregated time series, round, restore nan 243 | df = pd.concat([dfx, df], axis=1).round() 244 | df.replace(0, float('nan'), inplace=True) 245 | 246 | # Fill NA at the end and the beginning of the dataset arising from different local times 247 | df_short = df.loc[:, df.columns.get_level_values('unit') == 'MW'].copy().dropna(how='all') 248 | df = df.fillna(method='bfill').fillna(method='ffill') 249 | df[df_short.columns] = df_short.fillna(method='bfill').fillna(method='ffill') 250 | 251 | # Swap MultiIndex 252 | df = pd.concat([ 253 | df.loc[:, df.columns.get_level_values('unit') == 'MW'], 254 | df.loc[:, df.columns.get_level_values('unit') == 'MW/TWh'] 255 | ], axis=1, keys=['heat_demand', 'heat_profile']) 256 | df = df.swaplevel(i=0, j=2, axis=1) 257 | df = df.swaplevel(i=1, j=2, axis=1) 258 | df = df.sort_index(level=0, axis=1) 259 | df.columns.names = ['country', 'variable', 'attribute', 'unit'] 260 | 261 | return df 262 | -------------------------------------------------------------------------------- /scripts/metadata.py: -------------------------------------------------------------------------------- 1 | 2 | import json 3 | import yaml 4 | import os 5 | import hashlib 6 | import shutil 7 | 8 | 9 | # First YAML are defined, which are then parsed and stitched together in the function below 10 | 11 | 12 | metadata_head = head = ''' 13 | hide: true 14 | name: when2heat 15 | external: true 16 | id: https://doi.org/10.25832/when2heat/{version} 17 | profile: tabular-data-package 18 | licenses: 19 | - name: cc-by-4.0 20 | title: Creative Commons Attribution 4.0 21 | path: https://creativecommons.org/licenses/by/4.0/ 22 | attribution: 23 | "Attribution should be given as follows: " 31 | title: When2Heat Heating Profiles 32 | description: Simulated hourly country-aggregated heat demand and COP time series 33 | homepage: https://data.open-power-system-data.org/when2heat/{version} 34 | version: '{version}' 35 | sources: 36 | - title: ECMWF 37 | web: https://www.ecmwf.int/en/forecasts/datasets/reanalysis-datasets/era5 38 | - title: Eurostat 39 | web: http://ec.europa.eu/eurostat/web/gisco/geodata/reference-data/population-distribution-demography/geostat 40 | - title: JRC-IDEEES 41 | web: https://ec.europa.eu/jrc/en/potencia/jrc-idees 42 | - title: BGW 43 | web: http://www.gwb-netz.de/wa_files/05_bgw_leitfaden_lastprofile_56550.pdf 44 | description: Data from pages 55 and 85f are used. 45 | - title: BDEW 46 | web: https://www.enwg-veroeffentlichungen.de/badtoelz/Netze/Gasnetz/Netzbeschreibung/LF-Abwicklung-von-Standardlastprofilen-Gas-20110630-final.pdf 47 | contributors: 48 | - name: Oliver Ruhnau 49 | email: ruhnau@hertie-school.org 50 | role: author 51 | organization: Hertie School 52 | - name: Jarusch Muessel 53 | email: muessel@hertie-school.org 54 | role: author 55 | organization: Hertie School 56 | 57 | lastChanges: '{changes}' 58 | keywords: 59 | - Open Power System Data 60 | - When2Heat 61 | - heating profiles 62 | - time series 63 | - power systems 64 | - building heat 65 | - space heating 66 | - water heating 67 | - heat pumps 68 | - coefficient of performance 69 | publicationDate: '{version}' 70 | longDescription: 71 | This dataset comprises national time series for representing building heat pumps in power system models. 72 | The heat demand of buildings and the coefficient of performance (COP) of heat pumps is calculated 73 | for 28 European countries from {start} to {end} in an hourly resolution. 74 | 75 | Heat demand time series for space and water heating are computed by combining gas standard 76 | load profiles with spatial temperature and wind speed reanalysis data as well as population geodata. 77 | The profiles are year-wise scaled to 1 TWh each. For the years 2008 to 2015, the data is additionally 78 | scaled with annual statistics on the final energy consumption for heating. 79 | 80 | COP time series for different heat sources – air, ground, and groundwater – and different heat sinks 81 | – floor heating, radiators, and water heating – are calculated based on COP and heating curves 82 | using reanalysis temperature data, spatially aggregated with respect to the heat demand, 83 | and corrected based on field measurements. 84 | 85 | All data processing as well as the download of relevant input data is conducted in python 86 | and pandas and has been documented in the Jupyter notebooks linked below. Please also consider and cite 87 | our Data Descriptor of the original dataset as well as 88 | our Working Paper at on recent updates and extensions of the dataset. 89 | documentation: 'https://github.com/oruhnau/when2heat/blob/{version}/main.ipynb' 90 | spatial: 91 | location: 28 European countries 92 | resolution: Countries 93 | _external: true 94 | temporal: 95 | start: '{start}-01-01' 96 | end: '{end}-12-31' 97 | resolution: hourly 98 | _metadataVersion: 1.2 99 | resources: 100 | ''' 101 | 102 | excel_resource = ''' 103 | name: when2heat 104 | path: when2heat.xlsx 105 | title: When2Heat excel multiindex 106 | format: xlsx 107 | bytes: {bytes} 108 | hash: {hash} 109 | mediatype: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 110 | ''' 111 | 112 | csv_resource = ''' 113 | name: when2heat 114 | profile: tabular-data-resource 115 | path: when2heat.csv 116 | title: When2heat csv singleindex 117 | format: csv 118 | mediatype: text/csv 119 | encoding: UTF8 120 | bytes: {bytes} 121 | hash: {hash} 122 | dialect: 123 | delimiter: "," 124 | decimalChar: "." 125 | lineTerminator: "\\n" 126 | header: true 127 | _alternativeFormats: 128 | - path: when2heat.csv 129 | stacking: Singleindex 130 | format: csv 131 | - path: when2heat.xlsx 132 | stacking: Multiindex 133 | format: xlsx 134 | - path: when2heat_multiindex.csv 135 | stacking: Multiindex 136 | format: csv 137 | - path: when2heat_stacked.csv 138 | stacking: Stacked 139 | format: csv 140 | schema: 141 | primaryKey: utc_timestamp 142 | missingValues: "" 143 | fields: 144 | - name: utc_timestamp 145 | description: Start of timeperiod in Coordinated Universal Time 146 | type: datetime 147 | format: YYYY-MM-DDThh:mm:ssZ 148 | opsdContentfilter: true 149 | - name: cet_cest_timestamp 150 | description: Start of timeperiod in Central European (Summer-) Time 151 | type: datetime 152 | format: YYYY-MM-DDThh:mm:ss 153 | ''' 154 | 155 | field = ''' 156 | name: {country}_{variable}_{attribute} 157 | description: {description} 158 | type: number 159 | unit: {unit} 160 | opsdContentfilter: false 161 | opsdProperties: 162 | country: {country} 163 | variable: {variable} 164 | attribute: {attribute} 165 | ''' 166 | 167 | descriptions = ''' 168 | heat_demand: 169 | total: Heat demand in {country} in {unit} for space and water heating 170 | space: Heat demand in {country} in {unit} for space heating 171 | water: Heat demand in {country} in {unit} for water heating 172 | space_SFH: Heat demand in {country} in {unit} for space heating in single-family houses 173 | space_MFH: Heat demand in {country} in {unit} for space heating in multi-family houses 174 | space_COM: Heat demand in {country} in {unit} for space heating in commercial buildings 175 | water_SFH: Heat demand in {country} in {unit} for water heating in single-family houses 176 | water_MFH: Heat demand in {country} in {unit} for water heating in multi-family houses 177 | water_COM: Heat demand in {country} in {unit} for water heating in commercial buildings 178 | heat_profile: 179 | space_SFH: Normalized heat demand in {country} in {unit} for space heating in single-family houses 180 | space_MFH: Normalized heat demand in {country} in {unit} for space heating in multi-family houses 181 | space_COM: Normalized heat demand in {country} in {unit} for space heating in commercial buildings 182 | water_SFH: Normalized heat demand in {country} in {unit} for water heating in single-family houses 183 | water_MFH: Normalized heat demand in {country} in {unit} for water heating in multi-family houses 184 | water_COM: Normalized heat demand in {country} in {unit} for water heating in commercial buildings 185 | COP: 186 | ASHP_floor: COP of air-source heat pumps (ASHP) for space heating in {country} with floor heating 187 | ASHP_radiator: COP of air-source heat pumps (ASHP) for space heating in {country} with radiators 188 | ASHP_water: COP of air-source heat pumps (ASHP) for water heating in {country} 189 | GSHP_floor: COP of ground-source heat pumps (GSHP) for space heating in {country} with floor heating 190 | GSHP_radiator: COP of ground-source heat pumps (GSHP) for space heating in {country} with radiators 191 | GSHP_water: COP of ground-source heat pumps (GSHP) for water heating in {country} 192 | WSHP_floor: COP of groundwater-source heat pumps (WSHP) for space heating in {country} with floor heating 193 | WSHP_radiator: COP of groundwater-source heat pumps (WSHP) for space heating in {country} with radiators 194 | WSHP_water: COP of groundwater-source heat pumps (WSHP) for water heating in {country} 195 | ''' 196 | 197 | country_map = { 198 | 'AT': 'Austria', 199 | 'BE': 'Belgium', 200 | 'BG': 'Bulgaria', 201 | 'CH': 'Switzerland', 202 | 'CZ': 'Czech Republic', 203 | 'DE': 'Germany', 204 | 'DK': 'Denmark', 205 | 'EE': 'Estonia', 206 | 'ES': 'Spain', 207 | 'FI': 'Finland', 208 | 'FR': 'France', 209 | 'GB': 'Great Britain', 210 | 'GR': 'Greece', 211 | 'HR': 'Croatia', 212 | 'HU': 'Hungary', 213 | 'IE': 'Ireland', 214 | 'IT': 'Italy', 215 | 'LT': 'Lithuania', 216 | 'LU': 'Luxembourg', 217 | 'LV': 'Latvia', 218 | 'NL': 'Netherlands', 219 | 'NO': 'Norway', 220 | 'PL': 'Poland', 221 | 'PT': 'Portugal', 222 | 'RO': 'Romania', 223 | 'SE': 'Sweden', 224 | 'SI': 'Slovenia', 225 | 'SK': 'Slovakia', 226 | } 227 | 228 | 229 | def make_json(shaped_dfs, version, changes, year_start, year_end, output_path): 230 | 231 | # Header 232 | metadata = yaml.load( 233 | metadata_head.format(version=version, changes=changes, start=year_start, end=year_end, year=version[:4]), 234 | Loader=yaml.SafeLoader 235 | ) 236 | 237 | # List of resources (files included in the datapackage) 238 | metadata['resources'] = [ 239 | get_resource(excel_resource, os.path.join(output_path, 'when2heat.xlsx')), 240 | get_resource(csv_resource, os.path.join(output_path, 'when2heat.csv')) 241 | ] 242 | 243 | # List of fields 244 | for column in shaped_dfs['multiindex'].columns: 245 | metadata['resources'][1]['schema']['fields'].append(get_field(column)) 246 | 247 | # Write the metadata to disk 248 | with open(os.path.join(output_path, 'datapackage.json'), 'w') as f: 249 | f.write( 250 | json.dumps(metadata, indent=4, separators=(',', ': ')) 251 | ) 252 | 253 | 254 | def get_resource(template, file_path): 255 | 256 | file_size = os.path.getsize(file_path) 257 | 258 | with open(file_path, 'rb') as f: 259 | file_hash = hashlib.md5(f.read()).hexdigest() 260 | 261 | return yaml.load( 262 | template.format(bytes=file_size, hash=file_hash), Loader=yaml.SafeLoader 263 | ) 264 | 265 | 266 | def get_field(column): 267 | 268 | country, variable, attribute, unit = column 269 | 270 | description = yaml.load( 271 | descriptions.format(country=country_map[country], unit=unit), Loader=yaml.SafeLoader 272 | )[variable][attribute] 273 | 274 | return yaml.load( 275 | field.format( 276 | country=country, 277 | variable=variable, 278 | attribute=attribute, 279 | unit=unit, 280 | description=description 281 | ), Loader=yaml.SafeLoader 282 | ) 283 | 284 | 285 | def checksums(output_path, home_path): 286 | 287 | os.chdir(output_path) 288 | files = os.listdir(output_path) 289 | 290 | # Create checksums.txt in the output directory 291 | with open('checksums.txt', 'w') as f: 292 | for file_name in files: 293 | if file_name.split('.')[-1] in ['csv', 'sqlite', 'xlsx']: 294 | with open(file_name, 'rb') as fx: 295 | file_hash = hashlib.md5(fx.read()).hexdigest() 296 | f.write('{},{}\n'.format(file_name, file_hash)) 297 | 298 | # Copy the file to root directory from where it will be pushed to GitHub, 299 | # leaving a copy in the version directory for reference 300 | shutil.copyfile('checksums.txt', os.path.join(home_path, 'checksums.txt')) 301 | -------------------------------------------------------------------------------- /input/bgw_bdew/hourly_factors_COM.csv: -------------------------------------------------------------------------------- 1 | weekday;time;-15;-10;-5;0;5;10;15;20;25;30 2 | 0;00:00;0,0338;0,0342;0,0343;0,0313;0,0309;0,0302;0,0273;0,0268;0,0275;0,0276 3 | 0;01:00;0,0338;0,0342;0,0344;0,0327;0,0320;0,0355;0,0312;0,0315;0,0337;0,0353 4 | 0;02:00;0,0355;0,0352;0,0346;0,0326;0,0315;0,0324;0,0330;0,0367;0,0383;0,0398 5 | 0;03:00;0,0357;0,0359;0,0360;0,0354;0,0358;0,0375;0,0414;0,0439;0,0485;0,0551 6 | 0;04:00;0,0345;0,0357;0,0368;0,0386;0,0434;0,0454;0,0548;0,0572;0,0607;0,0677 7 | 0;05:00;0,0411;0,0436;0,0465;0,0491;0,0516;0,0556;0,0644;0,0681;0,0669;0,0724 8 | 0;06:00;0,0478;0,0449;0,0451;0,0459;0,0458;0,0472;0,0484;0,0519;0,0509;0,0521 9 | 0;07:00;0,0434;0,0440;0,0446;0,0462;0,0452;0,0467;0,0544;0,0580;0,0616;0,0633 10 | 0;08:00;0,0406;0,0413;0,0421;0,0453;0,0446;0,0437;0,0471;0,0441;0,0436;0,0432 11 | 0;09:00;0,0405;0,0405;0,0404;0,0430;0,0424;0,0423;0,0415;0,0408;0,0391;0,0375 12 | 0;10:00;0,0397;0,0393;0,0387;0,0414;0,0404;0,0395;0,0400;0,0400;0,0394;0,0392 13 | 0;11:00;0,0421;0,0414;0,0404;0,0415;0,0399;0,0385;0,0380;0,0356;0,0349;0,0337 14 | 0;12:00;0,0405;0,0395;0,0382;0,0393;0,0378;0,0365;0,0361;0,0350;0,0353;0,0341 15 | 0;13:00;0,0402;0,0392;0,0378;0,0386;0,0368;0,0347;0,0335;0,0333;0,0321;0,0302 16 | 0;14:00;0,0391;0,0385;0,0377;0,0379;0,0377;0,0359;0,0339;0,0320;0,0299;0,0278 17 | 0;15:00;0,0386;0,0388;0,0387;0,0388;0,0385;0,0345;0,0331;0,0306;0,0304;0,0274 18 | 0;16:00;0,0388;0,0389;0,0388;0,0391;0,0380;0,0370;0,0335;0,0301;0,0282;0,0257 19 | 0;17:00;0,0398;0,0400;0,0400;0,0406;0,0405;0,0390;0,0337;0,0327;0,0301;0,0266 20 | 0;18:00;0,0399;0,0400;0,0400;0,0400;0,0410;0,0393;0,0358;0,0337;0,0324;0,0306 21 | 0;19:00;0,0410;0,0407;0,0403;0,0395;0,0401;0,0394;0,0382;0,0343;0,0353;0,0344 22 | 0;20:00;0,0397;0,0400;0,0401;0,0389;0,0401;0,0412;0,0384;0,0384;0,0365;0,0343 23 | 0;21:00;0,0410;0,0410;0,0409;0,0383;0,0390;0,0394;0,0386;0,0414;0,0402;0,0391 24 | 0;22:00;0,0373;0,0375;0,0376;0,0346;0,0342;0,0370;0,0347;0,0349;0,0350;0,0340 25 | 0;23:00;0,0368;0,0365;0,0360;0,0314;0,0328;0,0316;0,0292;0,0296;0,0295;0,0288 26 | 1;00:00;0,0325;0,0339;0,0333;0,0313;0,0309;0,0293;0,0277;0,0272;0,0260;0,0252 27 | 1;01:00;0,0339;0,0349;0,0339;0,0328;0,0321;0,0318;0,0310;0,0316;0,0318;0,0317 28 | 1;02:00;0,0354;0,0352;0,0345;0,0335;0,0331;0,0330;0,0332;0,0313;0,0321;0,0328 29 | 1;03:00;0,0384;0,0381;0,0371;0,0355;0,0359;0,0362;0,0367;0,0347;0,0330;0,0335 30 | 1;04:00;0,0385;0,0383;0,0381;0,0377;0,0392;0,0415;0,0430;0,0433;0,0433;0,0446 31 | 1;05:00;0,0453;0,0449;0,0459;0,0465;0,0476;0,0479;0,0528;0,0559;0,0677;0,0723 32 | 1;06:00;0,0562;0,0533;0,0537;0,0535;0,0544;0,0553;0,0649;0,0707;0,0763;0,0796 33 | 1;07:00;0,0544;0,0547;0,0547;0,0562;0,0560;0,0586;0,0668;0,0742;0,0775;0,0796 34 | 1;08:00;0,0549;0,0540;0,0534;0,0554;0,0548;0,0572;0,0594;0,0589;0,0554;0,0562 35 | 1;09:00;0,0509;0,0506;0,0506;0,0535;0,0525;0,0535;0,0554;0,0540;0,0520;0,0520 36 | 1;10:00;0,0476;0,0475;0,0478;0,0503;0,0502;0,0502;0,0503;0,0506;0,0513;0,0509 37 | 1;11:00;0,0466;0,0464;0,0469;0,0477;0,0473;0,0460;0,0461;0,0457;0,0475;0,0475 38 | 1;12:00;0,0444;0,0441;0,0446;0,0461;0,0447;0,0446;0,0426;0,0449;0,0454;0,0451 39 | 1;13:00;0,0439;0,0433;0,0434;0,0448;0,0440;0,0422;0,0402;0,0392;0,0373;0,0363 40 | 1;14:00;0,0443;0,0430;0,0428;0,0434;0,0433;0,0415;0,0382;0,0384;0,0357;0,0339 41 | 1;15:00;0,0447;0,0436;0,0434;0,0427;0,0435;0,0411;0,0385;0,0350;0,0344;0,0333 42 | 1;16:00;0,0444;0,0431;0,0428;0,0426;0,0436;0,0423;0,0366;0,0336;0,0315;0,0301 43 | 1;17:00;0,0440;0,0433;0,0439;0,0439;0,0444;0,0425;0,0377;0,0350;0,0329;0,0313 44 | 1;18:00;0,0409;0,0420;0,0430;0,0437;0,0440;0,0436;0,0387;0,0366;0,0335;0,0325 45 | 1;19:00;0,0393;0,0414;0,0424;0,0430;0,0431;0,0434;0,0419;0,0401;0,0392;0,0383 46 | 1;20:00;0,0383;0,0404;0,0404;0,0405;0,0411;0,0430;0,0421;0,0426;0,0425;0,0414 47 | 1;21:00;0,0377;0,0403;0,0394;0,0385;0,0391;0,0402;0,0412;0,0405;0,0384;0,0378 48 | 1;22:00;0,0378;0,0384;0,0392;0,0352;0,0343;0,0347;0,0362;0,0367;0,0370;0,0366 49 | 1;23:00;0,0358;0,0356;0,0349;0,0319;0,0311;0,0304;0,0288;0,0294;0,0286;0,0275 50 | 2;00:00;0,0334;0,0344;0,0335;0,0313;0,0309;0,0293;0,0277;0,0252;0,0243;0,0240 51 | 2;01:00;0,0346;0,0354;0,0341;0,0327;0,0327;0,0312;0,0305;0,0303;0,0303;0,0302 52 | 2;02:00;0,0362;0,0356;0,0346;0,0334;0,0336;0,0324;0,0327;0,0307;0,0311;0,0314 53 | 2;03:00;0,0385;0,0383;0,0372;0,0355;0,0359;0,0355;0,0354;0,0342;0,0320;0,0321 54 | 2;04:00;0,0389;0,0383;0,0379;0,0372;0,0392;0,0409;0,0424;0,0428;0,0427;0,0441 55 | 2;05:00;0,0456;0,0450;0,0458;0,0464;0,0474;0,0477;0,0526;0,0547;0,0664;0,0712 56 | 2;06:00;0,0569;0,0538;0,0542;0,0538;0,0544;0,0559;0,0657;0,0712;0,0770;0,0804 57 | 2;07:00;0,0545;0,0549;0,0550;0,0563;0,0570;0,0592;0,0676;0,0745;0,0775;0,0799 58 | 2;08:00;0,0554;0,0546;0,0545;0,0572;0,0557;0,0598;0,0622;0,0620;0,0592;0,0602 59 | 2;09:00;0,0508;0,0506;0,0506;0,0534;0,0533;0,0547;0,0574;0,0567;0,0553;0,0559 60 | 2;10:00;0,0478;0,0478;0,0480;0,0503;0,0505;0,0508;0,0518;0,0534;0,0546;0,0548 61 | 2;11:00;0,0466;0,0464;0,0468;0,0484;0,0475;0,0474;0,0476;0,0477;0,0506;0,0509 62 | 2;12:00;0,0440;0,0439;0,0446;0,0462;0,0453;0,0451;0,0438;0,0473;0,0479;0,0482 63 | 2;13:00;0,0431;0,0428;0,0430;0,0445;0,0441;0,0435;0,0414;0,0409;0,0391;0,0385 64 | 2;14:00;0,0435;0,0426;0,0430;0,0439;0,0434;0,0428;0,0387;0,0382;0,0357;0,0341 65 | 2;15:00;0,0443;0,0436;0,0440;0,0440;0,0437;0,0421;0,0379;0,0353;0,0342;0,0328 66 | 2;16:00;0,0439;0,0428;0,0430;0,0433;0,0441;0,0420;0,0370;0,0332;0,0304;0,0283 67 | 2;17:00;0,0440;0,0431;0,0436;0,0438;0,0436;0,0427;0,0370;0,0343;0,0318;0,0302 68 | 2;18:00;0,0407;0,0417;0,0421;0,0427;0,0425;0,0418;0,0378;0,0357;0,0327;0,0318 69 | 2;19:00;0,0393;0,0413;0,0423;0,0423;0,0416;0,0419;0,0407;0,0390;0,0382;0,0371 70 | 2;20:00;0,0384;0,0404;0,0407;0,0405;0,0411;0,0416;0,0402;0,0407;0,0402;0,0386 71 | 2;21:00;0,0371;0,0397;0,0388;0,0375;0,0384;0,0390;0,0397;0,0389;0,0364;0,0354 72 | 2;22:00;0,0371;0,0375;0,0381;0,0340;0,0337;0,0331;0,0347;0,0353;0,0351;0,0344 73 | 2;23:00;0,0357;0,0355;0,0347;0,0315;0,0307;0,0295;0,0275;0,0278;0,0270;0,0258 74 | 3;00:00;0,0330;0,0341;0,0332;0,0310;0,0306;0,0290;0,0274;0,0250;0,0241;0,0238 75 | 3;01:00;0,0343;0,0351;0,0338;0,0323;0,0323;0,0309;0,0302;0,0300;0,0300;0,0299 76 | 3;02:00;0,0358;0,0353;0,0343;0,0330;0,0333;0,0321;0,0323;0,0304;0,0308;0,0311 77 | 3;03:00;0,0381;0,0379;0,0368;0,0352;0,0356;0,0352;0,0351;0,0339;0,0317;0,0318 78 | 3;04:00;0,0386;0,0379;0,0375;0,0368;0,0389;0,0405;0,0420;0,0424;0,0423;0,0437 79 | 3;05:00;0,0452;0,0446;0,0454;0,0459;0,0469;0,0472;0,0521;0,0542;0,0658;0,0705 80 | 3;06:00;0,0563;0,0532;0,0537;0,0532;0,0539;0,0554;0,0651;0,0705;0,0763;0,0797 81 | 3;07:00;0,0540;0,0544;0,0545;0,0558;0,0564;0,0587;0,0669;0,0737;0,0767;0,0792 82 | 3;08:00;0,0549;0,0541;0,0540;0,0566;0,0552;0,0593;0,0616;0,0614;0,0587;0,0596 83 | 3;09:00;0,0503;0,0501;0,0501;0,0528;0,0527;0,0542;0,0568;0,0561;0,0548;0,0554 84 | 3;10:00;0,0473;0,0473;0,0475;0,0498;0,0500;0,0503;0,0513;0,0528;0,0541;0,0543 85 | 3;11:00;0,0461;0,0459;0,0463;0,0479;0,0470;0,0469;0,0471;0,0472;0,0501;0,0504 86 | 3;12:00;0,0436;0,0435;0,0442;0,0458;0,0449;0,0447;0,0434;0,0468;0,0474;0,0477 87 | 3;13:00;0,0426;0,0424;0,0425;0,0441;0,0437;0,0430;0,0410;0,0405;0,0388;0,0381 88 | 3;14:00;0,0430;0,0422;0,0425;0,0435;0,0429;0,0424;0,0384;0,0378;0,0354;0,0338 89 | 3;15:00;0,0439;0,0431;0,0436;0,0436;0,0432;0,0417;0,0375;0,0350;0,0339;0,0324 90 | 3;16:00;0,0435;0,0424;0,0425;0,0428;0,0437;0,0416;0,0366;0,0328;0,0301;0,0281 91 | 3;17:00;0,0436;0,0426;0,0431;0,0434;0,0431;0,0423;0,0366;0,0340;0,0315;0,0299 92 | 3;18:00;0,0403;0,0413;0,0417;0,0423;0,0421;0,0414;0,0374;0,0354;0,0323;0,0315 93 | 3;19:00;0,0390;0,0409;0,0419;0,0419;0,0412;0,0415;0,0403;0,0387;0,0378;0,0367 94 | 3;20:00;0,0380;0,0400;0,0403;0,0401;0,0407;0,0412;0,0398;0,0403;0,0398;0,0383 95 | 3;21:00;0,0367;0,0393;0,0385;0,0371;0,0380;0,0387;0,0393;0,0386;0,0360;0,0351 96 | 3;22:00;0,0367;0,0371;0,0377;0,0337;0,0334;0,0327;0,0344;0,0350;0,0348;0,0341 97 | 3;23:00;0,0354;0,0352;0,0344;0,0312;0,0304;0,0292;0,0272;0,0275;0,0267;0,0255 98 | 4;00:00;0,0334;0,0344;0,0335;0,0313;0,0309;0,0293;0,0277;0,0252;0,0243;0,0240 99 | 4;01:00;0,0346;0,0354;0,0341;0,0327;0,0327;0,0312;0,0305;0,0303;0,0303;0,0302 100 | 4;02:00;0,0362;0,0356;0,0346;0,0334;0,0336;0,0324;0,0327;0,0307;0,0311;0,0314 101 | 4;03:00;0,0385;0,0383;0,0372;0,0355;0,0359;0,0355;0,0354;0,0342;0,0320;0,0321 102 | 4;04:00;0,0389;0,0383;0,0379;0,0372;0,0392;0,0409;0,0424;0,0428;0,0427;0,0441 103 | 4;05:00;0,0456;0,0450;0,0458;0,0464;0,0474;0,0477;0,0526;0,0547;0,0664;0,0712 104 | 4;06:00;0,0569;0,0538;0,0542;0,0538;0,0544;0,0559;0,0657;0,0712;0,0770;0,0804 105 | 4;07:00;0,0545;0,0549;0,0550;0,0563;0,0570;0,0592;0,0676;0,0745;0,0775;0,0799 106 | 4;08:00;0,0554;0,0546;0,0545;0,0572;0,0557;0,0598;0,0622;0,0620;0,0592;0,0602 107 | 4;09:00;0,0508;0,0506;0,0506;0,0534;0,0533;0,0547;0,0574;0,0567;0,0553;0,0559 108 | 4;10:00;0,0478;0,0478;0,0480;0,0503;0,0505;0,0508;0,0518;0,0534;0,0546;0,0548 109 | 4;11:00;0,0466;0,0464;0,0468;0,0484;0,0475;0,0474;0,0476;0,0477;0,0506;0,0509 110 | 4;12:00;0,0440;0,0439;0,0446;0,0462;0,0453;0,0451;0,0438;0,0473;0,0479;0,0482 111 | 4;13:00;0,0431;0,0428;0,0430;0,0445;0,0441;0,0435;0,0414;0,0409;0,0391;0,0385 112 | 4;14:00;0,0435;0,0426;0,0430;0,0439;0,0434;0,0428;0,0387;0,0382;0,0357;0,0341 113 | 4;15:00;0,0443;0,0436;0,0440;0,0440;0,0437;0,0421;0,0379;0,0353;0,0342;0,0328 114 | 4;16:00;0,0439;0,0428;0,0430;0,0433;0,0441;0,0420;0,0370;0,0332;0,0304;0,0283 115 | 4;17:00;0,0440;0,0431;0,0436;0,0438;0,0436;0,0427;0,0370;0,0343;0,0318;0,0302 116 | 4;18:00;0,0407;0,0417;0,0421;0,0427;0,0425;0,0418;0,0378;0,0357;0,0327;0,0318 117 | 4;19:00;0,0393;0,0413;0,0423;0,0423;0,0416;0,0419;0,0407;0,0390;0,0382;0,0371 118 | 4;20:00;0,0384;0,0404;0,0407;0,0405;0,0411;0,0416;0,0402;0,0407;0,0402;0,0386 119 | 4;21:00;0,0371;0,0397;0,0388;0,0375;0,0384;0,0390;0,0397;0,0389;0,0364;0,0354 120 | 4;22:00;0,0371;0,0375;0,0381;0,0340;0,0337;0,0331;0,0347;0,0353;0,0351;0,0344 121 | 4;23:00;0,0357;0,0355;0,0347;0,0315;0,0307;0,0295;0,0275;0,0278;0,0270;0,0258 122 | 5;00:00;0,0336;0,0341;0,0338;0,0305;0,0299;0,0283;0,0276;0,0256;0,0257;0,0263 123 | 5;01:00;0,0338;0,0337;0,0336;0,0311;0,0315;0,0300;0,0307;0,0297;0,0300;0,0303 124 | 5;02:00;0,0349;0,0342;0,0340;0,0321;0,0326;0,0310;0,0311;0,0296;0,0306;0,0311 125 | 5;03:00;0,0362;0,0361;0,0356;0,0337;0,0340;0,0333;0,0334;0,0325;0,0308;0,0309 126 | 5;04:00;0,0358;0,0356;0,0356;0,0352;0,0371;0,0380;0,0393;0,0404;0,0398;0,0402 127 | 5;05:00;0,0424;0,0427;0,0433;0,0439;0,0450;0,0442;0,0480;0,0518;0,0624;0,0658 128 | 5;06:00;0,0568;0,0534;0,0530;0,0531;0,0540;0,0549;0,0629;0,0654;0,0701;0,0726 129 | 5;07:00;0,0542;0,0542;0,0538;0,0553;0,0568;0,0588;0,0661;0,0737;0,0758;0,0774 130 | 5;08:00;0,0527;0,0530;0,0532;0,0563;0,0552;0,0595;0,0618;0,0631;0,0600;0,0610 131 | 5;09:00;0,0497;0,0499;0,0502;0,0528;0,0526;0,0545;0,0568;0,0571;0,0559;0,0565 132 | 5;10:00;0,0466;0,0469;0,0472;0,0499;0,0501;0,0507;0,0512;0,0526;0,0532;0,0526 133 | 5;11:00;0,0450;0,0452;0,0459;0,0480;0,0472;0,0473;0,0463;0,0459;0,0484;0,0483 134 | 5;12:00;0,0430;0,0428;0,0432;0,0459;0,0446;0,0447;0,0450;0,0453;0,0458;0,0461 135 | 5;13:00;0,0424;0,0422;0,0421;0,0439;0,0432;0,0429;0,0413;0,0409;0,0396;0,0395 136 | 5;14:00;0,0441;0,0430;0,0429;0,0435;0,0424;0,0423;0,0383;0,0363;0,0339;0,0325 137 | 5;15:00;0,0436;0,0429;0,0430;0,0434;0,0426;0,0415;0,0371;0,0346;0,0331;0,0313 138 | 5;16:00;0,0428;0,0422;0,0421;0,0428;0,0430;0,0410;0,0363;0,0316;0,0288;0,0266 139 | 5;17:00;0,0425;0,0420;0,0421;0,0427;0,0426;0,0415;0,0367;0,0336;0,0317;0,0306 140 | 5;18:00;0,0414;0,0419;0,0423;0,0426;0,0420;0,0417;0,0375;0,0366;0,0335;0,0328 141 | 5;19:00;0,0399;0,0417;0,0420;0,0417;0,0414;0,0416;0,0411;0,0406;0,0404;0,0401 142 | 5;20:00;0,0389;0,0402;0,0402;0,0397;0,0404;0,0410;0,0399;0,0407;0,0408;0,0399 143 | 5;21:00;0,0373;0,0392;0,0391;0,0369;0,0381;0,0388;0,0398;0,0400;0,0383;0,0382 144 | 5;22:00;0,0374;0,0379;0,0373;0,0336;0,0335;0,0330;0,0346;0,0350;0,0348;0,0343 145 | 5;23:00;0,0349;0,0347;0,0342;0,0311;0,0300;0,0294;0,0273;0,0271;0,0266;0,0256 146 | 6;00:00;0,0307;0,0312;0,0315;0,0299;0,0298;0,0280;0,0302;0,0272;0,0286;0,0321 147 | 6;01:00;0,0311;0,0314;0,0316;0,0303;0,0290;0,0285;0,0286;0,0276;0,0303;0,0319 148 | 6;02:00;0,0305;0,0309;0,0311;0,0303;0,0304;0,0285;0,0295;0,0265;0,0267;0,0244 149 | 6;03:00;0,0321;0,0325;0,0327;0,0318;0,0324;0,0322;0,0315;0,0300;0,0294;0,0306 150 | 6;04:00;0,0317;0,0327;0,0328;0,0340;0,0353;0,0374;0,0381;0,0373;0,0357;0,0377 151 | 6;05:00;0,0403;0,0411;0,0419;0,0426;0,0432;0,0442;0,0441;0,0434;0,0420;0,0453 152 | 6;06:00;0,0540;0,0510;0,0513;0,0499;0,0518;0,0529;0,0551;0,0601;0,0626;0,0683 153 | 6;07:00;0,0467;0,0476;0,0486;0,0512;0,0514;0,0546;0,0593;0,0640;0,0683;0,0726 154 | 6;08:00;0,0453;0,0462;0,0472;0,0485;0,0499;0,0532;0,0561;0,0570;0,0578;0,0542 155 | 6;09:00;0,0458;0,0447;0,0450;0,0467;0,0468;0,0491;0,0504;0,0512;0,0491;0,0470 156 | 6;10:00;0,0435;0,0426;0,0420;0,0447;0,0448;0,0455;0,0481;0,0507;0,0524;0,0543 157 | 6;11:00;0,0426;0,0424;0,0419;0,0435;0,0434;0,0435;0,0434;0,0429;0,0389;0,0395 158 | 6;12:00;0,0405;0,0405;0,0401;0,0416;0,0400;0,0410;0,0410;0,0417;0,0399;0,0387 159 | 6;13:00;0,0397;0,0392;0,0386;0,0399;0,0376;0,0354;0,0349;0,0360;0,0359;0,0313 160 | 6;14:00;0,0398;0,0392;0,0383;0,0393;0,0369;0,0347;0,0356;0,0380;0,0400;0,0443 161 | 6;15:00;0,0395;0,0393;0,0386;0,0379;0,0372;0,0353;0,0342;0,0356;0,0371;0,0401 162 | 6;16:00;0,0381;0,0381;0,0379;0,0384;0,0384;0,0363;0,0326;0,0341;0,0349;0,0346 163 | 6;17:00;0,0379;0,0382;0,0385;0,0387;0,0393;0,0374;0,0326;0,0326;0,0312;0,0271 164 | 6;18:00;0,0377;0,0379;0,0379;0,0383;0,0392;0,0388;0,0352;0,0324;0,0299;0,0292 165 | 6;19:00;0,0379;0,0381;0,0381;0,0378;0,0379;0,0373;0,0364;0,0345;0,0340;0,0307 166 | 6;20:00;0,0381;0,0381;0,0377;0,0370;0,0368;0,0379;0,0352;0,0337;0,0335;0,0300 167 | 6;21:00;0,0372;0,0373;0,0372;0,0354;0,0367;0,0365;0,0370;0,0356;0,0355;0,0336 168 | 6;22:00;0,0368;0,0366;0,0364;0,0326;0,0325;0,0335;0,0328;0,0320;0,0316;0,0303 169 | 6;23:00;0,0325;0,0326;0,0328;0,0299;0,0294;0,0284;0,0279;0,0260;0,0246;0,0223 170 | -------------------------------------------------------------------------------- /main.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "toc": true 7 | }, 8 | "source": [ 9 | "# Table of contents\n", 10 | "1. [About Open Power System Data](#opsd)\n", 11 | "2. [About Jupyter Notebooks and GitHub](#jupyter)\n", 12 | "3. [About this datapackage](#datapackage)\n", 13 | "4. [Data sources](#sources)\n", 14 | "5. [Naming conventions](#naming)\n", 15 | "6. [License](#license)" 16 | ] 17 | }, 18 | { 19 | "cell_type": "markdown", 20 | "metadata": {}, 21 | "source": [ 22 | "
\n", 23 | " Time series: Processing Notebook\n", 24 | " \n", 28 | "
This Notebook is part of the heating profiles data package contributed to the Open Power System Data platform.\n", 29 | "
" 30 | ] 31 | }, 32 | { 33 | "cell_type": "markdown", 34 | "metadata": {}, 35 | "source": [ 36 | "\n", 37 | "# 1. About Open Power System Data" 38 | ] 39 | }, 40 | { 41 | "cell_type": "markdown", 42 | "metadata": {}, 43 | "source": [ 44 | "This notebook is a contribution to the project [Open Power System Data](http://open-power-system-data.org). Open Power System Data develops a platform for free and open data for electricity system modeling. We collect, check, process, document, and provide data that are publicly available but currently inconvenient to use. \n", 45 | "More info on Open Power System Data:\n", 46 | "- [Information on the project on our website](http://open-power-system-data.org)\n", 47 | "- [Data and metadata on our data platform](http://data.open-power-system-data.org)\n", 48 | "- [Data processing scripts on our GitHub page](https://github.com/Open-Power-System-Data)" 49 | ] 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "metadata": {}, 54 | "source": [ 55 | "\n", 56 | "# 2. About Jupyter Notebooks and GitHub" 57 | ] 58 | }, 59 | { 60 | "cell_type": "markdown", 61 | "metadata": {}, 62 | "source": [ 63 | "This file is a [Jupyter Notebook](http://jupyter.org/). A Jupyter Notebook is a file that combines executable programming code with visualizations and comments in markdown format, allowing for an intuitive documentation of the code. We use Jupyter Notebooks for combined coding and documentation. We use Python 3 as programming language. All Notebooks are stored on [GitHub](https://github.com/), a platform for software development, and are publicly available. More information on our IT-concept can be found [here](http://open-power-system-data.org/it). See also our [step-by-step manual](http://open-power-system-data.org/step-by-step) how to use the dataplatform." 64 | ] 65 | }, 66 | { 67 | "cell_type": "markdown", 68 | "metadata": {}, 69 | "source": [ 70 | "\n", 71 | "# 3. About this datapackage" 72 | ] 73 | }, 74 | { 75 | "cell_type": "markdown", 76 | "metadata": {}, 77 | "source": [ 78 | "This dataset comprises national time series for representing building heat pumps in power system models. The heat demand of buildings and the coefficient of performance (COP) of heat pumps is calculated for several European countries and years in an hourly resolution. \n", 79 | " \n", 80 | "Heat demand time series for space and water heating are computed by combining gas standard load profiles with spatial temperature and wind speed reanalysis data as well as population geodata. The profiles are year-wise scaled to 1 TWh each. For the years 2008 to 2015, the data is additionally scaled with annual statistics on the final energy consumption for heating.\n", 81 | " \n", 82 | "COP time series for different heat sources – air, ground, and groundwater – and different heat sinks – floor heating, radiators, and water heating – are calculated based on COP and heating curves using reanalysis temperature data, spatially aggregated with respect to the heat demand, and corrected based on field measurements.\n", 83 | " \n", 84 | "All data processing as well as the download of relevant input data is conducted in python and pandas and has been documented in the processing notebooks linked below. Please also consider and cite our Data Descriptor." 85 | ] 86 | }, 87 | { 88 | "cell_type": "markdown", 89 | "metadata": {}, 90 | "source": [ 91 | "\n", 92 | "# 4. Data sources" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": {}, 98 | "source": [ 99 | "A complete list of data sources is provided on the [datapackage information website](http://data.open-power-system-data.org/when2heat/latest). They are also contained in the JSON file that contains all metadata." 100 | ] 101 | }, 102 | { 103 | "cell_type": "markdown", 104 | "metadata": {}, 105 | "source": [ 106 | "\n", 107 | "# 5. Naming conventions" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": 1, 113 | "metadata": { 114 | "scrolled": false 115 | }, 116 | "outputs": [ 117 | { 118 | "data": { 119 | "text/html": [ 120 | "
\n", 121 | "\n", 134 | "\n", 135 | " \n", 136 | " \n", 137 | " \n", 138 | " \n", 139 | " \n", 140 | " \n", 141 | " \n", 142 | " \n", 143 | " \n", 144 | " \n", 145 | " \n", 146 | " \n", 147 | " \n", 148 | " \n", 149 | " \n", 150 | " \n", 151 | " \n", 152 | " \n", 153 | " \n", 154 | " \n", 155 | " \n", 156 | " \n", 157 | " \n", 158 | " \n", 159 | " \n", 160 | " \n", 161 | " \n", 162 | " \n", 163 | " \n", 164 | " \n", 165 | " \n", 166 | " \n", 167 | " \n", 168 | " \n", 169 | " \n", 170 | " \n", 171 | " \n", 172 | " \n", 173 | " \n", 174 | " \n", 175 | " \n", 176 | " \n", 177 | " \n", 178 | " \n", 179 | " \n", 180 | " \n", 181 | " \n", 182 | " \n", 183 | " \n", 184 | " \n", 185 | " \n", 186 | " \n", 187 | " \n", 188 | " \n", 189 | " \n", 190 | " \n", 191 | " \n", 192 | " \n", 193 | " \n", 194 | " \n", 195 | " \n", 196 | " \n", 197 | " \n", 198 | " \n", 199 | " \n", 200 | " \n", 201 | " \n", 202 | " \n", 203 | " \n", 204 | " \n", 205 | " \n", 206 | " \n", 207 | " \n", 208 | " \n", 209 | " \n", 210 | " \n", 211 | " \n", 212 | " \n", 213 | " \n", 214 | " \n", 215 | " \n", 216 | " \n", 217 | " \n", 218 | " \n", 219 | " \n", 220 | " \n", 221 | " \n", 222 | " \n", 223 | " \n", 224 | " \n", 225 | " \n", 226 | " \n", 227 | " \n", 228 | " \n", 229 | " \n", 230 | " \n", 231 | " \n", 232 | " \n", 233 | " \n", 234 | " \n", 235 | " \n", 236 | " \n", 237 | " \n", 238 | " \n", 239 | " \n", 240 | " \n", 241 | " \n", 242 | " \n", 243 | " \n", 244 | " \n", 245 | " \n", 246 | " \n", 247 | " \n", 248 | " \n", 249 | " \n", 250 | " \n", 251 | "
countryvariableattributedescription
ISO-2 digit country codeheat_demandtotalHeat demand for space and water heating
spaceHeat demand for space heating
waterHeat demand for water heating
space_SFHHeat demand for space heating in single-family houses
space_MFHHeat demand for space heating in multi-family houses
space_COMHeat demand for space heating in commercial buildings
water_SFHHeat demand for water heating in single-family houses
water_MFHHeat demand for water heating in multi-family houses
water_COMHeat demand for water heating in commercial buildings
heat_profilespace_SFHNormalized heat demand for space heating in single-family houses
space_MFHNormalized heat demand for space heating in multi-family houses
space_COMNormalized heat demand for space heating in commercial buildings
water_SFHNormalized heat demand for water heating in single-family houses
water_MFHNormalized heat demand for water heating in multi-family houses
water_COMNormalized heat demand for water heating in commercial buildings
COPASHP_floorCOP of air-source heat pumps with floor heating
ASHP_radiatorCOP of air-source heat pumps with radiator heating
ASHP_waterCOP of air-source heat pumps with water heating
GSHP_floorCOP of ground-source heat pumps with floor heating
GSHP_radiatorCOP of ground-source heat pumps with radiator heating
GSHP_waterCOP of ground-source heat pumps with water heating
WSHP_floorCOP of groundwater-source heat pumps with floor heating
WSHP_radiatorCOP of groundwater-source heat pumps with radiator heating
WSHP_waterCOP of groundwater-source heat pumps with water heating
\n", 252 | "
" 253 | ], 254 | "text/plain": [ 255 | "Empty DataFrame\n", 256 | "Columns: []\n", 257 | "Index: [(ISO-2 digit country code, heat_demand, total, Heat demand for space and water heating), (ISO-2 digit country code, heat_demand, space, Heat demand for space heating), (ISO-2 digit country code, heat_demand, water, Heat demand for water heating), (ISO-2 digit country code, heat_demand, space_SFH, Heat demand for space heating in single-family houses), (ISO-2 digit country code, heat_demand, space_MFH, Heat demand for space heating in multi-family houses), (ISO-2 digit country code, heat_demand, space_COM, Heat demand for space heating in commercial buildings), (ISO-2 digit country code, heat_demand, water_SFH, Heat demand for water heating in single-family houses), (ISO-2 digit country code, heat_demand, water_MFH, Heat demand for water heating in multi-family houses), (ISO-2 digit country code, heat_demand, water_COM, Heat demand for water heating in commercial buildings), (ISO-2 digit country code, heat_profile, space_SFH, Normalized heat demand for space heating in single-family houses), (ISO-2 digit country code, heat_profile, space_MFH, Normalized heat demand for space heating in multi-family houses), (ISO-2 digit country code, heat_profile, space_COM, Normalized heat demand for space heating in commercial buildings), (ISO-2 digit country code, heat_profile, water_SFH, Normalized heat demand for water heating in single-family houses), (ISO-2 digit country code, heat_profile, water_MFH, Normalized heat demand for water heating in multi-family houses), (ISO-2 digit country code, heat_profile, water_COM, Normalized heat demand for water heating in commercial buildings), (ISO-2 digit country code, COP, ASHP_floor, COP of air-source heat pumps with floor heating), (ISO-2 digit country code, COP, ASHP_radiator, COP of air-source heat pumps with radiator heating), (ISO-2 digit country code, COP, ASHP_water, COP of air-source heat pumps with water heating), (ISO-2 digit country code, COP, GSHP_floor, COP of ground-source heat pumps with floor heating), (ISO-2 digit country code, COP, GSHP_radiator, COP of ground-source heat pumps with radiator heating), (ISO-2 digit country code, COP, GSHP_water, COP of ground-source heat pumps with water heating), (ISO-2 digit country code, COP, WSHP_floor, COP of groundwater-source heat pumps with floor heating), (ISO-2 digit country code, COP, WSHP_radiator, COP of groundwater-source heat pumps with radiator heating), (ISO-2 digit country code, COP, WSHP_water, COP of groundwater-source heat pumps with water heating)]" 258 | ] 259 | }, 260 | "execution_count": 1, 261 | "metadata": {}, 262 | "output_type": "execute_result" 263 | } 264 | ], 265 | "source": [ 266 | "import pandas as pd; pd.read_csv('input/notation.csv', index_col=list(range(4)))" 267 | ] 268 | }, 269 | { 270 | "cell_type": "markdown", 271 | "metadata": {}, 272 | "source": [ 273 | "\n", 274 | "# 6. License" 275 | ] 276 | }, 277 | { 278 | "cell_type": "markdown", 279 | "metadata": {}, 280 | "source": [ 281 | "This notebook as well as all other documents in this repository is published under the [MIT License](LICENSE.md)." 282 | ] 283 | } 284 | ], 285 | "metadata": { 286 | "anaconda-cloud": {}, 287 | "kernelspec": { 288 | "display_name": "Python 3", 289 | "language": "python", 290 | "name": "python3" 291 | }, 292 | "language_info": { 293 | "codemirror_mode": { 294 | "name": "ipython", 295 | "version": 3 296 | }, 297 | "file_extension": ".py", 298 | "mimetype": "text/x-python", 299 | "name": "python", 300 | "nbconvert_exporter": "python", 301 | "pygments_lexer": "ipython3", 302 | "version": "3.7.10" 303 | }, 304 | "toc": { 305 | "nav_menu": { 306 | "height": "120px", 307 | "width": "252px" 308 | }, 309 | "number_sections": false, 310 | "sideBar": true, 311 | "skip_h1_title": false, 312 | "toc_cell": true, 313 | "toc_position": {}, 314 | "toc_section_display": "block", 315 | "toc_window_display": true 316 | }, 317 | "varInspector": { 318 | "cols": { 319 | "lenName": 16, 320 | "lenType": 16, 321 | "lenVar": 40 322 | }, 323 | "kernels_config": { 324 | "python": { 325 | "delete_cmd_postfix": "", 326 | "delete_cmd_prefix": "del ", 327 | "library": "var_list.py", 328 | "varRefreshCmd": "print(var_dic_list())" 329 | }, 330 | "r": { 331 | "delete_cmd_postfix": ") ", 332 | "delete_cmd_prefix": "rm(", 333 | "library": "var_list.r", 334 | "varRefreshCmd": "cat(var_dic_list()) " 335 | } 336 | }, 337 | "types_to_exclude": [ 338 | "module", 339 | "function", 340 | "builtin_function_or_method", 341 | "instance", 342 | "_Feature" 343 | ], 344 | "window_display": false 345 | } 346 | }, 347 | "nbformat": 4, 348 | "nbformat_minor": 1 349 | } 350 | --------------------------------------------------------------------------------