diff --git a/changelog.d/wi-liheap-wave1d.added.md b/changelog.d/wi-liheap-wave1d.added.md new file mode 100644 index 00000000000..d39b7180444 --- /dev/null +++ b/changelog.d/wi-liheap-wave1d.added.md @@ -0,0 +1 @@ +Add Wisconsin Home Energy Assistance Program regular heating benefit. diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/eligibility/fpg_year_lag.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/eligibility/fpg_year_lag.yaml new file mode 100644 index 00000000000..617902a7616 --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/eligibility/fpg_year_lag.yaml @@ -0,0 +1,13 @@ +description: Wisconsin uses federal poverty guidelines from this many years before the modeled benefit year under the Home Energy Assistance Program. +# Model year 2026 is federal fiscal year 2026, which uses the 2025 guidelines +# ($15,650 plus $5,500 per added person). The federal 110% income floor uses +# the same guideline year. +values: + 2025-10-01: 1 +metadata: + unit: year + period: year + label: Wisconsin WHEAP federal poverty guideline year lag + reference: + - title: Wisconsin HE+ Benefit Calculator FFY2026, Computation!C6:C8 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/eligibility/smi_rate.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/eligibility/smi_rate.yaml new file mode 100644 index 00000000000..7454e3aa5a8 --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/eligibility/smi_rate.yaml @@ -0,0 +1,14 @@ +description: Wisconsin limits income to this share of the state median income under the Home Energy Assistance Program. +values: + 2025-10-01: 0.6 +metadata: + unit: /1 + period: year + label: Wisconsin WHEAP state median income rate + reference: + - title: Wis. Stat. 16.27(5)(b) + href: https://docs.legis.wisconsin.gov/statutes/statutes/16/i/27 + - title: "Wisconsin PY26 WHEAP Manual, sections 2.3.1 and 3.3.1 and Appendix E #page 26,45,166" + href: https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=26 + - title: Wisconsin HE+ Benefit Calculator FFY2026, SMI_Lookup!A4:D23 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/income/adult_age.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/income/adult_age.yaml new file mode 100644 index 00000000000..bbbf45aa9fc --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/income/adult_age.yaml @@ -0,0 +1,10 @@ +description: Wisconsin counts earned income only for household members at or above this age under the Home Energy Assistance Program. +values: + 2025-10-01: 18 +metadata: + unit: year + period: year + label: Wisconsin WHEAP adult earned income age + reference: + - title: "Wisconsin PY26 WHEAP Manual, section 3.3.2 and Tables 3.4 and 3.6 #page 46,58-59" + href: https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=46 diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/income/deductions.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/income/deductions.yaml new file mode 100644 index 00000000000..65b22a493ea --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/income/deductions.yaml @@ -0,0 +1,14 @@ +description: Wisconsin deducts these expenses from countable income under the Home Energy Assistance Program. +# Only court-ordered child support paid is deductible; the input does not +# distinguish voluntary payments. The Medicare premium deduction applies only +# to Social Security and Railroad Retirement benefits, in the income formula. +values: + 2025-10-01: + - child_support_expense +metadata: + unit: list + period: year + label: Wisconsin WHEAP income deductions + reference: + - title: "Wisconsin PY26 WHEAP Manual, sections 3.3.2 item 5 and 3.3.3 and Table 3.5 #page 48-50,58" + href: https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=48 diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/income/investment_minimum.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/income/investment_minimum.yaml new file mode 100644 index 00000000000..c20a6d0f69a --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/income/investment_minimum.yaml @@ -0,0 +1,10 @@ +description: Wisconsin counts a household member's combined interest and dividends only when they reach this annual amount under the Home Energy Assistance Program. +values: + 2025-10-01: 120 +metadata: + unit: currency-USD + period: year + label: Wisconsin WHEAP interest and dividend minimum + reference: + - title: "Wisconsin PY26 WHEAP Manual, section 3.3.2 item 3 and Tables 3.4 and 3.6 #page 47,55,59" + href: https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=47 diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/income/retirement_distribution_age.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/income/retirement_distribution_age.yaml new file mode 100644 index 00000000000..c6b30d280f6 --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/income/retirement_distribution_age.yaml @@ -0,0 +1,10 @@ +description: Wisconsin counts all retirement account withdrawals as income for account owners at or above this age under the Home Energy Assistance Program. +values: + 2025-10-01: 73 +metadata: + unit: year + period: year + label: Wisconsin WHEAP retirement withdrawal age + reference: + - title: "Wisconsin PY26 WHEAP Manual, section 3.3.2 item 7(g) and Table 3.6 #page 49,59" + href: https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=49 diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/income/sources/earned.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/income/sources/earned.yaml new file mode 100644 index 00000000000..0d26bfbd114 --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/income/sources/earned.yaml @@ -0,0 +1,11 @@ +description: Wisconsin counts these sources as earned income for adults who are not high school students under the Home Energy Assistance Program. +values: + 2025-10-01: + - employment_income +metadata: + unit: list + period: year + label: Wisconsin WHEAP earned income sources + reference: + - title: "Wisconsin PY26 WHEAP Manual, section 3.3.2 and Tables 3.4 and 3.6 #page 46,58-60" + href: https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=46 diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/income/sources/investment.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/income/sources/investment.yaml new file mode 100644 index 00000000000..b79193e50b6 --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/income/sources/investment.yaml @@ -0,0 +1,12 @@ +description: Wisconsin combines these interest and dividend sources for each household member under the Home Energy Assistance Program. +values: + 2025-10-01: + - interest_income + - ordinary_dividend_income +metadata: + unit: list + period: year + label: Wisconsin WHEAP interest and dividend sources + reference: + - title: "Wisconsin PY26 WHEAP Manual, section 3.3.2 item 3 and Table 3.4 #page 47,55" + href: https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=47 diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/income/sources/rental.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/income/sources/rental.yaml new file mode 100644 index 00000000000..b7307c8f87c --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/income/sources/rental.yaml @@ -0,0 +1,15 @@ +description: Wisconsin includes these rental and royalty sources in the self-employment offset under the Home Energy Assistance Program. +# Land rent is entered as rental income type (R), which section 3.3.5 +# offsets. The $120 land rent threshold and rent paid by other household +# members are not modeled. +values: + 2025-10-01: + - rental_income + - farm_rent_income +metadata: + unit: list + period: year + label: Wisconsin WHEAP rental income sources + reference: + - title: "Wisconsin PY26 WHEAP Manual, section 3.3.5 and Table 3.4 #page 50-51,56" + href: https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=50 diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/income/sources/self_employment.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/income/sources/self_employment.yaml new file mode 100644 index 00000000000..57035461db3 --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/income/sources/self_employment.yaml @@ -0,0 +1,16 @@ +description: Wisconsin nets these self-employment sources across sources and household members under the Home Energy Assistance Program. +# Net business inputs are used without a further expense deduction. Income +# reported on a Self-Generated Income Report Form is not separable. +values: + 2025-10-01: + - self_employment_income + - sstb_self_employment_income + - farm_operations_income + - partnership_s_corp_income +metadata: + unit: list + period: year + label: Wisconsin WHEAP self-employment income sources + reference: + - title: "Wisconsin PY26 WHEAP Manual, section 3.3.5 and Table 3.4 #page 50-51,56" + href: https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=50 diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/income/sources/unearned.yaml new file mode 100644 index 00000000000..e66fbbb96bd --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/income/sources/unearned.yaml @@ -0,0 +1,30 @@ +description: Wisconsin counts these sources as unearned income under the Home Energy Assistance Program. +# Gift income beyond financial_assistance, tribal per capita payments and land +# contract interest have no input. PY27 Table 3.6 ignores gambling winnings; +# FFY2027 is not encoded. +values: + 2025-10-01: + - ssi + # The state SSI line excludes SSI-E (wi_ssi_e). + - wi_ssp_regular + - wi_cts + - pension_income + - military_retirement_income + - veterans_benefits + - workers_compensation + - disability_benefits + - alimony_income + - child_support_received + - strike_benefits + - general_assistance + - gambling_winnings + - estate_income + - financial_assistance + - survivor_benefits +metadata: + unit: list + period: year + label: Wisconsin WHEAP unearned income sources + reference: + - title: "Wisconsin PY26 WHEAP Manual, sections 3.3.2-3.3.3 and Table 3.4 #page 48-49,55-58" + href: https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=48 diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/benefit_ratio.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/benefit_ratio.yaml new file mode 100644 index 00000000000..d466fe3cf58 --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/benefit_ratio.yaml @@ -0,0 +1,10 @@ +description: Wisconsin subtracts the household poverty ratio from this constant in the regular heating benefit formula under the Home Energy Assistance Program. +values: + 2025-10-01: 2.66667 +metadata: + unit: /1 + period: year + label: Wisconsin WHEAP benefit ratio constant + reference: + - title: Wisconsin HE+ Benefit Calculator FFY2026, Computation!C15 and C39 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/electric_baseload_rate.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/electric_baseload_rate.yaml new file mode 100644 index 00000000000..f321a3f2a4c --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/electric_baseload_rate.yaml @@ -0,0 +1,14 @@ +description: Wisconsin removes this share of the reported electric bill as non-heating use when electricity is the heating fuel under the Home Energy Assistance Program. +# HE+ removes the baseload only when one meter serves heat and other uses +# (Computation!C22); heat-only meters are not identified. +values: + 2025-10-01: 0.6 +metadata: + unit: /1 + period: year + label: Wisconsin WHEAP electric baseload rate + reference: + - title: Wisconsin HE+ Benefit Calculator FFY2026, Benefits!D19 and Computation!C19, C22 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx + - title: Wisconsin PY26 WHEAP Manual, section 2.3.1 + href: https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=26 diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/funding_factor.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/funding_factor.yaml new file mode 100644 index 00000000000..38adfce919b --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/funding_factor.yaml @@ -0,0 +1,10 @@ +description: Wisconsin divides the regular heating benefit formula by this funding factor under the Home Energy Assistance Program. +values: + 2025-10-01: 2.94269 +metadata: + unit: /1 + period: year + label: Wisconsin WHEAP funding factor + reference: + - title: Wisconsin HE+ Benefit Calculator FFY2026, Computation!C16 and C39 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/maximum.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/maximum.yaml new file mode 100644 index 00000000000..a5dfedea493 --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/maximum.yaml @@ -0,0 +1,15 @@ +description: Wisconsin limits the regular heating benefit to this amount under the Home Energy Assistance Program. +# The FFY2026 proxy table and formula constants yield at most $2,574, so the +# cap does not bind. Calculator Computation!C17 sets $2,575 and the ACF-posted +# plan item 2.6 prints $2,147, which would bind for single-family fuel oil +# homes of 5-9 rooms. FFY2027 plan item 2.6 sets $2,784 from 2026-10-01; +# FFY2027 is not encoded. +values: + 2025-10-01: 2_580 +metadata: + unit: currency-USD + period: year + label: Wisconsin WHEAP maximum benefit + reference: + - title: Wisconsin LIHEAP State Plan FFY2026, agency-posted edition, section 2.6 + href: https://web.archive.org/web/20260316195114/https://energyandhousing.wi.gov/Documents/WHEAP/LiheapPlan_FFY2026.pdf#page=7 diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/minimum.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/minimum.yaml new file mode 100644 index 00000000000..246816b7921 --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/minimum.yaml @@ -0,0 +1,14 @@ +description: Wisconsin pays no regular heating benefit below this amount under the Home Energy Assistance Program. +values: + 2025-10-01: 30 +metadata: + unit: currency-USD + period: year + label: Wisconsin WHEAP minimum benefit + reference: + - title: Wisconsin PY26 WHEAP Manual, section 2.3.1 + href: https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=26 + - title: Wisconsin HE+ Benefit Calculator FFY2026, Computation!C18 and C42 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx + - title: Wisconsin LIHEAP State Plan FFY2026, section 2.6 + href: https://liheapch.acf.gov/docs/2026/state-plans/WI_Plan_2026.pdf#page=12 diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/poverty_ratio/maximum.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/poverty_ratio/maximum.yaml new file mode 100644 index 00000000000..9707a504a55 --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/poverty_ratio/maximum.yaml @@ -0,0 +1,10 @@ +description: Wisconsin caps the household poverty ratio at this value in the regular heating benefit formula under the Home Energy Assistance Program. +values: + 2025-10-01: 2.15 +metadata: + unit: /1 + period: year + label: Wisconsin WHEAP maximum poverty ratio + reference: + - title: Wisconsin HE+ Benefit Calculator FFY2026, Computation!C10 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/poverty_ratio/minimum.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/poverty_ratio/minimum.yaml new file mode 100644 index 00000000000..6abf3d90100 --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/poverty_ratio/minimum.yaml @@ -0,0 +1,10 @@ +description: Wisconsin floors the household poverty ratio at this value in the regular heating benefit formula under the Home Energy Assistance Program. +values: + 2025-10-01: 0.6 +metadata: + unit: /1 + period: year + label: Wisconsin WHEAP minimum poverty ratio + reference: + - title: Wisconsin HE+ Benefit Calculator FFY2026, Computation!C11 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/base/electricity.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/base/electricity.yaml new file mode 100644 index 00000000000..8e6dafb191f --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/base/electricity.yaml @@ -0,0 +1,23 @@ +description: Wisconsin sets this base heat proxy amount for electricity heat by dwelling type under the Home Energy Assistance Program. +# The motel or camper row is the rooming house, motel and hotel row of the +# calculator. Cells the calculator flags as estimated (IsFake) are used as +# published. +SINGLE_FAMILY: + 2025-10-01: 261 +TWO_TO_FOUR_UNITS: + 2025-10-01: 207 +APARTMENT: + 2025-10-01: 88 +MOBILE_HOME: + 2025-10-01: 227 +MOTEL_OR_CAMPER: + 2025-10-01: 88 +metadata: + unit: currency-USD + period: year + label: Wisconsin WHEAP electricity base heat proxy + breakdown: + - wi_wheap_dwelling_type + reference: + - title: Wisconsin HE+ Benefit Calculator FFY2026, HT_Proxy!F7, F12, F17, F22, F27 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/base/fuel_oil.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/base/fuel_oil.yaml new file mode 100644 index 00000000000..b6d66e6d9e4 --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/base/fuel_oil.yaml @@ -0,0 +1,23 @@ +description: Wisconsin sets this base heat proxy amount for fuel oil heat by dwelling type under the Home Energy Assistance Program. +# The motel or camper row is the rooming house, motel and hotel row of the +# calculator. Cells the calculator flags as estimated (IsFake) are used as +# published. +SINGLE_FAMILY: + 2025-10-01: 1_221 +TWO_TO_FOUR_UNITS: + 2025-10-01: 1_335 +APARTMENT: + 2025-10-01: 1_335 +MOBILE_HOME: + 2025-10-01: 1_521 +MOTEL_OR_CAMPER: + 2025-10-01: 1_335 +metadata: + unit: currency-USD + period: year + label: Wisconsin WHEAP fuel oil base heat proxy + breakdown: + - wi_wheap_dwelling_type + reference: + - title: Wisconsin HE+ Benefit Calculator FFY2026, HT_Proxy!F4, F9, F14, F19, F24 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/base/natural_gas.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/base/natural_gas.yaml new file mode 100644 index 00000000000..1ebdaef1c8d --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/base/natural_gas.yaml @@ -0,0 +1,23 @@ +description: Wisconsin sets this base heat proxy amount for natural gas heat by dwelling type under the Home Energy Assistance Program. +# The motel or camper row is the rooming house, motel and hotel row of the +# calculator. Cells the calculator flags as estimated (IsFake) are used as +# published. +SINGLE_FAMILY: + 2025-10-01: 354 +TWO_TO_FOUR_UNITS: + 2025-10-01: 297 +APARTMENT: + 2025-10-01: 116 +MOBILE_HOME: + 2025-10-01: 471 +MOTEL_OR_CAMPER: + 2025-10-01: 116 +metadata: + unit: currency-USD + period: year + label: Wisconsin WHEAP natural gas base heat proxy + breakdown: + - wi_wheap_dwelling_type + reference: + - title: Wisconsin HE+ Benefit Calculator FFY2026, HT_Proxy!F5, F10, F15, F20, F25 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/base/propane.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/base/propane.yaml new file mode 100644 index 00000000000..88a078f1e1a --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/base/propane.yaml @@ -0,0 +1,23 @@ +description: Wisconsin sets this base heat proxy amount for propane heat by dwelling type under the Home Energy Assistance Program. +# The motel or camper row is the rooming house, motel and hotel row of the +# calculator. Cells the calculator flags as estimated (IsFake) are used as +# published. +SINGLE_FAMILY: + 2025-10-01: 736 +TWO_TO_FOUR_UNITS: + 2025-10-01: 751 +APARTMENT: + 2025-10-01: 751 +MOBILE_HOME: + 2025-10-01: 723 +MOTEL_OR_CAMPER: + 2025-10-01: 751 +metadata: + unit: currency-USD + period: year + label: Wisconsin WHEAP propane base heat proxy + breakdown: + - wi_wheap_dwelling_type + reference: + - title: Wisconsin HE+ Benefit Calculator FFY2026, HT_Proxy!F6, F11, F16, F21, F26 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/base/wood_other.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/base/wood_other.yaml new file mode 100644 index 00000000000..5f151337b51 --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/base/wood_other.yaml @@ -0,0 +1,23 @@ +description: Wisconsin sets this base heat proxy amount for wood or other fuels heat by dwelling type under the Home Energy Assistance Program. +# The motel or camper row is the rooming house, motel and hotel row of the +# calculator. Cells the calculator flags as estimated (IsFake) are used as +# published. +SINGLE_FAMILY: + 2025-10-01: 524 +TWO_TO_FOUR_UNITS: + 2025-10-01: 440 +APARTMENT: + 2025-10-01: 172 +MOBILE_HOME: + 2025-10-01: 697 +MOTEL_OR_CAMPER: + 2025-10-01: 172 +metadata: + unit: currency-USD + period: year + label: Wisconsin WHEAP wood or other fuels base heat proxy + breakdown: + - wi_wheap_dwelling_type + reference: + - title: Wisconsin HE+ Benefit Calculator FFY2026, HT_Proxy!F8, F13, F18, F23, F28 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/cap_rate.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/cap_rate.yaml new file mode 100644 index 00000000000..51e833b8c98 --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/cap_rate.yaml @@ -0,0 +1,12 @@ +description: Wisconsin caps the heating cost used for the regular heating benefit at this multiple of the heat proxy amount under the Home Energy Assistance Program. +values: + 2025-10-01: 2 +metadata: + unit: /1 + period: year + label: Wisconsin WHEAP heat proxy cap rate + reference: + - title: Wisconsin PY26 WHEAP Manual, section 2.3.1 item 3 + href: https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=27 + - title: Wisconsin HE+ Benefit Calculator FFY2026, Computation!C30 and C33 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/floor_rate.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/floor_rate.yaml new file mode 100644 index 00000000000..27bba6bf070 --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/floor_rate.yaml @@ -0,0 +1,14 @@ +description: Wisconsin bases the regular heating benefit on at least this share of the heat proxy amount under the Home Energy Assistance Program. +# The same share applies when heat is included in rent or no heating cost is +# reported. +values: + 2025-10-01: 0.75 +metadata: + unit: /1 + period: year + label: Wisconsin WHEAP heat proxy floor rate + reference: + - title: "Wisconsin PY26 WHEAP Manual, section 2.3.1 items 2-5 #page 26-27" + href: https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=26 + - title: Wisconsin HE+ Benefit Calculator FFY2026, Computation!C31, C34 and C35 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/max_rooms.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/max_rooms.yaml new file mode 100644 index 00000000000..74638734a17 --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/max_rooms.yaml @@ -0,0 +1,12 @@ +description: Wisconsin limits the room count used in the heat proxy amount to this number under the Home Energy Assistance Program. +values: + 2025-10-01: 9 +metadata: + unit: int + period: year + label: Wisconsin WHEAP heat proxy maximum rooms + reference: + - title: "Wisconsin PY26 WHEAP Manual, sections 2.3.1 item 7 and 4.4.17 #page 27,79" + href: https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=27 + - title: Wisconsin HE+ Benefit Calculator FFY2026, Benefits!B27 and D12 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/per_room/electricity.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/per_room/electricity.yaml new file mode 100644 index 00000000000..727552c9c7b --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/per_room/electricity.yaml @@ -0,0 +1,23 @@ +description: Wisconsin adds this heat proxy amount per room for electricity heat by dwelling type under the Home Energy Assistance Program. +# The motel or camper row is the rooming house, motel and hotel row of the +# calculator. Cells the calculator flags as estimated (IsFake) are used as +# published. +SINGLE_FAMILY: + 2025-10-01: 108 +TWO_TO_FOUR_UNITS: + 2025-10-01: 106 +APARTMENT: + 2025-10-01: 111 +MOBILE_HOME: + 2025-10-01: 120 +MOTEL_OR_CAMPER: + 2025-10-01: 111 +metadata: + unit: currency-USD + period: year + label: Wisconsin WHEAP electricity heat proxy per room + breakdown: + - wi_wheap_dwelling_type + reference: + - title: Wisconsin HE+ Benefit Calculator FFY2026, HT_Proxy!E7, E12, E17, E22, E27 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/per_room/fuel_oil.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/per_room/fuel_oil.yaml new file mode 100644 index 00000000000..befbcc813f1 --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/per_room/fuel_oil.yaml @@ -0,0 +1,23 @@ +description: Wisconsin adds this heat proxy amount per room for fuel oil heat by dwelling type under the Home Energy Assistance Program. +# The motel or camper row is the rooming house, motel and hotel row of the +# calculator. Cells the calculator flags as estimated (IsFake) are used as +# published. +SINGLE_FAMILY: + 2025-10-01: 68 +TWO_TO_FOUR_UNITS: + 2025-10-01: 8 +APARTMENT: + 2025-10-01: 8 +MOBILE_HOME: + 2025-10-01: -13 +MOTEL_OR_CAMPER: + 2025-10-01: 8 +metadata: + unit: currency-USD + period: year + label: Wisconsin WHEAP fuel oil heat proxy per room + breakdown: + - wi_wheap_dwelling_type + reference: + - title: Wisconsin HE+ Benefit Calculator FFY2026, HT_Proxy!E4, E9, E14, E19, E24 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/per_room/natural_gas.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/per_room/natural_gas.yaml new file mode 100644 index 00000000000..131bfd8ae55 --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/per_room/natural_gas.yaml @@ -0,0 +1,23 @@ +description: Wisconsin adds this heat proxy amount per room for natural gas heat by dwelling type under the Home Energy Assistance Program. +# The motel or camper row is the rooming house, motel and hotel row of the +# calculator. Cells the calculator flags as estimated (IsFake) are used as +# published. +SINGLE_FAMILY: + 2025-10-01: 60 +TWO_TO_FOUR_UNITS: + 2025-10-01: 65 +APARTMENT: + 2025-10-01: 68 +MOBILE_HOME: + 2025-10-01: 28 +MOTEL_OR_CAMPER: + 2025-10-01: 68 +metadata: + unit: currency-USD + period: year + label: Wisconsin WHEAP natural gas heat proxy per room + breakdown: + - wi_wheap_dwelling_type + reference: + - title: Wisconsin HE+ Benefit Calculator FFY2026, HT_Proxy!E5, E10, E15, E20, E25 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/per_room/propane.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/per_room/propane.yaml new file mode 100644 index 00000000000..b7e55bd0b81 --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/per_room/propane.yaml @@ -0,0 +1,23 @@ +description: Wisconsin adds this heat proxy amount per room for propane heat by dwelling type under the Home Energy Assistance Program. +# The motel or camper row is the rooming house, motel and hotel row of the +# calculator. Cells the calculator flags as estimated (IsFake) are used as +# published. +SINGLE_FAMILY: + 2025-10-01: 82 +TWO_TO_FOUR_UNITS: + 2025-10-01: 51 +APARTMENT: + 2025-10-01: 51 +MOBILE_HOME: + 2025-10-01: 69 +MOTEL_OR_CAMPER: + 2025-10-01: 51 +metadata: + unit: currency-USD + period: year + label: Wisconsin WHEAP propane heat proxy per room + breakdown: + - wi_wheap_dwelling_type + reference: + - title: Wisconsin HE+ Benefit Calculator FFY2026, HT_Proxy!E6, E11, E16, E21, E26 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/per_room/wood_other.yaml b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/per_room/wood_other.yaml new file mode 100644 index 00000000000..4f3c7e5ec57 --- /dev/null +++ b/policyengine_us/parameters/gov/states/wi/doa/wheap/payment/proxy/per_room/wood_other.yaml @@ -0,0 +1,23 @@ +description: Wisconsin adds this heat proxy amount per room for wood or other fuels heat by dwelling type under the Home Energy Assistance Program. +# The motel or camper row is the rooming house, motel and hotel row of the +# calculator. Cells the calculator flags as estimated (IsFake) are used as +# published. +SINGLE_FAMILY: + 2025-10-01: 89 +TWO_TO_FOUR_UNITS: + 2025-10-01: 96 +APARTMENT: + 2025-10-01: 101 +MOBILE_HOME: + 2025-10-01: 41 +MOTEL_OR_CAMPER: + 2025-10-01: 101 +metadata: + unit: currency-USD + period: year + label: Wisconsin WHEAP wood or other fuels heat proxy per room + breakdown: + - wi_wheap_dwelling_type + reference: + - title: Wisconsin HE+ Benefit Calculator FFY2026, HT_Proxy!E8, E13, E18, E23, E28 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 9930e6b8b2f..03a1c81c7c0 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -987,7 +987,7 @@ programs: category: Benefits agency: HHS status: partial - coverage: AK, AR, AL, AZ, DC, GA, Riverside County, MA, FL, IL, IA, IN, KS, KY, MD, MN, MS, NC, ND, NE, NH, NJ, NM, NV, NY, OH, OK, OR, SD, RI, TN, TX, UT, VA, WA, WY + coverage: AK, AR, AL, AZ, DC, GA, Riverside County, MA, FL, IL, IA, IN, KS, KY, MD, MN, MS, NC, ND, NE, NH, NJ, NM, NV, NY, OH, OK, OR, SD, RI, TN, TX, UT, VA, WA, WI, WY state_implementations: - state: AL status: partial @@ -1250,6 +1250,14 @@ programs: parameter_prefix: gov.states.wa.commerce.liheap verified_years: "2026" notes: FY2026 regular heating from reported annual heating costs, with the $250 minimum when heat is included in rent. Crisis, cooling, weatherization and equipment assistance are out of scope. + - state: WI + status: partial + name: Wisconsin WHEAP + full_name: Wisconsin Home Energy Assistance Program + variable: wi_wheap + parameter_prefix: gov.states.wi.doa.wheap + verified_years: "2026" + notes: FFY2026 regular heating benefit from the HE+ formula with the published heat proxy table. The non-heating electric benefit, crisis and weatherization are out of scope. - state: WY status: partial name: Wyoming LIEAP diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/doa/wheap/eligibility/wi_wheap_categorically_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/doa/wheap/eligibility/wi_wheap_categorically_eligible.yaml new file mode 100644 index 00000000000..9ab0a8e546e --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/doa/wheap/eligibility/wi_wheap_categorically_eligible.yaml @@ -0,0 +1,219 @@ +# Wisconsin WHEAP categorical eligibility: a household in which all members +# receive W-2 cash (TANF), FoodShare (SNAP) or SSI passes the income test +# (Wis. Stat. 16.27(5)(c); PY2026 WHEAP Manual 3.3.1). Annual receipt stands +# in for receipt during the entire prior month. + +- name: Case 1, FoodShare household is categorically eligible. + period: 2026 + input: + people: + person1: + age: 31 + employment_income: 22_000 + person2: + age: 7 + person3: + age: 3 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: NATURAL_GAS + snap: 3_600 + tanf: 0 + households: + household: + members: [person1, person2, person3] + state_code: WI + output: + # FoodShare 300 a month covers the whole household. + wi_wheap_categorically_eligible: true + +- name: Case 2, household in which every member receives SSI is categorically eligible. + period: 2026 + input: + people: + person1: + age: 72 + ssi: 8_946 + person2: + age: 70 + ssi: 8_946 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + snap: 0 + tanf: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + # 2026 SSI couple rate 1,491 a month, 745.50 each (8,946 a year each). + wi_wheap_categorically_eligible: true + +- name: Case 3, household with one member who does not receive SSI is not categorically eligible. + period: 2026 + input: + people: + person1: + age: 67 + ssi: 11_928 + person2: + age: 45 + employment_income: 30_000 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + snap: 0 + tanf: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + # Manual 3.3.1: every member must receive a qualifying benefit; the + # working adult receives none. + wi_wheap_categorically_eligible: false + +- name: Case 4, W-2 cash household is categorically eligible. + period: 2026 + input: + people: + person1: + age: 26 + person2: + age: 2 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + tanf: 7_296 + snap: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + # W-2 Transitions grant 608 a month x 12 = 7,296. + wi_wheap_categorically_eligible: true + +- name: Case 5, FoodShare household with an undocumented member is not categorically eligible. + period: 2026 + input: + people: + person1: + age: 30 + immigration_status: UNDOCUMENTED + employment_income: 18_000 + person2: + age: 6 + immigration_status: CITIZEN + person3: + age: 3 + immigration_status: CITIZEN + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: NATURAL_GAS + snap: 4_800 + tanf: 0 + households: + household: + members: [person1, person2, person3] + state_code: WI + output: + # Manual 3.3.1 item 2(d): if some but not all members (including + # ineligible non-citizens) receive a qualifying benefit, the household is + # not categorical. FoodShare covers only the two citizen children. + wi_wheap_categorically_eligible: false + +- name: Case 6, household in which every member reports SSI receipt is categorically eligible. + period: 2026 + input: + people: + person1: + age: 72 + receives_ssi: true + ssi: 0 + person2: + age: 70 + receives_ssi: true + ssi: 0 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + snap: 0 + tanf: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + # Receiving SSI = computed SSI above 0 or reported receipt (receives_ssi). + wi_wheap_categorically_eligible: true + +- name: Case 7, FoodShare household with a refugee member outside the FoodShare group is not categorically eligible. + period: 2026 + input: + people: + person1: + age: 36 + immigration_status: REFUGEE + employment_income: 40_000 + person2: + age: 34 + immigration_status: CITIZEN + person3: + age: 6 + immigration_status: CITIZEN + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: NATURAL_GAS + snap: 900 + tanf: 0 + households: + household: + members: [person1, person2, person3] + state_code: WI + output: + # From July 2025, P.L. 119-21 limits SNAP to citizens, lawful permanent + # residents and Cuban/Haitian entrants (7 U.S.C. 2015(f)), so the refugee + # is excluded from the FoodShare group; FoodShare (75 a month) covers the + # citizen spouse and child only. The refugee receives no SSI or W-2, so not + # every member is a recipient (Manual 3.3.1 item 2(d)). + wi_wheap_categorically_eligible: false + +- name: Case 8, W-2 household with an undocumented adult is not categorically eligible. + period: 2026 + input: + people: + person1: + age: 27 + immigration_status: CITIZEN + person2: + age: 29 + immigration_status: UNDOCUMENTED + person3: + age: 2 + immigration_status: CITIZEN + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: NATURAL_GAS + gas_expense: 900 + tanf: 7_296 + snap: 0 + households: + household: + members: [person1, person2, person3] + state_code: WI + output: + # W-2 Transitions grant 608 a month x 12 = 7,296 for the citizen parent + # and child. W-2 covers only citizens and qualified non-citizens, so the + # undocumented adult receives no qualifying benefit (Manual 3.3.1 item + # 2(d)). Without the citizenship condition every member would count. + wi_wheap_categorically_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/doa/wheap/eligibility/wi_wheap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/doa/wheap/eligibility/wi_wheap_eligible.yaml new file mode 100644 index 00000000000..3d679a839c1 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/doa/wheap/eligibility/wi_wheap_eligible.yaml @@ -0,0 +1,410 @@ +# Wisconsin WHEAP regular heating eligibility: at least one citizen or +# eligible noncitizen; income at or below 60 percent of state median income, +# below 110 percent of the poverty guideline (42 U.S.C. 8624(b)(2)(B)), or +# categorical eligibility (Wis. Stat. 16.27(5)(b), (5)(c)); a reported heating +# fuel; and an energy burden: a heating bill paid by the household, or heat +# included in rent without government rental assistance (PY2026 WHEAP Manual +# 3.2.5 and Table 3.2). + +- name: Case 1, income exactly at the limit is eligible. + period: 2026 + input: + people: + person1: + age: 41 + employment_income: 50_243 + person2: + age: 39 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + heating_expense: 900 + tanf: 0 + snap: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + # Size 2 limit 50,243 (Appendix E); 50,243 <= 50,243. + wi_wheap_eligible: true + +- name: Case 2, income one dollar over the limit is ineligible. + period: 2026 + input: + people: + person1: + age: 41 + employment_income: 50_244 + person2: + age: 39 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + heating_expense: 900 + tanf: 0 + snap: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + # 50,244 > 50,243, above 110% of the 2025 guideline (23,265), not + # categorical. + wi_wheap_eligible: false + +- name: Case 3, FoodShare household above the limit is eligible through categorical eligibility. + period: 2026 + input: + people: + person1: + age: 44 + employment_income: 64_000 + person2: + age: 42 + employment_income: 40_000 + person3: + age: 15 + person4: + age: 13 + person5: + age: 11 + person6: + age: 9 + person7: + age: 6 + person8: + age: 3 + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5, person6, person7, person8] + heating_type: NATURAL_GAS + heating_expense: 1_150 + snap: 1_200 + tanf: 0 + households: + household: + members: [person1, person2, person3, person4, person5, person6, person7, person8] + state_code: WI + output: + # 104,000 > size 8 limit 101,965, but FoodShare receipt (supplied as an + # input) passes the income test under Wis. Stat. 16.27(5)(c). + wi_wheap_eligible: true + +- name: Case 4, twenty-person household above the limit with income one dollar below 110 percent of the poverty guideline is eligible. + period: 2026 + input: + people: + person1: + age: 48 + employment_income: 42_164 + person2: + age: 46 + employment_income: 35_000 + person3: + age: 26 + employment_income: 30_000 + person4: + age: 24 + employment_income: 25_000 + person5: {age: 44} + person6: {age: 22} + person7: {age: 19} + person8: {age: 17} + person9: {age: 16} + person10: {age: 15} + person11: {age: 14} + person12: {age: 13} + person13: {age: 12} + person14: {age: 11} + person15: {age: 10} + person16: {age: 9} + person17: {age: 7} + person18: {age: 5} + person19: {age: 3} + person20: {age: 1} + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5, person6, person7, person8, person9, person10, person11, person12, person13, person14, person15, person16, person17, person18, person19, person20] + heating_type: NATURAL_GAS + heating_expense: 2_000 + tanf: 0 + snap: 0 + households: + household: + members: [person1, person2, person3, person4, person5, person6, person7, person8, person9, person10, person11, person12, person13, person14, person15, person16, person17, person18, person19, person20] + state_code: WI + output: + # Size 20 limit 128,565 < income 42,164 + 35,000 + 30,000 + 25,000 = + # 132,164. 2025 guideline for 20: 15,650 + 19 x 5,500 = 120,150; + # x 1.1 = 132,165 > 132,164, so the federal floor bars exclusion. + wi_wheap_income_limit: 128_565 + wi_wheap_eligible: true + +- name: Case 5, household without a citizen or qualified noncitizen is ineligible. + period: 2026 + input: + people: + person1: + age: 33 + immigration_status: UNDOCUMENTED + employment_income: 30_000 + person2: + age: 31 + immigration_status: UNDOCUMENTED + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + heating_expense: 800 + tanf: 0 + snap: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + # Manual 3.2.1: the case head must be a citizen or eligible noncitizen; + # no member qualifies, so the eligible-member size is 0. + wi_wheap_eligible: false + +- name: Case 6, mixed-status household with a citizen member is eligible. + period: 2026 + input: + people: + person1: + age: 41 + immigration_status: UNDOCUMENTED + employment_income: 30_000 + person2: + age: 39 + immigration_status: CITIZEN + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + heating_expense: 800 + tanf: 0 + snap: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + # Eligible-member size 1, limit 38,421; all income counts: 30,000 <= 38,421. + wi_wheap_eligible: true + +- name: Case 7, heat included in rent with rental assistance is ineligible. + period: 2026 + input: + people: + person1: + age: 58 + employment_income: 24_000 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + heating_expense: 0 + heat_expense_included_in_rent: true + receives_housing_assistance: true + tanf: 0 + snap: 0 + households: + household: + members: [person1] + state_code: WI + output: + # Manual 3.2.5.1 and Table 3.2: subsidized housing with heat in rent has no + # energy burden. + wi_wheap_eligible: false + +- name: Case 8, heat included in rent in public housing is ineligible. + period: 2026 + input: + people: + person1: + age: 58 + employment_income: 24_000 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + heating_expense: 0 + heat_expense_included_in_rent: true + tanf: 0 + snap: 0 + households: + household: + members: [person1] + state_code: WI + is_in_public_housing: true + output: + # Public housing is government-assisted housing: no energy burden. + wi_wheap_eligible: false + +- name: Case 9, heat included in rent without rental assistance is eligible. + period: 2026 + input: + people: + person1: + age: 58 + employment_income: 24_000 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + heating_expense: 0 + heat_expense_included_in_rent: true + tanf: 0 + snap: 0 + households: + household: + members: [person1] + state_code: WI + output: + # Table 3.2: heat in rent without rental assistance is an energy burden; + # 24,000 <= size 1 limit 38,421. + wi_wheap_eligible: true + +- name: Case 10, rental assistance tenant who pays the heating bill directly is eligible. + period: 2026 + input: + people: + person1: + age: 58 + employment_income: 24_000 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + heating_expense: 700 + receives_housing_assistance: true + tanf: 0 + snap: 0 + households: + household: + members: [person1] + state_code: WI + output: + # Manual 4.4.19: a subsidized tenant paying the vendor has an energy + # burden; only heat in rent is excluded. + wi_wheap_eligible: true + +- name: Case 11, unspecified heating fuel is ineligible. + period: 2026 + input: + people: + person1: + age: 58 + employment_income: 24_000 + spm_units: + spm_unit: + members: [person1] + heating_type: UNSPECIFIED + heating_expense: 900 + tanf: 0 + snap: 0 + households: + household: + members: [person1] + state_code: WI + output: + # Expense supplied so only the fuel gate decides: UNSPECIFIED pays nothing. + wi_wheap_eligible: false + +- name: Case 12, a home with no heating is ineligible. + period: 2026 + input: + people: + person1: + age: 58 + employment_income: 24_000 + spm_units: + spm_unit: + members: [person1] + heating_type: NONE + heating_expense: 600 + tanf: 0 + snap: 0 + households: + household: + members: [person1] + state_code: WI + output: + # Expense supplied so only the fuel gate decides: NONE has no heating fuel. + wi_wheap_eligible: false + +- name: Case 13, household paying its own heating with no heating cost is ineligible. + period: 2026 + input: + people: + person1: + age: 58 + employment_income: 24_000 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + heating_expense: 0 + tanf: 0 + snap: 0 + households: + household: + members: [person1] + state_code: WI + output: + # Manual 3.2.5: no heating payment and heat not in rent, so no energy + # burden. + wi_wheap_eligible: false + +- name: Case 14, twenty-person household above the limit with income exactly at 110 percent of the poverty guideline is ineligible. + period: 2026 + input: + people: + person1: + age: 48 + employment_income: 42_165 + person2: + age: 46 + employment_income: 35_000 + person3: + age: 26 + employment_income: 30_000 + person4: + age: 24 + employment_income: 25_000 + person5: {age: 44} + person6: {age: 22} + person7: {age: 19} + person8: {age: 17} + person9: {age: 16} + person10: {age: 15} + person11: {age: 14} + person12: {age: 13} + person13: {age: 12} + person14: {age: 11} + person15: {age: 10} + person16: {age: 9} + person17: {age: 7} + person18: {age: 5} + person19: {age: 3} + person20: {age: 1} + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5, person6, person7, person8, person9, person10, person11, person12, person13, person14, person15, person16, person17, person18, person19, person20] + heating_type: NATURAL_GAS + heating_expense: 2_000 + tanf: 0 + snap: 0 + households: + household: + members: [person1, person2, person3, person4, person5, person6, person7, person8, person9, person10, person11, person12, person13, person14, person15, person16, person17, person18, person19, person20] + state_code: WI + output: + # Income 132,165 is not less than the 132,165 floor (42 U.S.C. + # 8624(b)(2)(B): "less than 110 percent"), exceeds the size 20 limit + # 128,565, and the household is not categorical. + wi_wheap_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/doa/wheap/eligibility/wi_wheap_income_limit.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/doa/wheap/eligibility/wi_wheap_income_limit.yaml new file mode 100644 index 00000000000..1cec28b9e24 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/doa/wheap/eligibility/wi_wheap_income_limit.yaml @@ -0,0 +1,151 @@ +# Wisconsin WHEAP annual income limit: 60 percent of state median income by +# household size (Wis. Stat. 16.27(5)(b)). PY2026 annual limits from WHEAP +# Manual Appendix E (sizes 1-8) and the FFY2026 HE+ Benefit Calculator +# SMI_Lookup sheet (sizes 9-20). Household size counts eligible members only. + +- name: Case 1, one-person household. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + households: + household: + members: [person1] + state_code: WI + output: + # Appendix E, size 1 annual income: 38,421. + wi_wheap_income_limit: 38_421 + +- name: Case 2, four-person household. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 38 + person2: + age: 36 + person3: + age: 10 + person4: + age: 6 + spm_units: + spm_unit: + members: [person1, person2, person3, person4] + heating_type: NATURAL_GAS + households: + household: + members: [person1, person2, person3, person4] + state_code: WI + output: + # Appendix E, size 4: 73,888 (floor of 0.6 x 123,148 four-person SMI). + wi_wheap_income_limit: 73_888 + +- name: Case 3, eight-person household. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 42 + person2: + age: 40 + person3: + age: 17 + person4: + age: 15 + person5: + age: 12 + person6: + age: 10 + person7: + age: 7 + person8: + age: 3 + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5, person6, person7, person8] + heating_type: NATURAL_GAS + households: + household: + members: [person1, person2, person3, person4, person5, person6, person7, person8] + state_code: WI + output: + # Appendix E, size 8: 101,965. + wi_wheap_income_limit: 101_965 + +- name: Case 4, twenty-person household uses the last row of the calculator table. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 45 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + wi_wheap_household_size: 20 + households: + household: + members: [person1] + state_code: WI + output: + # Size supplied directly. SMI_Lookup row 20 annual income: 128,565. + wi_wheap_income_limit: 128_565 + +- name: Case 5, an undocumented member does not raise the household size for the limit. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 34 + immigration_status: UNDOCUMENTED + person2: + age: 32 + immigration_status: CITIZEN + person3: + age: 5 + immigration_status: CITIZEN + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: NATURAL_GAS + households: + household: + members: [person1, person2, person3] + state_code: WI + output: + # Manual 4.4.26.2: two eligible members, so the size 2 limit 50,243 + # applies instead of the size 3 limit 62,065. + wi_wheap_income_limit: 50_243 + +- name: Case 6, household with no eligible member has no limit. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + immigration_status: UNDOCUMENTED + person2: + age: 29 + immigration_status: UNDOCUMENTED + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + households: + household: + members: [person1, person2] + state_code: WI + output: + # Size 0; the table starts at size 1. + wi_wheap_income_limit: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/doa/wheap/income/wi_wheap_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/doa/wheap/income/wi_wheap_countable_income.yaml new file mode 100644 index 00000000000..8ac8c55b545 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/doa/wheap/income/wi_wheap_countable_income.yaml @@ -0,0 +1,373 @@ +# Wisconsin WHEAP countable gross income, PY2026 WHEAP Manual 3.3 and +# Tables 3.4-3.6: wages of members 18 or older and not in high school, +# self-employment and rental income netted across the household and floored at +# zero, unearned income, Social Security and Railroad Retirement net of Medicare +# premiums, interest and dividends of $120 or more per member, retirement +# withdrawals at 73 or older, W-2 cash grants, less court-ordered child support +# paid. Unemployment compensation is ignored. + +- name: Case 1, earnings of a 16-year-old and an 18-year-old high school student are excluded. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 45 + employment_income: 42_000 + person2: + age: 16 + employment_income: 3_000 + self_employment_income: 1_200 + person3: + age: 18 + is_in_k12_school: true + employment_income: 2_500 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: NATURAL_GAS + tanf: 0 + snap: 0 + households: + household: + members: [person1, person2, person3] + state_code: WI + output: + # Manual 3.3.2 and Table 3.6: earnings of members under 18 and of high + # school students are ignored, including the 16-year-old's 1,200 of + # lawn-mowing self-employment income. Only the parent's 42,000 counts. + wi_wheap_countable_income: 42_000 + +- name: Case 2, wages of an 18-year-old who has left high school count. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 45 + employment_income: 42_000 + person2: + age: 18 + is_in_k12_school: false + employment_income: 6_000 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + tanf: 0 + snap: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + # Age 18 is not under 18, so the exclusion does not apply: + # 42,000 + 6,000 = 48,000. + wi_wheap_countable_income: 48_000 + +- name: Case 3, one member's business loss offsets another member's business and rental profit. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 47 + self_employment_income: 18_000 + rental_income: 2_400 + person2: + age: 45 + sstb_self_employment_income: -5_000 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + tanf: 0 + snap: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + # Manual 3.3.5: losses of one self-employment source offset profits of + # another, across household members, with rental income in the offset: + # 18,000 + 2,400 - 5,000 = 15,400. + wi_wheap_countable_income: 15_400 + +- name: Case 4, a net household business loss counts as zero and does not reduce wages. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 39 + employment_income: 30_000 + rental_income: 1_200 + person2: + age: 37 + self_employment_income: -8_000 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + tanf: 0 + snap: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + # Business group: 1,200 - 8,000 = -6,800, counted as 0; Manual 3.3.5 bars + # offsetting wages with a self-employment loss. Wages 30,000. + wi_wheap_countable_income: 30_000 + +- name: Case 5, Social Security and Railroad Retirement count net of the Medicare Part B premium. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + social_security_retirement: 21_600 + medicare_part_b_premium: 2_434.80 + ssi: 0 + person2: + age: 68 + railroad_benefits: 14_400 + medicare_part_b_premium: 2_434.80 + ssi: 0 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + tanf: 0 + snap: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + # Table 3.5: Medicare premiums are deducted from the benefit. 2026 Part B + # standard premium 202.90 x 12 = 2,434.80 each. + # (21,600 - 2,434.80) + (14,400 - 2,434.80) = 19,165.20 + 11,965.20. + wi_wheap_countable_income: 31_130.40 + +- name: Case 6, interest and dividends count at 120 dollars per member but not at 119 dollars. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 52 + employment_income: 35_000 + taxable_interest_income: 80 + qualified_dividend_income: 40 + person2: + age: 50 + taxable_interest_income: 119 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + tanf: 0 + snap: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + # Manual 3.3.2 and Table 3.4: a member's combined interest and dividends + # count when at least 120 a year. person1 80 + 40 = 120 counts; + # person2 119 is ignored. 35,000 + 120 = 35,120. + wi_wheap_countable_income: 35_120 + +- name: Case 7, retirement account withdrawals count at age 73 but not at age 72. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 73 + taxable_ira_distributions: 9_000 + ssi: 0 + person2: + age: 72 + taxable_ira_distributions: 6_000 + ssi: 0 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + tanf: 0 + snap: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + # Manual 3.3.2 item g: all withdrawals count for an owner 73 or older; + # under 73 non-regular withdrawals are asset drawdowns (model counts 73+ + # only). Only person1's 9,000 counts. + wi_wheap_countable_income: 9_000 + +- name: Case 8, child support received counts and court-ordered child support paid is deducted. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 38 + employment_income: 36_000 + child_support_received: 3_600 + person2: + age: 40 + employment_income: 28_000 + child_support_expense: 4_200 + person3: + age: 8 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: NATURAL_GAS + tanf: 0 + snap: 0 + households: + household: + members: [person1, person2, person3] + state_code: WI + output: + # Manual 3.3.3 and Table 3.5: child support received counts; court-ordered + # child support paid is deducted. + # 36,000 + 28,000 + 3,600 - 4,200 = 63,400. + wi_wheap_countable_income: 63_400 + +- name: Case 9, unemployment compensation is ignored while gambling winnings and W-2 cash count. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 29 + unemployment_compensation: 4_200 + gambling_winnings: 650 + person2: + age: 4 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + tanf: 3_648 + snap: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + # Table 3.6 ignores unemployment insurance; Table 3.4 counts gambling + # winnings and W-2 cash grants (six months x 608 = 3,648). + # 650 + 3,648 = 4,298. + wi_wheap_countable_income: 4_298 + +- name: Case 10, child support paid above income leaves countable income at zero. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 44 + employment_income: 4_000 + child_support_expense: 6_000 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + tanf: 0 + snap: 0 + households: + household: + members: [person1] + state_code: WI + output: + # 4,000 - 6,000 = -2,000, floored at 0. + wi_wheap_countable_income: 0 + +- name: Case 11, every listed business, rental and unearned source counts, including the state SSI line and the Caretaker Supplement. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 45 + # SSI 994 a month; state SSI line 83.78 a month in January-April and + # 92.16 from May; Caretaker Supplement 275 a month for one child. + ssi: 11_928 + wi_ssp_regular: 1_072.40 + wi_cts: 3_300 + person2: + age: 9 + person3: + age: 66 + ssi: 0 + farm_operations_income: 1_000 + # Enters partnership_s_corp_income. + partnership_income: 2_000 + farm_rent_income: 3_000 + # Enters pension_income. + taxable_private_pension_income: 4_000 + # Enters military_retirement_income. + military_retirement_pay: 5_000 + veterans_benefits: 6_000 + workers_compensation: 7_000 + disability_benefits: 8_000 + alimony_income: 9_000 + strike_benefits: 1_100 + general_assistance: 1_200 + estate_income: 1_300 + financial_assistance: 1_400 + survivor_benefits: 1_500 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: NATURAL_GAS + tanf: 0 + snap: 0 + households: + household: + members: [person1, person2, person3] + state_code: WI + output: + # Business and rental offset group (person3): farm operations 1,000 + + # partnership 2,000 + farm rent 3,000 = 6,000. + # person3 unearned: pension 4,000 + military retirement 5,000 + veterans + # 6,000 + workers' compensation 7,000 + disability 8,000 + alimony 9,000 + # = 39,000; strike 1,100 + general assistance 1,200 + trust 1,300 + + # financial assistance 1,400 + survivor 1,500 = 6,500; 45,500 in all. + # person1, Table 3.4 federal and state SSI lines plus C-Supp: + # 994 x 12 = 11,928; 4 x 83.78 + 8 x 92.16 = 1,072.40; 275 x 12 = 3,300; + # 16,300.40 in all. person3's Part B premium nets only against Social + # Security and Railroad Retirement, both 0. + # 6,000 + 45,500 + 16,300.40 = 67,800.40. + wi_wheap_countable_income: 67_800.40 + +- name: Case 12, a trust loss counts as zero and does not reduce wages. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 52 + employment_income_before_lsr: 40_000 + estate_income: -2_500 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + tanf: 0 + snap: 0 + households: + household: + members: [person1] + state_code: WI + output: + # Each unearned source is floored at zero per member: + # 40,000 + max(-2,500, 0) = 40,000. Without the floor, 37,500. + wi_wheap_countable_income: 40_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/doa/wheap/payment/wi_wheap_heating_cost.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/doa/wheap/payment/wi_wheap_heating_cost.yaml new file mode 100644 index 00000000000..3ca2ae1cd6a --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/doa/wheap/payment/wi_wheap_heating_cost.yaml @@ -0,0 +1,240 @@ +# Wisconsin WHEAP benefit heating cost (FFY2026 HE+ Benefit Calculator, +# Computation C19-C35 and HT_Proxy A4:H28; PY2026 WHEAP Manual 2.3.1 and +# 4.4.13-4.4.28). Heat proxy = base + per-room amount x rooms by fuel class and +# dwelling type, rooms = bedrooms held between 1 and 9 (1 for a motel or +# camper). The reported cost, less a 60 percent baseload for electric heat +# (solar heat uses the electric row), is held between 0.75 and 2 times the +# proxy; heat included in rent uses 0.75 times the proxy. + +- name: Case 1, natural gas cost inside the bounds is used as reported. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + heating_expense: 950 + households: + household: + members: [person1] + state_code: WI + bedrooms: 3 + output: + # Dwelling type not supplied: SINGLE_FAMILY default. Proxy 354 + 60 x 3 = + # 534; bounds 400.50 to 1,068; 950 is inside. + wi_wheap_heating_cost: 950 + +- name: Case 2, cost below 0.75 times the proxy is raised to that floor. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + heating_expense: 300 + wi_wheap_dwelling_type: TWO_TO_FOUR_UNITS + households: + household: + members: [person1] + state_code: WI + bedrooms: 2 + output: + # 2 to 4 units, natural gas: 297 + 65 x 2 = 427; 0.75 x 427 = 320.25 > 300. + wi_wheap_heating_cost: 320.25 + +- name: Case 3, cost above twice the proxy is capped. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + spm_units: + spm_unit: + members: [person1] + heating_type: PROPANE + heating_expense: 2_400 + wi_wheap_dwelling_type: MOBILE_HOME + households: + household: + members: [person1] + state_code: WI + bedrooms: 2 + output: + # Mobile home, propane: 723 + 69 x 2 = 861; 2 x 861 = 1,722 < 2,400. + wi_wheap_heating_cost: 1_722 + +- name: Case 4, heat included in rent uses 0.75 times the proxy and a studio counts one room. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + heating_expense: 0 + heat_expense_included_in_rent: true + wi_wheap_dwelling_type: APARTMENT + households: + household: + members: [person1] + state_code: WI + bedrooms: 0 + output: + # Manual 4.4.17 and calculator Benefits!C12: at least 1 room. + # Apartment, natural gas: 116 + 68 x 1 = 184; 0.75 x 184 = 138. + wi_wheap_heating_cost: 138 + +- name: Case 5, electric heat removes a 60 percent baseload from the whole electric bill. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + heating_expense: 1_200 + wi_wheap_dwelling_type: APARTMENT + households: + household: + members: [person1] + state_code: WI + bedrooms: 2 + output: + # Eligible cost 1,200 - 0.6 x 1,200 = 480. Apartment, electric: + # 88 + 111 x 2 = 310; bounds 232.50 to 620; 480 is inside. + wi_wheap_heating_cost: 480 + +- name: Case 6, a motel or camper counts one room regardless of bedrooms. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + spm_units: + spm_unit: + members: [person1] + heating_type: PROPANE + heating_expense: 1_700 + wi_wheap_dwelling_type: MOTEL_OR_CAMPER + households: + household: + members: [person1] + state_code: WI + bedrooms: 2 + output: + # Manual 4.4.17: one room. Propane: 751 + 51 x 1 = 802; cap 1,604 < 1,700. + # Two rooms would give a cap of 1,706. + wi_wheap_heating_cost: 1_604 + +- name: Case 7, rooms above nine are capped at nine. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + spm_units: + spm_unit: + members: [person1] + heating_type: FUEL_OIL + heating_expense: 4_000 + wi_wheap_dwelling_type: SINGLE_FAMILY + households: + household: + members: [person1] + state_code: WI + bedrooms: 10 + output: + # Single family, fuel oil: 1,221 + 68 x 9 = 1,833; cap 3,666 < 4,000. + # Ten rooms would give a cap of 3,802. + wi_wheap_heating_cost: 3_666 + +- name: Case 8, kerosene uses the wood-or-other proxy. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + spm_units: + spm_unit: + members: [person1] + heating_type: KEROSENE + heating_expense: 1_800 + wi_wheap_dwelling_type: SINGLE_FAMILY + households: + household: + members: [person1] + state_code: WI + bedrooms: 3 + output: + # Manual 4.4.28.3 places kerosene in wood and other: 524 + 89 x 3 = 791; + # cap 1,582 < 1,800. The fuel oil proxy would allow 2,850. + wi_wheap_heating_cost: 1_582 + +- name: Case 9, solar heat uses the electricity row and its baseload. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + spm_units: + spm_unit: + members: [person1] + heating_type: SOLAR + # heating_expense reads a grid-tied solar home's electric bill. + pre_subsidy_electricity_expense: 1_500 + wi_wheap_dwelling_type: SINGLE_FAMILY + households: + household: + members: [person1] + state_code: WI + bedrooms: 3 + output: + # Eligible cost 1,500 - 0.6 x 1,500 = 600. Single family, electric: + # 261 + 108 x 3 = 585; bounds 438.75 to 1,170; 600 is inside. Without + # the baseload the 1,500 bill would be capped at 1,170. + wi_wheap_heating_cost: 600 + +- name: Case 10, heat included in rent uses 0.75 times the proxy even with a reported gas bill. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + # Gas for cooking and the water heater; heat is paid in the rent. + gas_expense: 360 + heat_expense_included_in_rent: true + wi_wheap_dwelling_type: APARTMENT + households: + household: + members: [person1] + state_code: WI + bedrooms: 2 + output: + # Apartment, natural gas: 116 + 68 x 2 = 252; 0.75 x 252 = 189. + # The 360 bill lies inside the bounds 189 to 504 and would be used if + # heat in rent were ignored. + wi_wheap_heating_cost: 189 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/doa/wheap/wi_wheap.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/doa/wheap/wi_wheap.yaml new file mode 100644 index 00000000000..eb3ea7ff058 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/doa/wheap/wi_wheap.yaml @@ -0,0 +1,331 @@ +# Wisconsin WHEAP regular heating benefit (FFY2026 HE+ Benefit Calculator, +# Computation C5-C43; PY2026 WHEAP Manual 2.3.1; FFY2026 agency plan 2.6). +# Poverty ratio = income / 2025 poverty guideline for the eligible-member size +# (15,650 + 5,500 per added person), held between 0.60 and 2.15; a +# categorically eligible household above its limit uses the limit as income. +# Raw = (2.66667 - ratio) / 2.94269 x benefit heating cost, truncated to whole +# dollars, capped at 2,580 and paid only when at least 30. + +- name: Case 1, Milwaukee family heating a single-family home with natural gas. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 36 + employment_income: 39_520 + person2: + age: 34 + person3: + age: 8 + person4: + age: 4 + spm_units: + spm_unit: + members: [person1, person2, person3, person4] + heating_type: NATURAL_GAS + gas_expense: 1_000 + wi_wheap_dwelling_type: SINGLE_FAMILY + tanf: 0 + snap: 0 + households: + household: + members: [person1, person2, person3, person4] + state_code: WI + bedrooms: 3 + output: + # Nursing assistant at 19 an hour x 2,080 hours = 39,520; size 4 limit + # 73,888. Guideline 15,650 + 3 x 5,500 = 32,150; ratio 1.22924. + # The gas bill reaches heating_expense through the natural gas input. + # Proxy 354 + 60 x 3 = 534, bounds 400.50 to 1,068, cost 1,000. + # (2.66667 - 1.22924) / 2.94269 x 1,000 = 488.48 -> 488. + wi_wheap_household_size: 4 + wi_wheap_countable_income: 39_520 + wi_wheap_income_limit: 73_888 + wi_wheap_categorically_eligible: false + wi_wheap_eligible: true + wi_wheap_heating_cost: 1_000 + wi_wheap: 488 + +- name: Case 2, poverty ratio below 0.60 is raised to 0.60 and the raw amount is truncated. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + employment_income: 12_000 + person2: + age: 9 + person3: + age: 6 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: NATURAL_GAS + heating_expense: 800 + wi_wheap_dwelling_type: TWO_TO_FOUR_UNITS + tanf: 0 + snap: 0 + households: + household: + members: [person1, person2, person3] + state_code: WI + bedrooms: 2 + output: + # Ratio 12,000 / 26,650 = 0.45 -> 0.60. Proxy 297 + 65 x 2 = 427, cost 800 + # inside 320.25 to 854. (2.66667 - 0.6) / 2.94269 x 800 = 561.85; + # TRUNC gives 561, not 562. + wi_wheap: 561 + +- name: Case 3, poverty ratio above 2.15 is held at 2.15. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 28 + employment_income: 36_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + heating_expense: 900 + wi_wheap_dwelling_type: APARTMENT + tanf: 0 + snap: 0 + households: + household: + members: [person1] + state_code: WI + bedrooms: 1 + output: + # 36,000 <= size 1 limit 38,421; ratio 36,000 / 15,650 = 2.30 -> 2.15. + # Electric cost 900 x 0.4 = 360, inside 149.25 to 398 (proxy 88 + 111). + # (2.66667 - 2.15) / 2.94269 x 360 = 63.21 -> 63. + wi_wheap: 63 + +- name: Case 4, categorically eligible household above its limit is paid as if income equaled the limit. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 44 + employment_income: 64_000 + person2: + age: 42 + employment_income: 40_000 + person3: + age: 15 + person4: + age: 13 + person5: + age: 11 + person6: + age: 9 + person7: + age: 6 + person8: + age: 3 + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5, person6, person7, person8] + heating_type: NATURAL_GAS + heating_expense: 1_150 + wi_wheap_dwelling_type: SINGLE_FAMILY + snap: 1_200 + tanf: 0 + households: + household: + members: [person1, person2, person3, person4, person5, person6, person7, person8] + state_code: WI + bedrooms: 4 + output: + # Manual 2.3.1 item 1: benefit income = size 8 limit 101,965, not 104,000. + # Guideline 15,650 + 7 x 5,500 = 54,150; ratio 1.88301. + # Proxy 354 + 60 x 4 = 594, cost 1,150 inside 445.50 to 1,188. + # (2.66667 - 1.88301) / 2.94269 x 1,150 = 306.25 -> 306 + # (actual income would give 291). + wi_wheap_countable_income: 104_000 + wi_wheap_income_limit: 101_965 + wi_wheap_categorically_eligible: true + wi_wheap_eligible: true + wi_wheap_heating_cost: 1_150 + wi_wheap: 306 + +- name: Case 5, poverty guideline uses the eligible-member size while all income counts. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 33 + immigration_status: UNDOCUMENTED + employment_income: 30_000 + person2: + age: 31 + immigration_status: LEGAL_PERMANENT_RESIDENT + person3: + age: 5 + immigration_status: CITIZEN + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: NATURAL_GAS + heating_expense: 700 + wi_wheap_dwelling_type: TWO_TO_FOUR_UNITS + tanf: 0 + snap: 0 + households: + household: + members: [person1, person2, person3] + state_code: WI + bedrooms: 2 + output: + # Manual 4.4.26.2: size 2; income 30,000 counts. Guideline 21,150; + # ratio 1.41844. Cost 700 inside 320.25 to 854. + # (2.66667 - 1.41844) / 2.94269 x 700 = 296.93 -> 296 + # (size 3 would give 366). + wi_wheap_household_size: 2 + wi_wheap: 296 + +- name: Case 6, benefit of exactly 30 dollars is paid. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 24 + employment_income: 31_500 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + heating_expense: 0 + heat_expense_included_in_rent: true + wi_wheap_dwelling_type: APARTMENT + tanf: 0 + snap: 0 + households: + household: + members: [person1] + state_code: WI + bedrooms: 0 + output: + # Studio with heat in rent: 0 bedrooms count as 1 room (Manual 4.4.17), + # cost 0.75 x (116 + 68) = 138. Ratio 31,500 / 15,650 = 2.01278. + # (2.66667 - 2.01278) / 2.94269 x 138 = 30.66 -> 30, which meets the + # 30 minimum. + wi_wheap: 30 + +- name: Case 7, raw amount of 29.77 dollars is truncated below the minimum and pays nothing. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 24 + employment_income: 31_800 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + heating_expense: 0 + heat_expense_included_in_rent: true + wi_wheap_dwelling_type: APARTMENT + tanf: 0 + snap: 0 + households: + household: + members: [person1] + state_code: WI + bedrooms: 0 + output: + # Ratio 31,800 / 15,650 = 2.03195; (2.66667 - 2.03195) / 2.94269 x 138 = + # 29.77 -> 29 < 30, so 0 (Manual 2.3.1: no payment below 30). Rounding + # would have given 30. + wi_wheap_eligible: true + wi_wheap: 0 + +- name: Case 8, noncitizen with Temporary Protected Status who receives Social Security is counted and paid. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 63 + immigration_status: TPS + social_security_retirement: 14_400 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + heating_expense: 900 + wi_wheap_dwelling_type: SINGLE_FAMILY + tanf: 0 + snap: 0 + households: + household: + members: [person1] + state_code: WI + bedrooms: 2 + output: + # Manual 3.2.2.4: a non-citizen receiving monthly Social Security is an + # eligible non-citizen, so size 1 (status alone would give size 0 and no + # case head). Early retirement benefit of 1,200 a month = 14,400; no + # Medicare Part B premium before 65. Size 1 limit 38,421. + # Ratio 14,400 / 15,650 = 0.92013. Proxy 354 + 60 x 2 = 474, bounds + # 355.50 to 948, cost 900. + # (2.66667 - 0.92013) / 2.94269 x 900 = 534.17 -> 534. + wi_wheap_household_size: 1 + wi_wheap_countable_income: 14_400 + wi_wheap_income_limit: 38_421 + wi_wheap_eligible: true + wi_wheap_heating_cost: 900 + wi_wheap: 534 + +- name: Case 9, Social Security of a spouse with Temporary Protected Status raises the size and limit. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 60 + immigration_status: CITIZEN + employment_income: 30_000 + person2: + age: 63 + immigration_status: TPS + social_security_retirement: 14_400 + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + filing_status: JOINT + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + heating_expense: 900 + wi_wheap_dwelling_type: SINGLE_FAMILY + tanf: 0 + snap: 0 + households: + household: + members: [person1, person2] + state_code: WI + bedrooms: 2 + output: + # Retail cashier at 15 an hour x 2,000 hours = 30,000. Size 2 + # (Manual 3.2.2.4), limit 50,243; income 30,000 + 14,400 = 44,400 is + # eligible (size 1 would give limit 38,421 and no benefit). + # Ratio 44,400 / 21,150 = 2.09929, inside 0.60 to 2.15; cost 900. + # (2.66667 - 2.09929) / 2.94269 x 900 = 173.53 -> 173. + wi_wheap_household_size: 2 + wi_wheap_countable_income: 44_400 + wi_wheap_income_limit: 50_243 + wi_wheap_eligible: true + wi_wheap: 173 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/doa/wheap/wi_wheap_household_size.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/doa/wheap/wi_wheap_household_size.yaml new file mode 100644 index 00000000000..4ca3621a57e --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/doa/wheap/wi_wheap_household_size.yaml @@ -0,0 +1,34 @@ +# Wisconsin WHEAP household size counts citizens, qualified noncitizens and +# noncitizens receiving Social Security (PY2026 WHEAP Manual 3.2.2.4). +# Manual 4.4.26.2: "The HE+ System will not count ineligible non-citizens as +# a household members, but it will count their income"; 3.2.6.2 enters them +# with their status flagged. + +- name: Case 1, ineligible noncitizens are not counted in a mixed-status household. + period: 2026 + input: + people: + person1: + age: 45 + immigration_status: CITIZEN + person2: + age: 43 + immigration_status: LEGAL_PERMANENT_RESIDENT + person3: + age: 20 + immigration_status: DACA + person4: + age: 50 + immigration_status: UNDOCUMENTED + spm_units: + spm_unit: + members: [person1, person2, person3, person4] + heating_type: NATURAL_GAS + households: + household: + members: [person1, person2, person3, person4] + state_code: WI + output: + # Citizen and lawful permanent resident (qualified noncitizen) count; + # DACA and undocumented members do not: 2 of 4. + wi_wheap_household_size: 2 diff --git a/policyengine_us/variables/gov/states/wi/doa/wheap/eligibility/wi_wheap_categorically_eligible.py b/policyengine_us/variables/gov/states/wi/doa/wheap/eligibility/wi_wheap_categorically_eligible.py new file mode 100644 index 00000000000..1f32b8c8bec --- /dev/null +++ b/policyengine_us/variables/gov/states/wi/doa/wheap/eligibility/wi_wheap_categorically_eligible.py @@ -0,0 +1,44 @@ +from policyengine_us.model_api import * + + +class wi_wheap_categorically_eligible(Variable): + value_type = bool + entity = SPMUnit + definition_period = YEAR + label = "Wisconsin WHEAP categorical eligibility" + defined_for = StateCode.WI + reference = ( + "https://docs.legis.wisconsin.gov/statutes/statutes/16/i/27", + # Section 3.3.1 item 2, including item 2(d). + # PDF pages 45-46 + "https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=45", + "https://liheapch.acf.gov/docs/2026/state-plans/WI_Plan_2026.pdf#page=5", + ) + + def formula(spm_unit, period, parameters): + # Every member, including ineligible non-citizens, must receive W-2 + # cash, FoodShare or SSI. FoodShare covers only members of the SNAP + # unit, and W-2 only citizens and qualified non-citizens. Unit-level + # receipt approximates each member's group membership, and receipt in + # any month approximates receipt for the entire prior month. + person = spm_unit.members + # Monthly flags are read month by month rather than as an annual + # aggregate, which would cache a summed value under the annual key + # and change what other formulas read. + first_month = period.first_month + months = [first_month.offset(offset) for offset in range(MONTHS_IN_YEAR)] + reported_ssi = np.any( + [person("receives_ssi", month) for month in months], axis=0 + ) + receives_ssi = (add(person, period, ["ssi"]) > 0) | reported_ssi + in_snap_unit = np.any( + [~person("is_snap_excluded_member", month) for month in months], + axis=0, + ) + receives_snap = spm_unit("snap", period, options=[ADD]) > 0 + foodshare_member = in_snap_unit & spm_unit.project(receives_snap) + receives_w2_cash = spm_unit("tanf", period) > 0 + w2_member = person("is_citizen_or_legal_immigrant", period) & spm_unit.project( + receives_w2_cash + ) + return spm_unit.all(receives_ssi | foodshare_member | w2_member) diff --git a/policyengine_us/variables/gov/states/wi/doa/wheap/eligibility/wi_wheap_eligible.py b/policyengine_us/variables/gov/states/wi/doa/wheap/eligibility/wi_wheap_eligible.py new file mode 100644 index 00000000000..571792bc398 --- /dev/null +++ b/policyengine_us/variables/gov/states/wi/doa/wheap/eligibility/wi_wheap_eligible.py @@ -0,0 +1,53 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.hhs.tax_unit_fpg import fpg + + +class wi_wheap_eligible(Variable): + value_type = bool + entity = SPMUnit + definition_period = YEAR + label = "Eligible for the Wisconsin WHEAP regular heating benefit" + defined_for = StateCode.WI + reference = ( + "https://docs.legis.wisconsin.gov/statutes/statutes/16/i/27", + # Sections 3.2.1, 3.2.5, 3.3.1 and 4.4.28.3. + # PDF pages 33, 41-43, 45-46, 87 + "https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=33", + "https://www.law.cornell.edu/uscode/text/42/8624#b_2_B", + ) + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.wi.doa.wheap.eligibility + # A positive size means a citizen or eligible non-citizen can serve as + # case head. + size = spm_unit("wi_wheap_household_size", period) + income = spm_unit("wi_wheap_countable_income", period) + limit = spm_unit("wi_wheap_income_limit", period) + # 42 U.S.C. 8624(b)(2)(B) bars excluding a household on income below + # 110% of the poverty guideline. + state_group = spm_unit.household("state_group_str", period) + guideline = fpg(size, state_group, period, parameters, year_lag=p.fpg_year_lag) + federal_floor = np.round( + guideline * parameters(period).gov.hhs.liheap.fpg_floor, 2 + ) + categorical = spm_unit("wi_wheap_categorically_eligible", period) + income_eligible = (income <= limit) | (income < federal_floor) | categorical + heating_type = spm_unit("heating_type", period) + fuel = heating_type.possible_values + # An application without a fuel type is denied (section 4.4.28.3). + heating_reported = (heating_type != fuel.UNSPECIFIED) & ( + heating_type != fuel.NONE + ) + # Heat included in rent is an energy burden unless the household + # receives rental assistance or lives in public housing. Section 42 + # tax credit units keep the burden but are not distinguishable, and + # shared-meter arrangements without payment are not identified. + heat_in_rent = spm_unit("heat_expense_included_in_rent", period) + subsidized = spm_unit("receives_housing_assistance", period) | ( + spm_unit.household("is_in_public_housing", period) + ) + energy_burden = spm_unit("has_heating_expense", period) | ( + heat_in_rent & ~subsidized + ) + # No asset test applies to regular benefits. + return (size > 0) & income_eligible & heating_reported & energy_burden diff --git a/policyengine_us/variables/gov/states/wi/doa/wheap/eligibility/wi_wheap_income_limit.py b/policyengine_us/variables/gov/states/wi/doa/wheap/eligibility/wi_wheap_income_limit.py new file mode 100644 index 00000000000..e2bc5b47e25 --- /dev/null +++ b/policyengine_us/variables/gov/states/wi/doa/wheap/eligibility/wi_wheap_income_limit.py @@ -0,0 +1,27 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.hhs.hhs_smi import liheap_smi_limit + + +class wi_wheap_income_limit(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Wisconsin WHEAP annual income limit" + defined_for = StateCode.WI + reference = ( + "https://docs.legis.wisconsin.gov/statutes/statutes/16/i/27", + # Section 3.3.1 and Appendix E. + # PDF pages 45, 166 + "https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=45", + "https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx", + ) + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.wi.doa.wheap.eligibility + size = spm_unit("wi_wheap_household_size", period) + state = spm_unit.household("state_code_str", period) + # The annual limit approximates the annualized one-month test; twelve + # times each printed one-month limit is within 4 cents of it. + limit = liheap_smi_limit(size, state, p.smi_rate, period, parameters) + return where(size > 0, limit, 0) diff --git a/policyengine_us/variables/gov/states/wi/doa/wheap/income/wi_wheap_countable_income.py b/policyengine_us/variables/gov/states/wi/doa/wheap/income/wi_wheap_countable_income.py new file mode 100644 index 00000000000..b7f5dfc8895 --- /dev/null +++ b/policyengine_us/variables/gov/states/wi/doa/wheap/income/wi_wheap_countable_income.py @@ -0,0 +1,70 @@ +from policyengine_us.model_api import * + + +class wi_wheap_countable_income(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Wisconsin WHEAP countable income" + defined_for = StateCode.WI + reference = ( + # Sections 3.3.2-3.3.5 and Tables 3.4-3.6. + # PDF pages 45-63 + "https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=45", + ) + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.wi.doa.wheap.income + person = spm_unit.members + age = person("age", period) + # Earned income of minors and of high school students 18 and older is + # ignored; is_in_k12_school identifies the students. + excluded = (age < p.adult_age) | person("is_in_k12_school", period) + wages = 0 + for source in p.sources.earned: + wages = wages + max_(person(source, period), 0) + # Self-employment, rental and royalty profits and losses offset each + # other across sources and members; a net loss counts as zero and + # cannot offset other income. + business = where( + excluded, 0, add(person, period, p.sources.self_employment) + ) + add(person, period, p.sources.rental) + unearned = 0 + for source in p.sources.unearned: + unearned = unearned + max_(person(source, period, options=[ADD]), 0) + # All withdrawals count from age 73. Regular withdrawals before 73 + # also count but cannot be told apart from lump sums, which are + # ignored as asset drawdowns. + distributions = where( + age >= p.retirement_distribution_age, + max_(person("retirement_distributions", period), 0), + 0, + ) + # Social Security and Railroad Retirement count net of Medicare + # premiums; Part C and Part D premiums have no input. + retirement_benefits = max_(person("social_security", period), 0) + max_( + person("railroad_benefits", period), 0 + ) + net_retirement_benefits = max_( + retirement_benefits - person("medicare_part_b_premium", period), 0 + ) + investment = 0 + for source in p.sources.investment: + investment = investment + max_(person(source, period), 0) + counted_investment = where(investment >= p.investment_minimum, investment, 0) + personal = ( + where(excluded, 0, wages) + + unearned + + distributions + + net_retirement_benefits + + counted_investment + ) + net_business = max_(spm_unit.sum(business), 0) + # W-2 cash grants count; unemployment compensation is ignored. + w2_cash = max_(spm_unit("tanf", period), 0) + deductions = add(spm_unit, period, p.deductions) + # Annual amounts approximate the prior-month test period. The extra + # paycheck rule, the present-month option and tribal per capita + # payments are not modeled. + return max_(spm_unit.sum(personal) + net_business + w2_cash - deductions, 0) diff --git a/policyengine_us/variables/gov/states/wi/doa/wheap/payment/wi_wheap_dwelling_type.py b/policyengine_us/variables/gov/states/wi/doa/wheap/payment/wi_wheap_dwelling_type.py new file mode 100644 index 00000000000..c520f7d71b3 --- /dev/null +++ b/policyengine_us/variables/gov/states/wi/doa/wheap/payment/wi_wheap_dwelling_type.py @@ -0,0 +1,22 @@ +from policyengine_us.model_api import * + + +class WIWHEAPDwellingType(Enum): + SINGLE_FAMILY = "Single family" + TWO_TO_FOUR_UNITS = "2- to 4-unit building" + APARTMENT = "Apartment or multi-unit building" + MOBILE_HOME = "Mobile home" + MOTEL_OR_CAMPER = "Motel or camper/RV" + + +class wi_wheap_dwelling_type(Variable): + value_type = Enum + entity = SPMUnit + possible_values = WIWHEAPDwellingType + default_value = WIWHEAPDwellingType.SINGLE_FAMILY + definition_period = YEAR + label = "Wisconsin WHEAP housing type" + defined_for = StateCode.WI + # Section 4.4.13 housing types. + # PDF pages 76-77 + reference = "https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=76" diff --git a/policyengine_us/variables/gov/states/wi/doa/wheap/payment/wi_wheap_heating_cost.py b/policyengine_us/variables/gov/states/wi/doa/wheap/payment/wi_wheap_heating_cost.py new file mode 100644 index 00000000000..690d014a1b3 --- /dev/null +++ b/policyengine_us/variables/gov/states/wi/doa/wheap/payment/wi_wheap_heating_cost.py @@ -0,0 +1,88 @@ +from policyengine_us.model_api import * + + +class wi_wheap_heating_cost(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Wisconsin WHEAP benefit heating cost" + defined_for = StateCode.WI + reference = ( + # Section 2.3.1 and sections 4.4.17 and 4.4.28.2-4.4.28.3. + # PDF pages 26-27, 79, 86-87 + "https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=26", + "https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx", + ) + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.wi.doa.wheap.payment + dwelling = spm_unit("wi_wheap_dwelling_type", period) + dwelling_types = dwelling.possible_values + # Bedrooms approximate the room count, which also includes kitchens + # and living rooms. HE+ accepts 1 to 9 rooms (section 4.4.17; the + # calculator's Benefits!C12 rejects 0), so a studio counts one room. + # Motels and campers count one room. + bedrooms = spm_unit.household("bedrooms", period) + rooms = where( + dwelling == dwelling_types.MOTEL_OR_CAMPER, + 1, + clip(bedrooms, 1, p.proxy.max_rooms), + ) + heating_type = spm_unit("heating_type", period) + fuel = heating_type.possible_values + is_electricity = (heating_type == fuel.ELECTRICITY) | ( + heating_type == fuel.SOLAR + ) + # Kerosene, coal and other fuels use the wood-or-other row. Solar heat + # uses the electricity row, as heating_expense reads a grid-tied solar + # home's electric bill. + fuel_classes = [ + heating_type == fuel.FUEL_OIL, + heating_type == fuel.NATURAL_GAS, + heating_type == fuel.PROPANE, + is_electricity, + (heating_type == fuel.WOOD) + | (heating_type == fuel.KEROSENE) + | (heating_type == fuel.COAL) + | (heating_type == fuel.OTHER), + ] + base = select( + fuel_classes, + [ + p.proxy.base.fuel_oil[dwelling], + p.proxy.base.natural_gas[dwelling], + p.proxy.base.propane[dwelling], + p.proxy.base.electricity[dwelling], + p.proxy.base.wood_other[dwelling], + ], + default=0, + ) + per_room = select( + fuel_classes, + [ + p.proxy.per_room.fuel_oil[dwelling], + p.proxy.per_room.natural_gas[dwelling], + p.proxy.per_room.propane[dwelling], + p.proxy.per_room.electricity[dwelling], + p.proxy.per_room.wood_other[dwelling], + ], + default=0, + ) + proxy = base + per_room * rooms + # An electric heating bill also covers non-heating use. + reported = spm_unit("heating_expense", period) + baseload = where(is_electricity, reported * p.electric_baseload_rate, 0) + eligible_cost = max_(reported - baseload, 0) + lower = p.proxy.floor_rate * proxy + upper = p.proxy.cap_rate * proxy + # Shared and business meters also use the proxy floor but cannot be + # identified from existing inputs. + heat_in_rent = spm_unit("heat_expense_included_in_rent", period) + cost = where( + heat_in_rent | (eligible_cost <= 0), + lower, + clip(eligible_cost, lower, upper), + ) + supported = np.any(fuel_classes, axis=0) + return where(supported, cost, 0) diff --git a/policyengine_us/variables/gov/states/wi/doa/wheap/wi_wheap.py b/policyengine_us/variables/gov/states/wi/doa/wheap/wi_wheap.py new file mode 100644 index 00000000000..06d28ac416f --- /dev/null +++ b/policyengine_us/variables/gov/states/wi/doa/wheap/wi_wheap.py @@ -0,0 +1,50 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.hhs.tax_unit_fpg import fpg + + +class wi_wheap(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Wisconsin WHEAP regular heating benefit" + defined_for = "wi_wheap_eligible" + reference = ( + "https://docs.legis.wisconsin.gov/statutes/statutes/16/i/27", + # Section 2.3.1. + "https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=26", + "https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx", + "https://liheapch.acf.gov/docs/2026/state-plans/WI_Plan_2026.pdf#page=5", + "https://web.archive.org/web/20260316195114/https://energyandhousing.wi.gov/Documents/WHEAP/LiheapPlan_FFY2026.pdf#page=7", + ) + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.wi.doa.wheap + payment = p.payment + size = spm_unit("wi_wheap_household_size", period) + state_group = spm_unit.household("state_group_str", period) + guideline = fpg( + size, state_group, period, parameters, year_lag=p.eligibility.fpg_year_lag + ) + income = spm_unit("wi_wheap_countable_income", period) + limit = spm_unit("wi_wheap_income_limit", period) + # Categorically eligible households above the limit are paid as if + # their income equaled it. Calculator cell C40 computes these with + # actual income; this follows manual 2.3.1 item 1 and plan 1.6. + categorical = spm_unit("wi_wheap_categorically_eligible", period) + benefit_income = where(categorical, min_(income, limit), income) + poverty_ratio = np.clip( + benefit_income / guideline, + payment.poverty_ratio.minimum, + payment.poverty_ratio.maximum, + ) + heating_cost = spm_unit("wi_wheap_heating_cost", period) + formula_amount = ( + (payment.benefit_ratio - poverty_ratio) + / payment.funding_factor + * heating_cost + ) + amount = min_(np.floor(formula_amount), payment.maximum) + # The February 1 reduction, supplemental benefits and the separate + # non-heating electric benefit are not modeled. + return where(amount >= payment.minimum, amount, 0) diff --git a/policyengine_us/variables/gov/states/wi/doa/wheap/wi_wheap_household_size.py b/policyengine_us/variables/gov/states/wi/doa/wheap/wi_wheap_household_size.py new file mode 100644 index 00000000000..de380da566c --- /dev/null +++ b/policyengine_us/variables/gov/states/wi/doa/wheap/wi_wheap_household_size.py @@ -0,0 +1,26 @@ +from policyengine_us.model_api import * + + +class wi_wheap_household_size(Variable): + value_type = int + entity = SPMUnit + definition_period = YEAR + label = "Wisconsin WHEAP household size" + defined_for = StateCode.WI + # Sections 3.2.2.4, 3.2.6.2, 3.2.6.4 and 4.4.26.2. + # PDF pages 35, 44-45, 82 + reference = "https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=35" + + def formula(spm_unit, period, parameters): + person = spm_unit.members + # Citizens, qualified non-citizens and non-citizens receiving any + # Social Security are counted. Ineligible non-citizens are excluded + # from size, but their income counts. FoodShare receipt has no + # person-level input, so non-citizens in FoodShare are counted only + # through their immigration status. Court-placed foster children are + # counted because no input identifies a court placement. SPM + # membership approximates the economic unit on the application date. + eligible_member = person("is_citizen_or_legal_immigrant", period) | ( + person("social_security", period) > 0 + ) + return spm_unit.sum(eligible_member) diff --git a/source_conflicts/candidates.yaml b/source_conflicts/candidates.yaml index 5e4066769ca..1bd189c54c3 100644 --- a/source_conflicts/candidates.yaml +++ b/source_conflicts/candidates.yaml @@ -8656,6 +8656,142 @@ candidates: state: ruled note: An October 2026 ruling followed the worksheet; the statute-first rule requires including the repayment deduction if the statutory reading is confirmed. +- id: us-wi:liheap-categorical-benefit-income + program: liheap + years: + from: 2026 + to: 2026 + type: federal-fiscal + question: Is a categorically eligible Wisconsin WHEAP household above 60% of state median income paid as if its income + equaled the limit, as the manual and plan state, or on its actual income, as the HE+ calculator computes? + sources: + - kind: statute + citation: Wis. Stat. 16.27(6) + url: https://docs.legis.wisconsin.gov/statutes/statutes/16/i/27 + - kind: manual + citation: Wisconsin PY26 WHEAP Manual, section 2.3.1 item 1 + url: https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=26 + - kind: plan + citation: Wisconsin FY2026 LIHEAP State Plan, section 1.6 + url: https://liheapch.acf.gov/docs/2026/state-plans/WI_Plan_2026.pdf#page=5 + - kind: calculator + citation: Wisconsin HE+ Benefit Calculator FFY2026, Computation!C13, C39 and C40 + url: https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx + readings: + statute: Section 16.27(6) pays heating assistance under a benefit schedule established by the department based on + household income, family size and energy costs; manual 2.3.1 item 1 and plan 1.6 pay a categorically eligible + household over 60% SMI as if its income were 60% SMI. + other: Calculator cell C40 keeps the amount that C39 computes from the household's actual income. + example: An eight-person household with $104,000 of income, a $1,150 gas bill and four bedrooms receives $306 at the + $101,965 limit and $291 at its actual income. + pe_us: + follows: gap-fill + code: [] + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10434 + state: ruled + note: 'Ruled 2026-10-21 by the user: follow manual 2.3.1 item 1 and plan 1.6. Code not yet on main: wi_wheap.' +- id: us-wi:liheap-general-relief-and-lump-sums + program: liheap + years: + from: 2026 + to: 2026 + type: federal-fiscal + question: Does Wisconsin WHEAP count General Relief and ignore most non-recurring lump sums, as the manual lists, or + follow the plan's income checklist, which leaves General Assistance unchecked and checks one-time lump sums? + sources: + - kind: statute + citation: Wis. Stat. 16.27(5)(b) and (6) + url: https://docs.legis.wisconsin.gov/statutes/statutes/16/i/27 + - kind: manual + citation: Wisconsin PY26 WHEAP Manual, Table 3.4 General Relief and Table 3.6 Lump Sum Payments, Non-recurring, PDF + pages 55 and 61 + url: https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=55 + - kind: plan + citation: Wisconsin FY2026 LIHEAP State Plan, section 1.9 + url: https://liheapch.acf.gov/docs/2026/state-plans/WI_Plan_2026.pdf#page=6 + readings: + statute: Section 16.27 does not define countable income; manual Table 3.4 counts General Relief, including Refugee + Cash Assistance, and Table 3.6 ignores most non-recurring lump sums such as tax refunds and rebates. + other: Plan 1.9 leaves General Assistance benefits unchecked and checks one-time lump-sum payments such as rebates, + credits, lottery winnings and refund deposits. + example: A single adult with $1,200 of General Relief and no other income has countable income of $1,200 under the + manual and $0 under the plan. + pe_us: + follows: gap-fill + code: [] + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10434 + state: ruled + note: 'Ruled 2026-10-06: follow the manual over the plan. No rebate, credit or refund deposit is counted. Code not + yet on main: gov.states.wi.doa.wheap.income.sources.unearned.' +- id: us-wi:liheap-maximum-benefit + program: liheap + years: + from: 2026 + to: 2026 + type: federal-fiscal + question: Is the Wisconsin WHEAP FFY2026 maximum regular heating benefit $2,580 as the agency-posted plan states, $2,147 + as the ACF-posted plan prints, or $2,575 as the HE+ calculator sets? + sources: + - kind: statute + citation: Wis. Stat. 16.27(6) + url: https://docs.legis.wisconsin.gov/statutes/statutes/16/i/27 + - kind: plan + citation: Wisconsin LIHEAP State Plan FFY2026, agency-posted edition, section 2.6 + url: https://web.archive.org/web/20260316195114/https://energyandhousing.wi.gov/Documents/WHEAP/LiheapPlan_FFY2026.pdf#page=7 + - kind: plan + citation: Wisconsin FY2026 LIHEAP State Plan as posted by ACF, section 2.6 + url: https://liheapch.acf.gov/docs/2026/state-plans/WI_Plan_2026.pdf#page=12 + - kind: calculator + citation: Wisconsin HE+ Benefit Calculator FFY2026, Computation!C17 + url: https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx + readings: + statute: Section 16.27(6) leaves the benefit schedule to the department and sets no maximum; the agency-posted plan + item 2.6 sets $2,580. + other: The ACF-posted plan item 2.6 prints $2,147 and calculator cell C17 sets $2,575. + example: A single-family fuel oil home of five rooms with a poverty ratio of 0.60 and costs above the $3,122 cap receives + $2,192 under the $2,580 or $2,575 maximum and $2,147 under the ACF-posted figure. + pe_us: + follows: gap-fill + code: [] + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10434 + state: ruled + note: 'Ruled 2026-10-21 by the user: $2,580. Code not yet on main: gov.states.wi.doa.wheap.payment.maximum.' +- id: us-wi:liheap-proxy-cost-bounds + program: liheap + years: + from: 2026 + to: 2026 + type: federal-fiscal + question: Is the Wisconsin WHEAP heating cost capped at twice a heat proxy equal to 75% of the table's average cost, as + the manual describes, or at twice the table amount with a floor of 0.75 times it, as the HE+ calculator computes? + sources: + - kind: statute + citation: Wis. Stat. 16.27(6) + url: https://docs.legis.wisconsin.gov/statutes/statutes/16/i/27 + - kind: manual + citation: Wisconsin PY26 WHEAP Manual, section 2.3.1 item 3 + url: https://web.archive.org/web/20260428215048/https://energyandhousing.wi.gov/Documents/WHEAP/WheapManual_PY26.pdf#page=27 + - kind: calculator + citation: Wisconsin HE+ Benefit Calculator FFY2026, Computation!C29, C30 and C31 + url: https://liheapch.acf.gov/docs/2026/benefits-matricies/WI_BenefitMatrix_2026.xlsx + readings: + statute: Section 16.27(6) bases the benefit schedule on energy costs and leaves the cost bounds to the department; + manual 2.3.1 item 3 sets the heat proxy at 75% of the average cost and caps costs at two times the proxy, or 1.5 + times the table amount. + other: Calculator cell C30 caps costs at 2 times and C31 floors them at 0.75 times the table amount. + example: A propane mobile home with two bedrooms and a $2,400 bill has a $861 table amount; the calculator caps its cost + at $1,722 and the manual reading at $1,291.50. + pe_us: + follows: gap-fill + code: [] + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10434 + state: ruled + note: 'Ruled 2026-10-21 by the user: calculator bounds. Code not yet on main: wi_wheap_heating_cost, + gov.states.wi.doa.wheap.payment.proxy.cap_rate.' - id: us-wi:medicaid-parent-student-option-full-time program: medicaid years: