Ventilation verification — ASHRAE 62.1 VRP & DCV
camber.ventilation does the explicit code-rate ventilation check that complements the CO₂
proxy in camber.iaq and the OA-fraction diagnostic in camber.oafraction.
- Ventilation Rate Procedure (VRP) — is the delivered outdoor air at least the ASHRAE 62.1 requirement for the zone?
- Demand-Controlled Ventilation (DCV) — does the outdoor air actually modulate with occupancy/CO₂, or is it static (DCV not working)?
flowchart TD
frame["role-frame (OA airflow, CO2, occupancy)"] --> vrp["assess_62_1 (VRP)"]
frame --> dcv["assess_dcv (DCV)"]
rates["OA_RATES_62_1 (Table 6.1)"] --> req["required_oa_cfm (Voz)"]
req --> vrp
vrp --> vstatus["status: under / adequate / over"]
dcv --> dstatus["status: functioning / static / uncorrelated"]
vstatus --> rule1["ventilation_rate_62_1 rule"]
dstatus --> rule2["dcv_verification rule"]
rule1 --> finding["Finding (warn / fault)"]
rule2 --> finding
Two independent checks: is enough OA delivered (VRP), and does OA modulate with demand (DCV).
The VRP requirement
ASHRAE 62.1 sets the zone outdoor-air requirement as
Vbz = Rp·Pz + Ra·Az (people term + area term)
Voz = Vbz / Ez (corrected for air-distribution effectiveness)
required_oa_cfm(area_sqft, population, *, rp, ra, ez=1.0) computes Voz. The Rp/Ra rates
come from OA_RATES_62_1 (62.1 Table 6.1, a public-standard subset) via oa_rates_for(space_type),
or you pass them explicitly.
from camber.ventilation import assess_62_1
# a 2,000 ft² office for 10 people, metering ~120 cfm OA over occupied hours
r = assess_62_1(
oa_cfm_series,
area_sqft=2000,
population=10,
space_type="office",
occupied_mask=occ,
aggregate="median",
)
r.required_cfm # 170.0 (5·10 + 0.06·2000)
r.status # "under" -> deficit 50 cfm
measured_oa_cfm may be a scalar or a time series; a series is filtered by occupied_mask and
reduced by aggregate. Status is under (ratio < under_tol), over (ratio > over_factor,
an energy penalty), or adequate.
Option flags — assess_62_1
| flag | default | effect |
|---|---|---|
space_type |
— | look up Rp/Ra from the 62.1 table |
rp, ra |
from table | override the people / area rates |
ez |
1.0 |
zone air-distribution effectiveness (raises the requirement when < 1) |
aggregate |
"median" |
reduce a series: median/mean/min/p05/p95 |
occupied_mask |
None |
restrict a series to occupied hours |
under_tol |
0.9 |
flag under below this fraction of required |
over_factor |
1.5 |
flag over above this multiple of required |
DCV verification
from camber.ventilation import assess_dcv
res = assess_dcv(oa_signal, co2_series, occupied_mask=occ, co2_setpoint=1000)
res.status # "functioning" | "static" | "uncorrelated" | "insufficient"
res.modulation # (max-min)/max of the OA signal
res.correlation # OA vs demand (DCV -> positive)
oa_signal can be OA flow, OA fraction, or OA-damper position; demand_signal is CO₂ or
occupancy. static = OA doesn't modulate (fixed OA / DCV off); uncorrelated = it modulates
but not with demand. With a co2_setpoint, co2_breach_at_min_pct reports under-ventilation that
persists while OA is pinned at its minimum.
Option flags — assess_dcv
| flag | default | effect |
|---|---|---|
min_corr |
0.3 |
min OA↔demand correlation to call DCV "functioning" |
min_modulation |
0.1 |
min OA range (max-min)/max; below ⇒ "static" |
co2_setpoint |
None |
also flag CO₂ breaching this while OA is at minimum |
occupied_mask |
None |
restrict to occupied hours |
Rules
dcv_verification(DemandControlledVentilation) — config-free, auto-registered. Uses OA airflow (Role.OA_AIRFLOW) or OA-damper position, and CO₂ or occupancy as demand;warnon static/uncorrelated,faultwhen CO₂ breaches setpoint at minimum OA. Construction flags:min_corr,min_modulation,co2_setpoint,occupied_only.ventilation_rate_62_1(VentilationRateProcedure) — needs the zone's design inputs, so it's instantiated explicitly (not auto-registered):
from camber.rules.ventilation_rule import VentilationRateProcedure
rule = VentilationRateProcedure(area_sqft=2000, population=10, space_type="office")
finding = rule.analyze("AHU-1", role_frame) # fault on under-ventilation
Scope
This verifies the rate from trends; it is not a substitute for a stamped 62.1 ventilation calculation. The Table 6.1 defaults are a convenience — confirm the zone's category and design population against the project's mechanical schedule.