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.
- ebm/__init__.py +0 -0
- ebm/__main__.py +152 -0
- ebm/__version__.py +1 -0
- ebm/cmd/__init__.py +0 -0
- ebm/cmd/calibrate.py +83 -0
- ebm/cmd/calibrate_excel_com_io.py +128 -0
- ebm/cmd/heating_systems_by_year.py +18 -0
- ebm/cmd/helpers.py +134 -0
- ebm/cmd/initialize.py +167 -0
- ebm/cmd/migrate.py +92 -0
- ebm/cmd/pipeline.py +227 -0
- ebm/cmd/prepare_main.py +174 -0
- ebm/cmd/result_handler.py +272 -0
- ebm/cmd/run_calculation.py +221 -0
- ebm/data/area.csv +92 -0
- ebm/data/area_new_residential_buildings.csv +3 -0
- ebm/data/area_per_person.csv +12 -0
- ebm/data/building_code_parameters.csv +9 -0
- ebm/data/energy_need_behaviour_factor.csv +6 -0
- ebm/data/energy_need_improvements.csv +7 -0
- ebm/data/energy_need_original_condition.csv +534 -0
- ebm/data/heating_system_efficiencies.csv +13 -0
- ebm/data/heating_system_forecast.csv +9 -0
- ebm/data/heating_system_initial_shares.csv +1113 -0
- ebm/data/holiday_home_energy_consumption.csv +24 -0
- ebm/data/holiday_home_stock.csv +25 -0
- ebm/data/improvement_building_upgrade.csv +9 -0
- ebm/data/new_buildings_residential.csv +32 -0
- ebm/data/population_forecast.csv +51 -0
- ebm/data/s_curve.csv +40 -0
- ebm/energy_consumption.py +307 -0
- ebm/extractors.py +115 -0
- ebm/heating_system_forecast.py +472 -0
- ebm/holiday_home_energy.py +341 -0
- ebm/migrations.py +224 -0
- ebm/model/__init__.py +0 -0
- ebm/model/area.py +403 -0
- ebm/model/bema.py +149 -0
- ebm/model/building_category.py +150 -0
- ebm/model/building_condition.py +78 -0
- ebm/model/calibrate_energy_requirements.py +84 -0
- ebm/model/calibrate_heating_systems.py +180 -0
- ebm/model/column_operations.py +157 -0
- ebm/model/construction.py +827 -0
- ebm/model/data_classes.py +223 -0
- ebm/model/database_manager.py +410 -0
- ebm/model/dataframemodels.py +115 -0
- ebm/model/defaults.py +30 -0
- ebm/model/energy_need.py +6 -0
- ebm/model/energy_need_filter.py +182 -0
- ebm/model/energy_purpose.py +115 -0
- ebm/model/energy_requirement.py +353 -0
- ebm/model/energy_use.py +202 -0
- ebm/model/enums.py +8 -0
- ebm/model/exceptions.py +4 -0
- ebm/model/file_handler.py +388 -0
- ebm/model/filter_scurve_params.py +83 -0
- ebm/model/filter_tek.py +152 -0
- ebm/model/heat_pump.py +53 -0
- ebm/model/heating_systems.py +20 -0
- ebm/model/heating_systems_parameter.py +17 -0
- ebm/model/heating_systems_projection.py +3 -0
- ebm/model/heating_systems_share.py +28 -0
- ebm/model/scurve.py +224 -0
- ebm/model/tek.py +1 -0
- ebm/s_curve.py +515 -0
- ebm/services/__init__.py +0 -0
- ebm/services/calibration_writer.py +262 -0
- ebm/services/console.py +106 -0
- ebm/services/excel_loader.py +66 -0
- ebm/services/files.py +38 -0
- ebm/services/spreadsheet.py +289 -0
- ebm/temp_calc.py +99 -0
- ebm/validators.py +565 -0
- ebm-0.99.3.dist-info/METADATA +217 -0
- ebm-0.99.3.dist-info/RECORD +80 -0
- ebm-0.99.3.dist-info/WHEEL +5 -0
- ebm-0.99.3.dist-info/entry_points.txt +3 -0
- ebm-0.99.3.dist-info/licenses/LICENSE +21 -0
- ebm-0.99.3.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,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
|
+
|