From 2810f388f2d3085c9b6025c88ce58d4fd0bb5270 Mon Sep 17 00:00:00 2001 From: Ziming Date: Sat, 3 Oct 2026 23:39:58 -0400 Subject: [PATCH 1/9] Add Arizona LIHEAP Standard benefit (az_liheap) Encode Arizona's FY2026 Standard LIHEAP benefit from the DES LIHEAP Policy (revision 13 effective August 25, 2025 and revision 15 effective March 23, 2026), the $640-limit eligibility worksheet and the FY2026 state plan: household size of citizens and qualified noncitizens, gross countable income of adults with the dated under-18 unearned income rule, an income limit equal to the greater of 60 percent of state median income and 150 percent of the prior-year poverty guideline reproduced from the federal parameters, TANF or SNAP categorical eligibility, and the points worksheet (income ratio, energy burden with portable fuel, energy need) paying $160, $320, $480 or $640. Registers the program with verified_years "2026". Crisis assistance, evaporative-cooling water and weatherization are out of scope. Co-Authored-By: Claude Fable 5.1 --- changelog.d/az-liheap-wave1c.added.md | 1 + .../eligibility/categorical_programs.yaml | 20 + .../az/des/liheap/eligibility/fpg_rate.yaml | 13 + .../des/liheap/eligibility/fpg_year_lag.yaml | 17 + .../liheap/eligibility/max_table_size.yaml | 11 + .../az/des/liheap/eligibility/smi_rate.yaml | 15 + .../az/des/liheap/income/adult_age.yaml | 11 + .../income/child_unearned_income_counted.yaml | 18 + .../az/des/liheap/income/sources/earned.yaml | 26 ++ .../des/liheap/income/sources/unearned.yaml | 39 ++ .../states/az/des/liheap/payment/amount.yaml | 41 ++ .../az/des/liheap/points/child_age_limit.yaml | 17 + .../az/des/liheap/points/elderly_age.yaml | 15 + .../az/des/liheap/points/energy_burden.yaml | 48 +++ .../az/des/liheap/points/income_ratio.yaml | 45 ++ .../az/des/liheap/points/portable_fuel.yaml | 15 + .../des/liheap/points/vulnerable_member.yaml | 17 + policyengine_us/programs.yaml | 10 +- .../gov/states/az/des/liheap/az_liheap.yaml | 352 +++++++++++++++ .../des/liheap/az_liheap_household_size.yaml | 166 +++++++ .../az_liheap_categorically_eligible.yaml | 120 ++++++ .../eligibility/az_liheap_eligible.yaml | 245 +++++++++++ .../az_liheap_income_eligible.yaml | 381 ++++++++++++++++ .../eligibility/az_liheap_income_limit.yaml | 94 ++++ .../az_liheap_countable_earned_income.yaml | 150 +++++++ .../income/az_liheap_countable_income.yaml | 66 +++ .../az_liheap_countable_unearned_income.yaml | 163 +++++++ .../gov/states/az/des/liheap/integration.yaml | 277 ++++++++++++ .../az_liheap_energy_burden_points.yaml | 405 ++++++++++++++++++ .../points/az_liheap_energy_need_points.yaml | 327 ++++++++++++++ .../points/az_liheap_income_points.yaml | 334 +++++++++++++++ .../gov/states/az/des/liheap/az_liheap.py | 41 ++ .../az/des/liheap/az_liheap_energy_cost.py | 34 ++ .../az/des/liheap/az_liheap_household_size.py | 23 + .../az_liheap_categorically_eligible.py | 28 ++ .../liheap/eligibility/az_liheap_eligible.py | 52 +++ .../eligibility/az_liheap_income_eligible.py | 23 + .../eligibility/az_liheap_income_limit.py | 74 ++++ .../az_liheap_countable_earned_income.py | 39 ++ .../income/az_liheap_countable_income.py | 39 ++ .../az_liheap_countable_unearned_income.py | 37 ++ .../points/az_liheap_energy_burden_points.py | 56 +++ .../points/az_liheap_energy_need_points.py | 51 +++ .../liheap/points/az_liheap_income_points.py | 41 ++ .../az/des/liheap/points/az_liheap_points.py | 21 + 45 files changed, 4017 insertions(+), 1 deletion(-) create mode 100644 changelog.d/az-liheap-wave1c.added.md create mode 100644 policyengine_us/parameters/gov/states/az/des/liheap/eligibility/categorical_programs.yaml create mode 100644 policyengine_us/parameters/gov/states/az/des/liheap/eligibility/fpg_rate.yaml create mode 100644 policyengine_us/parameters/gov/states/az/des/liheap/eligibility/fpg_year_lag.yaml create mode 100644 policyengine_us/parameters/gov/states/az/des/liheap/eligibility/max_table_size.yaml create mode 100644 policyengine_us/parameters/gov/states/az/des/liheap/eligibility/smi_rate.yaml create mode 100644 policyengine_us/parameters/gov/states/az/des/liheap/income/adult_age.yaml create mode 100644 policyengine_us/parameters/gov/states/az/des/liheap/income/child_unearned_income_counted.yaml create mode 100644 policyengine_us/parameters/gov/states/az/des/liheap/income/sources/earned.yaml create mode 100644 policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml create mode 100644 policyengine_us/parameters/gov/states/az/des/liheap/payment/amount.yaml create mode 100644 policyengine_us/parameters/gov/states/az/des/liheap/points/child_age_limit.yaml create mode 100644 policyengine_us/parameters/gov/states/az/des/liheap/points/elderly_age.yaml create mode 100644 policyengine_us/parameters/gov/states/az/des/liheap/points/energy_burden.yaml create mode 100644 policyengine_us/parameters/gov/states/az/des/liheap/points/income_ratio.yaml create mode 100644 policyengine_us/parameters/gov/states/az/des/liheap/points/portable_fuel.yaml create mode 100644 policyengine_us/parameters/gov/states/az/des/liheap/points/vulnerable_member.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap_household_size.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_categorically_eligible.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_eligible.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_income_limit.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_earned_income.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_income.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/integration.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/points/az_liheap_energy_burden_points.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/points/az_liheap_energy_need_points.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/points/az_liheap_income_points.yaml create mode 100644 policyengine_us/variables/gov/states/az/des/liheap/az_liheap.py create mode 100644 policyengine_us/variables/gov/states/az/des/liheap/az_liheap_energy_cost.py create mode 100644 policyengine_us/variables/gov/states/az/des/liheap/az_liheap_household_size.py create mode 100644 policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_categorically_eligible.py create mode 100644 policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_eligible.py create mode 100644 policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.py create mode 100644 policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_limit.py create mode 100644 policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_earned_income.py create mode 100644 policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_income.py create mode 100644 policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.py create mode 100644 policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_energy_burden_points.py create mode 100644 policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_energy_need_points.py create mode 100644 policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_income_points.py create mode 100644 policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_points.py diff --git a/changelog.d/az-liheap-wave1c.added.md b/changelog.d/az-liheap-wave1c.added.md new file mode 100644 index 00000000000..bcbfcaef0e6 --- /dev/null +++ b/changelog.d/az-liheap-wave1c.added.md @@ -0,0 +1 @@ +Add Arizona LIHEAP Standard benefit (az_liheap): FY2026 regular benefit from the points worksheet with the greater of 60% state median income or 150% federal poverty guideline income test and TANF or SNAP categorical eligibility. diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/categorical_programs.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/categorical_programs.yaml new file mode 100644 index 00000000000..f10575f4586 --- /dev/null +++ b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/categorical_programs.yaml @@ -0,0 +1,20 @@ +description: Arizona treats a household as categorically eligible when a member receives one of these benefits under the Low Income Home Energy Assistance Program. +# Section VI.A.6.b.i: "If one (1) Household member's current enrollment in Cash +# Assistance or Nutrition Assistance is verified, the Household will be +# considered categorically eligible." A positive modeled benefit stands for +# current enrollment. The FY2026 plan (Section 1.4) answers No for SSI and for +# means-tested veterans programs. +values: + 2025-08-25: + - snap + - az_tanf + +metadata: + unit: list + period: year + label: Arizona LIHEAP categorical eligibility programs + reference: + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Categorical Eligibility definition (pages 3-4) and Section VI.A.6.a-b, page 7 + href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=7 + - title: Arizona LIHEAP FY2026 State Plan, Sections 1.4 and 1.4a (TANF and SNAP yes; SSI and means-tested veterans programs no), page 5 + href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=5 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/fpg_rate.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/fpg_rate.yaml new file mode 100644 index 00000000000..70a1ae18f7e --- /dev/null +++ b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/fpg_rate.yaml @@ -0,0 +1,13 @@ +description: Arizona limits countable household income to this multiple of the federal poverty guidelines, when that arm exceeds the state median income arm, under the Low Income Home Energy Assistance Program. +values: + 2025-08-25: 1.5 + +metadata: + unit: /1 + period: year + label: Arizona LIHEAP federal poverty guideline rate + reference: + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.A.6.c.i (gross income at or below the greater of 60% of the SMI or 150% of the FPL), page 8 + href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8 + - title: Arizona LIHEAP FY2026 State Plan, Section 2.1 (household sizes 10 and over at 150.00% of the HHS Poverty Guidelines), page 9 + href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=9 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/fpg_year_lag.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/fpg_year_lag.yaml new file mode 100644 index 00000000000..9c75a61c7e7 --- /dev/null +++ b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/fpg_year_lag.yaml @@ -0,0 +1,17 @@ +description: Arizona uses federal poverty guidelines from this many years before the modeled program year for the income limit under the Low Income Home Energy Assistance Program. +# Verified for FFY2026: Appendix A's household sizes 10 to 12 and its $687 +# additional-person increment reproduce with the 2025 guideline (15,650 plus +# 5,500 per additional person), not the 2026 guideline. This is a relative year +# selection, not a fixed guideline year. Earlier Arizona policy is unverified. +values: + 2025-08-25: 1 + +metadata: + unit: year + period: year + label: Arizona LIHEAP federal poverty guideline year lag + reference: + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Appendix A Income Eligibility by Household Size, page 37 + href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=37 + - title: LIHEAP Clearinghouse, FFY2026 poverty guidelines by household size (2025 HHS guideline) + href: https://liheapch.acf.gov/profiles/povertytables/FY2026/popstate.htm diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/max_table_size.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/max_table_size.yaml new file mode 100644 index 00000000000..edc42593110 --- /dev/null +++ b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/max_table_size.yaml @@ -0,0 +1,11 @@ +description: Arizona tabulates monthly income limits through this household size, with an increment for each additional person, under the Low Income Home Energy Assistance Program. +values: + 2025-08-25: 12 + +metadata: + unit: person + period: year + label: Arizona LIHEAP maximum tabulated household size + reference: + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Appendix A Income Eligibility by Household Size (sizes 1 to 12 and "EACH ADDL OVER 12 +$687"), page 37 + href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=37 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/smi_rate.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/smi_rate.yaml new file mode 100644 index 00000000000..e3e9b3dac52 --- /dev/null +++ b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/smi_rate.yaml @@ -0,0 +1,15 @@ +description: Arizona limits countable household income to this share of the state median income, when that arm exceeds the federal poverty guideline arm, under the Low Income Home Energy Assistance Program. +values: + 2025-08-25: 0.6 + +metadata: + unit: /1 + period: year + label: Arizona LIHEAP state median income rate + reference: + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.A.6.c.i (gross income at or below the greater of 60% of the SMI or 150% of the FPL), page 8 + href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8 + - title: Arizona LIHEAP FY2026 State Plan, Section 2.1 (household sizes 1 to 9 at 60.00% of State Median Income), page 9 + href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=9 + - title: LIHEAP Clearinghouse, Arizona 60% of State Median Income, FFY2026 (four-person amount $64,789) + href: https://liheapch.acf.gov/profiles/povertytables/FY2026/azsmi.htm diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/income/adult_age.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/income/adult_age.yaml new file mode 100644 index 00000000000..8792c67a2c0 --- /dev/null +++ b/policyengine_us/parameters/gov/states/az/des/liheap/income/adult_age.yaml @@ -0,0 +1,11 @@ +description: Arizona counts the income of household members at or above this age under the Low Income Home Energy Assistance Program. +values: + 2025-08-25: 18 + +metadata: + unit: year + period: year + label: Arizona LIHEAP adult age + reference: + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.A.6.d.ii (income from all household members aged 18 and older, page 8) and Section VII.A.1.c.ii (page 13) + href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/income/child_unearned_income_counted.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/income/child_unearned_income_counted.yaml new file mode 100644 index 00000000000..237cb5c1212 --- /dev/null +++ b/policyengine_us/parameters/gov/states/az/des/liheap/income/child_unearned_income_counted.yaml @@ -0,0 +1,18 @@ +description: Arizona counts the unearned income of household members under the adult age when this is true under the Low Income Home Energy Assistance Program. +# Revision 13, Section VI.A.6.e.ii.E: "Income from Household members under the +# age of 18 is not considered Countable Income." Revision 15 (effective March +# 23, 2026) rewrote the sentence to "Earned income from Household members under +# the age of 18 is not considered Countable Income", so a child's SSI, survivor +# benefits or child support count from that date. Earned income of members +# under 18 is excluded under both revisions. +values: + 2025-08-25: false + 2026-03-23: true + +metadata: + unit: bool + period: year + label: Arizona LIHEAP counts unearned income of members under 18 + reference: + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025) and revision 15 (effective March 23, 2026), Section VI.A.6.e.ii.E, page 9 + href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=9 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/earned.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/earned.yaml new file mode 100644 index 00000000000..f3d18cb76b6 --- /dev/null +++ b/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/earned.yaml @@ -0,0 +1,26 @@ +description: Arizona uses these earned income sources to approximate countable household earnings under the Low Income Home Energy Assistance Program. +# Section VI.A.6.e.ii.A counts "Earned income from employment, including +# self-employment (includes wages, salaries, tips, etc.)"; the plan's Section +# 1.9 also checks contract income, commissions, WIA employment income and work +# study, which existing inputs report as wages. Section VII.A.1.c.ii.B verifies +# self-employment as "Profit or loss documentation" (Schedule C) and does not +# say whether a loss offsets other income, so the existing net self-employment +# inputs are used without a further expense deduction and each is floored at +# zero in the formula. +values: + 2025-08-25: + - employment_income + - self_employment_income + - sstb_self_employment_income + - partnership_s_corp_income + - farm_operations_income + +metadata: + unit: list + period: year + label: Arizona LIHEAP countable earned income sources + reference: + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.A.6.e.ii.A (page 8) and Section VII.A.1.c.ii.A-B (page 13) + href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8 + - title: Arizona LIHEAP FY2026 State Plan, Section 1.9 (wages, self-employment income, contract income, commissions, WIA employment income and work study counted), pages 6-7 + href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=6 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml new file mode 100644 index 00000000000..80fd101d34a --- /dev/null +++ b/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml @@ -0,0 +1,39 @@ +description: Arizona uses these person-level unearned income sources to determine gross household income under the Low Income Home Energy Assistance Program. +# Section VI.A.6.e.ii.B-D counts "Unearned income, including SSA and TANF +# benefits, alimony, child support, gambling profits, and retirement income", +# "Income from Unemployment Insurance" and "Consistent income received from +# other sources"; the plan's Section 1.9 checks the full list. Cash Assistance +# (az_tanf) is a household benefit and is added in az_liheap_countable_income. +values: + 2025-08-25: + - unemployment_compensation + - strike_benefits + # Section VII.A.1.c.ii.D note: Medicare premiums "should be subtracted when + # determining gross income". Social Security is counted gross here; the + # premium netting is deferred, as in the other state LIHEAP implementations. + - social_security + - ssi + - pension_income + # retirement_distributions already sums taxable and tax-exempt IRA, 401(k), + # SEP, 403(b) and Keogh distributions. + - retirement_distributions + - veterans_benefits + - alimony_income + - child_support_received + - rental_income + - interest_income + - ordinary_dividend_income + - general_assistance + # Cash gifts over $50, jury duty, legal settlements, insurance payments to + # the insured, mortgage or sales-contract payments, royalties and foster or + # adoption care payments have no model input. + +metadata: + unit: list + period: year + label: Arizona LIHEAP countable unearned income sources + reference: + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.A.6.e.ii.B-D (page 8) and Section VII.A.1.c.ii.C-F (pages 13-14) + href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8 + - title: Arizona LIHEAP FY2026 State Plan, Section 1.9 (unemployment insurance, strike pay, SSA benefits, SSI, retirement and pension, general assistance, TANF, rental income, alimony, child support, interest, dividends or royalties and VA benefits counted), pages 6-7 + href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=6 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/payment/amount.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/payment/amount.yaml new file mode 100644 index 00000000000..8f946c61a8a --- /dev/null +++ b/policyengine_us/parameters/gov/states/az/des/liheap/payment/amount.yaml @@ -0,0 +1,41 @@ +description: Arizona pays this annual Standard benefit by total benefit matrix points under the Low Income Home Energy Assistance Program. +# Worksheet Section 4: "1 - 4 $160, 5 - 8 $320, 9 - 12 $480, 13 or over $640". +# Thresholds are the band starts, read with the default lower-inclusive calc +# on integer points. The first threshold is 0 rather than the worksheet's 1: +# an income-eligible household always scores at least one income point, and +# the plan (Section 2.6) states a $160 minimum benefit, so a categorically +# eligible household above 100% of the income limit with no burden or need +# points receives the $160 minimum rather than nothing. +brackets: + - threshold: + 2024-10-12: 0 + amount: + 2024-10-12: 160 + - threshold: + 2024-10-12: 5 + amount: + 2024-10-12: 320 + - threshold: + 2024-10-12: 9 + amount: + 2024-10-12: 480 + - threshold: + 2024-10-12: 13 + amount: + 2024-10-12: 640 + +metadata: + type: single_amount + threshold_unit: /1 + amount_unit: currency-USD + period: year + label: Arizona LIHEAP Standard benefit by points + reference: + - title: Arizona LIHEAP Eligibility Worksheet, $640 Limit (effective October 12, 2024), Section 4 Payment Matrix + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1 + - title: Arizona DES, Low Income Home Energy Assistance Program page (benefit by points table) + href: https://des.az.gov/liheap + - title: Arizona LIHEAP FY2026 State Plan, Section 2.6 (minimum benefit $160, maximum benefit $640), page 10 + href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=10 + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VII.B.1.a (up to $640 for a standard payment, page 29) and Section VII.B.4.b (page 34) + href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=29 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/points/child_age_limit.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/points/child_age_limit.yaml new file mode 100644 index 00000000000..fc9ca843260 --- /dev/null +++ b/policyengine_us/parameters/gov/states/az/des/liheap/points/child_age_limit.yaml @@ -0,0 +1,17 @@ +description: Arizona awards an energy need point when a household member is younger than this age under the Low Income Home Energy Assistance Program. +# The worksheet row is "Child age 6 or younger" and the policy says "Aged six +# (6) and under", so the point applies below age 7. +values: + 2024-10-12: 7 + +metadata: + unit: year + period: year + label: Arizona LIHEAP young child age limit + reference: + - title: Arizona LIHEAP Eligibility Worksheet, $640 Limit (effective October 12, 2024), Section 3 Energy Need (Child age 6 or younger, 1 point) + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1 + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.B.1.d.i (household members "Aged six (6) and under"), page 10 + href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=10 + - title: Arizona LIHEAP FY2026 State Plan, Section 2.4 (additional points for children 6 years old and under), page 10 + href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=10 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/points/elderly_age.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/points/elderly_age.yaml new file mode 100644 index 00000000000..1370487b32d --- /dev/null +++ b/policyengine_us/parameters/gov/states/az/des/liheap/points/elderly_age.yaml @@ -0,0 +1,15 @@ +description: Arizona awards an energy need point when a household member is at or above this age under the Low Income Home Energy Assistance Program. +values: + 2025-08-25: 60 + +metadata: + unit: year + period: year + label: Arizona LIHEAP elderly age + reference: + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.B.1.d.ii (household members "Aged 60 and over"), page 10 + href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=10 + - title: Arizona LIHEAP FY2026 State Plan, Section 2.4 (additional points for the elderly, 60+ years old), page 10 + href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=10 + - title: Arizona LIHEAP Eligibility Worksheet, $640 Limit (effective October 12, 2024), Section 3 Energy Need (Elderly, 1 point) + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/points/energy_burden.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/points/energy_burden.yaml new file mode 100644 index 00000000000..9659bdaaa02 --- /dev/null +++ b/policyengine_us/parameters/gov/states/az/des/liheap/points/energy_burden.yaml @@ -0,0 +1,48 @@ +description: Arizona awards these benefit matrix points by the ratio of energy cost to gross income under the Low Income Home Energy Assistance Program. +# Worksheet rows "5% or less 0 points, 6% - 10% 1 point, 11% - 15% 2 points, +# 16% - 20% 3 points, 21% - 25% 4 points, 26% - 30% 5 points, 31% or over 6 +# points". "5% or less" is band-top wording and the DES page places its 17.5% +# example in the 16% to 20% band, so the thresholds are the band tops and +# callers use calc(burden, right=True). The -inf first threshold keeps a zero +# burden in the first band. +brackets: + - threshold: + 2024-10-12: -.inf + amount: + 2024-10-12: 0 + - threshold: + 2024-10-12: 0.05 + amount: + 2024-10-12: 1 + - threshold: + 2024-10-12: 0.1 + amount: + 2024-10-12: 2 + - threshold: + 2024-10-12: 0.15 + amount: + 2024-10-12: 3 + - threshold: + 2024-10-12: 0.2 + amount: + 2024-10-12: 4 + - threshold: + 2024-10-12: 0.25 + amount: + 2024-10-12: 5 + - threshold: + 2024-10-12: 0.3 + amount: + 2024-10-12: 6 + +metadata: + type: single_amount + threshold_unit: /1 + amount_unit: /1 + period: year + label: Arizona LIHEAP energy burden points + reference: + - title: Arizona LIHEAP Eligibility Worksheet, $640 Limit (effective October 12, 2024), Section 2 Energy Burden + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1 + - title: Arizona DES, Low Income Home Energy Assistance Program page (energy burden formula and the 350 / 2,000 = 17.5% example) + href: https://des.az.gov/liheap diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/points/income_ratio.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/points/income_ratio.yaml new file mode 100644 index 00000000000..5b386369218 --- /dev/null +++ b/policyengine_us/parameters/gov/states/az/des/liheap/points/income_ratio.yaml @@ -0,0 +1,45 @@ +description: Arizona awards these benefit matrix points by the ratio of gross income to the income limit for the household size under the Low Income Home Energy Assistance Program. +# Worksheet rows "0% - 20% 5 points, 21% - 40% 4 points, 41% - 60% 3 points, +# 61% - 80% 2 points, 81% - 100% 1 point". The DES page computes the ratio as +# "Total Monthly Gross Income Divided by the Monthly Gross Income Limit" and +# places its 44% example in the 41% to 60% band, so the thresholds are the +# band tops and callers use calc(ratio, right=True). The -inf first threshold +# keeps a zero ratio in the first band. No row is printed above 100%: only a +# categorically eligible household can reach it, and it scores 0 points. +brackets: + - threshold: + 2024-10-12: -.inf + amount: + 2024-10-12: 5 + - threshold: + 2024-10-12: 0.2 + amount: + 2024-10-12: 4 + - threshold: + 2024-10-12: 0.4 + amount: + 2024-10-12: 3 + - threshold: + 2024-10-12: 0.6 + amount: + 2024-10-12: 2 + - threshold: + 2024-10-12: 0.8 + amount: + 2024-10-12: 1 + - threshold: + 2024-10-12: 1 + amount: + 2024-10-12: 0 + +metadata: + type: single_amount + threshold_unit: /1 + amount_unit: /1 + period: year + label: Arizona LIHEAP income points + reference: + - title: Arizona LIHEAP Eligibility Worksheet, $640 Limit (effective October 12, 2024), Section 1 Income Eligibility + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1 + - title: Arizona DES, Low Income Home Energy Assistance Program page (income points formula and the 2,000 / 4,535 = 44% example) + href: https://des.az.gov/liheap diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/points/portable_fuel.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/points/portable_fuel.yaml new file mode 100644 index 00000000000..3a8b9e92c1b --- /dev/null +++ b/policyengine_us/parameters/gov/states/az/des/liheap/points/portable_fuel.yaml @@ -0,0 +1,15 @@ +description: Arizona adds this many benefit matrix points for households that use portable fuel under the Low Income Home Energy Assistance Program. +values: + 2024-10-12: 2 + +metadata: + unit: /1 + period: year + label: Arizona LIHEAP portable fuel points + reference: + - title: Arizona LIHEAP Eligibility Worksheet, $640 Limit (effective October 12, 2024), Section 2 note (add 2 points for portable fuels) + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1 + - title: Arizona DES, Low Income Home Energy Assistance Program page ("2 points are added for households that utilize deliverable/portal fuel") + href: https://des.az.gov/liheap + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Portable Fuel definition (propane, wood, coal, fuel for gas generators and pellets), page 5 + href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=5 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/points/vulnerable_member.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/points/vulnerable_member.yaml new file mode 100644 index 00000000000..3861058b749 --- /dev/null +++ b/policyengine_us/parameters/gov/states/az/des/liheap/points/vulnerable_member.yaml @@ -0,0 +1,17 @@ +description: Arizona awards this many benefit matrix points for each energy need category present in the household under the Low Income Home Energy Assistance Program. +# Worksheet Section 3: Elderly 1 point, Disabled 1 point, Veteran 1 point, +# Child age 6 or younger 1 point, "(Only one point per category allowed)". +values: + 2024-10-12: 1 + +metadata: + unit: /1 + period: year + label: Arizona LIHEAP energy need points per category + reference: + - title: Arizona LIHEAP Eligibility Worksheet, $640 Limit (effective October 12, 2024), Section 3 Energy Need + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1 + - title: Arizona LIHEAP FY2026 State Plan, Section 2.4 ("A household may receive one point for each category, if eligible"), page 10 + href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=10 + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.B.1.d (benefit factors), page 10 + href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=10 diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 756dc2c4988..d3ee47fa369 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -802,7 +802,7 @@ programs: category: Benefits agency: HHS status: partial - coverage: OR, DC, Riverside County, MA, IL, KS, MS, TX + coverage: OR, DC, Riverside County, MA, IL, KS, MS, AZ, TX state_implementations: - state: OR status: in_progress @@ -839,6 +839,14 @@ programs: parameter_prefix: gov.states.ms.mdhs.liheap verified_years: "2026" notes: FY2026 regular assistance for heating fuel and electric bills, using the published matrix as printed. Crisis assistance and energy equipment are out of scope. + - state: AZ + status: partial + name: Arizona LIHEAP + full_name: Arizona Low Income Home Energy Assistance Program + variable: az_liheap + parameter_prefix: gov.states.az.des.liheap + verified_years: "2026" + notes: FY2026 Standard benefit covering heating and cooling bills from the point-based benefit matrix. Crisis assistance, water for evaporative cooling and weatherization are out of scope. - state: TX status: partial name: Texas CEAP diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap.yaml new file mode 100644 index 00000000000..42281674472 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap.yaml @@ -0,0 +1,352 @@ +# Arizona Standard LIHEAP benefit: total points (income + energy burden + +# energy need) map to $160 for 1-4 points, $320 for 5-8, $480 for 9-12 and +# $640 for 13 or more (LIHEAP Eligibility Worksheet, $640 Limit, eff. +# 2024-10-12, section 4, +# https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1; +# FY2026 plan 2.6 minimum $160 and maximum $640, +# https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=10; +# DES program page payment table, https://des.az.gov/liheap). The benefit is +# not capped at the bill: any remainder is credited to the Priority 1 +# account (DES LIHEAP Policy rev. 13, pp. 33-34 VII.B.3.b.iii, +# https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=33). +# A categorically eligible household with zero points receives the plan's +# $160 minimum. Annual limits: size 1 33,684; size 2 44,052; size 3 54,420. + +- name: Case 1, single minimum-wage earner with a modest electric bill totals 2 points and receives 160. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 30_576 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # Income 30,576 / 33,684 = 0.908 -> 1; burden 1,800 / 30,576 = 0.059 -> 1; + # need 0; total 2 -> 160. + az_liheap_points: 2 + az_liheap: 160 + +- name: Case 2, four points is the top of the 160 band. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 30_576 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + pre_subsidy_electricity_expense: 3_600 + gas_expense: 1_400 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # Income 1; burden (3,600 + 1,400) / 30,576 = 0.164 -> 3; need 0; + # total 4 -> 160. + az_liheap_points: 4 + az_liheap: 160 + +- name: Case 3, five points is the bottom of the 320 band. + period: 2026 + input: + people: + person1: + age: 40 + is_veteran: true + employment_income: 30_576 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + pre_subsidy_electricity_expense: 3_600 + gas_expense: 1_400 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # Income 1; burden 3; veteran 1; total 5 -> 320. + az_liheap_points: 5 + az_liheap: 320 + +- name: Case 4, eight points is the top of the 320 band. + period: 2026 + input: + people: + person1: + age: 35 + employment_income: 12_000 + person2: + age: 37 + employment_income: 9_000 + person3: + age: 4 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 4_000 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1, person2, person3] + state_code: AZ + output: + # Income 21,000 / 54,420 = 0.386 -> 4; burden 4,000 / 21,000 = 0.190 -> 3; + # child aged 4 -> 1; total 8 -> 320. + az_liheap_points: 8 + az_liheap: 320 + +- name: Case 5, nine points is the bottom of the 480 band. + period: 2026 + input: + people: + person1: + age: 35 + employment_income: 12_000 + person2: + age: 37 + employment_income: 9_000 + person3: + age: 4 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: NATURAL_GAS + pre_subsidy_electricity_expense: 4_000 + gas_expense: 600 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1, person2, person3] + state_code: AZ + output: + # Income 4; burden (4,000 + 600) / 21,000 = 0.219 -> 4; child 1; + # total 9 -> 480. + az_liheap_points: 9 + az_liheap: 480 + +- name: Case 6, twelve points is the top of the 480 band. + period: 2026 + input: + people: + person1: + age: 62 + employment_income: 10_000 + ssi: 0 + person2: + age: 45 + is_disabled: true + ssi: 0 + person3: + age: 4 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_400 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1, person2, person3] + state_code: AZ + output: + # Income 10,000 / 54,420 = 0.184 -> 5; burden 2,400 / 10,000 = 0.24 -> 4; + # elderly 1 + disabled 1 + child 1 = 3; total 12 -> 480. + az_liheap_points: 12 + az_liheap: 480 + +- name: Case 7, thirteen points is the bottom of the 640 band. + period: 2026 + input: + people: + person1: + age: 62 + employment_income: 10_000 + ssi: 0 + person2: + age: 45 + is_disabled: true + ssi: 0 + person3: + age: 4 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_700 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1, person2, person3] + state_code: AZ + output: + # Income 5; burden 2,700 / 10,000 = 0.27 -> 5; need 3; total 13 -> 640. + az_liheap_points: 13 + az_liheap: 640 + +- name: Case 8, the maximum of seventeen points receives 640. + period: 2026 + input: + people: + person1: + age: 66 + is_disabled: true + veterans_benefits: 8_400 + ssi: 0 + person2: + age: 3 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: PROPANE + pre_subsidy_electricity_expense: 1_200 + bottled_gas_expense: 1_800 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1, person2] + state_code: AZ + output: + # Income 8,400 / 44,052 = 0.191 -> 5; burden (1,200 + 1,800) / 8,400 = + # 0.357 -> 6 + 2 portable; elderly + disabled + veteran + child = 4; + # total 17 -> 640. + az_liheap_points: 17 + az_liheap: 640 + +- name: Case 9, a SNAP household above the limit with zero points receives the 160 minimum. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 40_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + snap: 1_200 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # Income 40,000 / 33,684 = 1.19 -> 0; burden 1,800 / 40,000 = 0.045 -> 0; + # need 0; total 0. Plan 2.6: minimum benefit 160. + az_liheap_eligible: true + az_liheap_points: 0 + az_liheap: 160 + +- name: Case 10, the benefit is not capped at a small bill. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 0 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 300 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # Income 0 -> 5; burden 300 against zero income -> 6; need 0; total 11 + # -> 480, more than the 300 bill (policy pp. 33-34: remainder credited). + az_liheap_points: 11 + az_liheap: 480 + +- name: Case 11, an unspecified heating type pays nothing. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 30_576 + spm_units: + spm_unit: + members: [person1] + heating_type: UNSPECIFIED + pre_subsidy_electricity_expense: 1_800 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + az_liheap_eligible: false + az_liheap: 0 + +- name: Case 12, income over the limit without categorical eligibility pays nothing. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 40_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # 40,000 > 33,684 and no SNAP or Cash Assistance. + az_liheap_eligible: false + az_liheap: 0 + +- name: Case 13, the same household in New Mexico pays nothing. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 30_576 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: NM + output: + az_liheap: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap_household_size.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap_household_size.yaml new file mode 100644 index 00000000000..b950903fee3 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap_household_size.yaml @@ -0,0 +1,166 @@ +# Arizona LIHEAP household size. Only Eligible Household Members (U.S. citizens +# and Qualified Noncitizens under 8 U.S.C. 1641(b)) count toward the household +# size; in a Mixed-Status Household the income of every member aged 18 or +# older still counts (DES LIHEAP Policy rev. 13 eff. 2025-08-25, p. 4 +# definitions and p. 8 VI.A.6.d, +# https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8). +# The variable is defined only for Arizona households. + +- name: Case 1, single citizen adult counts as one. + period: 2026 + input: + people: + person1: + age: 40 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + households: + household: + members: [person1] + state_code: AZ + output: + az_liheap_household_size: 1 + +- name: Case 2, two citizen adults and two citizen children count four. + period: 2026 + 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 + pre_subsidy_electricity_expense: 2_000 + gas_expense: 600 + households: + household: + members: [person1, person2, person3, person4] + state_code: AZ + output: + az_liheap_household_size: 4 + +- name: Case 3, mixed-status household counts citizens only but all adult income. + period: 2026 + input: + people: + person1: + age: 34 + immigration_status: CITIZEN + employment_income: 26_000 + person2: + age: 36 + immigration_status: UNDOCUMENTED + employment_income: 22_000 + person3: + age: 9 + person4: + age: 5 + spm_units: + spm_unit: + members: [person1, person2, person3, person4] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_700 + households: + household: + members: [person1, person2, person3, person4] + state_code: AZ + output: + # Policy p. 8 VI.A.6.d.i: 4 members - 1 undocumented adult = 3. + az_liheap_household_size: 3 + # Policy p. 8 VI.A.6.d.ii: 26,000 + 22,000 = 48,000, both adults' wages. + az_liheap_countable_income: 48_000 + +- name: Case 4, lawful permanent resident is a qualified noncitizen and counts. + period: 2026 + input: + people: + person1: + age: 45 + immigration_status: CITIZEN + person2: + age: 44 + immigration_status: LEGAL_PERMANENT_RESIDENT + spm_units: + spm_unit: + members: [person1, person2] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_900 + households: + household: + members: [person1, person2] + state_code: AZ + output: + az_liheap_household_size: 2 + +- name: Case 5, two undocumented adults count zero. + period: 2026 + input: + people: + person1: + age: 30 + immigration_status: UNDOCUMENTED + person2: + age: 28 + immigration_status: UNDOCUMENTED + spm_units: + spm_unit: + members: [person1, person2] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_500 + households: + household: + members: [person1, person2] + state_code: AZ + output: + az_liheap_household_size: 0 + +- name: Case 6, DACA member is not a qualified noncitizen and is excluded. + period: 2026 + input: + people: + person1: + age: 50 + immigration_status: CITIZEN + person2: + age: 22 + immigration_status: DACA + spm_units: + spm_unit: + members: [person1, person2] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + households: + household: + members: [person1, person2] + state_code: AZ + output: + # Policy p. 5: DACA is not on the 8 U.S.C. 1641(b) qualified list. + az_liheap_household_size: 1 + +- name: Case 7, the same citizen in New Mexico has no Arizona household size. + period: 2026 + input: + people: + person1: + age: 40 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + households: + household: + members: [person1] + state_code: NM + output: + az_liheap_household_size: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_categorically_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_categorically_eligible.yaml new file mode 100644 index 00000000000..e9a0cad6474 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_categorically_eligible.yaml @@ -0,0 +1,120 @@ +# Arizona LIHEAP categorical eligibility: a household is categorically +# eligible when one member's current enrollment in DES Cash Assistance (TANF) +# or Nutrition Assistance (SNAP) is verified; SSI and veterans programs do not +# confer it (DES LIHEAP Policy rev. 13 eff. 2025-08-25, pp. 7-8 VI.A.6.b, +# https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=7; +# FY2026 plan 1.4 and 1.4a, +# https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=5). +# snap and az_tanf are monthly variables; in a yearly test their inputs are +# annual totals (snap 2,400 = 200 a month; az_tanf 4,164 = 347 a month). + +- name: Case 1, SNAP receipt makes a household above the income limit categorically eligible. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 36_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + snap: 2_400 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + az_liheap_categorically_eligible: true + +- name: Case 2, Cash Assistance receipt confers categorical eligibility. + period: 2026 + input: + people: + person1: + age: 28 + employment_income: 0 + person2: + age: 2 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_900 + snap: 0 + az_tanf: 4_164 + households: + household: + members: [person1, person2] + state_code: AZ + output: + az_liheap_categorically_eligible: true + +- name: Case 3, a household receiving neither program is not categorically eligible. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 30_576 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + az_liheap_categorically_eligible: false + +- name: Case 4, SSI receipt alone does not confer categorical eligibility. + period: 2026 + input: + people: + person1: + age: 50 + is_disabled: true + ssi: 11_928 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_600 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # Plan 1.4a: SSI is not a categorical program. + az_liheap_categorically_eligible: false + +- name: Case 5, VA benefits alone do not confer categorical eligibility. + period: 2026 + input: + people: + person1: + age: 58 + veterans_benefits: 20_000 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + pre_subsidy_electricity_expense: 1_500 + gas_expense: 600 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # Plan 1.4a: means-tested veterans programs are not categorical. + az_liheap_categorically_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_eligible.yaml new file mode 100644 index 00000000000..6ffac2023dc --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_eligible.yaml @@ -0,0 +1,245 @@ +# Arizona LIHEAP eligibility: an Arizona household with at least one eligible +# member that meets categorical or income eligibility and needs help with an +# energy bill (DES LIHEAP Policy rev. 13 eff. 2025-08-25, pp. 6-8 VI.A, +# https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=6). +# The Standard benefit covers every home energy bill (electricity, gas and +# portable fuel, p. 4 "Energy Utility"), so a home with no heating system but +# an electricity bill qualifies, and renters with energy in rent are eligible +# (p. 32 VII.B.3.a.ii.B). An unspecified heating type pays nothing (project +# rule). Size-1 annual limit 2,807 x 12 = 33,684. + +- name: Case 1, single minimum-wage earner with an electricity bill is eligible. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 30_576 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # 30,576 <= 33,684; energy cost 1,800 > 0. + az_liheap_eligible: true + +- name: Case 2, an unspecified heating type is not eligible. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 30_576 + spm_units: + spm_unit: + members: [person1] + heating_type: UNSPECIFIED + pre_subsidy_electricity_expense: 1_800 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + az_liheap_eligible: false + +- name: Case 3, a home without heating but with an electricity bill is eligible. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 30_576 + spm_units: + spm_unit: + members: [person1] + heating_type: NONE + pre_subsidy_electricity_expense: 1_800 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # The Standard benefit is for any Energy Utility bill, not heating alone. + az_liheap_eligible: true + +- name: Case 4, energy included in rent with no separate bill is eligible. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 30_576 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + heat_expense_included_in_rent: true + pre_subsidy_electricity_expense: 0 + gas_expense: 0 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # Policy p. 4: undesignated energy payments in rent form a household; + # p. 32: payment goes to the applicant when utilities are in rent. + az_liheap_energy_cost: 0 + az_liheap_eligible: true + +- name: Case 5, no energy bill and no energy in rent is not eligible. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 30_576 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + heat_expense_included_in_rent: false + pre_subsidy_electricity_expense: 0 + gas_expense: 0 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # Policy p. 6 VI.A.1: the applicant must require energy assistance. + az_liheap_eligible: false + +- name: Case 6, income over the limit without categorical eligibility is not eligible. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 40_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # 40,000 > 33,684. + az_liheap_income_eligible: false + az_liheap_categorically_eligible: false + az_liheap_eligible: false + +- name: Case 7, income over the limit with SNAP receipt is eligible. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 40_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + snap: 2_400 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # Policy p. 7 VI.A.6.a: categorical or income eligibility. + az_liheap_income_eligible: false + az_liheap_categorically_eligible: true + az_liheap_eligible: true + +- name: Case 8, a household with no eligible member is not eligible. + period: 2026 + input: + people: + person1: + age: 30 + immigration_status: UNDOCUMENTED + employment_income: 20_000 + person2: + age: 28 + immigration_status: UNDOCUMENTED + spm_units: + spm_unit: + members: [person1, person2] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_500 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1, person2] + state_code: AZ + output: + # Policy pp. 6-7 VI.A.3: at least one member must be a citizen or + # qualified noncitizen. + az_liheap_household_size: 0 + az_liheap_eligible: false + +- name: Case 9, the same household in New Mexico is not eligible. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 30_576 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: NM + output: + az_liheap_eligible: false + +- name: Case 10, a propane bill alone satisfies the energy-expense requirement. + period: 2026 + input: + people: + person1: + age: 52 + employment_income: 24_000 + spm_units: + spm_unit: + members: [person1] + heating_type: PROPANE + pre_subsidy_electricity_expense: 0 + bottled_gas_expense: 1_500 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # Policy p. 5: portable fuel (propane) is an Energy Utility. + az_liheap_energy_cost: 1_500 + az_liheap_eligible: true diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.yaml new file mode 100644 index 00000000000..23aa476262d --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.yaml @@ -0,0 +1,381 @@ +# Arizona LIHEAP income eligibility: when categorical eligibility does not +# apply, gross income must be "at or below" the Appendix A limit for the +# eligible household size (DES LIHEAP Policy rev. 13 eff. 2025-08-25, p. 8 +# VI.A.6.c.i and p. 37 Appendix A, +# https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=37). +# Annual limits are twelve times the monthly limits: size 1 2,807 x 12 = +# 33,684; size 2 3,671 x 12 = 44,052; size 5 6,262 x 12 = 75,144; size 6 +# 7,126 x 12 = 85,512; size 10 8,143 x 12 = 97,716; size 13 (9,518 + 687) x +# 12 = 122,460. The 30-day income snapshot is annualized by the policy's own +# arithmetic (p. 14 VII.A.1.c.v), so annual income compares with 12 x the +# monthly limit. + +- name: Case 1, single adult earning exactly the size-1 limit is eligible. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 33_684 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + households: + household: + members: [person1] + state_code: AZ + output: + az_liheap_countable_income: 33_684 + az_liheap_income_limit: 33_684 + # 33,684 <= 33,684. + az_liheap_income_eligible: true + +- name: Case 2, single adult one dollar over the size-1 limit is ineligible. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 33_685 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + households: + household: + members: [person1] + state_code: AZ + output: + # 33,685 > 33,684. + az_liheap_income_eligible: false + +- name: Case 3, five-person family at exactly the size-5 limit is eligible. + period: 2026 + input: + people: + person1: + age: 38 + employment_income: 44_000 + person2: + age: 36 + employment_income: 31_144 + person3: + age: 12 + person4: + age: 8 + person5: + age: 3 + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5] + heating_type: NATURAL_GAS + pre_subsidy_electricity_expense: 2_400 + gas_expense: 700 + households: + household: + members: [person1, person2, person3, person4, person5] + state_code: AZ + output: + # 44,000 + 31,144 = 75,144 <= 6,262 x 12 = 75,144. + az_liheap_countable_income: 75_144 + az_liheap_income_eligible: true + +- name: Case 4, five-person family one dollar over the size-5 limit is ineligible. + period: 2026 + input: + people: + person1: + age: 38 + employment_income: 44_000 + person2: + age: 36 + employment_income: 31_145 + person3: + age: 12 + person4: + age: 8 + person5: + age: 3 + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5] + heating_type: NATURAL_GAS + pre_subsidy_electricity_expense: 2_400 + gas_expense: 700 + households: + household: + members: [person1, person2, person3, person4, person5] + state_code: AZ + output: + # 44,000 + 31,145 = 75,145 > 75,144. + az_liheap_income_eligible: false + +- name: Case 5, six-person family at exactly the size-6 limit is eligible. + period: 2026 + input: + people: + person1: + age: 41 + employment_income: 50_000 + person2: + age: 39 + employment_income: 35_512 + person3: + age: 15 + person4: + age: 12 + person5: + age: 9 + person6: + age: 6 + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5, person6] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 3_000 + households: + household: + members: [person1, person2, person3, person4, person5, person6] + state_code: AZ + output: + # 50,000 + 35,512 = 85,512 <= 7,126 x 12 = 85,512. + az_liheap_countable_income: 85_512 + az_liheap_income_limit: 85_512 + az_liheap_income_eligible: true + +- name: Case 6, ten-person household at exactly the size-10 limit is eligible. + period: 2026 + input: + people: + person1: + age: 42 + employment_income: 55_000 + person2: + age: 40 + employment_income: 42_716 + person3: + age: 66 + ssi: 0 + person4: + age: 17 + person5: + age: 15 + person6: + age: 13 + person7: + age: 11 + person8: + age: 9 + person9: + age: 7 + person10: + age: 5 + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5, person6, person7, person8, person9, person10] + heating_type: NATURAL_GAS + pre_subsidy_electricity_expense: 3_600 + gas_expense: 900 + households: + household: + members: [person1, person2, person3, person4, person5, person6, person7, person8, person9, person10] + state_code: AZ + output: + az_liheap_household_size: 10 + # 55,000 + 42,716 = 97,716 <= 8,143 x 12 = 97,716 (the 150% FPG arm). + az_liheap_countable_income: 97_716 + az_liheap_income_limit: 97_716 + az_liheap_income_eligible: true + +- name: Case 7, ten-person household one dollar over the size-10 limit is ineligible. + period: 2026 + input: + people: + person1: + age: 42 + employment_income: 55_000 + person2: + age: 40 + employment_income: 42_717 + person3: + age: 66 + ssi: 0 + person4: + age: 17 + person5: + age: 15 + person6: + age: 13 + person7: + age: 11 + person8: + age: 9 + person9: + age: 7 + person10: + age: 5 + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5, person6, person7, person8, person9, person10] + heating_type: NATURAL_GAS + pre_subsidy_electricity_expense: 3_600 + gas_expense: 900 + households: + household: + members: [person1, person2, person3, person4, person5, person6, person7, person8, person9, person10] + state_code: AZ + output: + # 55,000 + 42,717 = 97,717 > 97,716. + az_liheap_income_eligible: false + +- name: Case 8, thirteen-person household at exactly the size-13 limit is eligible. + period: 2026 + input: + people: + person1: + age: 45 + employment_income: 70_000 + person2: + age: 43 + employment_income: 52_460 + person3: + age: 70 + ssi: 0 + person4: + age: 68 + ssi: 0 + person5: + age: 17 + person6: + age: 16 + person7: + age: 14 + person8: + age: 12 + person9: + age: 10 + person10: + age: 8 + person11: + age: 6 + person12: + age: 4 + person13: + age: 2 + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5, person6, person7, person8, person9, person10, person11, person12, person13] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 4_800 + households: + household: + members: [person1, person2, person3, person4, person5, person6, person7, person8, person9, person10, person11, person12, person13] + state_code: AZ + output: + az_liheap_household_size: 13 + # 70,000 + 52,460 = 122,460 <= (9,518 + 687) x 12 = 122,460. + az_liheap_countable_income: 122_460 + az_liheap_income_limit: 122_460 + az_liheap_income_eligible: true + +- name: Case 9, thirteen-person household one dollar over the size-13 limit is ineligible. + period: 2026 + input: + people: + person1: + age: 45 + employment_income: 70_000 + person2: + age: 43 + employment_income: 52_461 + person3: + age: 70 + ssi: 0 + person4: + age: 68 + ssi: 0 + person5: + age: 17 + person6: + age: 16 + person7: + age: 14 + person8: + age: 12 + person9: + age: 10 + person10: + age: 8 + person11: + age: 6 + person12: + age: 4 + person13: + age: 2 + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5, person6, person7, person8, person9, person10, person11, person12, person13] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 4_800 + households: + household: + members: [person1, person2, person3, person4, person5, person6, person7, person8, person9, person10, person11, person12, person13] + state_code: AZ + output: + # 70,000 + 52,461 = 122,461 > 122,460. + az_liheap_income_eligible: false + +- name: Case 10, a household with no income is income eligible. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 0 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + households: + household: + members: [person1] + state_code: AZ + output: + # 0 <= 33,684. + az_liheap_income_eligible: true + +- name: Case 11, mixed-status household is measured against the eligible-member size with all adult income. + period: 2026 + input: + people: + person1: + age: 34 + immigration_status: CITIZEN + employment_income: 24_000 + person2: + age: 36 + immigration_status: UNDOCUMENTED + employment_income: 24_000 + person3: + age: 5 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_400 + households: + household: + members: [person1, person2, person3] + state_code: AZ + output: + # Policy p. 8 VI.A.6.d: size 2 (citizen parent and child), limit + # 3,671 x 12 = 44,052; income 24,000 + 24,000 = 48,000 from both adults. + az_liheap_household_size: 2 + az_liheap_countable_income: 48_000 + az_liheap_income_limit: 44_052 + # 48,000 > 44,052 (it would pass the size-3 limit of 54,420). + az_liheap_income_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_income_limit.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_income_limit.yaml new file mode 100644 index 00000000000..f7cfe79f12c --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_income_limit.yaml @@ -0,0 +1,94 @@ +# Arizona LIHEAP annual income limit: twelve times the Appendix A monthly +# gross income limit, which is the greater of 60% of the state median income +# and 150% of the federal poverty level (DES LIHEAP Policy rev. 13 eff. +# 2025-08-25, p. 8 VI.A.6.c.i and p. 37 Appendix A, +# https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=37). +# FFY2026 Appendix A monthly limits: 2,807; 3,671; 4,535; 5,399; 6,262; 7,126; +# 7,288; 7,450; 7,612; 8,143; 8,831; 9,518; +687 for each person over 12. +# Reproduction from the federal guidelines (period 2026): +# SMI arm: four-person Arizona SMI 107,983 (FFY2026); floor(0.6 x 107,983) = +# 64,789; size share 0.52 + 0.16 per person two to six + 0.03 per person +# above six (45 CFR 96.85); floor(floor(share x 64,789) / 12). +# FPG arm: 2025 guideline 15,650 + 5,500 per additional person (one-year lag); +# floor(1.5 x FPG(size) / 12), table capped at 12 with +# floor(1.5 x 5,500 / 12) = floor(687.5) = 687 per person above 12. + +- name: Case 1, Appendix A monthly limits times twelve for sizes 1 to 14. + period: 2026 + input: + state_code: [AZ, AZ, AZ, AZ, AZ, AZ, AZ, AZ, AZ, AZ, AZ, AZ, AZ, AZ] + heating_type: [ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY] + pre_subsidy_electricity_expense: [1_800, 1_800, 1_800, 1_800, 1_800, 1_800, 1_800, 1_800, 1_800, 1_800, 1_800, 1_800, 1_800, 1_800] + az_liheap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] + output: + # size 1: floor(0.52 x 64,789) = 33,690; floor(33,690 / 12) = 2,807; + # FPG floor(1.5 x 15,650 / 12) = 1,956; max 2,807 x 12 = 33,684. + # size 2: floor(0.68 x 64,789) = 44,056 -> 3,671; FPG 2,643; 3,671 x 12 = 44,052. + # size 3: floor(0.84 x 64,789) = 54,422 -> 4,535; FPG 3,331; 4,535 x 12 = 54,420. + # size 4: 64,789 -> 5,399; FPG 4,018; 5,399 x 12 = 64,788. + # size 5: floor(1.16 x 64,789) = 75,155 -> 6,262; FPG 4,706; 6,262 x 12 = 75,144. + # size 6: floor(1.32 x 64,789) = 85,521 -> 7,126; FPG 5,393; 7,126 x 12 = 85,512. + # size 7: floor(1.35 x 64,789) = 87,465 -> 7,288; FPG 6,081; 7,288 x 12 = 87,456. + # size 8: floor(1.38 x 64,789) = 89,408 -> 7,450; FPG 6,768; 7,450 x 12 = 89,400. + # size 9: floor(1.41 x 64,789) = 91,352 -> 7,612; FPG 7,456; 7,612 x 12 = 91,344. + # size 10: SMI floor(1.44 x 64,789) = 93,296 -> 7,774; FPG floor(1.5 x + # 65,150 / 12) = floor(8,143.75) = 8,143; max 8,143 x 12 = 97,716. + # size 11: SMI 7,936; FPG floor(1.5 x 70,650 / 12) = 8,831; x 12 = 105,972. + # size 12: SMI 8,098; FPG floor(1.5 x 76,150 / 12) = 9,518; x 12 = 114,216. + # size 13: SMI 8,260; FPG 9,518 + 687 = 10,205; x 12 = 122,460. + # size 14: SMI 8,422; FPG 9,518 + 2 x 687 = 10,892; x 12 = 130,704. + az_liheap_income_limit: [33_684, 44_052, 54_420, 64_788, 75_144, 85_512, 87_456, 89_400, 91_344, 97_716, 105_972, 114_216, 122_460, 130_704] + +- name: Case 2, a household with no eligible member has no income limit. + period: 2026 + input: + people: + person1: + age: 30 + immigration_status: UNDOCUMENTED + person2: + age: 28 + immigration_status: UNDOCUMENTED + spm_units: + spm_unit: + members: [person1, person2] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_500 + households: + household: + members: [person1, person2] + state_code: AZ + output: + az_liheap_household_size: 0 + az_liheap_income_limit: 0 + +- name: Case 3, a five-person family's limit follows its computed household size. + period: 2026 + input: + people: + person1: + age: 38 + employment_income: 44_000 + person2: + age: 36 + employment_income: 31_000 + person3: + age: 12 + person4: + age: 8 + person5: + age: 3 + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5] + heating_type: NATURAL_GAS + pre_subsidy_electricity_expense: 2_400 + gas_expense: 700 + households: + household: + members: [person1, person2, person3, person4, person5] + state_code: AZ + output: + az_liheap_household_size: 5 + # Appendix A size 5: 6,262 x 12 = 75,144. + az_liheap_income_limit: 75_144 diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_earned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_earned_income.yaml new file mode 100644 index 00000000000..13889839577 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_earned_income.yaml @@ -0,0 +1,150 @@ +# Arizona LIHEAP countable earned income per person. Gross wages and +# self-employment income of every member aged 18 or older count (DES LIHEAP +# Policy rev. 13 eff. 2025-08-25, p. 8 VI.A.6.d.ii and p. 13 VII.A.1.c.ii, +# https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8). +# Earned income of members under 18 never counts: rev. 13 p. 9 E excludes all +# under-18 income and rev. 15 (eff. 2026-03-23) p. 9 E excludes their earned +# income. The FY2026 plan leaves "earned income of a child under 18" unchecked +# (https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=7). +# Self-employment is verified as "profit or loss" (policy p. 13); the model +# floors each net source at zero (project convention). +# Arizona's 2025 minimum wage is $14.70 an hour: 14.70 x 2,080 = 30,576. + +- name: Case 1, full-time minimum-wage earnings are counted in full. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 30_576 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + households: + household: + members: [person1] + state_code: AZ + output: + az_liheap_countable_earned_income: 30_576 + +- name: Case 2, a 17-year-old's wages are excluded. + period: 2026 + input: + people: + person1: + age: 42 + employment_income: 30_576 + person2: + age: 17 + employment_income: 6_000 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_100 + households: + household: + members: [person1, person2] + state_code: AZ + output: + # Policy p. 9 E: the 17-year-old's 6,000 is not countable income. + az_liheap_countable_earned_income: [30_576, 0] + az_liheap_countable_income: 30_576 + +- name: Case 3, an 18-year-old's wages are counted. + period: 2026 + input: + people: + person1: + age: 42 + employment_income: 30_576 + person2: + age: 18 + employment_income: 12_000 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_100 + households: + household: + members: [person1, person2] + state_code: AZ + output: + # Policy p. 8 VI.A.6.d.ii: members aged 18 and older; 30,576 + 12,000. + az_liheap_countable_earned_income: [30_576, 12_000] + az_liheap_countable_income: 42_576 + +- name: Case 4, a self-employment loss is floored at zero and does not offset wages. + period: 2026 + input: + people: + person1: + age: 45 + employment_income: 20_000 + self_employment_income: -4_000 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + pre_subsidy_electricity_expense: 1_400 + gas_expense: 500 + households: + household: + members: [person1] + state_code: AZ + output: + # max(20,000, 0) + max(-4,000, 0) = 20,000. + az_liheap_countable_earned_income: 20_000 + +- name: Case 5, every earned source is summed for an adult. + period: 2026 + input: + people: + person1: + age: 51 + employment_income: 18_000 + self_employment_income: 6_000 + farm_operations_income: 2_000 + partnership_s_corp_income: 3_000 + sstb_self_employment_income: 1_000 + spm_units: + spm_unit: + members: [person1] + heating_type: PROPANE + pre_subsidy_electricity_expense: 1_300 + bottled_gas_expense: 1_500 + households: + household: + members: [person1] + state_code: AZ + output: + # 18,000 + 6,000 + 2,000 + 3,000 + 1,000 = 30,000 (plan 1.9: wages and + # self-employment income checked). + az_liheap_countable_earned_income: 30_000 + +- name: Case 6, a 16-year-old's wages stay excluded under revision 15 in 2027. + period: 2027 + input: + people: + person1: + age: 39 + employment_income: 28_000 + person2: + age: 16 + employment_income: 4_500 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_000 + households: + household: + members: [person1, person2] + state_code: AZ + output: + # Rev. 15 p. 9 E: "Earned income from Household members under the age of + # 18 is not considered Countable Income." + az_liheap_countable_earned_income: [28_000, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_income.yaml new file mode 100644 index 00000000000..af6d391c32a --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_income.yaml @@ -0,0 +1,66 @@ +# Arizona LIHEAP countable income of the household: countable earned plus +# unearned income of every member, plus the unit's DES Cash Assistance (TANF) +# benefit (DES LIHEAP Policy rev. 13 eff. 2025-08-25, p. 8 VI.A.6.e.ii.B +# "Unearned income, including SSA and TANF benefits", +# https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8; +# FY2026 plan 1.9 TANF checked, +# https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=6). +# az_tanf is a monthly variable; in a yearly test its input is the annual +# total. Arizona's 2026 payment standard for a parent and one child is 347 a +# month: 347 x 12 = 4,164. + +- name: Case 1, TANF received by the unit is added to the parent's wages. + period: 2026 + input: + people: + person1: + age: 28 + employment_income: 9_000 + person2: + age: 2 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_900 + az_tanf: 4_164 + snap: 0 + households: + household: + members: [person1, person2] + state_code: AZ + output: + # 9,000 wages + 4,164 Cash Assistance = 13,164. + az_liheap_countable_income: 13_164 + +- name: Case 2, earned and unearned income of two adults are summed and a child's income is excluded. + period: 2026 + input: + people: + person1: + age: 44 + employment_income: 26_000 + unemployment_compensation: 2_400 + person2: + age: 67 + social_security: 18_000 + ssi: 0 + person3: + age: 15 + employment_income: 3_000 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: NATURAL_GAS + pre_subsidy_electricity_expense: 2_000 + gas_expense: 700 + az_tanf: 0 + snap: 0 + households: + household: + members: [person1, person2, person3] + state_code: AZ + output: + # 26,000 + 2,400 + 18,000 = 46,400; the 15-year-old's 3,000 is excluded + # (rev. 13 p. 9 E). + az_liheap_countable_income: 46_400 diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.yaml new file mode 100644 index 00000000000..dd1dc4a3d4b --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.yaml @@ -0,0 +1,163 @@ +# Arizona LIHEAP countable unearned income per person. Gross unearned income +# of every member aged 18 or older counts: SSA, SSI, retirement, unemployment +# insurance, alimony, child support, rental income, interest, dividends, +# general assistance and VA benefits (DES LIHEAP Policy rev. 13 eff. +# 2025-08-25, pp. 8-9 VI.A.6.e, +# https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8; +# FY2026 plan 1.9, +# https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=6). +# Members under 18: rev. 13 p. 9 E counts none of their income; rev. 15 +# (eff. 2026-03-23) p. 9 E excludes only their earned income, so a child's +# unearned income counts from period 2027 (parameters are read at the start +# of the period). Social Security is counted gross; the Medicare premium +# netting (policy p. 14 note) is deferred, as in Kansas. +# 2026 SSI federal benefit rate 994 a month: 994 x 12 = 11,928. + +- name: Case 1, Social Security and pension income of a retiree are counted gross. + period: 2026 + input: + people: + person1: + age: 70 + social_security: 23_712 + pension_income: 6_000 + ssi: 0 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + households: + household: + members: [person1] + state_code: AZ + output: + # 23,712 + 6,000 = 29,712 (plan 1.9: SSA and retirement/pension checked). + az_liheap_countable_unearned_income: 29_712 + +- name: Case 2, unemployment, child support, alimony and rent are counted. + period: 2026 + input: + people: + person1: + age: 45 + unemployment_compensation: 9_600 + child_support_received: 4_800 + alimony_income: 3_000 + rental_income: 6_000 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + pre_subsidy_electricity_expense: 1_500 + gas_expense: 600 + households: + household: + members: [person1] + state_code: AZ + output: + # 9,600 + 4,800 + 3,000 + 6,000 = 23,400. + az_liheap_countable_unearned_income: 23_400 + +- name: Case 3, SSI, general assistance and VA benefits of a disabled adult are counted. + period: 2026 + input: + people: + person1: + age: 50 + is_disabled: true + ssi: 11_928 + general_assistance: 1_200 + veterans_benefits: 4_800 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_600 + households: + household: + members: [person1] + state_code: AZ + output: + # 11,928 + 1,200 + 4,800 = 17,928 (plan 1.9: SSI, general assistance and + # VA benefits checked). + az_liheap_countable_unearned_income: 17_928 + +- name: Case 4, interest, dividends, strike pay and an IRA distribution each count once. + period: 2026 + input: + people: + person1: + age: 40 + taxable_interest_income: 1_200 + ordinary_dividend_income: 800 + strike_benefits: 2_000 + taxable_ira_distributions: 3_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_700 + households: + household: + members: [person1] + state_code: AZ + output: + # 1,200 + 800 + 2,000 + 3,000 = 7,000. The IRA distribution reaches the + # total once, through retirement_distributions (contract R1). + az_liheap_countable_unearned_income: 7_000 + +- name: Case 5, a child's SSI and child support are not counted in 2026 under revision 13. + period: 2026 + input: + people: + person1: + age: 35 + employment_income: 30_000 + person2: + age: 10 + is_disabled: true + ssi: 11_928 + child_support_received: 4_800 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_200 + households: + household: + members: [person1, person2] + state_code: AZ + output: + # Rev. 13 p. 9 E: "Income from Household members under the age of 18 is + # not considered Countable Income." + az_liheap_countable_unearned_income: [0, 0] + az_liheap_countable_income: 30_000 + +- name: Case 6, the same child's SSI and child support are counted in 2027 under revision 15. + period: 2027 + input: + people: + person1: + age: 35 + employment_income: 30_000 + person2: + age: 10 + is_disabled: true + ssi: 11_928 + child_support_received: 4_800 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_200 + households: + household: + members: [person1, person2] + state_code: AZ + output: + # Rev. 15 p. 9 E excludes only earned income of members under 18: + # 11,928 + 4,800 = 16,728 counts. + az_liheap_countable_unearned_income: [0, 16_728] + # 30,000 + 16,728 = 46,728. + az_liheap_countable_income: 46_728 diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/integration.yaml new file mode 100644 index 00000000000..98263590066 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/integration.yaml @@ -0,0 +1,277 @@ +# Arizona Standard LIHEAP benefit, end to end. Sources: DES LIHEAP Policy +# rev. 13 eff. 2025-08-25 (household and immigration pp. 4-8, categorical +# eligibility pp. 7-8, countable income pp. 8-9, benefit factors p. 10, +# Appendix A p. 37) and rev. 15 eff. 2026-03-23 (p. 9 E, earned income of +# under-18 members only), https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8; +# LIHEAP Eligibility Worksheet, $640 Limit, eff. 2024-10-12 (point tables and +# payment matrix), +# https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1; +# FY2026 plan (categorical programs 1.4, income 1.8-1.9, $160 minimum and +# $640 maximum 2.6), https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=10; +# DES program page (formulas and examples), https://des.az.gov/liheap. +# FFY2026 annual limits (Appendix A monthly x 12): size 1 2,807 x 12 = +# 33,684; size 2 3,671 x 12 = 44,052; size 3 4,535 x 12 = 54,420. +# Points: income ratio 0-20% 5, -40% 4, -60% 3, -80% 2, -100% 1; burden 5% or +# less 0, -10% 1, -15% 2, -20% 3, -25% 4, -30% 5, over 30% 6, +2 portable +# fuel; need 1 each for elderly 60+, disabled, veteran, child 6 or under. +# Payment: 1-4 points 160, 5-8 320, 9-12 480, 13+ 640; 0 points pays the +# plan's 160 minimum. + +- name: Case 1, two working parents and a preschooler in Phoenix with electric heat receive 320. + period: 2026 + input: + people: + person1: + age: 35 + employment_income: 14_000 + person2: + age: 37 + employment_income: 10_000 + person3: + age: 4 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_400 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1, person2, person3] + state_code: AZ + output: + az_liheap_household_size: 3 + # 14,000 + 10,000 = 24,000 (2,000 a month). + az_liheap_countable_income: 24_000 + # Appendix A size 3: 4,535 x 12. + az_liheap_income_limit: 54_420 + # 24,000 <= 54,420. + az_liheap_income_eligible: true + az_liheap_categorically_eligible: false + az_liheap_energy_cost: 2_400 + az_liheap_eligible: true + # 24,000 / 54,420 = 0.441 (2,000 / 4,535 = 44%): 41%-60% band. + az_liheap_income_points: 3 + # 2,400 / 24,000 = 0.10 exactly: top of the 6%-10% band, 1 point. + az_liheap_energy_burden_points: 1 + # Child aged 4. + az_liheap_energy_need_points: 1 + # 3 + 1 + 1 = 5: the 5-8 band. + az_liheap_points: 5 + az_liheap: 320 + +- name: Case 2, a retiree on SNAP above the income limit with propane heat receives 160. + period: 2026 + input: + people: + person1: + age: 70 + social_security: 36_000 + ssi: 0 + spm_units: + spm_unit: + members: [person1] + heating_type: PROPANE + pre_subsidy_electricity_expense: 1_200 + bottled_gas_expense: 1_500 + snap: 2_400 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + az_liheap_household_size: 1 + # Social Security counted gross. + az_liheap_countable_income: 36_000 + # Appendix A size 1: 2,807 x 12. + az_liheap_income_limit: 33_684 + # 36,000 > 33,684. + az_liheap_income_eligible: false + # SNAP receipt (200 a month) confers categorical eligibility. + az_liheap_categorically_eligible: true + # 1,200 + 1,500. + az_liheap_energy_cost: 2_700 + az_liheap_eligible: true + # 36,000 / 33,684 = 1.069 > 1.00: no income points. + az_liheap_income_points: 0 + # 2,700 / 36,000 = 0.075: 6%-10% band, 1 point + 2 portable fuel. + az_liheap_energy_burden_points: 3 + # Aged 60 and over. + az_liheap_energy_need_points: 1 + # 0 + 3 + 1 = 4: the 1-4 band. + az_liheap_points: 4 + az_liheap: 160 + +- name: Case 3, a mixed-status family of four is sized at three and receives 160. + period: 2026 + input: + people: + person1: + age: 34 + immigration_status: CITIZEN + employment_income: 26_000 + person2: + age: 36 + immigration_status: UNDOCUMENTED + employment_income: 22_000 + person3: + age: 9 + person4: + age: 5 + spm_units: + spm_unit: + members: [person1, person2, person3, person4] + heating_type: NATURAL_GAS + pre_subsidy_electricity_expense: 2_700 + gas_expense: 500 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1, person2, person3, person4] + state_code: AZ + output: + # Policy p. 8 VI.A.6.d.i: the undocumented parent is not counted. + az_liheap_household_size: 3 + # Policy p. 8 VI.A.6.d.ii: 26,000 + 22,000, both adults' wages. + az_liheap_countable_income: 48_000 + az_liheap_income_limit: 54_420 + # 48,000 <= 54,420. + az_liheap_income_eligible: true + # 2,700 + 500. + az_liheap_energy_cost: 3_200 + az_liheap_eligible: true + # 48,000 / 54,420 = 0.882: 81%-100% band. + az_liheap_income_points: 1 + # 3,200 / 48,000 = 0.067: 6%-10% band; natural gas, no bonus. + az_liheap_energy_burden_points: 1 + # Child aged 5. + az_liheap_energy_need_points: 1 + # 1 + 1 + 1 = 3. + az_liheap_points: 3 + az_liheap: 160 + +- name: Case 4, a Cash Assistance family with energy included in rent receives 320. + period: 2026 + input: + people: + person1: + age: 28 + employment_income: 0 + person2: + age: 2 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + heat_expense_included_in_rent: true + pre_subsidy_electricity_expense: 0 + gas_expense: 0 + snap: 0 + az_tanf: 4_164 + households: + household: + members: [person1, person2] + state_code: AZ + output: + az_liheap_household_size: 2 + # Cash Assistance 347 x 12 = 4,164 counts as income (policy p. 8 B). + az_liheap_countable_income: 4_164 + # Appendix A size 2: 3,671 x 12. + az_liheap_income_limit: 44_052 + az_liheap_income_eligible: true + # Current Cash Assistance enrollment (policy p. 7 VI.A.6.b). + az_liheap_categorically_eligible: true + az_liheap_energy_cost: 0 + # Energy in rent satisfies the energy-expense requirement. + az_liheap_eligible: true + # 4,164 / 44,052 = 0.095: 0%-20% band. + az_liheap_income_points: 5 + # No separate energy bill: 0 burden points. + az_liheap_energy_burden_points: 0 + # Child aged 2. + az_liheap_energy_need_points: 1 + # 5 + 0 + 1 = 6: the 5-8 band. + az_liheap_points: 6 + az_liheap: 320 + +- name: Case 5, an otherwise eligible household with an unspecified heating type receives nothing. + period: 2026 + input: + people: + person1: + age: 45 + employment_income: 28_000 + spm_units: + spm_unit: + members: [person1] + heating_type: UNSPECIFIED + pre_subsidy_electricity_expense: 2_000 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + az_liheap_household_size: 1 + az_liheap_countable_income: 28_000 + az_liheap_income_limit: 33_684 + # 28,000 <= 33,684. + az_liheap_income_eligible: true + az_liheap_energy_cost: 2_000 + # Project rule: an unspecified heating type is not eligible. + az_liheap_eligible: false + # 28,000 / 33,684 = 0.831: 81%-100% band. + az_liheap_income_points: 1 + # 2,000 / 28,000 = 0.071: 6%-10% band. + az_liheap_energy_burden_points: 1 + az_liheap_points: 2 + az_liheap: 0 + +- name: Case 6, in 2027 a child's child support counts under revision 15 and the FFY2027 limit applies. + period: 2027 + input: + people: + person1: + age: 30 + employment_income: 18_000 + person2: + age: 8 + child_support_received: 3_600 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_000 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1, person2] + state_code: AZ + output: + az_liheap_household_size: 2 + # Rev. 15 p. 9 E excludes only earned income of members under 18. + az_liheap_countable_unearned_income: [0, 3_600] + # 18,000 + 3,600. + az_liheap_countable_income: 21_600 + # Model projection for FFY2027: Arizona SMI 112,946; floor(0.6 x 112,946) + # = 67,767; floor(0.68 x 67,767) = 46,081; floor(46,081 / 12) = 3,840; + # FPG arm floor(1.5 x (15,960 + 5,680) / 12) = 2,705; 3,840 x 12 = 46,080. + # Equals the two-person limit on the DES program page captured 2026-09-21 + # (not independently verified policy). + az_liheap_income_limit: 46_080 + az_liheap_income_eligible: true + az_liheap_eligible: true + # 21,600 / 46,080 = 0.469: 41%-60% band. + az_liheap_income_points: 3 + # 2,000 / 21,600 = 0.093: 6%-10% band. + az_liheap_energy_burden_points: 1 + # Child aged 8: no need point. + az_liheap_energy_need_points: 0 + # 3 + 1 + 0 = 4: the 1-4 band. + az_liheap_points: 4 + az_liheap: 160 diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/points/az_liheap_energy_burden_points.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/points/az_liheap_energy_burden_points.yaml new file mode 100644 index 00000000000..d240f704acc --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/points/az_liheap_energy_burden_points.yaml @@ -0,0 +1,405 @@ +# Arizona LIHEAP energy burden points: total monthly energy cost (electric, +# gas and portable fuel) divided by monthly gross income; 5% or less 0 points, +# 6%-10% 1, 11%-15% 2, 16%-20% 3, 21%-25% 4, 26%-30% 5, 31% or over 6, plus 2 +# points for portable fuels (LIHEAP Eligibility Worksheet, $640 Limit, eff. +# 2024-10-12, section 2, +# https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1; +# DES program page example "350 / 2,000 = 17.5%, three points", +# https://des.az.gov/liheap). Portable Fuel is propane, wood, coal, generator +# fuel and pellets (DES LIHEAP Policy rev. 13, p. 5, +# https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=5), +# modeled as heating_type PROPANE, WOOD or COAL. Annual bills over annual +# income give the same ratio as the monthly figures; a band top belongs to its +# band (exactly 5% is 0 points). Zero income with a bill is the top band; no +# bill is 0 points. Single adult earning 24,000 (2,000 a month) throughout. + +- name: Case 1, the program-page example of a 350 bill on 2,000 income gives 3 points. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 24_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 4_200 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # 4,200 / 24,000 = 0.175 (350 / 2,000 = 17.5%): 16%-20% band. + az_liheap_energy_burden_points: 3 + +- name: Case 2, a bill at exactly 5 percent of income scores 0 points. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 24_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_200 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # 1,200 / 24,000 = 0.05 exactly: "5% or less". + az_liheap_energy_burden_points: 0 + +- name: Case 3, one dollar above 5 percent of income scores 1 point. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 24_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_201 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # 1,201 / 24,000 = 0.05004 > 0.05: 6%-10% band. + az_liheap_energy_burden_points: 1 + +- name: Case 4, a bill at exactly 10 percent of income keeps 1 point. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 24_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_400 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # 2,400 / 24,000 = 0.10 exactly: top of the 6%-10% band. + az_liheap_energy_burden_points: 1 + +- name: Case 5, a bill at exactly 15 percent of income keeps 2 points. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 24_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 3_600 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # 3,600 / 24,000 = 0.15 exactly: top of the 11%-15% band. + az_liheap_energy_burden_points: 2 + +- name: Case 6, a bill at exactly 20 percent of income keeps 3 points. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 24_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 4_800 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # 4,800 / 24,000 = 0.20 exactly: top of the 16%-20% band. + az_liheap_energy_burden_points: 3 + +- name: Case 7, a bill at exactly 25 percent of income keeps 4 points. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 24_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 6_000 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # 6,000 / 24,000 = 0.25 exactly: top of the 21%-25% band. + az_liheap_energy_burden_points: 4 + +- name: Case 8, a bill at exactly 30 percent of income keeps 5 points. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 24_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 7_200 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # 7,200 / 24,000 = 0.30 exactly: top of the 26%-30% band. + az_liheap_energy_burden_points: 5 + +- name: Case 9, a bill at 31 percent of income scores 6 points. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 24_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 7_440 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # 7,440 / 24,000 = 0.31: "31% or over". + az_liheap_energy_burden_points: 6 + +- name: Case 10, a household with no income and a bill scores the top band. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 0 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # 1,800 against zero income: the burden is unbounded, 31% or over. + az_liheap_energy_burden_points: 6 + +- name: Case 11, no energy bill scores 0 points even with low income. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 12_000 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + heat_expense_included_in_rent: true + pre_subsidy_electricity_expense: 0 + gas_expense: 0 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # 0 / 12,000 = 0: "5% or less". + az_liheap_energy_burden_points: 0 + +- name: Case 12, propane heat adds 2 portable-fuel points. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 24_000 + spm_units: + spm_unit: + members: [person1] + heating_type: PROPANE + pre_subsidy_electricity_expense: 1_200 + bottled_gas_expense: 1_500 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # (1,200 + 1,500) / 24,000 = 0.1125: 11%-15% band 2 points + 2 portable. + az_liheap_energy_cost: 2_700 + az_liheap_energy_burden_points: 4 + +- name: Case 13, wood heat adds 2 portable-fuel points. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 24_000 + spm_units: + spm_unit: + members: [person1] + heating_type: WOOD + pre_subsidy_electricity_expense: 1_200 + wood_expense: 900 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # (1,200 + 900) / 24,000 = 0.0875: 6%-10% band 1 point + 2 portable. + az_liheap_energy_burden_points: 3 + +- name: Case 14, coal heat adds 2 portable-fuel points. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 24_000 + spm_units: + spm_unit: + members: [person1] + heating_type: COAL + pre_subsidy_electricity_expense: 1_200 + coal_expense: 800 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # (1,200 + 800) / 24,000 = 0.0833: 6%-10% band 1 point + 2 portable. + az_liheap_energy_burden_points: 3 + +- name: Case 15, natural gas heat earns no portable-fuel points. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 24_000 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + pre_subsidy_electricity_expense: 1_200 + gas_expense: 600 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # (1,200 + 600) / 24,000 = 0.075: 6%-10% band, 1 point, no bonus. + az_liheap_energy_burden_points: 1 + +- name: Case 16, fuel oil is not an Arizona portable fuel and earns no bonus. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 24_000 + spm_units: + spm_unit: + members: [person1] + heating_type: FUEL_OIL + pre_subsidy_electricity_expense: 1_200 + fuel_oil_expense: 1_500 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # (1,200 + 1,500) / 24,000 = 0.1125: 2 points; policy p. 5 lists propane, + # wood, coal, generator fuel and pellets only. + az_liheap_energy_burden_points: 2 + +- name: Case 17, all seven energy bills are summed into the burden. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 24_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_200 + gas_expense: 600 + bottled_gas_expense: 300 + wood_expense: 200 + coal_expense: 100 + fuel_oil_expense: 150 + other_heating_fuel_expense: 450 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # 1,200 + 600 + 300 + 200 + 100 + 150 + 450 = 3,000. + az_liheap_energy_cost: 3_000 + # 3,000 / 24,000 = 0.125: 11%-15% band; electric heat, no bonus. + az_liheap_energy_burden_points: 2 diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/points/az_liheap_energy_need_points.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/points/az_liheap_energy_need_points.yaml new file mode 100644 index 00000000000..7f140079e88 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/points/az_liheap_energy_need_points.yaml @@ -0,0 +1,327 @@ +# Arizona LIHEAP energy need points: one point each, and only one point per +# category, when any member is elderly (60 and over), disabled, a veteran, or +# a child aged 6 or younger (LIHEAP Eligibility Worksheet, $640 Limit, eff. +# 2024-10-12, section 3, +# https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1; +# DES LIHEAP Policy rev. 13, p. 10 VI.B.1.d "Aged six (6) and under; Aged 60 +# and over; A veteran; A person with a disability", +# https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=10). +# Disability (policy p. 4): an ADA impairment, or categorically a recipient of +# Title II (SSDI) or Title XVI (SSI) benefits. 2026 SSI rate 994 x 12 = +# 11,928. Base household: a 40-year-old earning 30,000 with electric heat. + +- name: Case 1, a working-age adult with no need factor has 0 points. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 30_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + az_liheap_energy_need_points: 0 + +- name: Case 2, a member aged 60 is elderly and adds 1 point. + period: 2026 + input: + people: + person1: + age: 60 + employment_income: 30_000 + ssi: 0 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + az_liheap_energy_need_points: 1 + +- name: Case 3, a member aged 59 is not elderly. + period: 2026 + input: + people: + person1: + age: 59 + employment_income: 30_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + az_liheap_energy_need_points: 0 + +- name: Case 4, a disabled member adds 1 point. + period: 2026 + input: + people: + person1: + age: 40 + is_disabled: true + employment_income: 30_000 + ssi: 0 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + az_liheap_energy_need_points: 1 + +- name: Case 5, an SSI recipient is categorically disabled and adds 1 point. + period: 2026 + input: + people: + person1: + age: 40 + is_disabled: false + ssi: 11_928 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_600 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # Policy p. 4: a Title XVI recipient meets the disability definition. + az_liheap_energy_need_points: 1 + +- name: Case 6, an SSDI recipient is categorically disabled and adds 1 point. + period: 2026 + input: + people: + person1: + age: 48 + is_disabled: false + social_security_disability: 18_960 + ssi: 0 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_600 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # Policy p. 4: a Title II recipient meets the disability definition. + az_liheap_energy_need_points: 1 + +- name: Case 7, a veteran adds 1 point. + period: 2026 + input: + people: + person1: + age: 40 + is_veteran: true + employment_income: 30_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + az_liheap_energy_need_points: 1 + +- name: Case 8, VA benefit receipt identifies a veteran and adds 1 point. + period: 2026 + input: + people: + person1: + age: 45 + veterans_benefits: 9_600 + employment_income: 20_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + az_liheap_energy_need_points: 1 + +- name: Case 9, a child aged 6 adds 1 point. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 30_000 + person2: + age: 6 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_900 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1, person2] + state_code: AZ + output: + # "Aged six (6) and under". + az_liheap_energy_need_points: 1 + +- name: Case 10, a child aged 7 does not add a point. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 30_000 + person2: + age: 7 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_900 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1, person2] + state_code: AZ + output: + az_liheap_energy_need_points: 0 + +- name: Case 11, all four categories together add 4 points. + period: 2026 + input: + people: + person1: + age: 70 + social_security: 20_000 + ssi: 0 + person2: + age: 45 + is_disabled: true + employment_income: 15_000 + ssi: 0 + person3: + age: 50 + is_veteran: true + employment_income: 18_000 + person4: + age: 5 + spm_units: + spm_unit: + members: [person1, person2, person3, person4] + heating_type: NATURAL_GAS + pre_subsidy_electricity_expense: 2_400 + gas_expense: 700 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1, person2, person3, person4] + state_code: AZ + output: + # Elderly 1 + disabled 1 + veteran 1 + child aged 6 or under 1. + az_liheap_energy_need_points: 4 + +- name: Case 12, two elderly members and two young children count one point per category. + period: 2026 + input: + people: + person1: + age: 68 + social_security: 18_000 + ssi: 0 + person2: + age: 66 + social_security: 12_000 + ssi: 0 + person3: + age: 4 + person4: + age: 2 + spm_units: + spm_unit: + members: [person1, person2, person3, person4] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_600 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1, person2, person3, person4] + state_code: AZ + output: + # "Only one point per category allowed": elderly 1 + child 1 = 2. + az_liheap_energy_need_points: 2 + +- name: Case 13, one member who is elderly, disabled and a veteran earns 3 category points. + period: 2026 + input: + people: + person1: + age: 72 + is_disabled: true + is_veteran: true + social_security: 16_000 + ssi: 0 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_700 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # Points attach to categories, not persons: elderly + disabled + veteran. + az_liheap_energy_need_points: 3 diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/points/az_liheap_income_points.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/points/az_liheap_income_points.yaml new file mode 100644 index 00000000000..e566f4c199d --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/points/az_liheap_income_points.yaml @@ -0,0 +1,334 @@ +# Arizona LIHEAP income points: monthly gross income divided by the monthly +# income limit for the eligible household size; 0%-20% 5 points, 21%-40% 4, +# 41%-60% 3, 61%-80% 2, 81%-100% 1, above 100% none (LIHEAP Eligibility +# Worksheet, $640 Limit, eff. 2024-10-12, section 1, +# https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1; +# DES program page formula and example "2,000 / 4,535 = 44%, three points", +# https://des.az.gov/liheap). Annual income over the annual limit gives the +# same ratio as the monthly figures. The ratio is continuous and a band top +# belongs to its band (exactly 20% is 5 points). Household of three (two +# adults and a child aged 4): limit 4,535 x 12 = 54,420. + +- name: Case 1, the program-page example of 2,000 a month against the 4,535 limit gives 3 points. + period: 2026 + input: + people: + person1: + age: 35 + employment_income: 14_000 + person2: + age: 37 + employment_income: 10_000 + person3: + age: 4 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_400 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1, person2, person3] + state_code: AZ + output: + # 24,000 / 54,420 = 0.441 (2,000 / 4,535 = 44%): 41%-60% band. + az_liheap_income_points: 3 + +- name: Case 2, a household with no income has 5 points. + period: 2026 + input: + people: + person1: + age: 35 + employment_income: 0 + person2: + age: 37 + employment_income: 0 + person3: + age: 4 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_400 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1, person2, person3] + state_code: AZ + output: + # 0 / 54,420 = 0: 0%-20% band. + az_liheap_income_points: 5 + +- name: Case 3, income at exactly 20 percent of the limit keeps 5 points. + period: 2026 + input: + people: + person1: + age: 35 + employment_income: 10_884 + person2: + age: 37 + employment_income: 0 + person3: + age: 4 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_400 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1, person2, person3] + state_code: AZ + output: + # 10,884 / 54,420 = 0.20 exactly: top of the 0%-20% band. + az_liheap_income_points: 5 + +- name: Case 4, one dollar above 20 percent of the limit drops to 4 points. + period: 2026 + input: + people: + person1: + age: 35 + employment_income: 10_885 + person2: + age: 37 + employment_income: 0 + person3: + age: 4 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_400 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1, person2, person3] + state_code: AZ + output: + # 10,885 / 54,420 = 0.20002 > 0.20: 21%-40% band. + az_liheap_income_points: 4 + +- name: Case 5, income at exactly 40 percent of the limit keeps 4 points. + period: 2026 + input: + people: + person1: + age: 35 + employment_income: 21_768 + person2: + age: 37 + employment_income: 0 + person3: + age: 4 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_400 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1, person2, person3] + state_code: AZ + output: + # 21,768 / 54,420 = 0.40 exactly. + az_liheap_income_points: 4 + +- name: Case 6, one dollar above 40 percent of the limit drops to 3 points. + period: 2026 + input: + people: + person1: + age: 35 + employment_income: 21_769 + person2: + age: 37 + employment_income: 0 + person3: + age: 4 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_400 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1, person2, person3] + state_code: AZ + output: + # 21,769 / 54,420 = 0.40002 > 0.40. + az_liheap_income_points: 3 + +- name: Case 7, income at exactly 60 percent of the limit keeps 3 points. + period: 2026 + input: + people: + person1: + age: 35 + employment_income: 20_000 + person2: + age: 37 + employment_income: 12_652 + person3: + age: 4 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_400 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1, person2, person3] + state_code: AZ + output: + # 20,000 + 12,652 = 32,652; 32,652 / 54,420 = 0.60 exactly. + az_liheap_income_points: 3 + +- name: Case 8, one dollar above 60 percent of the limit drops to 2 points. + period: 2026 + input: + people: + person1: + age: 35 + employment_income: 20_000 + person2: + age: 37 + employment_income: 12_653 + person3: + age: 4 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_400 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1, person2, person3] + state_code: AZ + output: + # 32,653 / 54,420 = 0.60002 > 0.60. + az_liheap_income_points: 2 + +- name: Case 9, income at exactly 80 percent of the limit keeps 2 points. + period: 2026 + input: + people: + person1: + age: 35 + employment_income: 26_000 + person2: + age: 37 + employment_income: 17_536 + person3: + age: 4 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_400 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1, person2, person3] + state_code: AZ + output: + # 26,000 + 17,536 = 43,536; 43,536 / 54,420 = 0.80 exactly. + az_liheap_income_points: 2 + +- name: Case 10, one dollar above 80 percent of the limit drops to 1 point. + period: 2026 + input: + people: + person1: + age: 35 + employment_income: 26_000 + person2: + age: 37 + employment_income: 17_537 + person3: + age: 4 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_400 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1, person2, person3] + state_code: AZ + output: + # 43,537 / 54,420 = 0.80002 > 0.80. + az_liheap_income_points: 1 + +- name: Case 11, income at exactly the limit keeps 1 point. + period: 2026 + input: + people: + person1: + age: 35 + employment_income: 30_000 + person2: + age: 37 + employment_income: 24_420 + person3: + age: 4 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_400 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1, person2, person3] + state_code: AZ + output: + # 30,000 + 24,420 = 54,420; 54,420 / 54,420 = 1.00: 81%-100% band. + az_liheap_income_points: 1 + +- name: Case 12, a SNAP household above the limit has no income points. + period: 2026 + input: + people: + person1: + age: 35 + employment_income: 34_000 + person2: + age: 37 + employment_income: 26_000 + person3: + age: 4 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 2_400 + snap: 2_400 + az_tanf: 0 + households: + household: + members: [person1, person2, person3] + state_code: AZ + output: + # 60,000 / 54,420 = 1.10 > 1.00: no worksheet row. + az_liheap_income_points: 0 diff --git a/policyengine_us/variables/gov/states/az/des/liheap/az_liheap.py b/policyengine_us/variables/gov/states/az/des/liheap/az_liheap.py new file mode 100644 index 00000000000..b350fc04386 --- /dev/null +++ b/policyengine_us/variables/gov/states/az/des/liheap/az_liheap.py @@ -0,0 +1,41 @@ +from policyengine_us.model_api import * + + +class az_liheap(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Arizona LIHEAP" + documentation = ( + "Annual Arizona Standard LIHEAP benefit, paid once per 12-month " + "eligibility period from the benefit matrix points: $160 for up to 4 " + "points, $320 for 5 to 8, $480 for 9 to 12 and $640 for 13 or more. " + "It covers heating and cooling energy bills alike and is not capped at " + "the bill. Verified for FY2026; earlier years use model parameter " + "backfilling and are unverified historical estimates. Crisis " + "assistance, water for evaporative cooling, weatherization and the " + "Senior Utility Assistance Program recertification maximum are not " + "modeled." + ) + defined_for = "az_liheap_eligible" + reference = ( + "https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1", + # Section 1.2 (page 4) and Section 2.6 (page 10). + "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=10", + # Sections VI.B.1 (page 10), VII.B.1.a (page 29), VII.B.3.b.iii + # (pages 33-34) and VII.B.4.b (page 34). + "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=29", + "https://des.az.gov/liheap", + ) + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.az.des.liheap.payment + points = spm_unit("az_liheap_points", period) + # Section VII.B.3.b.iii.D.2: if all vendor accounts "have been paid in + # full by their LIHEAP benefit and there is a remaining benefit, this + # benefit will be allocated to the highest priority" account, so the + # payment is the matrix amount, not the bill. The plan's heating + # (16.80%) and cooling (38.12%) components are funding shares of this + # single Standard benefit, not separate household payments. + return p.amount.calc(points) diff --git a/policyengine_us/variables/gov/states/az/des/liheap/az_liheap_energy_cost.py b/policyengine_us/variables/gov/states/az/des/liheap/az_liheap_energy_cost.py new file mode 100644 index 00000000000..23a3131cb14 --- /dev/null +++ b/policyengine_us/variables/gov/states/az/des/liheap/az_liheap_energy_cost.py @@ -0,0 +1,34 @@ +from policyengine_us.model_api import * + + +class az_liheap_energy_cost(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Arizona LIHEAP annual energy cost" + documentation = "Annual electricity, gas and portable fuel bills entering the energy burden, the worksheet's 'Electric + Gas + Portable Fuel = Total Monthly Cost' taken over the year. Water for evaporative cooling, sewer, trash and phone bills are excluded." + reference = ( + "https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1", + # Energy Utility and Portable Fuel definitions (pages 4-5). + "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=4", + ) + defined_for = StateCode.AZ + + # "Energy Utility: A service or Portable Fuel that provides a source of + # heating or cooling for a Household. Eligible Energy Utility service + # providers include vendors of electricity, gas, or portable fuel" (page 4); + # "Portable Fuel: Energy that is derived from propane, wood, coal, fuel for + # gas generators, and/or pellets" (page 5). Fuel oil and other heating fuel + # bills are purchased home energy and are counted with them. The electric + # bill is read before subsidies, as heating_expense does, so the burden is + # gross of discount programs and no subsidy chain feeds back into LIHEAP. + adds = [ + "pre_subsidy_electricity_expense", + "gas_expense", + "bottled_gas_expense", + "wood_expense", + "coal_expense", + "fuel_oil_expense", + "other_heating_fuel_expense", + ] diff --git a/policyengine_us/variables/gov/states/az/des/liheap/az_liheap_household_size.py b/policyengine_us/variables/gov/states/az/des/liheap/az_liheap_household_size.py new file mode 100644 index 00000000000..282c28f4ff3 --- /dev/null +++ b/policyengine_us/variables/gov/states/az/des/liheap/az_liheap_household_size.py @@ -0,0 +1,23 @@ +from policyengine_us.model_api import * + + +class az_liheap_household_size(Variable): + value_type = int + entity = SPMUnit + definition_period = YEAR + label = "Arizona LIHEAP household size" + documentation = "Number of household members who are U.S. citizens or qualified noncitizens. Other members are not counted in household size, although the income of every adult member counts toward household income." + reference = ( + # Eligible Household Member and Mixed-Status Household definitions + # (pages 4-5) and Section VI.A.6.d.i (page 8). + "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8", + ) + defined_for = StateCode.AZ + + # Section VI.A.6.d: "In Mixed-Status Households: i. Only Eligible Household + # Members will be considered as part of the Household size; and ii. Income + # from all Household members aged 18 and older - regardless of eligibility - + # is considered Household income." An Eligible Household Member is "a U.S. + # citizen or Qualified Noncitizen" (page 4). The model's qualified statuses + # omit COFA citizens, trafficking victims and battered noncitizens. + adds = ["is_citizen_or_legal_immigrant"] diff --git a/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_categorically_eligible.py b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_categorically_eligible.py new file mode 100644 index 00000000000..86128cd4321 --- /dev/null +++ b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_categorically_eligible.py @@ -0,0 +1,28 @@ +from policyengine_us.model_api import * + + +class az_liheap_categorically_eligible(Variable): + value_type = bool + entity = SPMUnit + definition_period = YEAR + label = "Arizona LIHEAP categorically eligible" + documentation = "A household member receives DES Cash Assistance (TANF) or Nutrition Assistance (SNAP), which satisfies the income requirement; all other eligibility criteria still apply." + reference = ( + # Categorical Eligibility definition (pages 3-4) and Section VI.A.6.a-b + # (page 7). + "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=7", + # Sections 1.4 and 1.4a (page 5). + "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=5", + ) + defined_for = StateCode.AZ + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.az.des.liheap.eligibility + # Section VI.A.6.b.i: "If one (1) Household member's current enrollment + # in Cash Assistance or Nutrition Assistance is verified, the Household + # will be considered categorically eligible." A positive modeled + # benefit in the year stands for current enrollment; the MONTH-defined + # snap and az_tanf are summed over the year by add(). SSI and + # veterans programs do not confer categorical eligibility (plan + # Section 1.4). + return add(spm_unit, period, p.categorical_programs) > 0 diff --git a/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_eligible.py b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_eligible.py new file mode 100644 index 00000000000..442281d8e63 --- /dev/null +++ b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_eligible.py @@ -0,0 +1,52 @@ +from policyengine_us.model_api import * + + +class az_liheap_eligible(Variable): + value_type = bool + entity = SPMUnit + definition_period = YEAR + label = "Eligible for Arizona LIHEAP" + documentation = ( + "Arizona household with at least one citizen or qualified noncitizen " + "member that is categorically or income eligible, has a counted energy " + "bill or energy included in its rent, and reports its heating fuel. The " + "subsidized-housing utility allowance test, the Tribal LIHEAP " + "first-apply rule and the bar on costs already paid by another program " + "have no inputs and are not modeled." + ) + reference = ( + # Household definition (page 4), Sections VI.A.1-6 (pages 6-8) and + # Section VII.B.3.a.ii.B (page 32). + "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=6", + # Sections 2.1-2.3 (page 9). + "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=9", + ) + defined_for = StateCode.AZ + + def formula(spm_unit, period, parameters): + # Section VI.A.3: "An Applicant, or a Household member, must be an + # Eligible Household Member"; the size counts only such members. + has_eligible_member = spm_unit("az_liheap_household_size", period) > 0 + # Section VI.A.6.a: "A Household must either meet Categorical + # Eligibility or Income Eligibility requirements." + categorically_eligible = spm_unit("az_liheap_categorically_eligible", period) + income_eligible = spm_unit("az_liheap_income_eligible", period) + # Section VI.A.1: "An Applicant must require assistance with payment of + # energy-related expenses." The Household definition (page 4) includes + # members "who make undesignated payments for Energy Utilities in the + # form of rent", and Section VII.B.3.a.ii.B pays the applicant directly + # when "Energy Utilities are included within the cost of their rent". + has_energy_cost = spm_unit("az_liheap_energy_cost", period) > 0 + heat_in_rent = spm_unit("heat_expense_included_in_rent", period) + # heating_type is a required input and UNSPECIFIED pays nothing. NONE + # stays eligible: the Standard benefit pays any heating or cooling + # Energy Utility bill, not the heating fuel alone, so a home without + # heating that pays for electricity still requires assistance. + heating_type = spm_unit("heating_type", period) + known_fuel = heating_type != heating_type.possible_values.UNSPECIFIED + return ( + has_eligible_member + & (categorically_eligible | income_eligible) + & (has_energy_cost | heat_in_rent) + & known_fuel + ) diff --git a/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.py b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.py new file mode 100644 index 00000000000..96f1977a4e7 --- /dev/null +++ b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.py @@ -0,0 +1,23 @@ +from policyengine_us.model_api import * + + +class az_liheap_income_eligible(Variable): + value_type = bool + entity = SPMUnit + definition_period = YEAR + label = "Arizona LIHEAP income eligible" + documentation = "Countable household income does not exceed the Appendix A income limit for the eligible household size." + reference = ( + # Section VI.A.6.c.i (page 8) and Appendix A (page 37). + "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8", + # Section 2.1 (page 9). + "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=9", + ) + defined_for = StateCode.AZ + + def formula(spm_unit, period, parameters): + income = spm_unit("az_liheap_countable_income", period) + limit = spm_unit("az_liheap_income_limit", period) + # Section VI.A.6.c.i: income "must be at or below" the limit, so a + # household exactly at the limit qualifies. + return income <= limit diff --git a/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_limit.py b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_limit.py new file mode 100644 index 00000000000..f4921f81086 --- /dev/null +++ b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_limit.py @@ -0,0 +1,74 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.hhs.tax_unit_fpg import fpg + + +class az_liheap_income_limit(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Arizona LIHEAP annual income limit" + documentation = ( + "Twelve times the Appendix A monthly gross income limit for the " + "eligible household size: the greater of 60% of the state median income " + "and 150% of the federal poverty guideline, each floored to whole " + "monthly dollars." + ) + reference = ( + # Section VI.A.6.c.i (page 8) and Appendix A (page 37). + "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=37", + # Section 2.1 (page 9). + "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=9", + "https://liheapch.acf.gov/profiles/povertytables/FY2026/azsmi.htm", + "https://liheapch.acf.gov/profiles/povertytables/FY2026/popstate.htm", + ) + defined_for = StateCode.AZ + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.az.des.liheap.eligibility + size = spm_unit("az_liheap_household_size", period) + state = spm_unit.household("state_code_str", period) + state_group = spm_unit.household("state_group_str", period) + # Section VI.A.6.c.i: "Gross Income must be at or below the greater of: + # A) 60% of the SMI; or B) 150% of the FPL." Appendix A prints the + # FFY2026 monthly result for sizes 1 to 12 plus $687 per additional + # person. Its SMI arm reproduces only when the four-person 60% amount is + # floored first (64,789 from 107,983), then the size-adjusted annual + # amount, then the monthly amount; this is a reconciled numerical + # pattern, not an explicit rounding rule. + smi = parameters(period).gov.hhs.smi + four_person_limit = np.floor(smi.amount[state] * p.smi_rate) + adjustment = smi.household_size_adjustment + threshold = smi.additional_person_threshold + size_share = ( + adjustment.first_person + + adjustment.second_to_sixth_person * clip(size - 1, 0, threshold - 1) + + adjustment.additional_person * max_(size - threshold, 0) + ) + smi_monthly = np.floor( + np.floor(four_person_limit * size_share) / MONTHS_IN_YEAR + ) + # The FPL arm uses the guideline in force when the season's limits were + # set (the 2025 guideline for FFY2026) and floors the monthly amount. + # Appendix A's "EACH ADDL OVER 12 +$687" is the floored monthly value of + # 150% of the additional-person guideline, applied beyond the table. + capped_size = clip(size, 1, p.max_table_size) + guideline = fpg( + capped_size, state_group, period, parameters, year_lag=p.fpg_year_lag + ) + additional_guideline = ( + fpg( + capped_size + 1, + state_group, + period, + parameters, + year_lag=p.fpg_year_lag, + ) + - guideline + ) + fpg_monthly = np.floor(p.fpg_rate * guideline / MONTHS_IN_YEAR) + increment = np.floor(p.fpg_rate * additional_guideline / MONTHS_IN_YEAR) + additional_people = max_(size - p.max_table_size, 0) + fpg_monthly = fpg_monthly + additional_people * increment + monthly_limit = max_(smi_monthly, fpg_monthly) + return where(size > 0, monthly_limit * MONTHS_IN_YEAR, 0) diff --git a/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_earned_income.py b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_earned_income.py new file mode 100644 index 00000000000..ce4a6e72f99 --- /dev/null +++ b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_earned_income.py @@ -0,0 +1,39 @@ +from policyengine_us.model_api import * + + +class az_liheap_countable_earned_income(Variable): + value_type = float + entity = Person + definition_period = YEAR + label = "Arizona LIHEAP countable earned income" + documentation = ( + "Gross wages and reported net self-employment income of household " + "members aged 18 and older. Each source is floored at zero, so a " + "business loss does not offset other earnings. Earned income of members " + "under 18 never counts." + ) + unit = USD + reference = ( + # Sections VI.A.6.d.ii and VI.A.6.e.ii.A and E (pages 8-9) and Section + # VII.A.1.c.ii.A-B (page 13). + "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8", + # Section 1.9 (pages 6-7). + "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=6", + ) + defined_for = StateCode.AZ + + def formula(person, period, parameters): + p = parameters(period).gov.states.az.des.liheap.income + # Section VII.A.1.c.ii.B verifies "Profit or loss documentation from + # self-employment" without saying whether a loss offsets other income, + # so each net source is floored at zero (project convention). + earned = 0 + for source in p.sources.earned: + earned = earned + max_(person(source, period), 0) + # Section VI.A.6.d.ii counts "Income from all Household members aged 18 + # and older"; Section VI.A.6.e.ii.E excludes income of members under 18 + # in revision 13 and their earned income in revision 15, and the plan's + # Section 1.9 leaves "Earned income of a child under the age of 18" + # unchecked, so under-18 earnings never count. + age = person("age", period) + return where(age >= p.adult_age, earned, 0) diff --git a/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_income.py b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_income.py new file mode 100644 index 00000000000..83167e13e4c --- /dev/null +++ b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_income.py @@ -0,0 +1,39 @@ +from policyengine_us.model_api import * + + +class az_liheap_countable_income(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + label = "Arizona LIHEAP countable income" + documentation = ( + "Annual gross household income counted under Arizona LIHEAP, including " + "the income of members who are not citizens or qualified noncitizens. " + "Annual amounts stand for twelve times the policy's 30-day income " + "snapshot." + ) + unit = USD + reference = ( + # Gross Income definition (page 4), Section VI.A.6.e (pages 8-9), + # Section VII.A.1.c.v (page 14) and Section VII.B.1.c (page 31). + "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=14", + # Sections 1.8 and 1.9 (pages 5-7). + "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=6", + ) + defined_for = StateCode.AZ + + # Section VII.A.1.c.v annualizes the 30-day snapshot as "([Average Gross + # Income per Pay Period X Income Frequency] / 12)", so annual income over + # twelve is the policy's own arithmetic. Cash Assistance is a household + # benefit with no person-level attribution, so it is summed here rather + # than in the person-level unearned source list: Section VI.A.6.e.ii.B + # counts "SSA and TANF benefits", and Section VII.B.1.c.ii adds "the Cash + # Assistance benefit" to the reported income of households categorically + # eligible through it. The policy's narrower base for categorically + # eligible households (the enrolled member's reported income) is not + # modeled; the whole household's countable income is used. + adds = [ + "az_liheap_countable_earned_income", + "az_liheap_countable_unearned_income", + "az_tanf", + ] diff --git a/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.py b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.py new file mode 100644 index 00000000000..71ea100dc6d --- /dev/null +++ b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.py @@ -0,0 +1,37 @@ +from policyengine_us.model_api import * + + +class az_liheap_countable_unearned_income(Variable): + value_type = float + entity = Person + definition_period = YEAR + label = "Arizona LIHEAP countable unearned income" + documentation = ( + "Gross unearned income of a household member: counted in full for " + "members aged 18 and older, and for younger members only from policy " + "revision 15 (March 23, 2026). Social Security is counted gross; the " + "policy's Medicare premium subtraction is not applied. Cash Assistance " + "is a household benefit added in az_liheap_countable_income." + ) + unit = USD + reference = ( + # Sections VI.A.6.d.ii and VI.A.6.e.ii.B-E (pages 8-9) and Section + # VII.A.1.c.ii.C-F (pages 13-14). + "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8", + # Section 1.9 (pages 6-7). + "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=6", + ) + defined_for = StateCode.AZ + + def formula(person, period, parameters): + p = parameters(period).gov.states.az.des.liheap.income + # add() sums the MONTH-defined ssi over the year with the YEAR sources. + unearned = add(person, period, p.sources.unearned) + age = person("age", period) + # Revision 13, Section VI.A.6.e.ii.E: "Income from Household members + # under the age of 18 is not considered Countable Income." Revision 15 + # narrows this to "Earned income from Household members under the age + # of 18", so a child's SSI, survivor benefits or child support count + # from March 23, 2026. + counted = (age >= p.adult_age) | p.child_unearned_income_counted + return where(counted, unearned, 0) diff --git a/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_energy_burden_points.py b/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_energy_burden_points.py new file mode 100644 index 00000000000..de71374ab61 --- /dev/null +++ b/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_energy_burden_points.py @@ -0,0 +1,56 @@ +from policyengine_us.model_api import * + + +class az_liheap_energy_burden_points(Variable): + value_type = int + entity = SPMUnit + definition_period = YEAR + label = "Arizona LIHEAP energy burden points" + documentation = ( + "Benefit matrix points from energy cost as a share of countable income " + "(5% or less gives 0 points, then one point per five-percent band up to " + "6 points at 31% or over), plus 2 points for portable fuel heating " + "(propane, wood or coal). A positive cost with no income takes the top " + "band; no cost gives no burden points." + ) + reference = ( + "https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1", + "https://des.az.gov/liheap", + # Portable Fuel definition (page 5) and Section VI.B.1 (page 10). + "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=5", + ) + defined_for = StateCode.AZ + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.az.des.liheap.points + cost = spm_unit("az_liheap_energy_cost", period) + income = spm_unit("az_liheap_countable_income", period) + # Worksheet: "Total Monthly Cost ÷ 30 Days Gross Income = % Energy + # Burden"; annual over annual gives the same ratio. The worksheet does + # not address zero income: a positive cost with no income is an + # unbounded burden and takes the top band, and no cost is no burden. + # The quotient is formed in float64 so that an exact band top such as + # 2,400 / 24,000 = 0.10 stays in its band. + burden = np.divide( + cost.astype(np.float64), + income, + out=np.full(cost.shape, np.inf), + where=income > 0, + ) + burden = where(cost > 0, burden, 0) + # "5% or less" is 0 points and the DES example places 17.5% in the + # "16% - 20%" band, so right=True with the -inf first threshold. + burden_points = p.energy_burden.calc(burden, right=True) + # Worksheet: "Note: Add 2 points for portable fuels". "Portable Fuel: + # Energy that is derived from propane, wood, coal, fuel for gas + # generators, and/or pellets" (page 5); generator fuel and pellets have + # no heating type of their own, and fuel oil and kerosene are not + # portable fuels under this definition. + heating_type = spm_unit("heating_type", period) + types = heating_type.possible_values + portable_fuel = ( + (heating_type == types.PROPANE) + | (heating_type == types.WOOD) + | (heating_type == types.COAL) + ) + return burden_points + where(portable_fuel, p.portable_fuel, 0) diff --git a/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_energy_need_points.py b/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_energy_need_points.py new file mode 100644 index 00000000000..755971ecc09 --- /dev/null +++ b/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_energy_need_points.py @@ -0,0 +1,51 @@ +from policyengine_us.model_api import * + + +class az_liheap_energy_need_points(Variable): + value_type = int + entity = SPMUnit + definition_period = YEAR + label = "Arizona LIHEAP energy need points" + documentation = ( + "One benefit matrix point for each category present in the household, " + "at most one per category: a member aged 60 or over, a member with a " + "disability (including SSI or Social Security disability recipients), a " + "veteran, and a child aged 6 or under." + ) + reference = ( + "https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1", + # Disability definition (page 4) and Section VI.B.1.d (page 10). + "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=10", + # Section 2.4 (page 10). + "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=10", + ) + defined_for = StateCode.AZ + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.az.des.liheap.points + person = spm_unit.members + age = person("age", period) + # Section VI.B.1.d: whether any household members are "Aged six (6) and + # under; Aged 60 and over; A veteran; or A person with a disability." + # Worksheet: "(Only one point per category allowed)". + elderly = spm_unit.any(age >= p.elderly_age) + young_child = spm_unit.any(age < p.child_age_limit) + veteran = spm_unit.any(person("is_veteran", period)) + # "Disability: A physical or mental impairment that substantially limits + # at least one major life activity ... For the purposes of this Policy, + # a recipient of benefits under Title II or XVI of the Social Security + # Act, or other State-administered disability benefits, is considered + # to categorically meet this definition" (page 4). SSI is MONTH-defined, + # so add() sums it over the year. + disabled = spm_unit.any( + person("is_disabled", period) + | (add(person, period, ["ssi"]) > 0) + | (person("social_security_disability", period) > 0) + ) + point = p.vulnerable_member + return ( + where(elderly, point, 0) + + where(disabled, point, 0) + + where(veteran, point, 0) + + where(young_child, point, 0) + ) diff --git a/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_income_points.py b/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_income_points.py new file mode 100644 index 00000000000..82806b75996 --- /dev/null +++ b/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_income_points.py @@ -0,0 +1,41 @@ +from policyengine_us.model_api import * + + +class az_liheap_income_points(Variable): + value_type = int + entity = SPMUnit + definition_period = YEAR + label = "Arizona LIHEAP income points" + documentation = ( + "Benefit matrix points from countable income as a share of the income " + "limit for the household size: 0% to 20% gives 5 points, 21% to 40% 4, " + "41% to 60% 3, 61% to 80% 2, 81% to 100% 1, and above 100% (reachable " + "only through categorical eligibility) 0." + ) + reference = ( + "https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1", + "https://des.az.gov/liheap", + # Section VI.B.1 (page 10). + "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=10", + ) + defined_for = StateCode.AZ + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.az.des.liheap.points + income = spm_unit("az_liheap_countable_income", period) + limit = spm_unit("az_liheap_income_limit", period) + # DES: "Total Monthly Gross Income Divided by the Monthly Gross Income + # Limit for the Household Size = % Income"; annual over annual gives the + # same ratio. A zero limit (no eligible member) is treated as a ratio + # above every band, scoring 0 points. The quotient is formed in float64 + # so that an exact band top such as 10,884 / 54,420 = 0.20 stays in its + # band instead of rounding above the threshold. + ratio = np.divide( + income.astype(np.float64), + limit, + out=np.full(income.shape, np.inf), + where=limit > 0, + ) + # Band tops: "0% - 20%" is 5 points and the DES example places 44% in + # the 41% to 60% band, so right=True with the -inf first threshold. + return p.income_ratio.calc(ratio, right=True) diff --git a/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_points.py b/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_points.py new file mode 100644 index 00000000000..c236e59b030 --- /dev/null +++ b/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_points.py @@ -0,0 +1,21 @@ +from policyengine_us.model_api import * + + +class az_liheap_points(Variable): + value_type = int + entity = SPMUnit + definition_period = YEAR + label = "Arizona LIHEAP total benefit matrix points" + documentation = "Worksheet total of income points, energy burden points (including portable fuel points) and energy need points; at most 17." + reference = ( + "https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1", + "https://des.az.gov/liheap", + ) + defined_for = StateCode.AZ + + # Worksheet Section 4: "Total All Points: (a+b+c+d+e+f)". + adds = [ + "az_liheap_income_points", + "az_liheap_energy_burden_points", + "az_liheap_energy_need_points", + ] From 42aa03b394d5e727d1a67c44f74eb33d3f36abf7 Mon Sep 17 00:00:00 2001 From: Ziming Date: Mon, 19 Oct 2026 17:10:25 -0400 Subject: [PATCH 2/9] Use the shared HHS SMI helper for the Arizona income limit liheap_smi_limit (PR 10019) floors the four-person 60% amount and then the size-adjusted amount, the order Appendix A requires, so the formula keeps only the monthly floor and the 150% FPG arm. Co-Authored-By: Claude Fable 5.1 --- .../eligibility/az_liheap_income_limit.py | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_limit.py b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_limit.py index f4921f81086..feb609cfce5 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_limit.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_limit.py @@ -1,4 +1,5 @@ from policyengine_us.model_api import * +from policyengine_us.variables.gov.hhs.hhs_smi import liheap_smi_limit from policyengine_us.variables.gov.hhs.tax_unit_fpg import fpg @@ -32,22 +33,13 @@ def formula(spm_unit, period, parameters): # Section VI.A.6.c.i: "Gross Income must be at or below the greater of: # A) 60% of the SMI; or B) 150% of the FPL." Appendix A prints the # FFY2026 monthly result for sizes 1 to 12 plus $687 per additional - # person. Its SMI arm reproduces only when the four-person 60% amount is - # floored first (64,789 from 107,983), then the size-adjusted annual - # amount, then the monthly amount; this is a reconciled numerical - # pattern, not an explicit rounding rule. - smi = parameters(period).gov.hhs.smi - four_person_limit = np.floor(smi.amount[state] * p.smi_rate) - adjustment = smi.household_size_adjustment - threshold = smi.additional_person_threshold - size_share = ( - adjustment.first_person - + adjustment.second_to_sixth_person * clip(size - 1, 0, threshold - 1) - + adjustment.additional_person * max_(size - threshold, 0) - ) - smi_monthly = np.floor( - np.floor(four_person_limit * size_share) / MONTHS_IN_YEAR - ) + # person. Its SMI arm reproduces only with the shared HHS floor order, + # the four-person 60% amount first (64,789 from 107,983) and then the + # size-adjusted annual amount, followed by a floor of the monthly + # amount; this is a reconciled numerical pattern, not an explicit + # rounding rule. + smi_annual = liheap_smi_limit(size, state, p.smi_rate, period, parameters) + smi_monthly = np.floor(smi_annual / MONTHS_IN_YEAR) # The FPL arm uses the guideline in force when the season's limits were # set (the 2025 guideline for FFY2026) and floors the monthly amount. # Appendix A's "EACH ADDL OVER 12 +$687" is the floored monthly value of From 34b25423ff8d05fef6790f4261ec19b5ba3c0def Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 20 Oct 2026 01:09:41 -0400 Subject: [PATCH 3/9] Fix Arizona LIHEAP review findings - Count railroad retirement, employment disability benefits and workers' compensation as unearned income. - Disability need point: SSI counts only when paid on the basis of blindness or disability. - Label the revision 15 child unearned income rule as an inference. - References follow the repo convention for PDF pages; the $640 standard payment is on page 30. - Tests: new income source, railroad and SSI need-point cases (117 total). Co-Authored-By: Claude Opus 5.5 --- .../eligibility/categorical_programs.yaml | 6 +- .../az/des/liheap/eligibility/fpg_rate.yaml | 4 +- .../des/liheap/eligibility/fpg_year_lag.yaml | 2 +- .../liheap/eligibility/max_table_size.yaml | 2 +- .../az/des/liheap/eligibility/smi_rate.yaml | 4 +- .../az/des/liheap/income/adult_age.yaml | 2 +- .../income/child_unearned_income_counted.yaml | 9 +- .../az/des/liheap/income/sources/earned.yaml | 4 +- .../des/liheap/income/sources/unearned.yaml | 26 ++++-- .../states/az/des/liheap/payment/amount.yaml | 6 +- .../az/des/liheap/points/child_age_limit.yaml | 4 +- .../az/des/liheap/points/elderly_age.yaml | 4 +- .../az/des/liheap/points/portable_fuel.yaml | 2 +- .../des/liheap/points/vulnerable_member.yaml | 4 +- .../gov/states/az/des/liheap/az_liheap.yaml | 32 ++++++++ .../az_liheap_countable_unearned_income.yaml | 82 ++++++++++++++++++- .../points/az_liheap_energy_need_points.yaml | 61 +++++++++++++- .../gov/states/az/des/liheap/az_liheap.py | 9 +- .../az/des/liheap/az_liheap_energy_cost.py | 2 +- .../az/des/liheap/az_liheap_household_size.py | 5 +- .../az_liheap_categorically_eligible.py | 6 +- .../liheap/eligibility/az_liheap_eligible.py | 6 +- .../eligibility/az_liheap_income_eligible.py | 3 +- .../eligibility/az_liheap_income_limit.py | 5 +- .../az_liheap_countable_earned_income.py | 5 +- .../income/az_liheap_countable_income.py | 9 +- .../az_liheap_countable_unearned_income.py | 12 +-- .../points/az_liheap_energy_burden_points.py | 2 +- .../points/az_liheap_energy_need_points.py | 23 ++++-- .../liheap/points/az_liheap_income_points.py | 1 - 30 files changed, 258 insertions(+), 84 deletions(-) diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/categorical_programs.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/categorical_programs.yaml index f10575f4586..9efe7f0b219 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/categorical_programs.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/categorical_programs.yaml @@ -14,7 +14,7 @@ metadata: period: year label: Arizona LIHEAP categorical eligibility programs reference: - - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Categorical Eligibility definition (pages 3-4) and Section VI.A.6.a-b, page 7 - href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=7 - - title: Arizona LIHEAP FY2026 State Plan, Sections 1.4 and 1.4a (TANF and SNAP yes; SSI and means-tested veterans programs no), page 5 + - title: "Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Categorical Eligibility definition and Section VI.A.6.a-b #page 3-4,7" + href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=3 + - title: Arizona LIHEAP FY2026 State Plan, Sections 1.4 and 1.4a (TANF and SNAP yes; SSI and means-tested veterans programs no) href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=5 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/fpg_rate.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/fpg_rate.yaml index 70a1ae18f7e..71f1513da5e 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/fpg_rate.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/fpg_rate.yaml @@ -7,7 +7,7 @@ metadata: period: year label: Arizona LIHEAP federal poverty guideline rate reference: - - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.A.6.c.i (gross income at or below the greater of 60% of the SMI or 150% of the FPL), page 8 + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.A.6.c.i (gross income at or below the greater of 60% of the SMI or 150% of the FPL) href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8 - - title: Arizona LIHEAP FY2026 State Plan, Section 2.1 (household sizes 10 and over at 150.00% of the HHS Poverty Guidelines), page 9 + - title: Arizona LIHEAP FY2026 State Plan, Section 2.1 (household sizes 10 and over at 150.00% of the HHS Poverty Guidelines) href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=9 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/fpg_year_lag.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/fpg_year_lag.yaml index 9c75a61c7e7..65fda5a4d87 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/fpg_year_lag.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/fpg_year_lag.yaml @@ -11,7 +11,7 @@ metadata: period: year label: Arizona LIHEAP federal poverty guideline year lag reference: - - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Appendix A Income Eligibility by Household Size, page 37 + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Appendix A Income Eligibility by Household Size href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=37 - title: LIHEAP Clearinghouse, FFY2026 poverty guidelines by household size (2025 HHS guideline) href: https://liheapch.acf.gov/profiles/povertytables/FY2026/popstate.htm diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/max_table_size.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/max_table_size.yaml index edc42593110..15f6e6e7dee 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/max_table_size.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/max_table_size.yaml @@ -7,5 +7,5 @@ metadata: period: year label: Arizona LIHEAP maximum tabulated household size reference: - - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Appendix A Income Eligibility by Household Size (sizes 1 to 12 and "EACH ADDL OVER 12 +$687"), page 37 + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Appendix A Income Eligibility by Household Size (sizes 1 to 12 and "EACH ADDL OVER 12 +$687") href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=37 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/smi_rate.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/smi_rate.yaml index e3e9b3dac52..0e0f56490b1 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/smi_rate.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/smi_rate.yaml @@ -7,9 +7,9 @@ metadata: period: year label: Arizona LIHEAP state median income rate reference: - - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.A.6.c.i (gross income at or below the greater of 60% of the SMI or 150% of the FPL), page 8 + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.A.6.c.i (gross income at or below the greater of 60% of the SMI or 150% of the FPL) href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8 - - title: Arizona LIHEAP FY2026 State Plan, Section 2.1 (household sizes 1 to 9 at 60.00% of State Median Income), page 9 + - title: Arizona LIHEAP FY2026 State Plan, Section 2.1 (household sizes 1 to 9 at 60.00% of State Median Income) href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=9 - title: LIHEAP Clearinghouse, Arizona 60% of State Median Income, FFY2026 (four-person amount $64,789) href: https://liheapch.acf.gov/profiles/povertytables/FY2026/azsmi.htm diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/income/adult_age.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/income/adult_age.yaml index 8792c67a2c0..86740790728 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/income/adult_age.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/income/adult_age.yaml @@ -7,5 +7,5 @@ metadata: period: year label: Arizona LIHEAP adult age reference: - - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.A.6.d.ii (income from all household members aged 18 and older, page 8) and Section VII.A.1.c.ii (page 13) + - title: "Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.A.6.d.ii (income from all household members aged 18 and older) and Section VII.A.1.c.ii #page 8,13" href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/income/child_unearned_income_counted.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/income/child_unearned_income_counted.yaml index 237cb5c1212..5352950aae1 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/income/child_unearned_income_counted.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/income/child_unearned_income_counted.yaml @@ -2,9 +2,12 @@ description: Arizona counts the unearned income of household members under the a # Revision 13, Section VI.A.6.e.ii.E: "Income from Household members under the # age of 18 is not considered Countable Income." Revision 15 (effective March # 23, 2026) rewrote the sentence to "Earned income from Household members under -# the age of 18 is not considered Countable Income", so a child's SSI, survivor -# benefits or child support count from that date. Earned income of members +# the age of 18 is not considered Countable Income". Earned income of members # under 18 is excluded under both revisions. +# NOTE: Counting a child's unearned income (SSI, survivor benefits, child +# support) from March 23, 2026 is an inference from that narrowed exclusion. +# Revision 15 does not say so directly, and its Section VI.A.6.d.ii still says +# that the income of members aged 18 and older is household income. values: 2025-08-25: false 2026-03-23: true @@ -14,5 +17,5 @@ metadata: period: year label: Arizona LIHEAP counts unearned income of members under 18 reference: - - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025) and revision 15 (effective March 23, 2026), Section VI.A.6.e.ii.E, page 9 + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025) and revision 15 (effective March 23, 2026), Section VI.A.6.e.ii.E href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=9 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/earned.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/earned.yaml index f3d18cb76b6..9b0a24fbce1 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/earned.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/earned.yaml @@ -20,7 +20,7 @@ metadata: period: year label: Arizona LIHEAP countable earned income sources reference: - - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.A.6.e.ii.A (page 8) and Section VII.A.1.c.ii.A-B (page 13) + - title: "Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.A.6.e.ii.A and Section VII.A.1.c.ii.A-B #page 8,13" href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8 - - title: Arizona LIHEAP FY2026 State Plan, Section 1.9 (wages, self-employment income, contract income, commissions, WIA employment income and work study counted), pages 6-7 + - title: "Arizona LIHEAP FY2026 State Plan, Section 1.9 (wages, self-employment income, contract income, commissions, WIA employment income and work study counted) #page 6-7" href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=6 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml index 80fd101d34a..e2f536b84b6 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml @@ -17,6 +17,11 @@ values: # retirement_distributions already sums taxable and tax-exempt IRA, 401(k), # SEP, 403(b) and Keogh distributions. - retirement_distributions + # Railroad Retirement annuities are "retirement income" (Section + # VI.A.6.e.ii.B, page 8) and "retirement benefits" (Section + # VII.A.1.c.ii.D, page 14); plan Section 1.9 checks "Retirement / pension + # benefits" (page 6). + - railroad_benefits - veterans_benefits - alimony_income - child_support_received @@ -24,16 +29,25 @@ values: - interest_income - ordinary_dividend_income - general_assistance - # Cash gifts over $50, jury duty, legal settlements, insurance payments to - # the insured, mortgage or sales-contract payments, royalties and foster or - # adoption care payments have no model input. + # Employment disability insurance: plan Section 1.9 checks "Insurance + # payments made directly to the insured" (page 6), and Gross Income is + # "The total Household income for all Household members before taxes and + # other deductions" (page 4). + - disability_benefits + # Plan Section 1.9 neither lists nor excludes workers' compensation; the + # Gross Income definition (page 4) and Section VI.A.6.e.ii.D "Consistent + # income received from other sources" (page 8) support counting it. + - workers_compensation + # Cash gifts over $50, jury duty, legal settlements, other insurance + # payments to the insured, mortgage or sales-contract payments, royalties + # and foster or adoption care payments have no model input. metadata: unit: list period: year label: Arizona LIHEAP countable unearned income sources reference: - - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.A.6.e.ii.B-D (page 8) and Section VII.A.1.c.ii.C-F (pages 13-14) - href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8 - - title: Arizona LIHEAP FY2026 State Plan, Section 1.9 (unemployment insurance, strike pay, SSA benefits, SSI, retirement and pension, general assistance, TANF, rental income, alimony, child support, interest, dividends or royalties and VA benefits counted), pages 6-7 + - title: "Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Gross Income definition, Section VI.A.6.e.ii.B-D and Section VII.A.1.c.ii.C-F #page 4,8,13-14" + href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=4 + - title: "Arizona LIHEAP FY2026 State Plan, Section 1.9 (unemployment insurance, strike pay, SSA benefits, SSI, retirement and pension, general assistance, TANF, rental income, alimony, child support, interest, dividends or royalties, insurance payments made directly to the insured and VA benefits counted) #page 6-7" href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=6 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/payment/amount.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/payment/amount.yaml index 8f946c61a8a..7c51826eceb 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/payment/amount.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/payment/amount.yaml @@ -35,7 +35,7 @@ metadata: href: https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1 - title: Arizona DES, Low Income Home Energy Assistance Program page (benefit by points table) href: https://des.az.gov/liheap - - title: Arizona LIHEAP FY2026 State Plan, Section 2.6 (minimum benefit $160, maximum benefit $640), page 10 + - title: Arizona LIHEAP FY2026 State Plan, Section 2.6 (minimum benefit $160, maximum benefit $640) href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=10 - - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VII.B.1.a (up to $640 for a standard payment, page 29) and Section VII.B.4.b (page 34) - href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=29 + - title: "Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VII.B.1.a (up to $640 for a standard payment) and Section VII.B.4.b #page 30,34" + href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=30 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/points/child_age_limit.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/points/child_age_limit.yaml index fc9ca843260..0397ba2aa9d 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/points/child_age_limit.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/points/child_age_limit.yaml @@ -11,7 +11,7 @@ metadata: reference: - title: Arizona LIHEAP Eligibility Worksheet, $640 Limit (effective October 12, 2024), Section 3 Energy Need (Child age 6 or younger, 1 point) href: https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1 - - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.B.1.d.i (household members "Aged six (6) and under"), page 10 + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.B.1.d.i (household members "Aged six (6) and under") href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=10 - - title: Arizona LIHEAP FY2026 State Plan, Section 2.4 (additional points for children 6 years old and under), page 10 + - title: Arizona LIHEAP FY2026 State Plan, Section 2.4 (additional points for children 6 years old and under) href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=10 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/points/elderly_age.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/points/elderly_age.yaml index 1370487b32d..9d82451373f 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/points/elderly_age.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/points/elderly_age.yaml @@ -7,9 +7,9 @@ metadata: period: year label: Arizona LIHEAP elderly age reference: - - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.B.1.d.ii (household members "Aged 60 and over"), page 10 + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.B.1.d.ii (household members "Aged 60 and over") href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=10 - - title: Arizona LIHEAP FY2026 State Plan, Section 2.4 (additional points for the elderly, 60+ years old), page 10 + - title: Arizona LIHEAP FY2026 State Plan, Section 2.4 (additional points for the elderly, 60+ years old) href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=10 - title: Arizona LIHEAP Eligibility Worksheet, $640 Limit (effective October 12, 2024), Section 3 Energy Need (Elderly, 1 point) href: https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/points/portable_fuel.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/points/portable_fuel.yaml index 3a8b9e92c1b..cb2ba63fd63 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/points/portable_fuel.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/points/portable_fuel.yaml @@ -11,5 +11,5 @@ metadata: href: https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1 - title: Arizona DES, Low Income Home Energy Assistance Program page ("2 points are added for households that utilize deliverable/portal fuel") href: https://des.az.gov/liheap - - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Portable Fuel definition (propane, wood, coal, fuel for gas generators and pellets), page 5 + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Portable Fuel definition (propane, wood, coal, fuel for gas generators and pellets) href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=5 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/points/vulnerable_member.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/points/vulnerable_member.yaml index 3861058b749..dd6dc57177d 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/points/vulnerable_member.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/points/vulnerable_member.yaml @@ -11,7 +11,7 @@ metadata: reference: - title: Arizona LIHEAP Eligibility Worksheet, $640 Limit (effective October 12, 2024), Section 3 Energy Need href: https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1 - - title: Arizona LIHEAP FY2026 State Plan, Section 2.4 ("A household may receive one point for each category, if eligible"), page 10 + - title: Arizona LIHEAP FY2026 State Plan, Section 2.4 ("A household may receive one point for each category, if eligible") href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=10 - - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.B.1.d (benefit factors), page 10 + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.B.1.d (benefit factors) href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=10 diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap.yaml index 42281674472..5e401ca5ebe 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap.yaml @@ -350,3 +350,35 @@ state_code: NM output: az_liheap: 0 + +- name: Case 14, a railroad retiree's annuity puts a single person over the limit and pays nothing. + period: 2026 + input: + people: + person1: + age: 70 + railroad_benefits: 40_000 + ssi: 0 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_200 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # Railroad Retirement is "retirement income" (policy p. 8 VI.A.6.e.ii.B): + # countable income 40,000. + az_liheap_countable_income: 40_000 + # Appendix A size 1: 2,807 x 12 = 33,684. + az_liheap_income_limit: 33_684 + # 40,000 > 33,684. + az_liheap_income_eligible: false + # No SNAP or Cash Assistance. + az_liheap_categorically_eligible: false + az_liheap_eligible: false + az_liheap: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.yaml index dd1dc4a3d4b..239a8e2a196 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.yaml @@ -1,8 +1,9 @@ # Arizona LIHEAP countable unearned income per person. Gross unearned income -# of every member aged 18 or older counts: SSA, SSI, retirement, unemployment -# insurance, alimony, child support, rental income, interest, dividends, -# general assistance and VA benefits (DES LIHEAP Policy rev. 13 eff. -# 2025-08-25, pp. 8-9 VI.A.6.e, +# of every member aged 18 or older counts: SSA, SSI, retirement (including +# Railroad Retirement), unemployment insurance, alimony, child support, rental +# income, interest, dividends, general assistance, VA benefits, employment +# disability insurance and workers' compensation (DES LIHEAP Policy rev. 13 +# eff. 2025-08-25, pp. 8-9 VI.A.6.e, # https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8; # FY2026 plan 1.9, # https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=6). @@ -161,3 +162,76 @@ az_liheap_countable_unearned_income: [0, 16_728] # 30,000 + 16,728 = 46,728. az_liheap_countable_income: 46_728 + +- name: Case 7, a Railroad Retirement annuity is counted as retirement income. + period: 2026 + input: + people: + person1: + age: 68 + railroad_benefits: 30_000 + ssi: 0 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_500 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # Policy p. 8 VI.A.6.e.ii.B "retirement income", p. 14 "retirement + # benefits"; plan 1.9 "Retirement / pension benefits" checked: 30,000. + az_liheap_countable_unearned_income: 30_000 + az_liheap_countable_income: 30_000 + +- name: Case 8, long-term disability insurance paid to the insured worker is counted. + period: 2026 + input: + people: + person1: + age: 52 + disability_benefits: 27_000 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + pre_subsidy_electricity_expense: 1_400 + gas_expense: 500 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # Plan 1.9 "Insurance payments made directly to the insured" checked; + # policy p. 4 Gross Income is all income before taxes and deductions. + # 60% of a 45,000 salary = 27,000 counts. + az_liheap_countable_unearned_income: 27_000 + az_liheap_countable_income: 27_000 + +- name: Case 9, workers' compensation of an injured worker is counted. + period: 2026 + input: + people: + person1: + age: 38 + workers_compensation: 24_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_700 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # Policy p. 4 Gross Income and p. 8 VI.A.6.e.ii.D "Consistent income + # received from other sources"; two-thirds of 36,000 prior wages = 24,000 + # counts. + az_liheap_countable_unearned_income: 24_000 + az_liheap_countable_income: 24_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/points/az_liheap_energy_need_points.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/points/az_liheap_energy_need_points.yaml index 7f140079e88..99222965c58 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/points/az_liheap_energy_need_points.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/points/az_liheap_energy_need_points.yaml @@ -7,7 +7,9 @@ # and over; A veteran; A person with a disability", # https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=10). # Disability (policy p. 4): an ADA impairment, or categorically a recipient of -# Title II (SSDI) or Title XVI (SSI) benefits. 2026 SSI rate 994 x 12 = +# Title II or XVI benefits "or other State-administered disability benefits", +# read as disability benefits: SSDI, or SSI paid on the basis of blindness or +# disability (aged-only SSI does not qualify). 2026 SSI rate 994 x 12 = # 11,928. Base household: a 40-year-old earning 30,000 with electric heat. - name: Case 1, a working-age adult with no need factor has 0 points. @@ -97,13 +99,14 @@ output: az_liheap_energy_need_points: 1 -- name: Case 5, an SSI recipient is categorically disabled and adds 1 point. +- name: Case 5, an SSI recipient paid on the basis of disability is categorically disabled and adds 1 point. period: 2026 input: people: person1: age: 40 is_disabled: false + is_ssi_disabled: true ssi: 11_928 spm_units: spm_unit: @@ -117,7 +120,8 @@ members: [person1] state_code: AZ output: - # Policy p. 4: a Title XVI recipient meets the disability definition. + # Policy p. 4: a recipient of Title XVI disability benefits meets the + # disability definition: elderly 0 + disabled 1 = 1. az_liheap_energy_need_points: 1 - name: Case 6, an SSDI recipient is categorically disabled and adds 1 point. @@ -325,3 +329,54 @@ output: # Points attach to categories, not persons: elderly + disabled + veteran. az_liheap_energy_need_points: 3 + +- name: Case 14, an aged SSI recipient without a disability gets the elderly point only. + period: 2026 + input: + people: + person1: + age: 70 + is_disabled: false + is_blind: false + ssi: 11_928 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_600 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # Aged 70 >= 60: elderly 1. SSI paid on the basis of age alone is not a + # disability benefit under policy p. 4, and there is no impairment or + # SSDI: disabled 0. Total 1 + 0 = 1. + az_liheap_energy_need_points: 1 + +- name: Case 15, a blind SSI recipient aged 40 is categorically disabled and adds 1 point. + period: 2026 + input: + people: + person1: + age: 40 + is_disabled: false + is_blind: true + ssi: 11_928 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_600 + snap: 0 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # SSI paid on the basis of blindness is a Title XVI disability benefit + # (policy p. 4): disabled 1; aged 40, so elderly 0. Total 0 + 1 = 1. + az_liheap_energy_need_points: 1 diff --git a/policyengine_us/variables/gov/states/az/des/liheap/az_liheap.py b/policyengine_us/variables/gov/states/az/des/liheap/az_liheap.py index b350fc04386..a4e89f5800b 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/az_liheap.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/az_liheap.py @@ -21,11 +21,10 @@ class az_liheap(Variable): defined_for = "az_liheap_eligible" reference = ( "https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1", - # Section 1.2 (page 4) and Section 2.6 (page 10). - "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=10", - # Sections VI.B.1 (page 10), VII.B.1.a (page 29), VII.B.3.b.iii - # (pages 33-34) and VII.B.4.b (page 34). - "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=29", + # PDF pages 4, 10 + "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=4", + # PDF pages 10, 29-30, 33-34 + "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=10", "https://des.az.gov/liheap", ) diff --git a/policyengine_us/variables/gov/states/az/des/liheap/az_liheap_energy_cost.py b/policyengine_us/variables/gov/states/az/des/liheap/az_liheap_energy_cost.py index 23a3131cb14..46860ed95f0 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/az_liheap_energy_cost.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/az_liheap_energy_cost.py @@ -10,7 +10,7 @@ class az_liheap_energy_cost(Variable): documentation = "Annual electricity, gas and portable fuel bills entering the energy burden, the worksheet's 'Electric + Gas + Portable Fuel = Total Monthly Cost' taken over the year. Water for evaporative cooling, sewer, trash and phone bills are excluded." reference = ( "https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1", - # Energy Utility and Portable Fuel definitions (pages 4-5). + # PDF pages 4-5 "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=4", ) defined_for = StateCode.AZ diff --git a/policyengine_us/variables/gov/states/az/des/liheap/az_liheap_household_size.py b/policyengine_us/variables/gov/states/az/des/liheap/az_liheap_household_size.py index 282c28f4ff3..3e765cb7b4b 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/az_liheap_household_size.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/az_liheap_household_size.py @@ -8,9 +8,8 @@ class az_liheap_household_size(Variable): label = "Arizona LIHEAP household size" documentation = "Number of household members who are U.S. citizens or qualified noncitizens. Other members are not counted in household size, although the income of every adult member counts toward household income." reference = ( - # Eligible Household Member and Mixed-Status Household definitions - # (pages 4-5) and Section VI.A.6.d.i (page 8). - "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8", + # PDF pages 4-5, 8 + "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=4", ) defined_for = StateCode.AZ diff --git a/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_categorically_eligible.py b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_categorically_eligible.py index 86128cd4321..7d963c806a4 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_categorically_eligible.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_categorically_eligible.py @@ -8,10 +8,8 @@ class az_liheap_categorically_eligible(Variable): label = "Arizona LIHEAP categorically eligible" documentation = "A household member receives DES Cash Assistance (TANF) or Nutrition Assistance (SNAP), which satisfies the income requirement; all other eligibility criteria still apply." reference = ( - # Categorical Eligibility definition (pages 3-4) and Section VI.A.6.a-b - # (page 7). - "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=7", - # Sections 1.4 and 1.4a (page 5). + # PDF pages 3-4, 7 + "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=3", "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=5", ) defined_for = StateCode.AZ diff --git a/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_eligible.py b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_eligible.py index 442281d8e63..902b67080ca 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_eligible.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_eligible.py @@ -15,10 +15,8 @@ class az_liheap_eligible(Variable): "have no inputs and are not modeled." ) reference = ( - # Household definition (page 4), Sections VI.A.1-6 (pages 6-8) and - # Section VII.B.3.a.ii.B (page 32). - "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=6", - # Sections 2.1-2.3 (page 9). + # PDF pages 4, 6-8, 32 + "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=4", "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=9", ) defined_for = StateCode.AZ diff --git a/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.py b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.py index 96f1977a4e7..7947707b470 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.py @@ -8,9 +8,8 @@ class az_liheap_income_eligible(Variable): label = "Arizona LIHEAP income eligible" documentation = "Countable household income does not exceed the Appendix A income limit for the eligible household size." reference = ( - # Section VI.A.6.c.i (page 8) and Appendix A (page 37). + # PDF pages 8, 37 "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8", - # Section 2.1 (page 9). "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=9", ) defined_for = StateCode.AZ diff --git a/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_limit.py b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_limit.py index feb609cfce5..7dbd3fae776 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_limit.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_limit.py @@ -16,9 +16,8 @@ class az_liheap_income_limit(Variable): "monthly dollars." ) reference = ( - # Section VI.A.6.c.i (page 8) and Appendix A (page 37). - "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=37", - # Section 2.1 (page 9). + # PDF pages 8, 37 + "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8", "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=9", "https://liheapch.acf.gov/profiles/povertytables/FY2026/azsmi.htm", "https://liheapch.acf.gov/profiles/povertytables/FY2026/popstate.htm", diff --git a/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_earned_income.py b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_earned_income.py index ce4a6e72f99..6e0c7691bd4 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_earned_income.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_earned_income.py @@ -14,10 +14,9 @@ class az_liheap_countable_earned_income(Variable): ) unit = USD reference = ( - # Sections VI.A.6.d.ii and VI.A.6.e.ii.A and E (pages 8-9) and Section - # VII.A.1.c.ii.A-B (page 13). + # PDF pages 8-9, 13 "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8", - # Section 1.9 (pages 6-7). + # PDF pages 6-7 "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=6", ) defined_for = StateCode.AZ diff --git a/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_income.py b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_income.py index 83167e13e4c..c29821170ce 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_income.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_income.py @@ -14,11 +14,10 @@ class az_liheap_countable_income(Variable): ) unit = USD reference = ( - # Gross Income definition (page 4), Section VI.A.6.e (pages 8-9), - # Section VII.A.1.c.v (page 14) and Section VII.B.1.c (page 31). - "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=14", - # Sections 1.8 and 1.9 (pages 5-7). - "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=6", + # PDF pages 4, 8-9, 14, 31 + "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=4", + # PDF pages 5-7 + "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=5", ) defined_for = StateCode.AZ diff --git a/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.py b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.py index 71ea100dc6d..344b48bd14e 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.py @@ -15,10 +15,9 @@ class az_liheap_countable_unearned_income(Variable): ) unit = USD reference = ( - # Sections VI.A.6.d.ii and VI.A.6.e.ii.B-E (pages 8-9) and Section - # VII.A.1.c.ii.C-F (pages 13-14). - "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8", - # Section 1.9 (pages 6-7). + # PDF pages 4, 8-9, 13-14 + "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=4", + # PDF pages 6-7 "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=6", ) defined_for = StateCode.AZ @@ -31,7 +30,8 @@ def formula(person, period, parameters): # Revision 13, Section VI.A.6.e.ii.E: "Income from Household members # under the age of 18 is not considered Countable Income." Revision 15 # narrows this to "Earned income from Household members under the age - # of 18", so a child's SSI, survivor benefits or child support count - # from March 23, 2026. + # of 18". Counting a child's unearned income from March 23, 2026 is an + # inference from that narrowing; Section VI.A.6.d.ii still counts the + # income of members aged 18 and older. counted = (age >= p.adult_age) | p.child_unearned_income_counted return where(counted, unearned, 0) diff --git a/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_energy_burden_points.py b/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_energy_burden_points.py index de71374ab61..d289c3b0d1e 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_energy_burden_points.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_energy_burden_points.py @@ -16,7 +16,7 @@ class az_liheap_energy_burden_points(Variable): reference = ( "https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1", "https://des.az.gov/liheap", - # Portable Fuel definition (page 5) and Section VI.B.1 (page 10). + # PDF pages 5, 10 "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=5", ) defined_for = StateCode.AZ diff --git a/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_energy_need_points.py b/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_energy_need_points.py index 755971ecc09..84a86288a1f 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_energy_need_points.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_energy_need_points.py @@ -9,14 +9,14 @@ class az_liheap_energy_need_points(Variable): documentation = ( "One benefit matrix point for each category present in the household, " "at most one per category: a member aged 60 or over, a member with a " - "disability (including SSI or Social Security disability recipients), a " - "veteran, and a child aged 6 or under." + "disability (including recipients of SSI paid on the basis of blindness " + "or disability and of Social Security disability benefits), a veteran, " + "and a child aged 6 or under." ) reference = ( "https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1", - # Disability definition (page 4) and Section VI.B.1.d (page 10). - "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=10", - # Section 2.4 (page 10). + # PDF pages 4, 10 + "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=4", "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=10", ) defined_for = StateCode.AZ @@ -35,11 +35,18 @@ def formula(spm_unit, period, parameters): # at least one major life activity ... For the purposes of this Policy, # a recipient of benefits under Title II or XVI of the Social Security # Act, or other State-administered disability benefits, is considered - # to categorically meet this definition" (page 4). SSI is MONTH-defined, - # so add() sums it over the year. + # to categorically meet this definition" (page 4). "Other ... + # disability benefits" reads the Title II and XVI benefits as disability + # benefits, so Social Security retirement or survivor benefits and SSI + # paid only on the basis of age do not qualify; Title XVI counts when + # the SSI recipient is blind or SSI-disabled. SSI is MONTH-defined, so + # add() sums it over the year. + receives_ssi = add(person, period, ["ssi"]) > 0 + blind = person("is_blind", period) + ssi_disabled = person("is_ssi_disabled", period) disabled = spm_unit.any( person("is_disabled", period) - | (add(person, period, ["ssi"]) > 0) + | (receives_ssi & (blind | ssi_disabled)) | (person("social_security_disability", period) > 0) ) point = p.vulnerable_member diff --git a/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_income_points.py b/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_income_points.py index 82806b75996..90dde337a25 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_income_points.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_income_points.py @@ -15,7 +15,6 @@ class az_liheap_income_points(Variable): reference = ( "https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1", "https://des.az.gov/liheap", - # Section VI.B.1 (page 10). "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=10", ) defined_for = StateCode.AZ From cc76cdf244cfcb520c9f7a49960f9d762eba4631 Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 20 Oct 2026 10:51:38 -0400 Subject: [PATCH 4/9] Fix Arizona LIHEAP CI failures Read total_unemployment_compensation (main's code-health check rejects the reported-only input), and pin az_tanf to 0 in seven cases whose expected income assumes no Cash Assistance: with wages entered as employment_income, the TANF model sees no earnings and would otherwise pay these households. Co-Authored-By: Claude Opus 5.5 --- .../gov/states/az/des/liheap/income/sources/unearned.yaml | 2 +- .../gov/states/az/des/liheap/az_liheap_household_size.yaml | 1 + .../az/des/liheap/eligibility/az_liheap_income_eligible.yaml | 5 +++++ .../des/liheap/income/az_liheap_countable_earned_income.yaml | 1 + 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml index e2f536b84b6..e14cabfaca8 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml @@ -6,7 +6,7 @@ description: Arizona uses these person-level unearned income sources to determin # (az_tanf) is a household benefit and is added in az_liheap_countable_income. values: 2025-08-25: - - unemployment_compensation + - total_unemployment_compensation - strike_benefits # Section VII.A.1.c.ii.D note: Medicare premiums "should be subtracted when # determining gross income". Social Security is counted gross here; the diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap_household_size.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap_household_size.yaml index b950903fee3..3330249ffde 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap_household_size.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap_household_size.yaml @@ -68,6 +68,7 @@ spm_units: spm_unit: members: [person1, person2, person3, person4] + az_tanf: 0 heating_type: ELECTRICITY pre_subsidy_electricity_expense: 2_700 households: diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.yaml index 23aa476262d..15b8d1eee3b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.yaml @@ -71,6 +71,7 @@ spm_units: spm_unit: members: [person1, person2, person3, person4, person5] + az_tanf: 0 heating_type: NATURAL_GAS pre_subsidy_electricity_expense: 2_400 gas_expense: 700 @@ -134,6 +135,7 @@ spm_units: spm_unit: members: [person1, person2, person3, person4, person5, person6] + az_tanf: 0 heating_type: ELECTRICITY pre_subsidy_electricity_expense: 3_000 households: @@ -176,6 +178,7 @@ spm_units: spm_unit: members: [person1, person2, person3, person4, person5, person6, person7, person8, person9, person10] + az_tanf: 0 heating_type: NATURAL_GAS pre_subsidy_electricity_expense: 3_600 gas_expense: 900 @@ -268,6 +271,7 @@ spm_units: spm_unit: members: [person1, person2, person3, person4, person5, person6, person7, person8, person9, person10, person11, person12, person13] + az_tanf: 0 heating_type: ELECTRICITY pre_subsidy_electricity_expense: 4_800 households: @@ -365,6 +369,7 @@ spm_units: spm_unit: members: [person1, person2, person3] + az_tanf: 0 heating_type: ELECTRICITY pre_subsidy_electricity_expense: 2_400 households: diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_earned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_earned_income.yaml index 13889839577..d6ca7a521ac 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_earned_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_earned_income.yaml @@ -42,6 +42,7 @@ spm_units: spm_unit: members: [person1, person2] + az_tanf: 0 heating_type: ELECTRICITY pre_subsidy_electricity_expense: 2_100 households: From 8c9c5f1f8ded613c4721106450ad42cfdc278f0c Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 20 Oct 2026 11:18:03 -0400 Subject: [PATCH 5/9] Count military retirement income in Arizona LIHEAP Add main's military_retirement_income (military retirement pay and survivor benefit payments) to Arizona's unearned income list as retirement income (policy section VI.A.6.e.ii.B). Co-Authored-By: Claude Opus 5.5 --- .../des/liheap/income/sources/unearned.yaml | 3 ++ .../az_liheap_countable_unearned_income.yaml | 46 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml index e14cabfaca8..0329a11f31f 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml @@ -14,6 +14,9 @@ values: - social_security - ssi - pension_income + # Section VI.A.6.e.ii.B "retirement income": military retirement pay and + # survivor benefit payments. + - military_retirement_income # retirement_distributions already sums taxable and tax-exempt IRA, 401(k), # SEP, 403(b) and Keogh distributions. - retirement_distributions diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.yaml index 239a8e2a196..dfa4e94171b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.yaml @@ -235,3 +235,49 @@ # counts. az_liheap_countable_unearned_income: 24_000 az_liheap_countable_income: 24_000 + +- name: Case 10, military retirement pay is counted as retirement income. + period: 2026 + input: + people: + person1: + age: 48 + military_retirement_pay: 26_400 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + gas_expense: 900 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # Policy p. 8 VI.A.6.e.ii.B counts "retirement income"; a 20-year + # military retiree's 2,200 a month pension counts in full. + az_liheap_countable_unearned_income: 26_400 + az_liheap_countable_income: 26_400 + +- name: Case 11, military survivor benefit payments are counted as retirement income. + period: 2026 + input: + people: + person1: + age: 55 + military_retirement_pay_survivors: 14_400 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_500 + az_tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # Survivor Benefit Plan annuity of 1,200 a month is retirement income + # under VI.A.6.e.ii.B. + az_liheap_countable_unearned_income: 14_400 + az_liheap_countable_income: 14_400 From 49fa4c2f470ebf4b8bdf0c65086a79028ec69e14 Mon Sep 17 00:00:00 2001 From: Ziming Date: Fri, 23 Oct 2026 20:05:51 -0400 Subject: [PATCH 6/9] Fix issues from review: Medicare premium, rental floor, revision 16A anchors - Count Social Security net of the Medicare Part B premium (Policy VII.A.1.c.ii.D) - Floor unearned sources per source; add farm rent and gambling winnings - Read the take-up-gated tanf for income and categorical eligibility - Re-anchor policy references to revision 16A pages - Pin the zero-cost energy-burden guard Co-Authored-By: Claude Opus 5.5 --- .../eligibility/categorical_programs.yaml | 9 +- .../az/des/liheap/eligibility/fpg_rate.yaml | 2 +- .../des/liheap/eligibility/fpg_year_lag.yaml | 17 ++- .../liheap/eligibility/max_table_size.yaml | 4 +- .../az/des/liheap/eligibility/smi_rate.yaml | 2 +- .../az/des/liheap/income/adult_age.yaml | 2 +- .../income/child_unearned_income_counted.yaml | 11 +- .../az/des/liheap/income/sources/earned.yaml | 9 +- .../des/liheap/income/sources/unearned.yaml | 33 +++-- .../states/az/des/liheap/payment/amount.yaml | 4 +- .../az/des/liheap/points/child_age_limit.yaml | 2 +- .../az/des/liheap/points/elderly_age.yaml | 2 +- .../az/des/liheap/points/energy_burden.yaml | 2 + .../az/des/liheap/points/income_ratio.yaml | 2 + .../az/des/liheap/points/portable_fuel.yaml | 2 +- .../des/liheap/points/vulnerable_member.yaml | 2 +- .../gov/states/az/des/liheap/az_liheap.yaml | 8 +- .../des/liheap/az_liheap_household_size.yaml | 4 +- .../az_liheap_categorically_eligible.yaml | 2 +- .../eligibility/az_liheap_eligible.yaml | 10 +- .../az_liheap_income_eligible.yaml | 17 +-- .../eligibility/az_liheap_income_limit.yaml | 7 +- .../az_liheap_countable_earned_income.yaml | 9 +- .../income/az_liheap_countable_income.yaml | 9 +- .../az_liheap_countable_unearned_income.yaml | 121 ++++++++++++++++-- .../gov/states/az/des/liheap/integration.yaml | 54 ++++---- .../az_liheap_energy_burden_points.yaml | 33 ++++- .../points/az_liheap_energy_need_points.yaml | 2 +- .../gov/states/az/des/liheap/az_liheap.py | 2 +- .../az/des/liheap/az_liheap_energy_cost.py | 14 +- .../az_liheap_categorically_eligible.py | 8 +- .../liheap/eligibility/az_liheap_eligible.py | 4 +- .../eligibility/az_liheap_income_eligible.py | 2 +- .../eligibility/az_liheap_income_limit.py | 20 +-- .../az_liheap_countable_earned_income.py | 12 +- .../income/az_liheap_countable_income.py | 16 ++- .../az_liheap_countable_unearned_income.py | 38 ++++-- .../points/az_liheap_energy_burden_points.py | 2 +- .../points/az_liheap_energy_need_points.py | 2 +- .../liheap/points/az_liheap_income_points.py | 1 + 40 files changed, 346 insertions(+), 156 deletions(-) diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/categorical_programs.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/categorical_programs.yaml index 9efe7f0b219..44c5fc2ee21 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/categorical_programs.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/categorical_programs.yaml @@ -2,19 +2,20 @@ description: Arizona treats a household as categorically eligible when a member # Section VI.A.6.b.i: "If one (1) Household member's current enrollment in Cash # Assistance or Nutrition Assistance is verified, the Household will be # considered categorically eligible." A positive modeled benefit stands for -# current enrollment. The FY2026 plan (Section 1.4) answers No for SSI and for -# means-tested veterans programs. +# current enrollment; snap and tanf are take-up gated, and tanf includes +# Arizona Cash Assistance (az_tanf). The FY2026 plan (Section 1.4) answers No +# for SSI and for means-tested veterans programs. values: 2025-08-25: - snap - - az_tanf + - tanf metadata: unit: list period: year label: Arizona LIHEAP categorical eligibility programs reference: - - title: "Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Categorical Eligibility definition and Section VI.A.6.a-b #page 3-4,7" + - title: "Arizona DES LIHEAP Policy, revision 16A (effective July 1, 2026), Categorical Eligibility definition and Section VI.A.6.a-b #page 3-4,7-8" href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=3 - title: Arizona LIHEAP FY2026 State Plan, Sections 1.4 and 1.4a (TANF and SNAP yes; SSI and means-tested veterans programs no) href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=5 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/fpg_rate.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/fpg_rate.yaml index 71f1513da5e..09375227c23 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/fpg_rate.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/fpg_rate.yaml @@ -7,7 +7,7 @@ metadata: period: year label: Arizona LIHEAP federal poverty guideline rate reference: - - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.A.6.c.i (gross income at or below the greater of 60% of the SMI or 150% of the FPL) + - title: Arizona DES LIHEAP Policy, revision 16A (effective July 1, 2026), Section VI.A.6.c.i (gross income at or below the greater of 60% of the SMI or 150% of the FPL) href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8 - title: Arizona LIHEAP FY2026 State Plan, Section 2.1 (household sizes 10 and over at 150.00% of the HHS Poverty Guidelines) href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=9 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/fpg_year_lag.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/fpg_year_lag.yaml index 65fda5a4d87..a1c451ebe0a 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/fpg_year_lag.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/fpg_year_lag.yaml @@ -1,8 +1,11 @@ description: Arizona uses federal poverty guidelines from this many years before the modeled program year for the income limit under the Low Income Home Energy Assistance Program. -# Verified for FFY2026: Appendix A's household sizes 10 to 12 and its $687 -# additional-person increment reproduce with the 2025 guideline (15,650 plus -# 5,500 per additional person), not the 2026 guideline. This is a relative year -# selection, not a fixed guideline year. Earlier Arizona policy is unverified. +# Verified for FFY2026: revision 13's Appendix A household sizes 10 to 12 and +# its $687 additional-person increment reproduce with the 2025 guideline +# (15,650 plus 5,500 per additional person), not the 2026 guideline. Revision +# 16A's Appendix A prints the FFY2027 limits, whose sizes 10 to 12 and $710 +# increment reproduce with the 2026 guideline, the same one-year lag. This is a +# relative year selection, not a fixed guideline year. Earlier Arizona policy +# is unverified. values: 2025-08-25: 1 @@ -11,7 +14,9 @@ metadata: period: year label: Arizona LIHEAP federal poverty guideline year lag reference: - - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Appendix A Income Eligibility by Household Size - href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=37 + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Appendix A Income Eligibility by Household Size (FFY2026 limits, archived copy) + href: https://web.archive.org/web/20251030203752/https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=37 + - title: Arizona DES LIHEAP Policy, revision 16A (effective July 1, 2026), Appendix A LIHEAP Income Eligibility by Household Size (FFY2027 limits) + href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=39 - title: LIHEAP Clearinghouse, FFY2026 poverty guidelines by household size (2025 HHS guideline) href: https://liheapch.acf.gov/profiles/povertytables/FY2026/popstate.htm diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/max_table_size.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/max_table_size.yaml index 15f6e6e7dee..106db4cae44 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/max_table_size.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/max_table_size.yaml @@ -7,5 +7,5 @@ metadata: period: year label: Arizona LIHEAP maximum tabulated household size reference: - - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Appendix A Income Eligibility by Household Size (sizes 1 to 12 and "EACH ADDL OVER 12 +$687") - href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=37 + - title: Arizona DES LIHEAP Policy, revision 16A (effective July 1, 2026), Appendix A LIHEAP Income Eligibility by Household Size (sizes 1 to 12 and "EACH ADDL OVER 12 +$710" for FFY2027) + href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=39 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/smi_rate.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/smi_rate.yaml index 0e0f56490b1..c356939faf1 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/smi_rate.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/eligibility/smi_rate.yaml @@ -7,7 +7,7 @@ metadata: period: year label: Arizona LIHEAP state median income rate reference: - - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.A.6.c.i (gross income at or below the greater of 60% of the SMI or 150% of the FPL) + - title: Arizona DES LIHEAP Policy, revision 16A (effective July 1, 2026), Section VI.A.6.c.i (gross income at or below the greater of 60% of the SMI or 150% of the FPL) href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8 - title: Arizona LIHEAP FY2026 State Plan, Section 2.1 (household sizes 1 to 9 at 60.00% of State Median Income) href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=9 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/income/adult_age.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/income/adult_age.yaml index 86740790728..881aa92a750 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/income/adult_age.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/income/adult_age.yaml @@ -7,5 +7,5 @@ metadata: period: year label: Arizona LIHEAP adult age reference: - - title: "Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.A.6.d.ii (income from all household members aged 18 and older) and Section VII.A.1.c.ii #page 8,13" + - title: "Arizona DES LIHEAP Policy, revision 16A (effective July 1, 2026), Section VI.A.6.d.ii (income from all household members aged 18 and older) and Section VII.A.1.c.ii #page 8,13" href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/income/child_unearned_income_counted.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/income/child_unearned_income_counted.yaml index 5352950aae1..407e4dc7508 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/income/child_unearned_income_counted.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/income/child_unearned_income_counted.yaml @@ -2,11 +2,12 @@ description: Arizona counts the unearned income of household members under the a # Revision 13, Section VI.A.6.e.ii.E: "Income from Household members under the # age of 18 is not considered Countable Income." Revision 15 (effective March # 23, 2026) rewrote the sentence to "Earned income from Household members under -# the age of 18 is not considered Countable Income". Earned income of members -# under 18 is excluded under both revisions. +# the age of 18 is not considered Countable Income", and revision 16A keeps +# that wording. Earned income of members under 18 is excluded under every +# revision. # NOTE: Counting a child's unearned income (SSI, survivor benefits, child # support) from March 23, 2026 is an inference from that narrowed exclusion. -# Revision 15 does not say so directly, and its Section VI.A.6.d.ii still says +# The revisions do not say so directly, and Section VI.A.6.d.ii still says # that the income of members aged 18 and older is household income. values: 2025-08-25: false @@ -17,5 +18,7 @@ metadata: period: year label: Arizona LIHEAP counts unearned income of members under 18 reference: - - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025) and revision 15 (effective March 23, 2026), Section VI.A.6.e.ii.E + - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.A.6.e.ii.E (archived copy) + href: https://web.archive.org/web/20251030203752/https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=9 + - title: Arizona DES LIHEAP Policy, revision 16A (effective July 1, 2026), Section VI.A.6.e.ii.E (the revision 15 wording of March 23, 2026) href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=9 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/earned.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/earned.yaml index 9b0a24fbce1..0f6f65ebaba 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/earned.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/earned.yaml @@ -2,9 +2,10 @@ description: Arizona uses these earned income sources to approximate countable h # Section VI.A.6.e.ii.A counts "Earned income from employment, including # self-employment (includes wages, salaries, tips, etc.)"; the plan's Section # 1.9 also checks contract income, commissions, WIA employment income and work -# study, which existing inputs report as wages. Section VII.A.1.c.ii.B verifies -# self-employment as "Profit or loss documentation" (Schedule C) and does not -# say whether a loss offsets other income, so the existing net self-employment +# study, which existing inputs report as wages. Section VII.A.1.c.ii.B counts +# "Income from self-employment or sole-proprietorship" and Section +# VII.A.1.c.iii.B.1 accepts a "Schedule C (Profit and Loss Statement)"; neither +# says whether a loss offsets other income, so the existing net self-employment # inputs are used without a further expense deduction and each is floored at # zero in the formula. values: @@ -20,7 +21,7 @@ metadata: period: year label: Arizona LIHEAP countable earned income sources reference: - - title: "Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.A.6.e.ii.A and Section VII.A.1.c.ii.A-B #page 8,13" + - title: "Arizona DES LIHEAP Policy, revision 16A (effective July 1, 2026), Section VI.A.6.e.ii.A, Section VII.A.1.c.ii.A-B and Section VII.A.1.c.iii.B.1 #page 8,13-14" href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8 - title: "Arizona LIHEAP FY2026 State Plan, Section 1.9 (wages, self-employment income, contract income, commissions, WIA employment income and work study counted) #page 6-7" href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=6 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml index 0329a11f31f..941dd0bbea6 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml @@ -1,17 +1,16 @@ description: Arizona uses these person-level unearned income sources to determine gross household income under the Low Income Home Energy Assistance Program. -# Section VI.A.6.e.ii.B-D counts "Unearned income, including SSA and TANF -# benefits, alimony, child support, gambling profits, and retirement income", -# "Income from Unemployment Insurance" and "Consistent income received from -# other sources"; the plan's Section 1.9 checks the full list. Cash Assistance -# (az_tanf) is a household benefit and is added in az_liheap_countable_income. +# Section VI.A.6.e.ii.B-D counts "Unearned income, including Social Security +# and TANF benefits, alimony, child support, gambling profits, and retirement +# income", "Income from Unemployment Insurance" and "Consistent income received +# from other sources"; the plan's Section 1.9 checks the full list. Each source +# is floored at zero in the formula, so a rental loss does not offset other +# income. Social Security is added in the formula net of the Medicare Part B +# premium. Cash Assistance (tanf) is a household benefit and is added in +# az_liheap_countable_income. values: 2025-08-25: - total_unemployment_compensation - strike_benefits - # Section VII.A.1.c.ii.D note: Medicare premiums "should be subtracted when - # determining gross income". Social Security is counted gross here; the - # premium netting is deferred, as in the other state LIHEAP implementations. - - social_security - ssi - pension_income # Section VI.A.6.e.ii.B "retirement income": military retirement pay and @@ -21,17 +20,25 @@ values: # SEP, 403(b) and Keogh distributions. - retirement_distributions # Railroad Retirement annuities are "retirement income" (Section - # VI.A.6.e.ii.B, page 8) and "retirement benefits" (Section + # VI.A.6.e.ii.B, page 9) and "retirement benefits" (Section # VII.A.1.c.ii.D, page 14); plan Section 1.9 checks "Retirement / pension # benefits" (page 6). - railroad_benefits - veterans_benefits - alimony_income - child_support_received + # Plan Section 1.9 checks "Rental income" (page 6); farm rent is counted + # with it. - rental_income + - farm_rent_income - interest_income - ordinary_dividend_income - general_assistance + # Section VI.A.6.e.ii.B counts "gambling profits" (page 9). Plan Section 1.9 + # leaves one-time lump sums such as "winnings from lotteries" unchecked + # (page 6); gambling_winnings does not separate one-time winnings, so all + # are counted. + - gambling_winnings # Employment disability insurance: plan Section 1.9 checks "Insurance # payments made directly to the insured" (page 6), and Gross Income is # "The total Household income for all Household members before taxes and @@ -39,7 +46,7 @@ values: - disability_benefits # Plan Section 1.9 neither lists nor excludes workers' compensation; the # Gross Income definition (page 4) and Section VI.A.6.e.ii.D "Consistent - # income received from other sources" (page 8) support counting it. + # income received from other sources" (page 9) support counting it. - workers_compensation # Cash gifts over $50, jury duty, legal settlements, other insurance # payments to the insured, mortgage or sales-contract payments, royalties @@ -50,7 +57,7 @@ metadata: period: year label: Arizona LIHEAP countable unearned income sources reference: - - title: "Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Gross Income definition, Section VI.A.6.e.ii.B-D and Section VII.A.1.c.ii.C-F #page 4,8,13-14" + - title: "Arizona DES LIHEAP Policy, revision 16A (effective July 1, 2026), Gross Income definition, Section VI.A.6.e.ii.B-D and Section VII.A.1.c.ii.C-F #page 4,9,14" href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=4 - - title: "Arizona LIHEAP FY2026 State Plan, Section 1.9 (unemployment insurance, strike pay, SSA benefits, SSI, retirement and pension, general assistance, TANF, rental income, alimony, child support, interest, dividends or royalties, insurance payments made directly to the insured and VA benefits counted) #page 6-7" + - title: "Arizona LIHEAP FY2026 State Plan, Section 1.9 (unemployment insurance, strike pay, SSA benefits excluding the Medicare deduction, SSI, retirement and pension, general assistance, TANF, rental income, alimony, child support, interest, dividends or royalties, insurance payments made directly to the insured and VA benefits counted) #page 6-7" href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=6 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/payment/amount.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/payment/amount.yaml index 7c51826eceb..e57ba6b0164 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/payment/amount.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/payment/amount.yaml @@ -37,5 +37,5 @@ metadata: href: https://des.az.gov/liheap - title: Arizona LIHEAP FY2026 State Plan, Section 2.6 (minimum benefit $160, maximum benefit $640) href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=10 - - title: "Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VII.B.1.a (up to $640 for a standard payment) and Section VII.B.4.b #page 30,34" - href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=30 + - title: "Arizona DES LIHEAP Policy, revision 16A (effective July 1, 2026), Section VII.B.1.a (up to $640 for a standard payment), Section VII.B.4.b and Appendix B Section 4 Benefit Calculation #page 31,35,41" + href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=31 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/points/child_age_limit.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/points/child_age_limit.yaml index 0397ba2aa9d..5e05dc74841 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/points/child_age_limit.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/points/child_age_limit.yaml @@ -11,7 +11,7 @@ metadata: reference: - title: Arizona LIHEAP Eligibility Worksheet, $640 Limit (effective October 12, 2024), Section 3 Energy Need (Child age 6 or younger, 1 point) href: https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1 - - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.B.1.d.i (household members "Aged six (6) and under") + - title: 'Arizona DES LIHEAP Policy, revision 16A (effective July 1, 2026), Section VI.B.1.d.i (household members "Aged six (6) and under") and Appendix B Section 3 (Child age 6 or younger, 1 point) #page 10,41' href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=10 - title: Arizona LIHEAP FY2026 State Plan, Section 2.4 (additional points for children 6 years old and under) href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=10 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/points/elderly_age.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/points/elderly_age.yaml index 9d82451373f..b6930ef6db5 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/points/elderly_age.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/points/elderly_age.yaml @@ -7,7 +7,7 @@ metadata: period: year label: Arizona LIHEAP elderly age reference: - - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.B.1.d.ii (household members "Aged 60 and over") + - title: Arizona DES LIHEAP Policy, revision 16A (effective July 1, 2026), Section VI.B.1.d.ii (household members "Aged 60 and over") href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=10 - title: Arizona LIHEAP FY2026 State Plan, Section 2.4 (additional points for the elderly, 60+ years old) href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=10 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/points/energy_burden.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/points/energy_burden.yaml index 9659bdaaa02..54939f380ab 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/points/energy_burden.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/points/energy_burden.yaml @@ -46,3 +46,5 @@ metadata: href: https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1 - title: Arizona DES, Low Income Home Energy Assistance Program page (energy burden formula and the 350 / 2,000 = 17.5% example) href: https://des.az.gov/liheap + - title: Arizona DES LIHEAP Policy, revision 16A (effective July 1, 2026), Appendix B Section 2 Energy Burden + href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=40 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/points/income_ratio.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/points/income_ratio.yaml index 5b386369218..fe57e24eb91 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/points/income_ratio.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/points/income_ratio.yaml @@ -43,3 +43,5 @@ metadata: href: https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1 - title: Arizona DES, Low Income Home Energy Assistance Program page (income points formula and the 2,000 / 4,535 = 44% example) href: https://des.az.gov/liheap + - title: Arizona DES LIHEAP Policy, revision 16A (effective July 1, 2026), Appendix B Section 1 Income Eligibility + href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=40 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/points/portable_fuel.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/points/portable_fuel.yaml index cb2ba63fd63..e5d4f31dae8 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/points/portable_fuel.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/points/portable_fuel.yaml @@ -11,5 +11,5 @@ metadata: href: https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1 - title: Arizona DES, Low Income Home Energy Assistance Program page ("2 points are added for households that utilize deliverable/portal fuel") href: https://des.az.gov/liheap - - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Portable Fuel definition (propane, wood, coal, fuel for gas generators and pellets) + - title: "Arizona DES LIHEAP Policy, revision 16A (effective July 1, 2026), Portable Fuel definition (propane, wood, coal, fuel for gas generators and pellets) and Appendix B Section 2 (Deliverable/Portable Fuel, 2 points) #page 5,40" href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=5 diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/points/vulnerable_member.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/points/vulnerable_member.yaml index dd6dc57177d..92ea754da11 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/points/vulnerable_member.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/points/vulnerable_member.yaml @@ -13,5 +13,5 @@ metadata: href: https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1 - title: Arizona LIHEAP FY2026 State Plan, Section 2.4 ("A household may receive one point for each category, if eligible") href: https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=10 - - title: Arizona DES LIHEAP Policy, revision 13 (effective August 25, 2025), Section VI.B.1.d (benefit factors) + - title: "Arizona DES LIHEAP Policy, revision 16A (effective July 1, 2026), Section VI.B.1.d (benefit factors) and Appendix B Section 3 Energy Need (one point per category) #page 10,41" href: https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=10 diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap.yaml index 5e401ca5ebe..e00959ac381 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap.yaml @@ -7,8 +7,8 @@ # https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=10; # DES program page payment table, https://des.az.gov/liheap). The benefit is # not capped at the bill: any remainder is credited to the Priority 1 -# account (DES LIHEAP Policy rev. 13, pp. 33-34 VII.B.3.b.iii, -# https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=33). +# account (DES LIHEAP Policy rev. 16A, pp. 34-35 VII.B.3.b.iii, +# https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=34). # A categorically eligible household with zero points receives the plan's # $160 minimum. Annual limits: size 1 33,684; size 2 44,052; size 3 54,420. @@ -281,7 +281,7 @@ state_code: AZ output: # Income 0 -> 5; burden 300 against zero income -> 6; need 0; total 11 - # -> 480, more than the 300 bill (policy pp. 33-34: remainder credited). + # -> 480, more than the 300 bill (policy pp. 34-35: remainder credited). az_liheap_points: 11 az_liheap: 480 @@ -371,7 +371,7 @@ members: [person1] state_code: AZ output: - # Railroad Retirement is "retirement income" (policy p. 8 VI.A.6.e.ii.B): + # Railroad Retirement is "retirement income" (policy p. 9 VI.A.6.e.ii.B): # countable income 40,000. az_liheap_countable_income: 40_000 # Appendix A size 1: 2,807 x 12 = 33,684. diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap_household_size.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap_household_size.yaml index 3330249ffde..0bc1bcee266 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap_household_size.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap_household_size.yaml @@ -1,7 +1,7 @@ # Arizona LIHEAP household size. Only Eligible Household Members (U.S. citizens # and Qualified Noncitizens under 8 U.S.C. 1641(b)) count toward the household # size; in a Mixed-Status Household the income of every member aged 18 or -# older still counts (DES LIHEAP Policy rev. 13 eff. 2025-08-25, p. 4 +# older still counts (DES LIHEAP Policy rev. 16A eff. 2026-07-01, pp. 4-5 # definitions and p. 8 VI.A.6.d, # https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8). # The variable is defined only for Arizona households. @@ -68,7 +68,7 @@ spm_units: spm_unit: members: [person1, person2, person3, person4] - az_tanf: 0 + tanf: 0 heating_type: ELECTRICITY pre_subsidy_electricity_expense: 2_700 households: diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_categorically_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_categorically_eligible.yaml index e9a0cad6474..65e72aad5be 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_categorically_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_categorically_eligible.yaml @@ -1,7 +1,7 @@ # Arizona LIHEAP categorical eligibility: a household is categorically # eligible when one member's current enrollment in DES Cash Assistance (TANF) # or Nutrition Assistance (SNAP) is verified; SSI and veterans programs do not -# confer it (DES LIHEAP Policy rev. 13 eff. 2025-08-25, pp. 7-8 VI.A.6.b, +# confer it (DES LIHEAP Policy rev. 16A eff. 2026-07-01, pp. 7-8 VI.A.6.b, # https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=7; # FY2026 plan 1.4 and 1.4a, # https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=5). diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_eligible.yaml index 6ffac2023dc..438e2705b4f 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_eligible.yaml @@ -1,11 +1,11 @@ # Arizona LIHEAP eligibility: an Arizona household with at least one eligible # member that meets categorical or income eligibility and needs help with an -# energy bill (DES LIHEAP Policy rev. 13 eff. 2025-08-25, pp. 6-8 VI.A, +# energy bill (DES LIHEAP Policy rev. 16A eff. 2026-07-01, pp. 6-8 VI.A, # https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=6). # The Standard benefit covers every home energy bill (electricity, gas and # portable fuel, p. 4 "Energy Utility"), so a home with no heating system but # an electricity bill qualifies, and renters with energy in rent are eligible -# (p. 32 VII.B.3.a.ii.B). An unspecified heating type pays nothing (project +# (p. 33 VII.B.3.a.ii.B). An unspecified heating type pays nothing (project # rule). Size-1 annual limit 2,807 x 12 = 33,684. - name: Case 1, single minimum-wage earner with an electricity bill is eligible. @@ -94,8 +94,8 @@ members: [person1] state_code: AZ output: - # Policy p. 4: undesignated energy payments in rent form a household; - # p. 32: payment goes to the applicant when utilities are in rent. + # Policy p. 5: undesignated energy payments in rent form a household; + # p. 33: payment goes to the applicant when utilities are in rent. az_liheap_energy_cost: 0 az_liheap_eligible: true @@ -194,7 +194,7 @@ members: [person1, person2] state_code: AZ output: - # Policy pp. 6-7 VI.A.3: at least one member must be a citizen or + # Policy p. 7 VI.A.3: at least one member must be a citizen or # qualified noncitizen. az_liheap_household_size: 0 az_liheap_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.yaml index 15b8d1eee3b..3affe8179bb 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.yaml @@ -1,8 +1,9 @@ # Arizona LIHEAP income eligibility: when categorical eligibility does not # apply, gross income must be "at or below" the Appendix A limit for the -# eligible household size (DES LIHEAP Policy rev. 13 eff. 2025-08-25, p. 8 -# VI.A.6.c.i and p. 37 Appendix A, -# https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=37). +# eligible household size (DES LIHEAP Policy rev. 16A eff. 2026-07-01, p. 8 +# VI.A.6.c.i, https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8; +# FFY2026 Appendix A in rev. 13 eff. 2025-08-25, p. 37, +# https://web.archive.org/web/20251030203752/https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=37). # Annual limits are twelve times the monthly limits: size 1 2,807 x 12 = # 33,684; size 2 3,671 x 12 = 44,052; size 5 6,262 x 12 = 75,144; size 6 # 7,126 x 12 = 85,512; size 10 8,143 x 12 = 97,716; size 13 (9,518 + 687) x @@ -71,7 +72,7 @@ spm_units: spm_unit: members: [person1, person2, person3, person4, person5] - az_tanf: 0 + tanf: 0 heating_type: NATURAL_GAS pre_subsidy_electricity_expense: 2_400 gas_expense: 700 @@ -135,7 +136,7 @@ spm_units: spm_unit: members: [person1, person2, person3, person4, person5, person6] - az_tanf: 0 + tanf: 0 heating_type: ELECTRICITY pre_subsidy_electricity_expense: 3_000 households: @@ -178,7 +179,7 @@ spm_units: spm_unit: members: [person1, person2, person3, person4, person5, person6, person7, person8, person9, person10] - az_tanf: 0 + tanf: 0 heating_type: NATURAL_GAS pre_subsidy_electricity_expense: 3_600 gas_expense: 900 @@ -271,7 +272,7 @@ spm_units: spm_unit: members: [person1, person2, person3, person4, person5, person6, person7, person8, person9, person10, person11, person12, person13] - az_tanf: 0 + tanf: 0 heating_type: ELECTRICITY pre_subsidy_electricity_expense: 4_800 households: @@ -369,7 +370,7 @@ spm_units: spm_unit: members: [person1, person2, person3] - az_tanf: 0 + tanf: 0 heating_type: ELECTRICITY pre_subsidy_electricity_expense: 2_400 households: diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_income_limit.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_income_limit.yaml index f7cfe79f12c..ba13426436e 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_income_limit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_income_limit.yaml @@ -1,8 +1,9 @@ # Arizona LIHEAP annual income limit: twelve times the Appendix A monthly # gross income limit, which is the greater of 60% of the state median income -# and 150% of the federal poverty level (DES LIHEAP Policy rev. 13 eff. -# 2025-08-25, p. 8 VI.A.6.c.i and p. 37 Appendix A, -# https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=37). +# and 150% of the federal poverty level (DES LIHEAP Policy rev. 16A eff. +# 2026-07-01, p. 8 VI.A.6.c.i, https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8; +# FFY2026 Appendix A in rev. 13 eff. 2025-08-25, p. 37, +# https://web.archive.org/web/20251030203752/https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=37). # FFY2026 Appendix A monthly limits: 2,807; 3,671; 4,535; 5,399; 6,262; 7,126; # 7,288; 7,450; 7,612; 8,143; 8,831; 9,518; +687 for each person over 12. # Reproduction from the federal guidelines (period 2026): diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_earned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_earned_income.yaml index d6ca7a521ac..89b1d84ed88 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_earned_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_earned_income.yaml @@ -1,13 +1,14 @@ # Arizona LIHEAP countable earned income per person. Gross wages and # self-employment income of every member aged 18 or older count (DES LIHEAP -# Policy rev. 13 eff. 2025-08-25, p. 8 VI.A.6.d.ii and p. 13 VII.A.1.c.ii, +# Policy rev. 16A eff. 2026-07-01, p. 8 VI.A.6.d.ii and p. 13 VII.A.1.c.ii, # https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8). # Earned income of members under 18 never counts: rev. 13 p. 9 E excludes all # under-18 income and rev. 15 (eff. 2026-03-23) p. 9 E excludes their earned # income. The FY2026 plan leaves "earned income of a child under 18" unchecked # (https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=7). -# Self-employment is verified as "profit or loss" (policy p. 13); the model -# floors each net source at zero (project convention). +# Self-employment is documented with a Schedule C "Profit and Loss Statement" +# (policy p. 14 VII.A.1.c.iii.B.1); the model floors each net source at zero +# (project convention). # Arizona's 2025 minimum wage is $14.70 an hour: 14.70 x 2,080 = 30,576. - name: Case 1, full-time minimum-wage earnings are counted in full. @@ -42,7 +43,7 @@ spm_units: spm_unit: members: [person1, person2] - az_tanf: 0 + tanf: 0 heating_type: ELECTRICITY pre_subsidy_electricity_expense: 2_100 households: diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_income.yaml index af6d391c32a..c4024ff861c 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_income.yaml @@ -1,8 +1,8 @@ # Arizona LIHEAP countable income of the household: countable earned plus # unearned income of every member, plus the unit's DES Cash Assistance (TANF) -# benefit (DES LIHEAP Policy rev. 13 eff. 2025-08-25, p. 8 VI.A.6.e.ii.B -# "Unearned income, including SSA and TANF benefits", -# https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8; +# benefit (DES LIHEAP Policy rev. 16A eff. 2026-07-01, p. 9 VI.A.6.e.ii.B +# "Unearned income, including Social Security and TANF benefits", +# https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=9; # FY2026 plan 1.9 TANF checked, # https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=6). # az_tanf is a monthly variable; in a yearly test its input is the annual @@ -44,6 +44,9 @@ person2: age: 67 social_security: 18_000 + # Her own 1,500 a month is within the Medicare Savings Program limits, + # so the program pays her Part B premium and nothing is netted. + medicare_part_b_premium: 0 ssi: 0 person3: age: 15 diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.yaml index dfa4e94171b..f34800d0201 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.yaml @@ -1,26 +1,34 @@ # Arizona LIHEAP countable unearned income per person. Gross unearned income # of every member aged 18 or older counts: SSA, SSI, retirement (including # Railroad Retirement), unemployment insurance, alimony, child support, rental -# income, interest, dividends, general assistance, VA benefits, employment -# disability insurance and workers' compensation (DES LIHEAP Policy rev. 13 -# eff. 2025-08-25, pp. 8-9 VI.A.6.e, +# and farm rental income, gambling profits, interest, dividends, general +# assistance, VA benefits, employment disability insurance and workers' +# compensation (DES LIHEAP Policy rev. 16A eff. 2026-07-01, pp. 8-9 VI.A.6.e, # https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8; # FY2026 plan 1.9, # https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=6). # Members under 18: rev. 13 p. 9 E counts none of their income; rev. 15 # (eff. 2026-03-23) p. 9 E excludes only their earned income, so a child's # unearned income counts from period 2027 (parameters are read at the start -# of the period). Social Security is counted gross; the Medicare premium -# netting (policy p. 14 note) is deferred, as in Kansas. +# of the period). Social Security counts net of the member's out-of-pocket +# Medicare Part B premium (policy p. 14 VII.A.1.c.ii.D note; plan 1.9 +# "Excluding MediCare deduction"). Social Security after the premium, rental +# income and farm rental income are each floored at zero per person, so a +# premium or a loss does not offset other income. +# 2026 Part B standard premium 202.90 a month: 202.90 x 12 = 2,434.80. # 2026 SSI federal benefit rate 994 a month: 994 x 12 = 11,928. -- name: Case 1, Social Security and pension income of a retiree are counted gross. +- name: Case 1, a retiree's Social Security net of the Medicare Part B premium and pension income are counted. period: 2026 + absolute_error_margin: 0.01 input: people: person1: age: 70 social_security: 23_712 + # Income of 2,476 a month is above the Medicare Savings Program + # limits, so the retiree pays the 2,434.80 standard premium. + medicare_part_b_premium: 2_434.80 pension_income: 6_000 ssi: 0 spm_units: @@ -33,8 +41,9 @@ members: [person1] state_code: AZ output: - # 23,712 + 6,000 = 29,712 (plan 1.9: SSA and retirement/pension checked). - az_liheap_countable_unearned_income: 29_712 + # (23,712 - 2,434.80) + 6,000 = 27,277.20 (plan 1.9: SSA and + # retirement/pension checked). + az_liheap_countable_unearned_income: 27_277.20 - name: Case 2, unemployment, child support, alimony and rent are counted. period: 2026 @@ -105,7 +114,7 @@ state_code: AZ output: # 1,200 + 800 + 2,000 + 3,000 = 7,000. The IRA distribution reaches the - # total once, through retirement_distributions (contract R1). + # total once, through retirement_distributions. az_liheap_countable_unearned_income: 7_000 - name: Case 5, a child's SSI and child support are not counted in 2026 under revision 13. @@ -182,7 +191,7 @@ members: [person1] state_code: AZ output: - # Policy p. 8 VI.A.6.e.ii.B "retirement income", p. 14 "retirement + # Policy p. 9 VI.A.6.e.ii.B "retirement income", p. 14 "retirement # benefits"; plan 1.9 "Retirement / pension benefits" checked: 30,000. az_liheap_countable_unearned_income: 30_000 az_liheap_countable_income: 30_000 @@ -230,7 +239,7 @@ members: [person1] state_code: AZ output: - # Policy p. 4 Gross Income and p. 8 VI.A.6.e.ii.D "Consistent income + # Policy p. 4 Gross Income and p. 9 VI.A.6.e.ii.D "Consistent income # received from other sources"; two-thirds of 36,000 prior wages = 24,000 # counts. az_liheap_countable_unearned_income: 24_000 @@ -254,7 +263,7 @@ members: [person1] state_code: AZ output: - # Policy p. 8 VI.A.6.e.ii.B counts "retirement income"; a 20-year + # Policy p. 9 VI.A.6.e.ii.B counts "retirement income"; a 20-year # military retiree's 2,200 a month pension counts in full. az_liheap_countable_unearned_income: 26_400 az_liheap_countable_income: 26_400 @@ -281,3 +290,91 @@ # under VI.A.6.e.ii.B. az_liheap_countable_unearned_income: 14_400 az_liheap_countable_income: 14_400 + +- name: Case 12, netting the Medicare Part B premium brings a retiree under the income limit. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 67 + social_security: 34_000 + # Income of 2,833 a month is above the Medicare Savings Program + # limits, so the retiree pays the 2,434.80 standard premium. + medicare_part_b_premium: 2_434.80 + ssi: 0 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + snap: 0 + tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # 34,000 - 2,434.80 = 31,565.20 (policy p. 14 VII.A.1.c.ii.D note). + az_liheap_countable_unearned_income: 31_565.20 + az_liheap_countable_income: 31_565.20 + # 31,565.20 <= 33,684; gross Social Security of 34,000 would be over it. + az_liheap_income_eligible: true + # Income 31,565.20 / 33,684 = 0.937 -> 1; burden 1,800 / 31,565.20 = + # 0.057 -> 1; elderly 1; total 3 -> 160. + az_liheap_points: 3 + az_liheap: 160 + +- name: Case 13, a rental loss does not offset wages. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 36_000 + rental_income: -6_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + snap: 0 + tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # max(-6,000, 0) = 0: a loss is not income (policy p. 4 Gross Income). + az_liheap_countable_unearned_income: 0 + az_liheap_countable_income: 36_000 + # 36,000 > 33,684; netting the loss (30,000) would have paid 160. + az_liheap_income_eligible: false + az_liheap_eligible: false + az_liheap: 0 + +- name: Case 14, farm rent and gambling winnings are counted as unearned income. + period: 2026 + input: + people: + person1: + age: 58 + employment_income: 22_000 + farm_rent_income: 4_800 + gambling_winnings: 1_200 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_700 + tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # Plan 1.9 "Rental income" checked; policy VI.A.6.e.ii.B "gambling + # profits": 4,800 + 1,200 = 6,000. + az_liheap_countable_unearned_income: 6_000 + # 22,000 + 6,000 = 28,000. + az_liheap_countable_income: 28_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/integration.yaml index 98263590066..926d056feac 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/integration.yaml @@ -1,8 +1,10 @@ # Arizona Standard LIHEAP benefit, end to end. Sources: DES LIHEAP Policy -# rev. 13 eff. 2025-08-25 (household and immigration pp. 4-8, categorical -# eligibility pp. 7-8, countable income pp. 8-9, benefit factors p. 10, -# Appendix A p. 37) and rev. 15 eff. 2026-03-23 (p. 9 E, earned income of -# under-18 members only), https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8; +# rev. 16A eff. 2026-07-01 (household and immigration pp. 4-8, categorical +# eligibility pp. 7-8, countable income pp. 8-9 with the rev. 15 p. 9 E +# exclusion of under-18 earned income only, benefit factors p. 10, FY27 +# Appendix A p. 39), https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8; +# rev. 13 eff. 2025-08-25 (FFY2026 Appendix A p. 37), +# https://web.archive.org/web/20251030203752/https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=37; # LIHEAP Eligibility Worksheet, $640 Limit, eff. 2024-10-12 (point tables and # payment matrix), # https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1; @@ -61,13 +63,17 @@ az_liheap_points: 5 az_liheap: 320 -- name: Case 2, a retiree on SNAP above the income limit with propane heat receives 160. +- name: Case 2, a retiree on SNAP with propane heat, under the limit after the Medicare premium, receives 320. period: 2026 + absolute_error_margin: 0.01 input: people: person1: age: 70 social_security: 36_000 + # Income of 3,000 a month is above the Medicare Savings Program + # limits, so the retiree pays the 2026 standard premium of 202.90 x 12. + medicare_part_b_premium: 2_434.80 ssi: 0 spm_units: spm_unit: @@ -83,26 +89,27 @@ state_code: AZ output: az_liheap_household_size: 1 - # Social Security counted gross. - az_liheap_countable_income: 36_000 + # Social Security net of the Part B premium (policy p. 14 note): + # 36,000 - 2,434.80 = 33,565.20. + az_liheap_countable_income: 33_565.20 # Appendix A size 1: 2,807 x 12. az_liheap_income_limit: 33_684 - # 36,000 > 33,684. - az_liheap_income_eligible: false - # SNAP receipt (200 a month) confers categorical eligibility. + # 33,565.20 <= 33,684. + az_liheap_income_eligible: true + # SNAP receipt (200 a month) also confers categorical eligibility. az_liheap_categorically_eligible: true # 1,200 + 1,500. az_liheap_energy_cost: 2_700 az_liheap_eligible: true - # 36,000 / 33,684 = 1.069 > 1.00: no income points. - az_liheap_income_points: 0 - # 2,700 / 36,000 = 0.075: 6%-10% band, 1 point + 2 portable fuel. + # 33,565.20 / 33,684 = 0.996: 81%-100% band. + az_liheap_income_points: 1 + # 2,700 / 33,565.20 = 0.080: 6%-10% band, 1 point + 2 portable fuel. az_liheap_energy_burden_points: 3 # Aged 60 and over. az_liheap_energy_need_points: 1 - # 0 + 3 + 1 = 4: the 1-4 band. - az_liheap_points: 4 - az_liheap: 160 + # 1 + 3 + 1 = 5: the 5-8 band. + az_liheap_points: 5 + az_liheap: 320 - name: Case 3, a mixed-status family of four is sized at three and receives 160. period: 2026 @@ -170,14 +177,14 @@ pre_subsidy_electricity_expense: 0 gas_expense: 0 snap: 0 - az_tanf: 4_164 + tanf: 4_164 households: household: members: [person1, person2] state_code: AZ output: az_liheap_household_size: 2 - # Cash Assistance 347 x 12 = 4,164 counts as income (policy p. 8 B). + # Cash Assistance 347 x 12 = 4,164 counts as income (policy p. 9 B). az_liheap_countable_income: 4_164 # Appendix A size 2: 3,671 x 12. az_liheap_income_limit: 44_052 @@ -258,11 +265,12 @@ az_liheap_countable_unearned_income: [0, 3_600] # 18,000 + 3,600. az_liheap_countable_income: 21_600 - # Model projection for FFY2027: Arizona SMI 112,946; floor(0.6 x 112,946) - # = 67,767; floor(0.68 x 67,767) = 46,081; floor(46,081 / 12) = 3,840; - # FPG arm floor(1.5 x (15,960 + 5,680) / 12) = 2,705; 3,840 x 12 = 46,080. - # Equals the two-person limit on the DES program page captured 2026-09-21 - # (not independently verified policy). + # FY27 limit: Arizona SMI 112,946; floor(0.6 x 112,946) = 67,767; + # floor(0.68 x 67,767) = 46,081; floor(46,081 / 12) = 3,840; FPG arm + # floor(1.5 x (15,960 + 5,680) / 12) = 2,705; 3,840 x 12 = 46,080. The + # model reproduces the published FY27 size-2 limit of 3,840 a month (DES + # LIHEAP Policy revision 16A, Appendix A, p. 39). Verified coverage stays + # FY2026; 2027 is not claimed. az_liheap_income_limit: 46_080 az_liheap_income_eligible: true az_liheap_eligible: true diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/points/az_liheap_energy_burden_points.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/points/az_liheap_energy_burden_points.yaml index d240f704acc..5f295603a52 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/points/az_liheap_energy_burden_points.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/points/az_liheap_energy_burden_points.yaml @@ -6,7 +6,7 @@ # https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1; # DES program page example "350 / 2,000 = 17.5%, three points", # https://des.az.gov/liheap). Portable Fuel is propane, wood, coal, generator -# fuel and pellets (DES LIHEAP Policy rev. 13, p. 5, +# fuel and pellets (DES LIHEAP Policy rev. 16A, p. 5, # https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=5), # modeled as heating_type PROPANE, WOOD or COAL. Annual bills over annual # income give the same ratio as the monthly figures; a band top belongs to its @@ -403,3 +403,34 @@ az_liheap_energy_cost: 3_000 # 3,000 / 24,000 = 0.125: 11%-15% band; electric heat, no bonus. az_liheap_energy_burden_points: 2 + +- name: Case 18, no energy bill with no income scores 0 burden points. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 0 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + heat_expense_included_in_rent: true + pre_subsidy_electricity_expense: 0 + gas_expense: 0 + snap: 0 + tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + az_liheap_countable_income: 0 + az_liheap_energy_cost: 0 + # Energy in rent with no bill is no burden even though 0 / 0 is + # undefined; without the zero-cost guard it would take the 6-point band. + az_liheap_energy_burden_points: 0 + # Income 0 / 33,684 = 0 -> 5; burden 0; need 0; total 5 -> 320 (11 points + # and 480 without the guard). + az_liheap_points: 5 + az_liheap: 320 diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/points/az_liheap_energy_need_points.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/points/az_liheap_energy_need_points.yaml index 99222965c58..20f1a2a6261 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/points/az_liheap_energy_need_points.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/points/az_liheap_energy_need_points.yaml @@ -3,7 +3,7 @@ # a child aged 6 or younger (LIHEAP Eligibility Worksheet, $640 Limit, eff. # 2024-10-12, section 3, # https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1; -# DES LIHEAP Policy rev. 13, p. 10 VI.B.1.d "Aged six (6) and under; Aged 60 +# DES LIHEAP Policy rev. 16A, p. 10 VI.B.1.d "Aged six (6) and under; Aged 60 # and over; A veteran; A person with a disability", # https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=10). # Disability (policy p. 4): an ADA impairment, or categorically a recipient of diff --git a/policyengine_us/variables/gov/states/az/des/liheap/az_liheap.py b/policyengine_us/variables/gov/states/az/des/liheap/az_liheap.py index a4e89f5800b..282b9c3586e 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/az_liheap.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/az_liheap.py @@ -23,7 +23,7 @@ class az_liheap(Variable): "https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1", # PDF pages 4, 10 "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=4", - # PDF pages 10, 29-30, 33-34 + # PDF pages 10, 31, 34-35, 41 "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=10", "https://des.az.gov/liheap", ) diff --git a/policyengine_us/variables/gov/states/az/des/liheap/az_liheap_energy_cost.py b/policyengine_us/variables/gov/states/az/des/liheap/az_liheap_energy_cost.py index 46860ed95f0..69687c93e1e 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/az_liheap_energy_cost.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/az_liheap_energy_cost.py @@ -17,12 +17,14 @@ class az_liheap_energy_cost(Variable): # "Energy Utility: A service or Portable Fuel that provides a source of # heating or cooling for a Household. Eligible Energy Utility service - # providers include vendors of electricity, gas, or portable fuel" (page 4); - # "Portable Fuel: Energy that is derived from propane, wood, coal, fuel for - # gas generators, and/or pellets" (page 5). Fuel oil and other heating fuel - # bills are purchased home energy and are counted with them. The electric - # bill is read before subsidies, as heating_expense does, so the burden is - # gross of discount programs and no subsidy chain feeds back into LIHEAP. + # providers include vendors of electricity or gas; Portable Fuel for + # purposes of heating or cooling" (page 4); "Portable Fuel: Energy that is + # derived from propane, wood, coal, fuel for gas generators, and/or pellets + # and that is used for heating or cooling a residence" (page 5). Fuel oil + # and other heating fuel bills are purchased home energy and are counted + # with them. The electric bill is read before subsidies, as heating_expense + # does, so the burden is gross of discount programs and no subsidy chain + # feeds back into LIHEAP. adds = [ "pre_subsidy_electricity_expense", "gas_expense", diff --git a/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_categorically_eligible.py b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_categorically_eligible.py index 7d963c806a4..c9ac6f12515 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_categorically_eligible.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_categorically_eligible.py @@ -19,8 +19,8 @@ def formula(spm_unit, period, parameters): # Section VI.A.6.b.i: "If one (1) Household member's current enrollment # in Cash Assistance or Nutrition Assistance is verified, the Household # will be considered categorically eligible." A positive modeled - # benefit in the year stands for current enrollment; the MONTH-defined - # snap and az_tanf are summed over the year by add(). SSI and - # veterans programs do not confer categorical eligibility (plan - # Section 1.4). + # benefit in the year stands for current enrollment; add() sums the + # MONTH-defined snap over the year, and the annual tanf includes + # Arizona Cash Assistance. Both are take-up gated. SSI and veterans + # programs do not confer categorical eligibility (plan Section 1.4). return add(spm_unit, period, p.categorical_programs) > 0 diff --git a/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_eligible.py b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_eligible.py index 902b67080ca..79182279951 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_eligible.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_eligible.py @@ -15,7 +15,7 @@ class az_liheap_eligible(Variable): "have no inputs and are not modeled." ) reference = ( - # PDF pages 4, 6-8, 32 + # PDF pages 4-8, 33 "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=4", "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=9", ) @@ -30,7 +30,7 @@ def formula(spm_unit, period, parameters): categorically_eligible = spm_unit("az_liheap_categorically_eligible", period) income_eligible = spm_unit("az_liheap_income_eligible", period) # Section VI.A.1: "An Applicant must require assistance with payment of - # energy-related expenses." The Household definition (page 4) includes + # energy-related expenses." The Household definition (page 5) includes # members "who make undesignated payments for Energy Utilities in the # form of rent", and Section VII.B.3.a.ii.B pays the applicant directly # when "Energy Utilities are included within the cost of their rent". diff --git a/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.py b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.py index 7947707b470..cda9c9f6718 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.py @@ -8,7 +8,7 @@ class az_liheap_income_eligible(Variable): label = "Arizona LIHEAP income eligible" documentation = "Countable household income does not exceed the Appendix A income limit for the eligible household size." reference = ( - # PDF pages 8, 37 + # PDF pages 8, 39 "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8", "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=9", ) diff --git a/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_limit.py b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_limit.py index 7dbd3fae776..b2a8bf34149 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_limit.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/eligibility/az_liheap_income_limit.py @@ -16,8 +16,9 @@ class az_liheap_income_limit(Variable): "monthly dollars." ) reference = ( - # PDF pages 8, 37 + # PDF pages 8, 39 "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8", + "https://web.archive.org/web/20251030203752/https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=37", "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=9", "https://liheapch.acf.gov/profiles/povertytables/FY2026/azsmi.htm", "https://liheapch.acf.gov/profiles/povertytables/FY2026/popstate.htm", @@ -31,18 +32,19 @@ def formula(spm_unit, period, parameters): state_group = spm_unit.household("state_group_str", period) # Section VI.A.6.c.i: "Gross Income must be at or below the greater of: # A) 60% of the SMI; or B) 150% of the FPL." Appendix A prints the - # FFY2026 monthly result for sizes 1 to 12 plus $687 per additional - # person. Its SMI arm reproduces only with the shared HHS floor order, - # the four-person 60% amount first (64,789 from 107,983) and then the - # size-adjusted annual amount, followed by a floor of the monthly - # amount; this is a reconciled numerical pattern, not an explicit - # rounding rule. + # monthly result for sizes 1 to 12 plus an amount per additional person + # ($687 for FFY2026 in revision 13, $710 for FFY2027 in revision 16A). + # Its SMI arm reproduces only with the shared HHS floor order, the + # four-person 60% amount first (64,789 from 107,983 for FFY2026) and + # then the size-adjusted annual amount, followed by a floor of the + # monthly amount; this is a reconciled numerical pattern, not an + # explicit rounding rule. smi_annual = liheap_smi_limit(size, state, p.smi_rate, period, parameters) smi_monthly = np.floor(smi_annual / MONTHS_IN_YEAR) # The FPL arm uses the guideline in force when the season's limits were # set (the 2025 guideline for FFY2026) and floors the monthly amount. - # Appendix A's "EACH ADDL OVER 12 +$687" is the floored monthly value of - # 150% of the additional-person guideline, applied beyond the table. + # Appendix A's "EACH ADDL OVER 12" amount is the floored monthly value + # of 150% of the additional-person guideline, applied beyond the table. capped_size = clip(size, 1, p.max_table_size) guideline = fpg( capped_size, state_group, period, parameters, year_lag=p.fpg_year_lag diff --git a/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_earned_income.py b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_earned_income.py index 6e0c7691bd4..6c41fdb222e 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_earned_income.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_earned_income.py @@ -14,7 +14,7 @@ class az_liheap_countable_earned_income(Variable): ) unit = USD reference = ( - # PDF pages 8-9, 13 + # PDF pages 8-9, 13-14 "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=8", # PDF pages 6-7 "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=6", @@ -23,15 +23,17 @@ class az_liheap_countable_earned_income(Variable): def formula(person, period, parameters): p = parameters(period).gov.states.az.des.liheap.income - # Section VII.A.1.c.ii.B verifies "Profit or loss documentation from - # self-employment" without saying whether a loss offsets other income, - # so each net source is floored at zero (project convention). + # Section VII.A.1.c.ii.B counts "Income from self-employment or + # sole-proprietorship" and Section VII.A.1.c.iii.B.1 accepts a + # "Schedule C (Profit and Loss Statement)", without saying whether a + # loss offsets other income, so each net source is floored at zero + # (project convention). earned = 0 for source in p.sources.earned: earned = earned + max_(person(source, period), 0) # Section VI.A.6.d.ii counts "Income from all Household members aged 18 # and older"; Section VI.A.6.e.ii.E excludes income of members under 18 - # in revision 13 and their earned income in revision 15, and the plan's + # in revision 13 and their earned income from revision 15, and the plan's # Section 1.9 leaves "Earned income of a child under the age of 18" # unchecked, so under-18 earnings never count. age = person("age", period) diff --git a/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_income.py b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_income.py index c29821170ce..233511a0cef 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_income.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_income.py @@ -14,7 +14,7 @@ class az_liheap_countable_income(Variable): ) unit = USD reference = ( - # PDF pages 4, 8-9, 14, 31 + # PDF pages 4, 8-9, 14, 32 "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=4", # PDF pages 5-7 "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=5", @@ -26,13 +26,15 @@ class az_liheap_countable_income(Variable): # twelve is the policy's own arithmetic. Cash Assistance is a household # benefit with no person-level attribution, so it is summed here rather # than in the person-level unearned source list: Section VI.A.6.e.ii.B - # counts "SSA and TANF benefits", and Section VII.B.1.c.ii adds "the Cash - # Assistance benefit" to the reported income of households categorically - # eligible through it. The policy's narrower base for categorically - # eligible households (the enrolled member's reported income) is not - # modeled; the whole household's countable income is used. + # counts "Social Security and TANF benefits", and Section VII.B.1.c.ii adds + # "the Cash Assistance benefit" to the reported income of households + # categorically eligible through it. The take-up-gated tanf includes + # Arizona Cash Assistance, so a family that does not take it up has none + # counted. The policy's narrower base for categorically eligible + # households (the enrolled member's reported income) is not modeled; the + # whole household's countable income is used. adds = [ "az_liheap_countable_earned_income", "az_liheap_countable_unearned_income", - "az_tanf", + "tanf", ] diff --git a/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.py b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.py index 344b48bd14e..eaabede8e6b 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.py @@ -9,14 +9,16 @@ class az_liheap_countable_unearned_income(Variable): documentation = ( "Gross unearned income of a household member: counted in full for " "members aged 18 and older, and for younger members only from policy " - "revision 15 (March 23, 2026). Social Security is counted gross; the " - "policy's Medicare premium subtraction is not applied. Cash Assistance " - "is a household benefit added in az_liheap_countable_income." + "revision 15 (March 23, 2026). Each source is floored at zero, so a " + "rental loss does not offset other income, and Social Security is " + "counted net of the Medicare Part B premium the person pays. Cash " + "Assistance is a household benefit added in az_liheap_countable_income." ) unit = USD reference = ( - # PDF pages 4, 8-9, 13-14 + # PDF pages 4, 8-9, 14 "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=4", + "https://web.archive.org/web/20251030203752/https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=9", # PDF pages 6-7 "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=6", ) @@ -24,14 +26,30 @@ class az_liheap_countable_unearned_income(Variable): def formula(person, period, parameters): p = parameters(period).gov.states.az.des.liheap.income - # add() sums the MONTH-defined ssi over the year with the YEAR sources. - unearned = add(person, period, p.sources.unearned) + # Gross Income is "The total Household income for all Household + # members before taxes and other deductions" (page 4), and a loss is + # not income, so each source is floored at zero and a rental loss + # cannot offset another source. ADD sums the MONTH-defined ssi over + # the year. + unearned = 0 + for source in p.sources.unearned: + unearned = unearned + max_(person(source, period, options=[ADD]), 0) + # Section VII.A.1.c.ii.D note: "Expenses related to Medicare (such as + # premiums) should be subtracted when determining gross income", and + # plan Section 1.9 counts SSA benefits "Excluding MediCare deduction". + # The premium is netted from the Social Security benefit it is + # withheld from. medicare_part_b_premium is zero for a person who is + # not enrolled and excludes the share a Medicare Savings Program pays; + # no Part D premium input exists. + premium = person("medicare_part_b_premium", period) + social_security = max_(person("social_security", period) - premium, 0) age = person("age", period) # Revision 13, Section VI.A.6.e.ii.E: "Income from Household members # under the age of 18 is not considered Countable Income." Revision 15 # narrows this to "Earned income from Household members under the age - # of 18". Counting a child's unearned income from March 23, 2026 is an - # inference from that narrowing; Section VI.A.6.d.ii still counts the - # income of members aged 18 and older. + # of 18", as revision 16A still reads. Counting a child's unearned + # income from March 23, 2026 is an inference from that narrowing; + # Section VI.A.6.d.ii still counts the income of members aged 18 and + # older. counted = (age >= p.adult_age) | p.child_unearned_income_counted - return where(counted, unearned, 0) + return where(counted, unearned + social_security, 0) diff --git a/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_energy_burden_points.py b/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_energy_burden_points.py index d289c3b0d1e..c2068d6fc4a 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_energy_burden_points.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_energy_burden_points.py @@ -16,7 +16,7 @@ class az_liheap_energy_burden_points(Variable): reference = ( "https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1", "https://des.az.gov/liheap", - # PDF pages 5, 10 + # PDF pages 5, 10, 40 "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=5", ) defined_for = StateCode.AZ diff --git a/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_energy_need_points.py b/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_energy_need_points.py index 84a86288a1f..9a46c32fe26 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_energy_need_points.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_energy_need_points.py @@ -15,7 +15,7 @@ class az_liheap_energy_need_points(Variable): ) reference = ( "https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1", - # PDF pages 4, 10 + # PDF pages 4, 10, 41 "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=4", "https://liheapch.acf.gov/docs/2026/state-plans/AZ_Plan_2026.pdf#page=10", ) diff --git a/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_income_points.py b/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_income_points.py index 90dde337a25..e51a34f5ffb 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_income_points.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/points/az_liheap_income_points.py @@ -15,6 +15,7 @@ class az_liheap_income_points(Variable): reference = ( "https://liheapch.acf.gov/docs/2026/benefits-matricies/AZ_BenefitMatrix_Heating_2026.pdf#page=1", "https://des.az.gov/liheap", + # PDF pages 10, 40 "https://des.az.gov/sites/default/files/dl/CCSD-LIHEAP-Policy.pdf#page=10", ) defined_for = StateCode.AZ From 406a5efe6a988709fb166a54fb71097e2db7a36b Mon Sep 17 00:00:00 2001 From: Ziming Date: Fri, 23 Oct 2026 20:42:02 -0400 Subject: [PATCH 7/9] Deduct Medicare premiums from household gross income Revision 16A VII.A.1.c.ii.D: "Expenses related to Medicare (such as premiums) should be subtracted when determining gross income." Count Social Security gross and subtract members' Part B premiums in az_liheap_countable_income. Co-Authored-By: Claude Opus 5.5 --- .../des/liheap/income/sources/unearned.yaml | 7 +- .../gov/states/az/des/liheap/az_liheap.yaml | 12 ++- .../az_liheap_income_eligible.yaml | 14 ++++ .../income/az_liheap_countable_income.yaml | 73 ++++++++++++++++++- .../az_liheap_countable_unearned_income.yaml | 63 ++++------------ .../gov/states/az/des/liheap/integration.yaml | 2 +- .../income/az_liheap_countable_income.py | 59 ++++++++++----- .../az_liheap_countable_unearned_income.py | 22 +++--- 8 files changed, 162 insertions(+), 90 deletions(-) diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml index 941dd0bbea6..44fe35499cc 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml @@ -4,13 +4,14 @@ description: Arizona uses these person-level unearned income sources to determin # income", "Income from Unemployment Insurance" and "Consistent income received # from other sources"; the plan's Section 1.9 checks the full list. Each source # is floored at zero in the formula, so a rental loss does not offset other -# income. Social Security is added in the formula net of the Medicare Part B -# premium. Cash Assistance (tanf) is a household benefit and is added in -# az_liheap_countable_income. +# income. Cash Assistance (tanf) is a household benefit and is added in +# az_liheap_countable_income, which also subtracts the household's Medicare +# Part B premiums. values: 2025-08-25: - total_unemployment_compensation - strike_benefits + - social_security - ssi - pension_income # Section VI.A.6.e.ii.B "retirement income": military retirement pay and diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap.yaml index e00959ac381..d3eba53b8e1 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/az_liheap.yaml @@ -353,11 +353,15 @@ - name: Case 14, a railroad retiree's annuity puts a single person over the limit and pays nothing. period: 2026 + absolute_error_margin: 0.01 input: people: person1: age: 70 railroad_benefits: 40_000 + # Income of 3,333 a month is above the Medicare Savings Program + # limits, so the retiree pays the 2026 standard premium of 202.90 x 12. + medicare_part_b_premium: 2_434.80 ssi: 0 spm_units: spm_unit: @@ -371,12 +375,12 @@ members: [person1] state_code: AZ output: - # Railroad Retirement is "retirement income" (policy p. 9 VI.A.6.e.ii.B): - # countable income 40,000. - az_liheap_countable_income: 40_000 + # Railroad Retirement is "retirement income" (policy p. 9 VI.A.6.e.ii.B), + # less the Part B premium (p. 14 note): 40,000 - 2,434.80 = 37,565.20. + az_liheap_countable_income: 37_565.20 # Appendix A size 1: 2,807 x 12 = 33,684. az_liheap_income_limit: 33_684 - # 40,000 > 33,684. + # 37,565.20 > 33,684. az_liheap_income_eligible: false # No SNAP or Cash Assistance. az_liheap_categorically_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.yaml index 3affe8179bb..b4514cdb7ee 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/eligibility/az_liheap_income_eligible.yaml @@ -161,6 +161,9 @@ employment_income: 42_716 person3: age: 66 + # No income of their own, so a Medicare Savings Program pays the + # Part B premium and nothing is subtracted. + medicare_part_b_premium: 0 ssi: 0 person4: age: 17 @@ -206,6 +209,9 @@ employment_income: 42_717 person3: age: 66 + # No income of their own, so a Medicare Savings Program pays the + # Part B premium and nothing is subtracted. + medicare_part_b_premium: 0 ssi: 0 person4: age: 17 @@ -247,9 +253,13 @@ employment_income: 52_460 person3: age: 70 + # No income of their own, so a Medicare Savings Program pays the + # Part B premiums of both grandparents and nothing is subtracted. + medicare_part_b_premium: 0 ssi: 0 person4: age: 68 + medicare_part_b_premium: 0 ssi: 0 person5: age: 17 @@ -298,9 +308,13 @@ employment_income: 52_461 person3: age: 70 + # No income of their own, so a Medicare Savings Program pays the + # Part B premiums of both grandparents and nothing is subtracted. + medicare_part_b_premium: 0 ssi: 0 person4: age: 68 + medicare_part_b_premium: 0 ssi: 0 person5: age: 17 diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_income.yaml index c4024ff861c..1cc0aea4a02 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_income.yaml @@ -8,6 +8,11 @@ # az_tanf is a monthly variable; in a yearly test its input is the annual # total. Arizona's 2026 payment standard for a parent and one child is 347 a # month: 347 x 12 = 4,164. +# Members' out-of-pocket Medicare Part B premiums are subtracted from the +# household total, floored at zero (policy p. 14 VII.A.1.c.ii.D note: +# "Expenses related to Medicare (such as premiums) should be subtracted +# when determining gross income"). 2026 Part B standard premium 202.90 a +# month: 202.90 x 12 = 2,434.80. - name: Case 1, TANF received by the unit is added to the parent's wages. period: 2026 @@ -44,8 +49,9 @@ person2: age: 67 social_security: 18_000 - # Her own 1,500 a month is within the Medicare Savings Program limits, - # so the program pays her Part B premium and nothing is netted. + # Their own 1,500 a month is within the Medicare Savings Program + # limits, so the program pays the Part B premium and nothing is + # subtracted. medicare_part_b_premium: 0 ssi: 0 person3: @@ -67,3 +73,66 @@ # 26,000 + 2,400 + 18,000 = 46,400; the 15-year-old's 3,000 is excluded # (rev. 13 p. 9 E). az_liheap_countable_income: 46_400 + +- name: Case 3, subtracting the Medicare Part B premium brings a retiree under the income limit. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 67 + social_security: 34_000 + # Income of 2,833 a month is above the Medicare Savings Program + # limits, so the retiree pays the 2,434.80 standard premium. + medicare_part_b_premium: 2_434.80 + ssi: 0 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + snap: 0 + tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # Social Security counts gross at the person level. + az_liheap_countable_unearned_income: 34_000 + # 34,000 - 2,434.80 = 31,565.20. + az_liheap_countable_income: 31_565.20 + # 31,565.20 <= 33,684; gross income of 34,000 would be over it. + az_liheap_income_eligible: true + # Income 31,565.20 / 33,684 = 0.937 -> 1; burden 1,800 / 31,565.20 = + # 0.057 -> 1; elderly 1; total 3 -> 160. + az_liheap_points: 3 + az_liheap: 160 + +- name: Case 4, the Medicare Part B premium is subtracted from a pension with no Social Security. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 67 + pension_income: 24_000 + # Income of 2,000 a month is above the Medicare Savings Program + # limits, so the retiree pays the 2,434.80 standard premium. + medicare_part_b_premium: 2_434.80 + ssi: 0 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_600 + tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + az_liheap_countable_unearned_income: 24_000 + # 24,000 - 2,434.80 = 21,565.20: the premium comes off household income + # whatever the source it is paid from. + az_liheap_countable_income: 21_565.20 diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.yaml index f34800d0201..8048373396b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.yaml @@ -10,17 +10,15 @@ # Members under 18: rev. 13 p. 9 E counts none of their income; rev. 15 # (eff. 2026-03-23) p. 9 E excludes only their earned income, so a child's # unearned income counts from period 2027 (parameters are read at the start -# of the period). Social Security counts net of the member's out-of-pocket -# Medicare Part B premium (policy p. 14 VII.A.1.c.ii.D note; plan 1.9 -# "Excluding MediCare deduction"). Social Security after the premium, rental -# income and farm rental income are each floored at zero per person, so a -# premium or a loss does not offset other income. +# of the period). Each source is floored at zero per person, so a rental or +# farm rental loss does not offset other income. Social Security counts +# gross here: the Medicare Part B premium (policy p. 14 VII.A.1.c.ii.D note) +# comes off household income in az_liheap_countable_income. # 2026 Part B standard premium 202.90 a month: 202.90 x 12 = 2,434.80. # 2026 SSI federal benefit rate 994 a month: 994 x 12 = 11,928. -- name: Case 1, a retiree's Social Security net of the Medicare Part B premium and pension income are counted. +- name: Case 1, a retiree's Social Security and pension income are counted gross of the Medicare premium. period: 2026 - absolute_error_margin: 0.01 input: people: person1: @@ -41,9 +39,9 @@ members: [person1] state_code: AZ output: - # (23,712 - 2,434.80) + 6,000 = 27,277.20 (plan 1.9: SSA and - # retirement/pension checked). - az_liheap_countable_unearned_income: 27_277.20 + # 23,712 + 6,000 = 29,712 (plan 1.9: SSA and retirement/pension + # checked); the premium is not deducted at the person level. + az_liheap_countable_unearned_income: 29_712 - name: Case 2, unemployment, child support, alimony and rent are counted. period: 2026 @@ -174,11 +172,15 @@ - name: Case 7, a Railroad Retirement annuity is counted as retirement income. period: 2026 + absolute_error_margin: 0.01 input: people: person1: age: 68 railroad_benefits: 30_000 + # Income of 2,500 a month is above the Medicare Savings Program + # limits, so the retiree pays the 2,434.80 standard premium. + medicare_part_b_premium: 2_434.80 ssi: 0 spm_units: spm_unit: @@ -194,7 +196,8 @@ # Policy p. 9 VI.A.6.e.ii.B "retirement income", p. 14 "retirement # benefits"; plan 1.9 "Retirement / pension benefits" checked: 30,000. az_liheap_countable_unearned_income: 30_000 - az_liheap_countable_income: 30_000 + # Household income less the Part B premium: 30,000 - 2,434.80. + az_liheap_countable_income: 27_565.20 - name: Case 8, long-term disability insurance paid to the insured worker is counted. period: 2026 @@ -291,41 +294,7 @@ az_liheap_countable_unearned_income: 14_400 az_liheap_countable_income: 14_400 -- name: Case 12, netting the Medicare Part B premium brings a retiree under the income limit. - period: 2026 - absolute_error_margin: 0.01 - input: - people: - person1: - age: 67 - social_security: 34_000 - # Income of 2,833 a month is above the Medicare Savings Program - # limits, so the retiree pays the 2,434.80 standard premium. - medicare_part_b_premium: 2_434.80 - ssi: 0 - spm_units: - spm_unit: - members: [person1] - heating_type: ELECTRICITY - pre_subsidy_electricity_expense: 1_800 - snap: 0 - tanf: 0 - households: - household: - members: [person1] - state_code: AZ - output: - # 34,000 - 2,434.80 = 31,565.20 (policy p. 14 VII.A.1.c.ii.D note). - az_liheap_countable_unearned_income: 31_565.20 - az_liheap_countable_income: 31_565.20 - # 31,565.20 <= 33,684; gross Social Security of 34,000 would be over it. - az_liheap_income_eligible: true - # Income 31,565.20 / 33,684 = 0.937 -> 1; burden 1,800 / 31,565.20 = - # 0.057 -> 1; elderly 1; total 3 -> 160. - az_liheap_points: 3 - az_liheap: 160 - -- name: Case 13, a rental loss does not offset wages. +- name: Case 12, a rental loss does not offset wages. period: 2026 input: people: @@ -353,7 +322,7 @@ az_liheap_eligible: false az_liheap: 0 -- name: Case 14, farm rent and gambling winnings are counted as unearned income. +- name: Case 13, farm rent and gambling winnings are counted as unearned income. period: 2026 input: people: diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/integration.yaml index 926d056feac..a554e5c5e30 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/integration.yaml @@ -89,7 +89,7 @@ state_code: AZ output: az_liheap_household_size: 1 - # Social Security net of the Part B premium (policy p. 14 note): + # Gross income less the Part B premium (policy p. 14 note): # 36,000 - 2,434.80 = 33,565.20. az_liheap_countable_income: 33_565.20 # Appendix A size 1: 2,807 x 12. diff --git a/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_income.py b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_income.py index 233511a0cef..2a0d9d70d7d 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_income.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_income.py @@ -8,9 +8,13 @@ class az_liheap_countable_income(Variable): label = "Arizona LIHEAP countable income" documentation = ( "Annual gross household income counted under Arizona LIHEAP, including " - "the income of members who are not citizens or qualified noncitizens. " - "Annual amounts stand for twelve times the policy's 30-day income " - "snapshot." + "the income of members who are not citizens or qualified noncitizens, " + "less each member's out-of-pocket Medicare Part B premium and floored " + "at zero. Policy VII.A.1.c.ii.D (CCSD-LIHEAP-Policy.pdf#page=14): " + "'Expenses related to Medicare (such as premiums) should be subtracted " + "when determining gross income'; Part D has no premium variable and is " + "not deducted. Annual amounts stand for twelve times the policy's " + "30-day income snapshot." ) unit = USD reference = ( @@ -21,20 +25,35 @@ class az_liheap_countable_income(Variable): ) defined_for = StateCode.AZ - # Section VII.A.1.c.v annualizes the 30-day snapshot as "([Average Gross - # Income per Pay Period X Income Frequency] / 12)", so annual income over - # twelve is the policy's own arithmetic. Cash Assistance is a household - # benefit with no person-level attribution, so it is summed here rather - # than in the person-level unearned source list: Section VI.A.6.e.ii.B - # counts "Social Security and TANF benefits", and Section VII.B.1.c.ii adds - # "the Cash Assistance benefit" to the reported income of households - # categorically eligible through it. The take-up-gated tanf includes - # Arizona Cash Assistance, so a family that does not take it up has none - # counted. The policy's narrower base for categorically eligible - # households (the enrolled member's reported income) is not modeled; the - # whole household's countable income is used. - adds = [ - "az_liheap_countable_earned_income", - "az_liheap_countable_unearned_income", - "tanf", - ] + def formula(spm_unit, period, parameters): + # Section VII.A.1.c.v annualizes the 30-day snapshot as "([Average + # Gross Income per Pay Period X Income Frequency] / 12)", so annual + # income over twelve is the policy's own arithmetic. Cash Assistance + # is a household benefit with no person-level attribution, so it is + # summed here rather than in the person-level unearned source list: + # Section VI.A.6.e.ii.B counts "Social Security and TANF benefits", and + # Section VII.B.1.c.ii adds "the Cash Assistance benefit" to the + # reported income of households categorically eligible through it. The + # take-up-gated tanf includes Arizona Cash Assistance, so a family that + # does not take it up has none counted. The policy's narrower base for + # categorically eligible households (the enrolled member's reported + # income) is not modeled; the whole household's countable income is + # used. + gross_income = add( + spm_unit, + period, + [ + "az_liheap_countable_earned_income", + "az_liheap_countable_unearned_income", + "tanf", + ], + ) + # Section VII.A.1.c.ii.D note (CCSD-LIHEAP-Policy.pdf#page=14): + # "Expenses related to Medicare (such as premiums) should be + # subtracted when determining gross income." Plan Section 1.9 counts + # SSA benefits "Excluding MediCare deduction". medicare_part_b_premium + # is each member's out-of-pocket Part B premium, zero for a member who + # is not enrolled or whose premium a Medicare Savings Program pays; no + # Part D premium variable exists. + medicare_premiums = add(spm_unit, period, ["medicare_part_b_premium"]) + return max_(gross_income - medicare_premiums, 0) diff --git a/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.py b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.py index eaabede8e6b..f81d7d1bbe5 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.py @@ -10,9 +10,10 @@ class az_liheap_countable_unearned_income(Variable): "Gross unearned income of a household member: counted in full for " "members aged 18 and older, and for younger members only from policy " "revision 15 (March 23, 2026). Each source is floored at zero, so a " - "rental loss does not offset other income, and Social Security is " - "counted net of the Medicare Part B premium the person pays. Cash " - "Assistance is a household benefit added in az_liheap_countable_income." + "rental loss does not offset other income. Social Security counts " + "gross here; az_liheap_countable_income adds Cash Assistance, a " + "household benefit, and subtracts the household's Medicare Part B " + "premiums under the Policy VII.A.1.c.ii.D note." ) unit = USD reference = ( @@ -34,15 +35,10 @@ def formula(person, period, parameters): unearned = 0 for source in p.sources.unearned: unearned = unearned + max_(person(source, period, options=[ADD]), 0) - # Section VII.A.1.c.ii.D note: "Expenses related to Medicare (such as - # premiums) should be subtracted when determining gross income", and - # plan Section 1.9 counts SSA benefits "Excluding MediCare deduction". - # The premium is netted from the Social Security benefit it is - # withheld from. medicare_part_b_premium is zero for a person who is - # not enrolled and excludes the share a Medicare Savings Program pays; - # no Part D premium input exists. - premium = person("medicare_part_b_premium", period) - social_security = max_(person("social_security", period) - premium, 0) + # Social Security counts gross here. The Section VII.A.1.c.ii.D note + # (CCSD-LIHEAP-Policy.pdf#page=14), "Expenses related to Medicare (such + # as premiums) should be subtracted when determining gross income", is + # applied to household gross income in az_liheap_countable_income. age = person("age", period) # Revision 13, Section VI.A.6.e.ii.E: "Income from Household members # under the age of 18 is not considered Countable Income." Revision 15 @@ -52,4 +48,4 @@ def formula(person, period, parameters): # Section VI.A.6.d.ii still counts the income of members aged 18 and # older. counted = (age >= p.adult_age) | p.child_unearned_income_counted - return where(counted, unearned + social_security, 0) + return where(counted, unearned, 0) From e1fd45905ebc6e7e73c4f53cee76b03d3201ccb0 Mon Sep 17 00:00:00 2001 From: Ziming Date: Mon, 26 Oct 2026 14:11:22 -0400 Subject: [PATCH 8/9] Deduct Part A premiums; count gambling profits - Subtract Medicare Part A premiums with Part B (Policy VII.A.1.c.ii.D note) - Count gambling winnings less gambling losses, floored at zero ("gambling profits") - Move the registry entry after AR and keep the coverage line as on main; PR 10303 adds AZ there Co-Authored-By: Claude Opus 5.5 --- .../des/liheap/income/sources/unearned.yaml | 10 +++------- policyengine_us/programs.yaml | 18 ++++++++--------- .../income/az_liheap_countable_income.py | 20 ++++++++++++------- .../az_liheap_countable_unearned_income.py | 20 ++++++++++++++----- 4 files changed, 40 insertions(+), 28 deletions(-) diff --git a/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml index 44fe35499cc..fd82e590676 100644 --- a/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/az/des/liheap/income/sources/unearned.yaml @@ -4,9 +4,10 @@ description: Arizona uses these person-level unearned income sources to determin # income", "Income from Unemployment Insurance" and "Consistent income received # from other sources"; the plan's Section 1.9 checks the full list. Each source # is floored at zero in the formula, so a rental loss does not offset other -# income. Cash Assistance (tanf) is a household benefit and is added in +# income. Gambling profits (winnings less losses) are added in the formula. +# Cash Assistance (tanf) is a household benefit and is added in # az_liheap_countable_income, which also subtracts the household's Medicare -# Part B premiums. +# Part A and Part B premiums. values: 2025-08-25: - total_unemployment_compensation @@ -35,11 +36,6 @@ values: - interest_income - ordinary_dividend_income - general_assistance - # Section VI.A.6.e.ii.B counts "gambling profits" (page 9). Plan Section 1.9 - # leaves one-time lump sums such as "winnings from lotteries" unchecked - # (page 6); gambling_winnings does not separate one-time winnings, so all - # are counted. - - gambling_winnings # Employment disability insurance: plan Section 1.9 checks "Insurance # payments made directly to the insured" (page 6), and Gross Income is # "The total Household income for all Household members before taxes and diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index a7b10e6bd74..29836b75729 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -987,7 +987,7 @@ programs: category: Benefits agency: HHS status: partial - coverage: AR, AL, DC, GA, Riverside County, MA, FL, IL, AZ, IN, KS, KY, MD, MS, NC, NE, NJ, NY, OH, OK, OR, SD, RI, TX + coverage: AR, AL, DC, GA, Riverside County, MA, FL, IL, IN, KS, KY, MD, MS, NC, NE, NJ, NY, OH, OK, OR, SD, RI, TX state_implementations: - state: AL status: partial @@ -1004,6 +1004,14 @@ programs: variable: ar_liheap parameter_prefix: gov.states.ar.ee.aeo.liheap notes: FY2025-FY2026 regular heating matrices and income limits are checked; income rules follow the FFY 2025 manual (no FY2026 manual was found), and other years are unverified. Crisis, cooling, weatherization and equipment assistance are out of scope. + - state: AZ + status: partial + name: Arizona LIHEAP + full_name: Arizona Low Income Home Energy Assistance Program + variable: az_liheap + parameter_prefix: gov.states.az.des.liheap + verified_years: "2026" + notes: FY2026 Standard benefit covering heating and cooling bills from the point-based benefit matrix. Crisis assistance, water for evaporative cooling and weatherization are out of scope. - state: GA status: partial name: Georgia LIHEAP @@ -1060,14 +1068,6 @@ programs: full_name: Illinois Low Income Home Energy Assistance Program variable: il_liheap parameter_prefix: gov.states.il.dceo.liheap - - state: AZ - status: partial - name: Arizona LIHEAP - full_name: Arizona Low Income Home Energy Assistance Program - variable: az_liheap - parameter_prefix: gov.states.az.des.liheap - verified_years: "2026" - notes: FY2026 Standard benefit covering heating and cooling bills from the point-based benefit matrix. Crisis assistance, water for evaporative cooling and weatherization are out of scope. - state: IN status: partial name: Indiana EAP diff --git a/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_income.py b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_income.py index 2a0d9d70d7d..a9e775fe076 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_income.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_income.py @@ -9,8 +9,9 @@ class az_liheap_countable_income(Variable): documentation = ( "Annual gross household income counted under Arizona LIHEAP, including " "the income of members who are not citizens or qualified noncitizens, " - "less each member's out-of-pocket Medicare Part B premium and floored " - "at zero. Policy VII.A.1.c.ii.D (CCSD-LIHEAP-Policy.pdf#page=14): " + "less each member's out-of-pocket Medicare Part A and Part B premiums " + "and floored at zero. Policy VII.A.1.c.ii.D " + "(CCSD-LIHEAP-Policy.pdf#page=14): " "'Expenses related to Medicare (such as premiums) should be subtracted " "when determining gross income'; Part D has no premium variable and is " "not deducted. Annual amounts stand for twelve times the policy's " @@ -51,9 +52,14 @@ def formula(spm_unit, period, parameters): # Section VII.A.1.c.ii.D note (CCSD-LIHEAP-Policy.pdf#page=14): # "Expenses related to Medicare (such as premiums) should be # subtracted when determining gross income." Plan Section 1.9 counts - # SSA benefits "Excluding MediCare deduction". medicare_part_b_premium - # is each member's out-of-pocket Part B premium, zero for a member who - # is not enrolled or whose premium a Medicare Savings Program pays; no - # Part D premium variable exists. - medicare_premiums = add(spm_unit, period, ["medicare_part_b_premium"]) + # SSA benefits "Excluding MediCare deduction". The Part A and Part B + # premium variables are each member's out-of-pocket premiums, zero for + # a member who is not enrolled, whose premium a Medicare Savings + # Program pays, or (for Part A) who has premium-free Part A; no Part D + # premium variable exists. + medicare_premiums = add( + spm_unit, + period, + ["medicare_part_a_premium", "medicare_part_b_premium"], + ) return max_(gross_income - medicare_premiums, 0) diff --git a/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.py b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.py index f81d7d1bbe5..7716e5f7fca 100644 --- a/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.py +++ b/policyengine_us/variables/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.py @@ -10,10 +10,11 @@ class az_liheap_countable_unearned_income(Variable): "Gross unearned income of a household member: counted in full for " "members aged 18 and older, and for younger members only from policy " "revision 15 (March 23, 2026). Each source is floored at zero, so a " - "rental loss does not offset other income. Social Security counts " - "gross here; az_liheap_countable_income adds Cash Assistance, a " - "household benefit, and subtracts the household's Medicare Part B " - "premiums under the Policy VII.A.1.c.ii.D note." + "rental loss does not offset other income. Gambling counts as profits: " + "winnings less gambling losses, floored at zero. Social Security " + "counts gross here; az_liheap_countable_income adds Cash Assistance, a " + "household benefit, and subtracts the household's Medicare Part A and " + "Part B premiums under the Policy VII.A.1.c.ii.D note." ) unit = USD reference = ( @@ -35,6 +36,15 @@ def formula(person, period, parameters): unearned = 0 for source in p.sources.unearned: unearned = unearned + max_(person(source, period, options=[ADD]), 0) + # Section VI.A.6.e.ii.B counts "gambling profits" (page 9), so + # gambling losses offset winnings but not other income. Plan Section + # 1.9 leaves one-time lump sums such as "winnings from lotteries" + # unchecked (page 6); the inputs do not separate one-time winnings, so + # all profits are counted. + gambling_profits = max_( + person("gambling_winnings", period) - person("gambling_losses", period), + 0, + ) # Social Security counts gross here. The Section VII.A.1.c.ii.D note # (CCSD-LIHEAP-Policy.pdf#page=14), "Expenses related to Medicare (such # as premiums) should be subtracted when determining gross income", is @@ -48,4 +58,4 @@ def formula(person, period, parameters): # Section VI.A.6.d.ii still counts the income of members aged 18 and # older. counted = (age >= p.adult_age) | p.child_unearned_income_counted - return where(counted, unearned, 0) + return where(counted, unearned + gambling_profits, 0) From e0100838e2ff47e7798e59dd7a1b31367d168094 Mon Sep 17 00:00:00 2001 From: Ziming Date: Mon, 26 Oct 2026 14:54:02 -0400 Subject: [PATCH 9/9] Test the Part A premium and gambling profits - Countable income Case 5: Part A and Part B premiums both come off household income - Countable unearned Case 14 and countable income Case 6: losses offset winnings only, floored at zero, and never reduce wages Co-Authored-By: Claude Opus 5.5 --- .../income/az_liheap_countable_income.yaml | 54 +++++++++++++++++++ .../az_liheap_countable_unearned_income.yaml | 22 ++++++++ 2 files changed, 76 insertions(+) diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_income.yaml index 1cc0aea4a02..7e5886444a7 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_income.yaml @@ -136,3 +136,57 @@ # 24,000 - 2,434.80 = 21,565.20: the premium comes off household income # whatever the source it is paid from. az_liheap_countable_income: 21_565.20 + +- name: Case 5, a retiree's Medicare Part A premium is subtracted along with Part B. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 67 + pension_income: 24_000 + # 30 to 39 quarters of coverage: the 2026 reduced Part A premium + # 311 x 12 = 3,732, paid by the member. + medicare_part_a_premium: 3_732 + medicare_part_b_premium: 2_434.80 + ssi: 0 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_600 + tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + az_liheap_countable_unearned_income: 24_000 + # "Expenses related to Medicare (such as premiums)": 24,000 - 3,732 - + # 2,434.80 = 17,833.20. + az_liheap_countable_income: 17_833.20 + +- name: Case 6, gambling losses above winnings count as zero and do not reduce wages. + period: 2026 + input: + people: + person1: + age: 40 + employment_income: 20_000 + gambling_winnings: 1_000 + gambling_losses: 3_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_700 + tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # max(1,000 - 3,000, 0) = 0 gambling profits. + az_liheap_countable_unearned_income: 0 + # Wages of 20,000 are not reduced by the 2,000 net gambling loss. + az_liheap_countable_income: 20_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.yaml index 8048373396b..9d741f15ad4 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/des/liheap/income/az_liheap_countable_unearned_income.yaml @@ -347,3 +347,25 @@ az_liheap_countable_unearned_income: 6_000 # 22,000 + 6,000 = 28,000. az_liheap_countable_income: 28_000 + +- name: Case 14, gambling losses reduce gambling winnings to the profit. + period: 2026 + input: + people: + person1: + age: 40 + gambling_winnings: 3_000 + gambling_losses: 1_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_700 + tanf: 0 + households: + household: + members: [person1] + state_code: AZ + output: + # Policy VI.A.6.e.ii.B "gambling profits": 3,000 - 1,000 = 2,000. + az_liheap_countable_unearned_income: 2_000