Skip to main content

Thermal comfort prediction API — PMV, adaptive ASHRAE 55, EN 16798 — with uncertainty bounds and a deployable REST endpoint

Project description

comfortkit

PyPI Python License CI

A Python library and REST API for thermal comfort prediction with uncertainty bounds and personalised comfort learning. Wraps pythermalcomfort as the computational backend and adds what it doesn't have: a deployable REST endpoint, bootstrap and conformal confidence intervals on every prediction, an ML correction layer trained on real occupant votes, and a per-user Bayesian model that adapts from feedback. For interactive, point-and-click comfort analysis the CBE Thermal Comfort Tool remains the reference — comfortkit is its programmatic complement, built for automated pipelines, sensor integrations, and applications that need to call predictions at scale.


Quick start

pip install comfortkit
from comfortkit import predict

result = predict(
    model="pmv",
    ta=22.0,   # air temperature, °C
    tr=22.0,   # mean radiant temperature, °C
    vel=0.1,   # air velocity, m/s
    rh=60.0,   # relative humidity, %
    met=1.2,   # metabolic rate, met
    clo=0.5,   # clothing insulation, clo
)

print(result.pmv)        # -0.75
print(result.ppd)        # 16.9
print(result.sensation)  # "slightly cool"
print(result.ci_95)      # e.g. (-1.4, -0.2)  — varies; bootstrap CI is stochastic
print(result.compliant)  # False
print(result.warnings)   # []

The ci_95 field is a bootstrap confidence interval on PMV — accounting for typical sensor measurement uncertainty (±0.5°C air temp, ±2°C radiant temp, ±5% RH, ±0.05 m/s velocity). Pass your own sigma values via predict(..., sigma={...}) to match your actual sensor spec.


REST API

docker compose up
curl -X POST http://localhost:8000/v1/predict \
  -H "Content-Type: application/json" \
  -d '{
    "model": "pmv",
    "ta": 22.0,
    "tr": 22.0,
    "vel": 0.1,
    "rh": 60.0,
    "met": 1.2,
    "clo": 0.5
  }'
{
  "pmv": -0.75,
  "ppd": 16.9,
  "sensation": "slightly cool",
  "compliant": false,
  "model": "standard_pmv",
  "ci_95": [-1.47, -0.22],
  "warnings": []
}

Interactive API docs at http://localhost:8000/docs (OpenAPI, auto-generated).


Why comfortkit?

pythermalcomfort is the best open implementation of thermal comfort physics. comfortkit does not replace it — it builds on top of it:

Feature pythermalcomfort comfortkit
PMV/PPD, adaptive, UTCI, SET Yes Via adapter
Uncertainty / confidence intervals No Yes — bootstrap CI (sensor noise) + conformal CI (data-grounded, 95.3% empirical coverage)
REST API + Docker No Yes — FastAPI, OpenAPI docs
Pydantic v2 typed interface No Yes
Applicability warnings (non-fatal) Raises / returns nan Structured warnings list
Batch predictions Yes Yes
ML correction layer No Yes (v0.2) — LightGBM residual model, ONNX
Personalised comfort No Yes (v0.3) — per-user Bayesian update from occupant votes

Supported standards

Standard Model key Notes
ISO 7730 pmv / standard="iso" PMV/PPD via pmv_ppd_iso
ASHRAE 55 pmv / standard="ashrae" PMV/PPD via pmv_ppd_ashrae
ASHRAE 55 SET set Standard Effective Temperature; result in set_tmp
ASHRAE 55 adaptive adaptive_ashrae (alias adaptive) Requires t_running_mean; includes CE fix (see below)
EN 16798-1 Coming v1.0 Adaptive model

A note on numerical differences with the CBE Thermal Comfort Tool. The CBE Thermal Comfort Tool pins pythermalcomfort==2.10, while comfortkit targets pythermalcomfort>=3.9. Small PMV differences — typically less than 0.1 — are therefore expected for the same inputs. Neither result is wrong; they reflect different versions of the same underlying library. If you need comfortkit's output to match the CBE tool exactly for validation purposes, install pythermalcomfort==2.10 in an isolated environment and compare directly against that version.

A note on ISO vs ASHRAE divergence. The two standards produce different PMV values for the same inputs whenever air velocity is non-negligible, because they apply different relative air velocity adjustments before computing the convective heat loss term. For example, ISO 7730 Annex D case C (ta=27, tr=27, vel=0.3) yields PMV = +0.43 under ISO and PMV = +0.23 under ASHRAE — a difference of 0.20 PMV units. This is expected behaviour, not a bug. comfortkit faithfully reproduces pythermalcomfort's implementation of both standards; if you see this divergence, pass standard="iso" or standard="ashrae" explicitly to make the intended calculation clear.


