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,24 @@
|
|
1
|
+
year,electricity,fuelwood,fossilfuel
|
2
|
+
2001,1128,,
|
3
|
+
2002,1173,,
|
4
|
+
2003,1183,,
|
5
|
+
2004,1161,,
|
6
|
+
2005,1235,,
|
7
|
+
2006,1317,880.0,100.0
|
8
|
+
2007,1407,1050.0,
|
9
|
+
2008,1522,1140.0,
|
10
|
+
2009,1635,1090.0,
|
11
|
+
2010,1931,1180.0,
|
12
|
+
2011,1818,990.0,
|
13
|
+
2012,1929,700.0,
|
14
|
+
2013,2141,1000.0,
|
15
|
+
2014,2006,900.0,
|
16
|
+
2015,2118,1180.0,
|
17
|
+
2016,2278,1100.0,
|
18
|
+
2017,2350,1070.0,
|
19
|
+
2018,2417,1230.0,
|
20
|
+
2019,2384,1170.0,
|
21
|
+
2020,2467,1450.0,
|
22
|
+
2021,2819,1270.0,
|
23
|
+
2022,2318,1390.0,
|
24
|
+
2023,2427,1390.0,
|
@@ -0,0 +1,25 @@
|
|
1
|
+
"year","Existing buildings Chalet, summerhouses and other holiday houses","Existing buildings Detached houses and farmhouses used as holiday houses"
|
2
|
+
"2001",354060,23267
|
3
|
+
"2002",358997,26514
|
4
|
+
"2003",363889,26758
|
5
|
+
"2004",368933,26998
|
6
|
+
"2005",374470,27376
|
7
|
+
"2006",379169,27604
|
8
|
+
"2007",383112,27927
|
9
|
+
"2008",388938,28953
|
10
|
+
"2009",394102,29593
|
11
|
+
"2010",398884,30209
|
12
|
+
"2011",405883,32374
|
13
|
+
"2012",410333,32436
|
14
|
+
"2013",413318,32600
|
15
|
+
"2014",416621,32539
|
16
|
+
"2015",419449,32559
|
17
|
+
"2016",423041,32727
|
18
|
+
"2017",426932,32808
|
19
|
+
"2018",431028,32891
|
20
|
+
"2019",434809,32869
|
21
|
+
"2020",437833,32906
|
22
|
+
"2021",440443,33099
|
23
|
+
"2022",445715,33283
|
24
|
+
"2023",449009,32819
|
25
|
+
"2024",450492,32442
|
@@ -0,0 +1,9 @@
|
|
1
|
+
building_category,building_code,purpose,building_condition,reduction_share
|
2
|
+
default,default,heating_rv,original_condition,0.0
|
3
|
+
default,default,heating_rv,small_measure,0.07
|
4
|
+
default,default,heating_rv,renovation,0.2
|
5
|
+
default,default,heating_rv,renovation_and_small_measure,0.25
|
6
|
+
default,TEK17,heating_rv,original_condition,0.0
|
7
|
+
default,TEK17,heating_rv,small_measure,0.02
|
8
|
+
default,TEK17,heating_rv,renovation,0.05
|
9
|
+
default,TEK17,heating_rv,renovation_and_small_measure,0.07
|
@@ -0,0 +1,32 @@
|
|
1
|
+
"year",new_house_share,new_apartment_block_share,floor_area_new_house,flood_area_new_apartment_block
|
2
|
+
"2020","0.4532258064516","0.5467741935484","175","75"
|
3
|
+
"2021","0.4514516129032","0.5485483870968","175","75"
|
4
|
+
"2022","0.4496774193548","0.5503225806452","175","75"
|
5
|
+
"2023","0.4479032258065","0.5520967741935","175","75"
|
6
|
+
"2024","0.4461290322581","0.5538709677419","175","75"
|
7
|
+
"2025","0.4443548387097","0.5556451612903","175","75"
|
8
|
+
"2026","0.4425806451613","0.5574193548387","175","75"
|
9
|
+
"2027","0.4408064516129","0.5591935483871","175","75"
|
10
|
+
"2028","0.4390322580645","0.5609677419355","175","75"
|
11
|
+
"2029","0.4372580645161","0.5627419354839","175","75"
|
12
|
+
"2030","0.4354838709677","0.5645161290323","175","75"
|
13
|
+
"2031","0.4337096774194","0.5662903225806","175","75"
|
14
|
+
"2032","0.4319354838710","0.5680645161290","175","75"
|
15
|
+
"2033","0.4301612903226","0.5698387096774","175","75"
|
16
|
+
"2034","0.4283870967742","0.5716129032258","175","75"
|
17
|
+
"2035","0.4266129032258","0.5733870967742","175","75"
|
18
|
+
"2036","0.4248387096774","0.5751612903226","175","75"
|
19
|
+
"2037","0.4230645161290","0.5769354838710","175","75"
|
20
|
+
"2038","0.4212903225806","0.5787096774194","175","75"
|
21
|
+
"2039","0.4195161290323","0.5804838709677","175","75"
|
22
|
+
"2040","0.4177419354839","0.5822580645161","175","75"
|
23
|
+
"2041","0.4159677419355","0.5840322580645","175","75"
|
24
|
+
"2042","0.4141935483871","0.5858064516129","175","75"
|
25
|
+
"2043","0.4124193548387","0.5875806451613","175","75"
|
26
|
+
"2044","0.4106451612903","0.5893548387097","175","75"
|
27
|
+
"2045","0.4088709677419","0.5911290322581","175","75"
|
28
|
+
"2046","0.4070967741935","0.5929032258065","175","75"
|
29
|
+
"2047","0.4053225806452","0.5946774193548","175","75"
|
30
|
+
"2048","0.4035483870968","0.5964516129032","175","75"
|
31
|
+
"2049","0.4017741935484","0.5982258064516","175","75"
|
32
|
+
"2050","0.4000000000000","0.6000000000000","175","75"
|
@@ -0,0 +1,51 @@
|
|
1
|
+
year,population,household_size
|
2
|
+
2001,4503436,
|
3
|
+
2002,4524066,
|
4
|
+
2003,4552252,
|
5
|
+
2004,4577457,
|
6
|
+
2005,4606363,
|
7
|
+
2006,4640219,
|
8
|
+
2007,4681134,
|
9
|
+
2008,4737171,
|
10
|
+
2009,4799252,
|
11
|
+
2010,4858199,2.22
|
12
|
+
2011,4920305,2.22
|
13
|
+
2012,4985870,2.22
|
14
|
+
2013,5051275,2.2
|
15
|
+
2014,5109056,2.2
|
16
|
+
2015,5165802,2.2
|
17
|
+
2016,5213985,2.19
|
18
|
+
2017,5258317,2.19
|
19
|
+
2018,5295619,2.17
|
20
|
+
2019,5328212,2.16
|
21
|
+
2020,5367580,2.15
|
22
|
+
2021,5391369,2.14
|
23
|
+
2022,5425270,2.13
|
24
|
+
2023,5488984,2.12
|
25
|
+
2024,5550203,2.115
|
26
|
+
2025,5600121,2.11
|
27
|
+
2026,5638838,2.105
|
28
|
+
2027,5666689,2.1
|
29
|
+
2028,5694657,2.095
|
30
|
+
2029,5722427,2.09
|
31
|
+
2030,5749712,2.085
|
32
|
+
2031,5776723,2.081
|
33
|
+
2032,5803284,2.077
|
34
|
+
2033,5829350,2.073
|
35
|
+
2034,5855072,2.07
|
36
|
+
2035,5880318,2.067
|
37
|
+
2036,5905184,2.064
|
38
|
+
2037,5928866,2.062
|
39
|
+
2038,5951491,2.06
|
40
|
+
2039,5973100,2.059
|
41
|
+
2040,5993766,2.058
|
42
|
+
2041,6013501,2.057
|
43
|
+
2042,6032325,2.056
|
44
|
+
2043,6050194,2.055
|
45
|
+
2044,6067121,2.054
|
46
|
+
2045,6083032,2.053
|
47
|
+
2046,6097893,2.052
|
48
|
+
2047,6111684,2.051
|
49
|
+
2048,6124356,2.05
|
50
|
+
2049,6135899,2.05
|
51
|
+
2050,6146321,2.05
|
ebm/data/s_curve.csv
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
building_category,condition,earliest_age_for_measure,average_age_for_measure,rush_period_years,last_age_for_measure,rush_share,never_share
|
2
|
+
apartment_block,small_measure,5,20,20,50,0.8,0.1
|
3
|
+
apartment_block,renovation,10,30,14,60,0.6,0.15
|
4
|
+
apartment_block,demolition,60,90,40,150,0.7,0.05
|
5
|
+
house,small_measure,3,23,30,80,0.8,0.01
|
6
|
+
house,renovation,10,37,24,75,0.65,0.05
|
7
|
+
house,demolition,60,90,40,150,0.7,0.05
|
8
|
+
kindergarten,small_measure,3,25,20,50,0.8,0.01
|
9
|
+
kindergarten,renovation,10,50,40,90,0.7,0.05
|
10
|
+
kindergarten,demolition,50,100,30,130,0.7,0.05
|
11
|
+
school,small_measure,5,25,20,50,0.8,0.01
|
12
|
+
school,renovation,10,50,44,90,0.65,0.05
|
13
|
+
school,demolition,50,100,30,130,0.7,0.05
|
14
|
+
university,small_measure,5,25,20,50,0.8,0.01
|
15
|
+
university,renovation,15,60,30,100,0.6,0.05
|
16
|
+
university,demolition,50,100,30,130,0.7,0.05
|
17
|
+
office,small_measure,3,20,20,50,0.8,0.01
|
18
|
+
office,renovation,5,55,40,95,0.5,0.1
|
19
|
+
office,demolition,50,100,30,130,0.7,0.05
|
20
|
+
retail,small_measure,3,20,20,50,0.8,0.01
|
21
|
+
retail,renovation,5,48,34,80,0.6,0.1
|
22
|
+
retail,demolition,50,100,30,130,0.7,0.05
|
23
|
+
hotel,small_measure,3,20,20,50,0.8,0.01
|
24
|
+
hotel,renovation,5,45,30,80,0.65,0.02
|
25
|
+
hotel,demolition,50,100,30,130,0.7,0.05
|
26
|
+
hospital,small_measure,3,20,20,50,0.8,0.01
|
27
|
+
hospital,renovation,10,55,30,90,0.7,0.05
|
28
|
+
hospital,demolition,50,100,30,130,0.7,0.05
|
29
|
+
nursing_home,small_measure,3,20,20,50,0.8,0.01
|
30
|
+
nursing_home,renovation,5,45,40,80,0.7,0.05
|
31
|
+
nursing_home,demolition,50,100,30,130,0.7,0.05
|
32
|
+
culture,small_measure,3,20,20,50,0.8,0.01
|
33
|
+
culture,renovation,5,65,40,100,0.75,0.05
|
34
|
+
culture,demolition,50,100,30,130,0.7,0.05
|
35
|
+
sports,small_measure,3,20,20,50,0.8,0.01
|
36
|
+
sports,renovation,15,70,40,120,0.65,0.05
|
37
|
+
sports,demolition,50,100,30,130,0.7,0.05
|
38
|
+
storage_repairs,small_measure,3,20,20,50,0.8,0.01
|
39
|
+
storage_repairs,renovation,5,45,40,80,0.7,0.05
|
40
|
+
storage_repairs,demolition,50,100,30,130,0.7,0.05
|
@@ -0,0 +1,307 @@
|
|
1
|
+
from loguru import logger
|
2
|
+
import pandas as pd
|
3
|
+
|
4
|
+
from ebm.model.energy_purpose import EnergyPurpose
|
5
|
+
from ebm.model.filter_tek import FilterTek
|
6
|
+
|
7
|
+
ADJUSTED_REQUIREMENT = 'eq_ts'
|
8
|
+
# there are 3 time-of-use zones: peak, shoulder and offpeak.
|
9
|
+
HEATING_RV_BASE_TOTAL = 'RV_GL'
|
10
|
+
HEATING_RV_PEAK_TOTAL = 'RV_SL'
|
11
|
+
HEATING_RV_TERTIARY_TOTAL = 'RV_EL'
|
12
|
+
HEAT_PUMP = 'RV_HP'
|
13
|
+
COOLING_TOTAL = 'CL_KV'
|
14
|
+
DHW_TOTAL = 'DHW_TV'
|
15
|
+
OTHER_TOTAL = 'O_SV'
|
16
|
+
HEATING_RV = 'heating_rv'
|
17
|
+
HEATING_DHW = 'heating_dhw'
|
18
|
+
COOLING = 'cooling'
|
19
|
+
DHW_EFFICIENCY = 'domestic_hot_water_efficiency'
|
20
|
+
|
21
|
+
HEATING_SYSTEMS = 'heating_systems'
|
22
|
+
HEATING_SYSTEM_SHARE = 'heating_system_share'
|
23
|
+
GRUNNLAST_ANDEL = 'base_load_coverage'
|
24
|
+
BASE_LOAD_EFFICIENCY = 'base_load_efficiency'
|
25
|
+
COOLING_EFFICIENCY = 'cooling_efficiency'
|
26
|
+
SPESIFIKT_ELFORBRUK = 'Spesifikt elforbruk'
|
27
|
+
TERTIARY_LOAD_COVERAGE = 'tertiary_load_coverage'
|
28
|
+
TERTIARY_LOAD_EFFICIENCY = 'tertiary_load_efficiency'
|
29
|
+
PEAK_LOAD_COVERAGE = 'peak_load_coverage'
|
30
|
+
PEAK_LOAD_EFFICIENCY = 'peak_load_efficiency'
|
31
|
+
|
32
|
+
BASE_LOAD_ENERGY_PRODUCT = 'base_load_energy_product'
|
33
|
+
PEAK_LOAD_ENERGY_PRODUCT = 'peak_load_energy_product'
|
34
|
+
TERTIARY_LOAD_ENERGY_PRODUCT = 'tertiary_load_energy_product'
|
35
|
+
DOMESTIC_HOT_WATER_ENERGY_PRODUCT = 'domestic_hot_water_energy_product'
|
36
|
+
HP_ENERGY_SOURCE = 'hp_source'
|
37
|
+
|
38
|
+
|
39
|
+
class EnergyConsumption:
|
40
|
+
def __init__(self, heating_systems_parameters: pd.DataFrame = None):
|
41
|
+
self.heating_systems_parameters = heating_systems_parameters
|
42
|
+
|
43
|
+
def grouped_heating_systems(self) -> pd.DataFrame:
|
44
|
+
"""
|
45
|
+
Groups and sums heating_system_parameters over building_category, TEK, Oppvarmingstyper. All excess values will
|
46
|
+
be summed.
|
47
|
+
|
48
|
+
Returns
|
49
|
+
-------
|
50
|
+
pd.DataFrame
|
51
|
+
heating_systems_parameters grouped and summed
|
52
|
+
"""
|
53
|
+
df = self.heating_systems_parameters
|
54
|
+
df = df.rename(columns={'heating_system_share': HEATING_SYSTEM_SHARE})
|
55
|
+
|
56
|
+
aggregates = {'base_load_energy_product': 'first', 'peak_load_energy_product': 'first',
|
57
|
+
'tertiary_load_energy_product': 'first', 'domestic_hot_water_energy_product': 'first', HEATING_SYSTEM_SHARE: 'sum',
|
58
|
+
TERTIARY_LOAD_COVERAGE: 'sum', GRUNNLAST_ANDEL: 'sum', PEAK_LOAD_COVERAGE: 'sum',
|
59
|
+
BASE_LOAD_EFFICIENCY: 'sum', PEAK_LOAD_EFFICIENCY: 'sum', TERTIARY_LOAD_EFFICIENCY: 'sum',
|
60
|
+
DHW_EFFICIENCY: 'sum', SPESIFIKT_ELFORBRUK: 'sum', COOLING_EFFICIENCY: 'sum'}
|
61
|
+
grouped = df.groupby(by=['building_category', 'building_code', 'year', HEATING_SYSTEMS]).agg(aggregates)
|
62
|
+
return grouped.reset_index()
|
63
|
+
|
64
|
+
def calculate(self, energy_requirements: pd.DataFrame) -> pd.DataFrame:
|
65
|
+
"""
|
66
|
+
calculate energy usage by from energy_requirements and heating_systems_parameters
|
67
|
+
|
68
|
+
Parameters
|
69
|
+
----------
|
70
|
+
energy_requirements : pd.DataFrame
|
71
|
+
|
72
|
+
Returns
|
73
|
+
-------
|
74
|
+
pd.DataFrame
|
75
|
+
|
76
|
+
"""
|
77
|
+
logger.debug('Calculate heating systems')
|
78
|
+
if all([col in energy_requirements.columns for col in ['building_category', 'building_code', 'building_condition', 'year', 'purpose']]):
|
79
|
+
energy_requirements = energy_requirements.set_index(['building_category', 'building_code', 'building_condition', 'year', 'purpose'])
|
80
|
+
energy_requirements = self._remove_building_code_suffix(energy_requirements)
|
81
|
+
energy_requirements = self._group_and_sum_same_building_code(energy_requirements)
|
82
|
+
|
83
|
+
# If _RES of _COM is in building_codethis will not work
|
84
|
+
# energy_requirements.index[energy_requirements.index.str.endswith('_RES')]
|
85
|
+
if energy_requirements.index.get_level_values('building_code').str.endswith(
|
86
|
+
'_RES').any() or energy_requirements.index.get_level_values('building_code').str.endswith('_COM').any():
|
87
|
+
raise ValueError('Found _RES or _COM in energy_requirements')
|
88
|
+
self.heating_systems_parameters = self.heating_systems_parameters.rename(columns={'heating_system_share': HEATING_SYSTEM_SHARE})
|
89
|
+
# Merge energy_requirements and heating_systems into df
|
90
|
+
df = self._merge_energy_requirement_and_heating_systems(energy_requirements)
|
91
|
+
|
92
|
+
# Make column eq_ts for heating_system_share adjusted energy requirement
|
93
|
+
df[ADJUSTED_REQUIREMENT] = (df.energy_requirement * df[HEATING_SYSTEM_SHARE]).astype(float)
|
94
|
+
|
95
|
+
# Zero fill columns before calculating to prevent NaN from messing up sums
|
96
|
+
df.loc[:, [HEATING_RV_BASE_TOTAL, HEATING_RV_PEAK_TOTAL, HEATING_RV_TERTIARY_TOTAL, DHW_TOTAL, COOLING_TOTAL, OTHER_TOTAL,
|
97
|
+
HEAT_PUMP]] = 0.0
|
98
|
+
|
99
|
+
# Adjust energy requirements by efficiency
|
100
|
+
# heating rv
|
101
|
+
|
102
|
+
df = self.adjust_heat_pump(df)
|
103
|
+
df = self.adjust_heating_rv(df)
|
104
|
+
df = self.adjust_heating_dhw(df)
|
105
|
+
df = self.adjust_cooling(df)
|
106
|
+
df = self.adjust_other(df)
|
107
|
+
|
108
|
+
# sum energy use
|
109
|
+
df.loc[:, 'kwh'] = df.loc[:,
|
110
|
+
[HEATING_RV_BASE_TOTAL, HEATING_RV_PEAK_TOTAL, HEATING_RV_TERTIARY_TOTAL, DHW_TOTAL, COOLING_TOTAL,
|
111
|
+
OTHER_TOTAL]].sum(axis=1)
|
112
|
+
|
113
|
+
df.loc[:, 'gwh'] = df.loc[:, 'kwh'] / 10 ** 6
|
114
|
+
|
115
|
+
df = df.sort_index(level=['building_category', 'building_code', 'year', 'building_condition', 'purpose', HEATING_SYSTEMS])
|
116
|
+
return df[[HEATING_SYSTEM_SHARE, ADJUSTED_REQUIREMENT,
|
117
|
+
HEATING_RV_BASE_TOTAL, BASE_LOAD_ENERGY_PRODUCT, BASE_LOAD_EFFICIENCY,
|
118
|
+
HEATING_RV_PEAK_TOTAL, PEAK_LOAD_ENERGY_PRODUCT, PEAK_LOAD_EFFICIENCY,
|
119
|
+
HEATING_RV_TERTIARY_TOTAL, TERTIARY_LOAD_ENERGY_PRODUCT, TERTIARY_LOAD_EFFICIENCY,
|
120
|
+
DHW_TOTAL, DOMESTIC_HOT_WATER_ENERGY_PRODUCT, COOLING_TOTAL, OTHER_TOTAL, HEAT_PUMP, HP_ENERGY_SOURCE, 'kwh', 'gwh']]
|
121
|
+
|
122
|
+
def adjust_heat_pump(self, df):
|
123
|
+
df[HP_ENERGY_SOURCE] = None
|
124
|
+
gass = ['HP Central heating - Gas']
|
125
|
+
vannbasert = [n for n in df.index.get_level_values('heating_systems').unique() if
|
126
|
+
n.startswith('HP Central heating')]
|
127
|
+
elektrisk = [n for n in df.index.get_level_values('heating_systems').unique() if
|
128
|
+
n.startswith('HP') and n not in vannbasert]
|
129
|
+
|
130
|
+
gass_slice = (slice(None), slice(None), slice(None), slice(None), gass)
|
131
|
+
vann_slice = (slice(None), slice(None), ['heating_rv', 'heating_dhw'], slice(None), slice(None),
|
132
|
+
vannbasert) # , 'heating_dhw'
|
133
|
+
el_slice = (slice(None), slice(None), ['heating_rv'], slice(None), slice(None), elektrisk) # 'heating_dhw'
|
134
|
+
|
135
|
+
df.loc[vann_slice, HEAT_PUMP] = df.loc[vann_slice, ADJUSTED_REQUIREMENT] * df.loc[vann_slice, GRUNNLAST_ANDEL]
|
136
|
+
df.loc[vann_slice, HP_ENERGY_SOURCE] = 'Vannbåren varme'
|
137
|
+
|
138
|
+
df.loc[el_slice, HEAT_PUMP] = df.loc[el_slice, ADJUSTED_REQUIREMENT] * df.loc[el_slice, GRUNNLAST_ANDEL]
|
139
|
+
df.loc[el_slice, HP_ENERGY_SOURCE] = 'Luft/luft'
|
140
|
+
return df
|
141
|
+
|
142
|
+
def adjust_other(self, df):
|
143
|
+
# lighting, electrical equipment, fans and pumps energy use is calculated by dividing with spesific electricity
|
144
|
+
# useage
|
145
|
+
other_slice = (slice(None), slice(None), EnergyPurpose.other(), slice(None), slice(None))
|
146
|
+
df.loc[other_slice, OTHER_TOTAL] = df.loc[other_slice, ADJUSTED_REQUIREMENT] / df.loc[
|
147
|
+
other_slice, SPESIFIKT_ELFORBRUK]
|
148
|
+
return df
|
149
|
+
|
150
|
+
def adjust_cooling(self, df):
|
151
|
+
# cooling energy use is calculated by dividing cooling with 'cooling_efficiency'
|
152
|
+
cooling_slice = (slice(None), slice(None), COOLING, slice(None), slice(None))
|
153
|
+
df.loc[cooling_slice, COOLING_TOTAL] = df.loc[cooling_slice, ADJUSTED_REQUIREMENT] / df.loc[
|
154
|
+
cooling_slice, COOLING_EFFICIENCY]
|
155
|
+
return df
|
156
|
+
|
157
|
+
def adjust_heating_dhw(self, df):
|
158
|
+
# heating dhw energy use is calculated by dividing heating_dhw with 'domestic_hot_water_efficiency'
|
159
|
+
heating_dhw_slice = (slice(None), slice(None), HEATING_DHW, slice(None), slice(None))
|
160
|
+
df.loc[heating_dhw_slice, DHW_TOTAL] = df.loc[heating_dhw_slice, ADJUSTED_REQUIREMENT] / df.loc[
|
161
|
+
heating_dhw_slice, DHW_EFFICIENCY]
|
162
|
+
return df
|
163
|
+
|
164
|
+
def adjust_heating_rv(self, df):
|
165
|
+
heating_rv_slice = (slice(None), slice(None), HEATING_RV, slice(None), slice(None))
|
166
|
+
df.loc[heating_rv_slice, HEATING_RV_BASE_TOTAL] = (
|
167
|
+
df.loc[heating_rv_slice, ADJUSTED_REQUIREMENT] * df.loc[heating_rv_slice, GRUNNLAST_ANDEL] / df.loc[
|
168
|
+
heating_rv_slice, BASE_LOAD_EFFICIENCY])
|
169
|
+
df.loc[heating_rv_slice, HEATING_RV_PEAK_TOTAL] = (
|
170
|
+
df.loc[heating_rv_slice, ADJUSTED_REQUIREMENT] * df.loc[heating_rv_slice, PEAK_LOAD_COVERAGE] / df.loc[
|
171
|
+
heating_rv_slice, PEAK_LOAD_EFFICIENCY])
|
172
|
+
df.loc[heating_rv_slice, HEATING_RV_TERTIARY_TOTAL] = (
|
173
|
+
df.loc[heating_rv_slice, ADJUSTED_REQUIREMENT] * df.loc[heating_rv_slice, TERTIARY_LOAD_COVERAGE] / df.loc[
|
174
|
+
heating_rv_slice, TERTIARY_LOAD_EFFICIENCY])
|
175
|
+
return df
|
176
|
+
|
177
|
+
def _merge_energy_requirement_and_heating_systems(self, energy_requirements):
|
178
|
+
df = energy_requirements.reset_index().merge(self.heating_systems_parameters.reset_index(),
|
179
|
+
left_on=['building_category', 'building_code', 'year'], right_on=['building_category', 'building_code', 'year'])[
|
180
|
+
['building_category', 'building_condition', 'purpose', 'building_code', 'year', 'kwh_m2', 'm2', 'energy_requirement',
|
181
|
+
HEATING_SYSTEMS, HEATING_SYSTEM_SHARE, GRUNNLAST_ANDEL, BASE_LOAD_EFFICIENCY, BASE_LOAD_ENERGY_PRODUCT,
|
182
|
+
PEAK_LOAD_COVERAGE, PEAK_LOAD_EFFICIENCY, PEAK_LOAD_ENERGY_PRODUCT, TERTIARY_LOAD_EFFICIENCY, TERTIARY_LOAD_COVERAGE,
|
183
|
+
TERTIARY_LOAD_ENERGY_PRODUCT, DOMESTIC_HOT_WATER_ENERGY_PRODUCT, DHW_EFFICIENCY, SPESIFIKT_ELFORBRUK, COOLING_EFFICIENCY]]
|
184
|
+
# Unused columns
|
185
|
+
# ,'Innfyrt_energi_kWh','Innfyrt_energi_GWh','Energibehov_samlet_GWh']]
|
186
|
+
df = df.set_index(
|
187
|
+
['building_category', 'building_condition', 'purpose', 'building_code', 'year', HEATING_SYSTEMS]).sort_index()
|
188
|
+
return df
|
189
|
+
|
190
|
+
@staticmethod
|
191
|
+
def _group_and_sum_same_building_code(energy_requirements):
|
192
|
+
energy_requirements = FilterTek.merge_building_code(energy_requirements, 'TEK69', ['TEK69_1976', 'TEK69_1986'])
|
193
|
+
energy_requirements = FilterTek.merge_building_code(energy_requirements, 'PRE_TEK49',
|
194
|
+
['PRE_TEK49_1940', 'PRE_TEK49_1950'])
|
195
|
+
energy_requirements = energy_requirements.sort_index()
|
196
|
+
return energy_requirements
|
197
|
+
|
198
|
+
@staticmethod
|
199
|
+
def _remove_building_code_suffix(energy_requirements):
|
200
|
+
energy_requirements = FilterTek.remove_building_code_suffix(energy_requirements, suffix='_RES')
|
201
|
+
energy_requirements = FilterTek.remove_building_code_suffix(energy_requirements, suffix='_COM')
|
202
|
+
return energy_requirements
|
203
|
+
|
204
|
+
|
205
|
+
def calibrate_heating_systems(df: pd.DataFrame, factor: pd.DataFrame, multiply=False) -> pd.DataFrame:
|
206
|
+
# When factor is empty or all factors are 1.0, there is no need to change anything.
|
207
|
+
if len(factor) == 0 or (factor.factor == 1.0).all():
|
208
|
+
return df
|
209
|
+
original = df.copy()
|
210
|
+
factor = factor.copy()
|
211
|
+
|
212
|
+
if multiply:
|
213
|
+
factor['factor'] = factor['factor'] * 100
|
214
|
+
|
215
|
+
# Add action and value
|
216
|
+
df_to = original.merge(factor,
|
217
|
+
left_on=['building_category', 'heating_systems'],
|
218
|
+
right_on=['building_category', 'to'])
|
219
|
+
|
220
|
+
df_from = original.merge(factor,
|
221
|
+
left_on=['building_category', 'heating_systems'],
|
222
|
+
right_on=['building_category', 'from'])
|
223
|
+
|
224
|
+
# Calculate value to add
|
225
|
+
df_to_add = df_to.set_index(['building_category', 'building_code', 'year', 'to'])
|
226
|
+
|
227
|
+
if multiply:
|
228
|
+
df_to_add['v'] = (df_to_add.heating_system_share * df_to_add.factor)/100 - df_to_add.heating_system_share
|
229
|
+
else:
|
230
|
+
df_to_add['v'] = df_to_add.heating_system_share * df_to_add.factor - df_to_add.heating_system_share
|
231
|
+
|
232
|
+
# Calculate value to subtract
|
233
|
+
df_to_subtract = df_from.set_index(['building_category', 'building_code', 'year', 'from', 'to'])
|
234
|
+
|
235
|
+
df_to_subtract = df_to_subtract.sort_index()
|
236
|
+
df_to_subtract.loc[:, 'v'] = -df_to_add.reset_index().groupby(by=['building_category', 'building_code', 'year', 'from', 'to']).agg(
|
237
|
+
{'v': 'sum'})
|
238
|
+
|
239
|
+
# Join add and substract rows
|
240
|
+
addition_grouped = df_to_add.groupby(by=['building_category', 'building_code', 'year', 'to']).agg(
|
241
|
+
{'v': 'sum', 'heating_systems': 'first', 'heating_system_share': 'first', 'factor': 'first', 'from': 'first'})
|
242
|
+
subtraction_grouped = df_to_subtract.groupby(by=['building_category', 'building_code', 'year', 'from']).agg(
|
243
|
+
{'v': 'sum', 'heating_systems': 'first', 'heating_system_share': 'first', 'factor': 'first'})
|
244
|
+
|
245
|
+
df_to_sum = original.set_index(['building_category', 'building_code', 'year', 'heating_systems'])
|
246
|
+
df_to_sum = df_to_sum.sort_index()
|
247
|
+
df_to_sum.loc[:, 'add'] = addition_grouped.loc[:, 'v']
|
248
|
+
df_to_sum.loc[:, 'sub'] = subtraction_grouped.loc[:, 'v'].fillna(0)
|
249
|
+
df_to_sum.loc[:, 'sub'] = df_to_sum.loc[:, 'sub'].fillna(0)
|
250
|
+
|
251
|
+
df_to_sum.heating_system_share = df_to_sum.heating_system_share + df_to_sum['add'].fillna(0) + df_to_sum['sub'].fillna(0)
|
252
|
+
|
253
|
+
calibrated_and_original = pd.concat([df_to_sum.reset_index(), original.reset_index()]).drop_duplicates(
|
254
|
+
['building_category', 'building_code', 'year', 'heating_systems'],
|
255
|
+
keep='first').drop(columns='index',
|
256
|
+
errors='ignore')
|
257
|
+
|
258
|
+
columns_to_keep = ['building_category', 'building_code', 'year', 'heating_systems', 'heating_system_share']
|
259
|
+
return calibrated_and_original[columns_to_keep].reset_index(drop=True)
|
260
|
+
|
261
|
+
|
262
|
+
def calibrate_heating_systems_adder(df: pd.DataFrame, factor: pd.DataFrame) -> pd.DataFrame:
|
263
|
+
# When factor is empty or all factors are 1.0, there is no need to change anything.
|
264
|
+
if len(factor) == 0 or (factor.factor == 0.0).all():
|
265
|
+
return df
|
266
|
+
original = df.copy()
|
267
|
+
factor = factor.copy()
|
268
|
+
|
269
|
+
# Add action and value
|
270
|
+
df_to = original.merge(factor,
|
271
|
+
left_on=['building_category', 'heating_systems'],
|
272
|
+
right_on=['building_category', 'to'])
|
273
|
+
|
274
|
+
df_from = original.merge(factor,
|
275
|
+
left_on=['building_category', 'heating_systems'],
|
276
|
+
right_on=['building_category', 'from'])
|
277
|
+
|
278
|
+
# Calculate value to add
|
279
|
+
df_to_add = df_to.set_index(['building_category', 'building_code', 'year', 'to'])
|
280
|
+
|
281
|
+
df_to_add['v'] = df_to_add.factor
|
282
|
+
|
283
|
+
# Calculate value to subtract
|
284
|
+
df_to_subtract = df_from.set_index(['building_category', 'building_code', 'year', 'from', 'to'])
|
285
|
+
df_to_subtract.loc[:, 'v'] = -df_to_add.reset_index().groupby(by=['building_category', 'building_code', 'year', 'from', 'to']).agg(
|
286
|
+
{'v': 'sum'})
|
287
|
+
|
288
|
+
# Join add and substract rows
|
289
|
+
addition_grouped = df_to_add.groupby(by=['building_category', 'building_code', 'year', 'to']).agg(
|
290
|
+
{'v': 'sum', 'heating_systems': 'first', 'heating_system_share': 'first', 'factor': 'first', 'from': 'first'})
|
291
|
+
subtraction_grouped = df_to_subtract.groupby(by=['building_category', 'building_code', 'year', 'from']).agg(
|
292
|
+
{'v': 'sum', 'heating_systems': 'first', 'heating_system_share': 'first', 'factor': 'first'})
|
293
|
+
|
294
|
+
df_to_sum = original.set_index(['building_category', 'building_code', 'year', 'heating_systems'])
|
295
|
+
df_to_sum.loc[:, 'add'] = addition_grouped.loc[:, 'v']
|
296
|
+
df_to_sum.loc[:, 'sub'] = subtraction_grouped.loc[:, 'v'].fillna(0)
|
297
|
+
df_to_sum.loc[:, 'sub'] = df_to_sum.loc[:, 'sub'].fillna(0)
|
298
|
+
|
299
|
+
df_to_sum.heating_system_share = df_to_sum.heating_system_share + df_to_sum['add'].fillna(0) + df_to_sum['sub'].fillna(0)
|
300
|
+
|
301
|
+
calibrated_and_original = pd.concat([df_to_sum.reset_index(), original.reset_index()]).drop_duplicates(
|
302
|
+
['building_category', 'building_code', 'year', 'heating_systems'],
|
303
|
+
keep='first').drop(columns='index',
|
304
|
+
errors='ignore')
|
305
|
+
|
306
|
+
columns_to_keep = ['building_category', 'building_code', 'year', 'heating_systems', 'heating_system_share']
|
307
|
+
return calibrated_and_original[columns_to_keep].reset_index(drop=True)
|
ebm/extractors.py
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
import pandas as pd
|
2
|
+
from loguru import logger
|
3
|
+
|
4
|
+
from ebm.model import area
|
5
|
+
from ebm.holiday_home_energy import calculate_energy_use, transform_holiday_homes_to_horizontal
|
6
|
+
from ebm.heating_system_forecast import HeatingSystemsForecast
|
7
|
+
from ebm.model.construction import ConstructionCalculator
|
8
|
+
|
9
|
+
from ebm.model.data_classes import YearRange
|
10
|
+
from ebm.model.database_manager import DatabaseManager
|
11
|
+
from ebm.model.energy_requirement import EnergyRequirement
|
12
|
+
from ebm.s_curve import calculate_s_curves
|
13
|
+
|
14
|
+
|
15
|
+
def extract_area_forecast(years: YearRange, s_curves_by_condition: pd.DataFrame, building_code_parameters: pd.DataFrame, area_parameters: pd.DataFrame, database_manager:DatabaseManager):
|
16
|
+
logger.debug('Calculating area by condition')
|
17
|
+
|
18
|
+
s_curve_demolition = s_curves_by_condition['s_curve_demolition']
|
19
|
+
s_curves_by_condition = s_curves_by_condition[[
|
20
|
+
'original_condition', 'small_measure', 'renovation', 'renovation_and_small_measure', 'demolition'
|
21
|
+
]]
|
22
|
+
|
23
|
+
area_parameters = area_parameters.set_index(['building_category', 'building_code'])
|
24
|
+
|
25
|
+
demolition_floor_area_by_year = area.calculate_demolition_floor_area_by_year(area_parameters, s_curve_demolition)
|
26
|
+
|
27
|
+
building_category_demolition_by_year = area.sum_building_category_demolition_by_year(demolition_floor_area_by_year)
|
28
|
+
construction_floor_area_by_year = ConstructionCalculator.calculate_all_construction(
|
29
|
+
demolition_by_year=building_category_demolition_by_year,
|
30
|
+
database_manager=database_manager,
|
31
|
+
period=years)
|
32
|
+
|
33
|
+
construction_by_building_category_and_year = area.construction_with_building_code(
|
34
|
+
building_category_demolition_by_year=building_category_demolition_by_year,
|
35
|
+
construction_floor_area_by_year=construction_floor_area_by_year,
|
36
|
+
building_code=building_code_parameters,
|
37
|
+
years=years)
|
38
|
+
|
39
|
+
existing_area = area.calculate_existing_area(area_parameters, building_code_parameters, years)
|
40
|
+
|
41
|
+
total_area_floor_by_year = area.merge_total_area_by_year(construction_by_building_category_and_year, existing_area)
|
42
|
+
|
43
|
+
floor_area_forecast = area.multiply_s_curves_with_floor_area(s_curves_by_condition, total_area_floor_by_year)
|
44
|
+
|
45
|
+
return floor_area_forecast
|
46
|
+
|
47
|
+
|
48
|
+
def write_scurve(s_curves_by_condition):
|
49
|
+
try:
|
50
|
+
output_file = 'output/s_curves.xlsx'
|
51
|
+
with pd.ExcelWriter(output_file, engine='xlsxwriter') as writer:
|
52
|
+
s_curves_by_condition.to_excel(writer, sheet_name='s_curves_by_condition', merge_cells=False) # 💾
|
53
|
+
# s_curve_demolition.to_excel(writer, sheet_name='s_curve_demolition', merge_cells=False) # 💾
|
54
|
+
except IOError as ex:
|
55
|
+
logger.exception(ex)
|
56
|
+
logger.info(f'There was an IOError while writing to {output_file}. Moving on!')
|
57
|
+
|
58
|
+
|
59
|
+
def extract_energy_need(years: YearRange, dm: DatabaseManager) -> pd.DataFrame:
|
60
|
+
er_calculator = EnergyRequirement.new_instance(period=years, calibration_year=2023,
|
61
|
+
database_manager=dm)
|
62
|
+
energy_need = er_calculator.calculate_for_building_category(database_manager=dm)
|
63
|
+
|
64
|
+
energy_need = energy_need.set_index(['building_category', 'building_code', 'purpose', 'building_condition', 'year'])
|
65
|
+
|
66
|
+
return energy_need
|
67
|
+
|
68
|
+
|
69
|
+
def extract_heating_systems_forecast(years: YearRange, database_manager: DatabaseManager) -> pd.DataFrame:
|
70
|
+
forecast_period = YearRange(2023, 2050)
|
71
|
+
hsp = HeatingSystemsForecast.new_instance(forecast_period, database_manager)
|
72
|
+
df: pd.DataFrame = hsp.calculate_forecast()
|
73
|
+
df = hsp.pad_projection(df, YearRange(2020, 2022))
|
74
|
+
|
75
|
+
heating_system_forecast = df.copy()
|
76
|
+
return heating_system_forecast
|
77
|
+
|
78
|
+
|
79
|
+
def extract_energy_use_holiday_homes(database_manager):
|
80
|
+
df = transform_holiday_homes_to_horizontal(calculate_energy_use(database_manager)).copy()
|
81
|
+
df = df.rename(columns={'building_category': 'building_group'})
|
82
|
+
df.loc[df.energy_source=='Elektrisitet', 'energy_source'] = 'Electricity'
|
83
|
+
df.loc[df.energy_source=='fossil', 'energy_source'] = 'Fossil'
|
84
|
+
return df
|
85
|
+
|
86
|
+
|
87
|
+
def main():
|
88
|
+
from ebm.model.file_handler import FileHandler
|
89
|
+
fh = FileHandler(directory='input')
|
90
|
+
dm = DatabaseManager(fh)
|
91
|
+
years = YearRange(2020, 2050)
|
92
|
+
|
93
|
+
building_code_parameters = fh.get_building_code()
|
94
|
+
scurve_params = dm.get_scurve_params()
|
95
|
+
s_curves_by_condition = calculate_s_curves(scurve_params, building_code_parameters, years)
|
96
|
+
|
97
|
+
area_forecast = extract_area_forecast(years,
|
98
|
+
building_code_parameters=building_code_parameters,
|
99
|
+
area_parameters=dm.get_area_parameters(),
|
100
|
+
s_curves_by_condition=s_curves_by_condition,
|
101
|
+
database_manager=dm)
|
102
|
+
|
103
|
+
print(area_forecast)
|
104
|
+
|
105
|
+
energy_need_kwh_m2 = extract_energy_need(years, dm)
|
106
|
+
print(energy_need_kwh_m2)
|
107
|
+
|
108
|
+
heating_systems_projection = extract_heating_systems_forecast(years, dm)
|
109
|
+
print(heating_systems_projection)
|
110
|
+
|
111
|
+
|
112
|
+
|
113
|
+
|
114
|
+
if __name__ == '__main__':
|
115
|
+
main()
|