Skip to main content

buildingcalibration

District-scale static calibration, validation and representative-district clustering for the building-energy model family.

It covers the annual (non-hourly) half of the modelling chain:

  • Calibration — the annual static calibration loop that fits a building stock's energy model over buildingmodel's static inference.
  • Validation — the Eq. 5 error decomposition of simulated versus measured district consumption (ORE / Enedis), read through buildingdata.
  • Clustering — reduction of a national building stock to representative districts, plus screening of districts whose measured data cannot support calibration.
  • Plots — the figures for both stages.

Status

0.1.0 is released (tagged and on PyPI): the modules extracted from building_eload's core — core/static_simulation, core/validation, core/clustering.py, core/unreliable_districts.py, core/building_loader.py, utils/district_list.py, plots/static_calibration.py, plots/validation.py. Work since then is listed under [Unreleased] in the CHANGELOG.

Quick start

Calibrate one district (static, annual)

from buildingcalibration import StaticParameters, StaticSimulation

parameters = StaticParameters(
    n_inference=5,            # stochastic building-stock draws to average over
    calibration_year=2023,
    climate_year=2023,
    output_root="results",    # -> results/static_simulation/2023/
)
results = StaticSimulation("751010101", parameters).run()
results.save_results()

Nothing else is needed: the building footprints (BDTOPO), the IRIS district layer and the ORE per-IRIS annual consumption the calibration fits against are all fetched through buildingdata. Pass calibration_file= (a frame or a path) to pin a vintage, or building_footprint_folder= to use a local BDTOPO mirror.

Validate reconstructed load curves against measurements

from buildingcalibration import Validation

validation = Validation(
    year=2023,
    n_clusters=20,
    scope="national",
    building_type="residential",
    input_path="results/dynamic_simulation/2023",          # hourly parquets
    output_path="results/validation/2023",
    clustering_file=clustering_frame,                      # frame or path
    unreliable_districts=unreliable_iris_frame,            # frame, ids or path
)
validation.run()
error = validation.get_error()                             # paper Eq. 5 terms

The measured Enedis load curves come from buildingdata (get_enedis_national() / get_enedis_regional()) when validation_residential_file= is left unset.

Reduce a stock to representative districts

from buildingcalibration import cluster_districts
from buildingcalibration.clustering import screen_unreliable_iris_from_parquet

unreliable = screen_unreliable_iris_from_parquet("enedis_iris_consumption.parquet")

result = cluster_districts(
    district_data,                                  # one row per district
    n_clusters=20,
    feature_columns=["heating_needs", "dhw_needs", "specific_needs"],
    unreliable_ids=unreliable["code_iris"],         # never pick these as medoids
    seed=42,
)
result.medoid_ids        # the districts to simulate
result.scaling_weights   # count-based multiplier per medoid

Publish the representative-districts table the rest of the chain reads

cluster_districts() answers one cut at a time; the validation stage and the medoid selectors read a single wide table holding several cuts side by side (cluster_label_<key> / cluster_medoid_<key> per cut, plus the district attributes). build_representative_districts() is the converter and write_representative_districts() the thin writer:

from buildingcalibration import write_representative_districts
from buildingcalibration.clustering import (
    cluster_districts,
    cluster_key_from_fraction,
    representative_districts_file,
)

results = {n: cluster_districts(features, n_clusters=n, seed=42) for n in (10, 20)}

write_representative_districts(
    results,
    features,                                        # one row per district
    representative_districts_file("results/clustering", scope="national"),
)

Key convention — the column suffix is always a positive integer: the cluster count for a national table (cluster_medoid_20), the integer percent of districts kept for a regional one (cluster_medoid_5 is 5 %). Fractions are rejected, not guessed: convert once with cluster_key_from_fraction(0.05) -> 5. representative_districts_file() is the single definition of the conventional file name, shared with the readers (get_representative_districts(), Validation(clustering_path=...)), so a table can never be written under one name and looked up under another.

Sweep a parameter and decompose the error per variant

from buildingcalibration import average_over_years, run_validation_sweep

frame = run_validation_sweep(
    {
        f"heating_threshold={value:g}": [
            {"year": year, "input_path": f"results/dynamic/{value:g}/{year}"}
            for year in (2021, 2022, 2023)
        ]
        for value in (14.0, 15.0, 16.0)
    },
    common={"n_clusters": 20, "scope": "national", "clustering_file": table,
            "unreliable_districts": unreliable},
)
# frame: long, one row per (label, year, metric) -- the full Eq. 5 terms
average_over_years(frame)   # one row per (label, metric)

