2018 · Problem B — Cozy Smart House
Optimization Thermal modeling Control / schedule SensitivityThe prompt, restated
A homeowner is installing a "smart" climate-control system in a single-family house. The system can program heating, cooling, ventilation, blinds, and dehumidification on a per-hour schedule, learning from weather forecasts, electricity tariff schedules, occupancy patterns (everyone out 9–5 weekdays), and outdoor air-quality data (pollen, smoke). The homeowner wants the schedule to be optimal across four often-competing axes: thermal comfort, energy cost, indoor air quality, and equipment wear.
The team is asked to (1) build a thermal model of a representative home (walls, windows, internal mass), (2) define a multi-objective "comfort + cost + air quality" loss, (3) design a schedule (HVAC setpoint per hour, blinds open/closed, ventilation on/off) that minimizes that loss for a typical summer week and a typical winter week, (4) extend the schedule to a full year with a small number of seasonal "modes", and (5) write a one-page user-facing explainer for the homeowner.
Key modeling idea
This is optimization-over-time on a small thermal model. Indoor temperature $T_\text{in}(t)$ evolves under a first-order ODE driven by outdoor temperature, solar gain, and HVAC heat injection. The controller picks an HVAC schedule that minimizes a weighted sum of (comfort error)² + (electricity cost) + (CO₂ / pollen exceedance). This is a small LP or quadratic program if linearized, or a DP if discretized.
Suggested approach
- Step 1 — Lumped-RC thermal model. $C \dfrac{dT_\text{in}}{dt} = \dfrac{T_\text{out} - T_\text{in}}{R} + q_\text{solar} + q_\text{HVAC}$. Calibrate $R$ and $C$ against a published reference home (e.g., DOE ResStock).
- Step 2 — Loss function. $L = \alpha \cdot (T_\text{in} - T_\text{set})^2 + \beta \cdot \text{price}(t) \cdot |q_\text{HVAC}| + \gamma \cdot \text{AQ\_penalty}$. Use AHP-style weights $\alpha, \beta, \gamma$ from the homeowner.
- Step 3 — Schedule as LP. Hourly $q_\text{HVAC}(t)$ as decision variable; linearize $|q|$. Solve with PuLP for a 168-hour week.
- Step 4 — Compare scenarios. Hot summer week (cooling-limited), cold winter week (heating-limited), shoulder week (free cooling via ventilation).
- Step 5 — Annual extension. Cluster days into 4–6 archetypes with k-means on (T_out, solar, AQI); generate one optimal schedule per archetype.
Data sources to consider
| Source | What you get |
|---|---|
| NOAA NCEI Local Climatological Data | Hourly outdoor temperature, solar, humidity for any US site |
| EPA AirNow API | Hourly outdoor PM2.5 / ozone / pollen |
| NREL ResStock / EnergyPlus reference homes | Realistic RC parameters by climate zone |
| Utility time-of-use tariff sheets | Hourly electricity prices (peak/off-peak) |
| ASHRAE 55 thermal-comfort standard | Acceptable temperature/humidity bands |
| EPA Indoor airPLUS guidelines | Ventilation rate targets (ACH) |
Common pitfalls and judge commentary patterns
- No thermal model. Some teams just pick a setpoint per hour without any building physics — judges flagged this.
- Constant electricity price. The whole point of "smart" is shifting load to off-peak; modeling a single rate kills the answer.
- Ignoring solar gain. South-facing windows are huge; not modelling them means the blinds variable is useless.
- Forgetting humidity / air quality. The prompt explicitly asks for IAQ; optimizing only on temperature loses points.
- Schedule presented as a wall of numbers. The user-facing explainer needs a picture, not a CSV.
Python sketch
Tiny RC thermal sim with hourly LP setpoint optimization for a 24-hour day. Extend to 168 hours and to a multi-mode annual schedule for the real submission.
import numpy as np, pulp
H = 24
T_out = np.array([18,17,16,16,16,17,19,22,25,28,30,32, 33,34,33,32,30,28,26,24,22,20,19,18])
price = np.array([0.10]*7 + [0.20]*4 + [0.35]*5 + [0.20]*5 + [0.10]*3) # $/kWh
T_set = 23.0
R, C = 5.0, 12.0 # thermal resistance / capacitance (illustrative)
T0 = 24.0
m = pulp.LpProblem("hvac", pulp.LpMinimize)
q = [pulp.LpVariable(f"q{t}", -3, 3) for t in range(H)] # kW HVAC (+ heat, - cool)
qa = [pulp.LpVariable(f"a{t}", 0) for t in range(H)] # |q|
Tin = [pulp.LpVariable(f"T{t}", 18, 28) for t in range(H)]
err = [pulp.LpVariable(f"e{t}", 0) for t in range(H)] # |T - Tset|
alpha, beta = 5.0, 1.0
m += pulp.lpSum(alpha*err[t] + beta*price[t]*qa[t] for t in range(H))
for t in range(H):
m += qa[t] >= q[t]
m += qa[t] >= -q[t]
m += err[t] >= (Tin[t] - T_set)
m += err[t] >= -(Tin[t] - T_set)
prev = T0 if t == 0 else Tin[t-1]
m += Tin[t] == prev + (1/C) * ((T_out[t] - prev)/R + q[t])
m.solve(pulp.PULP_CBC_CMD(msg=False))
print("T_in:", [round(v.value(), 1) for v in Tin])
print("q :", [round(v.value(), 2) for v in q])
Sensitivity & validation checklist
- Vary loss weights $\alpha : \beta : \gamma$ across (5:1:0), (5:1:5), (1:5:1) — does the schedule change shape predictably?
- Compare against a dumb constant-setpoint baseline; quantify $ saved.
- Use a Phoenix summer week vs. a Minneapolis winter week — model should produce very different schedules.
- Run with R doubled (better-insulated house); peak HVAC load should drop materially.
- Confirm ASHRAE 55 comfort band is respected in all hours.