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,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)