PolicyEngine / PolicyEngine/policyengine-us
Atomic inputs: consolidate the heating/utility expense input family (LIHEAP, SNAP SUA, CA utility programs)
- Dominant language
- Python
- Stars
- 162
- Forks
- 212
- Avg merge
- 3d 3h
- Merged PRs (30d)
- 97
Description
Next migration under #9275 (atomic inputs). Unlike the mortgage case (#9276), this one is **not** a mechanical flip — it needs a design decision first, hence issue-before-PR.
## The redundancy
Three overlapping input vocabularies describe household heating/utility spending, arbitrated differently per program:
1. Person-level `heating_expense_person` (+ `heating_expenses` aggregator)
2. SPM-unit `heating_cooling_expense`
3. Per-fuel SPM-unit inputs: `pre_subsidy_electricity_expense` / `electricity_expense`, `gas_expense`, `fuel_oil_expense`, `propane_expense`, aggregated by `utility_expense`
Current arbitrations (verified on main): `dc_liheap_payment` prefers `heating_expense_person`, falling back to a per-fuel amount selected by `heating_type`; `il_liheap_base_payment` prefers `heating_expense_person`, falling back to `heating_cooling_expense`; `ma_liheap` touches all three vocabularies.
## Consumer map (verified)
| Input family | Consumers |
|---|---|
| `heating_cooling_expense` | IL/MA LIHEAP, **`snap_utility_allowance_type`** (SUA determination — every SNAP state), `utility_expense`, `has_heating_cooling_expense` |
| `heating_expense_person` | DC/IL/MA LIHEAP |
| per-fuel | DC/MA LIHEAP, CA CARE, CA FERA, LA DWP EZ-Save, Riverside SHARE, `utility_expense` |
## Data-compat: unconstrained
None of these columns appear in microcosm's certified US export (checked `release_input_coverage_manifest.json` and the build sources) — this family is household-input/imputation surface only, so unlike the mortgage migration there is no dataset staging problem and no loader-skip hazard (policyengine-core#530/#531 covers the general case anyway).
## Design question to settle
The atomic candidate set is the **per-fuel inputs** (they are the finest-grained facts; several programs' statutes key on fuel — LIHEAP payment matrices and heating type, CA program discounts) plus `heating_type`. Under that set:
- `heating_cooling_expense` becomes a derived aggregate (or stays as SNAP's statutory concept computed from per-fuel + cooling?) — SNAP's SUA test needs "heating or cooling expense" as a *fact of incurring*, which per-fuel inputs can supply;
- `heating_expense_person` is deleted (its person-level grain buys nothing: these bills attach to dwellings/SPM units, so SPM unit is the natural entity — the mortgage rule in reverse);
- DC/IL/MA LIHEAP formulas read one vocabulary, no arbitration.
Alternative: keep `heating_cooling_expense` as the atomic scalar and drop the per-fuel decomposition — rejected on first look because DC/MA LIHEAP and the CA programs genuinely need fuel-specific amounts, which a scalar can't supply.
Decision + migration in one PR is feasible once the SNAP interaction is pinned down (does `snap_utility_allowance_type` need the *amount* or just incurrence? — read the SUA rules before choosing).
Refs: #9275, #9276.
Contributor guide
Research direction
Read the SUA rules behind snap_utility_allowance_type first, then inspect the listed heating_cooling_expense, heating_expense_person, per-fuel inputs, and utility_expense consumers. Done means resolving whether SNAP needs an amount or only incurrence, choosing the atomic vocabulary, and completing the migration without arbitration across DC, IL, MA, and CA programs.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100