Uncertainty quantification

Every PMV prediction carries two complementary confidence intervals:

Bootstrap CI (ci_95) — parametric bootstrap over sensor measurement noise. Reflects how much PMV would shift if your sensors were reading slightly off. Width is typically ±0.5–0.8 PMV units under standard sensor tolerances.

Conformal CI (conformal_ci_95) — split conformal prediction interval calibrated on real occupant TSV votes from the ASHRAE Global Thermal Comfort Database II. Answers the question "how far might the actual occupant vote deviate from the PMV prediction?" — a harder, more honest uncertainty bound. Half-width q=2.77, empirical test-set coverage 95.3%.

result = predict(model="pmv", ta=22.0, tr=22.0, vel=0.1, rh=60.0, met=1.2, clo=0.5)

print(result.ci_95)           # e.g. (-1.4, -0.2)  — sensor-noise bootstrap
print(result.conformal_ci_95) # e.g. (-3.5, 1.8)   — data-grounded, TSV vs PMV

For custom sensor uncertainty, use monte_carlo_propagate directly:

from comfortkit.uncertainty import monte_carlo_propagate
from comfortkit.models.base import ComfortInputs

inputs = ComfortInputs(ta=24, tr=25, vel=0.1, rh=50, met=1.2, clo=0.7)

ci = monte_carlo_propagate(
    inputs,
    sigma={"ta": 0.3, "tr": 1.0, "vel": 0.02, "rh": 3.0},
    n_samples=5000,
)
print(ci)  # (lower_pmv, upper_pmv)

ML correction layer

model="ml" applies a LightGBM residual correction on top of standard PMV, trained on the ASHRAE Global Thermal Comfort Database II (~78 k field observations across 41 studies; Földváry Ličina et al., 2018). The model predicts the (TSV − PMV) residual and adds it back to the physics estimate.

from comfortkit import predict

result = predict(
    model="ml",
    ta=22.0,
    tr=22.0,
    vel=0.1,
    rh=60.0,
    met=1.2,
    clo=0.5,
    # optional — improve accuracy when available
    t_out=8.0,                         # outdoor monthly mean temp (°C)
    cooling_strategy="Naturally Ventilated",
    building_type="Office",
)

print(result.pmv)     # -0.62
print(result.ppd)     # 13.7
print(result.model)   # "ml_pmv"
print(result.ci_95)   # (-1.34, -0.10)

The three optional fields — t_out, cooling_strategy, building_type — capture adaptive-comfort effects that the standard PMV formula ignores. They are optional: if omitted, the model falls back to dataset medians / unknown-category encodings. The correction is served via ONNX Runtime with no LightGBM dependency at inference time.


Personalised comfort (v0.3)

POST /v1/feedback accepts an occupant thermal preference vote and updates a per-user Beta-Bernoulli posterior (Dirichlet-Multinomial, K=2) warm-started from the aggregate prior. GET /v1/comfort/{user_id} returns the current personalised prediction.

# Submit a vote: this occupant wants it Cooler
curl -X POST http://localhost:8000/v1/feedback \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "desk-42",
    "vote": 1,
    "heart_rate": 74.0,
    "t_net": 27.5,
    "rh_net": 65.0,
    "clothing": "Light",
    "met": "sitting"
  }'
{
  "user_id": "desk-42",
  "n_votes": 1,
  "p_cooler": 0.4907,
  "prediction": 0,
  "prediction_label": "Not Cooler"
}
# Retrieve the personalised prediction after enough votes have accumulated
curl http://localhost:8000/v1/comfort/desk-42
{
  "user_id": "desk-42",
  "p_cooler": 0.8731,
  "prediction": 1,
  "prediction_label": "Cooler",
  "n_votes": 47,
  "confidence": "medium"
}

The prior is weak (5 pseudo-counts from the Dorn study aggregate). Roughly 10–20 votes are enough to pull the posterior away from the global mean; by 50 votes, users with atypical preferences are predicted correctly >90% of the time. Contextual features (heart_rate, t_net, rh_net, clothing, met) are stored in the audit log for future feature-conditioned extensions.

Vote data is stored in SQLite by default (COMFORTKIT_DB env var to override). See CONTRIBUTING.md for the Postgres migration path before production deployment.


Roadmap

Version Scope
v0.1 PMV adapter + bootstrap CI + FastAPI REST + PyPI ✓
v0.2 ML correction layer trained on ASHRAE Global Thermal Comfort DB II + ONNX export ✓
v0.2.1 SET adapter + adaptive ASHRAE 55 adapter + CE bug fix (pythermalcomfort 3.9.2) ✓
v0.2.2 Conformal prediction CI calibrated on real TSV votes; q=2.77, 95.3% coverage ✓
v0.3 Personalised comfort — Bayesian update from occupant votes via /v1/feedback
v1.0 Stable API contract, full docs site, Zenodo DOI

