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
ebm/validators.py
ADDED
@@ -0,0 +1,565 @@
|
|
1
|
+
"""
|
2
|
+
Pandera validators for ebm input files.
|
3
|
+
"""
|
4
|
+
import itertools
|
5
|
+
|
6
|
+
import numpy as np
|
7
|
+
import pandas as pd
|
8
|
+
import pandera as pa
|
9
|
+
|
10
|
+
from ebm.model.building_category import BuildingCategory, RESIDENTIAL, NON_RESIDENTIAL
|
11
|
+
from ebm.model.building_condition import BuildingCondition
|
12
|
+
from ebm.model.column_operations import explode_unique_columns, explode_column_alias
|
13
|
+
from ebm.model.data_classes import YearRange
|
14
|
+
from ebm.model.energy_purpose import EnergyPurpose
|
15
|
+
from ebm.model.heating_systems import HeatingSystems
|
16
|
+
|
17
|
+
|
18
|
+
def check_building_category(value: pd.Series) -> pd.Series:
|
19
|
+
"""
|
20
|
+
Makes sure that the series value contains values that are corresponding to a BuildingCategory
|
21
|
+
|
22
|
+
Parameters
|
23
|
+
----------
|
24
|
+
value: pd.Series
|
25
|
+
A series of str that will be checked against BuildingCategory
|
26
|
+
|
27
|
+
Returns
|
28
|
+
-------
|
29
|
+
pd.Series of bool values
|
30
|
+
|
31
|
+
"""
|
32
|
+
return value.isin(iter(BuildingCategory))
|
33
|
+
|
34
|
+
def check_default_building_category(value: pd.Series) -> pd.Series:
|
35
|
+
"""
|
36
|
+
Makes sure that the series value contains values that are corresponding to a BuildingCategory or default
|
37
|
+
|
38
|
+
Parameters
|
39
|
+
----------
|
40
|
+
value: pd.Series
|
41
|
+
A series of str that will be checked against BuildingCategory and 'default'
|
42
|
+
|
43
|
+
Returns
|
44
|
+
-------
|
45
|
+
pd.Series of bool values
|
46
|
+
|
47
|
+
"""
|
48
|
+
return value.isin(list(BuildingCategory) + ['default'])
|
49
|
+
|
50
|
+
def check_default_building_category_with_group(value: pd.Series) -> pd.Series:
|
51
|
+
"""
|
52
|
+
Makes sure that the series value contains values that are corresponding to a BuildingCategory,
|
53
|
+
BuildingCategory group (RESIDENTIAL or NON_RESIDENTIAL) or 'default'
|
54
|
+
|
55
|
+
Parameters
|
56
|
+
----------
|
57
|
+
value: pd.Series
|
58
|
+
A series of str that will be checked against BuildingCategory, RESIDENTIAL, NON_RESIDENTIAL and 'default'
|
59
|
+
|
60
|
+
Returns
|
61
|
+
-------
|
62
|
+
pd.Series of bool values
|
63
|
+
"""
|
64
|
+
return value.isin(list(BuildingCategory) + ['default'] + [RESIDENTIAL, NON_RESIDENTIAL])
|
65
|
+
|
66
|
+
|
67
|
+
def check_building_condition(value: pd.Series) -> pd.Series:
|
68
|
+
"""
|
69
|
+
Makes sure that the series value contains values that are corresponding to a BuildingCondition
|
70
|
+
|
71
|
+
Parameters
|
72
|
+
----------
|
73
|
+
value: pd.Series
|
74
|
+
A series of str that will be checked against BuildingCondition
|
75
|
+
|
76
|
+
Returns
|
77
|
+
-------
|
78
|
+
pd.Series of bool values
|
79
|
+
|
80
|
+
"""
|
81
|
+
return value.isin(iter(BuildingCondition))
|
82
|
+
|
83
|
+
|
84
|
+
def check_existing_building_conditions(value: pd.Series) -> pd.Series:
|
85
|
+
"""
|
86
|
+
Makes sure that the series contains values that are corresponding to 'existing' building conditions.
|
87
|
+
|
88
|
+
Existing building conditions are all members (conditions) of BuildingCondition, except of DEMOLITION.
|
89
|
+
|
90
|
+
Parameters
|
91
|
+
----------
|
92
|
+
value: pd.Series
|
93
|
+
A series of str that will be checked against 'existing' BuildingCondition members
|
94
|
+
|
95
|
+
Returns
|
96
|
+
-------
|
97
|
+
pd.Series of bool values
|
98
|
+
|
99
|
+
"""
|
100
|
+
return value.isin(iter(BuildingCondition.existing_conditions()))
|
101
|
+
|
102
|
+
|
103
|
+
def check_all_existing_building_conditions_present(df: pd.DataFrame):
|
104
|
+
"""
|
105
|
+
Ensures that all 'existing' building conditions are present in the 'building_conditions' column for
|
106
|
+
each unique combination of 'building_category', 'building_code', and 'purpose'.
|
107
|
+
|
108
|
+
Existing building conditions are all members (conditions) of BuildingCondition, except of DEMOLITION.
|
109
|
+
|
110
|
+
Parameters
|
111
|
+
----------
|
112
|
+
df: pd.Dataframe
|
113
|
+
"""
|
114
|
+
grouped = df.groupby(['building_category', 'building_code', 'purpose'])['building_condition']
|
115
|
+
existing_conditions = set(BuildingCondition.existing_conditions())
|
116
|
+
for _, conditions in grouped:
|
117
|
+
if set(conditions) != existing_conditions:
|
118
|
+
return False
|
119
|
+
return True
|
120
|
+
|
121
|
+
|
122
|
+
def check_energy_purpose(value: pd.Series) -> pd.Series:
|
123
|
+
"""
|
124
|
+
Makes sure that the value contains one of the valid purpose values: 'Cooling', 'Electrical equipment', 'Fans and pumps', 'HeatingDHW', 'HeatingRV', or 'Lighting'
|
125
|
+
|
126
|
+
Args:
|
127
|
+
value: Input value to check against the valid purpose values
|
128
|
+
|
129
|
+
Returns:
|
130
|
+
pd.Series: Series of bool values indicating if each value matches a valid purpose
|
131
|
+
"""
|
132
|
+
return value.isin(iter(EnergyPurpose))
|
133
|
+
|
134
|
+
|
135
|
+
def check_default_energy_purpose(value: pd.Series) -> pd.Series:
|
136
|
+
"""
|
137
|
+
Makes sure that the value contains one of the default or purpose values: 'Cooling', 'Electrical equipment', 'Fans and pumps', 'HeatingDHW', 'HeatingRV', or 'Lighting'
|
138
|
+
|
139
|
+
Args:
|
140
|
+
value: Input value to check against the valid purpose values
|
141
|
+
|
142
|
+
Returns:
|
143
|
+
pd.Series: Series of bool values indicating if each value matches a valid purpose
|
144
|
+
"""
|
145
|
+
return value.isin(list(EnergyPurpose) + ['default'])
|
146
|
+
|
147
|
+
|
148
|
+
def check_building_code(value: str) -> bool:
|
149
|
+
"""
|
150
|
+
A crude check to determine if value is a 'building_code'
|
151
|
+
|
152
|
+
Args:
|
153
|
+
value (str): A string to check if it's a building_code
|
154
|
+
|
155
|
+
Returns:
|
156
|
+
bool: True when the function thinks that value might be a building_code
|
157
|
+
"""
|
158
|
+
return 'TEK' in value
|
159
|
+
|
160
|
+
|
161
|
+
def check_default_building_code(value: str) -> bool:
|
162
|
+
"""
|
163
|
+
A crude check to determine if value is a 'building_code' or default
|
164
|
+
|
165
|
+
Args:
|
166
|
+
value (str): A string to check if it's a TEK or default
|
167
|
+
|
168
|
+
Returns:
|
169
|
+
bool: True when the function thinks that value might be a TEK
|
170
|
+
"""
|
171
|
+
return check_building_code(value) or value == 'default'
|
172
|
+
|
173
|
+
#TODO: edge cases?
|
174
|
+
def check_overlapping_building_code_periods(df: pd.DataFrame) -> pd.Series:
|
175
|
+
"""
|
176
|
+
"""
|
177
|
+
df = df.sort_values(["period_end_year"])
|
178
|
+
end_years = df['period_end_year'] + 1
|
179
|
+
start_years = df["period_start_year"].shift(-1)
|
180
|
+
|
181
|
+
end_years = end_years.iloc[:-1]
|
182
|
+
start_years = start_years.iloc[:-1]
|
183
|
+
check = end_years == start_years
|
184
|
+
checked_series = pd.Series(check.to_list() +[True])
|
185
|
+
return checked_series
|
186
|
+
|
187
|
+
|
188
|
+
def check_building_category_share(values: pd.DataFrame) -> pd.Series:
|
189
|
+
"""
|
190
|
+
Makes sure that the sum of values in values.new_house_share + values.new_apartment_block_share is 1.0
|
191
|
+
|
192
|
+
Args:
|
193
|
+
values (pd.DataFrame): A dataframe with new_house_share and new_apartment_block_share
|
194
|
+
|
195
|
+
Returns:
|
196
|
+
pd.Series: A series of bool with the truth value of new_house_share + new_apartment_block_share equals 1.0
|
197
|
+
"""
|
198
|
+
return values.new_house_share + values.new_apartment_block_share == 1.0
|
199
|
+
|
200
|
+
|
201
|
+
def create_residential_area_checks():
|
202
|
+
"""
|
203
|
+
Creates a list of checks used for house and apartment_block categories.
|
204
|
+
- Checks that the first two rows are not empty
|
205
|
+
- Checks that the next (3) rows are empty
|
206
|
+
- Checks that non-empty rows are not negative
|
207
|
+
|
208
|
+
Returns
|
209
|
+
-------
|
210
|
+
List[pa.Check]
|
211
|
+
"""
|
212
|
+
return [
|
213
|
+
pa.Check(lambda s: s.iloc[:2].notnull().all(),
|
214
|
+
element_wise=False,
|
215
|
+
error='Expects number in first two rows for house'),
|
216
|
+
pa.Check(lambda s: s.iloc[2:].isnull().all(),
|
217
|
+
element_wise=False,
|
218
|
+
error='Expects empty in the last four years for house'),
|
219
|
+
pa.Check.greater_than_or_equal_to(0.0)]
|
220
|
+
|
221
|
+
|
222
|
+
def check_heating_systems(value: pd.Series) -> pd.Series:
|
223
|
+
"""
|
224
|
+
Makes sure that the series contains values that corresponds to a HeatingSystems
|
225
|
+
|
226
|
+
Parameters
|
227
|
+
----------
|
228
|
+
value: pd.Series
|
229
|
+
A series of str that will be checked against HeatingSystems
|
230
|
+
|
231
|
+
Returns
|
232
|
+
-------
|
233
|
+
pd.Series of bool values
|
234
|
+
|
235
|
+
"""
|
236
|
+
return value.isin(iter(HeatingSystems))
|
237
|
+
|
238
|
+
|
239
|
+
def check_sum_of_heating_system_shares_equal_1(df: pd.DataFrame):
|
240
|
+
"""
|
241
|
+
"""
|
242
|
+
precision = 4
|
243
|
+
df = df.groupby(by=['building_category', 'building_code'])[['heating_system_share']].sum()
|
244
|
+
df['heating_system_share'] = round(df['heating_system_share'] * 100, precision)
|
245
|
+
return_series = df["heating_system_share"] == 100.0
|
246
|
+
return return_series
|
247
|
+
|
248
|
+
|
249
|
+
def make_building_purpose(years: YearRange | None = None) -> pd.DataFrame:
|
250
|
+
"""
|
251
|
+
Returns a dataframe of all combinations building_categories, building_codes, original_condition, purposes
|
252
|
+
and optionally years.
|
253
|
+
|
254
|
+
Parameters
|
255
|
+
----------
|
256
|
+
years : YearRange, optional
|
257
|
+
|
258
|
+
Returns
|
259
|
+
-------
|
260
|
+
pd.DataFrame
|
261
|
+
"""
|
262
|
+
data = []
|
263
|
+
columns = [list(BuildingCategory),
|
264
|
+
['PRE_TEK49', 'TEK49', 'TEK69', 'TEK87', 'TEK97', 'TEK07', 'TEK10', 'TEK17'],
|
265
|
+
EnergyPurpose]
|
266
|
+
|
267
|
+
column_headers = ['building_category', 'building_code', 'building_condition', 'purpose']
|
268
|
+
if years:
|
269
|
+
columns.append(years)
|
270
|
+
column_headers.append('year')
|
271
|
+
|
272
|
+
for bc, building_code, purpose, *year in itertools.product(*columns):
|
273
|
+
row = [bc, building_code, 'original_condition', purpose]
|
274
|
+
if years:
|
275
|
+
row.append(year[0])
|
276
|
+
data.append(row)
|
277
|
+
|
278
|
+
return pd.DataFrame(data=data, columns=column_headers)
|
279
|
+
|
280
|
+
|
281
|
+
def behaviour_factor_parser(df: pd.DataFrame) -> pd.DataFrame:
|
282
|
+
model_years = YearRange(2020, 2050)
|
283
|
+
all_combinations = make_building_purpose(years=model_years)
|
284
|
+
|
285
|
+
if 'start_year' not in df.columns:
|
286
|
+
df=df.assign(**{'start_year': model_years.start})
|
287
|
+
if 'end_year' not in df.columns:
|
288
|
+
df=df.assign(**{'end_year': model_years.end})
|
289
|
+
if 'function' not in df.columns:
|
290
|
+
df=df.assign(function='noop')
|
291
|
+
else:
|
292
|
+
df['function'] = df.function.fillna('noop')
|
293
|
+
if 'parameter' not in df.columns:
|
294
|
+
df=df.assign(parameter=0.0)
|
295
|
+
|
296
|
+
df['start_year'] = df.start_year.fillna(model_years.start).astype(int)
|
297
|
+
df['end_year'] = df.end_year.fillna(model_years.end).astype(int)
|
298
|
+
|
299
|
+
unique_columns = ['building_category', 'building_code', 'purpose', 'start_year', 'end_year']
|
300
|
+
behaviour_factor = explode_unique_columns(df,
|
301
|
+
unique_columns=unique_columns)
|
302
|
+
|
303
|
+
behaviour_factor = explode_column_alias(behaviour_factor,
|
304
|
+
column='purpose',
|
305
|
+
values=[p for p in EnergyPurpose],
|
306
|
+
alias='default',
|
307
|
+
de_dup_by=unique_columns)
|
308
|
+
|
309
|
+
behaviour_factor['year'] = behaviour_factor.apply(
|
310
|
+
lambda row: range(row.start_year, row.end_year+1), axis=1)
|
311
|
+
behaviour_factor['interpolation'] = behaviour_factor.apply(
|
312
|
+
lambda row: np.linspace(row.behaviour_factor, row.parameter, num=row.end_year+1-row.start_year), axis=1)
|
313
|
+
|
314
|
+
behaviour_factor = behaviour_factor.explode(['year', 'interpolation'])
|
315
|
+
|
316
|
+
behaviour_factor['year'] = behaviour_factor['year'].astype(int)
|
317
|
+
|
318
|
+
interpolation_slice = (behaviour_factor.function == 'improvement_at_end_year') & (~behaviour_factor.interpolation.isna())
|
319
|
+
behaviour_factor.loc[interpolation_slice, 'behaviour_factor'] = behaviour_factor.loc[
|
320
|
+
interpolation_slice, 'interpolation'].astype(float)
|
321
|
+
|
322
|
+
behaviour_factor.sort_values(['building_category', 'building_code', 'purpose', 'year'])
|
323
|
+
|
324
|
+
behaviour_factor = calculate_yearly_reduction(behaviour_factor)
|
325
|
+
|
326
|
+
behaviour_factor=behaviour_factor.set_index(['building_category', 'building_code', 'purpose', 'year'], drop=True)
|
327
|
+
all_combinations=all_combinations.set_index(['building_category', 'building_code', 'purpose', 'year'], drop=True)
|
328
|
+
|
329
|
+
joined = all_combinations.join(behaviour_factor, how='left')
|
330
|
+
joined.behaviour_factor = joined.behaviour_factor.fillna(1.0)
|
331
|
+
return joined.reset_index()
|
332
|
+
|
333
|
+
|
334
|
+
def calculate_yearly_reduction(df):
|
335
|
+
reduction_slice = df[df['function'] == 'yearly_reduction'].index
|
336
|
+
df.loc[reduction_slice, 'behaviour_factor'] = df.loc[reduction_slice].behaviour_factor * ((1.0 - df.loc[
|
337
|
+
reduction_slice].parameter) ** (df.loc[reduction_slice].year - df.loc[reduction_slice].start_year))
|
338
|
+
return df
|
339
|
+
|
340
|
+
|
341
|
+
energy_need_behaviour_factor = pa.DataFrameSchema(
|
342
|
+
parsers=pa.Parser(behaviour_factor_parser),
|
343
|
+
columns={
|
344
|
+
"building_category": pa.Column(str),
|
345
|
+
'building_code': pa.Column(str), #
|
346
|
+
"purpose": pa.Column(str),
|
347
|
+
'year': pa.Column(int, required=False),
|
348
|
+
'behaviour_factor': pa.Column(float)
|
349
|
+
}
|
350
|
+
)
|
351
|
+
|
352
|
+
area = pa.DataFrameSchema(
|
353
|
+
columns={
|
354
|
+
"building_category": pa.Column(str, checks=[pa.Check(check_building_category)]),
|
355
|
+
'building_code': pa.Column(str, checks=[pa.Check(check_building_code, element_wise=True)]),
|
356
|
+
"area": pa.Column(float, checks=[pa.Check.greater_than(0)], coerce=True)},
|
357
|
+
name='area_parameters'
|
358
|
+
)
|
359
|
+
|
360
|
+
building_code_parameters = pa.DataFrameSchema(columns={
|
361
|
+
'building_code': pa.Column(str, unique=True, checks=[pa.Check(check_building_code, element_wise=True)]),
|
362
|
+
'building_year': pa.Column(int, checks=[
|
363
|
+
pa.Check.greater_than_or_equal_to(1940),
|
364
|
+
pa.Check.less_than_or_equal_to(2070)]),
|
365
|
+
'period_start_year': pa.Column(int, checks=[
|
366
|
+
pa.Check.greater_than_or_equal_to(0),
|
367
|
+
pa.Check.less_than_or_equal_to(2070),
|
368
|
+
]),
|
369
|
+
'period_end_year': pa.Column(int, checks=[
|
370
|
+
pa.Check.greater_than_or_equal_to(1940),
|
371
|
+
pa.Check.less_than_or_equal_to(2070, error='period_end_year should be 2070 or lower'),
|
372
|
+
pa.Check.between(1940, 2070, error='period_end_year should be between 1940 and 2070')])},
|
373
|
+
checks=[pa.Check(lambda df: df["period_end_year"] > df["period_start_year"],
|
374
|
+
error="period_end_year should be greater than period_start_year"),
|
375
|
+
pa.Check(check_overlapping_building_code_periods,
|
376
|
+
error="building_code periods do not overlap")],
|
377
|
+
name='building_code_parameters'
|
378
|
+
)
|
379
|
+
|
380
|
+
|
381
|
+
area_new_residential_buildings = pa.DataFrameSchema(
|
382
|
+
columns={
|
383
|
+
'year': pa.Column(int),
|
384
|
+
'house': pa.Column(pa.Float64, nullable=True, checks=create_residential_area_checks()),
|
385
|
+
'apartment_block': pa.Column(pa.Float64, nullable=True, checks=create_residential_area_checks())
|
386
|
+
},
|
387
|
+
name='construction_building_category_yearly'
|
388
|
+
)
|
389
|
+
|
390
|
+
|
391
|
+
new_buildings_residential = pa.DataFrameSchema(
|
392
|
+
columns={
|
393
|
+
'year': pa.Column(int, checks=[pa.Check.between(2010, 2070)]),
|
394
|
+
'new_house_share': pa.Column(float, checks=[pa.Check.between(0.0, 1.0)]),
|
395
|
+
'new_apartment_block_share': pa.Column(float, checks=[pa.Check.between(0.0, 1.0)]),
|
396
|
+
'floor_area_new_house': pa.Column(int, checks=[pa.Check.between(1, 1000)]),
|
397
|
+
'flood_area_new_apartment_block': pa.Column(int, checks=[pa.Check.between(1, 1000)])
|
398
|
+
},
|
399
|
+
checks=[pa.Check(check_building_category_share,
|
400
|
+
error='The sum of new_house_share and new_apartment_block_share should be 1.0 (100%)')],
|
401
|
+
name='new_buildings_house_share'
|
402
|
+
)
|
403
|
+
|
404
|
+
|
405
|
+
population_forecast = pa.DataFrameSchema(
|
406
|
+
columns={
|
407
|
+
'year': pa.Column(int, coerce=True, checks=[pa.Check.between(1900, 2070)]),
|
408
|
+
'population': pa.Column(int, coerce=True, checks=[pa.Check.greater_than_or_equal_to(0)]),
|
409
|
+
'household_size': pa.Column(float, coerce=True, nullable=True, checks=[pa.Check.greater_than_or_equal_to(0)])},
|
410
|
+
name='new_buildings_population')
|
411
|
+
|
412
|
+
|
413
|
+
#TODO: evaluete if restrictions on rush and never share make sense (if the program crashes unless they are there)
|
414
|
+
s_curve = pa.DataFrameSchema(
|
415
|
+
columns={
|
416
|
+
'building_category': pa.Column(str, checks=[pa.Check(check_building_category)]),
|
417
|
+
'condition': pa.Column(str, checks=[pa.Check(check_building_condition)]),
|
418
|
+
'earliest_age_for_measure': pa.Column(int, checks=[pa.Check.greater_than(0)]),
|
419
|
+
'average_age_for_measure': pa.Column(int, checks=[pa.Check.greater_than(0)]),
|
420
|
+
'rush_period_years': pa.Column(int, checks=[pa.Check.greater_than(0)]),
|
421
|
+
'last_age_for_measure': pa.Column(int, checks=[pa.Check.greater_than(0)]),
|
422
|
+
'rush_share': pa.Column(float, checks=[pa.Check.between(min_value=0.0, max_value=1.0, include_min=False)]),
|
423
|
+
'never_share': pa.Column(float, checks=[pa.Check.between(min_value=0.0, max_value=1.0, include_min=False)])
|
424
|
+
},
|
425
|
+
name='scurve_parameters')
|
426
|
+
|
427
|
+
|
428
|
+
### TODO: remove strong restrictions on float values and add warnings (should be able to be neg values)
|
429
|
+
energy_need_original_condition = pa.DataFrameSchema(
|
430
|
+
columns={
|
431
|
+
'building_category': pa.Column(str, checks=[pa.Check(check_default_building_category)]),
|
432
|
+
'building_code': pa.Column(str, checks=[pa.Check(check_default_building_code, element_wise=True)]),
|
433
|
+
'purpose': pa.Column(str, checks=[pa.Check(check_default_energy_purpose)]),
|
434
|
+
'kwh_m2': pa.Column(float, coerce=True, checks=[pa.Check.greater_than_or_equal_to(0)])
|
435
|
+
},
|
436
|
+
unique=['building_category', 'building_code', 'purpose'],
|
437
|
+
report_duplicates='all'
|
438
|
+
)
|
439
|
+
|
440
|
+
|
441
|
+
improvement_building_upgrade = pa.DataFrameSchema(
|
442
|
+
columns={
|
443
|
+
'building_category': pa.Column(str, checks=pa.Check(check_default_building_category)),
|
444
|
+
'building_code': pa.Column(str, checks=pa.Check(check_default_building_code, element_wise=True)),
|
445
|
+
'purpose': pa.Column(str, checks=pa.Check(check_default_energy_purpose)),
|
446
|
+
'building_condition': pa.Column(str, checks=[pa.Check(check_existing_building_conditions)]),
|
447
|
+
'reduction_share': pa.Column(float, coerce=True, checks=[pa.Check.between(min_value=0.0, include_min=True,
|
448
|
+
max_value=1.0, include_max=True)])
|
449
|
+
},
|
450
|
+
unique=['building_category', 'building_code', 'purpose', 'building_condition'],
|
451
|
+
report_duplicates='all'
|
452
|
+
)
|
453
|
+
|
454
|
+
|
455
|
+
energy_need_improvements = pa.DataFrameSchema(
|
456
|
+
columns={
|
457
|
+
'building_category': pa.Column(str, checks=pa.Check(check_default_building_category)),
|
458
|
+
'building_code': pa.Column(str, checks=pa.Check(check_default_building_code, element_wise=True)),
|
459
|
+
'purpose':pa.Column(str, checks=pa.Check(check_default_energy_purpose)),
|
460
|
+
'value': pa.Column(float, coerce=True,
|
461
|
+
checks=[pa.Check.between(min_value=0.0, include_min=True,
|
462
|
+
max_value=1.0, include_max=True)])
|
463
|
+
},
|
464
|
+
unique=['building_category', 'building_code', 'purpose', 'start_year', 'function', 'end_year'],
|
465
|
+
report_duplicates='all'
|
466
|
+
)
|
467
|
+
|
468
|
+
|
469
|
+
holiday_home_stock = pa.DataFrameSchema(
|
470
|
+
columns={
|
471
|
+
'year': pa.Column(int),
|
472
|
+
'Existing buildings Chalet, summerhouses and other holiday houses': pa.Column(int),
|
473
|
+
'Existing buildings Detached houses and farmhouses used as holiday houses': pa.Column(int)
|
474
|
+
}
|
475
|
+
)
|
476
|
+
|
477
|
+
|
478
|
+
holiday_home_energy_consumption = pa.DataFrameSchema(
|
479
|
+
columns={
|
480
|
+
'year': pa.Column(int),
|
481
|
+
'electricity': pa.Column(int),
|
482
|
+
'fuelwood': pa.Column(float, nullable=True)
|
483
|
+
}
|
484
|
+
)
|
485
|
+
|
486
|
+
area_per_person = pa.DataFrameSchema(
|
487
|
+
columns={
|
488
|
+
'building_category': pa.Column(str, checks=pa.Check(check_building_category)),
|
489
|
+
'area_per_person': pa.Column(float, nullable=True)
|
490
|
+
}
|
491
|
+
)
|
492
|
+
|
493
|
+
|
494
|
+
heating_system_initial_shares = pa.DataFrameSchema(
|
495
|
+
columns={
|
496
|
+
'building_category': pa.Column(str, checks=pa.Check(check_building_category)),
|
497
|
+
'building_code': pa.Column(str, checks=pa.Check(check_default_building_code, element_wise=True)),
|
498
|
+
'heating_systems': pa.Column(str, checks=pa.Check(check_heating_systems)),
|
499
|
+
'year': pa.Column(int, pa.Check(
|
500
|
+
lambda year: len(year.unique()) == 1,
|
501
|
+
error="All values in the 'year' column must be identical."
|
502
|
+
)),
|
503
|
+
'heating_system_share': pa.Column(float, coerce=True,
|
504
|
+
checks=[pa.Check.between(min_value=0.0, include_min=True,
|
505
|
+
max_value=1.0, include_max=True)])
|
506
|
+
},
|
507
|
+
#TODO: better warning messages to see where the issues are
|
508
|
+
checks=[pa.Check(check_sum_of_heating_system_shares_equal_1, raise_warning=True,
|
509
|
+
error="Sum of 'heating_system_share' do not equal 1 for one or more combination of 'building_category' and 'building_code'")],
|
510
|
+
name='heating_systems_shares_start_year'
|
511
|
+
)
|
512
|
+
|
513
|
+
|
514
|
+
#TODO:
|
515
|
+
# - add check on years. Parse to make long format and check years and values? years must be in order, max limit (2070) etc.
|
516
|
+
heating_system_forecast = pa.DataFrameSchema(
|
517
|
+
columns={
|
518
|
+
'building_category': pa.Column(str, checks=pa.Check(check_default_building_category_with_group)),
|
519
|
+
'building_code': pa.Column(str, checks=pa.Check(check_default_building_code, element_wise=True)),
|
520
|
+
'heating_systems': pa.Column(str, checks=pa.Check(check_heating_systems)),
|
521
|
+
'new_heating_systems': pa.Column(str, checks=pa.Check(check_heating_systems))
|
522
|
+
},
|
523
|
+
unique=['building_category', 'building_code', 'heating_systems', 'new_heating_systems'],
|
524
|
+
report_duplicates='all'
|
525
|
+
)
|
526
|
+
|
527
|
+
|
528
|
+
"""
|
529
|
+
TODO: how to check columns that are heating systems (but not in enum) and 'energivare'. Columns:
|
530
|
+
'Grunnlast': pa.Column(str),
|
531
|
+
'Spisslast': pa.Column(str),
|
532
|
+
'Ekstralast': pa.Column(str),
|
533
|
+
'base_load_energy_product': pa.Column(str),
|
534
|
+
'peak_load_energy_product': pa.Column(str),
|
535
|
+
'tertiary_load_energy_product': pa.Column(str),
|
536
|
+
"""
|
537
|
+
heating_system_efficiencies = pa.DataFrameSchema(
|
538
|
+
columns={
|
539
|
+
'heating_systems': pa.Column(str, checks=pa.Check(check_heating_systems)),
|
540
|
+
'base_load_energy_product': pa.Column(str),
|
541
|
+
'peak_load_energy_product': pa.Column(str),
|
542
|
+
'tertiary_load_energy_product': pa.Column(str),
|
543
|
+
'tertiary_load_coverage': pa.Column(float, coerce=True),
|
544
|
+
'base_load_coverage': pa.Column(float, coerce=True),
|
545
|
+
'peak_load_coverage': pa.Column(float, coerce=True),
|
546
|
+
'base_load_efficiency': pa.Column(float, coerce=True),
|
547
|
+
'peak_load_efficiency': pa.Column(float, coerce=True),
|
548
|
+
'tertiary_load_efficiency': pa.Column(float, coerce=True),
|
549
|
+
'domestic_hot_water_energy_product': pa.Column(str),
|
550
|
+
'domestic_hot_water_efficiency': pa.Column(float, coerce=True),
|
551
|
+
'Spesifikt elforbruk': pa.Column(float, coerce=True),
|
552
|
+
'cooling_efficiency': pa.Column(float, coerce=True)
|
553
|
+
}
|
554
|
+
)
|
555
|
+
|
556
|
+
|
557
|
+
__all__ = [area,
|
558
|
+
building_code_parameters,
|
559
|
+
area_new_residential_buildings,
|
560
|
+
new_buildings_residential,
|
561
|
+
population_forecast,
|
562
|
+
s_curve,
|
563
|
+
new_buildings_residential,
|
564
|
+
improvement_building_upgrade]
|
565
|
+
|