Input Assumptions for Modelling the Building Sector in the negaWatt-BE Scenario
ENERGY DEMAND FOR THE BUILDING SECTOR IN BELGIUM
Developer: LATERRE Antoine, BERNAERTS Valentine, QUOILIN Sylvain, MEYER Sébastien
To ensure transparency and consistency within the negaWatt-BE project, this notebook documents all key input assumptions related to the building sector. It computes the energy demand of the residential and tertiary sectors under sufficiency and efficiency measures over the energy transition period (2020–2050). Projections are based on statistical data from the starting reference year, and the resulting energy demand values are directly used as inputs for the PyPSA model.
Note: The reference year is 2019 instead of 2020, due to the disruptions caused by the COVID-19 pandemic in 2020. This choice ensures greater consistency and realism in the baseline and future projections.
- Introduction
- 1.1. Sufficiency vs Efficiency
- 1.2. Global Demands
- 1.2.1. Residential Sector
- 1.2.2. Tertiary Sector
- 1.2.3. Data Processing
- Residential Sector
- 2.1. Thermal Uses
- 2.2. Specific Electrical Uses
- 2.3. Global Data Processing
- Tertiary Sector
- 3.1. Thermal Uses
- 3.2. Specific Electrical Uses
- 3.3. Global Data Processing
- Total End-Use Demand
- Short List of Sufficiency Assumptions
- [1] Rozsai, Mate; Jaxa-Rozen, Marc; Salvucci, Raffaele; Sikora, Przemyslaw; Gea Bermudez, Juan; Neuwahl, Frederik (2025). JRC-IDEES-2023. European Commission, Joint Research Centre (JRC). [Dataset].
PID: http://data.europa.eu/89h/1f0b480c-6d21-4d95-897d-20c7ca33df6f - [2] Rozsai, Mate; Jaxa-Rozen, Marc; Salvucci, Raffaele; Sikora, Przemyslaw; Tattini, Jacopo; Neuwahl, Frederik (2024). JRC-IDEES-2021: the Integrated Database of the European Energy System – Data update and technical documentation, Publications Office of the European Union, Luxembourg, 2024. [Report].
- [3] De Grave, Denis; Van Moeseke, Geoffrey (2024). Slowheat : chauffer les corps, moins les logements - Une recherche collective sur la sobriété de nos pratiques de chauffage. Presses universitaires de Louvain, Louvain-la-Neuve, 2024. [Book].
ISBN: 978-2-39061-495-1, Available at: https://pul.uclouvain.be/book/?GCOI=29303100724760 - [4] Lund, H.; Østergaard, P. A.; Sorknæs, P.; Nielsen, S.; Skov, I. R.; Yuan, M.; Thellufsen, J. Z.; Mathiesen, B. V.; Benson, S. M.; Jentsch, A.; Zhang, X.; Werner, S.; Wiltshire, R.; Möller, B.; & Duic, N. (2025). District heating in clean energy systems. Nature Reviews Clean Technology, 1, 532–546. [Paper].
DOI: https://doi.org/10.1038/s44359-025-00076-8 - [5] EnergyVille (2025). Perspective2050: Residential & commercia- sector — Main Edition 2025 results [Web Page].
Available at: https://perspective2050.energyville.be/results/main-edition-2025/residential-commercial-sector - [6] European Commission, DG Energy (2024). Ecodesign Impact Accounting (EIA) - Overview report 2024 [Report].
- [7] European Commission, DG Energy. Energy renovation of buildings — Renovation Wave and revised EPBD [Web Page].
Available at: https://energy.ec.europa.eu/topics/energy-efficiency/energy-performance-buildings/energy-performance-buildings-directive/energy-renovation-buildings_en Retrieved from: https://circabc.europa.eu/ui/group/418195ae-4919-45fa-a959-3b695c9aab28/library/e2a752ef-c365-41df-8e50-98376e6ca756/details - [8] European Commission (2019). Commission Recommendation (EU) 2019/786 of 8 May 2019 on building renovation, OJ L 127, 16.5.2019, pp. 34-79. Annex, point 2.3.1.3 defines a deep renovation as one delivering "typically more than 60%" efficiency improvement, in delivered and final energy. [Legal act].
Available at: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX%3A32019H0786
# Automatically reload the file if it is modified:
%load_ext autoreload
%autoreload 2
# Load the macro parameters and necessary packages:
%run ./nW_BE_demand_model_macro.ipynb
# Option to print and plot the results:
post_process = True
The autoreload extension is already loaded. To reload it, use: %reload_ext autoreload
The method used to generate the energy demand projections for the building sector follows the approach of the Joint Research Centre (JRC) of the European Commission for building the Integrated Database of the European Energy System (IDEES) [1,2]. This database compiles statistics related to the residential and tertiary sectors.
1.1. Sufficiency vs Efficiency
To reduce primary energy consumption in the building sector, the negaWatt-BE approach relies on two complementary strategies: energy sufficiency, acting on usefull energy demand, and energy efficiency, acting on conversion losses. These strategies are implemented through several levers, presented below, which guide the transition towards a low-energy building sector.
- Energy Sufficiency refers to a deliberate, non-imposed reduction in end-use energy demand (energy services). This is achieved through behavioural and systemic changes, including an increased adoption of SlowHeat practices [3].
- Energy Efficiency refers to technical improvements that reduce the amount of primary energy consumed per building (like building insulation), without requiring behavioural or 'comfort' changes.
This section defines the demand in terms of available building area. For the residential sector, this corresponds to the equivalent residential surface available per household. For the tertiary sector, it corresponds to the equivalent tertiary surface available per person.
According to JRC-IDEES [1,2], the total residential surface area was 625,236 Mm² in 2019. Using the population size and average household size from Stabel (see nW_BE_demand_model_macro.ipynb), we get a specific residential surface area of 54,7 m²/person, and an average residential household surface area of 126,4 m²/household.
# Inputs - Define Sufficiency Scenario Data (SUF)
ref_RS_sur_tot = 625235.722685535*1e+3
ref_RS_sur_spe = ref_RS_sur_tot/population_dict[2019] # [m²/person]
ref_RS_sur_hld = ref_RS_sur_tot/households_dict[2019] # [m²/household]
Comment: Households do actually not represent proper dwellings. We follow the same approach as JRC-IDEES [2]: we assign them implicitly to households without distinguishing secondary residences or vacant dwellings.
Sufficiency Assumption
As a global sufficiency assumption, we want to reduce the specific residential surface area in order to reduce the material footprint and the footprint related to heating and cooling. We do not play on the average household size, as this is a demographic projection from Statbel (see nW_BE_demand_model_macro.ipynb). Nevertheless, projections show that the average household size is expected to decrease by -5,5% from 2019 to 2050. As a result, even without changing the available floor area per inhabitant, the average floor area per household will have to decrease.
In light of this, we project a -10% reduction in residential surface area available per person. This therefore corresponds to a reduction of -14,9% in the average floor area per household.
# Inputs - Define Sufficiency Scenario Data (SUF)
pro_RS_sur_spe = -0.10
# Outputs - Sufficiency Scenario Data (SUF)
SUF_data = {"RS specific surface [m²/person]": linear_growth(2019, ref_RS_sur_spe,
2050, ref_RS_sur_spe*(1+pro_RS_sur_spe), years)}
df_SUF = pd.DataFrame(SUF_data, index=years)
df_SUF["population [person]"] = df_SUF.index.map(population_dict)
df_SUF["households [household]"] = df_SUF.index.map(households_dict)
df_SUF["households [person]"] = df_SUF["population [person]"] /df_SUF["households [household]"]
df_SUF["RS total surface [Mm²]"] = df_SUF["RS specific surface [m²/person]"]*df_SUF["population [person]"]*1e-6
df_SUF["RS household surface [m²/household]"] = df_SUF["RS total surface [Mm²]"]*1e+6 /df_SUF["households [household]"]
Comment: Provide some examples motivating this reduction: expansion of shared-living arrangements, relocation to smaller dwellings once children have left the family home, development of light housing (such as tiny houses), etc.
According to JRC-IDEES [1,2], the total tertiary surface area was 227,073 Mm² in 2019. Using the population size Stabel (see nW_BE_demand_model_macro.ipynb), we get a specific tertiary surface area of 19,9 m²/person.
# Inputs - Define Sufficiency Scenario Data (SUF)
ref_TS_sur_tot = 227073.149760375*1e+3
ref_TS_sur_spe = ref_TS_sur_tot/population_dict[2019] # [m²/person]
Sufficiency Assumption
As a global sufficiency assumption, we want to reduce the specific tertiary surface area in order to reduce the material footprint and the footprint related to heating and cooling. In light of this, we project a -10% reduction in tertiary surface area available per person.
# Inputs - Define Sufficiency Scenario Data (SUF)
pro_TS_sur_spe = -0.10
# Outputs - Sufficiency Scenario Data (SUF)
df_SUF["TS specific surface [m²/person]"] = linear_growth(2019, ref_TS_sur_spe,
2050, ref_TS_sur_spe*(1+pro_TS_sur_spe), years)
df_SUF["TS total surface [Mm²]"] = df_SUF["TS specific surface [m²/person]"]*df_SUF["population [person]"]*1e-6
# Processing - Sectors
df_BD_macro = {
'Demographic': df_SUF[["population [person]","households [household]","households [person]"]].T,
'Residential': df_SUF[["RS total surface [Mm²]","RS household surface [m²/household]","RS specific surface [m²/person]"]].T,
'Tertiary': df_SUF[["TS total surface [Mm²]","TS specific surface [m²/person]"]].T,
}
rows = []
for sector, df in df_BD_macro.items():
temp = df.copy()
temp['Sector'] = sector
temp['Parameter'] = temp.index
temp = temp.reset_index(drop=True)
rows.append(temp)
df_BD_macro = pd.concat(rows, ignore_index=True)
if post_process:
# === Full table ===
df_fec = df_BD_macro
df_fec['Sector'] = df_fec['Sector'].mask(df_fec['Sector'].duplicated(), '')
df_fec_r = df_fec[['Sector', 'Parameter'] + list(years)]
styled = (
df_fec_r.style
.hide(axis='index')
.set_table_attributes('style="width:100%;table-layout:fixed;"')
.apply(highlight_mode_separator, axis=1)
.apply(lambda row: [bold_mode(cell, row['Sector'], col) for col, cell in zip(df_fec_r.columns, row)], axis=1)
.set_properties(subset=['Parameter'], **{'font-style':'italic'})
.format({year: "{:.3f}" for year in years})
)
display(styled)
# === Bar chart ===
plot_data = df_BD_macro.set_index('Parameter')[years].T
plot_data = plot_data[['RS specific surface [m²/person]', 'TS specific surface [m²/person]']]
ax = plot_data.plot(
kind='barh',
figsize=(14, 5),
width=0.8,
edgecolor='white'
)
ax.set_title('Projections of surface area per sector', fontsize=11, pad=15)
ax.set_xlabel('Specific surface area [m²/person]', fontsize=10, ha='right', va='top')
ax.set_ylabel('Years', fontsize=10, rotation=0, ha='left', va='bottom')
ax.yaxis.set_inverted(True) # inverted axis with autoscaling
ax.xaxis.set_label_coords(1, -0.1)
ax.yaxis.set_label_coords(-0.05, 1)
ax.yaxis.set_inverted(True) # inverted axis with autoscaling
ax.legend(bbox_to_anchor=(1, 1), loc='upper left', title="Sector", frameon=False)
ax.grid(axis='x', linestyle='--', alpha=0.6)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)
plt.tight_layout()
plt.show()
| Sector | Parameter | 2019 | 2025 | 2030 | 2035 | 2040 | 2045 | 2050 |
|---|---|---|---|---|---|---|---|---|
| Demographic | population [person] | 11431406.000 | 11816102.000 | 12023862.000 | 12186730.000 | 12347171.000 | 12490658.000 | 12600911.000 |
| households [household] | 4948398.000 | 5199667.000 | 5355123.000 | 5489927.000 | 5609698.000 | 5702818.000 | 5770867.000 | |
| households [person] | 2.310 | 2.272 | 2.245 | 2.220 | 2.201 | 2.190 | 2.184 | |
| Residential | RS total surface [Mm²] | 625.241 | 633.768 | 634.307 | 632.150 | 629.570 | 625.869 | 620.280 |
| RS household surface [m²/household] | 126.352 | 121.886 | 118.449 | 115.147 | 112.229 | 109.747 | 107.485 | |
| RS specific surface [m²/person] | 54.695 | 53.636 | 52.754 | 51.872 | 50.989 | 50.107 | 49.225 | |
| Tertiary | TS total surface [Mm²] | 227.073 | 230.178 | 230.365 | 229.586 | 228.645 | 227.305 | 225.279 |
| TS specific surface [m²/person] | 19.864 | 19.480 | 19.159 | 18.839 | 18.518 | 18.198 | 17.878 |
The distribution of the different energy services for thermal uses for the reference year in the residential sector is from JRC-IDEES [1,2]. The values are reported to different parameters, as different scaling and projection rules will be applied:
- Space heating: reported to average household surface area [kWh/m²], as proportional to this quantity (regardless of the number of inhabitants).
- Space cooling: reported to average household surface area [kWh/m²], as proportional to this quantity (regardless of the number of inhabitants).
- Sanitary hot water: reported to population [kWh/person], as proportional to this quantity (showering is the main driver).
- Cooking: reported to number households [kWh/household], as proportional to this quantity (cooking for the whole household).
# Inputs - Repartition of the thermal energy services (TES) in residential buildings for the reference year (2019)
ref_RS_tes_sht = 3664.049*ktoe_to_GWh*1e+6/ref_RS_sur_tot # [kWh/m²]
ref_RS_tes_scl = 23.455*ktoe_to_GWh*1e+6/ref_RS_sur_tot # [kWh/m²]
ref_RS_tes_shw = 663.435*ktoe_to_GWh*1e+6/df_SUF["population [person]"][2019] # [kWh/person]
ref_RS_tes_cok = 96.395*ktoe_to_GWh*1e+6/df_SUF["households [household]"][2019] # [kWh/household]
if post_process:
print(f'Energy service for space heating: {round(ref_RS_tes_sht,3)} kWh/m²')
print(f'Energy service for space cooling: {round(ref_RS_tes_scl,3)} kWh/m²')
print(f'Energy service for hot water: {round(ref_RS_tes_shw,3)} kWh/person')
print(f'Energy service for cooking: {round(ref_RS_tes_cok,3)} kWh/household')
Energy service for space heating: 68.155 kWh/m² Energy service for space cooling: 0.436 kWh/m² Energy service for hot water: 674.961 kWh/person Energy service for cooking: 226.553 kWh/household
2.1.1. Building Energy Performance
The energy performance of a building, generally expressed in kWh/m², is a key factor in reducing its total end-use energy demand. While many energy models distinguish this performance according to three categories within the building stock (existing, renovated, and new-built), thus allowing the direct modelling of the effect of the renovation rate of the stock, we favor a simplified approach that evaluates only the average performance of the overall building stock.
According to figures from JRC-IDEES [1,2], the average annual renewal rate of floor area (renovation + new construction) was around 3% between 2000 and 2023, with the associated reduction in end-use demand averaging -0,458 kWh/m²/year. We mainly attribute this decrease to efficiency measures (insulation), although some episodic reductions may also have been linked to sufficiency measures (reduced heating use during energy crises, etc.).
Caution on this 3%. It decomposes into about 2,3 points of renovation and about 0,7 points of net new construction. The renovation component is not an observation: it is a constant of the JRC-IDEES building-stock model (2,261%/year, with a standard deviation of 0,008 points over 2001-2023), so the whole year-to-year variation of the series comes from new construction. This renewal rate is also not comparable with the energy-renovation rate used in European policy, which counts energy renovations only and stands at around 1%/year in the EU, a rate the Commission aims to double [7]. The quantity we calibrate on below is therefore the -0,458 kWh/m²/year improvement in end-use demand, not the renewal rate.
Efficiency Assumption
We believe that the average annual improvement rate over the 2000-2023 period is too low to meet climate objectives. We therefore propose to double that improvement rate — the -0,458 kWh/m²/year reduction in end-use demand, not the renewal rate — in order to accelerate the energy transition. With this improvement rate of -0,916 kWh/m²/year, the efficiency-related building performance improves from 68,2 kWh/m² in 2019 to 39,8 kWh/m² in 2050.
The same assumption, read as renovation activity
A stock average in kWh/m² says nothing about how many dwellings are touched or how deeply, which is the form the question takes in every renovation policy. The two readings are the same arithmetic. If a constant share $r$ of the stock is renovated each year and each renovation cuts that dwelling's heating need by a fraction $d$, the stock average falls linearly — the very shape assumed above:
$$I(t) = I(0)\,\bigl[1 - d\,r\,t\bigr], \qquad r\,t \le 1$$so the product $d \times r$ is fixed by the trajectory and only one of the two is a free choice. We set the depth at 60%, the threshold the European Commission uses to call a renovation deep (Recommendation (EU) 2019/786 [8]): the scenario then reads as every energy renovation from now on is a deep one. The rate follows — 2,24% of the dwelling stock per year, that is 69% of it renovated at least once by 2050.
Applied to the observed period, the same reading sizes that choice. JRC-IDEES books 2,261%/year of renovation; attributing the whole -0,458 kWh/m²/year to it — an upper bound, since new construction lowers the average too — the average Belgian renovation has been cutting the heating need by about a quarter, a medium renovation in the Commission's classification (30-60%), not a deep one. In renovation terms the scenario is therefore the same number of renovations, each about 2,4 times deeper, not more renovations.
This is a reading of the assumption above, not a new degree of freedom: trg_RS_tes_sht and everything downstream of it are numerically unchanged. It exists so the assumption can be discussed in the units the sector actually uses, and it is the form the workshop module puts to participants.
acc_RS_tes_sht_ren = 2
cur_RS_tes_sht_ren = -0.458 # kWh/m²/year
trg_RS_tes_sht = ref_RS_tes_sht + acc_RS_tes_sht_ren*cur_RS_tes_sht_ren*(2050-2019) # [kWh/m²]
# --- The same assumption, read as renovation activity -------------------------
# The line above moves the stock average without saying how many dwellings are
# touched or how deeply. Renovation is what delivers it, so the link is written
# down here. If a constant share `rat` of the stock is renovated every year and
# each renovation cuts that dwelling's heating need by a fraction `dep`, the
# stock average falls *linearly*, which is the shape assumed above:
# I(t) = I(0) * [1 - dep*rat*t] for rat*t <= 1
# hence dep*rat = -acc*cur/ref, and fixing either one fixes the other. ONE new
# assumption enters -- the depth -- and the rate follows from the trajectory, so
# trg_RS_tes_sht and everything downstream of it are numerically unchanged.
#
# The depth is set at the threshold the European Commission uses to call a
# renovation "deep" (Recommendation (EU) 2019/786): the scenario then reads as
# "every energy renovation from now on is a deep one".
dep_RS_tes_sht_ren = 0.60 # [-] cut in heating need per renovation
shr_RS_tes_sht_ren = (1 - trg_RS_tes_sht/ref_RS_tes_sht)/dep_RS_tes_sht_ren # [-] share of the stock renovated by 2050
rat_RS_tes_sht_ren = shr_RS_tes_sht_ren/(2050-2019) # [1/year] renovation rate it implies
assert 0 < shr_RS_tes_sht_ren <= 1, (
f"a renovation depth of {dep_RS_tes_sht_ren:.0%} is too shallow for this "
f"trajectory: it would need {shr_RS_tes_sht_ren:.0%} of the stock renovated "
f"by 2050. Deepen the renovations, or slow the intensity target.")
# The same reading applied to the observed period, which is what sizes the choice
# of depth above. The JRC-IDEES floor-area series decomposes into a renovation
# component and net new construction (nW_BE_demand_data_aux.ipynb, cell 17); the
# observed improvement is the -0.458 kWh/m²/year fitted above, on a trend that
# starts at 79.5 kWh/m² in 2000. Attributing all of it to renovation is an upper
# bound -- new construction lowers the stock average too -- so the depth below is
# a ceiling on what the average Belgian renovation has been achieving.
obs_RS_tes_sht_2000 = 79.48 # [kWh/m²] fitted 2000 value of the -0.458 trend
obs_RS_sht_rat_ren = 2.261 # [%/year] renovated floor area (a JRC-IDEES constant, sd 0.008)
obs_RS_sht_rat_new = 0.746 # [%/year] net new construction (observed, sd 0.314)
obs_RS_sht_rat_all = 3.007 # [%/year] the two together: the "3%" of the prose above
obs_RS_tes_sht_dep = -cur_RS_tes_sht_ren/(obs_RS_sht_rat_ren/100*obs_RS_tes_sht_2000) # [-]
assert abs(obs_RS_sht_rat_ren + obs_RS_sht_rat_new - obs_RS_sht_rat_all) < 0.01
if post_process:
print( 'Renovation reading of the efficiency assumption:')
print(f' -> depth assumed for each renovation: {dep_RS_tes_sht_ren:>6.0%} of the heating need')
print(f' -> stock renovated at least once: {shr_RS_tes_sht_ren:>6.1%} by 2050')
print(f' -> renovation rate implied: {rat_RS_tes_sht_ren*100:>6.2f} %/year')
print(f' -> observed 2000-2023, for comparison: {obs_RS_tes_sht_dep:>6.1%} deep at {obs_RS_sht_rat_ren:.3f} %/year')
Next to efficiency measures, the end-use energy demand can also be reduced through behavioural changes. These are introduced below.
Space heating
The adoption of SlowHeat [3], which consists of voluntarily lowering heating temperature setpoints in favor of localised heating close to the body (appropriate clothing, electric blankets, heated garments, etc.), makes it possible to significantly reduce heat demand. A study conducted in Belgium indicates that an average indoor temperature of 15°C can be entirely acceptable and desirable, while the vigilance threshold appears to be around 12°C [3].
This practice nevertheless faces significant social and psychological resistance (in 2022, 19°C often constituted a lower bound), which makes its widespread adoption rather unrealistic.
Sufficiency Assumption
We estimate that by 2050, the evolution of social norms will allow average heating setpoints to be reduced by -2°C. Given that models indicate a -7% reduction in consumption for each -1°C decrease [3], we estimate an additional -14% reduction on top of the efficiency measures described above. This results in a heat demand of 34,2 kWh/m² in 2050.
d_cons_temp = 0.07
d_temp = 2
suf_RS_tes_sht = 1-d_temp*d_cons_temp
if post_process:
print(f'Evolution of space heating energy demand: {round(-(1-(trg_RS_tes_sht*suf_RS_tes_sht)/(ref_RS_tes_sht))*100,1)}% [kWh/m²]')
print(f' -> By 2019, the annual heating energy demand is {round(ref_RS_tes_sht, 2)} kWh/m²')
print(f' -> By 2050, the annual heating energy demand is {round(trg_RS_tes_sht*suf_RS_tes_sht,2)} kWh/m²')
Evolution of space heating energy demand: -49.8% [kWh/m²] -> By 2019, the annual heating energy demand is 68.15 kWh/m² -> By 2050, the annual heating energy demand is 34.19 kWh/m²
Comment: Should maybe add a bit of electricity to other electric appliances for SlowHeat devices.
Space cooling
Cooling demand increased by a factor of x22,5 between 2000 and 2023, rising from 0,035 kWh/m² to 1,010 kWh/m². The most striking expansion occurred between 2019 and 2023, during which demand increased by +0,572 kWh/m² in just four years.
Sufficiency Assumption
Due to climate change, we acknowledge that the deployment of air conditioning is becoming a necessity to ensure a decent quality of life across a range of applications. However, we believe that its deployment can be controlled and restricted to cases where it is genuinely necessary. The current trend of expanding the use of air conditioning in order to increase self-consumption in buildings equipped with photovoltaic panels does not, in our view, support the energy transition, as this solar potential (already limited in Belgium) could be used for other purposes (such as the production and seasonal storage of heat, etc.).
As a sufficiency assumption, we project that the rate of deployment of residential air conditioning over the period 2019-2050 will be the same as over the historical period (+0,035 kWh/m²/year).
acc_RS_tes_scl_ren = 1
cur_RS_tes_scl_ren = 0.035 # kWh/m²/year
trg_RS_tes_scl = ref_RS_tes_scl + acc_RS_tes_scl_ren*cur_RS_tes_scl_ren*(2050-2019) # [kWh/m²]
if post_process:
print(f'Evolution of space cooling energy demand: +{round(-(1-(trg_RS_tes_scl)/(ref_RS_tes_scl))*100,1)}% [kWh/m²]')
print(f' -> By 2019, the annual cooling energy demand is {round(ref_RS_tes_scl,2)} kWh/m²')
print(f' -> By 2050, the annual cooling energy demand is {round(trg_RS_tes_scl,2)} kWh/m²')
Evolution of space cooling energy demand: +248.7% [kWh/m²] -> By 2019, the annual cooling energy demand is 0.44 kWh/m² -> By 2050, the annual cooling energy demand is 1.52 kWh/m²
Sanitary hot water
By 2019, the specific annual energy consumption for hot water is of about 675 kWh/person. This is equivalent to about 64 l of 40°C water per day and per person.
Caution on this 2019 anchor. JRC-IDEES useful-energy DHW falls from 663,4 ktoe in 2019 to 437,7 ktoe in 2023. Eurostat
nrg_d_hhqdelivered energy for water heating in Belgian households is flat over the same years (43 175 TJ in 2019, 43 510 TJ in 2023, 43 268 TJ in 2024; dataset updated 2026-06-09). The implied conversion efficiency would have to collapse from 64 % to 42 %, which is not a physical change. We therefore keep the 2019 JRC value as the reference — Eurostat shows that year is typical, not a peak — and the workshop observed curve after 2019 is reconstructed from Eurostat delivered energy at the 2019 useful/final ratio. The 2050 target below is built from a shower recipe, not from this series.
Sufficiency Assumption
We project to limit the hot water consumption for showering to sufficient levels. We therefore assume one shower per day and per person, 5 min long shower (sinking water) with 38°C water at 7 l/min. We also consider a daily use of 10 l of 60°C hot water for other domestic purposes (doing the dish, etc.).
All in all, the specific energy consumption by 2050 is reduced by -21% compared with 2019. This is equivalent to 50 l of 40°C per day and per person.
# Inputs - Define Sufficiency Scenario Data (SUF)
shower_duration = 5 # [min]
shower_flow_rate = 7 # [l/min]
shower_temperature = 38 # [°C]
shower_energy = shower_duration*shower_flow_rate*rho_h2o*cp_h2o*(shower_temperature-15) # [kWh/shower]
others_volume = 10 # [l]
others_temperature = 60 # [°C]
others_energy = others_volume*rho_h2o*cp_h2o*(others_temperature-15) # [kWh]
# Outputs - Sufficiency Scenario Data (SUF)
trg_RS_tes_shw = (shower_energy+others_energy)*365
pro_RS_tes_shw = (trg_RS_tes_shw-ref_RS_tes_shw)/ref_RS_tes_shw
if post_process:
print(f'Evolution of sanitary hot water energy demand: {round(pro_RS_tes_shw*100,1)}% [kWh/person]')
amount_2019 = ref_RS_tes_shw/(rho_h2o*cp_h2o*(40-15))/365 # equivalent l/day/person of 40°C water
amount_2050 = trg_RS_tes_shw/(rho_h2o*cp_h2o*(40-15))/365 # equivalent l/day/person of 40°C water
print(f' -> By 2019, the equivalent energy demand is {round(amount_2019,1)} liters of 40°C water per day and per person')
print(f' -> By 2050, the equivalent energy demand is {round(amount_2050,1)} liters of 40°C water per day and per person')
Evolution of sanitary hot water energy demand: -21.2% [kWh/person] -> By 2019, the equivalent energy demand is 63.7 liters of 40°C water per day and per person -> By 2050, the equivalent energy demand is 50.2 liters of 40°C water per day and per person
Cooking
The historical average trend shows a slight decrease in energy consumption for cooking (-0,94 kWh per household per year between 2000 and 2019). This could be explained by changes in cooking habits and the increased purchase of processed foods.
Why the trend stops at 2019. Fitted over 2000-2023 the same series gives -1,6 kWh/household/year, but two thirds of that slope comes from a single step in JRC-IDEES: useful cooking energy holds at 102,0 ktoe in 2021 and drops to 77,1 in 2022, a quarter of the total in one year. Eurostat
nrg_d_hhqdelivered energy for cooking in Belgian households does not show it — 5 507 TJ in 2021, 5 517 in 2022 — and only falls in 2023-2024 (5 067, then 4 590), which is when the switch away from gas shows up in delivered energy rather than in useful heat. The 2019 reference (ref_RS_tes_cok, 96,395 ktoe) and the +15% assumption below are unaffected: neither reads the post-2019 part of the series. Same JRC sheet, sanitary hot water, has the same kind of break after 2019 (see §2.1.2 above).
Sufficiency Assumption
In our scenario, we advocate more home cooking using less processed products. We therefore anticipate a +15% increase in energy consumption related to cooking per household.
Efficiency Assumption
In addition, in order to limit greenhouse gas emissions, we prioritise the use of electric cookers and ovens. Nevertheless, we retain a 2% share of gas cooking by 2050.
# Inputs - Define Sufficiency Scenario Data (SUF)
pro_RS_tes_cok = 0.15
ref_RS_tes_cok_gas = (3.846+21.957)/96.395 # ktoe_gas/ktoe_tot (from JRC-IDEES)
trg_RS_tes_cok_gas = 0.02
# Outputs - Sufficiency Scenario Data (SUF)
trg_RS_tes_cok = (1+pro_RS_tes_cok)*ref_RS_tes_cok # kWh/person
share_cook_gas = linear_growth(2019, ref_RS_tes_cok_gas,
2050, trg_RS_tes_cok_gas,years) # [%]
share_cook_ele = linear_growth(2019,1-ref_RS_tes_cok_gas,
2050,1-trg_RS_tes_cok_gas,years) # [%]
print(f'Evolution of cooking energy demand: {round(pro_RS_tes_cok*100,1)}% [kWh/household]')
print(f' -> By 2019, the equivalent energy demand is {round(ref_RS_tes_cok,2)} kWh per household')
print(f' -> By 2050, the equivalent energy demand is {round(trg_RS_tes_cok,2)} kWh per household')
Evolution of cooking energy demand: 15.0% [kWh/household] -> By 2019, the equivalent energy demand is 226.55 kWh per household -> By 2050, the equivalent energy demand is 260.54 kWh per household
District heating networks are a key technological option for the energy transition. They enable effective decarbonisation of the heating sector by providing a large-scale source of sector coupling (use of industrial heat pumps with decarbonised sources such as geothermal energy, waste heat, biomass cogeneration, etc.) and a significant source of flexibility (buffer and seasonal thermal storage).
Nevertheless, the penetration of district heating networks is currently anecdotal in Belgium: 0.25% of residential space heating and hot water in 2019 (JRC-IDEES useful energy). Eurostat delivered energy on the same heat basis (derived heat over space heating plus water heating) is 0.21% that year, and 0.18% in 2024. The 45% techno-economic potential quoted by Lund et al. [4] is the Heat Roadmap Europe average. Heat Roadmap Belgium (HRE4) retains 37% of built-environment heat excluding industry by 2050, with an economic range of 20–54%. It is unlikely that even that Belgian potential will be fully exploited. For example, the PATHS2050 scenario from EnergyVille assumes a 13% coverage in the building sector (residential and tertiary) [5].
Efficiency Assumption
In our scenario, we assume a significant deployment of district heating networks, covering 15% of end-use heat demand in buildings by 2050.
Also, we exclude district cooling networks from our model.
ref_RS_tes_dhn = 10.823/(3664.049+663.435) # [%] 2019 reference share of district heating network in Belgium from JRC-IDEES
trg_RS_tes_dhn = 0.15 # [%] 2050 target share of district heating network in Belgium from nW-BE
share_heat_dhn = linear_growth(2019, ref_RS_tes_dhn,
2050, trg_RS_tes_dhn,years) # [%]
share_heat_ihs = linear_growth(2019,1-ref_RS_tes_dhn,
2050,1-trg_RS_tes_dhn,years) # [%]
# Inputs - Repartition of the electrical energy services (EES) in residential buildings for the reference year (2019)
ref_RS_ees_frg = 209.814*ktoe_to_GWh*1e+6/df_SUF["households [household]"][2019] # [kWh/household]
ref_RS_ees_wsh = 64.045*ktoe_to_GWh*1e+6/df_SUF["households [household]"][2019] # [kWh/household]
ref_RS_ees_dry = 73.446*ktoe_to_GWh*1e+6/df_SUF["households [household]"][2019] # [kWh/household]
ref_RS_ees_dsh = 59.199*ktoe_to_GWh*1e+6/df_SUF["households [household]"][2019] # [kWh/household]
ref_RS_ees_tvm = 257.507*ktoe_to_GWh*1e+6/df_SUF["households [household]"][2019] # [kWh/household]
ref_RS_ees_ict = 96.860*ktoe_to_GWh*1e+6/df_SUF["households [household]"][2019] # [kWh/household]
ref_RS_ees_lgt = 139.889*ktoe_to_GWh*1e+6/df_SUF["households [household]"][2019] # [kWh/household]
ref_RS_ees_oth = 97.004*ktoe_to_GWh*1e+6/df_SUF["households [household]"][2019] # [kWh/household]
if post_process:
print(f'Energy service for refrigeration: {round(ref_RS_ees_frg,3)} kWh/household')
print(f'Energy service for washing machines: {round(ref_RS_ees_wsh,3)} kWh/household')
print(f'Energy service for clothes dryer: {round(ref_RS_ees_dry,3)} kWh/household')
print(f'Energy service for dish washer: {round(ref_RS_ees_dsh,3)} kWh/household')
print(f'Energy service for TV and multimedia:{round(ref_RS_ees_tvm,3)} kWh/household')
print(f'Energy service for ICT: {round(ref_RS_ees_ict,3)} kWh/household')
print(f'Energy service for lights: {round(ref_RS_ees_lgt,3)} kWh/household')
print(f'Energy service for others: {round(ref_RS_ees_oth,3)} kWh/household')
Energy service for refrigeration: 493.117 kWh/household Energy service for washing machines: 150.522 kWh/household Energy service for clothes dryer: 172.617 kWh/household Energy service for dish washer: 139.133 kWh/household Energy service for TV and multimedia:605.207 kWh/household Energy service for ICT: 227.646 kWh/household Energy service for lights: 328.775 kWh/household Energy service for others: 227.984 kWh/household
Refrigeration
Following projections from the European Commission Ecodesign Impact Accounting 2024 report [6], the specific annual consumption of new housholds refrigeration units should be below 100 kWh/year by 2035. Historical data show that the average consumption associated with refrigeration has decreased almost linearly by -17,0 kWh/household/year from 2000 to 2023 [1,2].
Efficiency Assumption
We project an average value of 150 kWh/household for the entire fleet starting from 2040.
trg_RS_ees_frg = 150 # [kWh/household]
yea_RS_ees_frg = 2040
#2019+(ref_RS_ees_frg-trg_RS_ees_frg)/(16.967)
Comment: For complementary info regarding the target, see this EU labels (https://energy-efficient-products.ec.europa.eu/product-list/fridges-and-freezers_en)
Washing machines
Following projections from the European Commission Ecodesign Impact Accounting 2024 report [6], the specific annual consumption of new housholds washing machines should be below 100 kWh/year by 2025. Historical data show that the average consumption associated with washing machines has decreased almost linearly by -6,3 kWh/household/year from 2000 to 2023 [1,2], mainly driven by a reduction in water consumption per cycle and water temperature (90 l at 56°C in 1990 against 36 l at 40°C in 2020) [6].
Combined Efficiency and Sufficiency Assumption
According to the European Commission Ecodesign Impact Accounting 2024 report [6], the average performance of newsold washing machines should be around 96 kWh/year by 2030. In our scenario, we project widespread use of eco programmes (30°C and less water). We therefore estimate that average annual consumption will reach 90 kWh/household by 2030.
trg_RS_ees_wsh = 90 # [kWh/household]
yea_RS_ees_wsh = 2030
#2019+(ref_RS_ees_wsh-trg_RS_ees_wsh)/(6.277)
Comment: For complementary info regarding the target, see this EU labels (https://energy-efficient-products.ec.europa.eu/product-list/washing-machines_en)
Laundry dryers
Following projections from the European Commission Ecodesign Impact Accounting 2024 report [6], the specific annual consumption of new housholds laundry dryers should be around 100 kWh/year by 2030. Historical data show that the average consumption associated with laundry dryers has decreased by -4,2 kWh/household/year from 2000 to 2023 [1,2], mainly driven by (i) the deployement of condensing electrical units and (ii) condensing heat pumps units (from approx. 2010) [6].
Efficiency Assumption
We project that the fleet will gradually shift from vented and condensing electric to condensing heat pumps (most efficient).
Sufficiency Assumption
As these are energy intensive units, our scenario also projects to limit their usage and priviliege natural passive drying whenever possible. We expect to reach an average annual consumption of 80 kWh/household by 2040.
trg_RS_ees_dry = 80 # [kWh/household]
yea_RS_ees_dry = 2040
# 2019+(ref_RS_ees_dry-trg_RS_ees_dry)/(4.17)
Comment: For complementary info regarding the target, see this EU labels (https://energy-efficient-products.ec.europa.eu/product-list/tumble-dryers_en)
Dishwashers
Following projections from the European Commission Ecodesign Impact Accounting 2024 report [6], the specific annual consumption of new housholds dishwashers should be below 160 kWh/year by 2030, mainly driven by a reduction in water consumption per cycle (30 l in 1990 against 9 l in 2030). However, historical data show that the average consumption associated with dishwashers has increased by +0,35 kWh/household/year from 2000 to 2023, as the technology continues its deployement (all households are not equiped yet) [1,2].
Sufficiency Assumption
We project that the technology will continue developping as it contributes to increasing the quality of life. We assume a constant (energy) deployement rate. By 2050, we hence project a 150 kWh/household consumption.
trg_RS_ees_dsh = 150 # [kWh/household]
yea_RS_ees_dsh = 2050
#2019+(ref_RS_ees_dsh-trg_RS_ees_dsh)/(-0.351)
Comment: For complementary info regarding the target, see this EU labels (https://energy-efficient-products.ec.europa.eu/product-list/dishwashers_en)
TV and multimedia
Consumption related to TV and multimedia equipment grew until around 2015 [1,2]. Since then, there has been a slow decline in this area of consumption, undoubtedly due to increased energy efficiency. For example, the specific consumption of new screens has fallen from around 7,5 W/dm² in 2000 to nearly 1 W/m² in 2020 [6].
As we do not have any usage data at our disposal, it is difficult to say whether the decline observed since 2015 is due to decoupling (increased usage offset by efficiency gains) or to a coupled effect (stabilisation or even a decrease in usage combined with efficiency gains). Nevertheless, the rate of efficiency gains appears to be much faster than the rate of decline in energy consumption, which seems to reinforce the decoupling hypothesis.
Combined Efficiency and Sufficiency Assumption
Projections from the European Commission Ecodesign Impact Accounting 2024 report [6] indicate that the specific consumption of new screens could be halved between 2020 and 2030. We anticipate a slowdown in the growth of demand for TV and multimedia. Combined with efficiency gains, we project that electricity demand for this category could be reduced by -30% by 2050.
trg_RS_ees_tvm = (1-0.3)*ref_RS_ees_tvm # [kWh/household]
yea_RS_ees_tvm = 2050
ICT equipment
ICT-related consumption grew until around 2021, but seems to have stabilised since then [1,2].
Combined Efficiency and Sufficiency Assumption
We project that the combined effect of slower demand growth and efficiency gains will stabilise consumption in this area.
trg_RS_ees_ict = ref_RS_ees_ict # [kWh/household]
yea_RS_ees_ict = 2019
Lighting
Following projections from the European Commission Ecodesign Impact Accounting 2024 report [6], the specific annual consumption of lighting systems should be around 130 kWh/household/year by 2030 in EU27. Historical data show that the average consumption associated with lighting has decreased by -12,9 kWh/household/year from 2000 to 2023 [1,2] despite the rebund effect (from 22 light sources per houshold in 2005 to 29 in 2015 [6]).
Efficiency Assumption
Thanks to the continuous technological improvement, we expect to reach a consumption of 130 kWh/household by 2035.
trg_RS_ees_lgt = 130 # [kWh/household]
yea_RS_ees_lgt = 2035
# 2019+(ref_RS_ees_lgt-trg_RS_ees_lgt)/(12.853)
Comment: For complementary info regarding the target, see this EU labels (https://energy-efficient-products.ec.europa.eu/product-list/light-sources_en)
Other appliances
The consumption associated to other appliances, accounting for vacuum cleaners, irons, etc., is expected to remain constant.
trg_RS_ees_oth = ref_RS_ees_oth # [kWh/household]
yea_RS_ees_oth = 2019
# Residential Sector - Thermal Energy Services
tes_RS_tot = {
'space heating': linear_growth(2019,ref_RS_tes_sht *df_SUF["RS total surface [Mm²]"][2019]*1e-3,
2050,trg_RS_tes_sht*suf_RS_tes_sht*df_SUF["RS total surface [Mm²]"][2050]*1e-3,years), # [TWh] kWh/m² * m²
'space cooling': linear_growth(2019,ref_RS_tes_scl *df_SUF["RS total surface [Mm²]"][2019]*1e-3,
2050,trg_RS_tes_scl *df_SUF["RS total surface [Mm²]"][2050]*1e-3,years), # [TWh] kWh/m² * m²
'sanitary hot water': linear_growth(2019,ref_RS_tes_shw *df_SUF["population [person]"] [2019]*1e-9,
2050,trg_RS_tes_shw *df_SUF["population [person]"] [2050]*1e-9,years), # [TWh] kWh/person * person
'cooking': linear_growth(2019,ref_RS_tes_cok *df_SUF["households [household]"][2019]*1e-9,
2050,trg_RS_tes_cok *df_SUF["households [household]"][2050]*1e-9,years), # [TWh] kWh/household * household
}
if post_process:
total_TWh = sum(val[0] for val in tes_RS_tot.values())
total_ktoe = total_TWh*1e+3/ktoe_to_GWh
print(f'-> Relative difference to JRC-IDEES for thermal services: {round(100*(4447.334-total_ktoe)/4447.334,3)}%')
# === PLOT ===
df_tes_RS_tot = pd.DataFrame(tes_RS_tot, index=years)
dfts = df_tes_RS_tot
fig1, ax = plt.subplots(figsize=(14, 5))
bottom = np.zeros(len(dfts))
colors = ['tab:red','tab:blue','tab:orange','tab:green']
# Thicker bars → control via height argument
bar_height = 2.0 # default is ~0.8; increase to 0.9–1.0 for thicker bars
for i, activity in enumerate(dfts.columns):
ax.barh(dfts.index, dfts[activity],
left=bottom, label=activity,
color=colors[i % len(colors)],
height=bar_height)
bottom += dfts[activity]
ax.set_xlabel("End-Use Demand [TWh]", fontsize=10, ha='right', va='top')
ax.xaxis.set_label_coords(1, -0.1)
ax.set_ylabel("Years", fontsize=10, rotation=0, ha='left', va='bottom')
ax.yaxis.set_label_coords(-0.05, 1)
ax.set_yticks(years)
ax.yaxis.set_inverted(True) # inverted axis with autoscaling
ax.set_title("Evolution of End-Use Demand for Thermal Services in the Residential Sector (negaWatt-BE Scenario)", fontsize=11, pad=15)
ax.legend(bbox_to_anchor=(1, 1), loc='upper left', title="Service", frameon=False)
ax.grid(axis='x', linestyle='--', alpha=0.6)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)
fig1.tight_layout()
plt.show()
-> Relative difference to JRC-IDEES for thermal services: -0.001%
# Residential Sector - Electrical Energy Services
ees_RS_tot = {
'refrigeration': linear_with_middle_point(2019,ref_RS_ees_frg*df_SUF["households [household]"][2019] *1e-9,
yea_RS_ees_frg,trg_RS_ees_frg*df_SUF["households [household]"][yea_RS_ees_frg]*1e-9,
2050,trg_RS_ees_frg*df_SUF["households [household]"][2050] *1e-9, years), # [TWh] kWh/household * household
'washing machines': linear_with_middle_point(2019,ref_RS_ees_wsh*df_SUF["households [household]"][2019] *1e-9,
yea_RS_ees_wsh,trg_RS_ees_wsh*df_SUF["households [household]"][yea_RS_ees_wsh]*1e-9,
2050,trg_RS_ees_wsh*df_SUF["households [household]"][2050] *1e-9, years), # [TWh] kWh/household * household
'laundry dryers': linear_with_middle_point(2019,ref_RS_ees_dry*df_SUF["households [household]"][2019] *1e-9,
yea_RS_ees_dry,trg_RS_ees_dry*df_SUF["households [household]"][yea_RS_ees_dry]*1e-9,
2050,trg_RS_ees_dry*df_SUF["households [household]"][2050] *1e-9, years), # [TWh] kWh/household * household
'dishwashers': linear_with_middle_point(2019,ref_RS_ees_dsh*df_SUF["households [household]"][2019] *1e-9,
yea_RS_ees_dsh,trg_RS_ees_dsh*df_SUF["households [household]"][yea_RS_ees_dsh]*1e-9,
2050,trg_RS_ees_dsh*df_SUF["households [household]"][2050] *1e-9, years), # [TWh] kWh/household * household
'TV and multimedia': linear_with_middle_point(2019,ref_RS_ees_tvm*df_SUF["households [household]"][2019] *1e-9,
yea_RS_ees_tvm,trg_RS_ees_tvm*df_SUF["households [household]"][yea_RS_ees_tvm]*1e-9,
2050,trg_RS_ees_tvm*df_SUF["households [household]"][2050] *1e-9, years), # [TWh] kWh/household * household
'ICT': linear_with_middle_point(2019,ref_RS_ees_ict*df_SUF["households [household]"][2019] *1e-9,
yea_RS_ees_ict,trg_RS_ees_ict*df_SUF["households [household]"][yea_RS_ees_ict]*1e-9,
2050,trg_RS_ees_ict*df_SUF["households [household]"][2050] *1e-9, years), # [TWh] kWh/household * household
'light': linear_with_middle_point(2019,ref_RS_ees_lgt*df_SUF["households [household]"][2019] *1e-9,
yea_RS_ees_lgt,trg_RS_ees_lgt*df_SUF["households [household]"][yea_RS_ees_lgt]*1e-9,
2050,trg_RS_ees_lgt*df_SUF["households [household]"][2050] *1e-9, years), # [TWh] kWh/household * household
'others': linear_with_middle_point(2019,ref_RS_ees_oth*df_SUF["households [household]"][2019] *1e-9,
yea_RS_ees_oth,trg_RS_ees_oth*df_SUF["households [household]"][yea_RS_ees_oth]*1e-9,
2050,trg_RS_ees_oth*df_SUF["households [household]"][2050] *1e-9, years), # [TWh] kWh/household * household
}
if post_process:
total_TWh = sum(val[0] for val in ees_RS_tot.values())
total_ktoe = total_TWh*1e+3/ktoe_to_GWh
print(f'-> Relative difference to JRC-IDEES for electrical services: {round(100*(997.764-total_ktoe)/997.764,3)}%')
# === PLOT ===
df_ees_RS_tot = pd.DataFrame(ees_RS_tot, index=years)
dfes = df_ees_RS_tot
fig1, ax = plt.subplots(figsize=(14, 5))
bottom = np.zeros(len(dfes))
colors = plt.cm.tab10.colors # default matplotlib palette
# Thicker bars → control via height argument
bar_height = 2.0 # default is ~0.8; increase to 0.9–1.0 for thicker bars
for i, activity in enumerate(dfes.columns):
ax.barh(dfes.index, dfes[activity],
left=bottom, label=activity,
color=colors[i % len(colors)],
height=bar_height)
bottom += dfes[activity]
ax.set_xlabel("End-Use Demand [TWh]", fontsize=10, ha='right', va='top')
ax.xaxis.set_label_coords(1, -0.1)
ax.set_ylabel("Years", fontsize=10, rotation=0, ha='left', va='bottom')
ax.yaxis.set_label_coords(-0.05, 1)
ax.set_yticks(years)
ax.yaxis.set_inverted(True) # inverted axis with autoscaling
ax.set_title("Evolution of End-Use Demand for Electrical Services in the Residential Sector (negaWatt-BE Scenario)", fontsize=11, pad=15)
ax.legend(bbox_to_anchor=(1, 1), loc='upper left', title="Service", frameon=False)
ax.grid(axis='x', linestyle='--', alpha=0.6)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)
fig1.tight_layout()
plt.show()
-> Relative difference to JRC-IDEES for electrical services: 0.009%
Comment: Could compare to PATHS2050 (https://perspective2050.energyville.be/sites/paths2050/files/inline-files/Full-Fledged%20Report_1.pdf).
# End-Use Demand: thermal and electical
tes_RS_tot['heat_ihs'] = [(x+y)*z for x,y,z in zip(tes_RS_tot['space heating'], tes_RS_tot['sanitary hot water'], share_heat_ihs)]
tes_RS_tot['heat_dhn'] = [(x+y)*z for x,y,z in zip(tes_RS_tot['space heating'], tes_RS_tot['sanitary hot water'], share_heat_dhn)]
tes_RS_tot['cooking_ng'] = [ x*y for x,y in zip(tes_RS_tot['cooking'], share_cook_gas)]
tes_RS_tot['cooking_el'] = [ x*y for x,y in zip(tes_RS_tot['cooking'], share_cook_ele)]
df_tes_RS_tot = pd.DataFrame(tes_RS_tot, index=years) # [TWh]
df_ees_RS_tot = pd.DataFrame(ees_RS_tot, index=years) # [TWh]
# End-Use Demand: carrier distribution
df_eud_RS_tot_car = {
'heat-ihs': df_tes_RS_tot[["heat_ihs"]].T,
'heat-dhn': df_tes_RS_tot[["heat_dhn"]].T,
'cold': df_tes_RS_tot[["space cooling"]].T,
'electricity': pd.concat([df_tes_RS_tot[["cooking_el"]], df_ees_RS_tot], axis=1).T,
'fuel-gas': df_tes_RS_tot[["cooking_ng"]].T,
}
rows = []
for carrier, df in df_eud_RS_tot_car.items():
temp = df.copy()
temp['Carrier'] = carrier
temp['Activity'] = temp.index
temp = temp.reset_index(drop=True)
rows.append(temp)
df_eud_RS_tot_car = pd.concat(rows, ignore_index=True)
# Carriers distribution - aggregated per carrier
df_eud_RS_tot_cln = df_eud_RS_tot_car.groupby('Carrier')[years].sum()
df_eud_RS_tot_cln = df_eud_RS_tot_cln.reset_index()
# Carriers distribution - normalisations
divider_series = df_SUF["households [household]"]
divider_series.index = divider_series.index.astype(float)
df_eud_RS_hsd_car = (df_eud_RS_tot_car[years].div(divider_series, axis=1))*1e+9
df_eud_RS_hsd_car[['Carrier', 'Activity']] = df_eud_RS_tot_car[['Carrier', 'Activity']]
if post_process:
# === Table with values ===
df_abs = df_eud_RS_tot_car.set_index(['Carrier', 'Activity'])[years]
df_rel = df_eud_RS_hsd_car.set_index(['Carrier', 'Activity'])[years]
df_abs['Unit'] = 'TWh'
df_rel['Unit'] = 'kWh/household'
df_combined = pd.concat([df_abs, df_rel]).set_index('Unit', append=True)
df_combined = df_combined.sort_index(level=['Carrier', 'Activity'], sort_remaining=False)
df_final = df_combined.reset_index()
df_final['Carrier'] = df_final['Carrier'].mask(df_final['Carrier'].duplicated(), '')
df_final['Activity'] = df_final['Activity'].mask(df_final['Activity'].duplicated(), '')
styled = (
df_final.style
.hide(axis='index')
.apply(highlight_mode_separator, axis=1)
.set_properties(subset=['Unit'], **{'font-size': '85%', 'color': 'gray'})
.set_properties(subset=['Activity'], **{'font-style':'italic'})
.format({year: "{:.3f}" for year in years})
.set_table_attributes('style="width:100%;table-layout:fixed;"')
.apply(lambda row: [bold_mode(cell, row['Carrier'], col) for col, cell in zip(df_final.columns, row)], axis=1)
)
display(styled)
# === Breakdown bar chart ===
dfrs = df_eud_RS_tot_cln.set_index('Carrier').reindex(['heat-ihs', 'heat-dhn', 'cold', 'electricity', 'fuel-gas']).transpose()
fig1, ax = plt.subplots(figsize=(14, 5))
bottom = np.zeros(len(dfrs))
colors = ['tab:red','darkred','tab:blue','tab:green','tab:orange']
# Thicker bars → control via height argument
bar_height = 2.0 # default is ~0.8; increase to 0.9–1.0 for thicker bars
for i, carrier in enumerate(dfrs.columns):
ax.barh(dfrs.index, dfrs[carrier],
left=bottom, label=carrier,
color=colors[i % len(colors)],
height=bar_height)
bottom += dfrs[carrier]
ax.set_xlabel("End-Use Demand [TWh]", fontsize=10, ha='right', va='top')
ax.xaxis.set_label_coords(1, -0.1)
ax.set_ylabel("Years", fontsize=10, rotation=0, ha='left', va='bottom')
ax.yaxis.set_label_coords(-0.05, 1)
ax.set_yticks(years)
ax.yaxis.set_inverted(True) # inverted axis with autoscaling
ax.set_title("Evolution of End-Use Demand for Residential Sector (negaWatt-BE Scenario)", fontsize=11, pad=15)
ax.legend(bbox_to_anchor=(1, 1), loc='upper left', title="Carrier", frameon=False)
ax.grid(axis='x', linestyle='--', alpha=0.6)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)
fig1.tight_layout()
plt.show()
| Carrier | Activity | Unit | 2019 | 2025 | 2030 | 2035 | 2040 | 2045 | 2050 |
|---|---|---|---|---|---|---|---|---|---|
| cold | space cooling | TWh | 0.273 | 0.403 | 0.511 | 0.619 | 0.727 | 0.835 | 0.944 |
| kWh/household | 55.169 | 77.505 | 95.423 | 112.752 | 129.597 | 146.419 | 163.580 | ||
| electricity | ICT | TWh | 1.126 | 1.163 | 1.193 | 1.223 | 1.253 | 1.284 | 1.314 |
| kWh/household | 227.548 | 223.668 | 222.777 | 222.772 | 223.363 | 225.152 | 227.695 | ||
| TV and multimedia | TWh | 2.995 | 2.888 | 2.800 | 2.711 | 2.622 | 2.534 | 2.445 | |
| kWh/household | 605.246 | 555.420 | 522.864 | 493.813 | 467.405 | 444.342 | 423.680 | ||
| cooking_el | TWh | 0.821 | 0.932 | 1.031 | 1.133 | 1.242 | 1.355 | 1.474 | |
| kWh/household | 165.826 | 179.261 | 192.477 | 206.465 | 221.402 | 237.686 | 255.407 | ||
| dishwashers | TWh | 0.688 | 0.723 | 0.751 | 0.780 | 0.808 | 0.837 | 0.866 | |
| kWh/household | 139.035 | 139.047 | 140.240 | 142.078 | 144.036 | 146.770 | 150.064 | ||
| laundry dryers | TWh | 0.854 | 0.738 | 0.642 | 0.545 | 0.449 | 0.455 | 0.462 | |
| kWh/household | 172.581 | 141.932 | 119.885 | 99.273 | 80.040 | 79.785 | 80.057 | ||
| light | TWh | 1.627 | 1.284 | 0.999 | 0.714 | 0.726 | 0.738 | 0.750 | |
| kWh/household | 328.793 | 246.939 | 186.550 | 130.056 | 129.419 | 129.410 | 129.963 | ||
| others | TWh | 1.128 | 1.164 | 1.195 | 1.225 | 1.255 | 1.285 | 1.316 | |
| kWh/household | 227.953 | 223.860 | 223.151 | 223.136 | 223.720 | 225.327 | 228.042 | ||
| refrigeration | TWh | 2.440 | 1.983 | 1.603 | 1.222 | 0.841 | 0.854 | 0.866 | |
| kWh/household | 493.089 | 381.371 | 299.340 | 222.589 | 149.919 | 149.751 | 150.064 | ||
| washing machines | TWh | 0.745 | 0.601 | 0.482 | 0.491 | 0.501 | 0.510 | 0.519 | |
| kWh/household | 150.554 | 115.584 | 90.007 | 89.437 | 89.310 | 89.429 | 89.934 | ||
| fuel-gas | cooking_ng | TWh | 0.300 | 0.263 | 0.226 | 0.185 | 0.138 | 0.087 | 0.030 |
| kWh/household | 60.712 | 50.561 | 42.251 | 33.611 | 24.600 | 15.171 | 5.212 | ||
| heat-dhn | heat_dhn | TWh | 0.151 | 1.426 | 2.331 | 3.062 | 3.585 | 3.972 | 4.187 |
| kWh/household | 30.512 | 274.189 | 435.204 | 557.742 | 638.998 | 696.568 | 725.480 | ||
| heat-ihs | heat_ihs | TWh | 50.178 | 44.564 | 40.043 | 35.697 | 31.558 | 27.555 | 23.724 |
| kWh/household | 10140.254 | 8570.608 | 7477.593 | 6502.279 | 5625.689 | 4831.751 | 4111.055 |
The distribution of the different energy services for thermal uses for the reference year in the tertiary sector is from JRC-IDEES [1,2]. The values are reported to different parameters, as different scaling and projection rules will be applied:
- Space heating: reported to average household surface area [kWh/m²], as proportional to this quantity (regardless of the number of inhabitants).
- Space cooling: reported to average household surface area [kWh/m²], as proportional to this quantity (regardless of the number of inhabitants).
- Sanitary hot water: reported to population [kWh/person], as proportional to this quantity (showering is the main driver).
- Catering: reported to population [kWh/person], as proportional to this quantity (meals a per person).
# Inputs - Repartition of the thermal energy services (TES) in tertiary buildings for the reference year (2019)
ref_TS_tes_sht = 1862.949*ktoe_to_GWh*1e+6/ref_TS_sur_tot # [kWh/m²]
ref_TS_tes_scl = 336.272*ktoe_to_GWh*1e+6/ref_TS_sur_tot # [kWh/m²]
ref_TS_tes_shw = 299.836*ktoe_to_GWh*1e+6/population_dict[2019] # [kWh/person]
ref_TS_tes_cat = 284.204*ktoe_to_GWh*1e+6/population_dict[2019] # [kWh/person]
if post_process:
print(f'Energy service for space heating: {round(ref_TS_tes_sht,3)} kWh/m²')
print(f'Energy service for space cooling: {round(ref_TS_tes_scl,3)} kWh/m²')
print(f'Energy service for hot water: {round(ref_TS_tes_shw,3)} kWh/person')
print(f'Energy service for catering: {round(ref_TS_tes_cat,3)} kWh/person')
Energy service for space heating: 95.415 kWh/m² Energy service for space cooling: 17.223 kWh/m² Energy service for hot water: 305.045 kWh/person Energy service for catering: 289.141 kWh/person
3.1.1. Building Energy Performance
As for the residental sector, our approach evaluates the average performance of the overall building stock.
According to figures from JRC-IDEES [1,2], the average annual renewal rate of floor area (renovation + new construction) was around 1,7% between 2000 and 2023, with the associated reduction in end-use demand averaging -0,154 kWh/m²/year. We mainly attribute this decrease to efficiency measures (insulation), although some episodic reductions may also have been linked to sufficiency measures (reduced heating use during energy crises, etc.).
Caution on this 1,7%. As in the residential sector, it decomposes into about 1,2 points of net new construction and only about 0,5 points of renovation (0,46%/year on average, and a constant 0,51%/year from 2017 onwards in the JRC-IDEES building-stock model). New construction therefore dominates the series, and this renewal rate is not comparable with the energy-renovation rate used in European policy [7]. The quantity we calibrate on below is the -0,154 kWh/m²/year improvement in end-use demand, not the renewal rate.
Efficiency Assumption
We believe that the average annual improvement rate over the 2000-2023 period is too low to meet climate objectives. We therefore propose to increase that improvement rate — the -0,154 kWh/m²/year reduction in end-use demand, not the renewal rate — fivefold in order to accelerate the energy transition. With this improvement rate of -0,77 kWh/m²/year, the efficiency-related building performance improves from 95,4 kWh/m² in 2019 to 71,5 kWh/m² in 2050.
acc_TS_tes_sht_ren = 5
cur_TS_tes_sht_ren = -0.154 # kWh/m²/year
trg_TS_tes_sht = ref_TS_tes_sht + acc_TS_tes_sht_ren*cur_TS_tes_sht_ren*(2050-2019) # [kWh/m²]
Space heating
SlowHeat in the service sector has not yet been studied in depth, although research is underway.
Sufficiency Assumption
The adoption of SlowHeat in the tertiary sector seems even more subject to socio-psychological barriers than in the residential sector. The fear of a loss of attractiveness for commercial activities and disagreement among office staff are telling examples of this. We therefore plan to reduce the setpoint by only 1°C (compared to 2°C in the residential sector). We hence estimate an additional -7% reduction on top of the efficiency measures described above. This results in a heat demand of 66,5 kWh/m² in 2050.
d_cons_temp = 0.07
d_temp = 1
suf_TS_tes_sht = 1-d_temp*d_cons_temp
if post_process:
print(f'Evolution of space heating energy demand: {round(-(1-(trg_TS_tes_sht*suf_TS_tes_sht)/(ref_TS_tes_sht))*100,1)}% [kWh/m²]')
print(f' -> By 2019, the annual heating energy demand is {round(ref_TS_tes_sht, 2)} kWh/m²')
print(f' -> By 2050, the annual heating energy demand is {round(trg_TS_tes_sht*suf_TS_tes_sht,2)} kWh/m²')
Evolution of space heating energy demand: -30.3% [kWh/m²] -> By 2019, the annual heating energy demand is 95.41 kWh/m² -> By 2050, the annual heating energy demand is 66.54 kWh/m²
Comment: From 2001 to 2023, we observed -0,154 kWh/m²/year on average. Yet, from 2021 to 2023, following energy crisis, we went from 102,6 kWh/m² to 78,6 kWh/m² (-19,3 kWh/m²/year). This shows that there is HUGE margin! And that reaching about 60 kWh/m² is more than realistic!
Space cooling
Cooling demand increased by a factor of x9,7 between 2000 and 2019, rising from 1,8 kWh/m² to 17,2 kWh/m². From 2019 to 2022, the demand has stabilised and slightly decreased, probably due to the COVID-19 pandemic and the energy crisis.
Sufficiency Assumption
Due to climate change, we acknowledge that the deployment of air conditioning is becoming a necessity to ensure a decent quality of life across a range of applications. However, we believe that its deployment can be controlled and restricted to cases where it is genuinely necessary.
As a sufficiency assumption, we project that the rate of deployment of residential air conditioning over the period 2019-2050 will be equivalent to one third as over the historical period from 2000 to 2023 (+0,743 kWh/m²/year) and reach 24,9 kWh/m² in 2050.
acc_TS_tes_scl_ren = 1/3
cur_TS_tes_scl_ren = 0.743 # kWh/m²/year
trg_TS_tes_scl = ref_TS_tes_scl + acc_TS_tes_scl_ren*cur_TS_tes_scl_ren*(2050-2019) # [kWh/m²]
if post_process:
print(f'Evolution of space cooling energy demand: +{round(-(1-(trg_TS_tes_scl)/(ref_TS_tes_scl))*100,1)}% [kWh/m²]')
print(f' -> By 2019, the annual cooling energy demand is {round(ref_TS_tes_scl,2)} kWh/m²')
print(f' -> By 2050, the annual cooling energy demand is {round(trg_TS_tes_scl,2)} kWh/m²')
Evolution of space cooling energy demand: +44.6% [kWh/m²] -> By 2019, the annual cooling energy demand is 17.22 kWh/m² -> By 2050, the annual cooling energy demand is 24.9 kWh/m²
Comment: Since COVID19 + 2022 energy crisis, stagnation (2019 was 17.3 kWh/m², while 2023 was 16.4 kWh/m²). Containing this is thus realistic!!!
Sanitary hot water
The demand for sanitary has continuously increased from 2000 to 2023 [1,2].
Sufficiency Assumption
As a sufficiency measure, we propose to contain the sanitary hot water demand to 2019 levels (305,0 kWh/person).
# Inputs - Define Sufficiency Scenario Data (SUF)
pro_TS_tes_shw = 0.00
# Outputs - Sufficiency Scenario Data (SUF)
trg_TS_tes_shw = (1+pro_TS_tes_shw)*ref_TS_tes_shw # kWh/person
print(f'Evolution of sanitary hot water consumption: {round(pro_TS_tes_shw*100,1)}% [kWh/person]')
print(f' -> By 2019, the equivalent consumption is {round(ref_TS_tes_shw,2)} kWh per person')
print(f' -> By 2050, the equivalent consumption is {round(trg_TS_tes_shw,2)} kWh per person')
Evolution of sanitary hot water consumption: 0.0% [kWh/person] -> By 2019, the equivalent consumption is 305.04 kWh per person -> By 2050, the equivalent consumption is 305.04 kWh per person
Catering
While energy demand related to residential cooking decreased by -0,237 kWh/person/year from 2000 to 2019 (-0,523 over 2000-2023, but see the JRC break flagged in §2.1.2), it increased continuously by +4,297 kWh/person/year in the tertiary sector [1,2]. We project that this demand will have increased by a further 20% in 2050 compared to 2019.
Efficiency Assumption
In addition, in order to limit greenhouse gas emissions, we prioritise the use of electric cookers and ovens. Nevertheless, we retain a 5% share of gas cooking by 2050, and we maintain the share of biomass (because we love wood-fired pizzas!).
# Inputs - Define Sufficiency Scenario Data (SUF)
pro_TS_tes_cat = 0.20
ref_TS_tes_cat_gas = (8.421+106.309)/284.204 # ktoe_gas/ktoe_tot (from JRC-IDEES)
trg_TS_tes_cat_gas = 0.050
ref_TS_tes_cat_bio = 1.603 /284.204 # ktoe_bio/ktoe_tot (from JRC-IDEES)
trg_TS_tes_cat_bio = ref_TS_tes_cat_bio
# Outputs - Sufficiency Scenario Data (SUF)
trg_TS_tes_cat = (1+pro_TS_tes_cat)*ref_TS_tes_cat # kWh/person
share_ctrg_gas = linear_growth(2019, ref_TS_tes_cat_gas,
2050, trg_TS_tes_cat_gas,years) # [%]
share_ctrg_bio = linear_growth(2019, ref_TS_tes_cat_bio,
2050, trg_TS_tes_cat_bio,years) # [%]
share_ctrg_ele = linear_growth(2019,1-ref_TS_tes_cat_gas-ref_TS_tes_cat_bio,
2050,1-trg_TS_tes_cat_gas-trg_TS_tes_cat_bio,years) # [%]
print(f'Evolution of catering consumption: {round(pro_TS_tes_cat*100,1)}% [kWh/person]')
print(f' -> By 2019, the equivalent consumption is {round(ref_TS_tes_cat,2)} kWh per person')
print(f' -> By 2050, the equivalent consumption is {round(trg_TS_tes_cat,2)} kWh per person')
Evolution of catering consumption: 20.0% [kWh/person] -> By 2019, the equivalent consumption is 289.14 kWh per person -> By 2050, the equivalent consumption is 346.97 kWh per person
ref_TS_tes_dhn = (46.381+5.984)/(1862.949+299.836) # [%] 2019 reference share of district heating network in Belgium from JRC-IDEES
trg_TS_tes_dhn = 0.15 # [%] 2050 target share of district heating network in Belgium from nW-BE
share_heat_dhn = linear_growth(2019, ref_TS_tes_dhn,
2050, trg_TS_tes_dhn,years) # [%]
share_heat_ihs = linear_growth(2019,1-ref_TS_tes_dhn,
2050,1-trg_TS_tes_dhn,years) # [%]
# Inputs - Repartition of the electrical energy services (EES) in tertiary buildings for the reference year (2019)
ref_TS_ees_vnt = 71.632*ktoe_to_GWh*1e+6/df_SUF["population [person]"][2019] # [kWh/person]
ref_TS_ees_slt = 77.819*ktoe_to_GWh*1e+6/df_SUF["population [person]"][2019] # [kWh/person]
ref_TS_ees_blt = 297.768*ktoe_to_GWh*1e+6/df_SUF["population [person]"][2019] # [kWh/person]
ref_TS_ees_frg = 179.674*ktoe_to_GWh*1e+6/df_SUF["population [person]"][2019] # [kWh/person]
ref_TS_ees_msc = 176.613*ktoe_to_GWh*1e+6/df_SUF["population [person]"][2019] # [kWh/person]
ref_TS_ees_ict = 248.983*ktoe_to_GWh*1e+6/df_SUF["population [person]"][2019] # [kWh/person]
if post_process:
print(f'Energy service for ventilation: {round(ref_TS_ees_vnt,3)} kWh/person')
print(f'Energy service for street lighting: {round(ref_TS_ees_slt,3)} kWh/person')
print(f'Energy service for building lighting: {round(ref_TS_ees_blt,3)} kWh/person')
print(f'Energy service for refrigeration: {round(ref_TS_ees_frg,3)} kWh/person')
print(f'Energy service for miscellaneous: {round(ref_TS_ees_msc,3)} kWh/person')
print(f'Energy service for ICT & multimedia: {round(ref_TS_ees_ict,3)} kWh/person')
Energy service for ventilation: 72.876 kWh/person Energy service for street lighting: 79.171 kWh/person Energy service for building lighting: 302.941 kWh/person Energy service for refrigeration: 182.795 kWh/person Energy service for miscellaneous: 179.681 kWh/person Energy service for ICT & multimedia: 253.308 kWh/person
Comment: Less effort has been spent on this section. The normalisation should be improved (using e.g. the m²), and projections should be further discussed.
Ventilation and others
As this parameter has been rahter stable for the past years, we assume it constant.
trg_TS_ees_vnt = ref_TS_ees_vnt # [kWh/person]
Street lighting
We project a reduction of -0.476 kWh/person/year (average from 2000 to 2023). This assumption relies on efficiency gains and limited use (Belgium has been an historical exception).
trg_TS_ees_slt = ref_TS_ees_slt-0.476*(2050-2019) # [kWh/person]
Building lighting
Since the 2010s, the consumption associated with buildings lighting has significantly dropped thanks to efficiency gains. We project 150 kWh/person as a target for 2050.
trg_TS_ees_blt = 150 # [kWh/person]
Commercial refrigeration
This is value is rather stable, and even decreasing for the past decade (efficiency gains, including the obligation to use refrigerators with doors in distribution). For 2050, we assume a -15% reduction compared with 2019.
pro_TS_ees_frg = -0.15
trg_TS_ees_frg = (1+pro_TS_ees_frg)*ref_TS_ees_frg # [kWh/person]
Miscellaneous building technologies
We assume a constant value for this parameter.
trg_TS_ees_msc = ref_TS_ees_msc # [kWh/person]
ICT and multimedia
Given the historically growing trend, we assume +10% for 2050 compared with 2019.
pro_TS_ees_ict = 0.10
trg_TS_ees_ict = (1+pro_TS_ees_ict)*ref_TS_ees_ict # [kWh/person]
# Tertiary Sector - Thermal Energy Services
tes_TS_tot = {
'space heating': linear_growth(2019,ref_TS_tes_sht *df_SUF["TS total surface [Mm²]"][2019]*1e-3,
2050,trg_TS_tes_sht*suf_TS_tes_sht*df_SUF["TS total surface [Mm²]"][2050]*1e-3,years), # [TWh] kWh/m² * m²
'space cooling': linear_growth(2019,ref_TS_tes_scl *df_SUF["TS total surface [Mm²]"][2019]*1e-3,
2050,trg_TS_tes_scl *df_SUF["TS total surface [Mm²]"][2050]*1e-3,years), # [TWh] kWh/m² * m²
'sanitary hot water': linear_growth(2019,ref_TS_tes_shw *df_SUF["population [person]"] [2019]*1e-9,
2050,trg_TS_tes_shw *df_SUF["population [person]"] [2050]*1e-9,years), # [TWh] kWh/person * person
'catering': linear_growth(2019,ref_TS_tes_cat *df_SUF["population [person]"] [2019]*1e-9,
2050,trg_TS_tes_cat *df_SUF["population [person]"] [2050]*1e-9,years), # [TWh] kWh/person * person
}
if post_process:
total_TWh = sum(val[0] for val in tes_TS_tot.values())
total_ktoe = total_TWh*1e+3/ktoe_to_GWh
print(f'-> Relative difference to JRC-IDEES for thermal services: {round(100*(2783.260-total_ktoe)/2783.260,3)}%')
# === PLOT ===
df_tes_TS_tot = pd.DataFrame(tes_TS_tot, index=years)
dfts = df_tes_TS_tot
fig1, ax = plt.subplots(figsize=(14, 5))
bottom = np.zeros(len(dfts))
colors = ['tab:red','tab:blue','tab:orange','tab:green']
# Thicker bars → control via height argument
bar_height = 2.0 # default is ~0.8; increase to 0.9–1.0 for thicker bars
for i, activity in enumerate(dfts.columns):
ax.barh(dfts.index, dfts[activity],
left=bottom, label=activity,
color=colors[i % len(colors)],
height=bar_height)
bottom += dfts[activity]
ax.set_xlabel("End-Use Demand [TWh]", fontsize=10, ha='right', va='top')
ax.xaxis.set_label_coords(1, -0.1)
ax.set_ylabel("Years", fontsize=10, rotation=0, ha='left', va='bottom')
ax.yaxis.set_label_coords(-0.05, 1)
ax.set_yticks(years)
ax.yaxis.set_inverted(True) # inverted axis with autoscaling
ax.set_title("Evolution of End-Use Demand for Thermal Services in the Tertiary Sector (negaWatt-BE Scenario)", fontsize=11, pad=15)
ax.legend(bbox_to_anchor=(1, 1), loc='upper left', title="Service", frameon=False)
ax.grid(axis='x', linestyle='--', alpha=0.6)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)
fig1.tight_layout()
plt.show()
-> Relative difference to JRC-IDEES for thermal services: 0.001%
# Tertiary Sector - Electrical Energy Services
ees_TS_tot = {
'ventilation': linear_growth(2019, ref_TS_ees_vnt*df_SUF["population [person]"][2019] *1e-9,
2050, trg_TS_ees_vnt*df_SUF["population [person]"][2050] *1e-9, years), # [TWh] kWh/person * person
'street lighting': linear_growth(2019, ref_TS_ees_slt*df_SUF["population [person]"][2019] *1e-9,
2050, trg_TS_ees_slt*df_SUF["population [person]"][2050] *1e-9, years), # [TWh] kWh/person * person
'building lighting': linear_growth(2019, ref_TS_ees_blt*df_SUF["population [person]"][2019] *1e-9,
2050, trg_TS_ees_blt*df_SUF["population [person]"][2050] *1e-9, years), # [TWh] kWh/person * person
'refrigeration': linear_growth(2019, ref_TS_ees_frg*df_SUF["population [person]"][2019] *1e-9,
2050, trg_TS_ees_frg*df_SUF["population [person]"][2050] *1e-9, years), # [TWh] kWh/person * person
'miscellaneous': linear_growth(2019, ref_TS_ees_msc*df_SUF["population [person]"][2019] *1e-9,
2050, trg_TS_ees_msc*df_SUF["population [person]"][2050] *1e-9, years), # [TWh] kWh/person * person
'ICT': linear_growth(2019, ref_TS_ees_ict*df_SUF["population [person]"][2019] *1e-9,
2050, trg_TS_ees_ict*df_SUF["population [person]"][2050] *1e-9, years), # [TWh] kWh/person * person
}
if post_process:
total_TWh = sum(val[0] for val in ees_TS_tot.values())
total_ktoe = total_TWh*1e+3/ktoe_to_GWh
print(f'-> Relative difference to JRC-IDEES for electrical services: {round(100*(1052.489-total_ktoe)/1052.489,3)}%')
# === PLOT ===
df_ees_TS_tot = pd.DataFrame(ees_TS_tot, index=years)
dfes = df_ees_TS_tot
fig1, ax = plt.subplots(figsize=(14, 5))
bottom = np.zeros(len(dfes))
colors = plt.cm.tab10.colors # default matplotlib palette
# Thicker bars → control via height argument
bar_height = 2.0 # default is ~0.8; increase to 0.9–1.0 for thicker bars
for i, activity in enumerate(dfes.columns):
ax.barh(dfes.index, dfes[activity],
left=bottom, label=activity,
color=colors[i % len(colors)],
height=bar_height)
bottom += dfes[activity]
ax.set_xlabel("End-Use Demand [TWh]", fontsize=10, ha='right', va='top')
ax.xaxis.set_label_coords(1, -0.1)
ax.set_ylabel("Years", fontsize=10, rotation=0, ha='left', va='bottom')
ax.yaxis.set_label_coords(-0.05, 1)
ax.set_yticks(years)
ax.yaxis.set_inverted(True) # inverted axis with autoscaling
ax.set_title("Evolution of End-Use Demand for Electrical Services in the Tertiary Sector (negaWatt-BE Scenario)", fontsize=11, pad=15)
ax.legend(bbox_to_anchor=(1, 1), loc='upper left', title="Service", frameon=False)
ax.grid(axis='x', linestyle='--', alpha=0.6)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)
fig1.tight_layout()
plt.show()
-> Relative difference to JRC-IDEES for electrical services: -0.005%
Comment: Could compare to PATHS2050 (https://perspective2050.energyville.be/sites/paths2050/files/inline-files/Full-Fledged%20Report_1.pdf).
# End-Use Demand: thermal and electical
tes_TS_tot['heat_ihs'] = [(x+y)*z for x,y,z in zip(tes_TS_tot['space heating'], tes_TS_tot['sanitary hot water'], share_heat_ihs)]
tes_TS_tot['heat_dhn'] = [(x+y)*z for x,y,z in zip(tes_TS_tot['space heating'], tes_TS_tot['sanitary hot water'], share_heat_dhn)]
tes_TS_tot['cooking_ng'] = [ x*y for x,y in zip(tes_TS_tot['catering'], share_ctrg_gas)]
tes_TS_tot['cooking_bm'] = [ x*y for x,y in zip(tes_TS_tot['catering'], share_ctrg_bio)]
tes_TS_tot['cooking_el'] = [ x*y for x,y in zip(tes_TS_tot['catering'], share_ctrg_ele)]
df_tes_TS_tot = pd.DataFrame(tes_TS_tot, index=years) # [TWh]
df_ees_TS_tot = pd.DataFrame(ees_TS_tot, index=years) # [TWh]
# End-Use Demand: carrier distribution
df_eud_TS_tot_car = {
'heat-ihs': df_tes_TS_tot[["heat_ihs"]].T,
'heat-dhn': df_tes_TS_tot[["heat_dhn"]].T,
'cold': df_tes_TS_tot[["space cooling"]].T,
'electricity': pd.concat([df_tes_TS_tot[["cooking_el"]], df_ees_TS_tot], axis=1).T,
'fuel-gas': df_tes_TS_tot[["cooking_ng"]].T,
'fuel-bio': df_tes_TS_tot[["cooking_bm"]].T,
}
rows = []
for carrier, df in df_eud_TS_tot_car.items():
temp = df.copy()
temp['Carrier'] = carrier
temp['Activity'] = temp.index
temp = temp.reset_index(drop=True)
rows.append(temp)
df_eud_TS_tot_car = pd.concat(rows, ignore_index=True)
# Carriers distribution - aggregated per carrier
df_eud_TS_tot_cln = df_eud_TS_tot_car.groupby('Carrier')[years].sum()
df_eud_TS_tot_cln = df_eud_TS_tot_cln.reset_index()
# Carriers distribution - normalisations
divider_series = df_SUF["population [person]"]
divider_series.index = divider_series.index.astype(float)
df_eud_TS_hsd_car = (df_eud_TS_tot_car[years].div(divider_series, axis=1))*1e+9
df_eud_TS_hsd_car[['Carrier', 'Activity']] = df_eud_TS_tot_car[['Carrier', 'Activity']]
if post_process:
# === Table with values ===
df_abs = df_eud_TS_tot_car.set_index(['Carrier', 'Activity'])[years]
df_rel = df_eud_TS_hsd_car.set_index(['Carrier', 'Activity'])[years]
df_abs['Unit'] = 'TWh'
df_rel['Unit'] = 'kWh/person'
df_combined = pd.concat([df_abs, df_rel]).set_index('Unit', append=True)
df_combined = df_combined.sort_index(level=['Carrier', 'Activity'], sort_remaining=False)
df_final = df_combined.reset_index()
df_final['Carrier'] = df_final['Carrier'].mask(df_final['Carrier'].duplicated(), '')
df_final['Activity'] = df_final['Activity'].mask(df_final['Activity'].duplicated(), '')
styled = (
df_final.style
.hide(axis='index')
.apply(highlight_mode_separator, axis=1)
.set_properties(subset=['Unit'], **{'font-size': '85%', 'color': 'gray'})
.set_properties(subset=['Activity'], **{'font-style':'italic'})
.format({year: "{:.3f}" for year in years})
.set_table_attributes('style="width:100%;table-layout:fixed;"')
.apply(lambda row: [bold_mode(cell, row['Carrier'], col) for col, cell in zip(df_final.columns, row)], axis=1)
)
display(styled)
# === Breakdown bar chart ===
dfrs = df_eud_TS_tot_cln.set_index('Carrier').reindex(['heat-ihs', 'heat-dhn', 'cold', 'electricity', 'fuel-gas', 'fuel-bio']).transpose()
fig1, ax = plt.subplots(figsize=(14, 5))
bottom = np.zeros(len(dfrs))
colors = ['tab:red','darkred','tab:blue','tab:orange','tab:green','tab:brown']
# Thicker bars → control via height argument
bar_height = 2.0 # default is ~0.8; increase to 0.9–1.0 for thicker bars
for i, carrier in enumerate(dfrs.columns):
ax.barh(dfrs.index, dfrs[carrier],
left=bottom, label=carrier,
color=colors[i % len(colors)],
height=bar_height)
bottom += dfrs[carrier]
ax.set_xlabel("End-Use Demand [TWh]", fontsize=10, ha='right', va='top')
ax.xaxis.set_label_coords(1, -0.1)
ax.set_ylabel("Years", fontsize=10, rotation=0, ha='left', va='bottom')
ax.yaxis.set_label_coords(-0.05, 1)
ax.set_yticks(years)
ax.yaxis.set_inverted(True) # inverted axis with autoscaling
ax.set_title("Evolution of End-Use Demand for Tertiary Sector (negaWatt-BE Scenario)", fontsize=11, pad=15)
ax.legend(bbox_to_anchor=(1, 1), loc='upper left', title="Carrier", frameon=False)
ax.grid(axis='x', linestyle='--', alpha=0.6)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)
fig1.tight_layout()
plt.show()
| Carrier | Activity | Unit | 2019 | 2025 | 2030 | 2035 | 2040 | 2045 | 2050 |
|---|---|---|---|---|---|---|---|---|---|
| cold | space cooling | TWh | 3.911 | 4.240 | 4.514 | 4.788 | 5.062 | 5.336 | 5.610 |
| kWh/person | 342.128 | 358.832 | 375.420 | 392.886 | 409.972 | 427.199 | 445.206 | ||
| electricity | ICT | TWh | 2.896 | 3.015 | 3.114 | 3.213 | 3.313 | 3.412 | 3.511 |
| kWh/person | 253.337 | 255.160 | 258.985 | 263.647 | 268.321 | 273.164 | 278.631 | ||
| building lighting | TWh | 3.463 | 3.159 | 2.905 | 2.651 | 2.398 | 2.144 | 1.890 | |
| kWh/person | 302.937 | 267.347 | 241.603 | 217.532 | 194.215 | 171.648 | 149.989 | ||
| cooking_el | TWh | 1.953 | 2.262 | 2.535 | 2.818 | 3.115 | 3.424 | 3.744 | |
| kWh/person | 170.867 | 191.463 | 210.801 | 231.264 | 252.284 | 274.110 | 297.114 | ||
| miscellaneous | TWh | 2.054 | 2.095 | 2.129 | 2.162 | 2.196 | 2.230 | 2.264 | |
| kWh/person | 179.680 | 177.300 | 177.065 | 177.406 | 177.855 | 178.533 | 179.670 | ||
| refrigeration | TWh | 2.090 | 2.064 | 2.043 | 2.022 | 2.000 | 1.979 | 1.958 | |
| kWh/person | 182.830 | 174.677 | 169.912 | 165.918 | 161.980 | 158.438 | 155.386 | ||
| street lighting | TWh | 0.905 | 0.887 | 0.872 | 0.857 | 0.842 | 0.827 | 0.812 | |
| kWh/person | 79.168 | 75.067 | 72.522 | 70.322 | 68.194 | 66.209 | 64.440 | ||
| ventilation | TWh | 0.833 | 0.850 | 0.863 | 0.877 | 0.891 | 0.905 | 0.918 | |
| kWh/person | 72.869 | 71.936 | 71.774 | 71.964 | 72.162 | 72.454 | 72.852 | ||
| fuel-bio | cooking_bm | TWh | 0.020 | 0.021 | 0.021 | 0.022 | 0.023 | 0.023 | 0.024 |
| kWh/person | 1.735 | 1.743 | 1.766 | 1.795 | 1.824 | 1.854 | 1.888 | ||
| fuel-gas | cooking_ng | TWh | 1.335 | 1.150 | 0.984 | 0.806 | 0.615 | 0.413 | 0.198 |
| kWh/person | 116.803 | 97.329 | 81.847 | 66.118 | 49.849 | 33.066 | 15.737 | ||
| heat-dhn | heat_dhn | TWh | 0.604 | 1.173 | 1.581 | 1.948 | 2.275 | 2.581 | 2.825 |
| kWh/person | 52.808 | 99.235 | 131.477 | 159.871 | 184.257 | 206.615 | 224.186 | ||
| heat-ihs | heat_ihs | TWh | 24.549 | 22.757 | 21.330 | 19.943 | 18.597 | 17.271 | 16.008 |
| kWh/person | 2147.534 | 1925.968 | 1773.984 | 1636.428 | 1506.171 | 1382.733 | 1270.388 |
# Aggregate global demand data
df_eud_BS_tot_cln = pd.concat([df_eud_RS_tot_cln, df_eud_TS_tot_cln])
df_eud_BS_tot_fin = df_eud_BS_tot_cln.groupby('Carrier').sum().reset_index()
df_eud_BS_tot_fin.loc[ len(df_eud_BS_tot_fin)] = df_eud_BS_tot_fin.sum(numeric_only=True)
df_eud_BS_tot_fin.iloc[-1, df_eud_BS_tot_fin.columns.get_loc('Carrier')] = 'TOTAL'
df_eud_BS_tot_fin = df_eud_BS_tot_fin.set_index('Carrier')
custom_order = [
'heat-dhn',
'heat-ihs',
'cold',
'electricity',
'fuel-gas',
'fuel-bio',
'TOTAL'
]
df_eud_BS_tot_fin = df_eud_BS_tot_fin.reindex([c for c in custom_order if c in df_eud_BS_tot_fin.index])
df_eud_BS_tot_fin
| 2019 | 2025 | 2030 | 2035 | 2040 | 2045 | 2050 | |
|---|---|---|---|---|---|---|---|
| Carrier | |||||||
| heat-dhn | 0.754659 | 2.598260 | 3.911429 | 5.010260 | 5.859634 | 6.553162 | 7.011600 |
| heat-ihs | 74.727341 | 67.321740 | 61.373571 | 55.639740 | 50.155366 | 44.825838 | 39.732400 |
| cold | 4.184000 | 4.643000 | 5.025000 | 5.407000 | 5.789000 | 6.171000 | 6.554000 |
| electricity | 26.617827 | 25.808447 | 25.156380 | 24.644838 | 24.451990 | 24.773300 | 25.108824 |
| fuel-gas | 1.635648 | 1.412955 | 1.210380 | 0.990286 | 0.753492 | 0.499540 | 0.228380 |
| fuel-bio | 0.019830 | 0.020598 | 0.021240 | 0.021876 | 0.022518 | 0.023160 | 0.023796 |
| TOTAL | 107.939305 | 101.805000 | 96.698000 | 91.714000 | 87.032000 | 82.846000 | 78.659000 |
if post_process:
dfbs = df_eud_BS_tot_fin.loc[['heat-ihs', 'heat-dhn', 'cold', 'electricity', 'fuel-gas', 'fuel-bio']].transpose()
fig1, ax = plt.subplots(figsize=(14, 5))
bottom = np.zeros(len(dfbs))
colors = ['tab:red','darkred','tab:blue','tab:orange','tab:green','tab:brown']
# Thicker bars → control via height argument
bar_height = 2.0 # default is ~0.8; increase to 0.9–1.0 for thicker bars
for i, carrier in enumerate(dfbs.columns):
ax.barh(dfbs.index, dfbs[carrier],
left=bottom, label=carrier,
color=colors[i % len(colors)],
height=bar_height)
bottom += dfbs[carrier]
ax.set_xlabel("End-Use Demand [TWh]", fontsize=10, ha='right', va='top')
ax.xaxis.set_label_coords(1, -0.1)
ax.set_ylabel("Years", fontsize=10, rotation=0, ha='left', va='bottom')
ax.yaxis.set_label_coords(-0.05, 1)
ax.set_yticks(years)
ax.yaxis.set_inverted(True) # inverted axis with autoscaling
ax.set_title("Evolution of End-Use Demand for Building Sector (negaWatt-BE Scenario)", fontsize=11, pad=15)
ax.legend(bbox_to_anchor=(1, 1), loc='upper left', title="Carrier", frameon=False)
ax.grid(axis='x', linestyle='--', alpha=0.6)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)
fig1.tight_layout()
plt.show()
5. Short List of Sufficiency Assumptions
The cell below collects the elementary buildings hypotheses (residential and
tertiary) and exports them to the public website (website/data/buildings.js),
also printing a short summary table. Re-run it after changing any assumption so
the website stays in sync. See the Public sufficiency website section of
README.md for the data contract.
# ===== Website export: build website/data/buildings.js =====
# Numbers are read from the assumption variables defined above, so editing an
# assumption and re-running the notebook refreshes the public website.
_NB = "../notebooks/nW_BE_demand_model_buildings.html"
_H = []
def _add(*a, **k):
_H.append(make_hypothesis(*a, **k))
# --- Residential (RS) ---
_add("rs-surface", "Residential floor area per person", "Residential / sufficiency",
round(ref_RS_sur_spe, 1), round(ref_RS_sur_spe * (1 + pro_RS_sur_spe), 1),
unit="m\u00b2/person", notebook=_NB, reference="JRC-IDEES; shared living, downsizing")
_add("rs-heating", "Home space heating", "Residential / thermal",
round(ref_RS_tes_sht, 1), round(trg_RS_tes_sht * suf_RS_tes_sht, 1),
unit="kWh/m\u00b2", notebook=_NB, reference="Renovation (x2 rate) + SlowHeat (-2\u00b0C)")
_add("rs-cooling", "Home space cooling", "Residential / thermal",
round(ref_RS_tes_scl, 2), round(trg_RS_tes_scl, 2),
unit="kWh/m\u00b2", notebook=_NB, reference="Controlled AC deployment")
_add("rs-hotwater", "Domestic hot water", "Residential / thermal",
round(ref_RS_tes_shw), round(trg_RS_tes_shw), unit="kWh/person",
notebook=_NB, reference="5-min showers; sufficient use")
_add("rs-cooking", "Home cooking demand", "Residential / thermal",
round(ref_RS_tes_cok), round(trg_RS_tes_cok), unit="kWh/household",
notebook=_NB, reference="More home cooking, less processed food")
_add("rs-cooking-gas", "Gas share in home cooking", "Residential / carrier",
round(ref_RS_tes_cok_gas * 100, 1), round(trg_RS_tes_cok_gas * 100, 1),
unit="% gas", notebook=_NB, reference="Electrification of cooking")
_add("rs-dhn", "District heating (residential)", "Residential / carrier",
round(ref_RS_tes_dhn * 100, 1), round(trg_RS_tes_dhn * 100, 1),
unit="% of heat", notebook=_NB, reference="Lund et al.; EnergyVille PATHS2050")
_add("rs-fridge", "Refrigeration", "Residential / appliances",
round(ref_RS_ees_frg), round(trg_RS_ees_frg), unit="kWh/household",
notebook=_NB, reference="EU Ecodesign")
_add("rs-washing", "Washing machines", "Residential / appliances",
round(ref_RS_ees_wsh), round(trg_RS_ees_wsh), unit="kWh/household",
notebook=_NB, reference="EU Ecodesign; eco programmes")
_add("rs-dryer", "Laundry dryers", "Residential / appliances",
round(ref_RS_ees_dry), round(trg_RS_ees_dry), unit="kWh/household",
notebook=_NB, reference="Heat-pump dryers; passive drying")
_add("rs-dishwasher", "Dishwashers", "Residential / appliances",
round(ref_RS_ees_dsh), round(trg_RS_ees_dsh), unit="kWh/household",
notebook=_NB, reference="Continued deployment")
_add("rs-tv", "TV & multimedia", "Residential / appliances",
round(ref_RS_ees_tvm), round(trg_RS_ees_tvm), unit="kWh/household",
notebook=_NB, reference="EU Ecodesign")
_add("rs-ict", "ICT equipment", "Residential / appliances",
round(ref_RS_ees_ict), round(trg_RS_ees_ict), unit="kWh/household",
notebook=_NB, reference="Stabilised demand")
_add("rs-lighting", "Lighting", "Residential / appliances",
round(ref_RS_ees_lgt), round(trg_RS_ees_lgt), unit="kWh/household",
notebook=_NB, reference="LED efficiency")
_add("rs-other", "Other appliances", "Residential / appliances",
round(ref_RS_ees_oth), round(trg_RS_ees_oth), unit="kWh/household",
notebook=_NB, reference="Held constant")
# --- Tertiary (TS) ---
_add("ts-surface", "Tertiary floor area per person", "Tertiary / sufficiency",
round(ref_TS_sur_spe, 1), round(ref_TS_sur_spe * (1 + pro_TS_sur_spe), 1),
unit="m\u00b2/person", notebook=_NB, reference="Telework, space rationalisation")
_add("ts-heating", "Tertiary space heating", "Tertiary / thermal",
round(ref_TS_tes_sht, 1), round(trg_TS_tes_sht * suf_TS_tes_sht, 1),
unit="kWh/m\u00b2", notebook=_NB, reference="Renovation (x5 rate) + SlowHeat (-1\u00b0C)")
_add("ts-cooling", "Tertiary space cooling", "Tertiary / thermal",
round(ref_TS_tes_scl, 1), round(trg_TS_tes_scl, 1), unit="kWh/m\u00b2",
notebook=_NB, reference="Controlled AC deployment")
_add("ts-hotwater", "Tertiary hot water", "Tertiary / thermal",
round(ref_TS_tes_shw), round(trg_TS_tes_shw), unit="kWh/person",
notebook=_NB, reference="Contain rising trend")
_add("ts-catering", "Catering demand", "Tertiary / thermal",
round(ref_TS_tes_cat), round(trg_TS_tes_cat), unit="kWh/person",
notebook=_NB, reference="Growing out-of-home dining")
_add("ts-catering-gas", "Gas share in catering", "Tertiary / carrier",
round(ref_TS_tes_cat_gas * 100, 1), round(trg_TS_tes_cat_gas * 100, 1),
unit="% gas", notebook=_NB, reference="Electrification (some biomass kept)")
_add("ts-dhn", "District heating (tertiary)", "Tertiary / carrier",
round(ref_TS_tes_dhn * 100, 1), round(trg_TS_tes_dhn * 100, 1),
unit="% of heat", notebook=_NB, reference="Same 15% target as residential")
_add("ts-ventilation", "Ventilation", "Tertiary / electrical",
round(ref_TS_ees_vnt), round(trg_TS_ees_vnt), unit="kWh/person",
notebook=_NB, reference="Held constant")
_add("ts-streetlight", "Street lighting", "Tertiary / electrical",
round(ref_TS_ees_slt), round(trg_TS_ees_slt), unit="kWh/person",
notebook=_NB, reference="LED; less over-lighting")
_add("ts-buildinglight", "Building lighting", "Tertiary / electrical",
round(ref_TS_ees_blt), round(trg_TS_ees_blt), unit="kWh/person",
notebook=_NB, reference="Efficiency gains")
_add("ts-fridge", "Commercial refrigeration", "Tertiary / electrical",
round(ref_TS_ees_frg), round(trg_TS_ees_frg), unit="kWh/person",
notebook=_NB, reference="Door-fitted fridges")
_add("ts-misc", "Miscellaneous building tech", "Tertiary / electrical",
round(ref_TS_ees_msc), round(trg_TS_ees_msc), unit="kWh/person",
notebook=_NB, reference="Held constant")
_add("ts-ict", "Tertiary ICT & multimedia", "Tertiary / electrical",
round(ref_TS_ees_ict), round(trg_TS_ees_ict), unit="kWh/person",
notebook=_NB, reference="Growing trend")
write_hypotheses_js("buildings", _H, title="Buildings (residential & tertiary)")
# Pin buildings energy_totals overrides so CI can compare notebooks vs nW_BE.py.
_ov_path = make_energy_totals_overrides(
energy_totals_overrides_from_buildings(
df_tes_RS_tot, df_ees_RS_tot, df_tes_TS_tot, df_ees_TS_tot
),
replace_sources=["buildings"],
comment="generated from nW_BE demand-model notebooks; CI compares nW_BE.py to this file",
)
print(f"[energy_totals] wrote buildings rows to {_ov_path}")
import pandas as _pd
_pd.DataFrame([{ "id": h["id"], "hypothesis": h["name"], "category": h["category"],
"2019": h["refValue"], "2050": h["targetValue"], "unit": h["unit"] }
for h in _H])
Upstream Levers for the Interactive Workshop
The cell below exports the upstream levers used by the interactive workshop
(website/workshop/), where participants set those assumptions themselves before
the négaWatt value is revealed.
The levers live one module per topic under workshop_levers/ — residential_heat.py
and tertiary_heat.py — so the two buildings topics can be written and changed
independently. Each module reads the frames computed above and adds no assumptions
of its own.
Question wording, factual anchors and the written justifications are not here:
they live in website/workshop/content/<topic>.yaml. See docs/workshop_module.md.
# ===== Workshop export: build website/data/levers_buildings.js =====
# See docs/workshop_module.md. The levers are defined in workshop_levers/,
# one module per topic (residential-heat, tertiary-heat).
from workshop_levers import export_topics, topic_modules
_lev_path = export_topics("buildings", globals(), title="Buildings")
import pandas as _pd
_pd.DataFrame([{"topic": _m.TOPIC, "module": _m.__name__.split(".")[-1],
"levers": len(_m.build(globals())["levers"])}
for _m in topic_modules("buildings")])