Contributing

See CONTRIBUTING.md. Good first issues are labelled good-first-issue.

We especially welcome: additional model adapters (EN 16798 adaptive, UTCI), BMS/sensor platform integrations, and occupant comfort survey datasets for the v0.2 ML layer.


Citation

If you use comfortkit in research, please cite:

@software{comfortkit,
  author  = {Kottapalli, Kalyan},
  title   = {comfortkit: Thermal comfort prediction with uncertainty bounds and REST API},
  year    = {2026},
  url     = {https://github.com/K2alyan/comfortkit},
}

A Zenodo DOI will be registered at v1.0.


Data & Acknowledgements

ASHRAE Global Thermal Comfort Database II

Used to train the v0.2 ML correction layer (~78 k field observations, 41 studies). If you use model="ml" in your work, please cite:

Földváry Ličina, V., Cheung, T., Zhang, H., de Dear, R., Parkinson, T., Arens, E., & Schiavon, S. (2018). Development of the ASHRAE Global Thermal Comfort Database II. Building and Environment, 142, 502–512. https://doi.org/10.1016/j.buildenv.2018.06.022

Data: https://www.kaggle.com/datasets/claytonmiller/ashrae-global-thermal-comfort-database-ii

Dorn longitudinal study

Used to derive the warm-start prior for the v0.3 personalised comfort model (20 users, Singapore, Apr–Nov 2020) and as a reference dataset for the v0.4 zone aggregation validation. If you use POST /v1/feedback or ZoneAggregator in your work, please cite:

Tartarini, F., Schiavon, S., Quintana, M., & Miller, C. (2022). Indoor microclimate and thermal comfort of a Singaporean hawker centre and office building: A longitudinal study. Indoor Air, 32, e13160. https://doi.org/10.1111/ina.13160

Code: https://github.com/FedericoTartarini/dorn-longitudinal-tc-study

ENTH longitudinal dataset

Used for the v0.4 zone aggregation proof-of-concept validation (17 participants, 3 NUS SDE buildings, Mar–Apr 2021). If you use or extend the zone aggregation validation in your work, please cite:

Quintana, M., Abdelrahman, M., Frei, M., Tartarini, F., & Miller, C. (2021). Cohort comfort models — Personal thermal comfort models using a smart footwear system in an open-plan office. SenSys 2021, pp. 556–559. https://doi.org/10.1145/3485730.3493693

Data: https://zenodo.org/record/5502441

pythermalcomfort

comfortkit uses pythermalcomfort as its computational backend for all physics models (PMV/PPD, adaptive ASHRAE 55, SET, UTCI). If comfortkit is useful to you, consider starring that project too. If you publish results from any physics-based model, please cite:

Tartarini, F., & Schiavon, S. (2020). pythermalcomfort: A Python package for thermal comfort research. SoftwareX, 12, 100578. https://doi.org/10.1016/j.softx.2020.100578


License

MIT © Kalyan Kottapalli

Project details


Download files

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

Source Distribution

comfortkit-0.5.0.tar.gz (114.9 kB view details)

Uploaded Source

Built Distribution

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

comfortkit-0.5.0-py3-none-any.whl (78.9 kB view details)

Uploaded Python 3

File details

Details for the file comfortkit-0.5.0.tar.gz.

File metadata

  • Download URL: comfortkit-0.5.0.tar.gz
  • Upload date:
  • Size: 114.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for comfortkit-0.5.0.tar.gz
Algorithm Hash digest
SHA256 bb225a977285b9f584f4824548f7f72379ab4521dd6904491e2a6497328ccd5a
MD5 30a0d8c518f21898d083e1771e930ba7
BLAKE2b-256 949bc8325b7bbf23b7e531f22e90ed35eb2a08af6e7290a32dfcddb510bad8be

See more details on using hashes here.

Provenance

The following attestation bundles were made for comfortkit-0.5.0.tar.gz:

Publisher: publish.yml on K2alyan/comfortkit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file comfortkit-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: comfortkit-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 78.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for comfortkit-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e883dc7b8179bf21c844593a947959c34c99156041179b174129f3365467e6a2
MD5 26bf844851a678216f4d990b869d2ea8
BLAKE2b-256 11869dfdeb8689bac61c7cb31a6e4dbd68e9687c1621fa46cf1c3a72b9e779b6

See more details on using hashes here.

Provenance

The following attestation bundles were made for comfortkit-0.5.0-py3-none-any.whl:

Publisher: publish.yml on K2alyan/comfortkit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page