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.


Acknowledgements

comfortkit uses pythermalcomfort as its computational backend. If comfortkit is useful to you, consider starring that project too.

The v0.2 ML correction layer was trained on the ASHRAE Global Thermal Comfort Database II. If you use model="ml" in your work, please cite the original data paper:

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


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.4.0.tar.gz (110.8 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.4.0-py3-none-any.whl (77.8 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for comfortkit-0.4.0.tar.gz
Algorithm Hash digest
SHA256 268727e60bc921294c2f00ca517eb43c67d9c41b7e90113604e4edde7126a288
MD5 828afd4bf390862b294050f26efa1a59
BLAKE2b-256 39d096a592d48e18fad41795cad31eabcbe9f104a025738ec769e003e54d774d

See more details on using hashes here.

Provenance

The following attestation bundles were made for comfortkit-0.4.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.4.0-py3-none-any.whl.

File metadata

  • Download URL: comfortkit-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 77.8 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.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 bc654ce930f3b449508b80f782b7c16cc3281c9df2ba3b8eddba20ef80503751
MD5 554b8e1b630847baa4fddc5e771664c6
BLAKE2b-256 bf2b644d3476ed6560432b2e82e23669d0b18d6594153a66519a3f38bab149c1

See more details on using hashes here.

Provenance

The following attestation bundles were made for comfortkit-0.4.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