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.
- 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.5.dist-info/METADATA +212 -0
- ebm-0.99.5.dist-info/RECORD +80 -0
- ebm-0.99.5.dist-info/WHEEL +5 -0
- ebm-0.99.5.dist-info/entry_points.txt +3 -0
- ebm-0.99.5.dist-info/licenses/LICENSE +21 -0
- ebm-0.99.5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,272 @@
|
|
1
|
+
import pathlib
|
2
|
+
import time
|
3
|
+
|
4
|
+
from loguru import logger
|
5
|
+
import pandas as pd
|
6
|
+
|
7
|
+
from ebm.cmd.run_calculation import (calculate_building_category_area_forecast,
|
8
|
+
calculate_building_category_energy_requirements,
|
9
|
+
calculate_heating_systems)
|
10
|
+
from ebm.model import bema
|
11
|
+
from ebm.model.calibrate_heating_systems import group_heating_systems_by_energy_carrier
|
12
|
+
from ebm.model.building_category import BuildingCategory
|
13
|
+
from ebm.model.data_classes import YearRange
|
14
|
+
from ebm.model.database_manager import DatabaseManager
|
15
|
+
from ebm.services.spreadsheet import detect_format_from_values, find_max_column_width
|
16
|
+
|
17
|
+
|
18
|
+
def transform_model_to_horizontal(model, value_column = 'm2'):
|
19
|
+
hz = model.reset_index().copy()
|
20
|
+
if 'energy_requirement' in hz.columns:
|
21
|
+
value_column = 'GWh'
|
22
|
+
hz['GWh'] = hz['energy_requirement'] / 10**6
|
23
|
+
hz = hz.groupby(by=['building_category', 'building_code', 'building_condition', 'year'], as_index=False).sum()[
|
24
|
+
['building_category', 'building_code', 'building_condition', 'year', value_column]]
|
25
|
+
hz = hz.pivot(columns=['year'], index=['building_category', 'building_code', 'building_condition'], values=[
|
26
|
+
value_column]).reset_index()
|
27
|
+
|
28
|
+
hz = hz.sort_values(by=['building_category', 'building_code', 'building_condition'], key=bema.map_sort_order)
|
29
|
+
hz.insert(3, 'U', value_column)
|
30
|
+
hz.columns = ['building_category', 'building_code', 'building_condition', 'U'] + [y for y in range(2020, 2051)]
|
31
|
+
|
32
|
+
return hz
|
33
|
+
|
34
|
+
|
35
|
+
def transform_to_sorted_heating_systems(df: pd.DataFrame, holiday_homes: pd.DataFrame,
|
36
|
+
building_column: str='building_category',
|
37
|
+
) -> pd.DataFrame:
|
38
|
+
category_order = {'bloig': 100,
|
39
|
+
'Bolig': 100,
|
40
|
+
'Fritidsboliger': 200,
|
41
|
+
'fritidsboliger': 200,
|
42
|
+
'yrkesbygg': 300,
|
43
|
+
'Yrkesbygg': 300}
|
44
|
+
energy_source = {'Elektrisitet': 10, 'Fjernvarme': 11, 'Bio': 12, 'Fossil': 13, 'Solar': 13,
|
45
|
+
'Luft/luft': 24, 'Vannbåren varme': 25}
|
46
|
+
|
47
|
+
rs = pd.concat([df, holiday_homes]).reindex()
|
48
|
+
rs = rs.sort_values(by=[building_column, 'energy_source'],
|
49
|
+
key=lambda x: x.map(category_order) if x.name == building_column else x.map(
|
50
|
+
energy_source) if x.name == 'energy_source' else x)
|
51
|
+
|
52
|
+
hz = pd.concat([rs[~rs['energy_source'].isin(['Luft/luft', 'Vannbåren varme'])],
|
53
|
+
rs[rs['energy_source'].isin(['Luft/luft', 'Vannbåren varme'])]])
|
54
|
+
hz.insert(2, 'U', 'GWh')
|
55
|
+
return hz
|
56
|
+
|
57
|
+
|
58
|
+
def transform_heating_systems_to_horizontal(model: pd.DataFrame):
|
59
|
+
hs2 = model
|
60
|
+
energy_carrier_by_building_group = group_heating_systems_by_energy_carrier(hs2)
|
61
|
+
|
62
|
+
r2 = energy_carrier_by_building_group.reset_index()[['building_category', 'energy_source', 'year', 'energy_use']]
|
63
|
+
hz = r2.pivot(columns=['year'], index=['building_category', 'energy_source'],
|
64
|
+
values=['energy_use']).reset_index()
|
65
|
+
|
66
|
+
hz.columns = ['building_category', 'energy_source'] + [y for y in range(2020, 2051)]
|
67
|
+
return hz
|
68
|
+
|
69
|
+
|
70
|
+
def write_result(output_file, csv_delimiter, output, sheet_name='area forecast'):
|
71
|
+
logger.debug(f'Writing to {output_file}')
|
72
|
+
write_start = time.time()
|
73
|
+
if str(output_file) == '-':
|
74
|
+
try:
|
75
|
+
print(output.to_markdown())
|
76
|
+
except ImportError:
|
77
|
+
print(output.to_string())
|
78
|
+
elif output_file.suffix == '.csv':
|
79
|
+
output.to_csv(output_file, sep=csv_delimiter)
|
80
|
+
logger.success('Wrote {filename}', filename=output_file)
|
81
|
+
else:
|
82
|
+
excel_writer = pd.ExcelWriter(output_file, engine='openpyxl')
|
83
|
+
output.to_excel(excel_writer, sheet_name=sheet_name, merge_cells=False, freeze_panes=(1, 3))
|
84
|
+
excel_writer.close()
|
85
|
+
logger.success('Wrote {filename}', filename=output_file)
|
86
|
+
|
87
|
+
logger.debug(f' wrote {output_file.stat().st_size/1000:.0} in {time.time() - write_start:.4} seconds')
|
88
|
+
|
89
|
+
|
90
|
+
def append_result(output_file: pathlib.Path, df: pd.DataFrame, sheet_name='Sheet 1'):
|
91
|
+
"""
|
92
|
+
Write df to output_file using sheet_name. If output_file already exists the sheet will be added tp ouput_file rather
|
93
|
+
than replacing the entire content.
|
94
|
+
|
95
|
+
Parameters
|
96
|
+
----------
|
97
|
+
output_file :
|
98
|
+
df :
|
99
|
+
sheet_name :
|
100
|
+
|
101
|
+
Returns
|
102
|
+
-------
|
103
|
+
|
104
|
+
"""
|
105
|
+
more_options = {'mode': 'w'}
|
106
|
+
if output_file.is_file():
|
107
|
+
more_options = {'if_sheet_exists': 'replace', 'mode': 'a'}
|
108
|
+
|
109
|
+
with pd.ExcelWriter(output_file, engine='openpyxl', **more_options) as writer:
|
110
|
+
df.to_excel(writer, sheet_name=sheet_name, index=False)
|
111
|
+
sheet = writer.sheets[sheet_name]
|
112
|
+
|
113
|
+
columns = sheet.iter_cols(min_row=1, max_row=sheet.max_row, min_col=1, max_col=sheet.max_column)
|
114
|
+
|
115
|
+
logger.debug('Formatting cell')
|
116
|
+
for col, (col_name, col_values) in zip(columns, df.items()):
|
117
|
+
cell_format = detect_format_from_values(col_name, col_values, df)
|
118
|
+
if cell_format:
|
119
|
+
for row in col[1:]:
|
120
|
+
row.number_format = cell_format
|
121
|
+
|
122
|
+
logger.debug('Adjust columns width')
|
123
|
+
for col in sheet.iter_cols(min_col=1):
|
124
|
+
adjusted_width = find_max_column_width(col)
|
125
|
+
sheet.column_dimensions[col[0].column_letter].width = adjusted_width
|
126
|
+
logger.debug(f'Closing {output_file} {sheet_name}')
|
127
|
+
logger.debug(f'Wrote {output_file} {sheet_name}')
|
128
|
+
|
129
|
+
|
130
|
+
class EbmDefaultHandler:
|
131
|
+
def extract_model(self,
|
132
|
+
year_range: YearRange,
|
133
|
+
building_categories: list[BuildingCategory] | None,
|
134
|
+
database_manager: DatabaseManager,
|
135
|
+
step_choice: str='energy-use') -> pd.DataFrame:
|
136
|
+
"""
|
137
|
+
Extract dataframe for a certain step in the ebm model.
|
138
|
+
|
139
|
+
Possible steps are energy_use (default), heating-systems, energy-use, area-forecast
|
140
|
+
|
141
|
+
Parameters
|
142
|
+
----------
|
143
|
+
year_range : ebm.model.dataclasses.YearRange
|
144
|
+
building_categories : list[BuildingCategory]
|
145
|
+
database_manager : ebm.model.database_manager.DatabaseManager
|
146
|
+
step_choice : str, optional
|
147
|
+
|
148
|
+
Returns
|
149
|
+
-------
|
150
|
+
pd.DataFrame
|
151
|
+
"""
|
152
|
+
b_c = building_categories if building_categories else [e for e in BuildingCategory]
|
153
|
+
area_forecast = self.extract_area_forecast(b_c,
|
154
|
+
database_manager,
|
155
|
+
period=year_range)
|
156
|
+
area_forecast = area_forecast.set_index(['building_category', 'building_code', 'building_condition', 'year'])
|
157
|
+
df = area_forecast
|
158
|
+
|
159
|
+
if 'energy-requirements' in step_choice or 'heating-systems' in step_choice or 'energy-use' in step_choice:
|
160
|
+
logger.debug('Extracting area energy requirements')
|
161
|
+
energy_requirements_result = self.extract_energy_requirements(b_c,
|
162
|
+
database_manager,
|
163
|
+
area_forecast,
|
164
|
+
period=year_range)
|
165
|
+
df = energy_requirements_result
|
166
|
+
|
167
|
+
if 'heating-systems' in step_choice or 'energy-use' in step_choice:
|
168
|
+
logger.debug('Extracting heating systems')
|
169
|
+
df = calculate_heating_systems(energy_requirements=energy_requirements_result,
|
170
|
+
database_manager=database_manager)
|
171
|
+
return df
|
172
|
+
|
173
|
+
# noinspection PyTypeChecker
|
174
|
+
@staticmethod
|
175
|
+
def extract_energy_requirements(building_categories,
|
176
|
+
database_manager: DatabaseManager,
|
177
|
+
area_forecast: pd.DataFrame,
|
178
|
+
period: YearRange) -> pd.DataFrame:
|
179
|
+
"""
|
180
|
+
Extracts energy needs for building_categories and period
|
181
|
+
|
182
|
+
Parameters
|
183
|
+
----------
|
184
|
+
building_categories : list[BuildingCategory]
|
185
|
+
database_manager : ebm.model.database_manager.DatabaseManager
|
186
|
+
area_forecast : pd.DataFrame
|
187
|
+
period :ebm.model.dataclasses.YearRange
|
188
|
+
|
189
|
+
Returns
|
190
|
+
-------
|
191
|
+
pd.DataFrame
|
192
|
+
|
193
|
+
"""
|
194
|
+
energy_requirements_result = calculate_building_category_energy_requirements(
|
195
|
+
building_category=building_categories,
|
196
|
+
area_forecast=area_forecast,
|
197
|
+
database_manager=database_manager,
|
198
|
+
start_year=period.start,
|
199
|
+
end_year=period.end)
|
200
|
+
return energy_requirements_result
|
201
|
+
|
202
|
+
|
203
|
+
@staticmethod
|
204
|
+
def extract_area_forecast(building_categories,
|
205
|
+
database_manager: DatabaseManager,
|
206
|
+
period: YearRange) -> pd.DataFrame:
|
207
|
+
|
208
|
+
area_forecast = calculate_building_category_area_forecast(
|
209
|
+
database_manager=database_manager,
|
210
|
+
start_year=period.start,
|
211
|
+
end_year=period.end)
|
212
|
+
|
213
|
+
return area_forecast
|
214
|
+
|
215
|
+
@staticmethod
|
216
|
+
def write_tqdm_result(output_file: pathlib.Path, output: pd.DataFrame, csv_delimiter: str=',', reset_index=True):
|
217
|
+
try:
|
218
|
+
from tqdm import tqdm
|
219
|
+
except ImportError:
|
220
|
+
# When tqdm is not installed we use write_result instead
|
221
|
+
write_result(output_file, csv_delimiter, output)
|
222
|
+
return
|
223
|
+
|
224
|
+
logger.debug(f'Writing to {output_file}')
|
225
|
+
|
226
|
+
if str(output_file) == '-':
|
227
|
+
try:
|
228
|
+
print(output.to_markdown())
|
229
|
+
except ImportError:
|
230
|
+
print(output.to_string())
|
231
|
+
return
|
232
|
+
|
233
|
+
if reset_index:
|
234
|
+
logger.debug('Resetting dataframe index')
|
235
|
+
output = output.reset_index()
|
236
|
+
|
237
|
+
chunk_size = 2000
|
238
|
+
logger.debug(f'{chunk_size=}')
|
239
|
+
|
240
|
+
closing_file = time.time()
|
241
|
+
with tqdm(total=len(output), desc="Writing to spreadsheet") as pbar:
|
242
|
+
write_file = time.time()
|
243
|
+
if output_file.suffix == '.csv':
|
244
|
+
for i in range(0, len(output), chunk_size): # Adjust the chunk size as needed
|
245
|
+
building_category = output.iloc[i].building_category
|
246
|
+
pbar.update(chunk_size)
|
247
|
+
output.iloc[i:i + chunk_size].to_csv(output_file, mode='a', header=(i == 0), index=False,
|
248
|
+
sep=csv_delimiter)
|
249
|
+
pbar.display(f'Writing {building_category}')
|
250
|
+
closing_file = time.time()
|
251
|
+
pbar.display(f'Wrote {output_file}')
|
252
|
+
else:
|
253
|
+
with pd.ExcelWriter(output_file, engine='xlsxwriter') as excel_writer:
|
254
|
+
for i in range(0, len(output), chunk_size): # Adjust the chunk size as needed
|
255
|
+
building_category = output.iloc[i].name[0] if 'building_category' not in output.columns else \
|
256
|
+
output.building_category.iloc[i]
|
257
|
+
pbar.set_description(f'Writing {building_category}')
|
258
|
+
start_row = 0 if i == 0 else i + 1
|
259
|
+
page_start = i
|
260
|
+
page_end = min(i + chunk_size, len(output))
|
261
|
+
logger.trace(f'{start_row=} {page_start=} {page_end=}')
|
262
|
+
output.iloc[page_start:page_end].to_excel(excel_writer, startrow=start_row, header=(i == 0),
|
263
|
+
merge_cells=True, index=not reset_index)
|
264
|
+
pbar.update(chunk_size)
|
265
|
+
pbar.set_description(f'Closing {output_file}')
|
266
|
+
closing_file = time.time()
|
267
|
+
logger.success('Wrote {filename}', filename=output_file)
|
268
|
+
logger.debug(f' wrote dataframe in { closing_file - write_file:.4} seconds')
|
269
|
+
logger.debug(f' closed file in {time.time() - closing_file:.4} seconds')
|
270
|
+
logger.debug(f' wrote {int(output_file.stat().st_size/1_000_000):_d} MB in {time.time() - write_file:.4} seconds')
|
271
|
+
|
272
|
+
|
@@ -0,0 +1,221 @@
|
|
1
|
+
import os
|
2
|
+
import pathlib
|
3
|
+
from typing import Dict
|
4
|
+
|
5
|
+
import pandas as pd
|
6
|
+
from loguru import logger
|
7
|
+
|
8
|
+
from ebm.extractors import extract_area_forecast
|
9
|
+
from ebm.model.building_category import BuildingCategory
|
10
|
+
|
11
|
+
from ebm.model.data_classes import YearRange
|
12
|
+
from ebm.model.database_manager import DatabaseManager
|
13
|
+
from ebm.model.energy_requirement import EnergyRequirement
|
14
|
+
from ebm.energy_consumption import EnergyConsumption
|
15
|
+
from ebm.heating_system_forecast import HeatingSystemsForecast
|
16
|
+
from ebm.s_curve import calculate_s_curves
|
17
|
+
|
18
|
+
|
19
|
+
def area_forecast_result_to_dataframe(forecast: pd.DataFrame) -> pd.DataFrame:
|
20
|
+
"""
|
21
|
+
Create a dataframe from a forecast
|
22
|
+
|
23
|
+
Parameters
|
24
|
+
----------
|
25
|
+
forecast : Dict[str, list[float]]
|
26
|
+
|
27
|
+
Returns dataframe : pd.Dataframe
|
28
|
+
A dataframe of all area values in forecast indexed by building_category, tek and year
|
29
|
+
-------
|
30
|
+
|
31
|
+
"""
|
32
|
+
return forecast
|
33
|
+
|
34
|
+
|
35
|
+
# noinspection PyTypeChecker
|
36
|
+
def result_to_horizontal_dataframe(result: pd.DataFrame) -> pd.DataFrame:
|
37
|
+
"""
|
38
|
+
Create a dataframe from a forecast with years listed horizontally start to end year
|
39
|
+
|
40
|
+
Parameters
|
41
|
+
----------
|
42
|
+
result : pd.DataFrame:
|
43
|
+
|
44
|
+
Returns
|
45
|
+
-------
|
46
|
+
dataframe : pd.Dataframe
|
47
|
+
A dataframe of all area values in forecast indexed by building_category, tek and year
|
48
|
+
|
49
|
+
|
50
|
+
"""
|
51
|
+
stacked = result.stack().to_frame().reset_index()
|
52
|
+
|
53
|
+
columns = 'year'
|
54
|
+
index = list(filter(lambda x: x != columns and x != 'index' and x not in [0], stacked.columns))
|
55
|
+
values = stacked.columns[-1]
|
56
|
+
|
57
|
+
df = stacked.pivot(columns=columns, index=index, values=values)
|
58
|
+
|
59
|
+
df.index.names = df.index.names[:-1] + ['unit']
|
60
|
+
return df
|
61
|
+
|
62
|
+
|
63
|
+
def validate_years(start_year: int, end_year: int) -> YearRange:
|
64
|
+
"""
|
65
|
+
Validates the start and end year arguments and returns a YearRange
|
66
|
+
|
67
|
+
Parameters
|
68
|
+
----------
|
69
|
+
end_year : int
|
70
|
+
The end year to validate.
|
71
|
+
start_year : int
|
72
|
+
The start year to validate.
|
73
|
+
|
74
|
+
Returns
|
75
|
+
-------
|
76
|
+
YearRange
|
77
|
+
from start_year to end_year
|
78
|
+
|
79
|
+
Raises
|
80
|
+
------
|
81
|
+
ValueError
|
82
|
+
If `start_year` is greater than or equal to `end_year`.
|
83
|
+
If `end_year` is not exactly 40 years after `start_year`.
|
84
|
+
If `start_year` is not 2010 or `end_year` is not 2050.
|
85
|
+
"""
|
86
|
+
if start_year >= end_year:
|
87
|
+
msg = f'Unexpected input start year ({start_year} is greater than end year ({end_year})'
|
88
|
+
raise ValueError(msg)
|
89
|
+
if start_year < 2010:
|
90
|
+
msg = f'Unexpected start_year={start_year}. The minimum start year is 2010'
|
91
|
+
raise ValueError(msg)
|
92
|
+
if end_year > 2070:
|
93
|
+
msg = f'Unexpected end_year={end_year}. Max end_year year is 2070'
|
94
|
+
raise ValueError(msg)
|
95
|
+
return YearRange(start_year, end_year)
|
96
|
+
|
97
|
+
|
98
|
+
def calculate_building_category_energy_requirements(building_category: BuildingCategory,
|
99
|
+
area_forecast,
|
100
|
+
database_manager: DatabaseManager,
|
101
|
+
start_year: int,
|
102
|
+
end_year: int,
|
103
|
+
calibration_year: int = 2019) -> pd.DataFrame:
|
104
|
+
"""
|
105
|
+
Calculate energy need by building_category, TEK, building_condition and purpose.
|
106
|
+
|
107
|
+
The parameter building_category is not used and should be removed.
|
108
|
+
|
109
|
+
Parameters
|
110
|
+
----------
|
111
|
+
building_category : BuildingCategory, unused
|
112
|
+
area_forecast : pd.DataFrame
|
113
|
+
database_manager : DatabaseManager
|
114
|
+
start_year : int
|
115
|
+
end_year : int
|
116
|
+
calibration_year : int, optional
|
117
|
+
|
118
|
+
Returns
|
119
|
+
-------
|
120
|
+
pd.DataFrame
|
121
|
+
"""
|
122
|
+
energy_requirement = EnergyRequirement.new_instance(
|
123
|
+
period=YearRange(start_year, end_year),
|
124
|
+
calibration_year=calibration_year if calibration_year > start_year else start_year,
|
125
|
+
database_manager=database_manager)
|
126
|
+
df = energy_requirement.calculate_for_building_category(database_manager=database_manager)
|
127
|
+
|
128
|
+
df = df.set_index(['building_category', 'building_code', 'purpose', 'building_condition', 'year'])
|
129
|
+
|
130
|
+
q = area_forecast.reset_index()
|
131
|
+
|
132
|
+
q = q.set_index(['building_category', 'building_code', 'building_condition', 'year'])
|
133
|
+
|
134
|
+
merged = q.merge(df, left_index=True, right_index=True)
|
135
|
+
merged['energy_requirement'] = merged.kwh_m2 * merged.m2
|
136
|
+
|
137
|
+
return merged
|
138
|
+
|
139
|
+
|
140
|
+
def write_to_disk(constructed_floor_area, building_category: BuildingCategory):
|
141
|
+
"""Writes constructed_floor_area to disk if the environment variable EBM_WRITE_TO_DISK is True"""
|
142
|
+
if os.environ.get('EBM_WRITE_TO_DISK', 'False').upper() == 'TRUE':
|
143
|
+
df = result_to_horizontal_dataframe(constructed_floor_area)
|
144
|
+
df.insert(0, 'building_category', building_category)
|
145
|
+
file_path = pathlib.Path('output/construction.xlsx')
|
146
|
+
df.index.name = 'series'
|
147
|
+
|
148
|
+
if building_category == BuildingCategory.HOUSE or not file_path.is_file():
|
149
|
+
df.to_excel(file_path, sheet_name='construction')
|
150
|
+
logger.debug(f'Wrote {file_path}')
|
151
|
+
else:
|
152
|
+
with pd.ExcelWriter(file_path, engine='openpyxl', mode='a', if_sheet_exists='overlay') as writer:
|
153
|
+
start_row = writer.sheets['construction'].max_row
|
154
|
+
df.to_excel(writer, sheet_name='construction', index=True, header=False, startrow=start_row)
|
155
|
+
logger.debug(f'Added {building_category} to {file_path}')
|
156
|
+
|
157
|
+
|
158
|
+
def calculate_building_category_area_forecast(database_manager: DatabaseManager,
|
159
|
+
start_year: int,
|
160
|
+
end_year: int) -> pd.DataFrame:
|
161
|
+
"""
|
162
|
+
Calculates the area forecast for a given building category from start to end year (including).
|
163
|
+
|
164
|
+
Parameters
|
165
|
+
----------
|
166
|
+
database_manager : DatabaseManager
|
167
|
+
The database manager used to interact with the database.
|
168
|
+
start_year : int
|
169
|
+
The starting year of the forecast period.
|
170
|
+
end_year : int
|
171
|
+
The ending year of the forecast period.
|
172
|
+
|
173
|
+
Returns
|
174
|
+
-------
|
175
|
+
pd.DataFrame
|
176
|
+
A dictionary where keys are strings representing different area categories and values are lists of floats
|
177
|
+
representing the forecasted areas for each year in the specified period.
|
178
|
+
|
179
|
+
Notes
|
180
|
+
-----
|
181
|
+
This function builds the buildings for the specified category, calculates the area forecast, and accounts for
|
182
|
+
demolition and construction over the specified period.
|
183
|
+
"""
|
184
|
+
building_code_parameters = database_manager.file_handler.get_building_code()
|
185
|
+
years = YearRange(start_year, end_year)
|
186
|
+
scurve_params = database_manager.get_scurve_params()
|
187
|
+
s_curves_by_condition = calculate_s_curves(scurve_params, building_code_parameters, years)
|
188
|
+
|
189
|
+
area_forecast = extract_area_forecast(years,
|
190
|
+
building_code_parameters=building_code_parameters,
|
191
|
+
area_parameters=database_manager.get_area_parameters(),
|
192
|
+
s_curves_by_condition=s_curves_by_condition,
|
193
|
+
database_manager=database_manager)
|
194
|
+
|
195
|
+
return area_forecast
|
196
|
+
|
197
|
+
|
198
|
+
def calculate_heating_systems(energy_requirements, database_manager: DatabaseManager) -> pd.DataFrame:
|
199
|
+
"""
|
200
|
+
Calculate heating systems projection, efficiencies and multiplies by energy_requirements for total energy use
|
201
|
+
by building_category, TEK, building_condition, purpose and heating_system.
|
202
|
+
|
203
|
+
Parameters
|
204
|
+
----------
|
205
|
+
energy_requirements : pd.DataFrame
|
206
|
+
database_manager : ebm.model.database_manager.DatabaseManager
|
207
|
+
|
208
|
+
Returns
|
209
|
+
-------
|
210
|
+
pd.DataFrame
|
211
|
+
|
212
|
+
"""
|
213
|
+
projection_period = YearRange(2023, 2050)
|
214
|
+
hsp = HeatingSystemsForecast.new_instance(projection_period, database_manager)
|
215
|
+
hf = hsp.calculate_forecast()
|
216
|
+
hf = hsp.pad_projection(hf, YearRange(2020, 2022))
|
217
|
+
calculator = EnergyConsumption(hf)
|
218
|
+
calculator.heating_systems_parameters = calculator.grouped_heating_systems()
|
219
|
+
df = calculator.calculate(energy_requirements)
|
220
|
+
|
221
|
+
return df
|
ebm/data/area.csv
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
building_category,building_code,area
|
2
|
+
apartment_block,PRE_TEK49,12777246.0
|
3
|
+
apartment_block,TEK49,12793316.0
|
4
|
+
apartment_block,TEK69,12679268.0
|
5
|
+
apartment_block,TEK87,4623808.0
|
6
|
+
apartment_block,TEK97,10024890.0
|
7
|
+
apartment_block,TEK07,3477435.0
|
8
|
+
apartment_block,TEK10,11245568.0
|
9
|
+
house,PRE_TEK49,49408249.0
|
10
|
+
house,TEK49,71191856.0
|
11
|
+
house,TEK69,75010253.0
|
12
|
+
house,TEK87,26804616.0
|
13
|
+
house,TEK97,24161659.0
|
14
|
+
house,TEK07,8933605.0
|
15
|
+
house,TEK10,17513757.0
|
16
|
+
kindergarten,PRE_TEK49,360687.0
|
17
|
+
kindergarten,TEK49,395768.0
|
18
|
+
kindergarten,TEK69,465007.0
|
19
|
+
kindergarten,TEK87,459412.0
|
20
|
+
kindergarten,TEK97,480468.0
|
21
|
+
kindergarten,TEK07,445252.0
|
22
|
+
kindergarten,TEK10,453523.0
|
23
|
+
school,PRE_TEK49,2918530.0
|
24
|
+
school,TEK49,3825312.0
|
25
|
+
school,TEK69,2939528.0
|
26
|
+
school,TEK87,1024007.0
|
27
|
+
school,TEK97,1381987.0
|
28
|
+
school,TEK07,964171.0
|
29
|
+
school,TEK10,1885803.0
|
30
|
+
university,PRE_TEK49,470005.0
|
31
|
+
university,TEK49,543934.0
|
32
|
+
university,TEK69,690131.0
|
33
|
+
university,TEK87,521868.0
|
34
|
+
university,TEK97,517805.0
|
35
|
+
university,TEK07,226949.0
|
36
|
+
university,TEK10,186877.0
|
37
|
+
office,PRE_TEK49,5055317.0
|
38
|
+
office,TEK49,4858542.0
|
39
|
+
office,TEK69,6539198.0
|
40
|
+
office,TEK87,4264029.0
|
41
|
+
office,TEK97,3681521.0
|
42
|
+
office,TEK07,1975791.0
|
43
|
+
office,TEK10,3055337.0
|
44
|
+
retail,PRE_TEK49,4886906.0
|
45
|
+
retail,TEK49,6252160.0
|
46
|
+
retail,TEK69,7127892.0
|
47
|
+
retail,TEK87,3683906.0
|
48
|
+
retail,TEK97,4189579.0
|
49
|
+
retail,TEK07,2560015.0
|
50
|
+
retail,TEK10,3357155.0
|
51
|
+
hotel,PRE_TEK49,1940825.0
|
52
|
+
hotel,TEK49,1850727.0
|
53
|
+
hotel,TEK69,1519726.0
|
54
|
+
hotel,TEK87,749441.0
|
55
|
+
hotel,TEK97,942380.0
|
56
|
+
hotel,TEK07,677755.0
|
57
|
+
hotel,TEK10,802546.0
|
58
|
+
hospital,PRE_TEK49,701476.0
|
59
|
+
hospital,TEK49,674212.0
|
60
|
+
hospital,TEK69,635074.0
|
61
|
+
hospital,TEK87,357828.0
|
62
|
+
hospital,TEK97,576819.0
|
63
|
+
hospital,TEK07,82335.0
|
64
|
+
hospital,TEK10,414770.0
|
65
|
+
nursing_home,PRE_TEK49,991462.0
|
66
|
+
nursing_home,TEK49,1383981.0
|
67
|
+
nursing_home,TEK69,1481965.0
|
68
|
+
nursing_home,TEK87,670938.0
|
69
|
+
nursing_home,TEK97,1427901.0
|
70
|
+
nursing_home,TEK07,289386.0
|
71
|
+
nursing_home,TEK10,630817.0
|
72
|
+
culture,PRE_TEK49,1909630.0
|
73
|
+
culture,TEK49,1923480.0
|
74
|
+
culture,TEK69,1439745.0
|
75
|
+
culture,TEK87,781280.0
|
76
|
+
culture,TEK97,508477.0
|
77
|
+
culture,TEK07,260411.0
|
78
|
+
culture,TEK10,378210.0
|
79
|
+
sports,PRE_TEK49,531426.0
|
80
|
+
sports,TEK49,703080.0
|
81
|
+
sports,TEK69,1132460.0
|
82
|
+
sports,TEK87,961208.0
|
83
|
+
sports,TEK97,1109723.0
|
84
|
+
sports,TEK07,581204.0
|
85
|
+
sports,TEK10,1451364.0
|
86
|
+
storage_repairs,PRE_TEK49,2650280
|
87
|
+
storage_repairs,TEK49,3505868
|
88
|
+
storage_repairs,TEK69,3577553
|
89
|
+
storage_repairs,TEK87,2039191
|
90
|
+
storage_repairs,TEK97,3081789
|
91
|
+
storage_repairs,TEK07,1953926
|
92
|
+
storage_repairs,TEK10,3562046
|
@@ -0,0 +1,6 @@
|
|
1
|
+
building_category,building_code,purpose,behaviour_factor,start_year,end_year
|
2
|
+
residential,default,default,1,2020,2050
|
3
|
+
house,PRE_TEK49+TEK69+TEK87+TEK49+TEK97,default,0.85,,
|
4
|
+
house,TEK07+TEK10+TEK17,lighting,0.85,2020,2050
|
5
|
+
non_residential,default,default,1.15,,
|
6
|
+
retail,default,electrical_equipment,2,,
|
@@ -0,0 +1,7 @@
|
|
1
|
+
building_category,building_code,purpose,function,start_year,value,end_year
|
2
|
+
default,default,cooling,yearly_reduction,2020,0.0,2050
|
3
|
+
default,default,electrical_equipment,yearly_reduction,2021,0.01,2050
|
4
|
+
default,default,fans_and_pumps,yearly_reduction,2020,0.0,2050
|
5
|
+
default,default,heating_dhw,yearly_reduction, 2020,0.0,2050
|
6
|
+
default,default,lighting,improvement_at_end_year,2020,0.5555555555555556,2030
|
7
|
+
default,default,lighting,yearly_reduction,2031,0.005,2050
|