Demandlib Heating Model Example

This notebook demonstrates how to use Demandlib space heating model to simulate heating loads for multiple buildings.

Imports

Import required libraries and set visualization defaults.

import os

import pandas as pd
import matplotlib.pyplot as plt

from entise.core.generator import Generator
from entise.constants import Types
from entise.constants import Columns as Cols

Load Data

We load building parameters from objects.csv and simulation data from the data folder.

# Load data
cwd = "."  # notebook runs inside examples/heat_demandlib

objects = pd.read_csv(os.path.join(cwd, "objects.csv"))

data = {}
common_data_folder = "../common_data"

for file in os.listdir(os.path.join(cwd, common_data_folder)):
    if file.endswith(".csv"):
        name = file.split(".")[0]
        data[name] = pd.read_csv(
            os.path.join(cwd, common_data_folder, file),
            parse_dates=True,
        )

print("Loaded data keys:", list(data.keys()))
Loaded data keys: ['validation_weather', 'weather']

Instantiate and Configure Model

Initialize the time series generator and configure it with building objects.

gen = Generator()
gen.add_objects(objects)

summary, df = gen.generate(data, workers=1)
100%|██████████| 10/10 [00:01<00:00,  6.43it/s]

Results Summary

Below is a summary of the annual heating demands (in kWh/a) and peak loads (W).

print("Summary:")
summary_kwh = (summary / 1000).round(0).astype(int)
summary_kwh.rename(columns=lambda x: x.replace("[W]", "[kW]").replace("[Wh]", "[kWh]"), inplace=True)
print(summary_kwh.to_string())
Summary:
    heating:demand[kWh]  heating:load_max[kW]
1                  5578                     2
2                  1701                     1
3                  1073                     0
4                 11792                     5
5                  3487                     1
6                 28220                    12
7                 23140                    10
8                  5180                     2
9                 23180                    10
10                10614                     4

Preparation of Data

# Define the building ID to process and visualize
building_id = summary.index[0]  # Change index to visualize different buildings

# Visualize results for the processed building
# Note: We're using the same building_id as defined above
building_data = df[building_id][Types.HEATING]

Visualization of Results

Visualize heatingloads for a selected building.

def plot_heating_loads():
    """Heating and Cooling Loads"""
    fig, ax = plt.subplots(figsize=(14, 5))
    heating_MWh = summary.loc[building_id, "heating:demand[Wh]"] / 1e6
    (line1,) = ax.plot(
        pd.to_datetime(building_data.index, utc=True),
        building_data["heating:load[W]"],
        label=f"Heating: {heating_MWh:.1f} MWh",
        color="tab:red",
        alpha=0.8,
    )
    # Create the combined legend in the upper left corner
    ax.set_ylabel("Load (W)")
    ax.set_title(f"Building ID: {building_id} - Heating Loads")
    ax.legend()
    ax.grid(True)
    plt.tight_layout()
    plt.show()
plot_heating_loads()
../../_images/f76a5511e7459bd3880f4a475ac524ac6f57b2f2fcd7242f399f173ac0eb2863.png

Outdoor Temperature with Heating Loads

Next, we visualize the outdoor temperature alongside the heating loads to see how they correlate.

def plot_outdoor_temp_with_loads():
    """Outdoor Temperature with Heating & Cooling Loads"""
    fig, ax1 = plt.subplots(figsize=(15, 6))

    # Plot outdoor temperature on left y-axis
    air_temp = data["weather"][f"{Cols.TEMP_AIR}@2m"]
    ax1.plot(
        pd.to_datetime(building_data.index, utc=True), air_temp, label="Outdoor Temp", color="tab:cyan", alpha=0.7
    )

    ax1.set_ylabel("Outdoor Temp (°C)")
    ax1.set_ylim(air_temp.min().round() - 2, air_temp.max().round() + 2)

    # Create second y-axis for loads
    ax2 = ax1.twinx()
    ax2.plot(
        pd.to_datetime(building_data.index, utc=True),
        building_data["heating:load[W]"],
        label="Heating Load",
        color="tab:red",
        alpha=0.8,
    )
    ax2.set_ylabel("HVAC Load (W)")
    ax2.set_ylim(
        building_data["heating:load[W]"].min() * 1.1,
        building_data["heating:load[W]"].max() * 1.1,
    )

    # Combine legends from both axes
    lines1, labels1 = ax1.get_legend_handles_labels()
    lines2, labels2 = ax2.get_legend_handles_labels()
    ax1.legend(lines1 + lines2, labels1 + labels2, loc="upper left")

    ax1.set_title(f"Building ID: {building_id} - Outdoor Temp & Heating Loads")
    ax1.grid(True)
    fig.tight_layout()
    plt.show()
plot_outdoor_temp_with_loads()
../../_images/f0c93f50e842a5a34ff3e067185320be0fd726f7c26281cd45e4da812adfdb8b.png