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,223 @@
|
|
1
|
+
import typing
|
2
|
+
from dataclasses import dataclass
|
3
|
+
|
4
|
+
import pandas as pd
|
5
|
+
|
6
|
+
|
7
|
+
@dataclass
|
8
|
+
class ScurveParameters:
|
9
|
+
building_category: str
|
10
|
+
condition: str
|
11
|
+
earliest_age: int
|
12
|
+
average_age: int
|
13
|
+
rush_years: int
|
14
|
+
last_age: int
|
15
|
+
rush_share: float
|
16
|
+
never_share: float
|
17
|
+
|
18
|
+
|
19
|
+
@dataclass
|
20
|
+
class TEKParameters:
|
21
|
+
tek: str
|
22
|
+
building_year: int
|
23
|
+
start_year: int
|
24
|
+
end_year: int
|
25
|
+
|
26
|
+
|
27
|
+
@dataclass(frozen=True)
|
28
|
+
class YearRange:
|
29
|
+
"""
|
30
|
+
A class to represent a period model with a start and end year.
|
31
|
+
|
32
|
+
Attributes
|
33
|
+
----------
|
34
|
+
start : int
|
35
|
+
The starting year of the period.
|
36
|
+
end : int
|
37
|
+
The ending year of the period.
|
38
|
+
year_range : tuple of int
|
39
|
+
A tuple containing all years in the period from start to end (inclusive).
|
40
|
+
|
41
|
+
Methods
|
42
|
+
-------
|
43
|
+
__post_init__():
|
44
|
+
Initializes the years attribute after the object is created.
|
45
|
+
__iter__():
|
46
|
+
Returns an iterator over the years in the period.
|
47
|
+
range() -> tuple of int:
|
48
|
+
Returns a tuple of years from start to end (inclusive).
|
49
|
+
subset(offset: int = 0, length: int = -1) -> 'YearRange':
|
50
|
+
Creates a subset YearRange of this year range.
|
51
|
+
to_index() -> pd.Index:
|
52
|
+
Converts the year_range to a pandas Index.
|
53
|
+
|
54
|
+
Examples
|
55
|
+
--------
|
56
|
+
Slice pandas DataFrame with YearRange.
|
57
|
+
|
58
|
+
>>> df = pd.DataFrame(data=['first', 'b', 'c', 'd', 'last'],
|
59
|
+
... index=[2010, 2011, 2012, 2013, 2014])
|
60
|
+
>>> years = YearRange(2011, 2013)
|
61
|
+
>>> df.loc[years]
|
62
|
+
0
|
63
|
+
2011 b
|
64
|
+
2012 c
|
65
|
+
2013 d
|
66
|
+
>>>
|
67
|
+
|
68
|
+
"""
|
69
|
+
|
70
|
+
start: int
|
71
|
+
end: int
|
72
|
+
year_range: typing.Tuple[int] = tuple()
|
73
|
+
|
74
|
+
def __post_init__(self):
|
75
|
+
"""
|
76
|
+
Initializes the years attribute after the object is created.
|
77
|
+
"""
|
78
|
+
if self.start > self.end:
|
79
|
+
raise ValueError(f'Start year {self.start} cannot be greater than end year {self.end}')
|
80
|
+
object.__setattr__(self, 'year_range', self.range())
|
81
|
+
|
82
|
+
def __str__(self):
|
83
|
+
return f'YearRange(start={self.start}, end={self.end})'
|
84
|
+
|
85
|
+
def __repr__(self):
|
86
|
+
return str(self)
|
87
|
+
|
88
|
+
def __len__(self):
|
89
|
+
return len(self.year_range)
|
90
|
+
|
91
|
+
def __iter__(self) -> typing.Generator[int, None, None]:
|
92
|
+
"""
|
93
|
+
Returns an iterator over the years in the period.
|
94
|
+
|
95
|
+
Yields
|
96
|
+
------
|
97
|
+
int
|
98
|
+
The next year in the period.
|
99
|
+
"""
|
100
|
+
for y in self.year_range:
|
101
|
+
yield y
|
102
|
+
|
103
|
+
def range(self) -> typing.Tuple[int]:
|
104
|
+
"""
|
105
|
+
Returns a tuple of years from start to end for use with indexes and such.
|
106
|
+
|
107
|
+
Returns
|
108
|
+
-------
|
109
|
+
tuple of int
|
110
|
+
Tuple containing all years in sequence from start to end (inclusive).
|
111
|
+
"""
|
112
|
+
return tuple(range(self.start, self.end + 1))
|
113
|
+
|
114
|
+
def subset(self, offset: int = 0, length: int = -1) -> 'YearRange':
|
115
|
+
"""
|
116
|
+
Creates a subset YearRange of this year range.
|
117
|
+
|
118
|
+
Parameters
|
119
|
+
----------
|
120
|
+
offset : int
|
121
|
+
How many years to skip after the first year.
|
122
|
+
length : int, optional
|
123
|
+
How many years to return after the offset. When -1, all remaining years are returned. Default: -1
|
124
|
+
|
125
|
+
Returns
|
126
|
+
-------
|
127
|
+
year_range : YearRange
|
128
|
+
|
129
|
+
Raises
|
130
|
+
------
|
131
|
+
ValueError
|
132
|
+
When `offset` is less than 0 or `offset` is greater than the number of years in the YearRange.
|
133
|
+
|
134
|
+
Examples
|
135
|
+
--------
|
136
|
+
>>> YearRange(2010, 2016).subset(2,3)
|
137
|
+
YearRange(start=2012, end=2014, year_range=(2012, 2013, 2014))
|
138
|
+
>>> YearRange(2010, 2016).subset(2,-1)
|
139
|
+
YearRange(start=2012, end=2016, year_range=(2012, 2013, 2014, 2015, 2016))
|
140
|
+
>>> YearRange(2010, 2016).subset(3)
|
141
|
+
YearRange(start=2013, end=2016, year_range=(2013, 2014, 2015, 2016))
|
142
|
+
|
143
|
+
"""
|
144
|
+
if offset < 0:
|
145
|
+
raise ValueError(f'Offset cannot be negative: {offset}')
|
146
|
+
if offset + self.start > self.end:
|
147
|
+
raise ValueError(f'Offset is out of range: {offset=} >= {len(self)}')
|
148
|
+
start_year = self.start + offset
|
149
|
+
last_year = start_year + length - 1 if length > 0 and start_year + length < self.end else self.end
|
150
|
+
return YearRange(start_year, last_year)
|
151
|
+
|
152
|
+
def to_index(self, name='year') -> pd.Index:
|
153
|
+
"""
|
154
|
+
Converts the year_range to a pandas Index.
|
155
|
+
Parameters
|
156
|
+
----------
|
157
|
+
name : str, optional
|
158
|
+
name of the index. Default: 'name'
|
159
|
+
Returns
|
160
|
+
-------
|
161
|
+
pd.Index
|
162
|
+
Pandas Index object containing the years in the range.
|
163
|
+
"""
|
164
|
+
return pd.Index(self.year_range, name=name)
|
165
|
+
|
166
|
+
def to_dataframe(self, name='year') -> pd.DataFrame:
|
167
|
+
"""
|
168
|
+
Converts the year_range to a pandas DataFrame.
|
169
|
+
Parameters
|
170
|
+
----------
|
171
|
+
name : str, optional
|
172
|
+
name of the column. Default: 'year'
|
173
|
+
Returns
|
174
|
+
-------
|
175
|
+
pd.DataFrame
|
176
|
+
Pandas Dataframe object containing the years in the range in the column year.
|
177
|
+
"""
|
178
|
+
return pd.DataFrame(self.year_range, columns=[name])
|
179
|
+
|
180
|
+
def cross_join(self, df: pd.DataFrame) -> pd.DataFrame:
|
181
|
+
"""
|
182
|
+
Join every row in df with every year in a YearRange
|
183
|
+
|
184
|
+
Parameters
|
185
|
+
----------
|
186
|
+
df : pd.DataFrame
|
187
|
+
dataframe to join with YearRange
|
188
|
+
|
189
|
+
Returns
|
190
|
+
-------
|
191
|
+
pd.DataFrame
|
192
|
+
Pandas Dataframe containing the original dataframe and a year column
|
193
|
+
"""
|
194
|
+
return pd.merge(left=df,
|
195
|
+
right=self.to_dataframe(name='year'),
|
196
|
+
how='cross')
|
197
|
+
|
198
|
+
@staticmethod
|
199
|
+
def from_series(s : pd.Series):
|
200
|
+
if s.name == 'year':
|
201
|
+
return YearRange(s.min(), s.max())
|
202
|
+
return YearRange(s.index.get_level_values(level='year').min(), s.index.get_level_values(level='year').max())
|
203
|
+
|
204
|
+
def __getitem__(self, key: int | slice) -> pd.Index:
|
205
|
+
"""
|
206
|
+
Returns a pandas Index object for the specified slice of the year range.
|
207
|
+
|
208
|
+
Parameters
|
209
|
+
----------
|
210
|
+
key : int | slice
|
211
|
+
The index or slice of the year range to return.
|
212
|
+
|
213
|
+
Returns
|
214
|
+
-------
|
215
|
+
pd.Index
|
216
|
+
A pandas Index object containing the specified years.
|
217
|
+
"""
|
218
|
+
if isinstance(key, int):
|
219
|
+
return pd.Index([self.year_range[key]], name='year')
|
220
|
+
elif isinstance(key, slice):
|
221
|
+
return pd.Index(self.year_range[key], name='year')
|
222
|
+
else:
|
223
|
+
raise TypeError(f"Invalid key type: {type(key)}")
|
@@ -0,0 +1,410 @@
|
|
1
|
+
import itertools
|
2
|
+
import typing
|
3
|
+
|
4
|
+
import pandas as pd
|
5
|
+
from loguru import logger
|
6
|
+
|
7
|
+
from ebm import validators
|
8
|
+
from ebm.energy_consumption import calibrate_heating_systems
|
9
|
+
from ebm.model.column_operations import explode_building_category_column, explode_building_code_column, explode_unique_columns
|
10
|
+
from ebm.model.dataframemodels import EnergyNeedYearlyImprovements, YearlyReduction, PolicyImprovement
|
11
|
+
from ebm.model.energy_purpose import EnergyPurpose
|
12
|
+
from ebm.model.file_handler import FileHandler
|
13
|
+
from ebm.model.building_category import BuildingCategory, expand_building_categories
|
14
|
+
from ebm.model.data_classes import TEKParameters, YearRange
|
15
|
+
|
16
|
+
|
17
|
+
# TODO:
|
18
|
+
# - add method to change all strings to lower case and underscore instead of space
|
19
|
+
# - change column strings used in methods to constants
|
20
|
+
|
21
|
+
|
22
|
+
class DatabaseManager:
|
23
|
+
"""
|
24
|
+
Manages database operations.
|
25
|
+
"""
|
26
|
+
|
27
|
+
# Column names
|
28
|
+
COL_TEK = 'building_code'
|
29
|
+
COL_TEK_BUILDING_YEAR = 'building_year'
|
30
|
+
COL_TEK_START_YEAR = 'period_start_year'
|
31
|
+
COL_TEK_END_YEAR = 'period_end_year'
|
32
|
+
COL_BUILDING_CATEGORY = 'building_category'
|
33
|
+
COL_BUILDING_CONDITION = 'building_condition'
|
34
|
+
COL_AREA = 'area'
|
35
|
+
COL_ENERGY_REQUIREMENT_PURPOSE = 'purpose'
|
36
|
+
COL_ENERGY_REQUIREMENT_VALUE = 'kwh_m2'
|
37
|
+
COL_HEATING_REDUCTION = 'reduction_share'
|
38
|
+
|
39
|
+
DEFAULT_VALUE = 'default'
|
40
|
+
|
41
|
+
def __init__(self, file_handler: FileHandler = None):
|
42
|
+
# Create default FileHandler if file_handler is None
|
43
|
+
|
44
|
+
self.file_handler = file_handler if file_handler is not None else FileHandler()
|
45
|
+
|
46
|
+
def get_building_code_list(self):
|
47
|
+
"""
|
48
|
+
Get a list of building_code.
|
49
|
+
|
50
|
+
Returns:
|
51
|
+
- building_code_list (list): List of building_code.
|
52
|
+
"""
|
53
|
+
building_code_id = self.file_handler.get_building_code()
|
54
|
+
building_code_list = building_code_id[self.COL_TEK].unique()
|
55
|
+
return building_code_list
|
56
|
+
|
57
|
+
def make_building_purpose(self, years: YearRange | None = None) -> pd.DataFrame:
|
58
|
+
"""
|
59
|
+
Returns a dataframe of all combinations building_categories, teks, original_condition, purposes
|
60
|
+
and optionally years.
|
61
|
+
|
62
|
+
Parameters
|
63
|
+
----------
|
64
|
+
years : YearRange, optional
|
65
|
+
|
66
|
+
Returns
|
67
|
+
-------
|
68
|
+
pd.DataFrame
|
69
|
+
"""
|
70
|
+
data = []
|
71
|
+
columns = [list(BuildingCategory), self.get_building_code_list().tolist(), EnergyPurpose]
|
72
|
+
column_headers = ['building_category', 'building_code', 'building_condition', 'purpose']
|
73
|
+
if years:
|
74
|
+
columns.append(years)
|
75
|
+
column_headers.append('year')
|
76
|
+
|
77
|
+
for bc, tek, purpose, *year in itertools.product(*columns):
|
78
|
+
row = [bc, tek, 'original_condition', purpose]
|
79
|
+
if years:
|
80
|
+
row.append(year[0])
|
81
|
+
data.append(row)
|
82
|
+
|
83
|
+
return pd.DataFrame(data=data, columns=column_headers)
|
84
|
+
|
85
|
+
def get_building_codes(self) -> pd.DataFrame:
|
86
|
+
"""
|
87
|
+
Retrieve building_code_parameters
|
88
|
+
|
89
|
+
Returns
|
90
|
+
-------
|
91
|
+
pd.DataFrame
|
92
|
+
Pandas Dataframe containing building_code with parameters
|
93
|
+
"""
|
94
|
+
building_code_params_df = self.file_handler.get_building_code()
|
95
|
+
return building_code_params_df
|
96
|
+
|
97
|
+
|
98
|
+
def get_building_code_params(self, building_code_list: typing.List[str]=None):
|
99
|
+
"""
|
100
|
+
Retrieve building_codeparameters for a list of building_code.
|
101
|
+
|
102
|
+
This method fetches building_codeparameters for each building_codeID in the provided list,
|
103
|
+
converts the relevant data to a dictionary, and maps these values to the
|
104
|
+
corresponding attributes of the TEKParameters dataclass. The resulting
|
105
|
+
dataclass instances are stored in a dictionary with building_code as keys.
|
106
|
+
|
107
|
+
Parameters:
|
108
|
+
- building_code_list (list of str): List of building_code.
|
109
|
+
|
110
|
+
Returns:
|
111
|
+
- building_code_params (dict): Dictionary where each key is a building_codeID and each value
|
112
|
+
is a TEKParameters dataclass instance containing the
|
113
|
+
parameters for that building_codeID.
|
114
|
+
"""
|
115
|
+
building_code_params = {}
|
116
|
+
building_code_params_df = self.file_handler.get_building_code()
|
117
|
+
if not building_code_list:
|
118
|
+
return building_code_params_df
|
119
|
+
for tek in building_code_list:
|
120
|
+
# Filter on building_code
|
121
|
+
building_code_params_filtered = building_code_params_df[building_code_params_df[self.COL_TEK] == tek]
|
122
|
+
|
123
|
+
# Assuming there is only one row in the filtered DataFrame
|
124
|
+
building_code_params_row = building_code_params_filtered.iloc[0]
|
125
|
+
|
126
|
+
# Convert the single row to a dictionary
|
127
|
+
building_code_params_dict = building_code_params_row.to_dict()
|
128
|
+
|
129
|
+
# Map the dictionary values to the dataclass attributes
|
130
|
+
building_code_params_per_id = TEKParameters(
|
131
|
+
tek = building_code_params_dict[self.COL_TEK],
|
132
|
+
building_year = building_code_params_dict[self.COL_TEK_BUILDING_YEAR],
|
133
|
+
start_year = building_code_params_dict[self.COL_TEK_START_YEAR],
|
134
|
+
end_year = building_code_params_dict[self.COL_TEK_END_YEAR],
|
135
|
+
)
|
136
|
+
|
137
|
+
building_code_params[tek] = building_code_params_per_id
|
138
|
+
|
139
|
+
return building_code_params
|
140
|
+
|
141
|
+
def get_scurve_params(self):
|
142
|
+
"""
|
143
|
+
Get input dataframe with S-curve parameters/assumptions.
|
144
|
+
|
145
|
+
Returns:
|
146
|
+
- scurve_params (pd.DataFrame): DataFrame with S-curve parameters.
|
147
|
+
"""
|
148
|
+
scurve_params = self.file_handler.get_s_curve()
|
149
|
+
return scurve_params
|
150
|
+
|
151
|
+
def get_construction_population(self) -> pd.DataFrame:
|
152
|
+
"""
|
153
|
+
Get construction population DataFrame.
|
154
|
+
|
155
|
+
Returns:
|
156
|
+
- construction_population (pd.DataFrame): Dataframe containing population numbers
|
157
|
+
year population household_size
|
158
|
+
"""
|
159
|
+
new_buildings_population = self.file_handler.get_construction_population()
|
160
|
+
new_buildings_population["household_size"] = new_buildings_population['household_size'].astype('float64')
|
161
|
+
new_buildings_population = new_buildings_population.set_index('year')
|
162
|
+
return new_buildings_population
|
163
|
+
|
164
|
+
def get_new_buildings_category_share(self) -> pd.DataFrame:
|
165
|
+
"""
|
166
|
+
Get building category share by year as a DataFrame.
|
167
|
+
|
168
|
+
The number can be used in conjunction with number of households to calculate total number
|
169
|
+
of buildings of category house and apartment block
|
170
|
+
|
171
|
+
Returns:
|
172
|
+
- new_buildings_category_share (pd.DataFrame): Dataframe containing population numbers
|
173
|
+
"year", "Andel nye småhus", "Andel nye leiligheter", "Areal nye småhus", "Areal nye leiligheter"
|
174
|
+
"""
|
175
|
+
df = self.file_handler.get_construction_building_category_share()
|
176
|
+
df['year'] = df['year'].astype(int)
|
177
|
+
df = df.set_index('year')
|
178
|
+
return df
|
179
|
+
|
180
|
+
def get_building_category_floor_area(self, building_category: BuildingCategory) -> pd.Series:
|
181
|
+
"""
|
182
|
+
Get population and household size DataFrame from a file.
|
183
|
+
|
184
|
+
Returns:
|
185
|
+
- construction_population (pd.DataFrame): Dataframe containing population numbers
|
186
|
+
"area","type of building","2010","2011"
|
187
|
+
"""
|
188
|
+
df = self.file_handler.get_building_category_area()
|
189
|
+
|
190
|
+
building_category_floor_area = df[building_category].dropna()
|
191
|
+
|
192
|
+
return building_category_floor_area
|
193
|
+
|
194
|
+
#TODO: remove after refactoring
|
195
|
+
def get_area_parameters(self) -> pd.DataFrame:
|
196
|
+
"""
|
197
|
+
Get total area (m^2) per building category and TEK.
|
198
|
+
|
199
|
+
Parameters:
|
200
|
+
- building_category (str): Optional parameter that filter the returned dataframe by building_category
|
201
|
+
|
202
|
+
Returns:
|
203
|
+
- area_parameters (pd.DataFrame): Dataframe containing total area (m^2) per
|
204
|
+
building category and TEK.
|
205
|
+
"""
|
206
|
+
area_params = self.file_handler.get_area_parameters()
|
207
|
+
return area_params
|
208
|
+
|
209
|
+
def get_area_start_year(self) -> typing.Dict[BuildingCategory, pd.Series]:
|
210
|
+
"""
|
211
|
+
Retrieve total floor area in the model start year for each TEK within a building category.
|
212
|
+
|
213
|
+
Returns
|
214
|
+
-------
|
215
|
+
dict
|
216
|
+
A dictionary where:
|
217
|
+
- keys are `BuildingCategory` objects derived from the building category string.
|
218
|
+
- values are `pandas.Series` with the 'tek' column as the index and the corresponding
|
219
|
+
'area' column as the values.
|
220
|
+
"""
|
221
|
+
area_data = self.file_handler.get_area_parameters()
|
222
|
+
|
223
|
+
area_dict = {}
|
224
|
+
for building_category in area_data[self.COL_BUILDING_CATEGORY].unique():
|
225
|
+
area_building_category = area_data[area_data[self.COL_BUILDING_CATEGORY] == building_category]
|
226
|
+
area_series = area_building_category.set_index(self.COL_TEK)[self.COL_AREA]
|
227
|
+
area_series.index.name = "tek"
|
228
|
+
area_series.rename(f"{BuildingCategory.from_string(building_category)}_area", inplace=True)
|
229
|
+
|
230
|
+
area_dict[BuildingCategory.from_string(building_category)] = area_series
|
231
|
+
|
232
|
+
return area_dict
|
233
|
+
|
234
|
+
def get_behaviour_factor(self) -> pd.DataFrame:
|
235
|
+
f = self.file_handler.get_file(self.file_handler.BEHAVIOUR_FACTOR)
|
236
|
+
behaviour_factor = validators.energy_need_behaviour_factor.validate(f)
|
237
|
+
return behaviour_factor
|
238
|
+
|
239
|
+
|
240
|
+
def get_energy_req_original_condition(self) -> pd.DataFrame:
|
241
|
+
"""
|
242
|
+
Get dataframe with energy requirement (kWh/m^2) for floor area in original condition. The result will be
|
243
|
+
calibrated using the dataframe from DatabaseManger.get_calibrate_heating_rv
|
244
|
+
|
245
|
+
Returns
|
246
|
+
-------
|
247
|
+
pd.DataFrame
|
248
|
+
Dataframe containing energy requirement (kWh/m^2) for floor area in original condition,
|
249
|
+
per building category and purpose.
|
250
|
+
"""
|
251
|
+
logger.debug('Using default year 2020 -> 2050')
|
252
|
+
building_purpose = self.make_building_purpose(years=YearRange(2020, 2050)).set_index(
|
253
|
+
['building_category', 'purpose', 'building_code', 'year'], drop=True
|
254
|
+
)
|
255
|
+
building_purpose = building_purpose.drop(columns=['building_condition'])
|
256
|
+
ff = self.file_handler.get_energy_req_original_condition()[['building_category', 'building_code', 'purpose', 'kwh_m2']]
|
257
|
+
df = self.explode_unique_columns(ff, ['building_category', 'building_code', 'purpose'])
|
258
|
+
if len(df[df.building_code=='TEK21']) > 0:
|
259
|
+
logger.warning(f'Detected TEK21 in {self.file_handler.ENERGY_NEED_ORIGINAL_CONDITION}')
|
260
|
+
df = df.set_index(['building_category', 'purpose', 'building_code']).sort_index()
|
261
|
+
|
262
|
+
df = building_purpose.join(df, how='left')
|
263
|
+
|
264
|
+
behaviour_factor = self.get_behaviour_factor().set_index(['building_category', 'building_code', 'purpose', 'year'])
|
265
|
+
|
266
|
+
df = df.join(behaviour_factor, how='left')
|
267
|
+
|
268
|
+
heating_rv_factor = self.get_calibrate_heating_rv().set_index(['building_category', 'purpose']).heating_rv_factor
|
269
|
+
|
270
|
+
df['heating_rv_factor'] = heating_rv_factor
|
271
|
+
df['heating_rv_factor'] = df['heating_rv_factor'].astype(float).fillna(1.0)
|
272
|
+
df['uncalibrated_kwh_m2'] = df['kwh_m2']
|
273
|
+
df['calibrated_kwh_m2'] = df.heating_rv_factor * df.kwh_m2
|
274
|
+
df.loc[df.calibrated_kwh_m2.isna(), 'calibrated_kwh_m2'] = df.loc[df.calibrated_kwh_m2.isna(), 'kwh_m2']
|
275
|
+
df['kwh_m2'] = df['calibrated_kwh_m2']
|
276
|
+
return df.reset_index()
|
277
|
+
|
278
|
+
|
279
|
+
def get_energy_req_reduction_per_condition(self) -> pd.DataFrame:
|
280
|
+
"""
|
281
|
+
Get dataframe with shares for reducing the energy requirement of the different building conditions. This
|
282
|
+
function calls explode_unique_columns to expand building_category and TEK as necessary.
|
283
|
+
|
284
|
+
Returns
|
285
|
+
-------
|
286
|
+
pd.DataFrame
|
287
|
+
Dataframe containing energy requirement reduction shares for the different building conditions,
|
288
|
+
per building category, TEK and purpose.
|
289
|
+
"""
|
290
|
+
reduction_per_condition = self.file_handler.get_energy_req_reduction_per_condition()
|
291
|
+
if len(reduction_per_condition[reduction_per_condition.building_code=='TEK21']) > 0:
|
292
|
+
logger.warning(f'Detected TEK21 in energy_requirement_reduction_per_condition')
|
293
|
+
|
294
|
+
return self.explode_unique_columns(reduction_per_condition,
|
295
|
+
['building_category', 'building_code', 'purpose', 'building_condition'])
|
296
|
+
|
297
|
+
def get_energy_need_yearly_improvements(self) -> pd.DataFrame:
|
298
|
+
"""
|
299
|
+
Get dataframe with yearly efficiency rates for energy need improvements. This
|
300
|
+
function calls explode_unique_columns to expand building_category and TEK as necessary.
|
301
|
+
|
302
|
+
The column yearly_efficiency_improvement is expected to contain the yearly reduction as
|
303
|
+
a float between 0.1 and 1.0.
|
304
|
+
|
305
|
+
Returns
|
306
|
+
-------
|
307
|
+
pd.DataFrame
|
308
|
+
Dataframe containing yearly efficiency rates (%) for energy need improvements,
|
309
|
+
per building category, tek and purpose.
|
310
|
+
"""
|
311
|
+
yearly_improvements = self.file_handler.get_energy_need_yearly_improvements()
|
312
|
+
improvements = EnergyNeedYearlyImprovements.validate(yearly_improvements)
|
313
|
+
eny = YearlyReduction.from_energy_need_yearly_improvements(improvements)
|
314
|
+
return eny
|
315
|
+
|
316
|
+
def get_energy_need_policy_improvement(self) -> pd.DataFrame:
|
317
|
+
"""
|
318
|
+
Get dataframe with total energy need improvement in a period related to a policy. This
|
319
|
+
function calls explode_unique_columns to expand building_category and TEK as necessary.
|
320
|
+
|
321
|
+
Returns
|
322
|
+
-------
|
323
|
+
pd.DataFrame
|
324
|
+
Dataframe containing total energy need improvement (%) in a policy period,
|
325
|
+
per building category, tek and purpose.
|
326
|
+
"""
|
327
|
+
en_improvements = self.file_handler.get_energy_need_yearly_improvements()
|
328
|
+
improvements = EnergyNeedYearlyImprovements.validate(en_improvements)
|
329
|
+
enp = PolicyImprovement.from_energy_need_yearly_improvements(improvements)
|
330
|
+
return enp
|
331
|
+
|
332
|
+
def get_holiday_home_fuelwood_consumption(self) -> pd.Series:
|
333
|
+
df = self.file_handler.get_holiday_home_energy_consumption().set_index('year')["fuelwood"]
|
334
|
+
return df
|
335
|
+
|
336
|
+
def get_holiday_home_fossilfuel_consumption(self) -> pd.Series:
|
337
|
+
df = self.file_handler.get_holiday_home_energy_consumption().set_index('year')["fossilfuel"]
|
338
|
+
return df
|
339
|
+
|
340
|
+
def get_holiday_home_electricity_consumption(self) -> pd.Series:
|
341
|
+
df = self.file_handler.get_holiday_home_energy_consumption().set_index('year')["electricity"]
|
342
|
+
return df
|
343
|
+
|
344
|
+
def get_holiday_home_by_year(self) -> pd.DataFrame:
|
345
|
+
return self.file_handler.get_holiday_home_by_year().set_index('year')
|
346
|
+
|
347
|
+
def get_calibrate_heating_rv(self) -> pd.Series:
|
348
|
+
df = self.file_handler.get_calibrate_heating_rv()
|
349
|
+
df = expand_building_categories(df, unique_columns=['building_category', 'purpose'])
|
350
|
+
return df[['building_category', 'purpose', 'heating_rv_factor']]
|
351
|
+
|
352
|
+
def get_calibrate_heating_systems(self) -> pd.DataFrame:
|
353
|
+
df = self.file_handler.get_calibrate_heating_systems()
|
354
|
+
df = expand_building_categories(df, unique_columns=['building_category', 'to', 'from'])
|
355
|
+
return df
|
356
|
+
|
357
|
+
def get_area_per_person(self,
|
358
|
+
building_category: BuildingCategory = None) -> pd.Series:
|
359
|
+
"""
|
360
|
+
Return area_per_person as a pd.Series
|
361
|
+
|
362
|
+
Parameters
|
363
|
+
----------
|
364
|
+
building_category: BuildingCategory, optional
|
365
|
+
filter for building category
|
366
|
+
Returns
|
367
|
+
-------
|
368
|
+
pd.Series
|
369
|
+
float values indexed by building_category, (year)
|
370
|
+
"""
|
371
|
+
df = self.file_handler.get_area_per_person()
|
372
|
+
df = df.set_index('building_category')
|
373
|
+
|
374
|
+
if building_category:
|
375
|
+
return df.area_per_person.loc[building_category]
|
376
|
+
return df.area_per_person
|
377
|
+
|
378
|
+
def validate_database(self):
|
379
|
+
missing_files = self.file_handler.check_for_missing_files()
|
380
|
+
return True
|
381
|
+
|
382
|
+
def get_heating_systems_shares_start_year(self):
|
383
|
+
df = self.file_handler.get_heating_systems_shares_start_year()
|
384
|
+
heating_systems_factor = self.get_calibrate_heating_systems()
|
385
|
+
calibrated = calibrate_heating_systems(df, heating_systems_factor)
|
386
|
+
|
387
|
+
return calibrated
|
388
|
+
|
389
|
+
def get_heating_system_efficiencies(self):
|
390
|
+
return self.file_handler.get_heating_system_efficiencies()
|
391
|
+
|
392
|
+
def get_heating_system_forecast(self):
|
393
|
+
return self.file_handler.get_heating_system_forecast()
|
394
|
+
|
395
|
+
def explode_unique_columns(self, df, unique_columns):
|
396
|
+
return explode_unique_columns(df, unique_columns, default_building_code=self.get_building_code_list())
|
397
|
+
|
398
|
+
def explode_building_category_column(self, df, unique_columns):
|
399
|
+
return explode_building_category_column(df, unique_columns)
|
400
|
+
|
401
|
+
def explode_building_code_column(self, ff, unique_columns):
|
402
|
+
return explode_building_code_column(ff, unique_columns, default_building_code=self.get_building_code_list())
|
403
|
+
|
404
|
+
|
405
|
+
if __name__ == '__main__':
|
406
|
+
db = DatabaseManager()
|
407
|
+
building_category = BuildingCategory.HOUSE
|
408
|
+
|
409
|
+
a = db.get_energy_need_policy_improvement()
|
410
|
+
print(a)
|