Metadata-Version: 2.4
Name: open-fdd
Version: 4.4.9
Summary: Open-FDD for AI agents: RCx/FDD reports from mapped roles (local CSV, Railway or self-hosted Open-FDD, or a future vendor API), plus ECM engineering workbooks. Production FDD remains the DataFusion/GHCR stack.
Author-email: Ben Bartling <ben.bartling@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/bbartling/open-fdd
Project-URL: Documentation, https://bbartling.github.io/open-fdd/
Project-URL: Repository, https://github.com/bbartling/open-fdd
Keywords: hvac,ecm,fdd,fault-detection,energy-conservation,building-automation,open-fdd,rcx,ai-agent,reporting
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: openpyxl>=3.1.0
Provides-Extra: ecm-web
Requires-Dist: fastapi>=0.115; extra == "ecm-web"
Requires-Dist: uvicorn>=0.34; extra == "ecm-web"
Requires-Dist: pydantic<3,>=2.4; extra == "ecm-web"
Provides-Extra: oracle
Requires-Dist: pandas>=2.0.0; extra == "oracle"
Requires-Dist: numpy>=1.26.0; extra == "oracle"
Requires-Dist: pyyaml>=6.0; extra == "oracle"
Provides-Extra: reporting
Requires-Dist: open-fdd[oracle]; extra == "reporting"
Requires-Dist: python-docx>=1.1.0; extra == "reporting"
Requires-Dist: openpyxl>=3.1.0; extra == "reporting"
Requires-Dist: matplotlib>=3.8.0; extra == "reporting"
Requires-Dist: plotly>=5.22.0; extra == "reporting"
Requires-Dist: kaleido>=0.2.1; extra == "reporting"
Provides-Extra: analytics
Requires-Dist: open-fdd[oracle]; extra == "analytics"
Provides-Extra: anomaly
Requires-Dist: pandas>=2.0.0; extra == "anomaly"
Requires-Dist: numpy>=1.26.0; extra == "anomaly"
Requires-Dist: matplotlib>=3.9.0; extra == "anomaly"
Requires-Dist: statsmodels>=0.14.0; extra == "anomaly"
Requires-Dist: scikit-learn>=1.3.0; extra == "anomaly"
Provides-Extra: vibe19
Requires-Dist: open-fdd[reporting]; extra == "vibe19"
Provides-Extra: test
Requires-Dist: pytest>=7.0; extra == "test"
Requires-Dist: open-fdd[reporting]; extra == "test"
Requires-Dist: open-fdd[anomaly]; extra == "test"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: build>=1.2.0; extra == "dev"
Requires-Dist: twine>=6.0.0; extra == "dev"
Requires-Dist: open-fdd[reporting]; extra == "dev"
Requires-Dist: open-fdd[anomaly]; extra == "dev"
Provides-Extra: release
Requires-Dist: build>=1.2.0; extra == "release"
Requires-Dist: twine>=6.0.0; extra == "release"
Dynamic: license-file

# Open-FDD Python package (PyPI)

> **GitHub Pages:** this README is excluded from the docs site (`exclude: README.md`). Published overview lives at [`docs/ecm/index.md`](index.md) / [`overview.md`](overview.md) — section **PyPI agent tools**. ECM pages: https://bbartling.github.io/open-fdd/ecm/

`open-fdd` is the library AI agents install. It ships RCx/FDD reporting and ECM workbooks together.

1. **AI agent RCx/FDD report** — `open-fdd-anomaly report` writes a Typst PDF from **mapped roles**. It is not a Railway-only tool. The CLI reads a local device folder (`history_wide.csv` + `column_map.json`). The same template (`open_fdd.reporting.report_template`) accepts a role-named frame from a Railway or self-hosted Open-FDD central (the agent supplies the reader) or from a future vendor API.
2. **ECM engineering** (`open_fdd.ecm_engineering`) — agent-drivable HVAC spreadsheet workbooks + Python benchmarks. Guide: https://bbartling.github.io/open-fdd/ecm/
3. **Pandas oracle** (`open_fdd.rules`, `open_fdd.analytics`, `open_fdd.reporting`) — cookbook catalog, analytics helpers, Engineering Findings.

Works with any AI agent that can read markdown skills and run the PyPI CLI. Skills live in `openfdd_agent_spec/skills/` (not only `.cursor/`). Sync them with `./scripts/openfdd_install_agent_skills.sh --sync`.

AI agent reporting can consume FDD and analytics that DataFusion SQL already ran on an Open-FDD instance (Railway or self-hosted), or crunch the same mapped roles locally with the PyPI pandas oracle on a CSV. Either path is valid: not compute-only, and not Railway-only.

```bash
pip install "open-fdd[anomaly]" "open-fdd[reporting]"
open-fdd-anomaly report ./AHU_1 --out ./ahu1_report --month 2026-06 --compile \
  --location "AHU · ACME Office · Detroit, MI"
```

`--compile` is the only PDF path. `--title` overrides the default **Open-FDD AI Agent Report**.

The ECM API fills the same workbook input cells a human engineer would fill.
It does not replace the visible spreadsheet calculations.

