ebm 0.99.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. ebm/__init__.py +0 -0
  2. ebm/__main__.py +152 -0
  3. ebm/__version__.py +1 -0
  4. ebm/cmd/__init__.py +0 -0
  5. ebm/cmd/calibrate.py +83 -0
  6. ebm/cmd/calibrate_excel_com_io.py +128 -0
  7. ebm/cmd/heating_systems_by_year.py +18 -0
  8. ebm/cmd/helpers.py +134 -0
  9. ebm/cmd/initialize.py +167 -0
  10. ebm/cmd/migrate.py +92 -0
  11. ebm/cmd/pipeline.py +227 -0
  12. ebm/cmd/prepare_main.py +174 -0
  13. ebm/cmd/result_handler.py +272 -0
  14. ebm/cmd/run_calculation.py +221 -0
  15. ebm/data/area.csv +92 -0
  16. ebm/data/area_new_residential_buildings.csv +3 -0
  17. ebm/data/area_per_person.csv +12 -0
  18. ebm/data/building_code_parameters.csv +9 -0
  19. ebm/data/energy_need_behaviour_factor.csv +6 -0
  20. ebm/data/energy_need_improvements.csv +7 -0
  21. ebm/data/energy_need_original_condition.csv +534 -0
  22. ebm/data/heating_system_efficiencies.csv +13 -0
  23. ebm/data/heating_system_forecast.csv +9 -0
  24. ebm/data/heating_system_initial_shares.csv +1113 -0
  25. ebm/data/holiday_home_energy_consumption.csv +24 -0
  26. ebm/data/holiday_home_stock.csv +25 -0
  27. ebm/data/improvement_building_upgrade.csv +9 -0
  28. ebm/data/new_buildings_residential.csv +32 -0
  29. ebm/data/population_forecast.csv +51 -0
  30. ebm/data/s_curve.csv +40 -0
  31. ebm/energy_consumption.py +307 -0
  32. ebm/extractors.py +115 -0
  33. ebm/heating_system_forecast.py +472 -0
  34. ebm/holiday_home_energy.py +341 -0
  35. ebm/migrations.py +224 -0
  36. ebm/model/__init__.py +0 -0
  37. ebm/model/area.py +403 -0
  38. ebm/model/bema.py +149 -0
  39. ebm/model/building_category.py +150 -0
  40. ebm/model/building_condition.py +78 -0
  41. ebm/model/calibrate_energy_requirements.py +84 -0
  42. ebm/model/calibrate_heating_systems.py +180 -0
  43. ebm/model/column_operations.py +157 -0
  44. ebm/model/construction.py +827 -0
  45. ebm/model/data_classes.py +223 -0
  46. ebm/model/database_manager.py +410 -0
  47. ebm/model/dataframemodels.py +115 -0
  48. ebm/model/defaults.py +30 -0
  49. ebm/model/energy_need.py +6 -0
  50. ebm/model/energy_need_filter.py +182 -0
  51. ebm/model/energy_purpose.py +115 -0
  52. ebm/model/energy_requirement.py +353 -0
  53. ebm/model/energy_use.py +202 -0
  54. ebm/model/enums.py +8 -0
  55. ebm/model/exceptions.py +4 -0
  56. ebm/model/file_handler.py +388 -0
  57. ebm/model/filter_scurve_params.py +83 -0
  58. ebm/model/filter_tek.py +152 -0
  59. ebm/model/heat_pump.py +53 -0
  60. ebm/model/heating_systems.py +20 -0
  61. ebm/model/heating_systems_parameter.py +17 -0
  62. ebm/model/heating_systems_projection.py +3 -0
  63. ebm/model/heating_systems_share.py +28 -0
  64. ebm/model/scurve.py +224 -0
  65. ebm/model/tek.py +1 -0
  66. ebm/s_curve.py +515 -0
  67. ebm/services/__init__.py +0 -0
  68. ebm/services/calibration_writer.py +262 -0
  69. ebm/services/console.py +106 -0
  70. ebm/services/excel_loader.py +66 -0
  71. ebm/services/files.py +38 -0
  72. ebm/services/spreadsheet.py +289 -0
  73. ebm/temp_calc.py +99 -0
  74. ebm/validators.py +565 -0
  75. ebm-0.99.3.dist-info/METADATA +217 -0
  76. ebm-0.99.3.dist-info/RECORD +80 -0
  77. ebm-0.99.3.dist-info/WHEEL +5 -0
  78. ebm-0.99.3.dist-info/entry_points.txt +3 -0
  79. ebm-0.99.3.dist-info/licenses/LICENSE +21 -0
  80. ebm-0.99.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,24 @@
