diff --git a/changelog.d/in-eap.added.md b/changelog.d/in-eap.added.md new file mode 100644 index 00000000000..f2934b4a7ef --- /dev/null +++ b/changelog.d/in-eap.added.md @@ -0,0 +1 @@ +Add Indiana Energy Assistance Program (EAP) regular heating assistance and the winter electric allowance. diff --git a/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/amount_per_point.yaml b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/amount_per_point.yaml new file mode 100644 index 00000000000..fd60307690f --- /dev/null +++ b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/amount_per_point.yaml @@ -0,0 +1,10 @@ +description: Indiana provides this regular heating amount per matrix point under the Energy Assistance Program. +values: + 2025-10-01: 25 +metadata: + unit: currency-USD + period: year + label: Indiana EAP amount per point + reference: + - title: Indiana PY2026 EAP Intake and Operations Manual, Section 8.11, page 77 + href: https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=77 diff --git a/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/dwelling_points.yaml b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/dwelling_points.yaml new file mode 100644 index 00000000000..415dadb5bc7 --- /dev/null +++ b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/dwelling_points.yaml @@ -0,0 +1,18 @@ +description: Indiana provides these regular heating points by dwelling type under the Energy Assistance Program. +SINGLE_FAMILY: + 2025-10-01: 3 +MOBILE_HOME: + 2025-10-01: 2 +MULTI_UNIT: + 2025-10-01: 1 +metadata: + unit: /1 + period: year + breakdown: + - in_eap_dwelling_type + label: Indiana EAP dwelling points + reference: + - title: Indiana PY2026 EAP Intake and Operations Manual, Section 8.4, page 71 + href: https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=71 + - title: Indiana PY2025-26 EAP Benefit Matrix, page 1 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/IN_BenefitMatrix_Heat-Crisis_2026.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/electricity/higher_income.yaml b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/electricity/higher_income.yaml new file mode 100644 index 00000000000..0c895588d09 --- /dev/null +++ b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/electricity/higher_income.yaml @@ -0,0 +1,13 @@ +description: Indiana provides this regular electric supplement in the highest income band or when electricity is included in rent under the Energy Assistance Program. +values: + 2025-10-01: 50 +metadata: + unit: currency-USD + period: year + label: Indiana EAP higher income electric payment + reference: + # Page 72 holds the electricity-in-rent rule and page 73 the 45% to 60% chart. + - title: Indiana PY2026 EAP Intake and Operations Manual, Section 8.7, pages 72-73 + href: https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=72 + - title: Indiana PY2025-26 EAP Benefit Matrix, page 1 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/IN_BenefitMatrix_Heat-Crisis_2026.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/electricity/lower_income.yaml b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/electricity/lower_income.yaml new file mode 100644 index 00000000000..c21d369b210 --- /dev/null +++ b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/electricity/lower_income.yaml @@ -0,0 +1,15 @@ +description: Indiana provides this regular electric supplement in the lowest income band under the Energy Assistance Program. +values: + 2024-10-01: 100 + 2025-10-01: 125 +metadata: + unit: currency-USD + period: year + label: Indiana EAP lower income electric payment + reference: + - title: Indiana PY2026 EAP Intake and Operations Manual, Section 8.7, page 72 + href: https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=72 + - title: Indiana PY2025-26 EAP Benefit Matrix, page 1 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/IN_BenefitMatrix_Heat-Crisis_2026.pdf#page=1 + - title: Indiana PY2024-25 EAP Benefit Matrix, page 1 + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/IN_BenefitMatrix_2025.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/electricity/middle_income.yaml b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/electricity/middle_income.yaml new file mode 100644 index 00000000000..fddcb148a73 --- /dev/null +++ b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/electricity/middle_income.yaml @@ -0,0 +1,16 @@ +description: Indiana provides this regular electric supplement in the middle income band under the Energy Assistance Program. +values: + 2024-10-01: 75 + 2025-10-01: 100 +metadata: + unit: currency-USD + period: year + label: Indiana EAP middle income electric payment + reference: + # Page 72 holds the payment amount and page 73 the 30% to 45% chart. + - title: Indiana PY2026 EAP Intake and Operations Manual, Section 8.7, pages 72-73 + href: https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=72 + - title: Indiana PY2025-26 EAP Benefit Matrix, page 1 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/IN_BenefitMatrix_Heat-Crisis_2026.pdf#page=1 + - title: Indiana PY2024-25 EAP Benefit Matrix, page 1 + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/IN_BenefitMatrix_2025.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/fuel_points.yaml b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/fuel_points.yaml new file mode 100644 index 00000000000..f02a613d751 --- /dev/null +++ b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/fuel_points.yaml @@ -0,0 +1,47 @@ +description: Indiana provides these regular heating points by primary fuel under the Energy Assistance Program. +UNSPECIFIED: + 2025-10-01: 0 +# The PY2024-25 matrix awarded zero points for natural gas and electric heat. +ELECTRICITY: + 2024-10-01: 0 + 2025-10-01: 1 +NATURAL_GAS: + 2024-10-01: 0 + 2025-10-01: 1 +FUEL_OIL: + 2025-10-01: 6 +# Section 1.1 bars EAP funds for kerosene, and Section 8.5 does not list it as a +# bulk fuel, so a kerosene-primary household receives the electric allowance only. +KEROSENE: + 2025-10-01: 0 +PROPANE: + 2025-10-01: 6 +WOOD: + 2025-10-01: 6 +COAL: + 2025-10-01: 6 +# Assumption: neither the manual nor the matrix lists solar heat, so it is given +# no fuel points. +SOLAR: + 2025-10-01: 0 +# Assumption: Section 8.5 has no zero-point fuel outside rent. The remaining named +# fuels (wood pellets, corn and biofuels) are bulk fuels that the heating type +# input cannot separate within OTHER, so OTHER receives the bulk-fuel points. +OTHER: + 2025-10-01: 6 +NONE: + 2025-10-01: 0 +metadata: + unit: /1 + period: year + breakdown: + - heating_type + label: Indiana EAP heating fuel points + reference: + - title: Indiana PY2026 EAP Intake and Operations Manual, Sections 1.1 (page 10) and 8.5 (page 71) + href: https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=71 + - title: Indiana PY2025-26 EAP Benefit Matrix, page 1 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/IN_BenefitMatrix_Heat-Crisis_2026.pdf#page=1 + # Page 1 holds the fuel source rows and page 2 the fuel source instruction. + - title: Indiana PY2024-25 EAP Benefit Matrix, pages 1-2 + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/IN_BenefitMatrix_2025.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/heating_award_fuels.yaml b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/heating_award_fuels.yaml new file mode 100644 index 00000000000..b97b6e8d0c8 --- /dev/null +++ b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/heating_award_fuels.yaml @@ -0,0 +1,25 @@ +description: Indiana provides the regular heating award to households heating with these fuels under the Energy Assistance Program. +# The heating_type input names each fuel. OTHER covers wood pellets, corn and +# biofuels, which Section 8.5 lists as bulk fuels. +# KEROSENE is left out because Section 1.1 bars EAP funds for kerosene. +# SOLAR is left out as an assumption: neither the manual nor the matrix lists it. +# NONE and UNSPECIFIED are left out, so an unspecified fuel receives no heating +# award. Heat included in rent is handled in the benefit formula, not here. +values: + 2024-10-01: + - ELECTRICITY + - NATURAL_GAS + - FUEL_OIL + - PROPANE + - WOOD + - COAL + - OTHER +metadata: + unit: list + period: year + label: Indiana EAP heating award fuels + reference: + - title: Indiana PY2026 EAP Intake and Operations Manual, Sections 1.1 (page 10) and 8.5 (page 71) + href: https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=71 + - title: Indiana PY2024-25 EAP Benefit Matrix, page 1 + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/IN_BenefitMatrix_2025.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/income/higher_points.yaml b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/income/higher_points.yaml new file mode 100644 index 00000000000..658e16f934a --- /dev/null +++ b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/income/higher_points.yaml @@ -0,0 +1,13 @@ +description: Indiana provides these income points in the highest income band or when heat is included in rent under the Energy Assistance Program. +values: + 2025-10-01: 2 +metadata: + unit: /1 + period: year + label: Indiana EAP higher income points + reference: + # Page 69 holds the heat-in-rent rule and page 70 the 45% to 60% chart. + - title: Indiana PY2026 EAP Intake and Operations Manual, Section 8.3, pages 69-70 + href: https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=70 + - title: Indiana PY2025-26 EAP Benefit Matrix, page 1 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/IN_BenefitMatrix_Heat-Crisis_2026.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/income/lower_points.yaml b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/income/lower_points.yaml new file mode 100644 index 00000000000..13dfd2d2a28 --- /dev/null +++ b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/income/lower_points.yaml @@ -0,0 +1,15 @@ +description: Indiana provides these income points in the lowest income band under the Energy Assistance Program. +values: + 2024-10-01: 6 + 2025-10-01: 9 +metadata: + unit: /1 + period: year + label: Indiana EAP lower income points + reference: + - title: Indiana PY2026 EAP Intake and Operations Manual, Section 8.3, pages 69-70 + href: https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=69 + - title: Indiana PY2025-26 EAP Benefit Matrix, page 1 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/IN_BenefitMatrix_Heat-Crisis_2026.pdf#page=1 + - title: Indiana PY2024-25 EAP Benefit Matrix, page 1 + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/IN_BenefitMatrix_2025.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/income/lower_rate.yaml b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/income/lower_rate.yaml new file mode 100644 index 00000000000..f9b46945fce --- /dev/null +++ b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/income/lower_rate.yaml @@ -0,0 +1,10 @@ +description: Indiana sets the upper limit of the lowest income band at this share of state median income under the Energy Assistance Program. +values: + 2025-10-01: 0.3 +metadata: + unit: /1 + period: year + label: Indiana EAP lower income rate + reference: + - title: Indiana PY2026 EAP Intake and Operations Manual, Section 8.3, pages 69-70 + href: https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=69 diff --git a/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/income/middle_points.yaml b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/income/middle_points.yaml new file mode 100644 index 00000000000..d4f0fbeb90c --- /dev/null +++ b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/income/middle_points.yaml @@ -0,0 +1,15 @@ +description: Indiana provides these income points in the middle income band under the Energy Assistance Program. +values: + 2024-10-01: 4 + 2025-10-01: 5 +metadata: + unit: /1 + period: year + label: Indiana EAP middle income points + reference: + - title: Indiana PY2026 EAP Intake and Operations Manual, Section 8.3, page 70 + href: https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=70 + - title: Indiana PY2025-26 EAP Benefit Matrix, page 1 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/IN_BenefitMatrix_Heat-Crisis_2026.pdf#page=1 + - title: Indiana PY2024-25 EAP Benefit Matrix, page 1 + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/IN_BenefitMatrix_2025.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/income/middle_rate.yaml b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/income/middle_rate.yaml new file mode 100644 index 00000000000..ccc0e2b808b --- /dev/null +++ b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/income/middle_rate.yaml @@ -0,0 +1,10 @@ +description: Indiana sets the upper limit of the middle income band at this share of state median income under the Energy Assistance Program. +values: + 2025-10-01: 0.45 +metadata: + unit: /1 + period: year + label: Indiana EAP middle income rate + reference: + - title: Indiana PY2026 EAP Intake and Operations Manual, Section 8.3, page 70 + href: https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=70 diff --git a/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/vulnerability/elderly_age.yaml b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/vulnerability/elderly_age.yaml new file mode 100644 index 00000000000..19d0565abad --- /dev/null +++ b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/vulnerability/elderly_age.yaml @@ -0,0 +1,12 @@ +description: Indiana sets this minimum age for the elderly vulnerable population category under the Energy Assistance Program. +values: + 2025-10-01: 60 +metadata: + unit: year + period: year + label: Indiana EAP elderly age + reference: + - title: Indiana PY2026 EAP Intake and Operations Manual, Sections 3.4 (pages 26-27) and 8.6 (pages 71-72) + href: https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=26 + - title: Indiana PY2025-26 EAP Benefit Matrix, page 1 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/IN_BenefitMatrix_Heat-Crisis_2026.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/vulnerability/points.yaml b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/vulnerability/points.yaml new file mode 100644 index 00000000000..51ff391f34c --- /dev/null +++ b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/vulnerability/points.yaml @@ -0,0 +1,13 @@ +description: Indiana provides these additional points once per household with a vulnerable member under the Energy Assistance Program. +values: + 2025-10-01: 2 +metadata: + unit: /1 + period: year + label: Indiana EAP vulnerability points + reference: + # Section 8.6 starts on page 71; the two-point award is on page 72. + - title: Indiana PY2026 EAP Intake and Operations Manual, Section 8.6, pages 71-72 + href: https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=72 + - title: Indiana PY2025-26 EAP Benefit Matrix, page 1 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/IN_BenefitMatrix_Heat-Crisis_2026.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/vulnerability/young_child_age_limit.yaml b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/vulnerability/young_child_age_limit.yaml new file mode 100644 index 00000000000..a0942b3640d --- /dev/null +++ b/policyengine_us/parameters/gov/states/in/ihcda/eap/benefit/vulnerability/young_child_age_limit.yaml @@ -0,0 +1,12 @@ +description: Indiana sets this maximum age for the young child vulnerable population category under the Energy Assistance Program. +values: + 2025-10-01: 5 +metadata: + unit: year + period: year + label: Indiana EAP young child age limit + reference: + - title: Indiana PY2026 EAP Intake and Operations Manual, Sections 3.4 (pages 26-27) and 8.6 (pages 71-72) + href: https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=26 + - title: Indiana PY2025-26 EAP Benefit Matrix, page 1 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/IN_BenefitMatrix_Heat-Crisis_2026.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/in/ihcda/eap/eligibility/adult_age.yaml b/policyengine_us/parameters/gov/states/in/ihcda/eap/eligibility/adult_age.yaml new file mode 100644 index 00000000000..f4173dfc06e --- /dev/null +++ b/policyengine_us/parameters/gov/states/in/ihcda/eap/eligibility/adult_age.yaml @@ -0,0 +1,13 @@ +description: Indiana sets this minimum age for an applicant and for counting a household member's income under the Energy Assistance Program. +# Section 4.4 lets a person apply from this age. Section 6 counts income from this +# age, subject to the Section 6.2 student exclusions; Social Security benefits paid +# for younger members also count. +values: + 2025-10-01: 18 +metadata: + unit: year + period: year + label: Indiana EAP adult age + reference: + - title: Indiana PY2026 EAP Intake and Operations Manual, Sections 4.4 (page 29), 6 (page 47) and 6.2 (pages 57-58) + href: https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=47 diff --git a/policyengine_us/parameters/gov/states/in/ihcda/eap/eligibility/income_limit.yaml b/policyengine_us/parameters/gov/states/in/ihcda/eap/eligibility/income_limit.yaml new file mode 100644 index 00000000000..a893dddfc09 --- /dev/null +++ b/policyengine_us/parameters/gov/states/in/ihcda/eap/eligibility/income_limit.yaml @@ -0,0 +1,10 @@ +description: Indiana limits countable income to this share of state median income under the Energy Assistance Program. +values: + 2025-10-01: 0.6 +metadata: + unit: /1 + period: year + label: Indiana EAP income limit + reference: + - title: Indiana PY2026 EAP Intake and Operations Manual, Sections 3.1 (page 23) and 8.3 (pages 69-70) + href: https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=23 diff --git a/policyengine_us/parameters/gov/states/in/ihcda/eap/eligibility/minimum_monthly_rent.yaml b/policyengine_us/parameters/gov/states/in/ihcda/eap/eligibility/minimum_monthly_rent.yaml new file mode 100644 index 00000000000..8eaf78db205 --- /dev/null +++ b/policyengine_us/parameters/gov/states/in/ihcda/eap/eligibility/minimum_monthly_rent.yaml @@ -0,0 +1,10 @@ +description: Indiana sets this minimum monthly out-of-pocket rent for households with utilities included in rent under the Energy Assistance Program. +values: + 2025-10-01: 1 +metadata: + unit: currency-USD + period: month + label: Indiana EAP minimum monthly rent + reference: + - title: Indiana PY2026 EAP Intake and Operations Manual, Section 4.7, page 32 + href: https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=32 diff --git a/policyengine_us/parameters/gov/states/in/ihcda/eap/income/calculation_months.yaml b/policyengine_us/parameters/gov/states/in/ihcda/eap/income/calculation_months.yaml new file mode 100644 index 00000000000..72476bcb415 --- /dev/null +++ b/policyengine_us/parameters/gov/states/in/ihcda/eap/income/calculation_months.yaml @@ -0,0 +1,10 @@ +description: Indiana sets the income calculation period to this number of months under the Energy Assistance Program. +values: + 2025-10-01: 3 +metadata: + unit: month + period: year + label: Indiana EAP income calculation months + reference: + - title: Indiana PY2026 EAP Intake and Operations Manual, Sections 6 (page 47) and 8.3 (page 69) + href: https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=47 diff --git a/policyengine_us/parameters/gov/states/in/ihcda/eap/income/sources.yaml b/policyengine_us/parameters/gov/states/in/ihcda/eap/income/sources.yaml new file mode 100644 index 00000000000..b2e1e413598 --- /dev/null +++ b/policyengine_us/parameters/gov/states/in/ihcda/eap/income/sources.yaml @@ -0,0 +1,53 @@ +description: Indiana uses these income sources to compute countable income for adult members who are not exempt students under the Energy Assistance Program. +# Social Security and SSI are not listed here. The income variable adds them +# separately because benefits paid for children also count. +values: + 2025-10-01: + - employment_income + # Section 6.1 (pages 51-52) asks for gross business, farm and rental income before + # expenses. The business, farm and rental inputs below are net amounts, so + # countable income is understated for those households. + - self_employment_income + - sstb_self_employment_income + # Section 6.1 defines self-employment to include farming (Schedule F) and lists + # the Schedule E partnership and S corporation, estate and trust, and farm + # rental lines. + - farm_operations_income + - partnership_s_corp_income + - estate_income + - farm_rent_income + - unemployment_compensation + - strike_benefits + - pension_income + # Section 6.1 (pages 54-55) counts ongoing pension and retirement payments. + # Military retirement pay is a separate input from the pension inputs that + # pension_income adds, so both are listed; a household reports the amount + # in one or the other. + - military_retirement_pay + # Section 6.1 (pages 54-55) and Section 6.2 (page 59) exclude lump-sum retirement + # payments of the employee's contributions and interest, and withdrawals of + # principal. This input cannot separate them from recurring payments, so every + # distribution counts and income is overstated for those households. + - retirement_distributions + # Section 6.1 (pages 53-54) counts regular life insurance payments to a survivor, + # Black Lung benefits awarded to a survivor, and retirement funds paid to a + # surviving spouse or child. Social Security survivor benefits are added with + # Social Security, not here. + - survivor_benefits + - railroad_benefits + - alimony_income + - veterans_benefits + - workers_compensation + - disability_benefits + - financial_assistance + - ordinary_dividend_income + - interest_income + - rental_income + - gambling_winnings +metadata: + unit: list + period: year + label: Indiana EAP adult income sources + reference: + - title: Indiana PY2026 EAP Intake and Operations Manual, Sections 6.1 (pages 47-57) and 6.2 (pages 57-61) + href: https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=47 diff --git a/policyengine_us/parameters/gov/states/in/ihcda/eap/income/student_age_limit.yaml b/policyengine_us/parameters/gov/states/in/ihcda/eap/income/student_age_limit.yaml new file mode 100644 index 00000000000..c2d53662e2f --- /dev/null +++ b/policyengine_us/parameters/gov/states/in/ihcda/eap/income/student_age_limit.yaml @@ -0,0 +1,10 @@ +description: Indiana excludes dependent full-time college student income through this age under the Energy Assistance Program. +values: + 2025-10-01: 23 +metadata: + unit: year + period: year + label: Indiana EAP student age limit + reference: + - title: Indiana PY2026 EAP Intake and Operations Manual, Section 6.2, page 58 + href: https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=58 diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index ef4c5b64187..72d585a3538 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, KY, MS, NE, TX + coverage: OR, DC, Riverside County, MA, IL, IN, KS, KY, MS, NE, TX state_implementations: - state: OR status: in_progress @@ -823,6 +823,14 @@ programs: name: Illinois LIHEAP full_name: Illinois Low Income Home Energy Assistance Program variable: il_liheap + - state: IN + status: partial + name: Indiana EAP + full_name: Indiana Energy Assistance Program + variable: in_eap + parameter_prefix: gov.states.in.ihcda.eap + verified_years: "2026" + notes: PY2026 regular heating and the winter electric allowance only. Crisis, cooling and weatherization assistance are out of scope. - state: KS status: partial name: Kansas LIEAP diff --git a/policyengine_us/tests/policy/baseline/gov/states/in/ihcda/eap/in_eap.yaml b/policyengine_us/tests/policy/baseline/gov/states/in/ihcda/eap/in_eap.yaml new file mode 100644 index 00000000000..4ac6644c22d --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/in/ihcda/eap/in_eap.yaml @@ -0,0 +1,343 @@ +- name: Case 1, published three-month band 1 ceilings at the boundary. + period: 2026 + input: + state_code: [IN, IN, IN, IN, IN, IN, IN, IN, IN, IN] + age: [40, 40, 40, 40, 40, 40, 40, 40, 40, 40] + ssi: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + social_security: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + receives_ssi: [false, false, false, false, false, false, false, false, false, false] + receives_medicaid: [false, false, false, false, false, false, false, false, false, false] + rent: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + in_eap_eligible: [true, true, true, true, true, true, true, true, true, true] + in_eap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + in_eap_income: [16_776, 21_940, 27_100, 32_264, 37_428, 42_588, 43_556, 44_524, 45_492, 46_460] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, + NATURAL_GAS, NATURAL_GAS] + gas_expense: [1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000] + pre_subsidy_electricity_expense: [1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000] + output: + in_eap: [450, 450, 450, 450, 450, 450, 450, 450, 450, 450] +- name: Case 2, published three-month band 1 ceilings one dollar above. + period: 2026 + input: + state_code: [IN, IN, IN, IN, IN, IN, IN, IN, IN, IN] + age: [40, 40, 40, 40, 40, 40, 40, 40, 40, 40] + ssi: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + social_security: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + receives_ssi: [false, false, false, false, false, false, false, false, false, false] + receives_medicaid: [false, false, false, false, false, false, false, false, false, false] + rent: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + in_eap_eligible: [true, true, true, true, true, true, true, true, true, true] + in_eap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + in_eap_income: [16_780, 21_944, 27_104, 32_268, 37_432, 42_592, 43_560, 44_528, 45_496, 46_464] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, + NATURAL_GAS, NATURAL_GAS] + gas_expense: [1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000] + pre_subsidy_electricity_expense: [1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000] + output: + in_eap: [325, 325, 325, 325, 325, 325, 325, 325, 325, 325] +- name: Case 3, published three-month band 2 ceilings at the boundary. + period: 2026 + input: + state_code: [IN, IN, IN, IN, IN, IN, IN, IN, IN, IN] + age: [40, 40, 40, 40, 40, 40, 40, 40, 40, 40] + ssi: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + social_security: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + receives_ssi: [false, false, false, false, false, false, false, false, false, false] + receives_medicaid: [false, false, false, false, false, false, false, false, false, false] + rent: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + in_eap_eligible: [true, true, true, true, true, true, true, true, true, true] + in_eap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + in_eap_income: [25_164, 32_908, 40_652, 48_396, 56_140, 63_884, 65_336, 66_788, 68_240, 69_692] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, + NATURAL_GAS, NATURAL_GAS] + gas_expense: [1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000] + pre_subsidy_electricity_expense: [1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000] + output: + in_eap: [325, 325, 325, 325, 325, 325, 325, 325, 325, 325] +- name: Case 4, published three-month band 2 ceilings one dollar above. + period: 2026 + input: + state_code: [IN, IN, IN, IN, IN, IN, IN, IN, IN, IN] + age: [40, 40, 40, 40, 40, 40, 40, 40, 40, 40] + ssi: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + social_security: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + receives_ssi: [false, false, false, false, false, false, false, false, false, false] + receives_medicaid: [false, false, false, false, false, false, false, false, false, false] + rent: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + in_eap_eligible: [true, true, true, true, true, true, true, true, true, true] + in_eap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + in_eap_income: [25_168, 32_912, 40_656, 48_400, 56_144, 63_888, 65_340, 66_792, 68_244, 69_696] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, + NATURAL_GAS, NATURAL_GAS] + gas_expense: [1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000] + pre_subsidy_electricity_expense: [1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000] + output: + in_eap: [200, 200, 200, 200, 200, 200, 200, 200, 200, 200] +- name: Case 5, fuel points by heating type with kerosene, solar, unspecified and no heating limited to the electric allowance. + # One person, $0 income: band 1 (9 income points, $125 electric); single-family: 3 points. + # Section 8.5 (page 71): natural gas and electricity 1 point, bulk fuels 6 points. + # Electricity, natural gas: (9 + 3 + 1) x $25 + $125 = $450. + # Propane, fuel oil, coal, wood and other (pellets, corn, biofuels): (9 + 3 + 6) x $25 + $125 = $575. + # Kerosene (Section 1.1, page 10, bars EAP funds for it), solar, unspecified and no + # heating earn zero fuel points and no heating award: the $125 electric payment only. + period: 2026 + input: + state_code: [IN, IN, IN, IN, IN, IN, IN, IN, IN, IN, IN] + age: [40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40] + ssi: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + social_security: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + receives_ssi: [false, false, false, false, false, false, false, false, false, false, false] + receives_medicaid: [false, false, false, false, false, false, false, false, false, false, false] + rent: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + in_eap_eligible: [true, true, true, true, true, true, true, true, true, true, true] + in_eap_income: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + heating_type: [ELECTRICITY, NATURAL_GAS, PROPANE, FUEL_OIL, KEROSENE, COAL, WOOD, OTHER, SOLAR, UNSPECIFIED, + NONE] + heating_expense: [1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000] + pre_subsidy_electricity_expense: [1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000] + output: + in_eap: [450, 450, 575, 575, 125, 575, 575, 575, 125, 125, 125] +- name: Case 6, vulnerability points are awarded once across age, disability, military and pregnancy categories. + period: 2026 + input: + state_code: [IN, IN, IN, IN, IN, IN, IN, IN] + age: [59, 60, 40, 40, 40, 40, 40, 60] + ssi: [0, 0, 0, 0, 0, 1, 0, 1] + social_security: [0, 0, 0, 0, 0, 0, 0, 0] + receives_ssi: [false, false, false, false, false, false, false, false] + receives_medicaid: [false, false, false, false, false, false, true, true] + rent: [0, 0, 0, 0, 0, 0, 0, 0] + in_eap_eligible: [true, true, true, true, true, true, true, true] + in_eap_income: [0, 0, 0, 0, 0, 0, 0, 0] + heating_type: [PROPANE, PROPANE, PROPANE, PROPANE, PROPANE, PROPANE, PROPANE, PROPANE] + heating_expense: [1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000] + pre_subsidy_electricity_expense: [1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000, 1_000] + is_veteran: [false, false, true, false, false, false, false, true] + is_military: [false, false, false, true, false, false, false, true] + current_pregnancies: [0, 0, 0, 0, 1, 0, 0, 0] + is_disabled: [false, false, false, false, false, false, true, true] + output: + in_eap: [575, 625, 625, 625, 625, 625, 625, 625] +- name: Case 7, utilities included in rent require one dollar of monthly out-of-pocket rent. + period: 2026 + input: + state_code: [IN, IN, IN] + age: [40, 40, 40] + ssi: [0, 0, 0] + social_security: [0, 0, 0] + receives_ssi: [false, false, false] + receives_medicaid: [false, false, false] + rent: [0, 11, 12] + in_eap_eligible: [true, true, true] + in_eap_income: [0, 0, 0] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + heat_expense_included_in_rent: [true, true, true] + tenant_pays_utilities: [false, false, false] + output: + in_eap: [0, 0, 100] +- name: Case 8, heat in rent with separately billed electricity keeps the income-based electric payment. + period: 2026 + input: + state_code: [IN, IN, IN] + age: [40, 40, 40] + ssi: [0, 0, 0] + social_security: [0, 0, 0] + receives_ssi: [false, false, false] + receives_medicaid: [false, false, false] + rent: [1_200, 1_200, 1_200] + in_eap_eligible: [true, true, true] + in_eap_income: [0, 20_000, 30_000] + heating_type: [UNSPECIFIED, UNSPECIFIED, UNSPECIFIED] + heat_expense_included_in_rent: [true, true, true] + pre_subsidy_electricity_expense: [1_000, 1_000, 1_000] + output: + in_eap: [175, 150, 100] +- name: Case 9, regular benefits are not capped at current bills and no electric service means no electric payment. + period: 2026 + input: + state_code: [IN, IN] + age: [40, 40] + ssi: [0, 0] + social_security: [0, 0] + receives_ssi: [false, false] + receives_medicaid: [false, false] + rent: [0, 0] + in_eap_eligible: [true, true] + in_eap_income: [0, 0] + heating_type: [NATURAL_GAS, NATURAL_GAS] + gas_expense: [10, 10] + pre_subsidy_electricity_expense: [0, 1] + output: + in_eap: [325, 450] + +- name: Case 10, dwelling type applies the published three, two and one point awards. + # Section 8.4: single-family 3, mobile home 2, multi-unit 1; $25 per point. + # Low band: (9 + dwelling + 1 gas) * $25 + $125 electricity. + # High band: (2 + dwelling + 1 gas) * $25 + $50 electricity. + period: 2026 + input: + state_code: [IN, IN, IN, IN, IN, IN] + age: [40, 40, 40, 40, 40, 40] + ssi: [0, 0, 0, 0, 0, 0] + social_security: [0, 0, 0, 0, 0, 0] + receives_ssi: [false, false, false, false, false, false] + receives_medicaid: [false, false, false, false, false, false] + rent: [0, 0, 0, 0, 0, 0] + in_eap_eligible: [true, true, true, true, true, true] + in_eap_household_size: [1, 1, 1, 1, 1, 1] + in_eap_income: [0, 0, 0, 30_000, 30_000, 30_000] + in_eap_dwelling_type: [SINGLE_FAMILY, MOBILE_HOME, MULTI_UNIT, + SINGLE_FAMILY, MOBILE_HOME, MULTI_UNIT] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + gas_expense: [1_000, 1_000, 1_000, 1_000, 1_000, 1_000] + pre_subsidy_electricity_expense: [1_000, 1_000, 1_000, 1_000, 1_000, 1_000] + output: + in_eap: [450, 425, 400, 200, 175, 150] + +- name: Case 11, dwelling type does not change zero dwelling points when heat is in rent. + # Section 8.4: zero dwelling points for every type when heat is in rent. + # Two income points * $25 plus the $50 electricity-in-rent allowance. + period: 2026 + input: + state_code: [IN, IN, IN] + age: [40, 40, 40] + ssi: [0, 0, 0] + social_security: [0, 0, 0] + receives_ssi: [false, false, false] + receives_medicaid: [false, false, false] + rent: [1_200, 1_200, 1_200] + in_eap_eligible: [true, true, true] + in_eap_income: [0, 0, 0] + in_eap_dwelling_type: [SINGLE_FAMILY, MOBILE_HOME, MULTI_UNIT] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + heat_expense_included_in_rent: [true, true, true] + tenant_pays_utilities: [false, false, false] + output: + in_eap: [100, 100, 100] + +- name: Case 12, other fuel reads the other heating fuel bill for bulk-fuel points and kerosene earns no heating award. + # Wages: $7.25 x 2,080 hours = $15,080. Three-month income $15,080 x 3 / 12 = $3,770 + # is within the size 1 band 1 ceiling floor($55,928 x 0.30 x 3 / 12) = $4,194: + # 9 income points and the $125 electric payment. Single-family dwelling: 3 points. + # Household 1, OTHER with a $900 other-fuel bill (Section 8.5, page 71: pellets, corn + # and biofuels are 6-point bulk fuels): (9 + 3 + 6) x $25 + $125 = $575. + # Household 2, OTHER with the bill reported only as wood: heating_expense reads + # other_heating_fuel_expense for OTHER, so there is no heating bill: $125 only. + # Household 3, KEROSENE with a $1,400 fuel bill (Section 1.1, page 10): zero fuel + # points, so no heating award: $125 only. + period: 2026 + input: + state_code: [IN, IN, IN] + age: [40, 40, 40] + employment_income: [15_080, 15_080, 15_080] + heating_type: [OTHER, OTHER, KEROSENE] + other_heating_fuel_expense: [900, 0, 0] + wood_expense: [0, 900, 0] + fuel_oil_expense: [0, 0, 1_400] + pre_subsidy_electricity_expense: [1_200, 1_200, 1_200] + output: + heating_expense: [900, 0, 1_400] + in_eap_income: [15_080, 15_080, 15_080] + in_eap: [575, 125, 125] + +- name: Case 13, all utilities in rent through the tenant flag alone pays both benefits. + # Section 4.7 (page 33): a tenant who pays all utilities in rent receives both + # benefits. tenant_pays_utilities: false, with the heat flag omitted, places heat + # and electricity in rent: 2 income points regardless of income (here $12,000, a + # band 1 income), no dwelling or fuel points, and the $50 electric-in-rent payment. + # Household 1: 2 x $25 + $50 = $100. + # Household 2, age 60: (2 + 2 vulnerability) x $25 + $50 = $150. + # Household 3, $11 a year in rent, below $1 a month ($12 a year): $0. + # Household 4, gas and electric bills also reported: the bills earn no matrix + # heating points once heat is in rent, so 2 x $25 + $50 = $100. + period: 2026 + input: + state_code: [IN, IN, IN, IN] + age: [40, 60, 40, 40] + rent: [9_600, 9_600, 11, 9_600] + in_eap_eligible: [true, true, true, true] + in_eap_income: [12_000, 12_000, 12_000, 12_000] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + tenant_pays_utilities: [false, false, false, false] + gas_expense: [0, 0, 0, 1_000] + pre_subsidy_electricity_expense: [0, 0, 0, 1_000] + output: + in_eap: [100, 150, 0, 100] + +- name: Case 14, size 11 income bands follow the federal adjustment beyond the printed chart. + # Section 8.3 (page 69) prints the charts through size 10 and refers larger + # households to the calculation methodology. 45 CFR 96.85 adds 3% of the four-person + # state median income for each member beyond six, so size 11 uses 52% + 5 x 16% + + # 5 x 3% = 147%: floor($107,555 x 1.47) = $158,105. The same method at 144% gives + # the printed size 10 three-month ceilings of $11,615 and $17,423 (page 70). + # Band 1 three-month ceiling: floor($158,105 x 0.30 x 3 / 12) = $11,857, so annual + # income of $47,428 ($11,857 x 4) is in band 1 and $47,432 ($11,858 x 4) in band 2. + # Band 2 three-month ceiling: floor($158,105 x 0.45 x 3 / 12) = $17,786, so annual + # income of $71,144 ($17,786 x 4) is in band 2 and $71,148 ($17,787 x 4) in band 3. + # Natural gas, single-family (pages 69-72): band 1 (9 + 3 + 1) x $25 + $125 = $450; + # band 2 (5 + 3 + 1) x $25 + $100 = $325; band 3 (2 + 3 + 1) x $25 + $50 = $200. + period: 2026 + input: + state_code: [IN, IN, IN, IN] + age: [40, 40, 40, 40] + in_eap_eligible: [true, true, true, true] + in_eap_household_size: [11, 11, 11, 11] + in_eap_income: [47_428, 47_432, 71_144, 71_148] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + gas_expense: [1_000, 1_000, 1_000, 1_000] + pre_subsidy_electricity_expense: [1_000, 1_000, 1_000, 1_000] + output: + in_eap_smi: [158_105, 158_105, 158_105, 158_105] + in_eap: [450, 325, 325, 200] + +- name: Case 15, SSDI or reported SSI receipt alone makes a household vulnerable and a disability flag alone does not. + # Section 3.4 (page 26) treats a member as a person with a disability when they + # receive SSI, SSDI or another listed benefit. A disability reported without one of + # those benefits needs a doctor's statement and a pending SSA claim, which the model + # cannot identify. Section 8.6 (page 72) awards 2 points for a vulnerable member. + # No household receives an SSI payment in the model or Medicaid. + # Household 1, SSDI of $1,200 x 12 = $14,400: three-month income $3,600 is within the + # size 1 band 1 ceiling of $4,194 (page 69): (9 + 3 + 6 + 2) x $25 + $125 = $625. + # Household 2, reports SSI receipt, wages $12.00 x 780 hours = $9,360 (three-month + # income $2,340, band 1): (9 + 3 + 6 + 2) x $25 + $125 = $625. + # Household 3, disabled with no SSI, SSDI or Medicaid, wages $7.25 x 2,080 hours = + # $15,080 (three-month income $3,770, band 1): (9 + 3 + 6) x $25 + $125 = $575. + period: 2026 + input: + state_code: [IN, IN, IN] + age: [40, 40, 40] + ssi: [0, 0, 0] + social_security_disability: [14_400, 0, 0] + receives_ssi: [false, true, false] + is_disabled: [false, false, true] + receives_medicaid: [false, false, false] + employment_income: [0, 9_360, 15_080] + heating_type: [PROPANE, PROPANE, PROPANE] + bottled_gas_expense: [1_800, 1_800, 1_800] + pre_subsidy_electricity_expense: [1_200, 1_200, 1_200] + output: + in_eap_income: [14_400, 9_360, 15_080] + in_eap: [625, 625, 575] + +- name: Case 16, heat in rent with separately billed electricity and rent under one dollar a month pays only the electric benefit. + # Section 4.7 (page 32): a client with utilities included in rent must pay at least + # $1 a month in rent to receive that benefit. Page 33: when heat is in rent and the + # tenant pays the electric bill directly, the electric benefit goes to the vendor. + # Wages: $7.25 x 2,080 hours = $15,080. Three-month income $3,770 is within the size 1 + # band 1 ceiling of $4,194 (page 69), so the electric payment is $125 (page 72). + # Households 1 and 2, rent of $0 and $11 a year, below $12: no heating award, $125. + # Household 3, rent of $12 a year: heat in rent earns 2 income points (page 69) and + # no dwelling or fuel points: 2 x $25 + $125 = $175. + period: 2026 + input: + state_code: [IN, IN, IN] + age: [40, 40, 40] + employment_income: [15_080, 15_080, 15_080] + rent: [0, 11, 12] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + heat_expense_included_in_rent: [true, true, true] + pre_subsidy_electricity_expense: [1_200, 1_200, 1_200] + output: + in_eap_income: [15_080, 15_080, 15_080] + in_eap: [125, 125, 175] diff --git a/policyengine_us/tests/policy/baseline/gov/states/in/ihcda/eap/in_eap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/in/ihcda/eap/in_eap_eligible.yaml new file mode 100644 index 00000000000..981055ff692 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/in/ihcda/eap/in_eap_eligible.yaml @@ -0,0 +1,94 @@ +- name: Case 1, published annual 60% of state median income limits at the boundary. + period: 2026 + input: + state_code: [IN, IN, IN, IN, IN, IN, IN, IN, IN, IN] + age: [40, 40, 40, 40, 40, 40, 40, 40, 40, 40] + ssi: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + social_security: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + receives_ssi: [false, false, false, false, false, false, false, false, false, false] + receives_medicaid: [false, false, false, false, false, false, false, false, false, false] + rent: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + in_eap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + in_eap_income: [33_556, 43_882, 54_207, 64_533, 74_857, 85_183, 87_119, 89_055, 90_991, 92_927] + heating_type: [UNSPECIFIED, UNSPECIFIED, UNSPECIFIED, UNSPECIFIED, UNSPECIFIED, UNSPECIFIED, UNSPECIFIED, + UNSPECIFIED, UNSPECIFIED, UNSPECIFIED] + output: + in_eap_eligible: [true, true, true, true, true, true, true, true, true, true] +- name: Case 2, published annual 60% of state median income limits one dollar above. + period: 2026 + input: + state_code: [IN, IN, IN, IN, IN, IN, IN, IN, IN, IN] + age: [40, 40, 40, 40, 40, 40, 40, 40, 40, 40] + ssi: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + social_security: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + receives_ssi: [false, false, false, false, false, false, false, false, false, false] + receives_medicaid: [false, false, false, false, false, false, false, false, false, false] + rent: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + in_eap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + in_eap_income: [33_557, 43_883, 54_208, 64_534, 74_858, 85_184, 87_120, 89_056, 90_992, 92_928] + heating_type: [UNSPECIFIED, UNSPECIFIED, UNSPECIFIED, UNSPECIFIED, UNSPECIFIED, UNSPECIFIED, UNSPECIFIED, + UNSPECIFIED, UNSPECIFIED, UNSPECIFIED] + output: + in_eap_eligible: [false, false, false, false, false, false, false, false, false, false] +- name: Case 3, federal household adjustment extends to households larger than the printed chart. + period: 2026 + input: + state_code: [IN, IN] + age: [40, 40] + ssi: [0, 0] + social_security: [0, 0] + receives_ssi: [false, false] + receives_medicaid: [false, false] + rent: [0, 0] + in_eap_household_size: [11, 11] + in_eap_income: [94_863, 94_864] + heating_type: [UNSPECIFIED, UNSPECIFIED] + output: + in_eap_eligible: [true, false] +- name: Case 4, household eligibility requires an adult and an eligible member independently of a utility bill. + period: 2026 + input: + state_code: [IN, IN, IN] + age: [17, 18, 18] + ssi: [0, 0, 0] + social_security: [0, 0, 0] + receives_ssi: [false, false, false] + receives_medicaid: [false, false, false] + rent: [0, 0, 0] + immigration_status: [CITIZEN, CITIZEN, UNDOCUMENTED] + in_eap_income: [0, 0, 0] + heating_type: [UNSPECIFIED, UNSPECIFIED, UNSPECIFIED] + output: + in_eap_eligible: [false, true, false] + +- name: Case 5, farm operations income puts a farm couple over the income limit. + # Section 6.1 (pages 51-52) defines self-employment to include farming (Schedule F). + # Income: $58,000 farm operations + $16,640 wages ($16.00 x 1,040 hours) = $74,640. + # Size 2 SMI: floor($107,555 x 0.68) = $73,137; 60% limit floor($43,882.20) = $43,882. + # $74,640 > $43,882, so the household is ineligible and receives $0. The wages alone + # ($16,640) would be within the limit. + period: 2026 + input: + people: + person1: + age: 52 + farm_operations_income: 58_000 + person2: + age: 50 + employment_income: 16_640 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: PROPANE + bottled_gas_expense: 2_400 + pre_subsidy_electricity_expense: 1_800 + households: + household: + members: [person1, person2] + state_code: IN + output: + in_eap_household_size: 2 + in_eap_smi: 73_137 + in_eap_income: 74_640 + in_eap_eligible: false + in_eap: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/in/ihcda/eap/in_eap_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/in/ihcda/eap/in_eap_income.yaml new file mode 100644 index 00000000000..7703a961d5b --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/in/ihcda/eap/in_eap_income.yaml @@ -0,0 +1,190 @@ +- name: Case 1, existing net business income remains an approximation without a new business deduction. + period: 2026 + input: + state_code: IN + age: 40 + ssi: 0 + social_security: 0 + receives_ssi: false + receives_medicaid: false + rent: 0 + employment_income: 12_000 + self_employment_income: 6_000 + sstb_self_employment_income: 3_000 + output: + # 12,000 wages + 6,000 ordinary business + 3,000 specified service business. + in_eap_income: 21_000 +- name: Case 2, adult dependent secondary and college student exemptions do not extend past age 23 for college. + period: 2026 + input: + state_code: [IN, IN, IN, IN] + age: [18, 23, 24, 23] + ssi: [0, 0, 0, 0] + social_security: [0, 0, 0, 0] + receives_ssi: [false, false, false, false] + receives_medicaid: [false, false, false, false] + rent: [0, 0, 0, 0] + is_tax_unit_dependent: [true, true, true, false] + is_in_secondary_school: [true, false, false, false] + is_full_time_college_student: [false, true, true, true] + employment_income: [12_000, 12_000, 12_000, 12_000] + sstb_self_employment_income: [3_000, 3_000, 3_000, 3_000] + interest_income: [1_000, 1_000, 1_000, 1_000] + output: + in_eap_income: [0, 0, 16_000, 16_000] +- name: Case 3, support paid reduces income while TANF, support received and capital gains are excluded. + period: 2026 + input: + state_code: IN + age: 40 + ssi: 1_200 + social_security: 12_000 + receives_ssi: false + receives_medicaid: false + rent: 0 + employment_income: 12_000 + child_support_expense: 2_400 + child_support_received: 4_000 + tanf: 4_000 + long_term_capital_gains: 4_000 + output: + # 12_000 wages + 12_000 Social Security (a 40-year-old is not enrolled in + # Medicare, so no premium is deducted) + 1_200 SSI - 2_400 support paid = + # 22_800. + in_eap_income: 22_800 +- name: Case 4, immigration excludes an adult from size but counts earnings and child Social Security counts despite excluded child wages. + period: 2026 + input: + people: + person1: + age: 35 + immigration_status: UNDOCUMENTED + employment_income: 12_000 + ssi: 0 + social_security: 0 + person2: + age: 10 + employment_income: 6_000 + social_security: 2_400 + ssi: 1_200 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: UNSPECIFIED + households: + household: + members: [person1, person2] + state_code: IN + output: + in_eap_household_size: 1 + in_eap_income: 15_600 + in_eap_eligible: true + +- name: Case 5, farm, partnership and S corporation, estate, farm rental, strike and pension income count. + # Section 6.1 (pages 51-52, 54, 56): Schedule F farm income, Schedule E partnership + # and S corporation, estate and trust, and farm rental lines, strike benefits and + # pensions count. One counted adult per household, one source each: + # farm operations $21,000; partnership $9,000 + S corporation $6,000 = $15,000; + # estate $4,800; farm rent $7,200; strike benefits $2,400; a military pension of + # $18,000, reported in military_retirement_pay. + period: 2026 + input: + state_code: [IN, IN, IN, IN, IN, IN] + age: [45, 45, 45, 45, 45, 45] + farm_operations_income: [21_000, 0, 0, 0, 0, 0] + partnership_income: [0, 9_000, 0, 0, 0, 0] + s_corp_income: [0, 6_000, 0, 0, 0, 0] + estate_income: [0, 0, 4_800, 0, 0, 0] + farm_rent_income: [0, 0, 0, 7_200, 0, 0] + strike_benefits: [0, 0, 0, 0, 2_400, 0] + military_retirement_pay: [0, 0, 0, 0, 0, 18_000] + output: + in_eap_income: [21_000, 15_000, 4_800, 7_200, 2_400, 18_000] + +- name: Case 6, each income source is floored at zero so a business, rental or farm loss does not offset wages. + # Section 6.1 (pages 51-52) counts gross business income before expenses, so a net + # loss in one source cannot reduce other income. Wages: $14.00 x 2,080 hours = $29,120. + # Household 1: max($29,120, 0) + max(-$15,000 self-employment, 0) = $29,120. + # Household 2: max($29,120, 0) + max(-$6,000 rental, 0) = $29,120. + # Household 3: max($29,120, 0) + max(-$9,000 farm operations, 0) = $29,120. + # Each also has a $2,000 specified service business loss, floored at zero. + # $29,120 is within the size 1 limit of $33,556. Three-month income $29,120 x 3 / 12 + # = $7,280 exceeds the band 2 ceiling floor($55,928 x 0.45 x 3 / 12) = $6,291: band 3. + # Natural gas, single-family: (2 + 3 + 1) x $25 + $50 electric = $200. + period: 2026 + input: + state_code: [IN, IN, IN] + age: [40, 40, 40] + employment_income: [29_120, 29_120, 29_120] + self_employment_income: [-15_000, 0, 0] + sstb_self_employment_income: [-2_000, -2_000, -2_000] + rental_income: [0, -6_000, 0] + farm_operations_income: [0, 0, -9_000] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + gas_expense: [1_100, 1_100, 1_100] + pre_subsidy_electricity_expense: [1_300, 1_300, 1_300] + output: + in_eap_income: [29_120, 29_120, 29_120] + in_eap_eligible: [true, true, true] + in_eap: [200, 200, 200] + +- name: Case 7, service business profits above the income limit prevent a heating award. + # Section 6.1 counts business income; a specified service business is not + # exempt. Existing net profits remain the documented gross-income approximation. + period: 2026 + input: + state_code: IN + age: 40 + sstb_self_employment_income: 60_000 + ssi: 0 + social_security: 0 + receives_ssi: false + receives_medicaid: false + heating_type: NATURAL_GAS + gas_expense: 1_000 + pre_subsidy_electricity_expense: 1_000 + output: + in_eap_income: 60_000 + # Above the published one-person annual limit of 33,556. + in_eap_eligible: false + in_eap: 0 + +- name: Case 8, survivor benefits other than Social Security count as income. + # Section 6.1 (page 54) counts life insurance payments issued on a regular basis to + # a surviving household member and retirement funds paid to a surviving spouse, and + # page 53 counts Black Lung benefits awarded to a survivor. + # A widow receives a $900 monthly survivor annuity from her late husband's pension + # ($900 x 12 = $10,800) and earns $12.00 x 780 hours = $9,360. + # Income: $10,800 + $9,360 = $20,160. + period: 2026 + input: + state_code: IN + age: 58 + survivor_benefits: 10_800 + employment_income: 9_360 + output: + in_eap_income: 20_160 + +- name: Case 9, the Medicare Part B premium is deducted from Social Security only when the person pays it. + # Section 6.1 (page 55) counts the net Social Security check after the Part B + # deduction. The 2026 standard premium is $202.90 x 12 = $2,434.80. + # Household 1: age 70, $12,000 of Social Security; countable income of + # $1,000 - $20 a month is under 135% of the poverty guideline, so a Medicare + # Savings Program covers the premium and the full $12,000 is counted. + # Household 2: age 70, $2,000 of Social Security and a $24,000 private pension; + # the premium is paid and exceeds the benefit, which is floored at zero: + # $0 + $24,000. + # Household 3: age 40 with $12,000 of Social Security disability and no + # Medicare enrollment: no premium, $12,000. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: [IN, IN, IN] + age: [70, 70, 40] + social_security_retirement: [12_000, 2_000, 0] + social_security_disability: [0, 0, 12_000] + taxable_private_pension_income: [0, 24_000, 0] + ssi: [0, 0, 0] + output: + medicare_part_b_premium: [0, 2_434.8, 0] + in_eap_income: [12_000, 24_000, 12_000] diff --git a/policyengine_us/tests/policy/baseline/gov/states/in/ihcda/eap/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/in/ihcda/eap/integration.yaml new file mode 100644 index 00000000000..5da8ca3538f --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/in/ihcda/eap/integration.yaml @@ -0,0 +1,295 @@ +- name: Case 1, pregnancy increases household size and supplies a single vulnerability bonus. + period: 2026 + input: + state_code: IN + age: 40 + ssi: 0 + social_security: 0 + receives_ssi: false + receives_medicaid: false + rent: 0 + current_pregnancies: 1 + employment_income: 0 + heating_type: PROPANE + bottled_gas_expense: 1_000 + pre_subsidy_electricity_expense: 1_000 + output: + in_eap_household_size: 2 + in_eap: 625 +- name: Case 2, young-child vulnerability stops after age five. + period: 2026 + input: + people: + person1: + age: 40 + ssi: 0 + social_security: 0 + person2: + age: 5 + ssi: 0 + social_security: 0 + person3: + age: 40 + ssi: 0 + social_security: 0 + person4: + age: 6 + ssi: 0 + social_security: 0 + spm_units: + spm_unit1: + members: [person1, person2] + heating_type: PROPANE + bottled_gas_expense: 1_000 + pre_subsidy_electricity_expense: 1_000 + spm_unit2: + members: [person3, person4] + heating_type: PROPANE + bottled_gas_expense: 1_000 + pre_subsidy_electricity_expense: 1_000 + households: + household1: + members: [person1, person2] + state_code: IN + household2: + members: [person3, person4] + state_code: IN + output: + in_eap: [625, 575] +- name: Case 3, mixed-state and over-income households receive zero. + period: 2026 + input: + state_code: [IN, IN, IL] + age: [40, 40, 40] + ssi: [0, 0, 0] + social_security: [0, 0, 0] + receives_ssi: [false, false, false] + receives_medicaid: [false, false, false] + rent: [0, 0, 0] + employment_income: [0, 33_557, 0] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + gas_expense: [1_000, 1_000, 1_000] + pre_subsidy_electricity_expense: [1_000, 1_000, 1_000] + output: + in_eap: [450, 0, 0] + +# Period 2025 reads the values in force on 2025-01-01: the PY2024-25 benefit matrix +# (https://liheapch.acf.gov/docs/2025/benefits-matricies/IN_BenefitMatrix_2025.pdf#page=1) +# and the FY2025 state median income. FY2025 is not a verified year: values that the +# PY2024-25 matrix does not change are backfilled from FY2026. +- name: Case 4, period 2025 uses the PY2024-25 benefit matrix. + # PY2024-25 matrix, page 1: income points 6, 4, 2; electric benefit $100, $75, $50; + # site-built single family 3 points; bulk fuels 6 points; natural gas 0 points and + # electric 0 points; $25 per point. Page 2: "Award zero (0) points if the household + # heats with natural gas or electricity." + # Size 1 SMI: floor($103,324 x 0.52) = $53,728; 60% limit floor($32,236.80) = $32,236. + # Page 1, size 1 three-month ceilings: $4,029 (30% of SMI) and $6,044 (45% of SMI). + # Households 1 to 3 earn $12.00 x 1,040 hours = $12,480. Three-month income + # $12,480 x 3 / 12 = $3,120 is within $4,029: band 1 (6 points, $100 electric). + # Household 1, natural gas: (6 + 3 + 0) x $25 + $100 = $325. + # Household 2, propane: (6 + 3 + 6) x $25 + $100 = $475. + # Household 3, electric heat: (6 + 3 + 0) x $25 + $100 = $325. + # Household 4 earns $10.00 x 2,080 hours = $20,800. Three-month income + # $20,800 x 3 / 12 = $5,200 is above $4,029 and within $6,044: band 2 (4 points, + # $75 electric). Natural gas: (4 + 3 + 0) x $25 + $75 = $250. + period: 2025 + input: + state_code: [IN, IN, IN, IN] + age: [40, 40, 40, 40] + employment_income: [12_480, 12_480, 12_480, 20_800] + heating_type: [NATURAL_GAS, PROPANE, ELECTRICITY, NATURAL_GAS] + gas_expense: [1_000, 0, 0, 1_000] + bottled_gas_expense: [0, 1_800, 0, 0] + pre_subsidy_electricity_expense: [1_200, 1_200, 2_000, 1_200] + output: + heating_expense: [1_000, 1_800, 2_000, 1_000] + in_eap_household_size: [1, 1, 1, 1] + in_eap_smi: [53_728, 53_728, 53_728, 53_728] + in_eap_income: [12_480, 12_480, 12_480, 20_800] + in_eap_eligible: [true, true, true, true] + in_eap: [325, 475, 325, 250] + +# The cases below start from raw inputs: SSI, Social Security, rent, household size, +# income and eligibility are computed by the model rather than supplied. +- name: Case 5, two-earner family with a four-year-old lands in the middle band from wages. + # Wages: $27,040 ($13.00 x 2,080 hours) + $10,400 ($10.00 x 1,040 hours) = $37,440. + # No member is aged, blind or disabled, so SSI is $0; no rent is reported. + # Size 3 SMI: floor($107,555 x 0.84) = $90,346; 60% limit floor($54,207.60) = $54,207. + # Three-month income: $37,440 x 3 / 12 = $9,360. + # Band 1 ceiling floor($90,346 x 0.30 x 3 / 12) = $6,775; band 2 ceiling + # floor($90,346 x 0.45 x 3 / 12) = $10,163, so band 2 (5 points, $100 electric). + # Points: 5 income + 2 mobile home + 6 propane + 2 vulnerable (child age 4) = 15. + # Heating 15 x $25 = $375; electric $100; total $475. + period: 2026 + input: + people: + person1: + age: 34 + employment_income: 27_040 + person2: + age: 32 + employment_income: 10_400 + person3: + age: 4 + spm_units: + spm_unit: + members: [person1, person2, person3] + heating_type: PROPANE + bottled_gas_expense: 1_800 + pre_subsidy_electricity_expense: 1_500 + in_eap_dwelling_type: MOBILE_HOME + households: + household: + members: [person1, person2, person3] + state_code: IN + output: + ssi: [0, 0, 0] + rent: [0, 0, 0] + heating_expense: 1_800 + in_eap_household_size: 3 + in_eap_smi: 90_346 + in_eap_income: 37_440 + in_eap_eligible: true + in_eap: 475 + +- name: Case 6, single parent with full-time wages lands in the highest band. + # Wages: $17.50 x 2,080 hours = $36,400; the seven-year-old has no income. + # Size 2 SMI: floor($107,555 x 0.68) = $73,137; 60% limit floor($43,882.20) = $43,882. + # Three-month income: $36,400 x 3 / 12 = $9,100, above the band 2 ceiling + # floor($73,137 x 0.45 x 3 / 12) = $8,227, so band 3 (2 points, $50 electric). + # Points: 2 income + 3 single-family + 1 natural gas = 6; a seven-year-old is not + # a vulnerable member. Heating 6 x $25 = $150; electric $50; total $200. + period: 2026 + input: + people: + person1: + age: 29 + employment_income: 36_400 + person2: + age: 7 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + gas_expense: 1_100 + pre_subsidy_electricity_expense: 1_400 + households: + household: + members: [person1, person2] + state_code: IN + output: + heating_expense: 1_100 + in_eap_household_size: 2 + in_eap_smi: 73_137 + in_eap_income: 36_400 + in_eap_eligible: true + in_eap: 200 + +- name: Case 7, elderly widow with Social Security retirement and a pension. + # Section 6.1 (page 55) counts the Social Security check net of the Medicare + # Part B premium. Social Security: $1,800 x 12 = $21,600 retirement. Pension: + # $5,000. The 2026 standard Part B premium is $202.90 x 12 = $2,434.80; her + # countable income ($26,600 / 12 - $20 = $2,196.67 a month) is above the + # Medicare Savings Program limit, so she pays it. + # Income: ($21,600 - $2,434.80) + $5,000 = $24,165.20. + # Computed SSI: $2,196.67 a month exceeds the $994 federal benefit rate, so SSI is $0. + # Size 1 SMI: floor($107,555 x 0.52) = $55,928; 60% limit floor($33,556.80) = $33,556. + # Three-month income: $24,165.20 x 3 / 12 = $6,041.30, above the band 1 ceiling + # floor($55,928 x 0.30 x 3 / 12) = $4,194 and within the band 2 ceiling + # floor($55,928 x 0.45 x 3 / 12) = $6,291, so band 2 (5 points, $100 electric). + # Counted gross, $26,600 x 3 / 12 = $6,650 would be band 3 (2 points, $50). + # Points: 5 income + 3 single-family + 6 wood + 2 vulnerable (age 60 or older) = 16. + # Heating 16 x $25 = $400; electric $100; total $500. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 72 + social_security_retirement: 21_600 + taxable_private_pension_income: 5_000 + spm_units: + spm_unit: + members: [person1] + heating_type: WOOD + wood_expense: 900 + pre_subsidy_electricity_expense: 1_200 + households: + household: + members: [person1] + state_code: IN + output: + social_security: 21_600 + medicare_part_b_premium: 2_434.8 + ssi: 0 + heating_expense: 900 + in_eap_household_size: 1 + in_eap_smi: 55_928 + in_eap_income: 24_165.2 + in_eap_eligible: true + in_eap: 500 + +- name: Case 8, disabled part-time worker qualifies as vulnerable through computed SSI. + # The adult meets the SSI disability criteria and earns $1,000 a month ($12,000), + # below the 2026 substantial gainful activity limit of $1,690 a month. + # SSI countable income: ($1,000 - $20 general - $65 earned) x 0.5 = $457.50 a month. + # SSI: $994 - $457.50 = $536.50 a month; $6,438 a year. + # Income: $12,000 wages + $6,438 SSI = $18,438. + # Three-month income: $18,438 x 3 / 12 = $4,609.50, above the band 1 ceiling of + # $4,194 and within the band 2 ceiling of $6,291, so band 2 (5 points, $100 electric). + # Medicaid receipt is not reported, so the vulnerability points come from SSI > 0. + # Points: 5 income + 1 multi-unit + 1 electric heat + 2 vulnerable = 9. + # Heating 9 x $25 = $225; electric $100; total $325. + period: 2026 + input: + people: + person1: + age: 45 + is_disabled: true + meets_ssi_disability_criteria: true + employment_income: 12_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_500 + in_eap_dwelling_type: MULTI_UNIT + households: + household: + members: [person1] + state_code: IN + output: + ssi: 6_438 + heating_expense: 1_500 + in_eap_household_size: 1 + in_eap_smi: 55_928 + in_eap_income: 18_438 + in_eap_eligible: true + in_eap: 325 + +- name: Case 9, all utilities in rent require one dollar of monthly out-of-pocket rent computed from pre-subsidy rent. + # Section 4.7 (pages 32-33): a tenant with utilities in rent must pay at least $1 a + # month in rent, and a tenant who pays all utilities in rent receives both benefits. + # Each tenant earns $11.00 x 2,080 hours = $22,880, within the size 1 limit of $33,556, + # and receives no rental assistance, so out-of-pocket rent equals pre-subsidy rent. + # Heat in rent: 2 income points regardless of income, no dwelling or fuel points; + # electricity in rent: $50. With rent of at least $12 a year: 2 x $25 + $50 = $100. + # Household 1: $750 a month ($9,000) market rent: $100. + # Household 2: exactly $1 a month ($12), the boundary: $100. + # Household 3: $11 a year, below $1 a month: $0. + # Household 4: no rent paid: $0. + period: 2026 + input: + state_code: [IN, IN, IN, IN] + age: [26, 26, 26, 26] + employment_income: [22_880, 22_880, 22_880, 22_880] + pre_subsidy_rent: [9_000, 12, 11, 0] + takes_up_housing_assistance_if_eligible: [false, false, false, false] + tenant_pays_utilities: [false, false, false, false] + in_eap_dwelling_type: [MULTI_UNIT, MULTI_UNIT, MULTI_UNIT, MULTI_UNIT] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + output: + rent: [9_000, 12, 11, 0] + in_eap_income: [22_880, 22_880, 22_880, 22_880] + in_eap_eligible: [true, true, true, true] + in_eap: [100, 100, 0, 0] diff --git a/policyengine_us/variables/gov/states/in/ihcda/eap/in_eap.py b/policyengine_us/variables/gov/states/in/ihcda/eap/in_eap.py new file mode 100644 index 00000000000..1cbf4dd7aef --- /dev/null +++ b/policyengine_us/variables/gov/states/in/ihcda/eap/in_eap.py @@ -0,0 +1,120 @@ +from policyengine_us.model_api import * + + +class in_eap(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Indiana EAP regular heating assistance" + documentation = ( + "Verified for FY2026. FY2025 uses the PY2024-25 benefit matrix for the " + "income points, electric payments and fuel points that differ from " + "FY2026. Its other values, and all earlier years, use model parameter " + "backfilling and are unverified historical estimates." + ) + defined_for = "in_eap_eligible" + reference = ( + # Section 8.11 (page 77) benefit formula, Sections 8.3-8.7 (pages 69-73) points + # and electric payment, Section 8.8 (pages 73-75), Sections 8.12-8.13 (pages + # 77-78), Section 1.1 (page 10), Section 3.4 (pages 26-27), Section 4.7 (pages + # 32-33) and Section 6 (page 47). + "https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=77", + ) + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states["in"].ihcda.eap + person = spm_unit.members + smi_amount = spm_unit("in_eap_smi", period) + # Sections 6 and 8.3 place a household in an income band using the most recent + # three months of income, so annual amounts are scaled to that period. + months_share = p.income.calculation_months / MONTHS_IN_YEAR + calculation_income = spm_unit("in_eap_income", period) * months_share + low = calculation_income <= np.floor( + smi_amount * p.benefit.income.lower_rate * months_share + ) + middle = calculation_income <= np.floor( + smi_amount * p.benefit.income.middle_rate * months_share + ) + # The shared tenant flag identifies all utilities included in rent, so Section + # 4.7 places both heat and electricity in rent for that household. It does not + # identify electricity alone included in rent while heat is billed separately; + # that mixed arrangement remains unsupported without an electricity-specific + # input. + electric_in_rent = ~spm_unit.household("tenant_pays_utilities", period) + heat_in_rent = ( + spm_unit("heat_expense_included_in_rent", period) | electric_in_rent + ) + rent_paid = ( + add(spm_unit, period, ["rent"]) + >= p.eligibility.minimum_monthly_rent * MONTHS_IN_YEAR + ) + fuel = spm_unit("heating_type", period) + fuel_points = p.benefit.fuel_points[fuel] + heating_bill = spm_unit("heating_expense", period) > 0 + # The fuels that receive the heating award are listed separately from the + # fuel points: the PY2024-25 matrix gave natural gas and electric heat zero + # fuel points but still paid the award on the income and dwelling points. + award_fuel = np.isin(fuel.decode_to_str(), p.benefit.heating_award_fuels) + heating_burden = where(heat_in_rent, rent_paid, heating_bill & award_fuel) + income_points = select( + [heat_in_rent, low, middle], + [ + p.benefit.income.higher_points, + p.benefit.income.lower_points, + p.benefit.income.middle_points, + ], + default=p.benefit.income.higher_points, + ) + dwelling = spm_unit("in_eap_dwelling_type", period) + # Section 8.4 awards zero dwelling points whenever heat is included in rent. + dwelling_points = where(heat_in_rent, 0, p.benefit.dwelling_points[dwelling]) + fuel_points = where(heat_in_rent, 0, fuel_points) + age = person("age", period) + # Reuse available benefit-receipt evidence. Doctor certification with a pending + # SSA claim, vocational rehabilitation and black-lung receipt lack exact inputs. + # Disability with Medicaid receipt approximates the specified Medicaid pathways. + disability = ( + (person("ssi", period) > 0) + | (add(person, period, ["receives_ssi"]) > 0) + | (person("social_security_disability", period) > 0) + | (person("is_disabled", period) & person("receives_medicaid", period)) + ) + vulnerable = spm_unit.any( + (age >= p.benefit.vulnerability.elderly_age) + | (age <= p.benefit.vulnerability.young_child_age_limit) + | disability + | person("is_veteran", period) + | person("is_military", period) + | (person("current_pregnancies", period) > 0) + ) + points = ( + income_points + + dwelling_points + + fuel_points + + vulnerable * p.benefit.vulnerability.points + ) + heating = points * p.benefit.amount_per_point * heating_burden + # Section 8.7 expressly treats this winter electric allowance as supporting the + # heating system. Section 8.11 denies it when there is no electric utility service. + electric_bill = (spm_unit("pre_subsidy_electricity_expense", period) > 0) | ( + (fuel == fuel.possible_values.ELECTRICITY) & heating_bill + ) + electric_burden = where(electric_in_rent, rent_paid, electric_bill) + electric = ( + select( + [electric_in_rent, low, middle], + [ + p.benefit.electricity.higher_income, + p.benefit.electricity.lower_income, + p.benefit.electricity.middle_income, + ], + default=p.benefit.electricity.higher_income, + ) + * electric_burden + ) + # Regular awards can create credits and are not capped at the current bill. + # Existing credit balances, account coding/ownership, unsafe or broken equipment, + # prior awards and discretionary supplements are not identified by current inputs. + # Excludes all crisis, cooling, weatherization and equipment payments. + return heating + electric diff --git a/policyengine_us/variables/gov/states/in/ihcda/eap/in_eap_dwelling_type.py b/policyengine_us/variables/gov/states/in/ihcda/eap/in_eap_dwelling_type.py new file mode 100644 index 00000000000..e094a5c4b7a --- /dev/null +++ b/policyengine_us/variables/gov/states/in/ihcda/eap/in_eap_dwelling_type.py @@ -0,0 +1,26 @@ +from policyengine_us.model_api import * + + +class INEAPDwellingType(Enum): + MOBILE_HOME = "Mobile home" + MULTI_UNIT = "Multi-unit dwelling (duplex or larger)" + SINGLE_FAMILY = "Site-built single-family or manufactured home on a foundation" + + +class in_eap_dwelling_type(Variable): + value_type = Enum + entity = SPMUnit + possible_values = INEAPDwellingType + default_value = INEAPDwellingType.SINGLE_FAMILY + definition_period = YEAR + label = "Dwelling type for Indiana EAP" + documentation = ( + "Section 8.4 classifies a manufactured home on a foundation as " + "single-family. Multi-unit includes duplexes and apartments. If omitted, " + "the model assumes single-family; report the type to apply the correct " + "dwelling points. Heat included in rent is reported separately." + ) + reference = ( + # Section 8.4 (pages 70-71). + "https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=70", + ) diff --git a/policyengine_us/variables/gov/states/in/ihcda/eap/in_eap_eligible.py b/policyengine_us/variables/gov/states/in/ihcda/eap/in_eap_eligible.py new file mode 100644 index 00000000000..208fe21f869 --- /dev/null +++ b/policyengine_us/variables/gov/states/in/ihcda/eap/in_eap_eligible.py @@ -0,0 +1,28 @@ +from policyengine_us.model_api import * + + +class in_eap_eligible(Variable): + value_type = bool + entity = SPMUnit + definition_period = YEAR + label = "Indiana EAP household eligibility" + defined_for = StateCode.IN + reference = ( + # Section 3.1 (page 23), Section 4.4 (page 29), Section 8.3 (pages 69-70) and + # Section 8.8 (pages 73-75). + "https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=23", + ) + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states["in"].ihcda.eap + income = spm_unit("in_eap_income", period) + limit = np.floor(spm_unit("in_eap_smi", period) * p.eligibility.income_limit) + # Section 4.4 permits an ineligible adult to apply for eligible household members. + # Emancipated minors, residency duration, eviction writs, SSNs, and sanctions + # cannot be determined here. Section 8.8 separates household eligibility from + # whether each utility can receive a benefit; burden is checked in in_eap. + return ( + (add(spm_unit, period, ["is_citizen_or_legal_immigrant"]) > 0) + & spm_unit.any(spm_unit.members("age", period) >= p.eligibility.adult_age) + & (income <= limit) + ) diff --git a/policyengine_us/variables/gov/states/in/ihcda/eap/in_eap_household_size.py b/policyengine_us/variables/gov/states/in/ihcda/eap/in_eap_household_size.py new file mode 100644 index 00000000000..2da449ef7ba --- /dev/null +++ b/policyengine_us/variables/gov/states/in/ihcda/eap/in_eap_household_size.py @@ -0,0 +1,25 @@ +from policyengine_us.model_api import * + + +class in_eap_household_size(Variable): + value_type = int + entity = SPMUnit + definition_period = YEAR + label = "Indiana EAP eligible household size" + defined_for = StateCode.IN + reference = ( + # Sections 3.2-3.3 (pages 23-25) and Sections 4.4-4.5 (pages 29-30). + "https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=23", + ) + + def formula(spm_unit, period, parameters): + # SPM units approximate the residence-based unit. Section 3.2 counts roommates + # and housemates as household members even when they are unrelated, and SPM + # units place unrelated roommates in separate units. Current inputs cannot + # identify all excluded foster children, exchange students, transient guests, + # or changes in residence during the three-month budget period. Citizenship is + # approximated by the existing qualified-status flag; SSN/document verification + # is not modeled. Reported pregnancies count under Section 3.2. + return add( + spm_unit, period, ["is_citizen_or_legal_immigrant", "current_pregnancies"] + ) diff --git a/policyengine_us/variables/gov/states/in/ihcda/eap/in_eap_income.py b/policyengine_us/variables/gov/states/in/ihcda/eap/in_eap_income.py new file mode 100644 index 00000000000..06d35943ef5 --- /dev/null +++ b/policyengine_us/variables/gov/states/in/ihcda/eap/in_eap_income.py @@ -0,0 +1,54 @@ +from policyengine_us.model_api import * + + +class in_eap_income(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Indiana EAP annual countable income" + defined_for = StateCode.IN + reference = ( + # Sections 6 and 6.1 (pages 47-57) and Section 6.2 (pages 57-61). + "https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=47", + ) + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states["in"].ihcda.eap + person = spm_unit.members + age = person("age", period) + dependent = person("is_tax_unit_dependent", period) + student = person("is_in_secondary_school", period) | ( + person("is_full_time_college_student", period) + & (age <= p.income.student_age_limit) + ) + counted = (age >= p.eligibility.adult_age) & ~(dependent & student) + # Use annual inputs as an approximation to the annualized preceding three months. + # Section 6.1 prefers paystub federal taxable gross when supplied; current wages + # do not identify that paystub field. The manual asks for gross amounts: + # Schedule C line 5, Schedule F line 9 and the Schedule E rent, partnership and + # S corporation, estate and trust, and farm rental lines. The existing + # self-employment, farm and rental inputs are net approximations of those + # amounts: gross inputs and separate business/work-expense adjustments are not + # modeled. Each source is floored at zero, so a loss in one source cannot + # offset other counted income. + other = 0 + for source in p.income.sources: + other = other + max_(person(source, period), 0) + other = other * counted + # Child SSA benefits count despite the general under-18 exclusion. Section 6.1 + # (page 55) counts the Social Security check net of the Medicare Part B and + # Part D premiums. medicare_part_b_premium is the Part B premium the person + # pays: zero when not enrolled, and excluding the share a Medicare Savings + # Program pays, which is not withheld from the check. The model has no Part D + # premium variable, so that deduction, tax withholding, overpayment recovery + # and garnishment are not modeled. + premium = person("medicare_part_b_premium", period) + social_security = max_(person("social_security", period) - premium, 0) + ssa = social_security + person("ssi", period) + # Actual support paid is deductible. Child support received, TANF, capital gains, + # tax refunds, and educational assistance are excluded. Recurring versus lump-sum + # receipts, royalties, protected employment, and specific VA/insurance exclusions + # cannot all be distinguished from existing inputs. + support = max_(person("child_support_expense", period), 0) + return max_(spm_unit.sum(other + ssa - support), 0) diff --git a/policyengine_us/variables/gov/states/in/ihcda/eap/in_eap_smi.py b/policyengine_us/variables/gov/states/in/ihcda/eap/in_eap_smi.py new file mode 100644 index 00000000000..0de3f79d516 --- /dev/null +++ b/policyengine_us/variables/gov/states/in/ihcda/eap/in_eap_smi.py @@ -0,0 +1,23 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.hhs.hhs_smi import smi + + +class in_eap_smi(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Indiana EAP household-adjusted state median income" + defined_for = StateCode.IN + reference = ( + # Section 8.3 (pages 69-70) and Section 8.7 (pages 72-73). + "https://www.in.gov/ihcda/files/Indiana-LIHEAP-Intake-and-Operations-Program-Manual-PY2026.pdf#page=69", + ) + + def formula(spm_unit, period, parameters): + size = spm_unit("in_eap_household_size", period) + state = spm_unit.household("state_code_str", period) + # Truncate household-adjusted 100% SMI before applying each income-band rate. + # This numerical pattern reproduces all 90 published monthly, quarterly and + # annual table values; the manual does not state an explicit rounding rule. + return where(size > 0, np.floor(smi(size, state, period, parameters)), 0)