Each case carries its own inputs (frames or explicit paths); the sweep invents no path and writes nothing.

Doctrine: frames first, no path registry

Nothing in this package resolves a filesystem path at import time, and there is no data/ tree to install:

  • Pipeline intermediates — dynamic-simulation results, clustering tables, the unreliable-district list, output directories — are passed in as in-memory frames or as explicit paths, and have no default. A missing one raises a ValueError naming the parameter rather than reading from somewhere you did not choose.
  • External open data — BDTOPO footprints, IRIS districts, ORE annual consumption, Enedis measured load curves — defaults to a buildingdata getter (get_bdtopo(), get_districts(), get_ore(), get_enedis_national() / get_enedis_regional()), which owns the download, the cache and the vintage. Pass a frame or a path to pin a specific vintage instead.

The one exception is the SDES parc résidentiel workbook read by plots.validation.read_sdes_data(): buildingdata has no getter for it yet, so it is a required frame-or-path argument (and reading the .xlsx form needs openpyxl, which is not a declared dependency).

Relationship to building_eload

buildingcalibration and building_eload have no import relationship in either direction. building_eload becomes a pure hourly dynamic-simulation library; this package owns the static/annual side. The two are coupled only by parquet data contracts on disk — a calibrated stock written here is read there, and vice versa. That seam already existed inside the old monolith; the split just makes it a package boundary. A structural test (tests/test_data_path_doctrine.py) enforces both halves of that: no path registry, and no building_eload import anywhere in the package.

The family

Package Role Host
buildingdata dataset access layer: BDTOPO/WFS, ERA5, INSEE census, Enedis/ORE, ELMAS gitlab.com/energytransition
buildingmodel static inference engine: building-stock physical characteristics and annual demand gitlab.com/energytransition
heatpumpmodel shared heat-pump seasonal-performance physics (Rogeau et al. 2024) git.persee
buildingcalibration static calibration, validation, representative-district clustering git.persee
building_eload hourly dynamic simulation of district electric load git.persee
building_eload_paper Snakemake reproduction workflow for the published paper; pinned to building_eload==0.4.3 and unaffected by this split git.persee

Install

pip install buildingcalibration
# or, from a checkout:
pip install -e ".[dev]"

Python 3.10–3.13.

Tests

pytest                       # full suite
pytest -m "not integration"  # hermetic subset, what CI runs

The hermetic subset needs no data and no network. The integration tests run the calibration and validation stages on real districts; they read a local reference-data tree, <repo>/data by default, overridable with the BUILDING_ELOAD_DATA environment variable (the name is shared with building_eload on purpose, so one setting covers both checkouts).

One integration test — tests/integration/test_static_to_dynamic_seam.py, the cross-package end-to-end seam — additionally needs building_eload installed. It is an optional test-time requirement only: building_eload is not a runtime dependency, not part of the dev extra, and the library itself must never import it (a structural test enforces that). Install it yourself if you want to run that test; otherwise it skips.

Licence

MIT — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

buildingcalibration-0.2.0.tar.gz (97.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

buildingcalibration-0.2.0-py3-none-any.whl (110.8 kB view details)

Uploaded Python 3

File details

Details for the file buildingcalibration-0.2.0.tar.gz.

File metadata

  • Download URL: buildingcalibration-0.2.0.tar.gz
  • Upload date:
  • Size: 97.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.15

File hashes

Hashes for buildingcalibration-0.2.0.tar.gz
Algorithm Hash digest
SHA256 972f36493f1b27e4d726e2c3119788bfbb6772ba8c013a111557ebe83f66fb60
MD5 20efa5e330f0dba09682c6ed001b30b5
BLAKE2b-256 01346d99ce4092a931e01062069c5ebe111bc90365e336b1d24c1cebbb5fd3eb

See more details on using hashes here.

File details

Details for the file buildingcalibration-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for buildingcalibration-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 80c3defadea790542dbb012d68aa107d5f049b8fa7caa5f5f0bdfa0c1c3e1939
MD5 13ddfa0cdb045750a812e0645b1052e4
BLAKE2b-256 be88b8536a902fa6800c07174534ca3fb2ff797a84737eee9387122c710ad362

See more details on using hashes here.

Release history Release notifications | RSS feed

0.2.2

2 files

This release

0.2.0 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page