1
+ year,electricity,fuelwood,fossilfuel
2
+ 2001,1128,,
3
+ 2002,1173,,
4
+ 2003,1183,,
5
+ 2004,1161,,
6
+ 2005,1235,,
7
+ 2006,1317,880.0,100.0
8
+ 2007,1407,1050.0,
9
+ 2008,1522,1140.0,
10
+ 2009,1635,1090.0,
11
+ 2010,1931,1180.0,
12
+ 2011,1818,990.0,
13
+ 2012,1929,700.0,
14
+ 2013,2141,1000.0,
15
+ 2014,2006,900.0,
16
+ 2015,2118,1180.0,
17
+ 2016,2278,1100.0,
18
+ 2017,2350,1070.0,
19
+ 2018,2417,1230.0,
20
+ 2019,2384,1170.0,
21
+ 2020,2467,1450.0,
22
+ 2021,2819,1270.0,
23
+ 2022,2318,1390.0,
24
+ 2023,2427,1390.0,
@@ -0,0 +1,25 @@
1
+ "year","Existing buildings Chalet, summerhouses and other holiday houses","Existing buildings Detached houses and farmhouses used as holiday houses"
2
+ "2001",354060,23267
3
+ "2002",358997,26514
4
+ "2003",363889,26758
5
+ "2004",368933,26998
6
+ "2005",374470,27376
7
+ "2006",379169,27604
8
+ "2007",383112,27927
9
+ "2008",388938,28953
10
+ "2009",394102,29593
11
+ "2010",398884,30209
12
+ "2011",405883,32374
13
+ "2012",410333,32436
14
+ "2013",413318,32600
15
+ "2014",416621,32539
16
+ "2015",419449,32559
17
+ "2016",423041,32727
18
+ "2017",426932,32808
19
+ "2018",431028,32891
20
+ "2019",434809,32869
21
+ "2020",437833,32906
22
+ "2021",440443,33099
23
+ "2022",445715,33283
24
+ "2023",449009,32819
25
+ "2024",450492,32442
@@ -0,0 +1,9 @@
1
+ building_category,building_code,purpose,building_condition,reduction_share
2
+ default,default,heating_rv,original_condition,0.0
3
+ default,default,heating_rv,small_measure,0.07
4
+ default,default,heating_rv,renovation,0.2
5
+ default,default,heating_rv,renovation_and_small_measure,0.25
6
+ default,TEK17,heating_rv,original_condition,0.0
7
+ default,TEK17,heating_rv,small_measure,0.02
8
+ default,TEK17,heating_rv,renovation,0.05
9
+ default,TEK17,heating_rv,renovation_and_small_measure,0.07
@@ -0,0 +1,32 @@
1
+ "year",new_house_share,new_apartment_block_share,floor_area_new_house,flood_area_new_apartment_block
2
+ "2020","0.4532258064516","0.5467741935484","175","75"
3
+ "2021","0.4514516129032","0.5485483870968","175","75"
4
+ "2022","0.4496774193548","0.5503225806452","175","75"
5
+ "2023","0.4479032258065","0.5520967741935","175","75"
6
+ "2024","0.4461290322581","0.5538709677419","175","75"
7
+ "2025","0.4443548387097","0.5556451612903","175","75"
8
+ "2026","0.4425806451613","0.5574193548387","175","75"
9
+ "2027","0.4408064516129","0.5591935483871","175","75"
10
+ "2028","0.4390322580645","0.5609677419355","175","75"
11
+ "2029","0.4372580645161","0.5627419354839","175","75"
12
+ "2030","0.4354838709677","0.5645161290323","175","75"
13
+ "2031","0.4337096774194","0.5662903225806","175","75"
14
+ "2032","0.4319354838710","0.5680645161290","175","75"
15
+ "2033","0.4301612903226","0.5698387096774","175","75"
16
+ "2034","0.4283870967742","0.5716129032258","175","75"
17
+ "2035","0.4266129032258","0.5733870967742","175","75"
18
+ "2036","0.4248387096774","0.5751612903226","175","75"
19
+ "2037","0.4230645161290","0.5769354838710","175","75"
20
+ "2038","0.4212903225806","0.5787096774194","175","75"
21
+ "2039","0.4195161290323","0.5804838709677","175","75"
22
+ "2040","0.4177419354839","0.5822580645161","175","75"
23
+ "2041","0.4159677419355","0.5840322580645","175","75"
24
+ "2042","0.4141935483871","0.5858064516129","175","75"
25
+ "2043","0.4124193548387","0.5875806451613","175","75"
26
+ "2044","0.4106451612903","0.5893548387097","175","75"
27
+ "2045","0.4088709677419","0.5911290322581","175","75"
28
+ "2046","0.4070967741935","0.5929032258065","175","75"
29
+ "2047","0.4053225806452","0.5946774193548","175","75"
30
+ "2048","0.4035483870968","0.5964516129032","175","75"
31
+ "2049","0.4017741935484","0.5982258064516","175","75"
32
+ "2050","0.4000000000000","0.6000000000000","175","75"
@@ -0,0 +1,51 @@
1
+ year,population,household_size
2
+ 2001,4503436,
3
+ 2002,4524066,
4
+ 2003,4552252,
5
+ 2004,4577457,
6
+ 2005,4606363,
7
+ 2006,4640219,
8
+ 2007,4681134,
9
+ 2008,4737171,
10
+ 2009,4799252,
11
+ 2010,4858199,2.22
12
+ 2011,4920305,2.22
13
+ 2012,4985870,2.22
14
+ 2013,5051275,2.2
15
+ 2014,5109056,2.2
16
+ 2015,5165802,2.2
17
+ 2016,5213985,2.19
18
+ 2017,5258317,2.19
19
+ 2018,5295619,2.17
20
+ 2019,5328212,2.16
21
+ 2020,5367580,2.15
22
+ 2021,5391369,2.14
23
+ 2022,5425270,2.13
24
+ 2023,5488984,2.12
25
+ 2024,5550203,2.115
26
+ 2025,5600121,2.11
27
+ 2026,5638838,2.105
28
+ 2027,5666689,2.1
29
+ 2028,5694657,2.095
30
+ 2029,5722427,2.09
31
+ 2030,5749712,2.085
32
+ 2031,5776723,2.081
33
+ 2032,5803284,2.077
34
+ 2033,5829350,2.073
35
+ 2034,5855072,2.07
36
+ 2035,5880318,2.067
37
+ 2036,5905184,2.064
38
+ 2037,5928866,2.062
39
+ 2038,5951491,2.06
40
+ 2039,5973100,2.059
41
+ 2040,5993766,2.058
42
+ 2041,6013501,2.057
43
+ 2042,6032325,2.056
44
+ 2043,6050194,2.055
45
+ 2044,6067121,2.054
46
+ 2045,6083032,2.053
47
+ 2046,6097893,2.052
48
+ 2047,6111684,2.051
49
+ 2048,6124356,2.05
50
+ 2049,6135899,2.05
51
+ 2050,6146321,2.05
ebm/data/s_curve.csv ADDED
@@ -0,0 +1,40 @@
1
+ building_category,condition,earliest_age_for_measure,average_age_for_measure,rush_period_years,last_age_for_measure,rush_share,never_share
2
+ apartment_block,small_measure,5,20,20,50,0.8,0.1
3
+ apartment_block,renovation,10,30,14,60,0.6,0.15
4
+ apartment_block,demolition,60,90,40,150,0.7,0.05
5
+ house,small_measure,3,23,30,80,0.8,0.01
6
+ house,renovation,10,37,24,75,0.65,0.05
7
+ house,demolition,60,90,40,150,0.7,0.05
8
+ kindergarten,small_measure,3,25,20,50,0.8,0.01
9
+ kindergarten,renovation,10,50,40,90,0.7,0.05
10
+ kindergarten,demolition,50,100,30,130,0.7,0.05
11
+ school,small_measure,5,25,20,50,0.8,0.01
12
+ school,renovation,10,50,44,90,0.65,0.05
13
+ school,demolition,50,100,30,130,0.7,0.05
14
+ university,small_measure,5,25,20,50,0.8,0.01
15
+ university,renovation,15,60,30,100,0.6,0.05
16
+ university,demolition,50,100,30,130,0.7,0.05
17
+ office,small_measure,3,20,20,50,0.8,0.01
18
+ office,renovation,5,55,40,95,0.5,0.1
19
+ office,demolition,50,100,30,130,0.7,0.05
20
+ retail,small_measure,3,20,20,50,0.8,0.01
21
+ retail,renovation,5,48,34,80,0.6,0.1
22
+ retail,demolition,50,100,30,130,0.7,0.05
23
+ hotel,small_measure,3,20,20,50,0.8,0.01
24
+ hotel,renovation,5,45,30,80,0.65,0.02
25
+ hotel,demolition,50,100,30,130,0.7,0.05
26
+ hospital,small_measure,3,20,20,50,0.8,0.01
27
+ hospital,renovation,10,55,30,90,0.7,0.05
28
+ hospital,demolition,50,100,30,130,0.7,0.05
29
+ nursing_home,small_measure,3,20,20,50,0.8,0.01
30
+ nursing_home,renovation,5,45,40,80,0.7,0.05
31
+ nursing_home,demolition,50,100,30,130,0.7,0.05
32
+ culture,small_measure,3,20,20,50,0.8,0.01
33
+ culture,renovation,5,65,40,100,0.75,0.05
34
+ culture,demolition,50,100,30,130,0.7,0.05
35
+ sports,small_measure,3,20,20,50,0.8,0.01
36
+ sports,renovation,15,70,40,120,0.65,0.05
37
+ sports,demolition,50,100,30,130,0.7,0.05
38
+ storage_repairs,small_measure,3,20,20,50,0.8,0.01
39
+ storage_repairs,renovation,5,45,40,80,0.7,0.05
40
+ storage_repairs,demolition,50,100,30,130,0.7,0.05
@@ -0,0 +1,307 @@
1
+ from loguru import logger
2
+ import pandas as pd
3
+
4
+ from ebm.model.energy_purpose import EnergyPurpose
5
+ from ebm.model.filter_tek import FilterTek
6
+
7
+ ADJUSTED_REQUIREMENT = 'eq_ts'
8
+ # there are 3 time-of-use zones: peak, shoulder and offpeak.
9
+ HEATING_RV_BASE_TOTAL = 'RV_GL'
10
+ HEATING_RV_PEAK_TOTAL = 'RV_SL'
11
+ HEATING_RV_TERTIARY_TOTAL = 'RV_EL'
12
+ HEAT_PUMP = 'RV_HP'
13
+ COOLING_TOTAL = 'CL_KV'
14
+ DHW_TOTAL = 'DHW_TV'
15
+ OTHER_TOTAL = 'O_SV'
16
+ HEATING_RV = 'heating_rv'
17
+ HEATING_DHW = 'heating_dhw'
18
+ COOLING = 'cooling'
19
+ DHW_EFFICIENCY = 'domestic_hot_water_efficiency'
20
+
21
+ HEATING_SYSTEMS = 'heating_systems'
22
+ HEATING_SYSTEM_SHARE = 'heating_system_share'
23
+ GRUNNLAST_ANDEL = 'base_load_coverage'
24
+ BASE_LOAD_EFFICIENCY = 'base_load_efficiency'
25
+ COOLING_EFFICIENCY = 'cooling_efficiency'
26
+ SPESIFIKT_ELFORBRUK = 'Spesifikt elforbruk'
27
+ TERTIARY_LOAD_COVERAGE = 'tertiary_load_coverage'
28
+ TERTIARY_LOAD_EFFICIENCY = 'tertiary_load_efficiency'
29
+ PEAK_LOAD_COVERAGE = 'peak_load_coverage'
30
+ PEAK_LOAD_EFFICIENCY = 'peak_load_efficiency'
31
+
32
+ BASE_LOAD_ENERGY_PRODUCT = 'base_load_energy_product'
33
+ PEAK_LOAD_ENERGY_PRODUCT = 'peak_load_energy_product'
34
+ TERTIARY_LOAD_ENERGY_PRODUCT = 'tertiary_load_energy_product'
35
+ DOMESTIC_HOT_WATER_ENERGY_PRODUCT = 'domestic_hot_water_energy_product'
36
+ HP_ENERGY_SOURCE = 'hp_source'
37
+
38
+
39
+ class EnergyConsumption:
40
+ def __init__(self, heating_systems_parameters: pd.DataFrame = None):
41
+ self.heating_systems_parameters = heating_systems_parameters
42
+
43
+ def grouped_heating_systems(self) -> pd.DataFrame:
44
+ """
45
+ Groups and sums heating_system_parameters over building_category, TEK, Oppvarmingstyper. All excess values will
46
+ be summed.
47
+
48
+ Returns
49
+ -------
50
+ pd.DataFrame
51
+ heating_systems_parameters grouped and summed
52
+ """
53
+ df = self.heating_systems_parameters
54
+ df = df.rename(columns={'heating_system_share': HEATING_SYSTEM_SHARE})
55
+
56
+ aggregates = {'base_load_energy_product': 'first', 'peak_load_energy_product': 'first',
57
+ 'tertiary_load_energy_product': 'first', 'domestic_hot_water_energy_product': 'first', HEATING_SYSTEM_SHARE: 'sum',
58
+ TERTIARY_LOAD_COVERAGE: 'sum', GRUNNLAST_ANDEL: 'sum', PEAK_LOAD_COVERAGE: 'sum',
59
+ BASE_LOAD_EFFICIENCY: 'sum', PEAK_LOAD_EFFICIENCY: 'sum', TERTIARY_LOAD_EFFICIENCY: 'sum',
60
+ DHW_EFFICIENCY: 'sum', SPESIFIKT_ELFORBRUK: 'sum', COOLING_EFFICIENCY: 'sum'}
61
+ grouped = df.groupby(by=['building_category', 'building_code', 'year', HEATING_SYSTEMS]).agg(aggregates)
62
+ return grouped.reset_index()
63
+
64
+ def calculate(self, energy_requirements: pd.DataFrame) -> pd.DataFrame:
65
+ """
66
+ calculate energy usage by from energy_requirements and heating_systems_parameters
67
+
68
+ Parameters
69
+ ----------
70
+ energy_requirements : pd.DataFrame
71
+
72
+ Returns
73
+ -------
74
+ pd.DataFrame
75
+
76
+ """
77
+ logger.debug('Calculate heating systems')
78
+ if all([col in energy_requirements.columns for col in ['building_category', 'building_code', 'building_condition', 'year', 'purpose']]):
79
+ energy_requirements = energy_requirements.set_index(['building_category', 'building_code', 'building_condition', 'year', 'purpose'])
80
+ energy_requirements = self._remove_building_code_suffix(energy_requirements)
81
+ energy_requirements = self._group_and_sum_same_building_code(energy_requirements)
82
+
83
+ # If _RES of _COM is in building_codethis will not work
84
+ # energy_requirements.index[energy_requirements.index.str.endswith('_RES')]
85
+ if energy_requirements.index.get_level_values('building_code').str.endswith(
86
+ '_RES').any() or energy_requirements.index.get_level_values('building_code').str.endswith('_COM').any():
87
+ raise ValueError('Found _RES or _COM in energy_requirements')
88
+ self.heating_systems_parameters = self.heating_systems_parameters.rename(columns={'heating_system_share': HEATING_SYSTEM_SHARE})
89
+ # Merge energy_requirements and heating_systems into df
90
+ df = self._merge_energy_requirement_and_heating_systems(energy_requirements)
91
+
92
+ # Make column eq_ts for heating_system_share adjusted energy requirement
93
+ df[ADJUSTED_REQUIREMENT] = (df.energy_requirement * df[HEATING_SYSTEM_SHARE]).astype(float)
94
+
95
+ # Zero fill columns before calculating to prevent NaN from messing up sums
96
+ df.loc[:, [HEATING_RV_BASE_TOTAL, HEATING_RV_PEAK_TOTAL, HEATING_RV_TERTIARY_TOTAL, DHW_TOTAL, COOLING_TOTAL, OTHER_TOTAL,
97
+ HEAT_PUMP]] = 0.0
98
+
99
+ # Adjust energy requirements by efficiency
100
+ # heating rv
101
+
102
+ df = self.adjust_heat_pump(df)
103
+ df = self.adjust_heating_rv(df)
104
+ df = self.adjust_heating_dhw(df)
105
+ df = self.adjust_cooling(df)
106
+ df = self.adjust_other(df)
107
+
108
+ # sum energy use
109
+ df.loc[:, 'kwh'] = df.loc[:,
110
+ [HEATING_RV_BASE_TOTAL, HEATING_RV_PEAK_TOTAL, HEATING_RV_TERTIARY_TOTAL, DHW_TOTAL, COOLING_TOTAL,
111
+ OTHER_TOTAL]].sum(axis=1)
112
+
113
+ df.loc[:, 'gwh'] = df.loc[:, 'kwh'] / 10 ** 6
114
+
115
+ df = df.sort_index(level=['building_category', 'building_code', 'year', 'building_condition', 'purpose', HEATING_SYSTEMS])
116
+ return df[[HEATING_SYSTEM_SHARE, ADJUSTED_REQUIREMENT,
117
+ HEATING_RV_BASE_TOTAL, BASE_LOAD_ENERGY_PRODUCT, BASE_LOAD_EFFICIENCY,
118
+ HEATING_RV_PEAK_TOTAL, PEAK_LOAD_ENERGY_PRODUCT, PEAK_LOAD_EFFICIENCY,
119
+ HEATING_RV_TERTIARY_TOTAL, TERTIARY_LOAD_ENERGY_PRODUCT, TERTIARY_LOAD_EFFICIENCY,
120
+ DHW_TOTAL, DOMESTIC_HOT_WATER_ENERGY_PRODUCT, COOLING_TOTAL, OTHER_TOTAL, HEAT_PUMP, HP_ENERGY_SOURCE, 'kwh', 'gwh']]
121
+
122
+ def adjust_heat_pump(self, df):
123
+ df[HP_ENERGY_SOURCE] = None
124
+ gass = ['HP Central heating - Gas']
125
+ vannbasert = [n for n in df.index.get_level_values('heating_systems').unique() if
126
+ n.startswith('HP Central heating')]
127
+ elektrisk = [n for n in df.index.get_level_values('heating_systems').unique() if
128
+ n.startswith('HP') and n not in vannbasert]
129
+
130
+ gass_slice = (slice(None), slice(None), slice(None), slice(None), gass)
131
+ vann_slice = (slice(None), slice(None), ['heating_rv', 'heating_dhw'], slice(None), slice(None),
132
+ vannbasert) # , 'heating_dhw'
133
+ el_slice = (slice(None), slice(None), ['heating_rv'], slice(None), slice(None), elektrisk) # 'heating_dhw'
134
+
135
+ df.loc[vann_slice, HEAT_PUMP] = df.loc[vann_slice, ADJUSTED_REQUIREMENT] * df.loc[vann_slice, GRUNNLAST_ANDEL]
136
+ df.loc[vann_slice, HP_ENERGY_SOURCE] = 'Vannbåren varme'
137
+
138
+ df.loc[el_slice, HEAT_PUMP] = df.loc[el_slice, ADJUSTED_REQUIREMENT] * df.loc[el_slice, GRUNNLAST_ANDEL]
139
+ df.loc[el_slice, HP_ENERGY_SOURCE] = 'Luft/luft'
140
+ return df
141
+
142
+ def adjust_other(self, df):
143
+ # lighting, electrical equipment, fans and pumps energy use is calculated by dividing with spesific electricity
144
+ # useage
145
+ other_slice = (slice(None), slice(None), EnergyPurpose.other(), slice(None), slice(None))
146
+ df.loc[other_slice, OTHER_TOTAL] = df.loc[other_slice, ADJUSTED_REQUIREMENT] / df.loc[
147
+ other_slice, SPESIFIKT_ELFORBRUK]
148
+ return df
149
+
150
+ def adjust_cooling(self, df):
151
+ # cooling energy use is calculated by dividing cooling with 'cooling_efficiency'
152
+ cooling_slice = (slice(None), slice(None), COOLING, slice(None), slice(None))
153
+ df.loc[cooling_slice, COOLING_TOTAL] = df.loc[cooling_slice, ADJUSTED_REQUIREMENT] / df.loc[
154
+ cooling_slice, COOLING_EFFICIENCY]
155
+ return df
156
+
157
+ def adjust_heating_dhw(self, df):
158
+ # heating dhw energy use is calculated by dividing heating_dhw with 'domestic_hot_water_efficiency'
159
+ heating_dhw_slice = (slice(None), slice(None), HEATING_DHW, slice(None), slice(None))
160
+ df.loc[heating_dhw_slice, DHW_TOTAL] = df.loc[heating_dhw_slice, ADJUSTED_REQUIREMENT] / df.loc[
161
+ heating_dhw_slice, DHW_EFFICIENCY]
162
+ return df
163
+
164
+ def adjust_heating_rv(self, df):
165
+ heating_rv_slice = (slice(None), slice(None), HEATING_RV, slice(None), slice(None))
166
+ df.loc[heating_rv_slice, HEATING_RV_BASE_TOTAL] = (
167
+ df.loc[heating_rv_slice, ADJUSTED_REQUIREMENT] * df.loc[heating_rv_slice, GRUNNLAST_ANDEL] / df.loc[
168
+ heating_rv_slice, BASE_LOAD_EFFICIENCY])
169
+ df.loc[heating_rv_slice, HEATING_RV_PEAK_TOTAL] = (
170
+ df.loc[heating_rv_slice, ADJUSTED_REQUIREMENT] * df.loc[heating_rv_slice, PEAK_LOAD_COVERAGE] / df.loc[
171
+ heating_rv_slice, PEAK_LOAD_EFFICIENCY])
172
+ df.loc[heating_rv_slice, HEATING_RV_TERTIARY_TOTAL] = (
173
+ df.loc[heating_rv_slice, ADJUSTED_REQUIREMENT] * df.loc[heating_rv_slice, TERTIARY_LOAD_COVERAGE] / df.loc[
174
+ heating_rv_slice, TERTIARY_LOAD_EFFICIENCY])
175
+ return df
176
+
177
+ def _merge_energy_requirement_and_heating_systems(self, energy_requirements):
178
+ df = energy_requirements.reset_index().merge(self.heating_systems_parameters.reset_index(),
179
+ left_on=['building_category', 'building_code', 'year'], right_on=['building_category', 'building_code', 'year'])[
180
+ ['building_category', 'building_condition', 'purpose', 'building_code', 'year', 'kwh_m2', 'm2', 'energy_requirement',
181
+ HEATING_SYSTEMS, HEATING_SYSTEM_SHARE, GRUNNLAST_ANDEL, BASE_LOAD_EFFICIENCY, BASE_LOAD_ENERGY_PRODUCT,
182
+ PEAK_LOAD_COVERAGE, PEAK_LOAD_EFFICIENCY, PEAK_LOAD_ENERGY_PRODUCT, TERTIARY_LOAD_EFFICIENCY, TERTIARY_LOAD_COVERAGE,
183
+ TERTIARY_LOAD_ENERGY_PRODUCT, DOMESTIC_HOT_WATER_ENERGY_PRODUCT, DHW_EFFICIENCY, SPESIFIKT_ELFORBRUK, COOLING_EFFICIENCY]]
184
+ # Unused columns
185
+ # ,'Innfyrt_energi_kWh','Innfyrt_energi_GWh','Energibehov_samlet_GWh']]
186
+ df = df.set_index(
187
+ ['building_category', 'building_condition', 'purpose', 'building_code', 'year', HEATING_SYSTEMS]).sort_index()
188
+ return df
189
+
190
+ @staticmethod
191
+ def _group_and_sum_same_building_code(energy_requirements):
192
+ energy_requirements = FilterTek.merge_building_code(energy_requirements, 'TEK69', ['TEK69_1976', 'TEK69_1986'])
193
+ energy_requirements = FilterTek.merge_building_code(energy_requirements, 'PRE_TEK49',
194
+ ['PRE_TEK49_1940', 'PRE_TEK49_1950'])
195
+ energy_requirements = energy_requirements.sort_index()
196
+ return energy_requirements
197
+
198
+ @staticmethod
199
+ def _remove_building_code_suffix(energy_requirements):
200
+ energy_requirements = FilterTek.remove_building_code_suffix(energy_requirements, suffix='_RES')
201
+ energy_requirements = FilterTek.remove_building_code_suffix(energy_requirements, suffix='_COM')
202
+ return energy_requirements
203
+
204
+
205
+ def calibrate_heating_systems(df: pd.DataFrame, factor: pd.DataFrame, multiply=False) -> pd.DataFrame:
206
+ # When factor is empty or all factors are 1.0, there is no need to change anything.
207
+ if len(factor) == 0 or (factor.factor == 1.0).all():
208
+ return df
209
+ original = df.copy()
210
+ factor = factor.copy()
211
+
212
+ if multiply:
213
+ factor['factor'] = factor['factor'] * 100
214
+
215
+ # Add action and value
216
+ df_to = original.merge(factor,
217
+ left_on=['building_category', 'heating_systems'],
218
+ right_on=['building_category', 'to'])
219
+
220
+ df_from = original.merge(factor,
221
+ left_on=['building_category', 'heating_systems'],
222
+ right_on=['building_category', 'from'])
223
+
224
+ # Calculate value to add
225
+ df_to_add = df_to.set_index(['building_category', 'building_code', 'year', 'to'])
226
+
227
+ if multiply:
228
+ df_to_add['v'] = (df_to_add.heating_system_share * df_to_add.factor)/100 - df_to_add.heating_system_share
229
+ else:
230
+ df_to_add['v'] = df_to_add.heating_system_share * df_to_add.factor - df_to_add.heating_system_share
231
+
232
+ # Calculate value to subtract
233
+ df_to_subtract = df_from.set_index(['building_category', 'building_code', 'year', 'from', 'to'])
234
+
235
+ df_to_subtract = df_to_subtract.sort_index()
236
+ df_to_subtract.loc[:, 'v'] = -df_to_add.reset_index().groupby(by=['building_category', 'building_code', 'year', 'from', 'to']).agg(
237
+ {'v': 'sum'})
238
+
239
+ # Join add and substract rows
240
+ addition_grouped = df_to_add.groupby(by=['building_category', 'building_code', 'year', 'to']).agg(
241
+ {'v': 'sum', 'heating_systems': 'first', 'heating_system_share': 'first', 'factor': 'first', 'from': 'first'})
242
+ subtraction_grouped = df_to_subtract.groupby(by=['building_category', 'building_code', 'year', 'from']).agg(
243
+ {'v': 'sum', 'heating_systems': 'first', 'heating_system_share': 'first', 'factor': 'first'})
244
+
245
+ df_to_sum = original.set_index(['building_category', 'building_code', 'year', 'heating_systems'])
246
+ df_to_sum = df_to_sum.sort_index()
247
+ df_to_sum.loc[:, 'add'] = addition_grouped.loc[:, 'v']
248
+ df_to_sum.loc[:, 'sub'] = subtraction_grouped.loc[:, 'v'].fillna(0)
249
+ df_to_sum.loc[:, 'sub'] = df_to_sum.loc[:, 'sub'].fillna(0)
250
+
251
+ df_to_sum.heating_system_share = df_to_sum.heating_system_share + df_to_sum['add'].fillna(0) + df_to_sum['sub'].fillna(0)
252
+
253
+ calibrated_and_original = pd.concat([df_to_sum.reset_index(), original.reset_index()]).drop_duplicates(
254
+ ['building_category', 'building_code', 'year', 'heating_systems'],
255
+ keep='first').drop(columns='index',
256
+ errors='ignore')
257
+
258
+ columns_to_keep = ['building_category', 'building_code', 'year', 'heating_systems', 'heating_system_share']
259
+ return calibrated_and_original[columns_to_keep].reset_index(drop=True)
260
+
261
+
262
+ def calibrate_heating_systems_adder(df: pd.DataFrame, factor: pd.DataFrame) -> pd.DataFrame:
263
+ # When factor is empty or all factors are 1.0, there is no need to change anything.
264
+ if len(factor) == 0 or (factor.factor == 0.0).all():
265
+ return df
266
+ original = df.copy()
267
+ factor = factor.copy()
268
+
269
+ # Add action and value
270
+ df_to = original.merge(factor,
271
+ left_on=['building_category', 'heating_systems'],
272
+ right_on=['building_category', 'to'])
273
+
274
+ df_from = original.merge(factor,
275
+ left_on=['building_category', 'heating_systems'],
276
+ right_on=['building_category', 'from'])
277
+
278
+ # Calculate value to add
279
+ df_to_add = df_to.set_index(['building_category', 'building_code', 'year', 'to'])
280
+
281
+ df_to_add['v'] = df_to_add.factor
282
+
283
+ # Calculate value to subtract
284
+ df_to_subtract = df_from.set_index(['building_category', 'building_code', 'year', 'from', 'to'])
285
+ df_to_subtract.loc[:, 'v'] = -df_to_add.reset_index().groupby(by=['building_category', 'building_code', 'year', 'from', 'to']).agg(
286
+ {'v': 'sum'})
287
+
288
+ # Join add and substract rows
289
+ addition_grouped = df_to_add.groupby(by=['building_category', 'building_code', 'year', 'to']).agg(
290
+ {'v': 'sum', 'heating_systems': 'first', 'heating_system_share': 'first', 'factor': 'first', 'from': 'first'})
291
+ subtraction_grouped = df_to_subtract.groupby(by=['building_category', 'building_code', 'year', 'from']).agg(
292
+ {'v': 'sum', 'heating_systems': 'first', 'heating_system_share': 'first', 'factor': 'first'})
293
+
294
+ df_to_sum = original.set_index(['building_category', 'building_code', 'year', 'heating_systems'])
295
+ df_to_sum.loc[:, 'add'] = addition_grouped.loc[:, 'v']
296
+ df_to_sum.loc[:, 'sub'] = subtraction_grouped.loc[:, 'v'].fillna(0)
297
+ df_to_sum.loc[:, 'sub'] = df_to_sum.loc[:, 'sub'].fillna(0)
298
+
299
+ df_to_sum.heating_system_share = df_to_sum.heating_system_share + df_to_sum['add'].fillna(0) + df_to_sum['sub'].fillna(0)
300
+
301
+ calibrated_and_original = pd.concat([df_to_sum.reset_index(), original.reset_index()]).drop_duplicates(
302
+ ['building_category', 'building_code', 'year', 'heating_systems'],
303
+ keep='first').drop(columns='index',
304
+ errors='ignore')
305
+
306
+ columns_to_keep = ['building_category', 'building_code', 'year', 'heating_systems', 'heating_system_share']
307
+ return calibrated_and_original[columns_to_keep].reset_index(drop=True)
ebm/extractors.py ADDED
@@ -0,0 +1,115 @@
1
+ import pandas as pd
2
+ from loguru import logger
3
+
4
+ from ebm.model import area
5
+ from ebm.holiday_home_energy import calculate_energy_use, transform_holiday_homes_to_horizontal
6
+ from ebm.heating_system_forecast import HeatingSystemsForecast
7
+ from ebm.model.construction import ConstructionCalculator
8
+
9
+ from ebm.model.data_classes import YearRange
10
+ from ebm.model.database_manager import DatabaseManager
11
+ from ebm.model.energy_requirement import EnergyRequirement
12
+ from ebm.s_curve import calculate_s_curves
13
+
14
+
15
+ def extract_area_forecast(years: YearRange, s_curves_by_condition: pd.DataFrame, building_code_parameters: pd.DataFrame, area_parameters: pd.DataFrame, database_manager:DatabaseManager):
16
+ logger.debug('Calculating area by condition')
17
+
18
+ s_curve_demolition = s_curves_by_condition['s_curve_demolition']
19
+ s_curves_by_condition = s_curves_by_condition[[
20
+ 'original_condition', 'small_measure', 'renovation', 'renovation_and_small_measure', 'demolition'
21
+ ]]
22
+
23
+ area_parameters = area_parameters.set_index(['building_category', 'building_code'])
24
+
25
+ demolition_floor_area_by_year = area.calculate_demolition_floor_area_by_year(area_parameters, s_curve_demolition)
26
+
27
+ building_category_demolition_by_year = area.sum_building_category_demolition_by_year(demolition_floor_area_by_year)
28
+ construction_floor_area_by_year = ConstructionCalculator.calculate_all_construction(
29
+ demolition_by_year=building_category_demolition_by_year,
30
+ database_manager=database_manager,
31
+ period=years)
32
+
33
+ construction_by_building_category_and_year = area.construction_with_building_code(
34
+ building_category_demolition_by_year=building_category_demolition_by_year,
35
+ construction_floor_area_by_year=construction_floor_area_by_year,
36
+ building_code=building_code_parameters,
37
+ years=years)
38
+
39
+ existing_area = area.calculate_existing_area(area_parameters, building_code_parameters, years)
40
+
41
+ total_area_floor_by_year = area.merge_total_area_by_year(construction_by_building_category_and_year, existing_area)
42
+
43
+ floor_area_forecast = area.multiply_s_curves_with_floor_area(s_curves_by_condition, total_area_floor_by_year)
44
+
45
+ return floor_area_forecast
46
+
47
+
48
+ def write_scurve(s_curves_by_condition):
49
+ try:
50
+ output_file = 'output/s_curves.xlsx'
51
+ with pd.ExcelWriter(output_file, engine='xlsxwriter') as writer:
52
+ s_curves_by_condition.to_excel(writer, sheet_name='s_curves_by_condition', merge_cells=False) # 💾
53
+ # s_curve_demolition.to_excel(writer, sheet_name='s_curve_demolition', merge_cells=False) # 💾
54
+ except IOError as ex:
55
+ logger.exception(ex)
56
+ logger.info(f'There was an IOError while writing to {output_file}. Moving on!')
57
+
58
+
59
+ def extract_energy_need(years: YearRange, dm: DatabaseManager) -> pd.DataFrame:
60
+ er_calculator = EnergyRequirement.new_instance(period=years, calibration_year=2023,
61
+ database_manager=dm)
62
+ energy_need = er_calculator.calculate_for_building_category(database_manager=dm)
63
+
64
+ energy_need = energy_need.set_index(['building_category', 'building_code', 'purpose', 'building_condition', 'year'])
65
+
66
+ return energy_need
67
+
68
+
69
+ def extract_heating_systems_forecast(years: YearRange, database_manager: DatabaseManager) -> pd.DataFrame:
70
+ forecast_period = YearRange(2023, 2050)
71
+ hsp = HeatingSystemsForecast.new_instance(forecast_period, database_manager)
72
+ df: pd.DataFrame = hsp.calculate_forecast()
73
+ df = hsp.pad_projection(df, YearRange(2020, 2022))
74
+
75
+ heating_system_forecast = df.copy()
76
+ return heating_system_forecast
77
+
78
+
79
+ def extract_energy_use_holiday_homes(database_manager):
80
+ df = transform_holiday_homes_to_horizontal(calculate_energy_use(database_manager)).copy()
81
+ df = df.rename(columns={'building_category': 'building_group'})
82
+ df.loc[df.energy_source=='Elektrisitet', 'energy_source'] = 'Electricity'
83
+ df.loc[df.energy_source=='fossil', 'energy_source'] = 'Fossil'
84
+ return df
85
+
86
+
87
+ def main():
88
+ from ebm.model.file_handler import FileHandler
89
+ fh = FileHandler(directory='input')
90
+ dm = DatabaseManager(fh)
91
+ years = YearRange(2020, 2050)
92
+
93
+ building_code_parameters = fh.get_building_code()
94
+ scurve_params = dm.get_scurve_params()
95
+ s_curves_by_condition = calculate_s_curves(scurve_params, building_code_parameters, years)
96
+
97
+ area_forecast = extract_area_forecast(years,
98
+ building_code_parameters=building_code_parameters,
99
+ area_parameters=dm.get_area_parameters(),
100
+ s_curves_by_condition=s_curves_by_condition,
101
+ database_manager=dm)
102
+
103
+ print(area_forecast)
104
+
105
+ energy_need_kwh_m2 = extract_energy_need(years, dm)
106
+ print(energy_need_kwh_m2)
107
+
108
+ heating_systems_projection = extract_heating_systems_forecast(years, dm)
109
+ print(heating_systems_projection)
110
+
111
+
112
+
113
+
114
+ if __name__ == '__main__':
115
+ main()