ebm 0.99.5__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.5.dist-info/METADATA +212 -0
  76. ebm-0.99.5.dist-info/RECORD +80 -0
  77. ebm-0.99.5.dist-info/WHEEL +5 -0
  78. ebm-0.99.5.dist-info/entry_points.txt +3 -0
  79. ebm-0.99.5.dist-info/licenses/LICENSE +21 -0
  80. ebm-0.99.5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,20 @@
1
+ from enum import unique, StrEnum
2
+
3
+ @unique
4
+ class HeatingSystems(StrEnum):
5
+ ELECTRICITY = 'Electricity'
6
+ ELECTRICITY_BIO = 'Electricity - Bio'
7
+ ELECTRIC_BOILER = 'Electric boiler'
8
+ ELECTRIC_BOILER_SOLAR = 'Electric boiler - Solar'
9
+ GAS = 'Gas'
10
+ DISTRICT_HEATING = 'DH'
11
+ DISTRICT_HEATING_BIO = 'DH - Bio'
12
+ HP_BIO = 'HP - Bio - Electricity'
13
+ HP_ELECTRICITY = 'HP - Electricity'
14
+ HP_CENTRAL_HEATING = 'HP Central heating - Electric boiler'
15
+ HP_CENTRAL_HEATING_GAS = 'HP Central heating - Gas'
16
+ HP_CENTRAL_HEATING_BIO = 'HP Central heating - Bio'
17
+
18
+
19
+ def __repr__(self):
20
+ return f'{self.__class__.__name__}.{self.name}'
@@ -0,0 +1,17 @@
1
+ import pandas as pd
2
+
3
+ from ebm.energy_consumption import EnergyConsumption
4
+
5
+
6
+ def heating_systems_parameter_from_projection(heating_systems_projection: pd.DataFrame) -> pd.DataFrame:
7
+ calculator = EnergyConsumption(heating_systems_projection.copy())
8
+
9
+ return calculator.grouped_heating_systems()
10
+
11
+
12
+ def expand_heating_system_parameters(heating_systems_parameter):
13
+ df = heating_systems_parameter
14
+ df = df.assign(**{'heating_system': df['heating_systems'].str.split('-')}).explode('heating_system')
15
+ df['heating_system'] = df['heating_system'].str.strip()
16
+ df['load_share'] = df['base_load_coverage']
17
+ return df
@@ -0,0 +1,3 @@
1
+
2
+
3
+
@@ -0,0 +1,28 @@
1
+ import pandas as pd
2
+
3
+ from ebm.model.bema import BUILDING_CATEGORY_ORDER
4
+
5
+
6
+ def transform_heating_systems_share_long(heating_systems_projection: pd.DataFrame) -> pd.DataFrame:
7
+ df = heating_systems_projection.copy()
8
+
9
+ fane2_columns = ['building_category', 'heating_systems', 'year', 'heating_system_share']
10
+
11
+ df.loc[~df['building_category'].isin(['house', 'apartment_block']), 'building_category'] = 'non_residential'
12
+
13
+ mean_heating_system_shares_yearly = df[fane2_columns].groupby(by=['year', 'building_category', 'heating_systems']).mean()
14
+ return mean_heating_system_shares_yearly
15
+
16
+
17
+ def transform_heating_systems_share_wide(heating_systems_share_long: pd.DataFrame) -> pd.DataFrame:
18
+ value_column = 'heating_system_share'
19
+ df = heating_systems_share_long.copy().reset_index()
20
+ df = df.pivot(columns=['year'], index=['building_category', 'heating_systems'], values=[value_column]).reset_index()
21
+
22
+ df = df.sort_values(by=['building_category', 'heating_systems'],
23
+ key=lambda x: x.map(BUILDING_CATEGORY_ORDER) if x.name == 'building_category' else x)
24
+ df.insert(2, 'U', value_column)
25
+ df['U'] = '%'
26
+
27
+ df.columns = ['building_category', 'heating_systems', 'U'] + [y for y in range(2020, 2051)]
28
+ return df
ebm/model/scurve.py ADDED
@@ -0,0 +1,224 @@
1
+ import pandas as pd
2
+ from loguru import logger
3
+
4
+
5
+ class SCurve:
6
+ """
7
+ Calculates S-curve per building condition.
8
+
9
+ Raises
10
+ ------
11
+ ValueError
12
+ When any of the arguments are less than zero
13
+
14
+ Notes
15
+ -----
16
+ To make calculations return better rounded more and accurate results, _rush_share and _never_share area
17
+ multiplied by 100 internally. _calc_pre_rush_rate() _calc_rush_rate() _calc_post_rush_rate() will
18
+ still return percent as a value between 0 and 1.
19
+ """
20
+
21
+ # TODO:
22
+ # - add check to control that defined periods match building lifetime index in get_rates_per_year
23
+
24
+ earliest_age: int = 0
25
+ average_age: int
26
+ last_age: int
27
+ rush_years: int
28
+ rush_share: float
29
+ never_share: float
30
+ building_lifetime: int = 130
31
+
32
+ def __init__(self,
33
+ earliest_age: int,
34
+ average_age: int,
35
+ last_age: int,
36
+ rush_years: int,
37
+ rush_share: float,
38
+ never_share: float,
39
+ building_lifetime: int = 130):
40
+ errors = []
41
+ if earliest_age < 0:
42
+ logger.warning(f'Expected value above zero for {earliest_age=}')
43
+ errors.append('earliest_age')
44
+ if average_age < 0:
45
+ logger.warning(f'Expected value above zero for {average_age=}')
46
+ errors.append('average_age')
47
+ if last_age < 0:
48
+ logger.warning(f'Expected value above zero for {last_age=}')
49
+ errors.append('last_age')
50
+ if rush_share < 0:
51
+ logger.warning(f'Expected value above zero for {rush_share=}')
52
+ errors.append('rush_share')
53
+ if never_share < 0:
54
+ logger.warning(f'Expected value above zero for {never_share=}')
55
+ errors.append('never_share')
56
+ if errors:
57
+ msg = f'Illegal value for {" ".join(errors)}'
58
+ raise ValueError(msg)
59
+
60
+ self._building_lifetime = building_lifetime
61
+ self._earliest_age = earliest_age
62
+ self._average_age = average_age
63
+ self._last_age = last_age
64
+ self._rush_years = rush_years
65
+ self._rush_share = rush_share * 100
66
+ self._never_share = never_share * 100
67
+
68
+ # Calculate yearly rates
69
+ self._pre_rush_rate = self._calc_pre_rush_rate()
70
+ self._rush_rate = self._calc_rush_rate()
71
+ self._post_rush_rate = self._calc_post_rush_rate()
72
+
73
+ # Calculate S-curves
74
+ self.scurve = self.calc_scurve()
75
+
76
+ def _calc_pre_rush_rate(self) -> float:
77
+ """
78
+ Calculate the yearly measure rate for the pre-rush period.
79
+
80
+ The pre-rush rate represents the percentage share of building area that has
81
+ undergone a measure per year during the period before the rush period begins.
82
+
83
+ Returns
84
+ -------
85
+ float
86
+ Yearly measure rate in the pre-rush period.
87
+
88
+ Notes
89
+ -----
90
+ To make calculations return better rounded more and accurate results, _rush_share and _never_share area
91
+ multiplied by 100 internally. _calc_pre_rush_rate() _calc_rush_rate() _calc_post_rush_rate() will
92
+ still return percent as a value between 0 and 1.
93
+ """
94
+ remaining_share = (100 - self._rush_share - self._never_share)
95
+ age_range = (50 / (self._average_age - self._earliest_age - (self._rush_years / 2)))
96
+ pre_rush_rate = remaining_share * age_range / 100
97
+ return round(pre_rush_rate / 100, 13)
98
+
99
+ def _calc_rush_rate(self) -> float:
100
+ """
101
+ Calculate the yearly measure rate for the rush period.
102
+
103
+ The rush rate represents the percentage share of building area that has
104
+ undergone a measure per year during the rush period.
105
+
106
+ Returns
107
+ -------
108
+ float
109
+ Yearly rate in the rush period.
110
+
111
+ Notes
112
+ -----
113
+ To make calculations return better rounded more and accurate results, _rush_share and _never_share area
114
+ multiplied by 100 internally. _calc_pre_rush_rate() _calc_rush_rate() _calc_post_rush_rate() will
115
+ still return percent as a value between 0 and 1.
116
+ """
117
+ rush_rate = self._rush_share / self._rush_years
118
+ return round(rush_rate / 100, 13)
119
+
120
+ def _calc_post_rush_rate(self) -> float:
121
+ """
122
+ Calculate the yearly measure rate for the post-rush period.
123
+
124
+ The post-rush rate represents the percentage share of building area that has
125
+ undergone a measure per year during the period after the rush period ends.
126
+
127
+ Returns
128
+ -------
129
+ float
130
+ Yearly rate in the post-rush period.
131
+
132
+ Notes
133
+ -----
134
+ To make calculations return better rounded more and accurate results, _rush_share and _never_share area
135
+ multiplied by 100 internally. _calc_pre_rush_rate() _calc_rush_rate() _calc_post_rush_rate() will
136
+ still return percent as a value between 0 and 1.
137
+ """
138
+ remaining_share = (100 - self._rush_share - self._never_share)
139
+ age_range = (50 / (self._last_age - self._average_age - (self._rush_years / 2)))
140
+ post_rush_rate = remaining_share * age_range / 100
141
+ return round(post_rush_rate / 100, 13)
142
+
143
+ def get_rates_per_year_over_building_lifetime(self) -> pd.Series:
144
+ """
145
+ Create a series that holds the yearly measure rates over the building lifetime.
146
+
147
+ This method defines the periods in the S-curve, adds the yearly measure rates to
148
+ the corresponding periods, and stores them in a pandas Series.
149
+
150
+ Returns
151
+ -------
152
+ pd.Series
153
+ A Series containing the yearly measure rates over the building lifetime
154
+ with an index representing the age from 1 to the building lifetime.
155
+
156
+ """
157
+
158
+ # Define the length of the different periods in the S-curve
159
+ earliest_years = self._earliest_age - 1
160
+ pre_rush_years = int(self._average_age - self._earliest_age - (self._rush_years/2))
161
+ rush_years = self._rush_years
162
+ post_rush_years = int(self._last_age - self._average_age - (self._rush_years/2))
163
+ last_years = self._building_lifetime - earliest_years - pre_rush_years - rush_years - post_rush_years
164
+
165
+ # Redefine periods for Demolition, as the post_rush_year calculation isn't the same as for Small measures and
166
+ # rehabilitation
167
+ if last_years < 0:
168
+ last_years = 0
169
+ post_rush_years = self._building_lifetime - earliest_years - pre_rush_years - rush_years
170
+
171
+ # Create list where the yearly rates are placed according to their corresponding period in the buildings
172
+ # lifetime
173
+
174
+ rates_per_year = (
175
+ [0] * earliest_years +
176
+ [self._pre_rush_rate] * pre_rush_years +
177
+ [self._rush_rate] * rush_years +
178
+ [self._post_rush_rate] * post_rush_years +
179
+ [0] * last_years
180
+ )
181
+
182
+ # Create a pd.Series with an index from 1 to building_lifetime
183
+ index = range(1, self._building_lifetime + 1)
184
+ rates_per_year = pd.Series(rates_per_year, index=index, name='scurve')
185
+ rates_per_year.index.name = 'age'
186
+
187
+ return rates_per_year
188
+
189
+ def calc_scurve(self) -> pd.Series:
190
+ """
191
+ Calculates the S-curve by accumulating the yearly measure rates over the building's lifetime.
192
+
193
+ This method returns a pandas Series representing the S-curve, where each value corresponds
194
+ to the accumulated rate up to that age.
195
+
196
+ Returns
197
+ -------
198
+ pd.Series
199
+ A Series containing the accumulated rates of the S-curve with an index representing the age from 1 to the
200
+ building lifetime.
201
+ """
202
+ # Get rates_per_year and accumulate them over the building lifetime
203
+ rates_per_year = self.get_rates_per_year_over_building_lifetime()
204
+ scurve = rates_per_year.cumsum()
205
+ return scurve
206
+
207
+
208
+ def main():
209
+ import pathlib
210
+ logger.info('Calculate all scurves from data/s_curve.csv')
211
+ area_parameters_path = pathlib.Path(__file__).parent.parent / 'data/s_curve.csv'
212
+ df = pd.read_csv(area_parameters_path)
213
+ for r, v in df.iterrows():
214
+ scurve = SCurve(earliest_age=v.earliest_age_for_measure,
215
+ average_age=v.average_age_for_measure,
216
+ last_age=v.last_age_for_measure,
217
+ rush_years=v.rush_period_years,
218
+ never_share=v.never_share,
219
+ rush_share=v.rush_share)
220
+ print(scurve.calc_scurve())
221
+ logger.info('done')
222
+
223
+ if __name__ == '__main__':
224
+ main()
ebm/model/tek.py ADDED
@@ -0,0 +1 @@
1
+