**Production FDD** (DataFusion SQL fault detection) lives in the [GHCR container stack](https://bbartling.github.io/open-fdd/quick-start/docker-ghcr.html), not this wheel.

**Product freeze / upsell:** [Engineer upsell brief — open-fdd + PyPI (vibe freeze)](ENGINEER_UPSELL_BRIEF.md) — after 2026-07-30, customers see ECM via **PyPI → open-fdd**, not vibe19/vibe20 tip churn.

**Build handoff / golden example:** [OPENFDD_AGENT_ECM_HANDOFF.md](OPENFDD_AGENT_ECM_HANDOFF.md) · packaged workbook [`examples/liberty_dual_ahu/ECM_FULL_PARITY.xlsx`](../../open_fdd/ecm_engineering/examples/liberty_dual_ahu/ECM_FULL_PARITY.xlsx).

## Install

```bash
pip install open-fdd                 # ECM only (openpyxl)
pip install "open-fdd[oracle]"       # + pandas rules
pip install "open-fdd[analytics]"    # + analytics helpers (same as oracle)
pip install "open-fdd[reporting]"    # + Engineering Findings extras
pip install "open-fdd[anomaly]"      # + offline anomaly screening (STL, Isolation Forest)
```

For the FastAPI ECM example:

```bash
pip install "open-fdd[ecm-web]"
```

## Oracle rules (pandas)

```python
from open_fdd.rules import RULES, run_rule
```

## Generate a workbook in a few lines

```python
from open_fdd.ecm_engineering import ECMJob

job = (
    ECMJob("Lincoln Middle School")
    .set_global(
        area_ft2=85000,
        electric_rate=0.145,
        gas_rate=0.92,
    )
    .add_ecm(
        "static_pressure_reset",
        fan_kw=55.9,
        hours=4100,
        baseline_speed=0.82,
        proposed_speed=0.67,
    )
    .add_ecm(
        "boiler_reset",
        base_therms=48000,
        base_eff=0.86,
        prop_eff=0.92,
    )
)

# set_many / add_ecm already persist; save() is idempotent on the same path
# (BUG-OFDD-ECM-002) and can also copy to another path.
job.save("Lincoln_Middle_School_ECMs.xlsx")
```

The resulting XLSX contains the engineering inputs and formulas for human review.

### Module names vs calculators

```python
from open_fdd.ecm_engineering import list_ecm_modules, list_calculators

list_ecm_modules()   # names accepted by add_ecm (aliases included)
list_calculators()   # independent Python benchmarks (job.calc), not sheet names
```

New in **4.2.0**: `chiller_lockout`, `load_shed`, `schedule_align` / `ahu_sched_align`.

### Honesty / twin compare export (4.2.0)

```python
job.attach_twin_compare({
    "provenance": {"idf_path": "...", "g14_pass": True},
    "inputs": [{"name": "lockout_hours", "value": 612, "provenance": "FITTED_FROM_EPLUS"}],
    "measures": [{
        "measure_id": "ECM-CHILLER-LOCKOUT",
        "name": "Chiller OAT lockout",
        "eplus_source": "cascade",
        "fitted_sheet_kwh": 101580.56,
        "eplus_kwh": 101580.56,
        "hours_provenance": "FITTED_FROM_EPLUS",
    }],
    "demand": {"july_weekday_kw": 420, "july_weekend_kw": 280, "loadshed_kw": 365},
})
job.save("honesty.xlsx")  # Contents, Measures, … (FITTED ≠ independent validation)
```

### IPMVP change-point / G14 (4.4.3)

```python
from open_fdd.ecm_engineering import fit_changepoint, score_g14_monthly, option_c_savings
```

Docs: [IPMVP change-point & G14](ipmvp-changepoint.md). Requires `numpy` (`pip install "open-fdd[oracle]"`).

## Independent benchmark

```python
from open_fdd.ecm_engineering import ECMJob

job = ECMJob("demo")
result = job.calc(
    "fan_affinity",
    design_kw=55.9,
    hours=4100,
    baseline_speed_fraction=0.82,
    proposed_speed_fraction=0.67,
)
```

## CLI

```bash
open-fdd-ecm calculators
open-fdd-ecm demo --out Demo_ECMs.xlsx
```

### Anomaly screening

Offline AHU IO screen of a device folder (`history_wide.csv` + `column_map.json`).
Z-score and MAD are SQL-portable rolling stats. STL and Isolation Forest are
Python-only (`open-fdd[anomaly]`).

```bash
open-fdd-anomaly screen ./AHU_1 --out ./anomaly_out
# defaults: --top-n 5 --max-days 10 --methods zscore,mad,stl,iforest

# Single AHU FDD/RCx PDF. Only this CLI; no build_april_report.py or other local runner.
# History is mapped roles: local CSV+map, Railway or self-hosted Open-FDD, or a future vendor API.
# --compile writes report.pdf when typst is on PATH.
# --month filters every plot. --week pins the RCx window (YYYY-MM-DD, 7 days).
# Web OAT: mapped column, CSV (web_oa_t reindexed onto the BAS clock), or fetch.
# Economizer scatter: x = OAT−RAT, y = MAT−RAT, bottom-left quadrant only.
# build_economizer_delta_points (alias economizer_delta_frame).
open-fdd-anomaly report ./AHU_1 --out ./ahu1_report --month 2026-06 --compile
open-fdd-anomaly report ./AHU_1 --out ./ahu1_report --month 2026-06 --web-oat ./weather.csv --compile
# April folder (8640 rows @ 5 min). Do not fetch this in CI.
open-fdd-anomaly report ./AHU_1 --out ./april_report --month 2026-04 \
  --web-oat ./open_meteo_april.csv --week 2026-04-06 --compile
# building folder of device subfolders:
open-fdd-anomaly report ./BUILDING --scope building --out ./bldg_report --compile
```

## Engineering posture

Prefer measured BAS, utility, TAB, nameplate and manufacturer data over defaults.
Generic chiller `%/°F` methods are screening proxies; manufacturer performance
maps or calibrated EnergyPlus should replace them when stronger estimates are needed.
